import db from "@/db/db" import { contracts, contractItems } from "@/db/schema/contract" import { vendors } from "@/db/schema/vendors" import { items } from "@/db/schema/items" import { projects } from "@/db/schema/projects" import { faker } from "@faker-js/faker" import { eq } from "drizzle-orm" // 명령행 인자 파싱 const args = process.argv.slice(2); let vendorId: number | undefined, projectId: number | undefined, itemIds: number[] | undefined, numContracts = 1, shouldDeleteExisting = false; // 명령행 인자 파서 function parseArgs() { for (const arg of args) { if (arg.startsWith('--vendor=')) { vendorId = parseInt(arg.split('=')[1], 10); } else if (arg.startsWith('--project=')) { projectId = parseInt(arg.split('=')[1], 10); } else if (arg.startsWith('--items=')) { itemIds = arg.split('=')[1].split(',').map(id => parseInt(id.trim(), 10)); } else if (arg.startsWith('--count=')) { numContracts = parseInt(arg.split('=')[1], 10); } else if (arg === '--delete-existing') { shouldDeleteExisting = true; } else if (arg === '--help' || arg === '-h') { showHelp(); process.exit(0); } } if (!vendorId || !projectId || !itemIds || itemIds.length === 0) { console.log("필수 인자가 누락되었습니다: vendorId, projectId, 또는 itemIds"); showHelp(); process.exit(1); } } function showHelp() { console.log(` 사용법: npx tsx create-contract-cli.ts [옵션] 필수 옵션: --vendor= 협력업체 ID --project= 프로젝트 ID --items= 품목 ID (쉼표로 구분) 선택 옵션: --count= 생성할 계약 수량 (기본값: 1) --delete-existing 기존 계약 데이터 모두 삭제 --help, -h 도움말 표시 예시: npx tsx create-contract-cli.ts --vendor=1 --project=2 --items=3,4,5 --count=3 `); } async function main() { console.log("계약 시딩 시작..."); parseArgs(); console.log(`설정: vendorId=${vendorId}, projectId=${projectId}, itemIds=[${itemIds!.join(', ')}], 계약 수량=${numContracts}`); // DB에서 선택된 vendor, project, items 확인 const vendor = await db.select().from(vendors).where(eq(vendors.id, vendorId!)).limit(1); if (vendor.length === 0) { console.log(`ID가 ${vendorId}인 협력업체를 찾을 수 없습니다.`); process.exit(1); } const project = await db.select().from(projects).where(eq(projects.id, projectId!)).limit(1); if (project.length === 0) { console.log(`ID가 ${projectId}인 프로젝트를 찾을 수 없습니다.`); process.exit(1); } // 여러 아이템 ID 확인 const selectedItems = await db.select().from(items).where( itemIds!.map(id => eq(items.id, id)).reduce((a, b) => db.or(a, b)) ); if (selectedItems.length !== itemIds!.length) { const foundIds = selectedItems.map(item => item.id); const missingIds = itemIds!.filter(id => !foundIds.includes(id)); console.log(`일부 아이템 ID를 찾을 수 없습니다: ${missingIds.join(', ')}`); process.exit(1); } console.log(`협력업체: ${vendor[0].name}`); console.log(`프로젝트: ${project[0].name}`); console.log(`선택된 품목: ${selectedItems.map(i => i.name || i.itemCode).join(', ')}`); // 기존 데이터 삭제 (요청된 경우) if (shouldDeleteExisting) { await db.delete(contractItems); await db.delete(contracts); console.log("기존 계약 데이터가 모두 삭제되었습니다."); } // -- 조선업 맥락에 맞는 예시 문구들 -- const shipyardProjectNamesKR = [ "벌크선 선체 건조", "컨테이너선 엔진 오버홀", "해양 플랜트 모듈 제작", "LNG 운반선 화물창 시공", "FPSO 개조 작업", "밸러스트 수 처리장치 설치", "VLCC 프로펠러 교체", "드릴십 탑사이드 모듈 제작", ] const paymentTermsExamples = [ "선금 30%, 중도금 20%, 잔금 50% (인도 후 60일)", "LC 결제 (신용장 발행 후 30일)", "전액 선결제", "계약금 10%, 선박 명명식 시 40%, 인도 시 50%", ] const deliveryTermsExamples = [ "FOB 부산항", "CIF 상하이항", "DAP 울산조선소", "DDP 거제 옥포조선소", ] const deliveryLocations = [ "부산 영도조선소", "울산 본사 도크 #3", "거제 옥포조선소 해양공장", "목포신항 부두", ] // 계약 생성 루프 for (let i = 0; i < numContracts; i++) { // 예: C-1234 const contractNo = `C-${faker.number.int({ min: 1000, max: 9999 })}` // 예: "조선 프로젝트: 벌크선 선체 건조 - (프로젝트명)" const randomShipyardName = faker.helpers.arrayElement(shipyardProjectNamesKR) const contractName = `조선 프로젝트: ${randomShipyardName} - ${project[0].name}` // 상태 const randomStatus = faker.helpers.arrayElement([ "ACTIVE", "FINISHED", "CANCELED", "ON_HOLD", ]) // 날짜 const startDate = faker.helpers.maybe(() => faker.date.past({ years: 1 }), { probability: 0.7, }) const endDate = faker.helpers.maybe(() => faker.date.future({ years: 0.5 }), { probability: 0.5, }) // PO에 추가된 필드들에 대해 랜덤 할당 const paymentTerms = faker.helpers.arrayElement(paymentTermsExamples) const deliveryTerms = faker.helpers.arrayElement(deliveryTermsExamples) const deliveryDate = faker.date.future({ years: 1 }) const deliveryLocation = faker.helpers.arrayElement(deliveryLocations) // 가격 관련 (단순 랜덤) const totalAmount = faker.number.float({ min: 5000000, max: 500000000 }) const discount = faker.number.float({ min: 0, max: 500000}) const tax = faker.number.float({ min: 0, max: 1000000 }) const shippingFee = faker.number.float({ min: 0, max: 300000}) const netTotal = totalAmount - discount + tax + shippingFee // 부분 납품/결제 가능 여부 const partialShippingAllowed = faker.datatype.boolean() const partialPaymentAllowed = faker.datatype.boolean() // remarks, version const remarks = faker.helpers.maybe(() => faker.lorem.sentence(), { probability: 0.4, }) const version = 1 // DB에 계약 insert const [insertedContract] = await db .insert(contracts) .values({ projectId: projectId!, vendorId: vendorId!, contractNo, contractName, status: randomStatus, startDate: startDate ?? null, endDate: endDate ?? null, paymentTerms, deliveryTerms, deliveryDate, deliveryLocation, currency: faker.helpers.arrayElement(["KRW", "USD"]), totalAmount, discount, tax, shippingFee, netTotal, partialShippingAllowed, partialPaymentAllowed, remarks: remarks ?? null, version, }) .returning({ id: contracts.id }) const contractId = insertedContract.id console.log(`계약 생성 완료 #${contractId}: ${contractNo}`) // 선택한 아이템들에 대해 contract_items 생성 for (const itemId of itemIds!) { const item = selectedItems.find(i => i.id === itemId); const quantity = faker.number.int({ min: 1, max: 100 }) const unitPrice = faker.number.float({ min: 1000, max: 500000 }) // 세율, 세금, 합계 계산 const taxRate = faker.helpers.arrayElement([0, 0.05, 0.1]) const taxAmount = parseFloat((unitPrice * quantity * taxRate).toFixed(2)) const totalLineAmount = parseFloat((unitPrice * quantity + taxAmount).toFixed(2)) const description = item?.name || faker.commerce.productName() const remark = faker.helpers.maybe(() => faker.lorem.sentence(), { probability: 0.3 }) const [insertedContractItem] = await db .insert(contractItems) .values({ contractId, itemId, description, quantity, unitPrice, taxRate, taxAmount, totalLineAmount, remark: remark ?? null, }) .returning({ id: contractItems.id }) console.log( ` -> 계약 품목 생성 완료 #${insertedContractItem.id} (itemId=${itemId})` ) } } console.log("시딩 작업이 완료되었습니다.") process.exit(0) } main().catch((err) => { console.error(err) process.exit(1) })