summaryrefslogtreecommitdiff
path: root/db/seeds
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-05-14 01:40:53 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-05-14 01:40:53 +0000
commitf3a2e8e2dab46ad0725fe8861395cbcb68c2bbbf (patch)
tree5785e226d7de8435090b5d8daa0a45efe89852a2 /db/seeds
parent195643ba38999454c63b77fecc8902c0a91eb842 (diff)
(대표님) 스키마 변경사항 및 마이그레이션
Diffstat (limited to 'db/seeds')
-rw-r--r--db/seeds/po-rfq.ts352
1 files changed, 352 insertions, 0 deletions
diff --git a/db/seeds/po-rfq.ts b/db/seeds/po-rfq.ts
new file mode 100644
index 00000000..62bb39bd
--- /dev/null
+++ b/db/seeds/po-rfq.ts
@@ -0,0 +1,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);
+ });
+} \ No newline at end of file