feat: 생산/품질/자재/출고/주문 관리 페이지 구현
- 생산관리: 대시보드, 작업지시, 작업실적, 작업자화면 - 품질관리: 검사관리 (리스트/등록/상세) - 자재관리: 입고관리, 재고현황 - 출고관리: 출하관리 (리스트/등록/상세/수정) - 주문관리: 수주관리, 생산의뢰 - 기존 컴포넌트 개선: CardTransactionInquiry, VendorDetail, QuoteRegistration - IntegratedListTemplateV2 개선 - 공통 컴포넌트 분석 문서 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
202
src/components/orders/documents/TransactionDocument.tsx
Normal file
202
src/components/orders/documents/TransactionDocument.tsx
Normal file
@@ -0,0 +1,202 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* 거래명세서 문서 컴포넌트
|
||||
* - 스크린샷 형식 + 지출결의서 디자인 스타일
|
||||
*/
|
||||
|
||||
import { formatAmount } from "@/utils/formatAmount";
|
||||
import { OrderItem } from "@/components/orders";
|
||||
|
||||
interface TransactionDocumentProps {
|
||||
orderNumber: string;
|
||||
orderDate: string;
|
||||
client: string;
|
||||
clientBusinessNumber?: string;
|
||||
clientCeo?: string;
|
||||
clientContact?: string;
|
||||
clientAddress?: string;
|
||||
clientSiteName?: string;
|
||||
companyName?: string;
|
||||
companyCeo?: string;
|
||||
companyBusinessNumber?: string;
|
||||
companyContact?: string;
|
||||
companyAddress?: string;
|
||||
items: OrderItem[];
|
||||
subtotal: number;
|
||||
discountRate: number;
|
||||
totalAmount: number;
|
||||
}
|
||||
|
||||
export function TransactionDocument({
|
||||
orderNumber,
|
||||
orderDate,
|
||||
client,
|
||||
clientBusinessNumber = "123-45-67890",
|
||||
clientCeo = "대표자",
|
||||
clientContact = "010-0123-4567",
|
||||
clientAddress = "서울시 강남구",
|
||||
clientSiteName = "-",
|
||||
companyName = "(주)케이디산업",
|
||||
companyCeo = "홍길동",
|
||||
companyBusinessNumber = "123-45-67890",
|
||||
companyContact = "02-1234-5678",
|
||||
companyAddress = "서울 강남구 테헤란로 123",
|
||||
items,
|
||||
subtotal,
|
||||
discountRate,
|
||||
totalAmount,
|
||||
}: TransactionDocumentProps) {
|
||||
const discountAmount = Math.round(subtotal * (discountRate / 100));
|
||||
const afterDiscount = subtotal - discountAmount;
|
||||
const vat = Math.round(afterDiscount * 0.1);
|
||||
const finalTotal = afterDiscount + vat;
|
||||
|
||||
return (
|
||||
<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">
|
||||
수주번호: {orderNumber} | 발행일: {orderDate}
|
||||
</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>{companyName}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="w-20 text-gray-600">대표자</span>
|
||||
<span>{companyCeo}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="w-20 text-gray-600">사업자번호</span>
|
||||
<span>{companyBusinessNumber}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="w-20 text-gray-600">주소</span>
|
||||
<span>{companyAddress}</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>{client}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="w-20 text-gray-600">담당자</span>
|
||||
<span>{clientCeo}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="w-20 text-gray-600">연락처</span>
|
||||
<span>{clientContact}</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="w-20 text-gray-600">현장명</span>
|
||||
<span>{clientSiteName}</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-20">품목코드</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>
|
||||
{items.length > 0 ? (
|
||||
items.map((item, index) => (
|
||||
<tr key={item.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">{item.itemCode}</td>
|
||||
<td className="p-2 border-r border-gray-300">{item.itemName}</td>
|
||||
<td className="p-2 text-center border-r border-gray-300">{item.spec}</td>
|
||||
<td className="p-2 text-center border-r border-gray-300">{item.quantity}</td>
|
||||
<td className="p-2 text-center border-r border-gray-300">{item.unit}</td>
|
||||
<td className="p-2 text-right border-r border-gray-300">{formatAmount(item.unitPrice)}</td>
|
||||
<td className="p-2 text-right">{formatAmount(item.amount)}</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">{formatAmount(subtotal)}원</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">{discountRate}%</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">-{formatAmount(discountAmount)}원</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">{formatAmount(afterDiscount)}원</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">{formatAmount(vat)}원</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">₩ {formatAmount(finalTotal)}</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">{orderDate}</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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user