diff options
| author | joonhoekim <26rote@gmail.com> | 2025-03-25 15:55:45 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-03-25 15:55:45 +0900 |
| commit | 1a2241c40e10193c5ff7008a7b7b36cc1d855d96 (patch) | |
| tree | 8a5587f10ca55b162d7e3254cb088b323a34c41b /db/seeds/contract.ts | |
initial commit
Diffstat (limited to 'db/seeds/contract.ts')
| -rw-r--r-- | db/seeds/contract.ts | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/db/seeds/contract.ts b/db/seeds/contract.ts new file mode 100644 index 00000000..1c266c72 --- /dev/null +++ b/db/seeds/contract.ts @@ -0,0 +1,197 @@ +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" + +async function main() { + console.log("Seeding contracts / contract_items...") + + // (옵션) 기존 데이터 삭제 + await db.delete(contractItems) + await db.delete(contracts) + + // 1) 기존 vendor, item, project 목록 조회 + const allVendors = await db.select().from(vendors) + const allItems = await db.select().from(items) + const allProjects = await db.select().from(projects) + + if (allVendors.length === 0 || allItems.length === 0 || allProjects.length === 0) { + console.log("Vendors / Items / Projects 테이블 중 하나가 비어있습니다. 먼저 해당 테이블을 seed 해주세요.") + process.exit(1) + } + + // -- 조선업 맥락에 맞는 예시 문구들 -- + 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", + "거제 옥포조선소 해양공장", + "목포신항 부두", + ] + + // (여기서는 30개) 계약 생성 + const NUM_CONTRACTS = 30 + + for (let i = 0; i < NUM_CONTRACTS; i++) { + // 무작위 벤더, 프로젝트 선택 + const randomVendor = faker.helpers.arrayElement(allVendors) + const randomProject = faker.helpers.arrayElement(allProjects) + + // 예: C-1234 + const contractNo = `C-${faker.number.int({ min: 1000, max: 9999 })}` + + // 예: "조선 프로젝트: 벌크선 선체 건조 - (프로젝트명)" + const randomShipyardName = faker.helpers.arrayElement(shipyardProjectNamesKR) + const contractName = `조선 프로젝트: ${randomShipyardName} - ${randomProject.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 // 혹은 faker.number.int({ min: 1, max: 5 }) + + // DB에 insert + const [insertedContract] = await db + .insert(contracts) + .values({ + projectId: randomProject.id, + vendorId: randomVendor.id, + 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(`Inserted Contract #${contractId}: ${contractNo}`) + + // 3) 각 계약에 대해 2~5개 contract_items + const numItems = faker.number.int({ min: 2, max: 5 }) + const usedItemIds = new Set<number>() + + for (let j = 0; j < numItems; j++) { + // 중복되지 않게 아이템 추출 + let randomItem + do { + randomItem = faker.helpers.arrayElement(allItems) + } while (usedItemIds.has(randomItem.id)) + usedItemIds.add(randomItem.id) + + const quantity = faker.number.int({ min: 1, max: 100 }) + // unitPrice: 1천원 ~ 50만원 범위 (소수점 2자리) + 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 = faker.commerce.productName() // 간단히 예시 + const remark = faker.helpers.maybe(() => faker.lorem.sentence(), { probability: 0.3 }) + + const [insertedContractItem] = await db + .insert(contractItems) + .values({ + contractId, + itemId: randomItem.id, + description, + quantity, + unitPrice, + taxRate, + taxAmount, + totalLineAmount, + remark: remark ?? null, + }) + .returning({ id: contractItems.id }) + + console.log( + ` -> Inserted ContractItem #${insertedContractItem.id} (itemId=${randomItem.id})` + ) + } + } + + console.log("Seeding completed.") + process.exit(0) +} + +main().catch((err) => { + console.error(err) + process.exit(1) +})
\ No newline at end of file |
