import { pgTable, integer, varchar, text, timestamp, boolean, decimal, serial } from 'drizzle-orm/pg-core'; import { relations } from 'drizzle-orm'; import { basicContract } from './basicContractDocumnet'; import { users } from './users'; // 1. 설문조사 템플릿 (어떤 계약 타입에 어떤 설문을 적용할지) export const complianceSurveyTemplates = pgTable('compliance_survey_templates', { id: serial('id').primaryKey(), name: varchar('name', { length: 255 }).notNull(), // '기본 준법 설문', '금융업 준법 설문' 등 description: text('description'), version: varchar('version', { length: 50 }).notNull().default('1.0'), isActive: boolean('is_active').notNull().default(true), createdAt: timestamp('created_at').defaultNow(), updatedAt: timestamp('updated_at').defaultNow(), }); // 2. 설문 질문들 export const complianceQuestions = pgTable('compliance_questions', { id: serial('id').primaryKey(), templateId: integer('template_id').references(() => complianceSurveyTemplates.id).notNull(), questionNumber: varchar('question_number', { length: 10 }).notNull(), // '4', '10-1' 등 questionText: text('question_text').notNull(), questionType: varchar('question_type', { length: 20 }).notNull(), // 'RADIO', 'CHECKBOX', 'TEXT', 'TEXTAREA', 'DROPDOWN', 'FILE', 'PERCENTAGE', 'CONDITIONAL' isRequired: boolean('is_required').notNull().default(true), isRedFlag: boolean('is_red_flag').notNull().default(false), // 레드플래그 질문 여부 hasDetailText: boolean('has_detail_text').notNull().default(false), // 상세 기술 필요 여부 hasFileUpload: boolean('has_file_upload').notNull().default(false), // 첨부파일 필요 여부 parentQuestionId: integer('parent_question_id'), // 조건부 질문의 부모 conditionalValue: varchar('conditional_value', { length: 100 }), // 부모 질문의 어떤 답변에서 나타날지 displayOrder: integer('display_order').notNull(), createdAt: timestamp('created_at').defaultNow(), }); // 3. 선택형 질문의 옵션들 export const complianceQuestionOptions = pgTable('compliance_question_options', { id: serial('id').primaryKey(), questionId: integer('question_id').references(() => complianceQuestions.id).notNull(), optionValue: varchar('option_value', { length: 100 }).notNull(), // 'YES', 'NO', 'COMPANY_CORP' 등 optionText: varchar('option_text', { length: 255 }).notNull(), // '네', '아니오', '주식회사/유한회사' 등 allowsOtherInput: boolean('allows_other_input').notNull().default(false), // '기타' 선택 시 수기입력 가능 displayOrder: integer('display_order').notNull(), }); // 4. 설문 응답 (기본계약과 연결) export const complianceResponses = pgTable('compliance_responses', { id: serial('id').primaryKey(), basicContractId: integer('basic_contract_id').references(() => basicContract.id).notNull(), templateId: integer('template_id').references(() => complianceSurveyTemplates.id).notNull(), status: varchar('status', { length: 20 }).notNull().default('IN_PROGRESS'), // 'IN_PROGRESS', 'COMPLETED', 'REVIEWED' completedAt: timestamp('completed_at'), reviewedBy: integer('reviewed_by').references(() => users.id), // 검토자 reviewedAt: timestamp('reviewed_at'), reviewNotes: text('review_notes'), // 검토 의견 redFlagResolvedAt: timestamp('red_flag_resolved_at'), // RED FLAG 해제 일시 redFlagResolutionApprovalId: varchar('red_flag_resolution_approval_id', { length: 255 }), // RED FLAG 해소 합의 결재 ID (knox apInfId) createdAt: timestamp('created_at').defaultNow(), updatedAt: timestamp('updated_at').defaultNow(), }); // 5. 각 질문에 대한 답변 export const complianceResponseAnswers = pgTable('compliance_response_answers', { id: serial('id').primaryKey(), responseId: integer('response_id').references(() => complianceResponses.id).notNull(), questionId: integer('question_id').references(() => complianceQuestions.id).notNull(), answerValue: text('answer_value'), // 선택값 또는 입력값 detailText: text('detail_text'), // 상세 기술 내용 otherText: varchar('other_text', { length: 500 }), // '기타' 선택 시 수기입력 내용 percentageValue: decimal('percentage_value', { precision: 5, scale: 2 }), // 지분율 등 createdAt: timestamp('created_at').defaultNow(), updatedAt: timestamp('updated_at').defaultNow(), }); // 6. 응답에 첨부된 파일들 export const complianceResponseFiles = pgTable('compliance_response_files', { id: serial('id').primaryKey(), answerId: integer('answer_id').references(() => complianceResponseAnswers.id).notNull(), fileName: varchar('file_name', { length: 255 }).notNull(), filePath: varchar('file_path', { length: 1024 }).notNull(), fileSize: integer('file_size'), mimeType: varchar('mime_type', { length: 100 }), uploadedAt: timestamp('uploaded_at').defaultNow(), }); // 7. 레드플래그 담당자 관리 export const redFlagManagers = pgTable('red_flag_managers', { id: serial('id').primaryKey(), purchasingManagerId: integer('purchasing_manager_id').references(() => users.id), // 구매기획 담당자 complianceManagerId: integer('compliance_manager_id').references(() => users.id), // 준법 담당자 createdAt: timestamp('created_at').defaultNow(), updatedAt: timestamp('updated_at').defaultNow(), }); // Relations 정의 export const complianceSurveyTemplatesRelations = relations(complianceSurveyTemplates, ({ many }) => ({ questions: many(complianceQuestions), responses: many(complianceResponses), })); export const complianceQuestionsRelations = relations(complianceQuestions, ({ one, many }) => ({ template: one(complianceSurveyTemplates, { fields: [complianceQuestions.templateId], references: [complianceSurveyTemplates.id], }), parentQuestion: one(complianceQuestions, { fields: [complianceQuestions.parentQuestionId], references: [complianceQuestions.id], }), childQuestions: many(complianceQuestions), options: many(complianceQuestionOptions), answers: many(complianceResponseAnswers), })); export const complianceQuestionOptionsRelations = relations(complianceQuestionOptions, ({ one }) => ({ question: one(complianceQuestions, { fields: [complianceQuestionOptions.questionId], references: [complianceQuestions.id], }), })); export const complianceResponsesRelations = relations(complianceResponses, ({ one, many }) => ({ basicContract: one(basicContract, { fields: [complianceResponses.basicContractId], references: [basicContract.id], }), template: one(complianceSurveyTemplates, { fields: [complianceResponses.templateId], references: [complianceSurveyTemplates.id], }), answers: many(complianceResponseAnswers), })); export const complianceResponseAnswersRelations = relations(complianceResponseAnswers, ({ one, many }) => ({ response: one(complianceResponses, { fields: [complianceResponseAnswers.responseId], references: [complianceResponses.id], }), question: one(complianceQuestions, { fields: [complianceResponseAnswers.questionId], references: [complianceQuestions.id], }), files: many(complianceResponseFiles), })); export const complianceResponseFilesRelations = relations(complianceResponseFiles, ({ one }) => ({ answer: one(complianceResponseAnswers, { fields: [complianceResponseFiles.answerId], references: [complianceResponseAnswers.id], }), })); export const redFlagManagersRelations = relations(redFlagManagers, ({ one }) => ({ purchasingManager: one(users, { fields: [redFlagManagers.purchasingManagerId], references: [users.id], }), complianceManager: one(users, { fields: [redFlagManagers.complianceManagerId], references: [users.id], }), })); // 타입 정의 export type ComplianceSurveyTemplate = typeof complianceSurveyTemplates.$inferSelect; export type ComplianceQuestion = typeof complianceQuestions.$inferSelect; export type ComplianceQuestionOption = typeof complianceQuestionOptions.$inferSelect; export type ComplianceResponse = typeof complianceResponses.$inferSelect; export type ComplianceResponseAnswer = typeof complianceResponseAnswers.$inferSelect; export type ComplianceResponseFile = typeof complianceResponseFiles.$inferSelect; export type RedFlagManager = typeof redFlagManagers.$inferSelect; // Insert 타입 export type NewComplianceSurveyTemplate = typeof complianceSurveyTemplates.$inferInsert; export type NewComplianceQuestion = typeof complianceQuestions.$inferInsert; export type NewComplianceQuestionOption = typeof complianceQuestionOptions.$inferInsert; export type NewComplianceResponse = typeof complianceResponses.$inferInsert; export type NewComplianceResponseAnswer = typeof complianceResponseAnswers.$inferInsert; export type NewComplianceResponseFile = typeof complianceResponseFiles.$inferInsert; export type NewRedFlagManager = typeof redFlagManagers.$inferInsert; // 질문 타입 상수 export const QUESTION_TYPES = { RADIO: 'RADIO', // 라디오 버튼 (단일 선택) CHECKBOX: 'CHECKBOX', // 체크박스 (다중 선택) TEXTAREA: 'TEXTAREA', // 다중 라인 텍스트 DROPDOWN: 'DROPDOWN', // 드롭다운 선택 FILE: 'FILE', // 파일 업로드 PERCENTAGE: 'PERCENTAGE', // 지분율 등 퍼센트 입력 CONDITIONAL: 'CONDITIONAL', // 조건부 질문 } as const; // 응답 상태 상수 export const RESPONSE_STATUS = { IN_PROGRESS: 'IN_PROGRESS', COMPLETED: 'COMPLETED', REVIEWED: 'REVIEWED', } as const;