summaryrefslogtreecommitdiff
path: root/db/seeds/contract.ts
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-03-25 15:55:45 +0900
committerjoonhoekim <26rote@gmail.com>2025-03-25 15:55:45 +0900
commit1a2241c40e10193c5ff7008a7b7b36cc1d855d96 (patch)
tree8a5587f10ca55b162d7e3254cb088b323a34c41b /db/seeds/contract.ts
initial commit
Diffstat (limited to 'db/seeds/contract.ts')
-rw-r--r--db/seeds/contract.ts197
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