summaryrefslogtreecommitdiff
path: root/lib/general-contracts/approval-actions.ts
blob: e75d6cd65ca53b79c5b3f6a20350fff71a4c71ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
 * 일반계약 관련 결재 서버 액션
 * 
 * 사용자가 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<string, unknown>;
  items: Record<string, unknown>[];
  subcontractChecklist: Record<string, unknown> | null;
  storageInfo?: Record<string, unknown>[];
  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;
}