222 lines
9.3 KiB
TypeScript
222 lines
9.3 KiB
TypeScript
|
|
'use client';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 거래명세서 보기 모달
|
||
|
|
*
|
||
|
|
* 견적 데이터를 거래명세서 양식으로 표시
|
||
|
|
* - 공급자/공급받는자 정보
|
||
|
|
* - 품목내역
|
||
|
|
* - 금액 계산 (공급가액, 할인, 부가세, 합계)
|
||
|
|
* - 증명 문구 + 인감
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { DocumentViewer } from '@/components/document-system';
|
||
|
|
import type { QuoteFormDataV2 } from './QuoteRegistrationV2';
|
||
|
|
|
||
|
|
interface QuoteTransactionModalProps {
|
||
|
|
open: boolean;
|
||
|
|
onOpenChange: (open: boolean) => void;
|
||
|
|
quoteData: QuoteFormDataV2 | null;
|
||
|
|
/** 할인율 (%) */
|
||
|
|
discountRate?: number;
|
||
|
|
/** 할인금액 (원) */
|
||
|
|
discountAmount?: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function QuoteTransactionModal({
|
||
|
|
open,
|
||
|
|
onOpenChange,
|
||
|
|
quoteData,
|
||
|
|
discountRate = 0,
|
||
|
|
discountAmount = 0,
|
||
|
|
}: QuoteTransactionModalProps) {
|
||
|
|
if (!quoteData) return null;
|
||
|
|
|
||
|
|
// locations 배열 (undefined 방어)
|
||
|
|
const locations = quoteData.locations || [];
|
||
|
|
|
||
|
|
// 금액 계산
|
||
|
|
const subtotal = locations.reduce((sum, loc) => {
|
||
|
|
const locationTotal = (loc.items || []).reduce((itemSum, item) => itemSum + (item.amount || 0), 0);
|
||
|
|
return sum + locationTotal * (loc.quantity || 1);
|
||
|
|
}, 0);
|
||
|
|
const afterDiscount = subtotal - discountAmount;
|
||
|
|
const vat = Math.round(afterDiscount * 0.1);
|
||
|
|
const finalTotal = afterDiscount + vat;
|
||
|
|
|
||
|
|
// 부가세 포함 여부
|
||
|
|
const vatIncluded = quoteData.vatType === 'included';
|
||
|
|
|
||
|
|
// 오늘 날짜
|
||
|
|
const today = new Date().toISOString().split('T')[0];
|
||
|
|
|
||
|
|
return (
|
||
|
|
<DocumentViewer
|
||
|
|
title="거래명세서"
|
||
|
|
preset="inspection"
|
||
|
|
open={open}
|
||
|
|
onOpenChange={onOpenChange}
|
||
|
|
>
|
||
|
|
<div className="bg-white p-8 min-h-full">
|
||
|
|
{/* 문서 헤더 */}
|
||
|
|
<div className="text-center mb-6">
|
||
|
|
<h1 className="text-2xl font-bold tracking-widest mb-2">거 래 명 세 서</h1>
|
||
|
|
<p className="text-sm text-gray-600">
|
||
|
|
견적번호: {quoteData.quoteNumber || '-'} | 발행일: {quoteData.receiptDate || today}
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 공급자/공급받는자 정보 */}
|
||
|
|
<div className="grid grid-cols-2 gap-4 mb-4">
|
||
|
|
{/* 공급자 */}
|
||
|
|
<div className="border border-gray-300">
|
||
|
|
<div className="bg-gray-800 text-white p-2 text-sm font-medium text-center">
|
||
|
|
공급자
|
||
|
|
</div>
|
||
|
|
<div className="p-3 space-y-1 text-sm">
|
||
|
|
<div className="flex">
|
||
|
|
<span className="w-20 text-gray-600">상호</span>
|
||
|
|
<span>회사명</span>
|
||
|
|
</div>
|
||
|
|
<div className="flex">
|
||
|
|
<span className="w-20 text-gray-600">대표자</span>
|
||
|
|
<span>홍길동</span>
|
||
|
|
</div>
|
||
|
|
<div className="flex">
|
||
|
|
<span className="w-20 text-gray-600">사업자번호</span>
|
||
|
|
<span>123-12-12345</span>
|
||
|
|
</div>
|
||
|
|
<div className="flex">
|
||
|
|
<span className="w-20 text-gray-600">주소</span>
|
||
|
|
<span>주소명</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 공급받는자 */}
|
||
|
|
<div className="border border-gray-300">
|
||
|
|
<div className="bg-gray-800 text-white p-2 text-sm font-medium text-center">
|
||
|
|
공급받는자
|
||
|
|
</div>
|
||
|
|
<div className="p-3 space-y-1 text-sm">
|
||
|
|
<div className="flex">
|
||
|
|
<span className="w-20 text-gray-600">상호</span>
|
||
|
|
<span>{quoteData.clientName || '-'}</span>
|
||
|
|
</div>
|
||
|
|
<div className="flex">
|
||
|
|
<span className="w-20 text-gray-600">담당자</span>
|
||
|
|
<span>{quoteData.managerName || '-'}</span>
|
||
|
|
</div>
|
||
|
|
<div className="flex">
|
||
|
|
<span className="w-20 text-gray-600">연락처</span>
|
||
|
|
<span>{quoteData.contact || '-'}</span>
|
||
|
|
</div>
|
||
|
|
<div className="flex">
|
||
|
|
<span className="w-20 text-gray-600">현장명</span>
|
||
|
|
<span>{quoteData.siteName || '-'}</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 품목내역 */}
|
||
|
|
<div className="border border-gray-300 mb-4">
|
||
|
|
<div className="bg-gray-800 text-white p-2 text-sm font-medium text-center">
|
||
|
|
품목내역
|
||
|
|
</div>
|
||
|
|
<table className="w-full text-sm">
|
||
|
|
<thead>
|
||
|
|
<tr className="bg-gray-100 border-b border-gray-300">
|
||
|
|
<th className="p-2 text-center font-medium border-r border-gray-300 w-12">순번</th>
|
||
|
|
<th className="p-2 text-center font-medium border-r border-gray-300 w-28">품목코드</th>
|
||
|
|
<th className="p-2 text-left font-medium border-r border-gray-300">품명</th>
|
||
|
|
<th className="p-2 text-center font-medium border-r border-gray-300 w-24">규격</th>
|
||
|
|
<th className="p-2 text-center font-medium border-r border-gray-300 w-12">수량</th>
|
||
|
|
<th className="p-2 text-center font-medium border-r border-gray-300 w-12">단위</th>
|
||
|
|
<th className="p-2 text-right font-medium border-r border-gray-300 w-24">단가</th>
|
||
|
|
<th className="p-2 text-right font-medium w-24">공급가액</th>
|
||
|
|
</tr>
|
||
|
|
</thead>
|
||
|
|
<tbody>
|
||
|
|
{locations.length > 0 ? (
|
||
|
|
locations.map((location, index) => (
|
||
|
|
<tr key={location.id} className="border-b border-gray-300">
|
||
|
|
<td className="p-2 text-center border-r border-gray-300">{index + 1}</td>
|
||
|
|
<td className="p-2 text-center border-r border-gray-300">{location.productCode || '-'}</td>
|
||
|
|
<td className="p-2 border-r border-gray-300">{location.floor} / {location.symbol}</td>
|
||
|
|
<td className="p-2 text-center border-r border-gray-300">{location.width}x{location.height}</td>
|
||
|
|
<td className="p-2 text-center border-r border-gray-300">{location.quantity || 1}</td>
|
||
|
|
<td className="p-2 text-center border-r border-gray-300">SET</td>
|
||
|
|
<td className="p-2 text-right border-r border-gray-300">
|
||
|
|
{(location.items || []).reduce((sum, item) => sum + (item.amount || 0), 0).toLocaleString()}
|
||
|
|
</td>
|
||
|
|
<td className="p-2 text-right">
|
||
|
|
{((location.items || []).reduce((sum, item) => sum + (item.amount || 0), 0) * (location.quantity || 1)).toLocaleString()}
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
))
|
||
|
|
) : (
|
||
|
|
<tr className="border-b border-gray-300">
|
||
|
|
<td colSpan={8} className="p-4 text-center text-gray-400">
|
||
|
|
등록된 품목이 없습니다
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
)}
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 금액 계산 */}
|
||
|
|
<div className="border border-gray-300 mb-6">
|
||
|
|
<table className="w-full text-sm">
|
||
|
|
<tbody>
|
||
|
|
<tr className="border-b border-gray-300">
|
||
|
|
<td className="p-2 bg-gray-100 border-r border-gray-300 w-32">공급가액</td>
|
||
|
|
<td className="p-2 text-right">{subtotal.toLocaleString()}원</td>
|
||
|
|
</tr>
|
||
|
|
{/* 할인 적용 시에만 표시 */}
|
||
|
|
{(discountRate > 0 || discountAmount > 0) && (
|
||
|
|
<>
|
||
|
|
<tr className="border-b border-gray-300">
|
||
|
|
<td className="p-2 bg-gray-100 border-r border-gray-300">할인율</td>
|
||
|
|
<td className="p-2 text-right">{discountRate.toFixed(2)}%</td>
|
||
|
|
</tr>
|
||
|
|
<tr className="border-b border-gray-300">
|
||
|
|
<td className="p-2 bg-gray-100 border-r border-gray-300">할인액</td>
|
||
|
|
<td className="p-2 text-right text-red-600">
|
||
|
|
-{discountAmount.toLocaleString()}원
|
||
|
|
</td>
|
||
|
|
</tr>
|
||
|
|
<tr className="border-b border-gray-300">
|
||
|
|
<td className="p-2 bg-gray-100 border-r border-gray-300">할인 후 공급가액</td>
|
||
|
|
<td className="p-2 text-right">{afterDiscount.toLocaleString()}원</td>
|
||
|
|
</tr>
|
||
|
|
</>
|
||
|
|
)}
|
||
|
|
<tr className="border-b border-gray-300">
|
||
|
|
<td className="p-2 bg-gray-100 border-r border-gray-300">부가세 (10%)</td>
|
||
|
|
<td className="p-2 text-right">{vat.toLocaleString()}원</td>
|
||
|
|
</tr>
|
||
|
|
<tr>
|
||
|
|
<td className="p-2 bg-gray-100 border-r border-gray-300 font-medium">합계 금액</td>
|
||
|
|
<td className="p-2 text-right font-bold text-lg">₩ {finalTotal.toLocaleString()}</td>
|
||
|
|
</tr>
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* 증명 문구 */}
|
||
|
|
<div className="text-center py-6 border-t border-gray-300">
|
||
|
|
<p className="text-sm mb-4">위 금액을 거래하였음을 증명합니다.</p>
|
||
|
|
<p className="text-sm text-gray-600 mb-4">{quoteData.receiptDate || today}</p>
|
||
|
|
<div className="flex justify-center">
|
||
|
|
<div className="w-12 h-12 border-2 border-red-400 rounded-full flex items-center justify-center text-red-400 text-xs">
|
||
|
|
印
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</DocumentViewer>
|
||
|
|
);
|
||
|
|
}
|