1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
// db/seed/rfqSeed.ts
import { faker } from "@faker-js/faker";
import db from "@/db/db"
import { eq, inArray } 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";
export async function seedRfqData() {
// 1) 기존 데이터 삭제 (참조 순서 유의)
await db.delete(rfqEvaluations);
await db.delete(rfqComments);
await db.delete(rfqAttachments);
await db.delete(vendorQuotes);
await db.delete(rfqVendors);
await db.delete(rfqItems);
await db.delete(rfqs);
// (협력업체 목록이 이미 존재한다고 가정)
// 혹은 필요한 경우 vendors도 시드할 수 있음
const existingVendors = await db.select().from(vendors);
if (existingVendors.length === 0) {
console.log("No vendors found. Please seed vendors first.");
return;
}
// 2) N개의 RFQ 생성
const rfqCount = 5;
const rfqIds: number[] = [];
for (let i = 0; i < rfqCount; i++) {
const newRfqData = {
rfqCode: `RFQ-${faker.string.alpha({ length: 4 }).toUpperCase()}-${faker.number.int({
min: 100,
max: 999,
})}`,
projectCode: `PRJ-${faker.number.int({ min: 1000, max: 9999 })}`,
projectName: faker.company.name(),
dueDate: faker.date.future(), // 임의 미래 날짜
status: faker.helpers.arrayElement(["DRAFT", "PUBLISHED", "EVALUATION", "AWARDED"]),
createdBy: faker.number.int({ min: 1, max: 10 }),
};
const [insertedRfq] = await db.insert(rfqs).values(newRfqData).returning();
rfqIds.push(insertedRfq.id);
}
// 3) For each RFQ, create items, vendors, quotes, attachments, comments, evaluations
for (const rfqId of rfqIds) {
// 3-1) RFQ Items
const itemCount = faker.number.int({ min: 1, max: 3 });
const rfqItemIds: number[] = [];
for (let j = 0; j < itemCount; j++) {
const newItem = {
rfqId,
itemCode: `ITEM-${faker.string.alphanumeric({ length: 5 }).toUpperCase()}`,
itemName: faker.commerce.productName(),
description: faker.commerce.productDescription(),
quantity: faker.number.float({ min: 1, max: 20, fractionDigits: 2 }),
uom: faker.helpers.arrayElement(["EA", "KG", "BOX"]),
};
const [insertedItem] = await db.insert(rfqItems).values(newItem).returning();
rfqItemIds.push(insertedItem.id);
}
// 3-2) RFQ Vendors
// - 랜덤으로 2~3개 협력업체를 pick
const pickedVendors = faker.helpers.arrayElements(existingVendors, faker.number.int({ min: 2, max: 3 }));
for (const ven of pickedVendors) {
const newRfqVendor = {
rfqId,
vendorId: ven.id,
status: faker.helpers.arrayElement(["INVITED", "ACCEPTED", "REJECTED", "QUOTED"]),
};
const [insertedRfqVendor] = await db.insert(rfqVendors).values(newRfqVendor).returning();
// 3-3) 임의로 협력업체 Quotes 생성 (50% 확률)
if (faker.datatype.boolean()) {
const newQuote = {
rfqId,
vendorId: ven.id,
totalAmount: faker.number.float({ min: 1000, max: 50000, fractionDigits: 2 }),
currency: faker.helpers.arrayElement(["USD", "EUR", "KRW"]),
leadTime: `${faker.number.int({ min: 10, max: 40 })} days`,
notes: faker.lorem.sentence(),
};
await db.insert(vendorQuotes).values(newQuote);
}
}
// 3-4) Attachments (랜덤 0~2개)
const attachCount = faker.number.int({ min: 0, max: 2 });
for (let a = 0; a < attachCount; a++) {
const newAttach = {
rfqId,
vendorId: null, // or pick some vendorId
fileName: faker.system.fileName(),
filePath: faker.system.filePath(),
// evaluationId: null,
};
await db.insert(rfqAttachments).values(newAttach);
}
// 3-5) Comments (랜덤 1~2개)
const commentCount = faker.number.int({ min: 1, max: 2 });
for (let c = 0; c < commentCount; c++) {
const newComment = {
rfqId,
vendorId: null,
commentText: faker.lorem.sentence(),
commentedBy: faker.number.int({ min: 1, max: 10 }),
// evaluationId: null,
};
await db.insert(rfqComments).values(newComment);
}
// 3-6) Evaluations (랜덤 0~1개)
if (faker.datatype.boolean()) {
const newEval = {
rfqId,
vendorId: faker.helpers.arrayElement(pickedVendors).id,
evalType: faker.helpers.arrayElement(["TBE", "CBE"]),
result: faker.helpers.arrayElement(["PASS", "FAIL", "ACCEPTABLE"]),
notes: faker.lorem.sentences(2),
};
await db.insert(rfqEvaluations).values(newEval);
}
}
console.log(`✅ Seeded ${rfqIds.length} RFQs (with items, vendors, quotes, attachments, comments, evaluations).`);
}
if (require.main === module) {
seedRfqData()
.then(() => {
console.log("RFQ seeding complete!");
process.exit(0);
})
.catch((err) => {
console.error("RFQ seeding failed:", err);
process.exit(1);
});
}
|