summaryrefslogtreecommitdiff
path: root/lib/techsales-rfq/approval-actions.ts
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-11-18 10:33:51 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-11-18 10:33:51 +0000
commitbe5d5ab488ae875e7c56306403aba923e1784021 (patch)
treed43be7bf1204d6ba3862da24f7e2c2049d6c62fd /lib/techsales-rfq/approval-actions.ts
parent41ad2455ac47d8e2da331d7240ded1354df9a784 (diff)
(최겸) 기술영업 첨부파일 결재 등록 및 요구사항 개발
Diffstat (limited to 'lib/techsales-rfq/approval-actions.ts')
-rw-r--r--lib/techsales-rfq/approval-actions.ts244
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 재발송 결재 상신에 실패했습니다.'
+ );
+ }
+}
+