diff options
Diffstat (limited to 'db/schema')
| -rw-r--r-- | db/schema/pcr.ts | 233 |
1 files changed, 233 insertions, 0 deletions
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;
|
