summaryrefslogtreecommitdiff
path: root/db/seeds/po-rfq.ts
blob: 62bb39bd8fc33bd7997f4609a38934281addfd0e (plain)
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
import { faker } from "@faker-js/faker";
import db from "@/db/db"

import { 
  procurementRfqs, 
  paymentTerms, 
  incoterms, 
  procurementRfqDetails, 
  prItems 
} from "@/db/schema";
import { eq } from "drizzle-orm";

// 랜덤 데이터 생성 시 일관성을 위해 시드 설정
faker.seed(123);

// 생성할 데이터 수량 설정
const NUM_PAYMENT_TERMS = 5;
const NUM_INCOTERMS = 7;
const NUM_RFQS = 50;
const NUM_RFQ_DETAILS_PER_RFQ = 1; // 각 RFQ마다 생성할 detail 수
const NUM_PR_ITEMS_PER_RFQ = 3; // 각 RFQ마다 생성할 PR item 수

/**
 * 기존 데이터를 가져오는 함수들
 */
async function getRandomUser() {
  const users = await db.query.users.findMany({ limit: 50 });
  return faker.helpers.arrayElement(users);
}

async function getRandomProject() {
  const projects = await db.query.projects.findMany({ limit: 50 });
  return faker.helpers.arrayElement(projects);
}

async function getRandomItem() {
  const items = await db.query.items.findMany({ limit: 50 });
  return faker.helpers.arrayElement(items);
}

async function getRandomVendor() {
  const vendors = await db.query.vendors.findMany({ limit: 50 });
  return faker.helpers.arrayElement(vendors);
}

/**
 * 시드 데이터 생성 함수
 */

// 지불 조건 시드 데이터 생성
async function seedPaymentTerms() {
  console.log("🌱 시드 데이터 생성 중: 지불 조건");
  
  // 기존 데이터 확인
  const existingTerms = await db.query.paymentTerms.findMany();
  if (existingTerms.length > 0) {
    console.log("  ✓ 지불 조건 데이터가 이미 존재합니다.");
    return existingTerms;
  }
  
  const terms = [
    { code: "NET30", description: "Net 30 days" },
    { code: "NET60", description: "Net 60 days" },
    { code: "COD", description: "Cash on Delivery" },
    { code: "ADV", description: "Advance Payment" },
    { code: "L/C", description: "Letter of Credit" }
  ];
  
  const randomUser = await getRandomUser();
  
  const paymentTermsData = terms.map(term => ({
    code: term.code,
    description: term.description,
    isActive: true,
    createdBy: randomUser.id,
    createdAt: new Date()
  }));
  
  await db.insert(paymentTerms).values(paymentTermsData);
  console.log(`  ✓ ${terms.length}개의 지불 조건이 생성되었습니다.`);
  
  return await db.query.paymentTerms.findMany();
}

// 인코텀즈 시드 데이터 생성
async function seedIncoterms() {
  console.log("🌱 시드 데이터 생성 중: 인코텀즈");
  
  // 기존 데이터 확인
  const existingIncoterms = await db.query.incoterms.findMany();
  if (existingIncoterms.length > 0) {
    console.log("  ✓ 인코텀즈 데이터가 이미 존재합니다.");
    return existingIncoterms;
  }
  
  const terms = [
    { code: "EXW", description: "Ex Works" },
    { code: "FCA", description: "Free Carrier" },
    { code: "FAS", description: "Free Alongside Ship" },
    { code: "FOB", description: "Free On Board" },
    { code: "CFR", description: "Cost and Freight" },
    { code: "CIF", description: "Cost, Insurance and Freight" },
    { code: "DDP", description: "Delivered Duty Paid" }
  ];
  
  const randomUser = await getRandomUser();
  
  const incotermsData = terms.map(term => ({
    code: term.code,
    description: term.description,
    isActive: true,
    createdBy: randomUser.id,
    createdAt: new Date()
  }));
  
  await db.insert(incoterms).values(incotermsData);
  console.log(`  ✓ ${terms.length}개의 인코텀즈가 생성되었습니다.`);
  
  return await db.query.incoterms.findMany();
}

