From ee77f36b1ceece1236d45fba102c3ea410acebc1 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Thu, 11 Sep 2025 11:20:42 +0000 Subject: (최겸) 구매 계약 메인 및 상세 기능 개발(템플릿 연동 및 계약 전달 개발 필요) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/schema/generalContract.ts | 209 +++++++++++++++++++++++++++++++++++++++++++ db/schema/index.ts | 1 + 2 files changed, 210 insertions(+) create mode 100644 db/schema/generalContract.ts (limited to 'db') diff --git a/db/schema/generalContract.ts b/db/schema/generalContract.ts new file mode 100644 index 00000000..bb671494 --- /dev/null +++ b/db/schema/generalContract.ts @@ -0,0 +1,209 @@ +import { pgTable, serial, varchar, integer, date, timestamp, decimal, text, jsonb, boolean } from 'drizzle-orm/pg-core'; +import { relations } from 'drizzle-orm'; +import { users } from './users'; // users 테이블이 존재한다고 가정 +import { vendors } from './vendors'; // vendors 테이블이 존재한다고 가정 + +export const generalContractTemplates = pgTable('general_contract_templates', { + id: integer("id").primaryKey().generatedAlwaysAsIdentity(), + contractTemplateType: varchar('contract_template_type', { length: 2 }).notNull(), + contractTemplateName: text('contract_template_name').notNull(), + revision: integer('revision').notNull().default(1), + status: varchar('status', { length: 20 }).notNull().default('ACTIVE'), + fileName: varchar("file_name", { length: 255 }), + filePath: varchar("file_path", { length: 1024 }), + legalReviewRequired: boolean('legal_review_required').notNull().default(false), + createdAt: timestamp('created_at').defaultNow(), + createdBy: integer('created_by').references(() => users.id), + updatedAt: timestamp('updated_at').defaultNow(), + updatedBy: integer('updated_by').references(() => users.id), + disposedAt: timestamp('disposed_at'), + restoredAt: timestamp('restored_at'), +}); + + +export const generalContracts = pgTable('general_contracts', { + // ═══════════════════════════════════════════════════════════════ + // 기본 식별 정보 + // ═══════════════════════════════════════════════════════════════ + id: serial('id').primaryKey(), // 계약 고유 ID + contractNumber: varchar('contract_number', { length: 255 }).notNull().unique(), // 계약번호 (자동 채번) + revision: integer('revision').notNull().default(0), // 계약 개정 번호 + + // ═══════════════════════════════════════════════════════════════ + // 계약 분류 및 상태 + // ═══════════════════════════════════════════════════════════════ + status: varchar('status', { length: 50 }).notNull(), // 계약 상태 (Draft, Complete the Contract, Contract Delete 등) + category: varchar('category', { length: 50 }).notNull(), // 계약구분 (단가계약, 일반계약, 매각계약) + type: varchar('type', { length: 50 }).notNull(), // 계약종류 (UP, LE, IL, AL 등) + executionMethod: varchar('execution_method', { length: 50 }).notNull(), // 체결방식 (단가계약, 일반계약 등) + name: varchar('name', { length: 255 }).notNull(), // 계약명 + selectionMethod: varchar('selection_method', { length: 50 }), // 업체선정방법 + + // ═══════════════════════════════════════════════════════════════ + // 협력업체 및 계약 기간 + // ═══════════════════════════════════════════════════════════════ + vendorId: integer('vendor_id').notNull().references(() => vendors.id), // 협력업체 ID + startDate: date('start_date').notNull(), // 계약 시작일 + endDate: date('end_date').notNull(), // 계약 종료일 + validityEndDate: date('validity_end_date').notNull(), // 계약 유효기간 종료일 + + // ═══════════════════════════════════════════════════════════════ + // 연계 정보 + // ═══════════════════════════════════════════════════════════════ + linkedRfqOrItb: varchar('linked_rfq_or_itb', { length: 255 }), // 연계 RFQ/ITB 번호 + linkedPoNumber: varchar('linked_po_number', { length: 255 }), // 연계 PO 번호 + linkedBidNumber: varchar('linked_bid_number', { length: 255 }), // 연계 BID 번호 + + // ═══════════════════════════════════════════════════════════════ + // 계약 범위 및 사양 + // ═══════════════════════════════════════════════════════════════ + contractScope: varchar('contract_scope', { length: 50 }), // 계약 범위 + warrantyPeriod: jsonb('warranty_period').default({}), // 품질/하자 보증기간 + specificationType: varchar('specification_type', { length: 50 }), // 사양 유형 + specificationManualText: text('specification_manual_text'), // 사양 매뉴얼 텍스트 + + // ═══════════════════════════════════════════════════════════════ + // 금액 정보 + // ═══════════════════════════════════════════════════════════════ + unitPriceType: varchar('unit_price_type', { length: 50 }), // 단가 유형 + contractAmount: decimal('contract_amount', { precision: 15, scale: 2 }), // 계약 금액 + currency: varchar('currency', { length: 10 }), // 통화 + totalAmount: decimal('total_amount', { precision: 15, scale: 2 }), // 총 금액 + availableBudget: decimal('available_budget', { precision: 15, scale: 2 }), // 가용 예산 + + // ═══════════════════════════════════════════════════════════════ + // 지급조건 (Payment Condition) + // ═══════════════════════════════════════════════════════════════ + paymentBeforeDelivery: jsonb('payment_before_delivery').default({}), // 납품 전 지급조건 + paymentDelivery: varchar('payment_delivery', { length: 50 }), // 납품 지급조건 + paymentAfterDelivery: jsonb('payment_after_delivery').default({}), // 납품 외 지급조건 + contractCurrency: varchar('contract_currency', { length: 10 }), // 계약통화 + paymentTerm: varchar('payment_term', { length: 50 }), // 지불조건 (L003 등) + taxType: varchar('tax_type', { length: 50 }), // 세금 (VV 등) + liquidatedDamages: decimal('liquidated_damages', { precision: 15, scale: 2 }), // 지체상금 + liquidatedDamagesPercent: decimal('liquidated_damages_percent', { precision: 5, scale: 2 }), // 지체상금 비율 + claimAmount: jsonb('claim_amount').default({}), // 클레임금액 + + // ═══════════════════════════════════════════════════════════════ + // 인도조건 (Delivery Condition) + // ═══════════════════════════════════════════════════════════════ + deliveryType: varchar('delivery_type', { length: 50 }), // 납기종류 (단일납기, 분할납기, 구간납기) + deliveryTerm: varchar('delivery_term', { length: 50 }), // 인도조건 (FOB 등) + shippingLocation: varchar('shipping_location', { length: 100 }), // 선적지 + dischargeLocation: varchar('discharge_location', { length: 100 }), // 하역지 + contractDeliveryDate: date('contract_delivery_date'), // 계약납기일 + + // ═══════════════════════════════════════════════════════════════ + // 추가조건 (Additional Condition) + // ═══════════════════════════════════════════════════════════════ + contractEstablishmentConditions: jsonb('contract_establishment_conditions').default({}), // 계약성립조건 + interlockingSystem: varchar('interlocking_system', { length: 10 }), // 연동제적용 (Y/N) + mandatoryDocuments: jsonb('mandatory_documents').default({}), // 필수문서동의 + contractTerminationConditions: jsonb('contract_termination_conditions').default({}), // 계약해지조건 + + // ═══════════════════════════════════════════════════════════════ + // 기타 계약 조건 및 약관 (JSON 형태) + // ═══════════════════════════════════════════════════════════════ + terms: jsonb('terms').default({}), // 계약 조건 + complianceChecklist: jsonb('compliance_checklist').default({}), // 컴플라이언스 체크리스트 + communicationChannels: jsonb('communication_channels').default({}), // 커뮤니케이션 채널 + locations: jsonb('locations').default({}), // 위치 정보 + fieldServiceRates: jsonb('field_service_rates').default({}), // 현장 서비스 요금 + offsetDetails: jsonb('offset_details').default({}), // 오프셋 세부사항 + + // ═══════════════════════════════════════════════════════════════ + // 시스템 관리 정보 + // ═══════════════════════════════════════════════════════════════ + registeredById: integer('registered_by_id').notNull().references(() => users.id), // 등록자 ID + registeredAt: timestamp('registered_at').notNull().defaultNow(), // 등록일시 + signedAt: timestamp('signed_at'), // 계약 체결일시 + lastUpdatedById: integer('last_updated_by_id').notNull().references(() => users.id), // 최종 수정자 ID + lastUpdatedAt: timestamp('last_updated_at').notNull().defaultNow(), // 최종 수정일시 + notes: text('notes'), // 비고 +}); + +export const generalContractItems = pgTable('general_contract_items', { + // ═══════════════════════════════════════════════════════════════ + // 기본 식별 정보 + // ═══════════════════════════════════════════════════════════════ + id: serial('id').primaryKey(), // 품목 고유 ID + contractId: integer('contract_id').notNull().references(() => generalContracts.id), // 계약 ID (외래키) + + // ═══════════════════════════════════════════════════════════════ + // 품목 기본 정보 + // ═══════════════════════════════════════════════════════════════ + project: varchar('project', { length: 255 }), // 프로젝트 명 + itemCode: varchar('item_code', { length: 100 }), // 품목코드 (PKG No.) + itemInfo: varchar('item_info', { length: 500 }), // Item 정보 (자재그룹 / 자재코드) + specification: varchar('specification', { length: 500 }), // 규격 + + // ═══════════════════════════════════════════════════════════════ + // 수량 및 단가 정보 + // ═══════════════════════════════════════════════════════════════ + quantity: decimal('quantity', { precision: 15, scale: 3 }), // 수량 + quantityUnit: varchar('quantity_unit', { length: 50 }), // 수량단위 + contractDeliveryDate: date('contract_delivery_date'), // 계약납기일 + contractUnitPrice: decimal('contract_unit_price', { precision: 15, scale: 2 }), // 계약단가 + contractAmount: decimal('contract_amount', { precision: 15, scale: 2 }), // 계약금액 + contractCurrency: varchar('contract_currency', { length: 10 }), // 계약통화 + + // ═══════════════════════════════════════════════════════════════ + // 시스템 관리 정보 + // ═══════════════════════════════════════════════════════════════ + createdAt: timestamp('created_at').notNull().defaultNow(), // 생성일시 + updatedAt: timestamp('updated_at').notNull().defaultNow(), // 수정일시 +}); + +export const generalContractAttachments = pgTable('general_contract_attachments', { + id: serial('id').primaryKey(), + contractId: integer('contract_id').notNull().references(() => generalContracts.id), + documentName: varchar('document_name', { length: 255 }).notNull(), // '사양 및 공급범위', '단가파일', '계약서 서명본' 등 + fileName: varchar('file_name', { length: 255 }).notNull(), // 실제 파일명 + filePath: varchar('file_path', { length: 512 }).notNull(), // 파일 저장 경로 (S3 URL 등) + shiComment: text('shi_comment'), + vendorComment: text('vendor_comment'), + legalReview: boolean('legal_review').notNull().default(false), // 법무 검토 여부 + uploadedAt: timestamp('uploaded_at').notNull().defaultNow(), + uploadedById: integer('uploaded_by_id').notNull().references(() => users.id), +}); + +export const generalContractAttachmentsRelations = relations(generalContractAttachments, ({ one }) => ({ + contract: one(generalContracts, { + fields: [generalContractAttachments.contractId], + references: [generalContracts.id], + }), +})); +export const generalContractItemsRelations = relations(generalContractItems, ({ one }) => ({ + contract: one(generalContracts, { + fields: [generalContractItems.contractId], + references: [generalContracts.id], + }), +})); +export const generalContractsRelations = relations(generalContracts, ({ one, many }) => ({ + manager: one(users, { + fields: [generalContracts.registeredById], + references: [users.id], + relationName: 'contract_manager', + }), + lastUpdatedBy: one(users, { + fields: [generalContracts.lastUpdatedById], + references: [users.id], + relationName: 'contract_last_updated_by', + }), + vendor: one(vendors, { + fields: [generalContracts.vendorId], + references: [vendors.id], + }), + items: many(generalContractItems), + attachments: many(generalContractAttachments), + })); + + // TypeScript 타입 정의 +export type GeneralContractTemplate = typeof generalContractTemplates.$inferSelect; +export type NewGeneralContractTemplate = typeof generalContractTemplates.$inferInsert; +export type GeneralContract = typeof generalContracts.$inferSelect; +export type NewGeneralContract = typeof generalContracts.$inferInsert; +export type GeneralContractItem = typeof generalContractItems.$inferSelect; +export type NewGeneralContractItem = typeof generalContractItems.$inferInsert; +export type GeneralContractAttachment = typeof generalContractAttachments.$inferSelect; +export type NewGeneralContractAttachment = typeof generalContractAttachments.$inferInsert; \ No newline at end of file diff --git a/db/schema/index.ts b/db/schema/index.ts index bcabac1b..1c2d5998 100644 --- a/db/schema/index.ts +++ b/db/schema/index.ts @@ -38,6 +38,7 @@ export * from './vendorRegistrations'; export * from './compliance'; export * from './rfqLast'; export * from './rfqVendor'; +export * from './generalContract'; // 부서별 도메인 할당 관리 export * from './departmentDomainAssignments'; -- cgit v1.2.3