diff options
Diffstat (limited to 'lib/techsales-rfq/approval-actions.ts')
| -rw-r--r-- | lib/techsales-rfq/approval-actions.ts | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/lib/techsales-rfq/approval-actions.ts b/lib/techsales-rfq/approval-actions.ts new file mode 100644 index 00000000..175bca1d --- /dev/null +++ b/lib/techsales-rfq/approval-actions.ts @@ -0,0 +1,244 @@ +/** + * 기술영업 RFQ 발송 결재 서버 액션 + * + * DRM 파일이 있는 기술영업 RFQ를 발송할 때 결재를 거치는 서버 액션 + */ + +'use server'; + +import { ApprovalSubmissionSaga } from '@/lib/approval'; +import { mapTechSalesRfqSendToTemplateVariables } from './approval-handlers'; + +interface TechSalesRfqSendApprovalData { + // RFQ 기본 정보 + rfqId: number; + rfqCode?: string; + + // 발송 데이터 + vendorIds: number[]; + selectedContacts?: Array<{ + vendorId: number; + contactId: number; + contactEmail: string; + contactName: string; + }>; + drmAttachmentIds: number[]; + + // 첨부파일 정보 (파일명, 크기 등) + drmAttachments: Array<{ + fileName?: string | null; + fileSize?: number | null; + }>; + + // 신청 사유 + applicationReason: string; + + // 결재 정보 + currentUser: { + id: number; + epId: string | null; + name?: string; + email?: string; + }; + approvers?: string[]; // Knox EP ID 배열 +} + +/** + * 기술영업 RFQ 발송 결재 상신 (초기 발송) + * + * DRM 파일이 있는 경우 결재를 거쳐 RFQ를 발송합니다. + */ +export async function requestTechSalesRfqSendWithApproval(data: TechSalesRfqSendApprovalData) { + // 1. 입력 검증 + if (!data.currentUser.epId) { + throw new Error('Knox EP ID가 필요합니다. 시스템 관리자에게 문의하세요.'); + } + + if (!data.vendorIds || data.vendorIds.length === 0) { + throw new Error('발송할 벤더를 선택해주세요.'); + } + + if (!data.drmAttachmentIds || data.drmAttachmentIds.length === 0) { + throw new Error('DRM 첨부파일이 없습니다. 결재가 필요하지 않습니다.'); + } + + console.log('[TechSales RFQ Approval] Starting approval process for RFQ send'); + console.log('[TechSales RFQ Approval] RFQ ID:', data.rfqId); + console.log('[TechSales RFQ Approval] Vendors:', data.vendorIds.length); + console.log('[TechSales RFQ Approval] DRM Attachments:', data.drmAttachmentIds.length); + + try { + // 2. RFQ 상태를 "결재 진행중"으로 변경 + const db = (await import('@/db/db')).default; + const { techSalesRfqs, TECH_SALES_RFQ_STATUSES } = await import('@/db/schema/techSales'); + const { eq } = await import('drizzle-orm'); + + await db.update(techSalesRfqs) + .set({ + status: TECH_SALES_RFQ_STATUSES.APPROVAL_IN_PROGRESS, + updatedAt: new Date(), + }) + .where(eq(techSalesRfqs.id, data.rfqId)); + + console.log('[TechSales RFQ Approval] RFQ status updated to APPROVAL_IN_PROGRESS'); + + // 3. 벤더 이름 조회 + const { getTechSalesRfqVendors } = await import('./service'); + const vendorsResult = await getTechSalesRfqVendors(data.rfqId); + const vendorNames = vendorsResult.data?.filter(v => data.vendorIds.includes(v.vendorId)) + .map(v => v.vendorName) || []; + + // 4. 템플릿 변수 매핑 + const variables = await mapTechSalesRfqSendToTemplateVariables({ + attachments: data.drmAttachments, + vendorNames: vendorNames, + applicationReason: data.applicationReason, + }); + + // 5. 결재 상신용 payload 구성 + const approvalPayload = { + rfqId: data.rfqId, + rfqCode: data.rfqCode, + vendorIds: data.vendorIds, + selectedContacts: data.selectedContacts, + drmAttachmentIds: data.drmAttachmentIds, + currentUser: { + id: data.currentUser.id, + name: data.currentUser.name, + email: data.currentUser.email, + epId: data.currentUser.epId, + }, + }; + + // 6. Saga로 결재 상신 + const saga = new ApprovalSubmissionSaga( + 'tech_sales_rfq_send_with_drm', // 핸들러 키 + approvalPayload, // 결재 승인 후 실행될 데이터 + { + title: `암호화해제 신청 - ${data.rfqCode || 'RFQ'}`, + description: `${vendorNames.length}개 업체에 DRM 첨부파일 ${data.drmAttachmentIds.length}개를 포함한 암호화해제 신청`, + templateName: '암호화해제 신청', // DB에 있어야 함 + variables, + approvers: data.approvers, + currentUser: { + id: data.currentUser.id, + epId: data.currentUser.epId, + email: data.currentUser.email, + }, + } + ); + + const result = await saga.execute(); + + console.log('[TechSales RFQ Approval] ✅ Approval submitted successfully'); + console.log('[TechSales RFQ Approval] Approval ID:', result.approvalId); + console.log('[TechSales RFQ Approval] Pending Action ID:', result.pendingActionId); + + return { + success: true, + ...result, + message: `결재가 상신되었습니다. (결재 ID: ${result.approvalId})`, + }; + + } catch (error) { + console.error('[TechSales RFQ Approval] ❌ Failed to submit approval:', error); + throw new Error( + error instanceof Error + ? error.message + : '기술영업 RFQ 발송 결재 상신에 실패했습니다.' + ); + } +} + +/** + * 기술영업 RFQ 재발송 결재 상신 + * + * 이미 발송된 RFQ에 DRM 파일이 추가된 경우 재발송을 위한 결재 상신 + */ +export async function requestRfqResendWithDrmApproval(data: { + rfqId: number; + rfqCode?: string; + drmFiles: Array<{ + file: File; + attachmentType: string; + description?: string; + }>; + applicationReason: string; + currentUser: { + id: number; + epId: string | null; + name?: string; + email?: string; + }; + approvers?: string[]; +}) { + if (!data.currentUser.epId) { + throw new Error('Knox EP ID가 필요합니다.'); + } + + console.log('[RFQ Resend Approval] Starting resend approval process'); + console.log('[RFQ Resend Approval] RFQ ID:', data.rfqId); + console.log('[RFQ Resend Approval] DRM Files:', data.drmFiles.length); + + try { + // 템플릿 변수 매핑 + const variables = await mapTechSalesRfqSendToTemplateVariables({ + attachments: data.drmFiles.map(f => ({ + fileName: f.file.name, + fileSize: f.file.size, + })), + vendorNames: [], // 기존 벤더 목록은 후처리에서 조회 + applicationReason: data.applicationReason, + }); + + // 결재 payload 구성 + const approvalPayload = { + rfqId: data.rfqId, + rfqCode: data.rfqCode, + drmFiles: data.drmFiles, + currentUser: { + id: data.currentUser.id, + name: data.currentUser.name, + email: data.currentUser.email, + epId: data.currentUser.epId, + }, + }; + + // Saga로 결재 상신 + const saga = new ApprovalSubmissionSaga( + 'tech_sales_rfq_resend_with_drm', // 핸들러 키 + approvalPayload, + { + title: `DRM 파일 재발송 결재 - ${data.rfqCode || 'RFQ'}`, + description: `이미 발송된 RFQ에 ${data.drmFiles.length}개의 DRM 파일이 추가되어 재발송을 요청합니다.`, + templateName: '암호화해제 신청', + variables, + approvers: data.approvers, + currentUser: { + id: data.currentUser.id, + epId: data.currentUser.epId, + email: data.currentUser.email, + }, + } + ); + + const result = await saga.execute(); + + console.log('[RFQ Resend Approval] ✅ Resend approval submitted successfully'); + + return { + success: true, + ...result, + message: `재발송 결재가 상신되었습니다. (결재 ID: ${result.approvalId})`, + }; + + } catch (error) { + console.error('[RFQ Resend Approval] ❌ Failed to submit resend approval:', error); + throw new Error( + error instanceof Error + ? error.message + : 'RFQ 재발송 결재 상신에 실패했습니다.' + ); + } +} + |
