/** * PQ 실사 관련 결재 액션 핸들러 * * ✅ 베스트 프랙티스: * - 'use server' 지시어 없음 (순수 비즈니스 로직만) * - 결재 승인 후 실행될 최소한의 데이터만 처리 * - DB 조작 및 실제 비즈니스 로직만 포함 */ import { debugLog, debugError, debugSuccess } from '@/lib/debug-utils'; /** * PQ 실사 의뢰 핸들러 (결재 승인 후 실행됨) * * ✅ Internal 함수: 결재 워크플로우에서 자동 호출됨 (직접 호출 금지) * * @param payload - withApproval()에서 전달한 actionPayload (최소 데이터만) */ export async function requestPQInvestigationInternal(payload: { pqSubmissionIds: number[]; qmManagerId: number; forecastedAt: Date; investigationAddress: string; investigationNotes?: string; currentUserId: number; // ✅ 결재 상신한 사용자 ID }) { debugLog('[PQInvestigationHandler] 실사 의뢰 핸들러 시작', { pqCount: payload.pqSubmissionIds.length, qmManagerId: payload.qmManagerId, currentUserId: payload.currentUserId, }); // ✅ userId 검증: 핸들러에서 userId가 없으면 잘못된 상황 (예외 처리) if (!payload.currentUserId || payload.currentUserId <= 0) { const errorMessage = 'currentUserId가 없습니다. actionPayload에 currentUserId가 포함되지 않았습니다.'; debugError('[PQInvestigationHandler]', errorMessage); throw new Error(errorMessage); } // ✅ Date 문자열을 Date 객체로 변환 // DB의 jsonb에서 읽을 때 Date 객체가 ISO 문자열로 변환되어 있음 const forecastedAt = typeof payload.forecastedAt === 'string' ? new Date(payload.forecastedAt) : payload.forecastedAt; debugLog('[PQInvestigationHandler] Date 변환 완료', { originalType: typeof payload.forecastedAt, convertedType: typeof forecastedAt, forecastedAt: forecastedAt.toISOString(), }); try { // 기존 PQ 서비스 함수 사용 (DB 트랜잭션 포함) const { requestInvestigationAction } = await import('@/lib/pq/service'); debugLog('[PQInvestigationHandler] requestInvestigationAction 호출'); const result = await requestInvestigationAction( payload.pqSubmissionIds, { id: payload.currentUserId, epId: null, email: undefined }, // ✅ 실제 사용자 ID 전달 { qmManagerId: payload.qmManagerId, forecastedAt: forecastedAt, // ✅ Date 객체 전달 investigationAddress: payload.investigationAddress, investigationNotes: payload.investigationNotes, }, { skipRevalidation: true, // ✅ 폴링 컨텍스트에서는 revalidation 건너뛰기 } ); if (!result.success) { debugError('[PQInvestigationHandler] 실사 의뢰 실패', result.error); throw new Error(result.error || '실사 의뢰에 실패했습니다.'); } debugSuccess('[PQInvestigationHandler] 실사 의뢰 완료', { count: result.count, }); return { success: true, count: result.count, message: `${result.count}개 업체에 대해 실사가 의뢰되었습니다.`, }; } catch (error) { debugError('[PQInvestigationHandler] 실사 의뢰 중 에러', error); throw error; } } /** * PQ 실사 의뢰 데이터를 결재 템플릿 변수로 매핑 * * @param payload - 실사 의뢰 데이터 * @returns 템플릿 변수 객체 (Record) */ export async function mapPQInvestigationToTemplateVariables(payload: { vendorNames: string; // 여러 업체명 (쉼표로 구분) qmManagerName: string; qmManagerEmail?: string; forecastedAt: Date; investigationAddress: string; investigationNotes?: string; requestedAt: Date; }): Promise> { // 담당자 연락처 (QM담당자 이메일) const contactInfo = payload.qmManagerEmail ? `

${payload.qmManagerName}: ${payload.qmManagerEmail}

` : `

${payload.qmManagerName}

`; // 실사 사유/목적 (있으면 포함, 없으면 빈 문자열) const investigationPurpose = payload.investigationNotes || ''; return { 협력사명: payload.vendorNames, 실사요청일: new Date(payload.requestedAt).toLocaleDateString('ko-KR'), 실사예정일: new Date(payload.forecastedAt).toLocaleDateString('ko-KR'), 실사장소: payload.investigationAddress, QM담당자: payload.qmManagerName, 담당자연락처: contactInfo, 실사사유목적: investigationPurpose, }; } /** * PQ 실사 재의뢰 핸들러 (결재 승인 후 실행됨) * * ✅ Internal 함수: 결재 워크플로우에서 자동 호출됨 (직접 호출 금지) * * @param payload - withApproval()에서 전달한 actionPayload (최소 데이터만) */ export async function reRequestPQInvestigationInternal(payload: { investigationIds: number[]; currentUserId: number; // ✅ 결재 상신한 사용자 ID }) { debugLog('[PQReRequestHandler] 실사 재의뢰 핸들러 시작', { investigationCount: payload.investigationIds.length, currentUserId: payload.currentUserId, }); // ✅ userId 검증: 핸들러에서 userId가 없으면 잘못된 상황 (예외 처리) if (!payload.currentUserId || payload.currentUserId <= 0) { const errorMessage = 'currentUserId가 없습니다. actionPayload에 currentUserId가 포함되지 않았습니다.'; debugError('[PQReRequestHandler]', errorMessage); throw new Error(errorMessage); } try { // 기존 PQ 서비스 함수 사용 const { reRequestInvestigationAction } = await import('@/lib/pq/service'); debugLog('[PQReRequestHandler] reRequestInvestigationAction 호출'); const result = await reRequestInvestigationAction( payload.investigationIds, { id: payload.currentUserId }, // ✅ 실제 사용자 ID 전달 { skipRevalidation: true, // ✅ 폴링 컨텍스트에서는 revalidation 건너뛰기 } ); if (!result.success) { debugError('[PQReRequestHandler] 실사 재의뢰 실패', result.error); throw new Error(result.error || '실사 재의뢰에 실패했습니다.'); } debugSuccess('[PQReRequestHandler] 실사 재의뢰 완료', { count: result.count, }); return { success: true, count: result.count, message: `${result.count}개 업체에 대해 실사가 재의뢰되었습니다.`, }; } catch (error) { debugError('[PQReRequestHandler] 실사 재의뢰 중 에러', error); throw error; } } /** * PQ 실사 재의뢰 데이터를 결재 템플릿 변수로 매핑 * * @param payload - 실사 재의뢰 데이터 * @returns 템플릿 변수 객체 (Record) */ export async function mapPQReRequestToTemplateVariables(payload: { vendorNames: string; // 여러 업체명 (쉼표로 구분) investigationCount: number; canceledDate?: Date; reRequestedAt: Date; reason?: string; // 기존 실사 정보 (재의뢰 시 필요) forecastedAt?: Date; investigationAddress?: string; qmManagerName?: string; qmManagerEmail?: string; }): Promise> { // 실사요청일은 재의뢰 요청일로 설정 const requestDate = new Date(payload.reRequestedAt).toLocaleDateString('ko-KR'); // 실사예정일 (기존 실사 정보 사용, 없으면 빈 문자열) const forecastedDate = payload.forecastedAt ? new Date(payload.forecastedAt).toLocaleDateString('ko-KR') : ''; // 담당자 연락처 (QM담당자 이메일, 없으면 빈 문자열) const contactInfo = payload.qmManagerEmail ? `

${payload.qmManagerName || 'QM담당자'}: ${payload.qmManagerEmail}

` : '

담당자 정보 없음

'; // 실사 사유/목적 (재의뢰 사유, 있으면 포함, 없으면 빈 문자열) const investigationPurpose = payload.reason || ''; return { 협력사명: payload.vendorNames, 실사요청일: requestDate, 실사예정일: forecastedDate, 실사장소: payload.investigationAddress || '', QM담당자: payload.qmManagerName || '', 담당자연락처: contactInfo, 실사사유목적: investigationPurpose, }; }