From 25b916d040a512cd5248dff319d727ae144d0652 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Mon, 15 Sep 2025 03:24:12 +0000 Subject: (최겸) 구매 PCR 개발(po -> pcr, ecc pcr-confirm test 필) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/schema/pcr.ts | 233 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 db/schema/pcr.ts (limited to 'db/schema') diff --git a/db/schema/pcr.ts b/db/schema/pcr.ts new file mode 100644 index 00000000..8d774f0d --- /dev/null +++ b/db/schema/pcr.ts @@ -0,0 +1,233 @@ +/** + * PCR (Purchase Change Request) 관련 스키마 + * + * PCR_PO: 구매/계약 정보 변경 요청 테이블 + * PCR_PR: 구매 요청 세부 정보 테이블 + */ + +import { + pgTable, + serial, + varchar, + text, + timestamp, + integer, + date, + numeric, + index, +} from "drizzle-orm/pg-core"; +import { relations } from "drizzle-orm"; +import { users } from "./users"; +import { vendors } from "./vendors"; + +// ===== PCR_PR 첨부파일 테이블 ===== +export const pcrPrAttachment = pgTable("pcr_pr_attachment", { + id: serial("id").primaryKey(), + pcrPrId: integer("pcr_pr_id").notNull().references(() => pcrPr.id), // PCR_PR 참조 + type: varchar("type", { length: 20 }).notNull(), // 'BEFORE' | 'AFTER' (변경전/변경후) + fileName: varchar("file_name", { length: 255 }).notNull(), + filePath: text("file_path").notNull(), + fileSize: integer("file_size"), + mimeType: varchar("mime_type", { length: 100 }), + createdBy: integer("created_by").references(() => users.id), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedBy: integer("updated_by").references(() => users.id), + updatedAt: timestamp("updated_at").defaultNow().notNull(), +}); + +// ===== PCR_PO 테이블 (상단 테이블) ===== +export const pcrPo = pgTable("pcr_po", { + id: serial("id").primaryKey(), + // 협력업체 정보 (EvcP 페이지에만 표시) + vendorId: integer("vendor_id").references(() => vendors.id), + + // PCR 승인 상태 + pcrApprovalStatus: varchar("pcr_approval_status", { length: 20 }) + .default('PENDING'), + + // 변경 구분 + changeType: varchar("change_type", { length: 20 }) + .default('OTHER'), + + // 상세 + details: text("details"), + + // 프로젝트 + project: varchar("project", { length: 255 }), + + // PCR 요청 일자 + pcrRequestDate: date("pcr_request_date", { mode: "date" }).notNull(), + + // PO/계약 번호 + poContractNumber: varchar("po_contract_number", { length: 100 }).notNull(), + + // Rev./품번 + revItemNumber: varchar("rev_item_number", { length: 100 }), + + // 구매/계약 담당자 + purchaseContractManager: varchar("purchase_contract_manager", { length: 100 }), + + // PCR 생성자 + pcrCreator: varchar("pcr_creator", { length: 100 }), + + // PO/계약 금액 (전) + poContractAmountBefore: numeric("po_contract_amount_before", { precision: 15, scale: 2 }), + + // PO/계약 금액 (후) + poContractAmountAfter: numeric("po_contract_amount_after", { precision: 15, scale: 2 }), + + // 계약 통화 + contractCurrency: varchar("contract_currency", { length: 10 }).default("KRW"), + + // PCR 사유 + pcrReason: text("pcr_reason"), + + // 상세 사유 + detailsReason: text("details_reason"), + + // 거절 사유 + rejectionReason: text("rejection_reason"), + + // PCR 회신 일 + pcrResponseDate: date("pcr_response_date", { mode: "date" }), + + + // 감사 필드 + createdBy: integer("created_by") + .notNull() + .references(() => users.id, { onDelete: "set null" }), + updatedBy: integer("updated_by") + .notNull() + .references(() => users.id, { onDelete: "set null" }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().notNull(), +}, (table) => ({ + // 인덱스 정의 + poContractNumberIdx: index("pcr_po_po_contract_number_idx").on(table.poContractNumber), + vendorIdIdx: index("pcr_po_vendor_id_idx").on(table.vendorId), + pcrApprovalStatusIdx: index("pcr_po_approval_status_idx").on(table.pcrApprovalStatus), + createdAtIdx: index("pcr_po_created_at_idx").on(table.createdAt), +})); + +// ===== PCR_PR 테이블 (하단 테이블) ===== +export const pcrPr = pgTable("pcr_pr", { + id: serial("id").primaryKey(), + + // 자재번호 + materialNumber: varchar("material_number", { length: 100 }).notNull(), + + // 자재내역 + materialDetails: text("material_details"), + + // 수량 (변경전) + quantityBefore: numeric("quantity_before", { precision: 12, scale: 3 }), + + // 수량 (변경후) + quantityAfter: numeric("quantity_after", { precision: 12, scale: 3 }), + + // 중량 (변경전) + weightBefore: numeric("weight_before", { precision: 12, scale: 3 }), + + // 중량 (변경후) + weightAfter: numeric("weight_after", { precision: 12, scale: 3 }), + + // 사급중량 (변경전) + subcontractorWeightBefore: numeric("subcontractor_weight_before", { precision: 12, scale: 3 }), + + // 사급중량 (변경후) + subcontractorWeightAfter: numeric("subcontractor_weight_after", { precision: 12, scale: 3 }), + + // 도급중량 (변경전) + supplierWeightBefore: numeric("supplier_weight_before", { precision: 12, scale: 3 }), + + // 도급중량 (변경후) + supplierWeightAfter: numeric("supplier_weight_after", { precision: 12, scale: 3 }), + + // SPEC 도면 (변경전) + specDrawingBefore: text("spec_drawing_before"), + + // SPEC 도면 (변경후) + specDrawingAfter: text("spec_drawing_after"), + + // 최초 PO/계약 일 + initialPoContractDate: date("initial_po_contract_date", { mode: "date" }), + + // SPEC 변경 일 + specChangeDate: date("spec_change_date", { mode: "date" }), + + // PO/계약 수정 일 + poContractModifiedDate: date("po_contract_modified_date", { mode: "date" }), + + // 확인 일 + confirmationDate: date("confirmation_date", { mode: "date" }), + + // 설계 담당자 + designManager: varchar("design_manager", { length: 100 }), + + // PO/계약 번호 (PCR_PO와 연결) + poContractNumber: varchar("po_contract_number", { length: 100 }).notNull(), + + // 감사 필드 + createdBy: integer("created_by") + .notNull() + .references(() => users.id, { onDelete: "set null" }), + updatedBy: integer("updated_by") + .notNull() + .references(() => users.id, { onDelete: "set null" }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().notNull(), +}, (table) => ({ + // 인덱스 정의 + poContractNumberIdx: index("pcr_pr_po_contract_number_idx").on(table.poContractNumber), + materialNumberIdx: index("pcr_pr_material_number_idx").on(table.materialNumber), + createdAtIdx: index("pcr_pr_created_at_idx").on(table.createdAt), +})); + +// ===== Relations 정의 ===== + +export const pcrPoRelations = relations(pcrPo, ({ one, many }) => ({ + // 생성자 관계 + createdByUser: one(users, { + fields: [pcrPo.createdBy], + references: [users.id], + relationName: "pcrPoCreatedBy", + }), + updatedByUser: one(users, { + fields: [pcrPo.updatedBy], + references: [users.id], + relationName: "pcrPoUpdatedBy", + }), + // 협력업체 관계 + vendor: one(vendors, { + fields: [pcrPo.vendorId], + references: [vendors.id], + }), + // 하위 PCR_PR 관계 + pcrPrItems: many(pcrPr), +})); + +export const pcrPrRelations = relations(pcrPr, ({ one }) => ({ + // 생성자 관계 + createdByUser: one(users, { + fields: [pcrPr.createdBy], + references: [users.id], + relationName: "pcrPrCreatedBy", + }), + updatedByUser: one(users, { + fields: [pcrPr.updatedBy], + references: [users.id], + relationName: "pcrPrUpdatedBy", + }), + // 상위 PCR_PO 관계 + pcrPoItem: one(pcrPo, { + fields: [pcrPr.poContractNumber], + references: [pcrPo.poContractNumber], + }), +})); + +// ===== 타입 정의 ===== +export type PcrPo = typeof pcrPo.$inferSelect; +export type PcrPr = typeof pcrPr.$inferSelect; + +export type NewPcrPo = typeof pcrPo.$inferInsert; +export type NewPcrPr = typeof pcrPr.$inferInsert; -- cgit v1.2.3