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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
/**
* RFQ 발송 결재 핸들러
*
* 첨부파일이 있는 RFQ 발송 시 결재 승인 후 실제 발송을 처리하는 핸들러
*/
'use server';
import { sendRfqToVendors } from './service';
/**
* RFQ 발송 핸들러 (결재 승인 후 자동 실행)
*
* @param payload - 결재 상신 시 저장한 RFQ 발송 데이터
*/
export async function sendRfqWithApprovalInternal(payload: {
rfqId: number;
rfqCode?: string;
vendors: Array<{
vendorId: number;
vendorName: string;
vendorCode?: string | null;
vendorCountry?: string | null;
selectedMainEmail: string;
additionalEmails: string[];
customEmails?: Array<{ email: string; name?: string }>;
currency?: string | null;
contractRequirements?: {
ndaYn: boolean;
generalGtcYn: boolean;
projectGtcYn: boolean;
agreementYn: boolean;
projectCode?: string;
};
isResend: boolean;
sendVersion?: number;
}>;
attachmentIds: number[];
message?: string;
generatedPdfs?: Array<{
key: string;
buffer: number[];
fileName: string;
}>;
hasToSendEmail?: boolean;
currentUser: { // cronjob 환경을 위한 필수 정보
id: string | number;
name?: string | null;
email?: string | null;
epId?: string | null;
};
}) {
console.log('[RFQ Approval Handler] Starting RFQ send after approval');
console.log('[RFQ Approval Handler] RFQ ID:', payload.rfqId);
console.log('[RFQ Approval Handler] Vendors count:', payload.vendors.length);
console.log('[RFQ Approval Handler] Attachments count:', payload.attachmentIds.length);
console.log('[RFQ Approval Handler] User ID:', payload.currentUser.id);
try {
// 결재 승인 후 실제 RFQ 발송 처리
// currentUser를 payload에서 전달 (cronjob 환경에서는 session 없음)
const result = await sendRfqToVendors({
rfqId: payload.rfqId,
rfqCode: payload.rfqCode,
vendors: payload.vendors,
attachmentIds: payload.attachmentIds,
message: payload.message,
generatedPdfs: payload.generatedPdfs,
hasToSendEmail: payload.hasToSendEmail ?? true,
currentUser: payload.currentUser, // ✅ payload의 currentUser 전달
});
console.log('[RFQ Approval Handler] ✅ RFQ sent successfully');
console.log('[RFQ Approval Handler] Total sent:', result.totalSent);
console.log('[RFQ Approval Handler] Total contracts:', result.totalContracts);
return {
success: true,
...result,
};
} catch (error) {
console.error('[RFQ Approval Handler] ❌ Failed to send RFQ:', error);
throw new Error(
error instanceof Error
? `RFQ 발송 실패: ${error.message}`
: 'RFQ 발송 중 오류가 발생했습니다.'
);
}
}
/**
* 템플릿 변수 매핑 함수
* RFQ 발송 정보를 결재 템플릿 변수로 변환
*/
export async function mapRfqSendToTemplateVariables(data: {
attachments: Array<{
fileName?: string | null;
fileSize?: number | null;
}>;
vendorNames: string[];
applicationReason: string;
}) {
const { htmlTableConverter, htmlListConverter } = await import('@/lib/approval/template-utils');
// 파일 크기를 읽기 쉬운 형식으로 변환
const formatFileSize = (bytes?: number | null): string => {
if (!bytes || bytes === 0) return '-';
const units = ['B', 'KB', 'MB', 'GB'];
let size = bytes;
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return `${size.toFixed(2)} ${units[unitIndex]}`;
};
// 첨부파일 테이블 데이터 준비 (순번, 파일명, 파일크기만)
const attachmentTableData = data.attachments.map((att, index) => ({
'순번': String(index + 1),
'파일명': att.fileName || '-',
'파일 크기': formatFileSize(att.fileSize),
}));
// 첨부파일 테이블 HTML 생성
const attachmentTableHtml = await htmlTableConverter(
attachmentTableData.length > 0 ? attachmentTableData : [],
[
{ key: '순번', label: '순번' },
{ key: '파일명', label: '파일명' },
{ key: '파일 크기', label: '파일 크기' },
]
);
// 제출처 (벤더 이름들) HTML 생성
const vendorListHtml = await htmlListConverter(
data.vendorNames.length > 0
? data.vendorNames
: ['제출처가 없습니다.']
);
return {
'파일 테이블': attachmentTableHtml,
'제출처': vendorListHtml,
'신청사유': data.applicationReason || '사유 없음',
};
}
|