// RFQ 시드 데이터 생성
async function seedRfqs() {
  console.log("🌱 시드 데이터 생성 중: RFQ");
  
  // 기존 데이터 확인
  const existingRfqs = await db.query.procurementRfqs.findMany();
  if (existingRfqs.length > 0) {
    console.log(`  ✓ RFQ 데이터가 이미 ${existingRfqs.length}개 존재합니다.`);
    return existingRfqs;
  }
  
  const statusOptions = [
    "RFQ Created", 
    "RFQ Vendor Assignned", 
    "RFQ Sent", 
    "Quotation Analysis", 
    "PO Transfer", 
    "PO Create"
  ];
  
  const rfqsData = [];
  
  for (let i = 1; i <= NUM_RFQS; i++) {
    const randomUser = await getRandomUser();
    const randomProject = await getRandomProject();
    const randomItem = await getRandomItem();
    
    // RFQ 전송일은 과거 1~90일 사이
    const rfqSendDate = faker.date.past({ days: 90 });
    
    // 마감일은 전송일로부터 7~30일 이후
    const dueDate = new Date(rfqSendDate);
    dueDate.setDate(dueDate.getDate() + faker.number.int({ min: 7, max: 30 }));
    
    const series = faker.helpers.arrayElement(["A", "B", "C", "D", "E"]);
    const rfqCode = `RFQ-${new Date().getFullYear()}-${String(i).padStart(3, '0')}`;
    
    rfqsData.push({
      rfqCode,
      projectId: randomProject.id,
      series,
      itemId: randomItem.id,
      dueDate,
      rfqSendDate,
      status: faker.helpers.arrayElement(statusOptions),
      rfqSealedYn: faker.datatype.boolean(0.3), // 30% 확률로 true
      picCode: faker.person.lastName() + faker.string.numeric(2), // 발주담당 코드
      remark: faker.helpers.maybe(() => faker.lorem.sentence(), { probability: 0.6 }),
      createdBy: randomUser.id,
      updatedBy: randomUser.id,
      createdAt: faker.date.recent({ days: 100 }),
      updatedAt: new Date()
    });
  }
  
  // 청크로 나누어 대용량 데이터 삽입
  const chunkSize = 10;
  for (let i = 0; i < rfqsData.length; i += chunkSize) {
    const chunk = rfqsData.slice(i, i + chunkSize);
    await db.insert(procurementRfqs).values(chunk);
  }
  
  console.log(`  ✓ ${rfqsData.length}개의 RFQ가 생성되었습니다.`);
  
  return await db.query.procurementRfqs.findMany();
}

// RFQ 상세 정보 시드 데이터 생성
async function seedRfqDetails(rfqs, paymentTermsList, incotermsList) {
  console.log("🌱 시드 데이터 생성 중: RFQ 상세 정보");
  
  // 기존 데이터 확인
  const existingDetails = await db.query.procurementRfqDetails.findMany();
  if (existingDetails.length > 0) {
    console.log(`  ✓ RFQ 상세 정보가 이미 ${existingDetails.length}개 존재합니다.`);
    return existingDetails;
  }
  
  const rfqDetailsData = [];
  
  for (const rfq of rfqs) {
    for (let i = 0; i < NUM_RFQ_DETAILS_PER_RFQ; i++) {
      const randomVendor = await getRandomVendor();
      const randomUser = await getRandomUser();
      
      const paymentTerm = faker.helpers.arrayElement(paymentTermsList);
      const incoterm = faker.helpers.arrayElement(incotermsList);
      
      // 배송일은 RFQ 마감일로부터 14~60일 이후
      const deliveryDate = new Date(rfq.dueDate);
      deliveryDate.setDate(deliveryDate.getDate() + faker.number.int({ min: 14, max: 60 }));
      
      const currencies = ["USD", "EUR", "JPY", "KRW", "CNY"];
      
      rfqDetailsData.push({
        procurementRfqsId: rfq.id,
        vendorsId: randomVendor.id,
        currency: faker.helpers.arrayElement(currencies),
        paymentTermsCode: paymentTerm.code,
        incotermsCode: incoterm.code,
        incotermsDetail: faker.helpers.maybe(() => faker.lorem.sentence(), { probability: 0.7 }),
        deliveryDate,
        taxCode: faker.helpers.arrayElement(["VV", "V1", "V2", "V0"]),
        placeOfShipping: faker.location.city() + ", " + faker.location.country(),
        placeOfDestination: faker.location.city() + ", " + faker.location.country(),
        remark: faker.helpers.maybe(() => faker.lorem.paragraph(), { probability: 0.5 }),
        cancelReason: faker.helpers.maybe(() => faker.lorem.sentence(), { probability: 0.1 }),
        updatedBy: randomUser.id,
        updatedAt: new Date(),
        materialPriceRelatedYn: faker.datatype.boolean(0.4) // 40% 확률로 true
      });
    }
  }
  
  // 청크로 나누어 대용량 데이터 삽입
  const chunkSize = 20;
  for (let i = 0; i < rfqDetailsData.length; i += chunkSize) {
    const chunk = rfqDetailsData.slice(i, i + chunkSize);
    await db.insert(procurementRfqDetails).values(chunk);
  }
  
  console.log(`  ✓ ${rfqDetailsData.length}개의 RFQ 상세 정보가 생성되었습니다.`);
  
  return await db.query.procurementRfqDetails.findMany();
}

