From 82ae9ab953c03d1871b2a05d14794ef61cbb5f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Thu, 29 Jan 2026 10:03:52 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=82=B0=EC=B6=9C=EB=82=B4=EC=97=AD?= =?UTF-8?q?=EC=84=9C=20=EC=84=B8=EB=B6=80=ED=95=AD=EB=AA=A9=20=ED=91=9C?= =?UTF-8?q?=EC=8B=9C=20=EB=B0=8F=20=EB=B6=80=ED=98=B8=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 세부산출내역서: 개소별 BOM 품목 상세 표시 (품명/규격/수량/단가/금액) - 개소별 소계 행 추가 및 전체 합계 표시 - 부호 표시: loc.symbol → loc.code로 수정 (내역/세부산출 양쪽) - types.ts: 할인율/할인금액 필드 추가 (discountRate, discountAmount) --- src/components/quotes/QuotePreviewContent.tsx | 117 +++++++++++++----- src/components/quotes/types.ts | 8 ++ 2 files changed, 91 insertions(+), 34 deletions(-) diff --git a/src/components/quotes/QuotePreviewContent.tsx b/src/components/quotes/QuotePreviewContent.tsx index 6ff8d21f..a091aeed 100644 --- a/src/components/quotes/QuotePreviewContent.tsx +++ b/src/components/quotes/QuotePreviewContent.tsx @@ -204,7 +204,7 @@ export function QuotePreviewContent({ {index + 1} {loc.floor || '-'} - {loc.symbol || '-'} + {loc.code || '-'} {loc.productCode} {loc.openWidth} {loc.openHeight} @@ -301,45 +301,94 @@ export function QuotePreviewContent({ - - + + - - - - + + + + - {quoteData.locations.map((loc) => ( - - {/* 각 개소별 품목 상세 */} - - - - - - - - - - - ))} - {/* 소계 */} - - + + + + {/* BOM 품목 상세 */} + {bomItems.length > 0 ? ( + bomItems.map((item, itemIndex) => ( + + + + + + + + + + )) + ) : ( + + + + + )} + + {/* 개소별 소계 */} + + + + + + + + + ); + })} + + {/* 전체 합계 */} + + - - - -
부호항목부호품명 규격수량단위단가합계수량단위단가금액
{loc.symbol || '-'}항목명규격명{loc.quantity}SET - {(loc.unitPrice || 0).toLocaleString()} - - {(loc.totalPrice || 0).toLocaleString()} -
- 소계 + {quoteData.locations.map((loc, locIndex) => { + const bomItems = loc.bomResult?.items || []; + const locationSymbol = loc.code || `${loc.floor}-${locIndex + 1}`; + const locationSubtotal = loc.bomResult?.grand_total || loc.totalPrice || 0; + + return ( + + {/* 개소 헤더 */} +
+ [{locationSymbol}] {loc.floor} - {loc.name || loc.code} (수량: {loc.quantity}개) +
+ {itemIndex === 0 ? locationSymbol : ''} + + {item.item_name || item.item_code} + + {item.specification || '-'} + + {item.quantity} + + {item.unit || 'EA'} + + {item.unit_price.toLocaleString()} + + {item.total_price.toLocaleString()} +
{locationSymbol} + BOM 계산 결과 없음 +
+ [{locationSymbol}] 소계 ({loc.quantity}개) + + {loc.quantity} + + {(loc.bomResult?.grand_total || 0).toLocaleString()} + + {(locationSubtotal * loc.quantity).toLocaleString()} +
+ 총 합계 - {quoteData.locations.reduce((sum, loc) => sum + (loc.quantity || 0), 0)} + + {subtotal.toLocaleString()} {subtotal.toLocaleString()}
diff --git a/src/components/quotes/types.ts b/src/components/quotes/types.ts index ce4d3965..bb116005 100644 --- a/src/components/quotes/types.ts +++ b/src/components/quotes/types.ts @@ -158,6 +158,8 @@ export interface QuoteApiData { supply_amount: string | number; tax_amount: string | number; total_amount: string | number; + discount_rate?: number | null; // 할인율 (%) + discount_amount?: number | null; // 할인 금액 status: QuoteStatus; current_revision: number; is_final: boolean; @@ -688,6 +690,8 @@ export interface QuoteFormDataV2 { dueDate: string; remarks: string; status: 'draft' | 'temporary' | 'final'; // 작성중, 임시저장, 최종저장 + discountRate: number; // 할인율 (%) + discountAmount: number; // 할인 금액 locations: LocationItem[]; } @@ -853,6 +857,8 @@ export function transformV2ToApi( quantity: data.locations.reduce((sum, loc) => sum + loc.quantity, 0), unit_symbol: '개소', total_amount: grandTotal, + discount_rate: data.discountRate || 0, + discount_amount: data.discountAmount || 0, status: data.status === 'final' ? 'finalized' : 'draft', is_final: data.status === 'final', calculation_inputs: calculationInputs, @@ -972,6 +978,8 @@ export function transformApiToV2(apiData: QuoteApiData): QuoteFormDataV2 { // raw API: remarks || description, transformed: description remarks: apiData.remarks || apiData.description || transformed.description || '', status: mapStatus(apiData.status), + discountRate: Number(apiData.discount_rate) || 0, + discountAmount: Number(apiData.discount_amount) || 0, locations: locations, }; }