/** * PQ 실사 관련 결재 서버 액션 * * ✅ 베스트 프랙티스: * - 'use server' 지시어 포함 (서버 액션) * - UI에서 호출하는 진입점 함수들 * - withApproval()을 사용하여 결재 프로세스 시작 * - 템플릿 변수 준비 및 입력 검증 * - 핸들러(Internal)에는 최소 데이터만 전달 */ 'use server'; import { withApproval } from '@/lib/approval/approval-workflow'; import { mapPQInvestigationToTemplateVariables } from './handlers'; import { debugLog, debugError, debugSuccess } from '@/lib/debug-utils'; /** * 결재를 거쳐 PQ 실사 의뢰를 생성하는 서버 액션 * * ✅ 사용법 (클라이언트 컴포넌트에서): * ```typescript * const result = await requestPQInvestigationWithApproval({ * pqSubmissionIds: [1, 2, 3], * vendorNames: "협력사A, 협력사B", * qmManagerId: 123, * qmManagerName: "홍길동", * qmManagerEmail: "hong@example.com", * forecastedAt: new Date('2025-11-01'), * investigationAddress: "경기도 ...", * investigationNotes: "...", * currentUser: { id: 1, epId: 'EP001', email: 'user@example.com' }, * approvers: ['EP002', 'EP003'] * }); * * if (result.status === 'pending_approval') { * toast.success(`결재가 상신되었습니다. (ID: ${result.approvalId})`); * } * ``` */ export async function requestPQInvestigationWithApproval(data: { pqSubmissionIds: number[]; vendorNames: string; // 여러 업체명 (쉼표로 구분) qmManagerId: number; qmManagerName: string; qmManagerEmail?: string; forecastedAt: Date; investigationAddress: string; investigationNotes?: string; currentUser: { id: number; epId: string | null; email?: string }; approvers?: string[]; // Knox EP ID 배열 (결재선) }) { debugLog('[PQInvestigationApproval] 실사 의뢰 결재 서버 액션 시작', { pqCount: data.pqSubmissionIds.length, vendorNames: data.vendorNames, qmManagerId: data.qmManagerId, qmManagerName: data.qmManagerName, hasQmEmail: !!data.qmManagerEmail, userId: data.currentUser.id, hasEpId: !!data.currentUser.epId, }); // 1. 입력 검증 if (!data.currentUser.epId) { debugError('[PQInvestigationApproval] Knox EP ID 없음'); throw new Error('Knox EP ID가 필요합니다'); } if (data.pqSubmissionIds.length === 0) { debugError('[PQInvestigationApproval] PQ 제출 ID 없음'); throw new Error('실사 의뢰할 PQ를 선택해주세요'); } // 2. 템플릿 변수 매핑 debugLog('[PQInvestigationApproval] 템플릿 변수 매핑 시작'); const requestedAt = new Date(); const variables = await mapPQInvestigationToTemplateVariables({ vendorNames: data.vendorNames, qmManagerName: data.qmManagerName, qmManagerEmail: data.qmManagerEmail, forecastedAt: data.forecastedAt, investigationAddress: data.investigationAddress, investigationNotes: data.investigationNotes, requestedAt, }); debugLog('[PQInvestigationApproval] 템플릿 변수 매핑 완료', { variableKeys: Object.keys(variables), }); // 3. 결재 워크플로우 시작 debugLog('[PQInvestigationApproval] withApproval 호출'); const result = await withApproval( // actionType: 핸들러를 찾을 때 사용할 키 'pq_investigation_request', // actionPayload: 결재 승인 후 핸들러에 전달될 데이터 (최소 데이터만) { pqSubmissionIds: data.pqSubmissionIds, qmManagerId: data.qmManagerId, forecastedAt: data.forecastedAt, investigationAddress: data.investigationAddress, investigationNotes: data.investigationNotes, }, // approvalConfig: 결재 상신 정보 (템플릿 포함) { title: `Vendor 실사의뢰 - ${data.vendorNames}`, description: `${data.vendorNames}에 대한 실사 의뢰`, templateName: 'Vendor 실사의뢰', // 한국어 템플릿명 variables, // 치환할 변수들 approvers: data.approvers, currentUser: data.currentUser, } ); debugSuccess('[PQInvestigationApproval] 결재 워크플로우 완료', { approvalId: result.approvalId, pendingActionId: result.pendingActionId, status: result.status, }); return result; } /** * 결재를 거쳐 PQ 실사 재의뢰를 처리하는 서버 액션 * * ✅ 사용법 (클라이언트 컴포넌트에서): * ```typescript * const result = await reRequestPQInvestigationWithApproval({ * investigationIds: [1, 2, 3], * vendorNames: "협력사A, 협력사B", * currentUser: { id: 1, epId: 'EP001', email: 'user@example.com' }, * approvers: ['EP002', 'EP003'], * reason: '재의뢰 사유...' * }); * * if (result.status === 'pending_approval') { * toast.success(`재의뢰 결재가 상신되었습니다. (ID: ${result.approvalId})`); * } * ``` */ export async function reRequestPQInvestigationWithApproval(data: { investigationIds: number[]; vendorNames: string; // 여러 업체명 (쉼표로 구분) currentUser: { id: number; epId: string | null; email?: string }; approvers?: string[]; // Knox EP ID 배열 (결재선) reason?: string; // 재의뢰 사유 }) { debugLog('[PQReRequestApproval] 실사 재의뢰 결재 서버 액션 시작', { investigationCount: data.investigationIds.length, vendorNames: data.vendorNames, userId: data.currentUser.id, hasEpId: !!data.currentUser.epId, hasReason: !!data.reason, }); // 1. 입력 검증 if (!data.currentUser.epId) { debugError('[PQReRequestApproval] Knox EP ID 없음'); throw new Error('Knox EP ID가 필요합니다'); } if (data.investigationIds.length === 0) { debugError('[PQReRequestApproval] 실사 ID 없음'); throw new Error('재의뢰할 실사를 선택해주세요'); } // 2. 기존 실사 정보 조회 (첫 번째 실사 기준 - 템플릿 변수용) debugLog('[PQReRequestApproval] 기존 실사 정보 조회'); const { default: db } = await import('@/db/db'); const { vendorInvestigations, users } = await import('@/db/schema'); const { eq } = await import('drizzle-orm'); const results = await db .select({ id: vendorInvestigations.id, forecastedAt: vendorInvestigations.forecastedAt, investigationAddress: vendorInvestigations.investigationAddress, qmManagerId: vendorInvestigations.qmManagerId, qmManagerName: users.name, qmManagerEmail: users.email, }) .from(vendorInvestigations) .leftJoin(users, eq(vendorInvestigations.qmManagerId, users.id)) .where(eq(vendorInvestigations.id, data.investigationIds[0])) .limit(1); const existingInvestigation = results[0]; if (!existingInvestigation) { debugError('[PQReRequestApproval] 기존 실사 정보를 찾을 수 없음'); throw new Error('기존 실사 정보를 찾을 수 없습니다.'); } debugLog('[PQReRequestApproval] 기존 실사 정보 조회 완료', { investigationId: existingInvestigation.id, qmManagerName: existingInvestigation.qmManagerName, forecastedAt: existingInvestigation.forecastedAt, }); // 3. 템플릿 변수 매핑 debugLog('[PQReRequestApproval] 템플릿 변수 매핑 시작'); const reRequestedAt = new Date(); const { mapPQReRequestToTemplateVariables } = await import('./handlers'); const variables = await mapPQReRequestToTemplateVariables({ vendorNames: data.vendorNames, investigationCount: data.investigationIds.length, reRequestedAt, reason: data.reason, // 기존 실사 정보 전달 (템플릿 표시용) forecastedAt: existingInvestigation.forecastedAt || undefined, investigationAddress: existingInvestigation.investigationAddress || undefined, qmManagerName: existingInvestigation.qmManagerName || undefined, qmManagerEmail: existingInvestigation.qmManagerEmail || undefined, }); debugLog('[PQReRequestApproval] 템플릿 변수 매핑 완료', { variableKeys: Object.keys(variables), }); // 4. 결재 워크플로우 시작 debugLog('[PQReRequestApproval] withApproval 호출'); const result = await withApproval( // actionType: 핸들러를 찾을 때 사용할 키 'pq_investigation_rerequest', // actionPayload: 결재 승인 후 핸들러에 전달될 데이터 (최소 데이터만) { investigationIds: data.investigationIds, }, // approvalConfig: 결재 상신 정보 (템플릿 포함) { title: `Vendor 실사 재의뢰 - ${data.vendorNames}`, description: `${data.vendorNames}에 대한 실사 재의뢰`, templateName: 'Vendor 실사 재의뢰', // 한국어 템플릿명 variables, // 치환할 변수들 approvers: data.approvers, currentUser: data.currentUser, } ); debugSuccess('[PQReRequestApproval] 재의뢰 결재 워크플로우 완료', { approvalId: result.approvalId, pendingActionId: result.pendingActionId, status: result.status, }); return result; }