diff options
Diffstat (limited to 'lib/vendors/table/vendor-all-export.ts')
| -rw-r--r-- | lib/vendors/table/vendor-all-export.ts | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/lib/vendors/table/vendor-all-export.ts b/lib/vendors/table/vendor-all-export.ts new file mode 100644 index 00000000..cef801fd --- /dev/null +++ b/lib/vendors/table/vendor-all-export.ts @@ -0,0 +1,486 @@ +// /lib/vendor-export.ts +import ExcelJS from "exceljs" +import { VendorWithType } from "@/db/schema/vendors" +import { exportVendorDetails } from "../service"; + +// 연락처 인터페이스 정의 +interface VendorContact { + contactName: string; + contactPosition?: string | null; + contactEmail: string; + contactPhone?: string | null; + isPrimary: boolean; +} + +// 아이템 인터페이스 정의 +interface VendorItem { + itemCode: string; + itemName: string; + description?: string | null; + createdAt?: Date | string; +} + +// RFQ 인터페이스 정의 +interface VendorRFQ { + rfqNumber: string; + title: string; + status: string; + requestDate?: Date | string | null; + dueDate?: Date | string | null; + description?: string | null; +} + +// 계약 인터페이스 정의 +interface VendorContract { + projectCode: string; + projectName: string; + contractNo: string; + contractName: string; + status: string; + paymentTerms: string; + deliveryTerms: string; + deliveryDate: Date | string; + deliveryLocation: string; + startDate?: Date | string | null; + endDate?: Date | string | null; + currency: string; + totalAmount?: number | null; +} + +// 서비스에서 반환하는 실제 데이터 구조 +interface VendorData { + id: number; + vendorName: string; + vendorCode: string | null; + taxId: string; + address: string | null; + country: string | null; + phone: string | null; + email: string | null; + website: string | null; + status: string; + representativeName: string | null; + representativeBirth: string | null; + representativeEmail: string | null; + representativePhone: string | null; + corporateRegistrationNumber: string | null; + creditAgency: string | null; + creditRating: string | null; + cashFlowRating: string | null; +// items: string | null; + createdAt: Date; + updatedAt: Date; + vendorContacts: VendorContact[]; + vendorItems: VendorItem[]; + vendorRfqs: VendorRFQ[]; + vendorContracts: VendorContract[]; +} + +/** + * 선택된 벤더의 모든 관련 정보를 통합 시트 형식으로 엑셀로 내보내는 함수 + * - 기본정보 시트 + * - 연락처 시트 + * - 아이템 시트 + * - RFQ 시트 + * - 계약 시트 + * 각 시트에는 식별을 위한 벤더 코드, 벤더명, 세금ID가 포함됨 + */ +export async function exportVendorsWithRelatedData( + vendors: VendorWithType[], + filename = "vendors-detailed" +): Promise<void> { + if (!vendors.length) return; + + // 선택된 벤더 ID 목록 + const vendorIds = vendors.map(vendor => vendor.id); + + try { + // 서버로부터 모든 관련 데이터 가져오기 + const vendorsWithDetails = await exportVendorDetails(vendorIds); + + if (!vendorsWithDetails.length) { + throw new Error("내보내기 데이터를 가져오는 중 오류가 발생했습니다."); + } + + // 워크북 생성 + const workbook = new ExcelJS.Workbook(); + + // 데이터 타입 확인 (서비스에서 반환하는 실제 데이터 형태) + const vendorData = vendorsWithDetails as unknown as any[]; + + // ===== 1. 기본 정보 시트 ===== + createBasicInfoSheet(workbook, vendorData); + + // ===== 2. 연락처 시트 ===== + createContactsSheet(workbook, vendorData); + + // ===== 3. 아이템 시트 ===== + createItemsSheet(workbook, vendorData); + + // ===== 4. RFQ 시트 ===== + createRFQsSheet(workbook, vendorData); + + // ===== 5. 계약 시트 ===== + createContractsSheet(workbook, vendorData); + + // 파일 다운로드 + const buffer = await workbook.xlsx.writeBuffer(); + const blob = new Blob([buffer], { + type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = `${filename}-${new Date().toISOString().split("T")[0]}.xlsx`; + link.click(); + URL.revokeObjectURL(url); + + return; + } catch (error) { + console.error("Export error:", error); + throw error; + } +} + +// 기본 정보 시트 생성 함수 +function createBasicInfoSheet( + workbook: ExcelJS.Workbook, + vendors: VendorData[] +): void { + const basicInfoSheet = workbook.addWorksheet("기본정보"); + + // 기본 정보 시트 헤더 설정 + basicInfoSheet.columns = [ + { header: "업체코드", key: "vendorCode", width: 15 }, + { header: "업체명", key: "vendorName", width: 20 }, + { header: "세금ID", key: "taxId", width: 15 }, + { header: "국가", key: "country", width: 10 }, + { header: "상태", key: "status", width: 15 }, + { header: "이메일", key: "email", width: 20 }, + { header: "전화번호", key: "phone", width: 15 }, + { header: "웹사이트", key: "website", width: 20 }, + { header: "주소", key: "address", width: 30 }, + { header: "대표자명", key: "representativeName", width: 15 }, + { header: "신용등급", key: "creditRating", width: 10 }, + { header: "현금흐름등급", key: "cashFlowRating", width: 10 }, + { header: "생성일", key: "createdAt", width: 15 }, + ]; + + // 헤더 스타일 설정 + applyHeaderStyle(basicInfoSheet); + + // 벤더 데이터 추가 + vendors.forEach((vendor: VendorData) => { + basicInfoSheet.addRow({ + vendorCode: vendor.vendorCode || "", + vendorName: vendor.vendorName, + taxId: vendor.taxId, + country: vendor.country, + status: getStatusText(vendor.status), // 상태 코드를 읽기 쉬운 텍스트로 변환 + email: vendor.email, + phone: vendor.phone, + website: vendor.website, + address: vendor.address, + representativeName: vendor.representativeName, + creditRating: vendor.creditRating, + cashFlowRating: vendor.cashFlowRating, + createdAt: vendor.createdAt ? formatDate(vendor.createdAt) : "", + }); + }); +} + +// 연락처 시트 생성 함수 +function createContactsSheet( + workbook: ExcelJS.Workbook, + vendors: VendorData[] +): void { + const contactsSheet = workbook.addWorksheet("연락처"); + + contactsSheet.columns = [ + // 벤더 식별 정보 + { header: "업체코드", key: "vendorCode", width: 15 }, + { header: "업체명", key: "vendorName", width: 20 }, + { header: "세금ID", key: "taxId", width: 15 }, + // 연락처 정보 + { header: "이름", key: "contactName", width: 15 }, + { header: "직책", key: "contactPosition", width: 15 }, + { header: "이메일", key: "contactEmail", width: 25 }, + { header: "전화번호", key: "contactPhone", width: 15 }, + { header: "주요 연락처", key: "isPrimary", width: 10 }, + ]; + + // 헤더 스타일 설정 + applyHeaderStyle(contactsSheet); + + // 벤더별 연락처 데이터 추가 + vendors.forEach((vendor: VendorData) => { + if (vendor.vendorContacts && vendor.vendorContacts.length > 0) { + vendor.vendorContacts.forEach((contact: VendorContact) => { + contactsSheet.addRow({ + // 벤더 식별 정보 + vendorCode: vendor.vendorCode || "", + vendorName: vendor.vendorName, + taxId: vendor.taxId, + // 연락처 정보 + contactName: contact.contactName, + contactPosition: contact.contactPosition || "", + contactEmail: contact.contactEmail, + contactPhone: contact.contactPhone || "", + isPrimary: contact.isPrimary ? "예" : "아니오", + }); + }); + } else { + // 연락처가 없는 경우에도 벤더 정보만 추가 + contactsSheet.addRow({ + vendorCode: vendor.vendorCode || "", + vendorName: vendor.vendorName, + taxId: vendor.taxId, + contactName: "", + contactPosition: "", + contactEmail: "", + contactPhone: "", + isPrimary: "", + }); + } + }); +} + +// 아이템 시트 생성 함수 +function createItemsSheet( + workbook: ExcelJS.Workbook, + vendors: VendorData[] +): void { + const itemsSheet = workbook.addWorksheet("아이템"); + + itemsSheet.columns = [ + // 벤더 식별 정보 + { header: "업체코드", key: "vendorCode", width: 15 }, + { header: "업체명", key: "vendorName", width: 20 }, + { header: "세금ID", key: "taxId", width: 15 }, + // 아이템 정보 + { header: "아이템 코드", key: "itemCode", width: 15 }, + { header: "아이템명", key: "itemName", width: 25 }, + { header: "설명", key: "description", width: 30 }, + { header: "등록일", key: "createdAt", width: 15 }, + ]; + + // 헤더 스타일 설정 + applyHeaderStyle(itemsSheet); + + // 벤더별 아이템 데이터 추가 + vendors.forEach((vendor: VendorData) => { + if (vendor.vendorItems && vendor.vendorItems.length > 0) { + vendor.vendorItems.forEach((item: VendorItem) => { + itemsSheet.addRow({ + // 벤더 식별 정보 + vendorCode: vendor.vendorCode || "", + vendorName: vendor.vendorName, + taxId: vendor.taxId, + // 아이템 정보 + itemCode: item.itemCode, + itemName: item.itemName, + description: item.description || "", + createdAt: item.createdAt ? formatDate(item.createdAt) : "", + }); + }); + } else { + // 아이템이 없는 경우에도 벤더 정보만 추가 + itemsSheet.addRow({ + vendorCode: vendor.vendorCode || "", + vendorName: vendor.vendorName, + taxId: vendor.taxId, + itemCode: "", + itemName: "", + description: "", + createdAt: "", + }); + } + }); +} + +// RFQ 시트 생성 함수 +function createRFQsSheet( + workbook: ExcelJS.Workbook, + vendors: VendorData[] +): void { + const rfqsSheet = workbook.addWorksheet("RFQ"); + + rfqsSheet.columns = [ + // 벤더 식별 정보 + { header: "업체코드", key: "vendorCode", width: 15 }, + { header: "업체명", key: "vendorName", width: 20 }, + { header: "세금ID", key: "taxId", width: 15 }, + // RFQ 정보 + { header: "RFQ 번호", key: "rfqNumber", width: 15 }, + { header: "제목", key: "title", width: 25 }, + { header: "상태", key: "status", width: 15 }, + { header: "요청일", key: "requestDate", width: 15 }, + { header: "마감일", key: "dueDate", width: 15 }, + { header: "설명", key: "description", width: 30 }, + ]; + + // 헤더 스타일 설정 + applyHeaderStyle(rfqsSheet); + + // 벤더별 RFQ 데이터 추가 + vendors.forEach((vendor: VendorData) => { + if (vendor.vendorRfqs && vendor.vendorRfqs.length > 0) { + vendor.vendorRfqs.forEach((rfq: VendorRFQ) => { + rfqsSheet.addRow({ + // 벤더 식별 정보 + vendorCode: vendor.vendorCode || "", + vendorName: vendor.vendorName, + taxId: vendor.taxId, + // RFQ 정보 + rfqNumber: rfq.rfqNumber, + title: rfq.title, + status: rfq.status, + requestDate: rfq.requestDate ? formatDate(rfq.requestDate) : "", + dueDate: rfq.dueDate ? formatDate(rfq.dueDate) : "", + description: rfq.description || "", + }); + }); + } else { + // RFQ가 없는 경우에도 벤더 정보만 추가 + rfqsSheet.addRow({ + vendorCode: vendor.vendorCode || "", + vendorName: vendor.vendorName, + taxId: vendor.taxId, + rfqNumber: "", + title: "", + status: "", + requestDate: "", + dueDate: "", + description: "", + }); + } + }); +} + +// 계약 시트 생성 함수 +function createContractsSheet( + workbook: ExcelJS.Workbook, + vendors: VendorData[] +): void { + const contractsSheet = workbook.addWorksheet("계약"); + + contractsSheet.columns = [ + // 벤더 식별 정보 + { header: "업체코드", key: "vendorCode", width: 15 }, + { header: "업체명", key: "vendorName", width: 20 }, + { header: "세금ID", key: "taxId", width: 15 }, + // 계약 정보 + { header: "프로젝트 코드", key: "projectCode", width: 15 }, + { header: "프로젝트명", key: "projectName", width: 20 }, + { header: "계약 번호", key: "contractNo", width: 15 }, + { header: "계약명", key: "contractName", width: 25 }, + { header: "상태", key: "status", width: 15 }, + { header: "지급 조건", key: "paymentTerms", width: 15 }, + { header: "납품 조건", key: "deliveryTerms", width: 15 }, + { header: "납품 일자", key: "deliveryDate", width: 15 }, + { header: "납품 위치", key: "deliveryLocation", width: 20 }, + { header: "계약 시작일", key: "startDate", width: 15 }, + { header: "계약 종료일", key: "endDate", width: 15 }, + { header: "통화", key: "currency", width: 10 }, + { header: "총액", key: "totalAmount", width: 15 }, + ]; + + // 헤더 스타일 설정 + applyHeaderStyle(contractsSheet); + + // 벤더별 계약 데이터 추가 + vendors.forEach((vendor: VendorData) => { + if (vendor.vendorContracts && vendor.vendorContracts.length > 0) { + vendor.vendorContracts.forEach((contract: VendorContract) => { + contractsSheet.addRow({ + // 벤더 식별 정보 + vendorCode: vendor.vendorCode || "", + vendorName: vendor.vendorName, + taxId: vendor.taxId, + // 계약 정보 + projectCode: contract.projectCode, + projectName: contract.projectName, + contractNo: contract.contractNo, + contractName: contract.contractName, + status: contract.status, + paymentTerms: contract.paymentTerms, + deliveryTerms: contract.deliveryTerms, + deliveryDate: contract.deliveryDate ? formatDate(contract.deliveryDate) : "", + deliveryLocation: contract.deliveryLocation, + startDate: contract.startDate ? formatDate(contract.startDate) : "", + endDate: contract.endDate ? formatDate(contract.endDate) : "", + currency: contract.currency, + totalAmount: contract.totalAmount ? formatAmount(contract.totalAmount) : "", + }); + }); + } else { + // 계약이 없는 경우에도 벤더 정보만 추가 + contractsSheet.addRow({ + vendorCode: vendor.vendorCode || "", + vendorName: vendor.vendorName, + taxId: vendor.taxId, + projectCode: "", + projectName: "", + contractNo: "", + contractName: "", + status: "", + paymentTerms: "", + deliveryTerms: "", + deliveryDate: "", + deliveryLocation: "", + startDate: "", + endDate: "", + currency: "", + totalAmount: "", + }); + } + }); +} + +// 헤더 스타일 적용 함수 +function applyHeaderStyle(sheet: ExcelJS.Worksheet): void { + const headerRow = sheet.getRow(1); + headerRow.font = { bold: true }; + headerRow.alignment = { horizontal: "center" }; + headerRow.eachCell((cell: ExcelJS.Cell) => { + cell.fill = { + type: "pattern", + pattern: "solid", + fgColor: { argb: "FFCCCCCC" }, + }; + }); +} + +// 날짜 포맷 함수 +function formatDate(date: Date | string): string { + if (!date) return ""; + if (typeof date === 'string') { + date = new Date(date); + } + return date.toISOString().split('T')[0]; +} + +// 금액 포맷 함수 +function formatAmount(amount: number): string { + return amount.toLocaleString(); +} + +// 상태 코드를 읽기 쉬운 텍스트로 변환하는 함수 +function getStatusText(status: string): string { + const statusMap: Record<string, string> = { + "PENDING_REVIEW": "검토 대기중", + "IN_REVIEW": "검토 중", + "REJECTED": "거부됨", + "IN_PQ": "PQ 진행 중", + "PQ_SUBMITTED": "PQ 제출됨", + "PQ_FAILED": "PQ 실패", + "PQ_APPROVED": "PQ 승인됨", + "APPROVED": "승인됨", + "READY_TO_SEND": "발송 준비됨", + "ACTIVE": "활성", + "INACTIVE": "비활성", + "BLACKLISTED": "거래 금지" + }; + + return statusMap[status] || status; +}
\ No newline at end of file |
