/** * 일반계약 관련 결재 서버 액션 * * 사용자가 UI에서 호출하는 함수들 * ApprovalSubmissionSaga를 사용하여 결재 프로세스를 시작 */ 'use server'; import { ApprovalSubmissionSaga } from '@/lib/approval'; import { mapContractToApprovalTemplateVariables } from './approval-template-variables'; import { debugLog, debugError, debugSuccess } from '@/lib/debug-utils'; import db from '@/db/db'; import { eq } from 'drizzle-orm'; import { generalContracts } from '@/db/schema/generalContract'; import { users } from '@/db/schema'; interface ContractSummary { basicInfo: Record; items: Record[]; subcontractChecklist: Record | null; storageInfo?: Record[]; pdfPath?: string; basicContractPdfs?: Array<{ key: string; buffer: number[]; fileName: string }>; } /** * 결재를 거쳐 일반계약 승인 요청을 처리하는 서버 액션 * * 사용법 (클라이언트 컴포넌트에서): * ```typescript * const result = await requestContractApprovalWithApproval({ * contractId: 123, * contractSummary: summaryData, * currentUser: { id: 1, epId: 'EP001', email: 'user@example.com' }, * approvers: ['EP002', 'EP003'], * title: '계약 체결 진행 품의 요청서' * }); * * if (result.status === 'pending_approval') { * console.log('결재 ID:', result.approvalId); * } * ``` */ export async function requestContractApprovalWithApproval(data: { contractId: number; contractSummary: ContractSummary; currentUser: { id: number; epId: string | null; email?: string }; approvers?: string[]; // Knox EP ID 배열 (결재선) title?: string; // 결재 제목 (선택사항, 미지정 시 자동 생성) }) { debugLog('[ContractApproval] 일반계약 승인 요청 결재 서버 액션 시작', { contractId: data.contractId, contractNumber: data.contractSummary.basicInfo?.contractNumber, contractName: data.contractSummary.basicInfo?.name, userId: data.currentUser.id, hasEpId: !!data.currentUser.epId, }); // 입력 검증 if (!data.currentUser.epId) { debugError('[ContractApproval] Knox EP ID 없음'); throw new Error('Knox EP ID가 필요합니다'); } if (!data.contractId) { debugError('[ContractApproval] 계약 ID 없음'); throw new Error('계약 ID가 필요합니다'); } // 1. 유저의 nonsapUserId 조회 (Cronjob 환경을 위해) debugLog('[ContractApproval] nonsapUserId 조회'); const userResult = await db.query.users.findFirst({ where: eq(users.id, data.currentUser.id), columns: { nonsapUserId: true } }); const nonsapUserId = userResult?.nonsapUserId || null; debugLog('[ContractApproval] nonsapUserId 조회 완료', { nonsapUserId }); // 2. 템플릿 변수 매핑 debugLog('[ContractApproval] 템플릿 변수 매핑 시작'); const variables = await mapContractToApprovalTemplateVariables(data.contractSummary); debugLog('[ContractApproval] 템플릿 변수 매핑 완료', { variableKeys: Object.keys(variables), }); // 3. 결재 워크플로우 시작 (Saga 패턴) debugLog('[ContractApproval] ApprovalSubmissionSaga 생성'); const saga = new ApprovalSubmissionSaga( // actionType: 핸들러를 찾을 때 사용할 키 'general_contract_approval', // actionPayload: 결재 승인 후 핸들러에 전달될 데이터 { contractId: data.contractId, contractSummary: data.contractSummary, currentUser: { id: data.currentUser.id, email: data.currentUser.email, nonsapUserId: nonsapUserId, }, }, // approvalConfig: 결재 상신 정보 (템플릿 포함) { title: data.title || `계약 체결 진행 품의 요청서 - ${data.contractSummary.basicInfo?.contractNumber || data.contractId}`, description: `${data.contractSummary.basicInfo?.name || '일반계약'} 계약 체결 진행 품의 요청`, templateName: '일반계약 결재', // 한국어 템플릿명 variables, // 치환할 변수들 approvers: data.approvers, currentUser: data.currentUser, } ); debugLog('[ContractApproval] Saga 실행 시작'); const result = await saga.execute(); // 4. 결재 상신 성공 시 상태를 'approval_in_progress'로 변경 if (result.status === 'pending_approval') { debugLog('[ContractApproval] 상태를 approval_in_progress로 변경'); await db.update(generalContracts) .set({ status: 'approval_in_progress', lastUpdatedAt: new Date() }) .where(eq(generalContracts.id, data.contractId)); } debugSuccess('[ContractApproval] 결재 워크플로우 완료', { approvalId: result.approvalId, status: result.status, }); return result; }