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_2/rfqSeed.ts | |
initial commit
Diffstat (limited to 'db/seeds_2/rfqSeed.ts')
| -rw-r--r-- | db/seeds_2/rfqSeed.ts | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/db/seeds_2/rfqSeed.ts b/db/seeds_2/rfqSeed.ts new file mode 100644 index 00000000..f2cde1b2 --- /dev/null +++ b/db/seeds_2/rfqSeed.ts @@ -0,0 +1,148 @@ +// db/seed/rfqSeed.ts +import { faker } from "@faker-js/faker"; +import db from "@/db/db" +import { eq, inArray, desc } from "drizzle-orm"; + +// 스키마 import +import { + rfqs, rfqItems, rfqVendors, vendorQuotes, rfqAttachments, rfqComments, rfqEvaluations, + Rfq, RfqItem, RfqVendor, VendorQuote, RfqAttach, RfqComment, RfqEvaluation +} from "../schema/rfq"; +import { vendors } from "../schema/vendors"; +import { users } from "../schema/users"; +import { items } from "../schema/items"; + +async function generateRfq(index: number, allUsers: any[], allVendors: any[], allItems: any[]) { + const year = faker.number.int({ min: 2022, max: 2024 }); + const sequence = String(index + 1).padStart(3, "0"); + const rfqCode = `RFQ-${year}-${sequence}`; + + // 상태별 비율 설정 + const statusDistribution = { + DRAFT: 0.3, // 30% + PUBLISHED: 0.3, // 30% + EVALUATION: 0.2, // 20% + AWARDED: 0.2 // 20% + }; + + // 랜덤 상태 결정 + const rand = Math.random(); + let status: "DRAFT" | "PUBLISHED" | "EVALUATION" | "AWARDED"; + if (rand < statusDistribution.DRAFT) status = "DRAFT"; + else if (rand < statusDistribution.DRAFT + statusDistribution.PUBLISHED) status = "PUBLISHED"; + else if (rand < statusDistribution.DRAFT + statusDistribution.PUBLISHED + statusDistribution.EVALUATION) status = "EVALUATION"; + else status = "AWARDED"; + + // RFQ 생성 + const [rfq] = await db.insert(rfqs).values({ + rfqCode, + description: faker.commerce.productDescription(), + projectCode: `PRJ-${faker.string.alphanumeric(6).toUpperCase()}`, + projectName: faker.company.catchPhrase(), + dueDate: faker.date.future(), + status, + createdBy: faker.helpers.arrayElement(allUsers).id, + }).returning(); + + // 3-10개의 items 추가 + const itemCount = faker.number.int({ min: 3, max: 10 }); + const selectedItems = faker.helpers.arrayElements(allItems, itemCount); + for (const item of selectedItems) { + await db.insert(rfqItems).values({ + rfqId: rfq.id, + itemCode: item.itemCode, + description: faker.commerce.productDescription(), + quantity: faker.number.float({ min: 1, max: 1000, fractionDigits: 2 }), + uom: faker.helpers.arrayElement(["EA", "KG", "M", "L", "SET"]), + }); + } + + // 2-5개의 vendors 초대 + const vendorCount = faker.number.int({ min: 2, max: 5 }); + const selectedVendors = faker.helpers.arrayElements(allVendors, vendorCount); + for (const vendor of selectedVendors) { + await db.insert(rfqVendors).values({ + rfqId: rfq.id, + vendorId: vendor.id, + status: faker.helpers.arrayElement(["INVITED", "ACCEPTED", "REJECTED", "QUOTED"]), + }); + + // 50% 확률로 quote 생성 + if (Math.random() > 0.5) { + await db.insert(vendorQuotes).values({ + rfqId: rfq.id, + vendorId: vendor.id, + totalAmount: faker.number.float({ min: 1000, max: 1000000, fractionDigits: 2 }), + currency: faker.helpers.arrayElement(["USD", "EUR", "KRW"]), + leadTime: `${faker.number.int({ min: 1, max: 52 })} weeks`, + notes: faker.lorem.paragraph(), + }); + } + + // 30% 확률로 attachment 추가 + if (Math.random() > 0.7) { + await db.insert(rfqAttachments).values({ + rfqId: rfq.id, + vendorId: vendor.id, + fileName: `${faker.system.fileName()}.pdf`, + filePath: `/uploads/rfq/${rfq.id}/${faker.system.fileName()}.pdf`, + }); + } + + // 40% 확률로 comment 추가 + if (Math.random() > 0.6) { + await db.insert(rfqComments).values({ + rfqId: rfq.id, + vendorId: vendor.id, + commentText: faker.lorem.paragraph(), + commentedBy: faker.helpers.arrayElement(allUsers).id, + }); + } + + // EVALUATION 또는 AWARDED 상태인 경우 평가 데이터 추가 + if (status === "EVALUATION" || status === "AWARDED") { + await db.insert(rfqEvaluations).values({ + rfqId: rfq.id, + vendorId: vendor.id, + evalType: faker.helpers.arrayElement(["TBE", "CBE"]), + result: faker.helpers.arrayElement(["PASS", "FAIL", "ACCEPTABLE"]), + notes: faker.lorem.paragraph(), + }); + } + } + + return rfq; +} + +export async function seedRfqData(input: { count: number }) { + try { + const allUsers = await db.select().from(users); + const allVendors = await db.select().from(vendors); + const allItems = await db.select().from(items); + + // 현재 가장 큰 RFQ 코드 찾기 + const [lastRfq] = await db.select() + .from(rfqs) + .orderBy(desc(rfqs.rfqCode)) + .limit(1); + + // RFQ-YYYY-ddd 형식에서 마지막 번호 추출 + const startIndex = lastRfq?.rfqCode + ? parseInt(lastRfq.rfqCode.split('-')[2]) + : 0; + + console.log("📝 Generating RFQ data...", { lastRfqCode: lastRfq?.rfqCode, startIndex }); + + const generatedRfqs = []; + for (let i = 0; i < input.count; i++) { + const rfq = await generateRfq(startIndex + i, allUsers, allVendors, allItems); + generatedRfqs.push(rfq); + } + + console.log(`✅ Successfully generated ${generatedRfqs.length} new RFQs with related data`); + return generatedRfqs; + } catch (err) { + console.error("Failed to seed RFQ data:", err); + throw err; + } +}
\ No newline at end of file |
