diff --git a/src/components/quotes/QuoteCalculationReport.tsx b/src/components/quotes/QuoteCalculationReport.tsx new file mode 100755 index 00000000..ba3f7096 --- /dev/null +++ b/src/components/quotes/QuoteCalculationReport.tsx @@ -0,0 +1,539 @@ +/** + * 견적 산출내역서 / 견적서 컴포넌트 + * - documentType="견적서": 간단한 견적서 + * - documentType="견적산출내역서": 상세 산출내역서 + 소요자재 내역 + */ + +import { QuoteFormDataV2 } from "./QuoteRegistration"; +import type { BomMaterial } from "./types"; +import type { CompanyFormData } from "@/components/settings/CompanyInfoManagement/types"; + +interface QuoteCalculationReportProps { + quote: QuoteFormDataV2; + companyInfo?: CompanyFormData | null; + documentType?: "견적산출내역서" | "견적서"; + showDetailedBreakdown?: boolean; + showMaterialList?: boolean; +} + +export function QuoteCalculationReport({ + quote, + companyInfo, + documentType = "견적산출내역서", + showDetailedBreakdown = true, + showMaterialList = true +}: QuoteCalculationReportProps) { + const formatAmount = (amount: number | null | undefined) => { + if (amount == null) return '0'; + return Number(amount).toLocaleString('ko-KR'); + }; + + const formatDate = (dateStr: string) => { + if (!dateStr) return ''; + const date = new Date(dateStr); + return `${date.getFullYear()}년 ${String(date.getMonth() + 1).padStart(2, '0')}월 ${String(date.getDate()).padStart(2, '0')}일`; + }; + + // 총 금액 계산 (totalAmount > unitPrice * quantity > inspectionFee 우선순위) + const totalAmount = quote.items?.reduce((sum, item) => { + const itemTotal = item.totalAmount || + (item.unitPrice || 0) * (item.quantity || 1) || + (item.inspectionFee || 0) * (item.quantity || 1); + return sum + itemTotal; + }, 0) || 0; + + // 소요자재 내역 - BOM 자재 목록 (quote.bomMaterials)에서 가져옴 + // bomMaterials가 없으면 빈 배열 (BOM 계산 데이터 없음) + const materialItems = (quote.bomMaterials || []).map((material, index) => ({ + no: index + 1, + itemCode: material.itemCode || '-', + name: material.itemName || '-', + spec: material.specification || '-', + quantity: Math.floor(material.quantity || 1), + unit: material.unit || 'EA', + unitPrice: material.unitPrice || 0, + totalPrice: material.totalPrice || 0, + })); + + return ( + <> + + + {/* 문서 컴포넌트 */} +
+ {/* 문서 헤더 */} +
+
+ {documentType === "견적서" ? "견 적 서" : "견 적 산 출 내 역 서"} +
+
+ 문서번호: {quote.id || '-'} | 작성일자: {formatDate(quote.registrationDate || '')} +
+
+ + {/* 수요자 정보 */} +
+
수 요 자
+
+ + + + + + + + + + + + + + + + + + + +
업체명{quote.clientName || '-'}
현장명{quote.siteName || '-'}담당자{quote.manager || '-'}
제품명{quote.items?.[0]?.productName || '-'}연락처{quote.contact || '-'}
+
+
+ + {/* 공급자 정보 */} +
+
공 급 자
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
상호{companyInfo?.companyName || '-'}사업자등록번호{companyInfo?.businessNumber || '-'}
대표자{companyInfo?.representativeName || '-'}업태{companyInfo?.businessType || '-'}
종목{companyInfo?.businessCategory || '-'}
사업장주소{companyInfo?.address || '-'}
전화{companyInfo?.managerPhone || '-'}이메일{companyInfo?.email || '-'}
+
+
+ + {/* 총 견적금액 */} +
+
총 견적금액
+
₩ {formatAmount(totalAmount)}
+
※ 부가가치세 별도
+
+ + {/* 세부 산출내역서 */} + {showDetailedBreakdown && quote.items && quote.items.length > 0 && ( +
+
세 부 산 출 내 역
+ + + + + + + + + + + + + + + {quote.items.map((item, index) => { + // 단가: unitPrice > inspectionFee 우선순위 + const unitPrice = item.unitPrice || item.inspectionFee || 0; + // 금액: totalAmount > unitPrice * quantity 우선순위 + const itemTotal = item.totalAmount || unitPrice * (item.quantity || 1); + return ( + + + + + + + + + + ); + })} + + + + + + + +
No.품목명규격수량단위단가금액
{index + 1}{item.productName}{`${item.openWidth}×${item.openHeight}mm`}{Math.floor(item.quantity || 0)}{item.unit || 'SET'}{formatAmount(unitPrice)}{formatAmount(itemTotal)}
공급가액 합계{formatAmount(totalAmount)}
+
+ )} + + {/* 소요자재 내역 */} + {showMaterialList && documentType !== "견적서" && ( +
+
소 요 자 재 내 역
+ + {/* 제품 정보 */} +
+
+ + + + + + + + + + + + + + + + + + + + + +
제품구분{quote.items?.[0]?.productCategory === 'steel' ? '철재' : '스크린'}부호{quote.items?.[0]?.code || '-'}
오픈사이즈W {quote.items?.[0]?.openWidth || '-'} × H {quote.items?.[0]?.openHeight || '-'} (mm)제작사이즈W {Number(quote.items?.[0]?.openWidth || 0) + 100} × H {Number(quote.items?.[0]?.openHeight || 0) + 100} (mm)
수량{Math.floor(quote.items?.[0]?.quantity || 1)} {quote.items?.[0]?.unit || 'SET'}케이스2438 × 550 (mm)
+
+
+ + {/* 자재 목록 테이블 */} + {materialItems.length > 0 ? ( + + + + + + + + + + + + + {materialItems.map((item, index) => ( + + + + + + + + + ))} + +
No.품목코드자재명규격수량단위
{index + 1}{item.itemCode}{item.name}{item.spec}{item.quantity}{item.unit}
+ ) : ( +
+ 소요자재 정보가 없습니다. (BOM 계산 데이터가 필요합니다) +
+ )} +
+ )} + + {/* 비고사항 */} + {quote.remarks && ( +
+
비 고 사 항
+
+ {quote.remarks} +
+
+ )} + + {/* 서명란 */} +
+
+
+ 상기와 같이 견적합니다. +
+
+
+
{formatDate(quote.registrationDate || '')}
+
+ 공급자: {companyInfo?.companyName || '-'} (인) +
+
+
+
+ (인감
날인) +
+
+
+
+
+ + {/* 하단 안내사항 */} +
+

【 유의사항 】

+

1. 본 견적서는 {formatDate(quote.registrationDate || '')} 기준으로 작성되었으며, 자재 가격 변동 시 조정될 수 있습니다.

+

2. 견적 유효기간은 발행일로부터 30일이며, 기간 경과 시 재견적이 필요합니다.

+

3. 제작 사양 및 수량 변경 시 견적 금액이 변동될 수 있습니다.

+

4. 현장 여건에 따라 추가 비용이 발생할 수 있습니다.

+

+ 문의: {companyInfo?.managerName || quote.manager || '담당자'} | {companyInfo?.managerPhone || '-'} +

+
+
+ + ); +} \ No newline at end of file