diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-05-14 01:40:53 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-05-14 01:40:53 +0000 |
| commit | f3a2e8e2dab46ad0725fe8861395cbcb68c2bbbf (patch) | |
| tree | 5785e226d7de8435090b5d8daa0a45efe89852a2 /db/seeds | |
| parent | 195643ba38999454c63b77fecc8902c0a91eb842 (diff) | |
(대표님) 스키마 변경사항 및 마이그레이션
Diffstat (limited to 'db/seeds')
| -rw-r--r-- | db/seeds/po-rfq.ts | 352 |
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 |
