summaryrefslogtreecommitdiff
path: root/db/seeds_2/rfqSeed.ts
blob: f2cde1b21f82d1379f3a63ef144f99c5beab2d7e (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
// 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;
  }
}