|
|
|
|
@@ -6,19 +6,28 @@ import { DocumentViewer } from '@/components/document-system';
|
|
|
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
|
import { toast } from 'sonner';
|
|
|
|
|
import { Document, DocumentItem } from '../types';
|
|
|
|
|
import { MOCK_ORDER_DATA, MOCK_SHIPMENT_DETAIL } from '../mockData';
|
|
|
|
|
import { MOCK_SHIPMENT_DETAIL } from '../mockData';
|
|
|
|
|
|
|
|
|
|
// 기존 문서 컴포넌트 import
|
|
|
|
|
import { DeliveryConfirmation } from '@/components/outbound/ShipmentManagement/documents/DeliveryConfirmation';
|
|
|
|
|
import { ShippingSlip } from '@/components/outbound/ShipmentManagement/documents/ShippingSlip';
|
|
|
|
|
|
|
|
|
|
// 수주서 문서 컴포넌트 import
|
|
|
|
|
import { SalesOrderDocument } from '@/components/orders/documents/SalesOrderDocument';
|
|
|
|
|
import type { ProductInfo } from '@/components/orders/documents/OrderDocumentModal';
|
|
|
|
|
import type { OrderItem } from '@/components/orders/actions';
|
|
|
|
|
|
|
|
|
|
// 품질검사 문서 컴포넌트 import
|
|
|
|
|
import {
|
|
|
|
|
ImportInspectionDocument,
|
|
|
|
|
ProductInspectionDocument,
|
|
|
|
|
JointbarInspectionDocument,
|
|
|
|
|
QualityDocumentUploader,
|
|
|
|
|
} from './documents';
|
|
|
|
|
|
|
|
|
|
// 제품검사 성적서 (신규 양식) import
|
|
|
|
|
import { InspectionReportDocument } from '@/components/quality/InspectionManagement/documents/InspectionReportDocument';
|
|
|
|
|
import { mockReportInspectionItems } from '@/components/quality/InspectionManagement/mockData';
|
|
|
|
|
import type { InspectionReportDocument as InspectionReportDocumentType } from '@/components/quality/InspectionManagement/types';
|
|
|
|
|
import type { ImportInspectionTemplate, ImportInspectionRef } from './documents/ImportInspectionDocument';
|
|
|
|
|
|
|
|
|
|
// 작업일지 + 중간검사 성적서 문서 컴포넌트 import (공정별 신규 버전)
|
|
|
|
|
@@ -44,6 +53,8 @@ interface InspectionModalV2Props {
|
|
|
|
|
itemName?: string;
|
|
|
|
|
specification?: string;
|
|
|
|
|
supplier?: string;
|
|
|
|
|
// 읽기 전용 모드 (QMS 심사 확인용)
|
|
|
|
|
readOnly?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 문서 타입별 정보
|
|
|
|
|
@@ -85,132 +96,38 @@ const PlaceholderDocument = ({ docType, docItem }: { docType: string; docItem: D
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 수주서 문서 컴포넌트 (간소화 버전)
|
|
|
|
|
const OrderDocument = () => {
|
|
|
|
|
const data = MOCK_ORDER_DATA;
|
|
|
|
|
// QMS용 수주서 Mock 데이터
|
|
|
|
|
const QMS_MOCK_PRODUCTS: ProductInfo[] = [
|
|
|
|
|
{ productName: '방화 스크린 셔터 (표준형)', productCategory: '스크린', openWidth: '3000', openHeight: '2500', quantity: 5, floor: '1F', code: 'FSS-01' },
|
|
|
|
|
{ productName: '방화 스크린 셔터 (방화형)', productCategory: '스크린', openWidth: '3000', openHeight: '2500', quantity: 3, floor: '2F', code: 'FSS-02' },
|
|
|
|
|
];
|
|
|
|
|
const QMS_MOCK_ORDER_ITEMS: OrderItem[] = [
|
|
|
|
|
{ id: 'mt-1', itemCode: 'MT-001', itemName: '모터(380V 단상)', specification: '150K', type: '모터', quantity: 8, unit: 'EA', unitPrice: 120000, supplyAmount: 960000, taxAmount: 96000, totalAmount: 1056000, sortOrder: 1 },
|
|
|
|
|
{ id: 'br-1', itemCode: 'BR-001', itemName: '브라켓트', specification: '380X180 [2-4"]', type: '브라켓', quantity: 16, unit: 'EA', unitPrice: 15000, supplyAmount: 240000, taxAmount: 24000, totalAmount: 264000, sortOrder: 2 },
|
|
|
|
|
{ id: 'gr-1', itemCode: 'GR-001', itemName: '가이드레일 백면형 (120X70)', specification: 'EGI 1.5ST', type: '가이드레일', quantity: 16, unit: 'EA', unitPrice: 25000, supplyAmount: 400000, taxAmount: 40000, totalAmount: 440000, width: 120, height: 2500, sortOrder: 3 },
|
|
|
|
|
{ id: 'cs-1', itemCode: 'CS-001', itemName: '케이스(셔터박스)', specification: 'EGI 1.5ST 380X180', type: '케이스', quantity: 8, unit: 'EA', unitPrice: 35000, supplyAmount: 280000, taxAmount: 28000, totalAmount: 308000, width: 380, height: 180, sortOrder: 4 },
|
|
|
|
|
{ id: 'bf-1', itemCode: 'BF-001', itemName: '하단마감재', specification: 'EGI 1.5ST', type: '하단마감재', quantity: 8, unit: 'EA', unitPrice: 18000, supplyAmount: 144000, taxAmount: 14400, totalAmount: 158400, sortOrder: 5 },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="bg-white p-8 w-full text-sm shadow-sm">
|
|
|
|
|
{/* 헤더 */}
|
|
|
|
|
<div className="flex justify-between items-start mb-6">
|
|
|
|
|
<div className="flex items-center gap-4">
|
|
|
|
|
<div className="text-2xl font-bold">KD</div>
|
|
|
|
|
<div className="text-xs">경동기업</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-2xl font-bold tracking-[0.5rem]">수 주 서</div>
|
|
|
|
|
<table className="text-xs border-collapse">
|
|
|
|
|
<tbody>
|
|
|
|
|
<tr>
|
|
|
|
|
<td className="border px-2 py-1 bg-gray-100" rowSpan={3}>
|
|
|
|
|
<div className="flex flex-col items-center">
|
|
|
|
|
<span>결</span><span>재</span>
|
|
|
|
|
</div>
|
|
|
|
|
</td>
|
|
|
|
|
<td className="border px-2 py-1 bg-gray-100 text-center w-16">작성</td>
|
|
|
|
|
<td className="border px-2 py-1 bg-gray-100 text-center w-16">검토</td>
|
|
|
|
|
<td className="border px-2 py-1 bg-gray-100 text-center w-16">승인</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td className="border px-2 py-1 h-10"></td>
|
|
|
|
|
<td className="border px-2 py-1 h-10"></td>
|
|
|
|
|
<td className="border px-2 py-1 h-10"></td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td className="border px-2 py-1 text-center bg-gray-50">판매</td>
|
|
|
|
|
<td className="border px-2 py-1 text-center bg-gray-50">생산</td>
|
|
|
|
|
<td className="border px-2 py-1 text-center bg-gray-50">품질</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* 기본 정보 */}
|
|
|
|
|
<table className="w-full border-collapse mb-6 text-xs">
|
|
|
|
|
<tbody>
|
|
|
|
|
<tr>
|
|
|
|
|
<td className="border px-3 py-2 bg-gray-100 w-24">LOT NO.</td>
|
|
|
|
|
<td className="border px-3 py-2">{data.lotNumber}</td>
|
|
|
|
|
<td className="border px-3 py-2 bg-gray-100 w-24">수주일</td>
|
|
|
|
|
<td className="border px-3 py-2">{data.orderDate}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td className="border px-3 py-2 bg-gray-100">발주처</td>
|
|
|
|
|
<td className="border px-3 py-2">{data.client}</td>
|
|
|
|
|
<td className="border px-3 py-2 bg-gray-100">현장명</td>
|
|
|
|
|
<td className="border px-3 py-2">{data.siteName}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td className="border px-3 py-2 bg-gray-100">담당자</td>
|
|
|
|
|
<td className="border px-3 py-2">{data.manager}</td>
|
|
|
|
|
<td className="border px-3 py-2 bg-gray-100">연락처</td>
|
|
|
|
|
<td className="border px-3 py-2">{data.managerContact}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td className="border px-3 py-2 bg-gray-100">납기요청일</td>
|
|
|
|
|
<td className="border px-3 py-2">{data.deliveryRequestDate}</td>
|
|
|
|
|
<td className="border px-3 py-2 bg-gray-100">출고예정일</td>
|
|
|
|
|
<td className="border px-3 py-2">{data.expectedShipDate}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td className="border px-3 py-2 bg-gray-100">배송방법</td>
|
|
|
|
|
<td className="border px-3 py-2">{data.deliveryMethod}</td>
|
|
|
|
|
<td className="border px-3 py-2 bg-gray-100">배송지</td>
|
|
|
|
|
<td className="border px-3 py-2">{data.address}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
{/* 품목 테이블 */}
|
|
|
|
|
<table className="w-full border-collapse mb-6 text-xs">
|
|
|
|
|
<thead>
|
|
|
|
|
<tr className="bg-gray-100">
|
|
|
|
|
<th className="border px-2 py-2 w-10">No</th>
|
|
|
|
|
<th className="border px-2 py-2">품목명</th>
|
|
|
|
|
<th className="border px-2 py-2 w-24">규격</th>
|
|
|
|
|
<th className="border px-2 py-2 w-12">단위</th>
|
|
|
|
|
<th className="border px-2 py-2 w-12">수량</th>
|
|
|
|
|
<th className="border px-2 py-2 w-20">단가</th>
|
|
|
|
|
<th className="border px-2 py-2 w-24">금액</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
{data.items.map((item, index) => (
|
|
|
|
|
<tr key={item.id}>
|
|
|
|
|
<td className="border px-2 py-2 text-center">{index + 1}</td>
|
|
|
|
|
<td className="border px-2 py-2">{item.name}</td>
|
|
|
|
|
<td className="border px-2 py-2 text-center">{item.specification}</td>
|
|
|
|
|
<td className="border px-2 py-2 text-center">{item.unit}</td>
|
|
|
|
|
<td className="border px-2 py-2 text-center">{item.quantity}</td>
|
|
|
|
|
<td className="border px-2 py-2 text-right">{item.unitPrice?.toLocaleString()}</td>
|
|
|
|
|
<td className="border px-2 py-2 text-right">{item.amount?.toLocaleString()}</td>
|
|
|
|
|
</tr>
|
|
|
|
|
))}
|
|
|
|
|
</tbody>
|
|
|
|
|
<tfoot>
|
|
|
|
|
<tr>
|
|
|
|
|
<td colSpan={5} className="border px-2 py-2 text-right bg-gray-50">소계</td>
|
|
|
|
|
<td colSpan={2} className="border px-2 py-2 text-right">{data.subtotal.toLocaleString()}원</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td colSpan={5} className="border px-2 py-2 text-right bg-gray-50">할인 ({data.discountRate}%)</td>
|
|
|
|
|
<td colSpan={2} className="border px-2 py-2 text-right text-red-600">-{(data.subtotal * data.discountRate / 100).toLocaleString()}원</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr>
|
|
|
|
|
<td colSpan={5} className="border px-2 py-2 text-right bg-gray-100 font-bold">총액</td>
|
|
|
|
|
<td colSpan={2} className="border px-2 py-2 text-right font-bold text-blue-600">{data.totalAmount.toLocaleString()}원</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</tfoot>
|
|
|
|
|
</table>
|
|
|
|
|
|
|
|
|
|
{/* 비고 */}
|
|
|
|
|
{data.remarks && (
|
|
|
|
|
<div className="border p-4">
|
|
|
|
|
<h3 className="font-medium mb-2 text-xs">비고</h3>
|
|
|
|
|
<p className="text-xs text-gray-600">{data.remarks}</p>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
// QMS용 제품검사 성적서 Mock 데이터
|
|
|
|
|
const QMS_MOCK_REPORT_DATA: InspectionReportDocumentType = {
|
|
|
|
|
documentNumber: 'RPT-KD-SS-2024-530',
|
|
|
|
|
createdDate: '2024-09-24',
|
|
|
|
|
approvalLine: [
|
|
|
|
|
{ role: '작성', name: '김검사', department: '품질관리부' },
|
|
|
|
|
{ role: '승인', name: '박승인', department: '품질관리부' },
|
|
|
|
|
],
|
|
|
|
|
productName: '방화스크린',
|
|
|
|
|
productLotNo: 'KD-SS-240924-19',
|
|
|
|
|
productCode: 'WY-SC780',
|
|
|
|
|
lotSize: '8',
|
|
|
|
|
client: '삼성물산(주)',
|
|
|
|
|
inspectionDate: '2024-09-26',
|
|
|
|
|
siteName: '강남 아파트 단지',
|
|
|
|
|
inspector: '김검사',
|
|
|
|
|
inspectionItems: mockReportInspectionItems,
|
|
|
|
|
specialNotes: '',
|
|
|
|
|
finalJudgment: '합격',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// QMS용 작업일지 Mock WorkOrder 생성
|
|
|
|
|
@@ -289,6 +206,7 @@ export const InspectionModalV2 = ({
|
|
|
|
|
itemName,
|
|
|
|
|
specification,
|
|
|
|
|
supplier,
|
|
|
|
|
readOnly = false,
|
|
|
|
|
}: InspectionModalV2Props) => {
|
|
|
|
|
// 수입검사 템플릿 상태
|
|
|
|
|
const [importTemplate, setImportTemplate] = useState<ImportInspectionTemplate | null>(null);
|
|
|
|
|
@@ -339,6 +257,23 @@ export const InspectionModalV2 = ({
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 수입검사 저장 핸들러 (hooks는 early return 전에 호출해야 함)
|
|
|
|
|
const handleImportSave = useCallback(async () => {
|
|
|
|
|
if (!importDocRef.current) return;
|
|
|
|
|
|
|
|
|
|
const data = importDocRef.current.getInspectionData();
|
|
|
|
|
setIsSaving(true);
|
|
|
|
|
try {
|
|
|
|
|
// TODO: 실제 저장 API 연동
|
|
|
|
|
console.log('[InspectionModalV2] 수입검사 저장 데이터:', data);
|
|
|
|
|
toast.success('검사 데이터가 저장되었습니다.');
|
|
|
|
|
} catch {
|
|
|
|
|
toast.error('저장 중 오류가 발생했습니다.');
|
|
|
|
|
} finally {
|
|
|
|
|
setIsSaving(false);
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
if (!doc) return null;
|
|
|
|
|
|
|
|
|
|
const docInfo = DOCUMENT_INFO[doc.type] || { label: doc.title, hasTemplate: false, color: 'text-gray-600' };
|
|
|
|
|
@@ -391,23 +326,6 @@ export const InspectionModalV2 = ({
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 수입검사 저장 핸들러
|
|
|
|
|
const handleImportSave = useCallback(async () => {
|
|
|
|
|
if (!importDocRef.current) return;
|
|
|
|
|
|
|
|
|
|
const data = importDocRef.current.getInspectionData();
|
|
|
|
|
setIsSaving(true);
|
|
|
|
|
try {
|
|
|
|
|
// TODO: 실제 저장 API 연동
|
|
|
|
|
console.log('[InspectionModalV2] 수입검사 저장 데이터:', data);
|
|
|
|
|
toast.success('검사 데이터가 저장되었습니다.');
|
|
|
|
|
} catch {
|
|
|
|
|
toast.error('저장 중 오류가 발생했습니다.');
|
|
|
|
|
} finally {
|
|
|
|
|
setIsSaving(false);
|
|
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// 수입검사 문서 렌더링 (Lazy Loading)
|
|
|
|
|
const renderImportInspectionDocument = () => {
|
|
|
|
|
if (isLoadingTemplate) {
|
|
|
|
|
@@ -419,14 +337,35 @@ export const InspectionModalV2 = ({
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 템플릿이 로드되면 전달, 아니면 기본 템플릿 사용
|
|
|
|
|
return <ImportInspectionDocument ref={importDocRef} template={importTemplate || undefined} />;
|
|
|
|
|
return <ImportInspectionDocument ref={importDocRef} template={importTemplate || undefined} readOnly={readOnly} />;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 문서 타입에 따른 컨텐츠 렌더링
|
|
|
|
|
const renderDocumentContent = () => {
|
|
|
|
|
switch (doc.type) {
|
|
|
|
|
case 'order':
|
|
|
|
|
return <OrderDocument />;
|
|
|
|
|
return (
|
|
|
|
|
<SalesOrderDocument
|
|
|
|
|
orderNumber="KD-SS-240924-19"
|
|
|
|
|
documentNumber="KD-SS-240924-19"
|
|
|
|
|
certificationNumber="KD-SS-240924-19"
|
|
|
|
|
orderDate="2024-09-24"
|
|
|
|
|
client="삼성물산(주)"
|
|
|
|
|
siteName="강남 아파트 단지"
|
|
|
|
|
manager="김담당"
|
|
|
|
|
managerContact="010-1234-5678"
|
|
|
|
|
deliveryRequestDate="2024-10-05"
|
|
|
|
|
expectedShipDate="2024-10-04"
|
|
|
|
|
deliveryMethod="직접배차"
|
|
|
|
|
address="서울시 강남구 테헤란로 123"
|
|
|
|
|
recipientName="김인수"
|
|
|
|
|
recipientContact="010-9876-5432"
|
|
|
|
|
shutterCount={8}
|
|
|
|
|
products={QMS_MOCK_PRODUCTS}
|
|
|
|
|
items={QMS_MOCK_ORDER_ITEMS}
|
|
|
|
|
remarks="납기일 엄수 요청"
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
case 'log':
|
|
|
|
|
return renderWorkLogDocument();
|
|
|
|
|
case 'confirmation':
|
|
|
|
|
@@ -436,7 +375,7 @@ export const InspectionModalV2 = ({
|
|
|
|
|
case 'import':
|
|
|
|
|
return renderImportInspectionDocument();
|
|
|
|
|
case 'product':
|
|
|
|
|
return <ProductInspectionDocument />;
|
|
|
|
|
return <InspectionReportDocument data={QMS_MOCK_REPORT_DATA} />;
|
|
|
|
|
case 'report':
|
|
|
|
|
return renderReportDocument();
|
|
|
|
|
case 'quality':
|
|
|
|
|
@@ -444,6 +383,7 @@ export const InspectionModalV2 = ({
|
|
|
|
|
<QualityDocumentUploader
|
|
|
|
|
onFileUpload={handleQualityFileUpload}
|
|
|
|
|
onFileDelete={handleQualityFileDelete}
|
|
|
|
|
disabled={readOnly}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
default:
|
|
|
|
|
@@ -456,8 +396,8 @@ export const InspectionModalV2 = ({
|
|
|
|
|
console.log('[InspectionModalV2] 다운로드 요청:', doc.type);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 수입검사 저장 버튼 (toolbarExtra)
|
|
|
|
|
const importToolbarExtra = doc.type === 'import' ? (
|
|
|
|
|
// 수입검사 저장 버튼 (toolbarExtra) - readOnly일 때 숨김
|
|
|
|
|
const importToolbarExtra = doc.type === 'import' && !readOnly ? (
|
|
|
|
|
<Button onClick={handleImportSave} disabled={isSaving} size="sm">
|
|
|
|
|
{isSaving ? (
|
|
|
|
|
<Loader2 className="w-4 h-4 mr-1.5 animate-spin" />
|
|
|
|
|
|