// PR 아이템 시드 데이터 생성
async function seedPrItems(rfqs) {
  console.log("🌱 시드 데이터 생성 중: PR 아이템");
  
  // 기존 데이터 확인
  const existingItems = await db.query.prItems.findMany();
  if (existingItems.length > 0) {
    console.log(`  ✓ PR 아이템이 이미 ${existingItems.length}개 존재합니다.`);
    return existingItems;
  }
  
  const prItemsData = [];
  
  for (const rfq of rfqs) {
    // 각 RFQ마다 여러 개의 PR 아이템 생성
    for (let i = 0; i < NUM_PR_ITEMS_PER_RFQ; i++) {
      const randomItem = await getRandomItem();
      
      // 하나의 RFQ에 하나의 major item만 존재하도록 설정
      const majorYn = i === 0;
      
      // 배송일은 RFQ 전송일로부터 30~120일 이후
      const deliveryDate = new Date(rfq.rfqSendDate);
      deliveryDate.setDate(deliveryDate.getDate() + faker.number.int({ min: 30, max: 120 }));
      
      const materialCode = faker.string.alphanumeric(8).toUpperCase();
      const rfqItem = `${String(i + 1).padStart(2, '0')}`;
      const prItem = `${String(i + 1).padStart(2, '0')}`;
      const prNo = `PR-${new Date().getFullYear()}-${faker.string.numeric(4)}`;
      
      prItemsData.push({
        procurementRfqsId: rfq.id,
        rfqItem,
        prItem,
        prNo,
        itemId: randomItem.id,
        materialCode,
        materialCategory: faker.helpers.arrayElement(["RAW", "SEMI", "FIN", "SERV", "TOOL"]),
        acc: faker.string.alphanumeric(6).toUpperCase(),
        materialDescription: faker.commerce.productName(),
        size: faker.helpers.maybe(() => `${faker.number.int({ min: 10, max: 200 })}x${faker.number.int({ min: 10, max: 200 })}`, { probability: 0.7 }),
        deliveryDate,
        quantity: faker.number.float({ min: 1, max: 1000, precision: 0.01 }),
        uom: faker.helpers.arrayElement(["EA", "KG", "M", "L", "SET"]),
        grossWeight: faker.number.float({ min: 0.1, max: 500, precision: 0.01 }),
        gwUom: faker.helpers.arrayElement(["KG", "TON"]),
        specNo: faker.helpers.maybe(() => `SPEC-${faker.string.alphanumeric(6).toUpperCase()}`, { probability: 0.6 }),
        specUrl: faker.helpers.maybe(() => faker.internet.url(), { probability: 0.4 }),
        trackingNo: faker.helpers.maybe(() => `TRK-${faker.string.alphanumeric(8).toUpperCase()}`, { probability: 0.5 }),
        majorYn,
        projectDef: faker.helpers.maybe(() => faker.string.alphanumeric(5).toUpperCase(), { probability: 0.8 }),
        projectSc: faker.helpers.maybe(() => faker.string.alphanumeric(5).toUpperCase(), { probability: 0.7 }),
        projectKl: faker.helpers.maybe(() => faker.string.alphanumeric(5).toUpperCase(), { probability: 0.7 }),
        projectLc: faker.helpers.maybe(() => faker.string.alphanumeric(5).toUpperCase(), { probability: 0.7 }),
        projectDl: faker.helpers.maybe(() => faker.string.alphanumeric(5).toUpperCase(), { probability: 0.7 }),
        remark: faker.helpers.maybe(() => faker.lorem.sentence(), { probability: 0.4 })
      });
    }
  }
  
  // 청크로 나누어 대용량 데이터 삽입
  const chunkSize = 30;
  for (let i = 0; i < prItemsData.length; i += chunkSize) {
    const chunk = prItemsData.slice(i, i + chunkSize);
    await db.insert(prItems).values(chunk);
  }
  
  console.log(`  ✓ ${prItemsData.length}개의 PR 아이템이 생성되었습니다.`);
  
  return await db.query.prItems.findMany();
}

/**
 * 시드 실행 함수
 */
export async function seedProcurement() {
  console.log("🔍 procurement 데이터 시딩 시작...");
  
  try {
    // 순서대로 데이터 시딩
    const paymentTermsList = await seedPaymentTerms();
    const incotermsList = await seedIncoterms();
    const rfqsList = await seedRfqs();
    await seedRfqDetails(rfqsList, paymentTermsList, incotermsList);
    await seedPrItems(rfqsList);
    
    console.log("✅ procurement 데이터 시딩 완료!");
  } catch (error) {
    console.error("❌ 데이터 시딩 중 오류 발생:", error);
    throw error;
  }
}

// 스크립트가 직접 실행될 때만 시드 함수 호출
if (require.main === module) {
  seedProcurement()
    .then(() => {
      console.log("시드 스크립트 실행 완료. 프로세스를 종료합니다.");
      process.exit(0);
    })
    .catch((error) => {
      console.error("시드 스크립트 실행 중 오류:", error);
      process.exit(1);
    });
}