diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-15 04:43:44 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-06-15 04:43:44 +0000 |
| commit | b63d886613707c99cb4b244c8442860c2a15af69 (patch) | |
| tree | 4e33596a626f9dba47836a207b456a86eef5b977 /db/schema/techSales.ts | |
| parent | a15475296b9da1cb83995b24acf9a6a20b635756 (diff) | |
(최겸) 메뉴 및 DB 수정(기술영업 벤더, RFQ, User)
Diffstat (limited to 'db/schema/techSales.ts')
| -rw-r--r-- | db/schema/techSales.ts | 152 |
1 files changed, 78 insertions, 74 deletions
diff --git a/db/schema/techSales.ts b/db/schema/techSales.ts index 082ab592..334bf6bb 100644 --- a/db/schema/techSales.ts +++ b/db/schema/techSales.ts @@ -34,13 +34,12 @@ import { integer, numeric, date, - jsonb, } from "drizzle-orm/pg-core"; import { relations } from "drizzle-orm"; import { biddingProjects } from "./projects"; import { users } from "./users"; -import { itemShipbuilding } from "./items"; -import { vendors } from "./vendors"; +import { itemOffshoreHull, itemOffshoreTop, itemShipbuilding } from "./items"; +import { techVendors } from "./techVendors"; // ===== 기술영업 상태 관리 상수 및 타입 ===== @@ -60,7 +59,6 @@ export const TECH_SALES_QUOTATION_STATUSES = { DRAFT: "Draft", SUBMITTED: "Submitted", REVISED: "Revised", - REJECTED: "Rejected", ACCEPTED: "Accepted", } as const; @@ -86,12 +84,6 @@ export const TECH_SALES_QUOTATION_STATUS_CONFIG = { description: "수정된 견적서", color: "text-purple-600", }, - [TECH_SALES_QUOTATION_STATUSES.REJECTED]: { - label: "반려됨", - variant: "destructive" as const, - description: "반려된 견적서", - color: "text-red-600", - }, [TECH_SALES_QUOTATION_STATUSES.ACCEPTED]: { label: "승인됨", variant: "success" as const, @@ -102,19 +94,22 @@ export const TECH_SALES_QUOTATION_STATUS_CONFIG = { // ===== 스키마 정의 ===== -// 기술영업 RFQ 테이블 +// 기술영업 RFQ 테이블 - 아이템 관계를 1:N으로 변경 export const techSalesRfqs = pgTable("tech_sales_rfqs", { id: serial("id").primaryKey(), rfqCode: varchar("rfq_code", { length: 50 }).unique(), // ex) "RFQ-2025-001" - // item에서 기술영업에서 사용하는 추가 정보는 itemShipbuilding 테이블에 저장되어 있다. - itemShipbuildingId: integer("item_shipbuilding_id") - .notNull() - .references(() => itemShipbuilding.id, { onDelete: "cascade" }), + // 아이템 직접 참조 제거 - tech_sales_rfq_items 테이블로 분리 + // itemShipbuildingId: integer("item_shipbuilding_id") + // .notNull() + // .references(() => itemShipbuilding.id, { onDelete: "cascade" }), // 프로젝트 참조 ID biddingProjectId: integer("bidding_project_id").references(() => biddingProjects.id, { onDelete: "set null" }), + // RFQ 설명 (새로 추가) + description: text("description"), + remark: text("remark"), // 기술영업에서 벤더에게 제공할 정보로, 모든 벤더에게 동일하게 제공함. materialCode: varchar("material_code", { length: 255 }), @@ -126,12 +121,10 @@ export const techSalesRfqs = pgTable("tech_sales_rfqs", { .$type<TechSalesRfqStatus>() .default(TECH_SALES_RFQ_STATUSES.RFQ_CREATED) .notNull(), - - // rfq 밀봉 기능은, 기술영업에서 사용하지 않겠다고 함. - + //picCode: 발주자 코드 picCode: varchar("pic_code", { length: 50 }), - remark: text("remark"), + // WHO sentBy: integer("sent_by").references(() => users.id, { @@ -150,48 +143,37 @@ export const techSalesRfqs = pgTable("tech_sales_rfqs", { // 삼성중공업이 RFQ를 취소한 경우 cancelReason: text("cancel_reason"), + // RFQ 타입 구분 (조선/해양top/해양hull) + rfqType: varchar("rfq_type", { length: 20 }) + .$type<"SHIP" | "TOP" | "HULL">() + .default("SHIP") + .notNull(), +}); - // 프로젝트 정보 스냅샷 (프로젝트 관련 모든 정보) - // 기존 개별 컬럼 방식에서 jsonb로 마이그레이션 시: - // 1. 기존 RFQ 데이터는 pspid 등의 개별 컬럼 값을 기반으로 jsonb 형태로 변환하여 마이그레이션 - // 2. 새로운 RFQ 생성 시에는 biddingProjects와 projectSeries 테이블에서 정보를 조회하여 스냅샷으로 저장 - projectSnapshot: jsonb("project_snapshot").$type<{ - pspid: string; // 견적프로젝트번호 - projNm?: string; // 견적프로젝트명 - sector?: string; // 부문(S / M) - projMsrm?: number; // 척수 - kunnr?: string; // 선주코드 - kunnrNm?: string; // 선주명 - cls1?: string; // 선급코드 - cls1Nm?: string; // 선급명 - ptype?: string; // 선종코드 - ptypeNm?: string; // 선종명 - pmodelCd?: string; // 선형코드 - pmodelNm?: string; // 선형명 - pmodelSz?: string; // 선형크기 - pmodelUom?: string; // 선형단위 - txt04?: string; // 견적상태코드 - txt30?: string; // 견적상태명 - estmPm?: string; // 견적대표PM 성명 - pspCreatedAt?: Date | string; // 원래 생성 일자 - pspUpdatedAt?: Date | string; // 원래 업데이트 일자 - }>(), +// 기술영업 RFQ 아이템 테이블 (1:N 관계) +export const techSalesRfqItems = pgTable("tech_sales_rfq_items", { + id: serial("id").primaryKey(), + rfqId: integer("rfq_id") + .notNull() + .references(() => techSalesRfqs.id, { onDelete: "cascade" }), - // 프로젝트 시리즈 정보 스냅샷 - // 시리즈 정보는 배열 형태로 저장되며, 프로젝트의 모든 시리즈 정보를 포함 - // RFQ 생성 시점의 시리즈 정보를 스냅샷으로 보존함으로써 후속 변경에 영향을 받지 않음 - seriesSnapshot: jsonb("series_snapshot").$type<Array<{ - pspid: string; // 견적프로젝트번호 - sersNo: string; // 시리즈번호 - scDt?: string; // Steel Cutting Date - klDt?: string; // Keel Laying Date - lcDt?: string; // Launching Date - dlDt?: string; // Delivery Date - dockNo?: string; // 도크코드 - dockNm?: string; // 도크명 - projNo?: string; // SN공사번호(계약후) - post1?: string; // SN공사명(계약후) - }>>(), + // 아이템 타입별 참조 + itemShipbuildingId: integer("item_shipbuilding_id") + .references(() => itemShipbuilding.id, { onDelete: "cascade" }), + // 해양 관련 아이템들 + itemOffshoreTopId: integer("item_offshore_top_id") + .references(() => itemOffshoreTop.id, { onDelete: "cascade" }), + itemOffshoreHullId: integer("item_offshore_hull_id") + .references(() => itemOffshoreHull.id, { onDelete: "cascade" }), + + // 아이템 타입 + itemType: varchar("item_type", { length: 20 }) + .$type<"SHIP" | "TOP" | "HULL">() + .notNull(), + + // 생성 정보 + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().notNull(), }); // 기술영업 첨부파일 테이블 (RFQ 에 첨부되는 것임) @@ -227,7 +209,7 @@ export const techSalesVendorQuotations = pgTable( .references(() => techSalesRfqs.id, { onDelete: "cascade" }), vendorId: integer("vendor_id") .notNull() - .references(() => vendors.id, { onDelete: "set null" }), + .references(() => techVendors.id, { onDelete: "set null" }), // === [시작]견적 응답 정보 === quotationCode: varchar("quotation_code", { length: 50 }), @@ -266,7 +248,7 @@ export const techSalesRfqComments = pgTable( rfqId: integer("rfq_id") .notNull() .references(() => techSalesRfqs.id, { onDelete: "cascade" }), - vendorId: integer("vendor_id").references(() => vendors.id, { + vendorId: integer("vendor_id").references(() => techVendors.id, { onDelete: "set null", }), userId: integer("user_id").references(() => users.id, { @@ -311,7 +293,7 @@ export const techSalesRfqCommentAttachments = pgTable("tech_sales_rfq_comment_at uploadedBy: integer("uploaded_by").references(() => users.id, { onDelete: "set null", }), - vendorId: integer("vendor_id").references(() => vendors.id, { + vendorId: integer("vendor_id").references(() => techVendors.id, { onDelete: "set null", }), uploadedAt: timestamp("uploaded_at").defaultNow().notNull(), @@ -324,12 +306,6 @@ export type TechSalesVendorQuotations = // Relations 정의 export const techSalesRfqsRelations = relations(techSalesRfqs, ({ one, many }) => ({ - // 조선 아이템 관계 - itemShipbuilding: one(itemShipbuilding, { - fields: [techSalesRfqs.itemShipbuildingId], - references: [itemShipbuilding.id], - }), - // 프로젝트 관계 biddingProject: one(biddingProjects, { fields: [techSalesRfqs.biddingProjectId], @@ -354,11 +330,39 @@ export const techSalesRfqsRelations = relations(techSalesRfqs, ({ one, many }) = }), // 하위 관계들 + rfqItems: many(techSalesRfqItems), // 새로 추가된 1:N 관계 vendorQuotations: many(techSalesVendorQuotations), attachments: many(techSalesAttachments), comments: many(techSalesRfqComments), })); +// 새로운 RFQ Items 관계 +export const techSalesRfqItemsRelations = relations(techSalesRfqItems, ({ one }) => ({ + // 상위 RFQ 관계 + rfq: one(techSalesRfqs, { + fields: [techSalesRfqItems.rfqId], + references: [techSalesRfqs.id], + }), + + // 조선 아이템 관계 + itemShipbuilding: one(itemShipbuilding, { + fields: [techSalesRfqItems.itemShipbuildingId], + references: [itemShipbuilding.id], + }), + + // 해양 Hull 아이템 관계 + itemOffshoreHull: one(itemOffshoreHull, { + fields: [techSalesRfqItems.itemOffshoreHullId], + references: [itemOffshoreHull.id], + }), + + // 해양 Top 아이템 관계 + itemOffshoreTop: one(itemOffshoreTop, { + fields: [techSalesRfqItems.itemOffshoreTopId], + references: [itemOffshoreTop.id], + }), +})); + export const techSalesVendorQuotationsRelations = relations(techSalesVendorQuotations, ({ one, many }) => ({ // 상위 RFQ 관계 rfq: one(techSalesRfqs, { @@ -367,9 +371,9 @@ export const techSalesVendorQuotationsRelations = relations(techSalesVendorQuota }), // 벤더 관계 - vendor: one(vendors, { + vendor: one(techVendors, { fields: [techSalesVendorQuotations.vendorId], - references: [vendors.id], + references: [techVendors.id], }), // 사용자 관계 @@ -411,9 +415,9 @@ export const techSalesRfqCommentsRelations = relations(techSalesRfqComments, ({ }), // 벤더 관계 - vendor: one(vendors, { + vendor: one(techVendors, { fields: [techSalesRfqComments.vendorId], - references: [vendors.id], + references: [techVendors.id], }), // 사용자 관계 @@ -466,8 +470,8 @@ export const techSalesRfqCommentAttachmentsRelations = relations(techSalesRfqCom }), // 벤더 관계 - vendor: one(vendors, { + vendor: one(techVendors, { fields: [techSalesRfqCommentAttachments.vendorId], - references: [vendors.id], + references: [techVendors.id], }), }));
\ No newline at end of file |
