// schema.ts (입찰 시스템 전체 스키마 - 실무 요구사항 최종 반영) import { pgTable, serial, varchar, text, timestamp, integer, decimal, boolean, pgEnum, jsonb, date, } from 'drizzle-orm/pg-core' import { Vendor, vendors } from './vendors' import { projects } from './projects'; import { users } from './users'; // 입찰공고문 템플릿 (기존) export const biddingNoticeTemplate = pgTable('bidding_notice_template', { id: serial('id').primaryKey(), biddingId: integer('bidding_id').references(() => biddings.id), // 연결된 입찰 ID (null이면 템플릿) type: varchar('type', { length: 50 }).notNull().default('standard'), // 입찰공고 타입 title: varchar('title', { length: 200 }).notNull().default('입찰공고문'), content: text('content').notNull().default(''), isTemplate: boolean('is_template').default(false).notNull(), // 템플릿 여부 createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }) export type BiddingNoticeTemplate = typeof biddingNoticeTemplate.$inferSelect export type NewBiddingNoticeTemplate = typeof biddingNoticeTemplate.$inferInsert // 1. 입찰 상태 enum (실무 워크플로우 반영) export const biddingStatusEnum = pgEnum('bidding_status', [ 'bidding_generated', // 입찰생성 'request_for_quotation', // 사전견적요청 'received_quotation', // 사전견적접수 'set_target_price', // 내정가 산정 'bidding_opened', // 입찰공고 'bidding_closed', // 입찰마감 'evaluation_of_bidding', // 입찰평가중 'approval_pending', // 결재 진행중 'bidding_disposal', // 유찰 'vendor_selected', // 업체선정 'bid_opening', // 개찰 'early_bid_opening', // 조기개찰 'rebidding', // 재입찰 'disposal_cancelled', // 유찰취소 'bid_closure', // 폐찰 'round_increase', // 차수증가 'deleted' // 삭제 ]) // 2. 계약구분 enum export const contractTypeEnum = pgEnum('contract_type', [ 'unit_price', // 단가계약 'general', // 일반계약 'sale' // 매각계약 ]) // 3. 입찰유형 enum export const biddingTypeEnum = pgEnum('bidding_type', [ 'equipment', // 기자재 'construction', // 공사 'service', // 용역 'lease', // 임차 'transport', // 운송 'waste', // 폐기물 'sale', // 매각 'other' // 기타(직접입력) ]) // 4. 낙찰업체 수 enum export const awardCountEnum = pgEnum('award_count', [ 'single', // 단수 'multiple' // 복수 ]) // 5. 업체 초대/응답 상태 enum export const invitationStatusEnum = pgEnum('invitation_status', [ 'pending', // 초대 대기 'pre_quote_sent', // 사전견적 초대 발송 'pre_quote_accepted', // 사전견적 참여 'pre_quote_declined', // 사전견적 미참여 'pre_quote_submitted', // 사전견적제출완료 'bidding_sent', // 입찰 초대 발송 'bidding_accepted', // 입찰 참여 'bidding_declined', // 입찰 미참여 'bidding_cancelled', // 응찰 취소 'bidding_submitted' // 응찰 완료 ]) // invitationStatus 라벨 맵핑 export const invitationStatusLabels: Record = { pending: '초대 대기', pre_quote_sent: '사전견적 초대 발송', pre_quote_accepted: '사전견적 참여', pre_quote_declined: '사전견적 미참여', pre_quote_submitted: '사전견적제출완료', bidding_sent: '입찰 초대 발송', bidding_accepted: '응찰', bidding_declined: '응찰 포기', bidding_cancelled: '응찰 취소', bidding_submitted: '최종 응찰' } // 6. 문서 타입 enum export const documentTypeEnum = pgEnum('document_type', [ 'notice', // 입찰공고서 'specification', // 사양서 'specification_meeting', // 사양설명회 'contract_draft', // 계약서 초안 'company_proposal', // 협력업체용 첨부파일 'financial_doc', // 재무 관련 문서 'technical_doc', // 기술 관련 문서 'certificate', // 인증서류 'pr_document', // PR 문서 'spec_document', // SPEC 문서 'evaluation_doc', // SHI용 첨부파일 (평가 관련 문서) 'bid_attachment', // 입찰 첨부파일 'selection_result', // 선정결과 첨부파일 'other' // 기타 ]) // 7. 수량 단위 enum export const quantityUnitEnum = pgEnum('quantity_unit', [ 'ea', // 개 'set', // 세트 'kg', // 킬로그램 'ton', // 톤 'm', // 미터 'm2', // 제곱미터 'm3', // 세제곱미터 'lot', // 로트 'other' // 기타 ]) // 8. 중량 단위 enum export const weightUnitEnum = pgEnum('weight_unit', [ 'kg', // 킬로그램 'ton', // 톤 'lb', // 파운드 'g' // 그램 ]) // 8. 입찰 메인 테이블 (실무 요구사항 반영) export const biddings = pgTable('biddings', { id: serial('id').primaryKey(), biddingNumber: varchar('bidding_number', { length: 50 }).unique().notNull(), // 입찰 No. originalBiddingNumber: varchar('original_bidding_number', { length: 50 }), // 원입찰번호 revision: integer('revision').default(0), // Rev. //견적에서 넘어온 레코드인지, 자체생산인지, 디폴트는 자체생산, notnull biddingSourceType: varchar('bidding_source_type', { length: 20 }).notNull().default('manual'), // 기본 정보 projectName: varchar('project_name', { length: 300 }), // 프로젝트명 projectCode: varchar('project_code', { length: 100 }), // 프로젝트 코드 (새로 추가) itemName: varchar('item_name', { length: 300 }), // 품목명 title: varchar('title', { length: 300 }).notNull(), // 입찰명 description: text('description'), // 입찰공고 내용은 별도 biddingNotices 테이블에서 관리됨 // 계약 정보 contractType: contractTypeEnum('contract_type').notNull(), // 계약구분 noticeType: varchar('notice_type', { length: 50 }).default('standard'), // 입찰공고 타입 biddingType: biddingTypeEnum('bidding_type').notNull(), // 입찰유형 awardCount: awardCountEnum('award_count').default('single'), // 낙찰업체 수 // contractPeriod: varchar('contract_period', { length: 100 }), // 계약기간 //시작일 (기본값: 현재 날짜) contractStartDate: date('contract_start_date').defaultNow(), //종료일 (기본값: 시작일로부터 1년 후) contractEndDate: date('contract_end_date'), // 일정 관리 preQuoteDate: date('pre_quote_date'), // 사전견적일 biddingRegistrationDate: date('bidding_registration_date'), // 입찰등록일 submissionStartDate: timestamp('submission_start_date'), // 입찰서제출기간 시작 submissionEndDate: timestamp('submission_end_date'), // 입찰서제출기간 끝 evaluationDate: timestamp('evaluation_date'), // 사양설명회 hasSpecificationMeeting: boolean('has_specification_meeting').default(false), // 예산 및 가격 정보 currency: varchar('currency', { length: 3 }).default('KRW'), // 통화 budget: decimal('budget', { precision: 15, scale: 2 }), // 예산 targetPrice: decimal('target_price', { precision: 15, scale: 2 }), // 내정가 targetPriceCalculationCriteria: text('target_price_calculation_criteria'), // 내정가 산정 기준 finalBidPrice: decimal('final_bid_price', { precision: 15, scale: 2 }), // 최종입찰가 // PR 정보 prNumber: varchar('pr_number', { length: 50 }), // PR No. hasPrDocument: boolean('has_pr_document').default(false), // PR 문서 여부 // 상태 및 설정 status: biddingStatusEnum('status').default('bidding_generated').notNull(), isPublic: boolean('is_public').default(false), // 공개 입찰 여부 isUrgent: boolean('is_urgent').default(false), // 긴급여부 // 구매조직 정보 purchasingOrganization: varchar('purchasing_organization', { length: 100 }), // 구매조직 // 담당자 정보 (개선된 구조) bidPicId: integer('bid_pic_id').references(() => users.id), // 입찰담당자 ID bidPicName: varchar('bid_pic_name', { length: 100 }), // 입찰담당자 이름 bidPicCode: varchar('bid_pic_code', { length: 50 }), // 입찰담당자 코드 supplyPicId: integer('supply_pic_id').references(() => users.id), // 조달담당자 ID supplyPicName: varchar('supply_pic_name', { length: 100 }), // 조달담당자 이름 supplyPicCode: varchar('supply_pic_code', { length: 50 }), // 조달담당자 코드 // 기존 담당자 정보 제거됨 (user FK로 대체) // 메타 정보 remarks: text('remarks'), // 비고 createdBy: varchar('created_by', { length: 100 }), // 생성자 createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), // 최종수정일 updatedBy: varchar('updated_by', { length: 100 }), // 최종수정자 // 개찰 정보 openedAt: timestamp('opened_at'), // 개찰일 openedBy: varchar('opened_by', { length: 100 }), // 개찰자 ANFNR: varchar({length: 50}).unique(), // 원본 ANFNR 추적을 위한 Bidding/RFQ Number (ECC), onConflict target이므로 unique 처리 }) // 9. 사양설명회 정보 테이블 export const specificationMeetings = pgTable('specification_meetings', { id: serial('id').primaryKey(), biddingId: integer('bidding_id').references(() => biddings.id).notNull(), meetingDate: timestamp('meeting_date').notNull(), meetingTime: varchar('meeting_time', { length: 20 }), location: varchar('location', { length: 500 }), address: text('address'), contactPerson: varchar('contact_person', { length: 100 }), contactPhone: varchar('contact_phone', { length: 20 }), contactEmail: varchar('contact_email', { length: 100 }), agenda: text('agenda'), // 회의 안건 materials: text('materials'), // 준비물 notes: text('notes'), // 특이사항 isRequired: boolean('is_required').default(false), // 필수 참석 여부 createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }) // 10. PR 문서 테이블 export const prDocuments = pgTable('pr_documents', { id: serial('id').primaryKey(), biddingId: integer('bidding_id').references(() => biddings.id).notNull(), documentName: varchar('document_name', { length: 300 }).notNull(), // 문서명 fileName: varchar('file_name', { length: 500 }).notNull(), originalFileName: varchar('original_file_name', { length: 500 }).notNull(), fileSize: integer('file_size'), // bytes mimeType: varchar('mime_type', { length: 100 }), filePath: varchar('file_path', { length: 1000 }).notNull(), // 실제 파일 경로 registeredAt: timestamp('registered_at').defaultNow().notNull(), // 등재일 registeredBy: varchar('registered_by', { length: 100 }).notNull(), // 등재자 description: text('description'), version: varchar('version', { length: 20 }), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }) // 11. PR 아이템 테이블 (상세 응찰 정보) export const prItemsForBidding = pgTable('pr_items_for_bidding', { id: serial('id').primaryKey(), biddingId: integer('bidding_id').references(() => biddings.id).notNull(), // 기본 정보 itemNumber: varchar('item_number', { length: 50 }), // 아이템 번호 projectId: integer('project_id').references(() => projects.id), // 프로젝트 ID (새로 추가) projectInfo: varchar('project_info', { length: 300 }), // 프로젝트 정보 (기존, 추후 제거 가능) itemInfo: varchar('item_info', { length: 300 }), // 품목정보 shi: varchar('shi', { length: 100 }), // SHI // 자재 그룹 정보 (새로 추가) materialGroupNumber: varchar('material_group_number', { length: 100 }), // 자재그룹번호 materialGroupInfo: varchar('material_group_info', { length: 300 }), // 자재그룹정보 // 자재 정보 (새로 추가) materialNumber: varchar('material_number', { length: 100 }), // 자재번호 materialInfo: varchar('material_info', { length: 500 }), // 자재명 // 납품 일정 requestedDeliveryDate: date('requested_delivery_date'), // 납품요청일 // 가격 정보 annualUnitPrice: decimal('annual_unit_price', { precision: 15, scale: 2 }), // 연간단가 currency: varchar('currency', { length: 3 }).default('KRW'), // 수량 및 중량 quantity: decimal('quantity', { precision: 10, scale: 3 }), // 수량 quantityUnit: varchar('quantity_unit', { length: 50 }), // 수량단위 (구매단위) totalWeight: decimal('total_weight', { precision: 10, scale: 3 }), // 총 중량 weightUnit: varchar('weight_unit', { length: 50 }), // 중량단위 (자재순중량) // 가격 단위 추가 priceUnit: varchar('price_unit', { length: 50 }), // 가격단위 purchaseUnit: varchar('purchase_unit', { length: 50 }), // 구매단위 materialWeight: decimal('material_weight', { precision: 10, scale: 3 }), // 자재순중량 // WBS 정보 wbsCode: varchar('wbs_code', { length: 100 }), // WBS 코드 wbsName: varchar('wbs_name', { length: 300 }), // WBS 명칭 // Cost Center 정보 costCenterCode: varchar('cost_center_code', { length: 100 }), // 코스트센터 코드 costCenterName: varchar('cost_center_name', { length: 300 }), // 코스트센터 명칭 // GL Account 정보 glAccountCode: varchar('gl_account_code', { length: 100 }), // GL 계정 코드 glAccountName: varchar('gl_account_name', { length: 300 }), // GL 계정 명칭 // 내정 정보 (새로 추가) targetUnitPrice: decimal('target_unit_price', { precision: 15, scale: 2 }), // 내정단가 targetAmount: decimal('target_amount', { precision: 15, scale: 2 }), // 내정금액 targetCurrency: varchar('target_currency', { length: 3 }).default('KRW'), // 내정통화 // 예산 정보 (새로 추가) budgetAmount: decimal('budget_amount', { precision: 15, scale: 2 }), // 예산금액 budgetCurrency: varchar('budget_currency', { length: 3 }).default('KRW'), // 예산통화 // 실적 정보 (새로 추가) actualAmount: decimal('actual_amount', { precision: 15, scale: 2 }), // 실적금액 actualCurrency: varchar('actual_currency', { length: 3 }).default('KRW'), // 실적통화 prNumber: varchar('pr_number', { length: 50 }), // PR번호 // SPEC 파일 정보 hasSpecDocument: boolean('has_spec_document').default(false), specification: varchar('specification', { length: 2000 }), // Specification createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }) // 12. 입찰 조건 테이블 (SHI 구매자가 제시하는 조건들) export const biddingConditions = pgTable('bidding_conditions', { id: serial('id').primaryKey(), biddingId: integer('bidding_id').references(() => biddings.id).notNull(), // 지급조건 paymentTerms: text('payment_terms'), // 지급조건 옵션들 // 세금 taxConditions: text('tax_conditions'), // Tax 옵션들 // 계약 및 납기 contractDeliveryDate: date('contract_delivery_date'), // 계약납기일 isPriceAdjustmentApplicable: boolean('is_price_adjustment_applicable'), // 연동제적용 여부 // 무역조건 incoterms: text('incoterms'), // Incoterms 옵션들 incotermsOption: text('incoterms_option'), // Incoterms 옵션 (추가) shippingPort: varchar('shipping_port', { length: 200 }), // 선적지 destinationPort: varchar('destination_port', { length: 200 }), // 하역지 // 기타 sparePartOptions: text('spare_part_options'), // Spare part 옵션들 createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }) // 13. 입찰 참여 업체 테이블2 export const biddingCompanies = pgTable('bidding_companies', { id: serial('id').primaryKey(), biddingId: integer('bidding_id').references(() => biddings.id).notNull(), companyId: integer('company_id').references(() => vendors.id).notNull(), // 초대 및 응답 상태 invitationStatus: invitationStatusEnum('invitation_status').notNull(), invitedAt: timestamp('invited_at'), respondedAt: timestamp('responded_at'), // 사전견적 정보 preQuoteAmount: decimal('pre_quote_amount', { precision: 15, scale: 2 }), preQuoteSubmittedAt: timestamp('pre_quote_submitted_at'), preQuoteDeadline: timestamp('pre_quote_deadline'), // 사전견적 마감일 isPreQuoteSelected: boolean('is_pre_quote_selected').default(false), // 본입찰 대상 선정 여부 isPreQuoteParticipated: boolean('is_pre_quote_participated'), // 사전견적 참여 여부 // 본입찰 정보 isBiddingInvited: boolean('is_bidding_invited').default(false), // 본입찰 초대 여부 isBiddingParticipated: boolean('is_bidding_participated'),//본입찰 참여 여부 finalQuoteAmount: decimal('final_quote_amount', { precision: 15, scale: 2 }), finalQuoteSubmittedAt: timestamp('final_quote_submitted_at'), isFinalSubmission: boolean('is_final_submission').default(false), // 최종제출 여부 // 견적 히스토리 스냅샷 (JSON 배열) quotationSnapshots: jsonb('quotation_snapshots'), // 응찰 시점의 품목별 견적 데이터 스냅샷 isWinner: boolean('is_winner'), // 낙찰 여부 isAttendingMeeting: boolean('is_attending_meeting'), // 사양설명회 참석 여부 awardRatio: decimal('award_ratio', { precision: 5, scale: 2 }), // 발주비율 //연동제 적용요건 문의 여부 isPriceAdjustmentApplicableQuestion: boolean('is_price_adjustment_applicable_question').default(false), // 연동제 적용요건 문의 여부 // 기타 notes: text('notes'), // 특이사항 contactPerson: varchar('contact_person', { length: 100 }), // 업체 담당자 contactEmail: varchar('contact_email', { length: 100 }), contactPhone: varchar('contact_phone', { length: 20 }), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }) // 13-1. 입찰 참여 업체 담당자 테이블 export const biddingCompaniesContacts = pgTable('bidding_companies_contacts', { id: serial('id').primaryKey(), biddingId: integer('bidding_id').references(() => biddings.id).notNull(), vendorId: integer('vendor_id').references(() => vendors.id).notNull(), contactName: varchar('contact_name', { length: 255 }).notNull(), contactEmail: varchar('contact_email', { length: 255 }).notNull(), contactNumber: varchar('contact_number', { length: 50 }), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }) // 14. 업체별 PR 아이템 응찰 정보 export const companyPrItemBids = pgTable('company_pr_item_bids', { id: serial('id').primaryKey(), biddingCompanyId: integer('bidding_company_id').references(() => biddingCompanies.id).notNull(), prItemId: integer('pr_item_id').references(() => prItemsForBidding.id).notNull(), // 업체 응찰 정보 proposedDeliveryDate: date('proposed_delivery_date'), // 납품예정일 bidUnitPrice: decimal('bid_unit_price', { precision: 15, scale: 2 }), // 입찰단가 bidAmount: decimal('bid_amount', { precision: 15, scale: 2 }), // 입찰금액 currency: varchar('currency', { length: 3 }).default('KRW'), // 기술 정보 technicalSpecification: text('technical_specification'), // 기술 사양 // 제출 정보 isPreQuote: boolean('is_pre_quote').default(false), // 사전견적 여부 submittedAt: timestamp('submitted_at').defaultNow().notNull(), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }) // 15. 업체별 입찰 조건 응답 export const companyConditionResponses = pgTable('company_condition_responses', { id: serial('id').primaryKey(), biddingCompanyId: integer('bidding_company_id').references(() => biddingCompanies.id).notNull(), // 각 조건에 대한 응답 (JSON 형태로 저장) paymentTermsResponse: varchar('payment_terms_response', { length: 200 }), // 선택된 지급조건 taxConditionsResponse: varchar('tax_conditions_response', { length: 200 }), // 선택된 Tax 조건 // 계약 및 납기 응답 proposedContractDeliveryDate: date('proposed_contract_delivery_date'), // 제안 계약납기일 priceAdjustmentResponse: boolean('price_adjustment_response'), // 연동제적용 응답 isInitialResponse: boolean('is_initial_response'), // 초도여부 응답 // 무역조건 응답 incotermsResponse: varchar('incoterms_response', { length: 100 }), // 선택된 Incoterms proposedShippingPort: varchar('proposed_shipping_port', { length: 200 }), // 제안 선적지 proposedDestinationPort: varchar('proposed_destination_port', { length: 200 }), // 제안 하역지 // 기타 응답 sparePartResponse: varchar('spare_part_response', { length: 200 }), // Spare part 응답 // 추가 제안사항 additionalProposals: text('additional_proposals'), // 추가 제안사항 // 제출 정보 isPreQuote: boolean('is_pre_quote').default(false), // 사전견적 여부 submittedAt: timestamp('submitted_at').defaultNow().notNull(), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }) // 16. 입찰 관련 문서 테이블 (강화) export const biddingDocuments = pgTable('bidding_documents', { id: serial('id').primaryKey(), biddingId: integer('bidding_id').references(() => biddings.id).notNull(), companyId: integer('company_id').references(() => vendors.id), // null이면 발주처 문서 prItemId: integer('pr_item_id').references(() => prItemsForBidding.id), // SPEC 문서인 경우 연결 specificationMeetingId: integer('specification_meeting_id').references(() => specificationMeetings.id), // 사양설명회 문서인 경우 연결 documentType: documentTypeEnum('document_type').notNull(), fileName: varchar('file_name', { length: 500 }).notNull(), originalFileName: varchar('original_file_name', { length: 500 }).notNull(), fileSize: integer('file_size'), // bytes mimeType: varchar('mime_type', { length: 100 }), filePath: varchar('file_path', { length: 1000 }).notNull(), // 실제 파일 경로 title: varchar('title', { length: 300 }), description: text('description'), // 접근 권한 isPublic: boolean('is_public').default(false), // 모든 참여업체 열람 가능 isRequired: boolean('is_required').default(false), // 필수 제출 문서 uploadedBy: varchar('uploaded_by', { length: 100 }), uploadedAt: timestamp('uploaded_at').defaultNow().notNull(), }) // 17. 업체 선정 결과 테이블 (평가 점수 대신 선정 사유) export const vendorSelectionResults = pgTable('vendor_selection_results', { id: serial('id').primaryKey(), biddingId: integer('bidding_id').references(() => biddings.id).notNull(), selectedCompanyId: integer('selected_company_id').references(() => vendors.id), // null이면 전체 선정결과 // 선정 사유 및 결과 selectionReason: text('selection_reason').notNull(), // 선정 사유 evaluationSummary: text('evaluation_summary'), // 평가 요약 // 선정 결과 관련 첨부파일들 hasResultDocuments: boolean('has_result_documents').default(false), // 선정 정보 selectedAt: timestamp('selected_at').defaultNow().notNull(), selectedBy: varchar('selected_by', { length: 100 }).notNull(), // 기타 remarks: text('remarks'), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }) // 19. 하도급대금 등 연동표 테이블 export const priceAdjustmentForms = pgTable('price_adjustment_forms', { id: serial('id').primaryKey(), // companyConditionResponses 테이블과 외래 키로 연결 companyConditionResponsesId: integer('company_condition_responses_id') .notNull() .references(() => companyConditionResponses.id), // 품목등의 명칭 itemName: varchar('item_name', { length: 255 }), // 조정대금 반영시점 adjustmentReflectionPoint: varchar('adjustment_reflection_point', { length: 255 }), // 연동대상 주요 원재료 majorApplicableRawMaterial: text('major_applicable_raw_material'), // 하도급대금등 연동 산식 adjustmentFormula: text('adjustment_formula'), // 원재료 가격 기준지표 rawMaterialPriceIndex: text('raw_material_price_index'), // 기준시점 및 비교시점 referenceDate: date('reference_date'), // 기준시점 comparisonDate: date('comparison_date'), // 비교시점 // 연동 비율 adjustmentRatio: decimal('adjustment_ratio', { precision: 5, scale: 2 }), // 소수점 2자리까지 // 기타 사항 notes: text('notes'), // 조정요건 adjustmentConditions: text('adjustment_conditions'), // 연동 미적용 주요 원재료 majorNonApplicableRawMaterial: text('major_non_applicable_raw_material'), // 조정주기 adjustmentPeriod: varchar('adjustment_period', { length: 100 }), // 수탁기업(협력사) 작성자 contractorWriter: varchar('contractor_writer', { length: 100 }), // 조정일 adjustmentDate: date('adjustment_date'), // 연동 미적용 사유 nonApplicableReason: text('non_applicable_reason'), // 메타 정보 createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }); // 타입 정의 export type Bidding = typeof biddings.$inferSelect & { // 추가 필드들 (쿼리 결과에 포함되므로 타입에도 추가) purchasingOrganization?: string | null bidPicId?: number | null bidPicName?: string | null supplyPicId?: number | null supplyPicName?: string | null noticeType?: string | null } export type NewBidding = typeof biddings.$inferInsert export type SpecificationMeeting = typeof specificationMeetings.$inferSelect export type NewSpecificationMeeting = typeof specificationMeetings.$inferInsert export type PrDocument = typeof prDocuments.$inferSelect export type NewPrDocument = typeof prDocuments.$inferInsert export type PrItem = typeof prItemsForBidding.$inferSelect export type NewPrItem = typeof prItemsForBidding.$inferInsert export type BiddingConditions = typeof biddingConditions.$inferSelect export type NewBiddingConditions = typeof biddingConditions.$inferInsert export type BiddingCompany = typeof biddingCompanies.$inferSelect export type NewBiddingCompany = typeof biddingCompanies.$inferInsert export type BiddingCompanyContact = typeof biddingCompaniesContacts.$inferSelect export type NewBiddingCompanyContact = typeof biddingCompaniesContacts.$inferInsert export type CompanyPrItemBid = typeof companyPrItemBids.$inferSelect export type NewCompanyPrItemBid = typeof companyPrItemBids.$inferInsert export type CompanyConditionResponse = typeof companyConditionResponses.$inferSelect export type NewCompanyConditionResponse = typeof companyConditionResponses.$inferInsert export type BiddingDocument = typeof biddingDocuments.$inferSelect export type NewBiddingDocument = typeof biddingDocuments.$inferInsert export type VendorSelectionResult = typeof vendorSelectionResults.$inferSelect export type NewVendorSelectionResult = typeof vendorSelectionResults.$inferInsert export type PriceAdjustmentForm = typeof priceAdjustmentForms.$inferSelect export type NewPriceAdjustmentForm = typeof priceAdjustmentForms.$inferInsert // 조인 타입 정의 (자주 사용될 것들) export type BiddingWithDetails = Bidding & { specificationMeeting?: SpecificationMeeting prDocuments: PrDocument[] prItemsForBidding: (PrItem & { specDocuments: BiddingDocument[] })[] biddingConditions?: BiddingConditions companies: (BiddingCompany & { company: Vendor prItemBids: CompanyPrItemBid[] conditionResponses: CompanyConditionResponse[] documents: BiddingDocument[] })[] documents: BiddingDocument[] selectionResult?: VendorSelectionResult } export type BiddingListItem = Bidding & { // 전체 참여 현황 participantExpected: number // 초대업체 수 participationRate: number // 참여율 (입찰 기준) // 사전견적 참여 현황 preQuotePending: number // 사전견적 초대 대기/발송 preQuoteAccepted: number // 사전견적 참여 preQuoteDeclined: number // 사전견적 미참여 preQuoteSubmitted: number // 사전견적 제출완료 // 입찰 참여 현황 biddingPending: number // 입찰 초대 발송 biddingAccepted: number // 입찰 참여 biddingDeclined: number // 입찰 미참여 biddingCancelled: number // 응찰 포기 biddingSubmitted: number // 응찰 완료 // 호환성을 위한 기존 필드 (deprecated) participantParticipated: number // 참여 participantDeclined: number // 포기 participantPending: number // 대기 participantAccepted: number // 수락 participantStats: { expected: number // 참여예정 participated: number // 참여 declined: number // 포기 } specificationMeeting?: SpecificationMeeting prDocuments: PrDocument[] } // 상태별 한글 매핑 export const biddingStatusLabels = { bidding_generated: '입찰생성', request_for_quotation: '사전견적요청', received_quotation: '사전견적접수', set_target_price: '내정가 산정', bidding_opened: '입찰공고', bidding_closed: '입찰마감', approval_pending: '결재 진행중', evaluation_of_bidding: '입찰평가중(개찰완료)', bidding_disposal: '유찰', vendor_selected: '업체선정', bid_opening: '개찰', early_bid_opening: '조기개찰', rebidding: '입찰종료-재입찰', disposal_cancelled: '유찰취소', bid_closure: '폐찰', round_increase: '입찰종료-차수증가', deleted: '삭제' } as const export const contractTypeLabels = { unit_price: '단가계약', general: '일반계약', sale: '매각계약' } as const export const biddingNoticeTypeLabels = { standard: '일반', facility: '시설재', unit_price: '단가계약' } as const export const biddingTypeLabels = { equipment: '기자재', construction: '공사', service: '용역', lease: '임차', transport: '운송', waste: '폐기물', sale: '매각', other: '기타(직접입력)' } as const export const awardCountLabels = { single: '단수', multiple: '복수' } as const export const quantityUnitLabels = { ea: '개', set: '세트', kg: 'kg', ton: 'ton', m: 'm', m2: 'm²', m3: 'm³', lot: 'lot', other: '기타' } as const export const weightUnitLabels = { kg: 'kg', ton: 'ton', lb: 'lb', g: 'g' } as const