// 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; } }