summaryrefslogtreecommitdiff
path: root/lib/techsales-rfq
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-11-24 02:52:34 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-11-24 02:52:34 +0000
commit58d700b925967bfe470c944b380b02b2140cab8a (patch)
treee445dc37215f048759542e198cf6c5f41ebb0d5c /lib/techsales-rfq
parent26365ef08588d53b8c5d9c7cfaefb244536e6743 (diff)
(최겸) 구매 기술영업 결재 개발, 입찰 수정
Diffstat (limited to 'lib/techsales-rfq')
-rw-r--r--lib/techsales-rfq/approval-handlers.ts10
-rw-r--r--lib/techsales-rfq/service.ts76
-rw-r--r--lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx62
3 files changed, 87 insertions, 61 deletions
diff --git a/lib/techsales-rfq/approval-handlers.ts b/lib/techsales-rfq/approval-handlers.ts
index 6ffd9fb4..0d4629e4 100644
--- a/lib/techsales-rfq/approval-handlers.ts
+++ b/lib/techsales-rfq/approval-handlers.ts
@@ -98,15 +98,7 @@ export async function sendTechSalesRfqWithApprovalInternal(payload: {
}
}
- // 2. RFQ 상태를 "RFQ Sent"로 변경
- await db.update(techSalesRfqs)
- .set({
- status: TECH_SALES_RFQ_STATUSES.RFQ_SENT,
- updatedAt: new Date(),
- })
- .where(eq(techSalesRfqs.id, payload.rfqId));
-
- // 3. 실제 RFQ 발송 실행
+ // 2. 실제 RFQ 발송 실행 (상태 변경과 이메일 발송은 sendTechSalesRfqToVendors에서 처리)
const sendResult = await sendTechSalesRfqToVendors({
rfqId: payload.rfqId,
vendorIds: payload.vendorIds,
diff --git a/lib/techsales-rfq/service.ts b/lib/techsales-rfq/service.ts
index dc5950e0..ed3472b1 100644
--- a/lib/techsales-rfq/service.ts
+++ b/lib/techsales-rfq/service.ts
@@ -502,7 +502,45 @@ export async function getTechSalesRfqVendors(rfqId: number) {
}
/**
+ * 기술영업 RFQ 첨부파일 중 DRM 해제 여부 확인
+ */
+export async function checkTechSalesRfqHasDrmAttachments(rfqId: number) {
+ unstable_noStore();
+ try {
+ const attachments = await db.query.techSalesAttachments.findMany({
+ where: eq(techSalesAttachments.techSalesRfqId, rfqId),
+ columns: {
+ id: true,
+ drmEncrypted: true,
+ fileName: true,
+ fileSize: true,
+ originalFileName: true,
+ }
+ });
+
+ const drmAttachments = attachments.filter(att => att.drmEncrypted === true);
+
+ return {
+ hasDrm: drmAttachments.length > 0,
+ drmAttachmentIds: drmAttachments.map(att => att.id),
+ drmAttachments: drmAttachments.map(att => ({
+ fileName: att.originalFileName || att.fileName,
+ fileSize: att.fileSize,
+ })),
+ };
+ } catch (err) {
+ console.error("기술영업 RFQ DRM 첨부파일 확인 오류:", err);
+ return {
+ hasDrm: false,
+ drmAttachmentIds: [],
+ drmAttachments: [],
+ };
+ }
+}
+
+/**
* 기술영업 RFQ 발송 (선택된 벤더들의 선택된 contact들에게)
+ * DRM 해제가 필요없는 경우에만 사용 (결재 프로세스 없이 바로 발송)
*/
export async function sendTechSalesRfqToVendors(input: {
rfqId: number;
@@ -559,7 +597,7 @@ export async function sendTechSalesRfqToVendors(input: {
};
}
- // 발송 가능한 상태인지 확인 (결재 진행중 상태는 제외)
+ // 발송 가능한 상태인지 확인 (결재 진행중 상태 포함 - 결재 승인 후 발송 가능)
if (rfq.status !== TECH_SALES_RFQ_STATUSES.RFQ_VENDOR_ASSIGNED &&
rfq.status !== TECH_SALES_RFQ_STATUSES.RFQ_SENT &&
rfq.status !== TECH_SALES_RFQ_STATUSES.APPROVAL_IN_PROGRESS) {
@@ -568,14 +606,6 @@ export async function sendTechSalesRfqToVendors(input: {
message: "벤더가 할당된 RFQ 또는 이미 전송된 RFQ만 다시 전송할 수 있습니다",
};
}
-
- // 결재 진행중 상태에서는 결재 승인 후처리 핸들러에서만 발송 가능
- if (rfq.status === TECH_SALES_RFQ_STATUSES.APPROVAL_IN_PROGRESS) {
- return {
- success: false,
- message: "결재 진행 중인 RFQ는 결재 승인 후 자동으로 발송됩니다",
- };
- }
const isResend = rfq.status === TECH_SALES_RFQ_STATUSES.RFQ_SENT;
@@ -626,34 +656,6 @@ export async function sendTechSalesRfqToVendors(input: {
};
}
- // RFQ 첨부파일 중 DRM 파일 확인
- const attachments = await db.query.techSalesAttachments.findMany({
- where: eq(techSalesAttachments.techSalesRfqId, input.rfqId),
- columns: {
- id: true,
- drmEncrypted: true,
- fileName: true,
- fileSize: true,
- originalFileName: true,
- }
- });
-
- const drmAttachments = attachments.filter(att => att.drmEncrypted === true);
-
- // DRM 파일이 있으면 결재 프로세스 필요 (이 함수는 직접 발송하지 않고 결재 필요 신호 반환)
- if (drmAttachments.length > 0) {
- return {
- success: false,
- requiresApproval: true,
- message: "DRM 파일이 포함되어 있어 결재가 필요합니다",
- drmAttachmentIds: drmAttachments.map(att => att.id),
- drmAttachments: drmAttachments.map(att => ({
- fileName: att.originalFileName || att.fileName,
- fileSize: att.fileSize,
- })),
- };
- }
-
// 트랜잭션 시작
await db.transaction(async (tx) => {
// 1. RFQ 상태 업데이트 (최초 발송인 경우 rfqSendDate 설정)
diff --git a/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx b/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx
index 52758412..db2331af 100644
--- a/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx
+++ b/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx
@@ -259,26 +259,20 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
try {
setIsSendingRfq(true);
- // 기술영업 RFQ 발송 서비스 함수 호출 (contact 정보 포함)
- const vendorIds = selectedRows.map(row => row.vendorId).filter(Boolean);
- const { sendTechSalesRfqToVendors } = await import("@/lib/techsales-rfq/service");
+ // DRM 해제 여부 확인
+ const { checkTechSalesRfqHasDrmAttachments, sendTechSalesRfqToVendors } = await import("@/lib/techsales-rfq/service");
+ const drmCheck = await checkTechSalesRfqHasDrmAttachments(selectedRfqId);
- const result = await sendTechSalesRfqToVendors({
- rfqId: selectedRfqId,
- vendorIds: vendorIds as number[],
- selectedContacts: selectedContacts
- });
-
- // DRM 파일이 있어서 결재가 필요한 경우
- if (!result.success && result.requiresApproval) {
+ // DRM 파일이 걸려있으면 결재 프로세스 진행
+ if (drmCheck.hasDrm) {
// 결재 데이터 저장
setApprovalPreviewData({
vendors: selectedRows.map(row => ({
vendorId: row.vendorId!,
vendorName: row.vendorName || "",
})),
- drmAttachments: result.drmAttachments || [],
- drmAttachmentIds: result.drmAttachmentIds || [],
+ drmAttachments: drmCheck.drmAttachments,
+ drmAttachmentIds: drmCheck.drmAttachmentIds,
selectedContacts: selectedContacts,
});
@@ -288,6 +282,14 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
return;
}
+ // DRM 해제가 안 걸려있으면 바로 발송
+ const vendorIds = selectedRows.map(row => row.vendorId).filter(Boolean);
+ const result = await sendTechSalesRfqToVendors({
+ rfqId: selectedRfqId,
+ vendorIds: vendorIds as number[],
+ selectedContacts: selectedContacts
+ });
+
if (result.success) {
toast.success(result.message || `${selectedContacts.length}명의 연락처에게 RFQ가 발송되었습니다.`);
} else {
@@ -483,8 +485,7 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
applicationReason: reason,
});
- // 신청사유 다이얼로그 닫고 결재 미리보기 열기
- setShowApplicationReasonDialog(false);
+ // 결재 미리보기 열기
setShowApprovalPreview(true);
} catch (error) {
console.error("템플릿 변수 생성 실패:", error);
@@ -936,6 +937,37 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps
quotationId={selectedQuotationForContacts?.id || null}
vendorName={selectedQuotationForContacts?.vendorName}
/>
+
+ {/* 신청사유 입력 다이얼로그 */}
+ {approvalPreviewData && (
+ <ApplicationReasonDialog
+ open={showApplicationReasonDialog}
+ onOpenChange={setShowApplicationReasonDialog}
+ onConfirm={handleApplicationReasonConfirm}
+ vendorCount={approvalPreviewData.vendors.length}
+ attachmentCount={approvalPreviewData.drmAttachmentIds.length}
+ />
+ )}
+
+ {/* 결재 미리보기 다이얼로그 */}
+ {approvalPreviewData && approvalPreviewData.templateVariables && (
+ <ApprovalPreviewDialog
+ open={showApprovalPreview}
+ onOpenChange={setShowApprovalPreview}
+ templateName="암호화해제 신청"
+ variables={approvalPreviewData.templateVariables}
+ title={`암호화해제 신청 - ${selectedRfq?.rfqCode || 'RFQ'}`}
+ currentUser={{
+ id: Number(session.data.user.id),
+ epId: session.data.user.epId,
+ name: session.data.user.name || undefined,
+ email: session.data.user.email || undefined,
+ }}
+ onConfirm={handleApprovalConfirm}
+ allowTitleEdit={false}
+ allowDescriptionEdit={false}
+ />
+ )}
</div>
)
} \ No newline at end of file