summaryrefslogtreecommitdiff
path: root/lib/rfq-last
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-12-09 06:09:09 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-12-09 06:09:09 +0000
commitcf3f7cf0efa2753a401b36f6eb3a49cb9697ddce (patch)
treec5174948c8bb35171151605bbbe19f6d18c30509 /lib/rfq-last
parentea8aed1e1d62fb9fa6716347de73e4ef13040929 (diff)
(최겸) 구매 rfq, 기술영업 rfq drm 해제 시 결재 상신에 첨부파일 포함 로직 적용
Diffstat (limited to 'lib/rfq-last')
-rw-r--r--lib/rfq-last/approval-actions.ts26
-rw-r--r--lib/rfq-last/service.ts2
-rw-r--r--lib/rfq-last/vendor-response/editor/vendor-response-editor.tsx44
3 files changed, 68 insertions, 4 deletions
diff --git a/lib/rfq-last/approval-actions.ts b/lib/rfq-last/approval-actions.ts
index be435931..2f9d0843 100644
--- a/lib/rfq-last/approval-actions.ts
+++ b/lib/rfq-last/approval-actions.ts
@@ -8,6 +8,7 @@
import { ApprovalSubmissionSaga } from '@/lib/approval';
import { mapRfqSendToTemplateVariables } from './approval-handlers';
+import { prepareEmailAttachments } from './service';
interface RfqSendApprovalData {
// RFQ 기본 정보
@@ -95,7 +96,27 @@ export async function requestRfqSendWithApproval(data: RfqSendApprovalData) {
applicationReason: data.applicationReason,
});
- // 3. 결재 상신용 payload 구성
+ // 3. Knox 상신용 첨부파일 준비 (실제 파일 객체로 변환)
+ const emailAttachments = await prepareEmailAttachments(data.rfqId, data.attachmentIds);
+ type PreparedAttachment = {
+ filename?: string | null;
+ content: BlobPart;
+ contentType?: string | null;
+ };
+ const knoxAttachments = (emailAttachments as PreparedAttachment[])
+ .filter((att): att is PreparedAttachment => Boolean(att && att.content))
+ .map(
+ (att) =>
+ new File([att.content], att.filename || 'attachment', {
+ type: att.contentType || 'application/octet-stream',
+ })
+ );
+
+ if (knoxAttachments.length === 0) {
+ throw new Error('상신할 첨부파일을 준비하지 못했습니다.');
+ }
+
+ // 4. 결재 상신용 payload 구성
// ⚠️ cronjob 환경에서 실행되므로 currentUser 정보를 포함해야 함
const approvalPayload = {
rfqId: data.rfqId,
@@ -113,7 +134,7 @@ export async function requestRfqSendWithApproval(data: RfqSendApprovalData) {
},
};
- // 4. Saga로 결재 상신
+ // 5. Saga로 결재 상신
const saga = new ApprovalSubmissionSaga(
'rfq_send_with_attachments', // 핸들러 키
approvalPayload, // 결재 승인 후 실행될 데이터
@@ -128,6 +149,7 @@ export async function requestRfqSendWithApproval(data: RfqSendApprovalData) {
epId: data.currentUser.epId,
email: data.currentUser.email,
},
+ attachments: knoxAttachments,
}
);
diff --git a/lib/rfq-last/service.ts b/lib/rfq-last/service.ts
index 68cfdac7..23f5f63a 100644
--- a/lib/rfq-last/service.ts
+++ b/lib/rfq-last/service.ts
@@ -3349,7 +3349,7 @@ async function getProjectInfo(projectId: number) {
return project;
}
-async function prepareEmailAttachments(rfqId: number, attachmentIds: number[]) {
+export async function prepareEmailAttachments(rfqId: number, attachmentIds: number[]) {
const attachments = await db
.select({
attachment: rfqLastAttachments,
diff --git a/lib/rfq-last/vendor-response/editor/vendor-response-editor.tsx b/lib/rfq-last/vendor-response/editor/vendor-response-editor.tsx
index 8c70b8dd..18fc5d50 100644
--- a/lib/rfq-last/vendor-response/editor/vendor-response-editor.tsx
+++ b/lib/rfq-last/vendor-response/editor/vendor-response-editor.tsx
@@ -9,6 +9,14 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
import { Button } from "@/components/ui/button"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Badge } from "@/components/ui/badge"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog"
import { toast } from "sonner"
import RfqInfoHeader from "./rfq-info-header"
import CommercialTermsForm from "./commercial-terms-form"
@@ -130,6 +138,7 @@ export default function VendorResponseEditor({
const [deletedAttachments, setDeletedAttachments] = useState<any[]>([])
const [uploadProgress, setUploadProgress] = useState(0) // 추가
const [currencyDecimalPlaces, setCurrencyDecimalPlaces] = useState<number>(2) // 통화별 소수점 자리수
+ const [confirmOpen, setConfirmOpen] = useState(false)
console.log(existingResponse,"existingResponse")
@@ -682,7 +691,7 @@ export default function VendorResponseEditor({
<Button
type="button"
variant="default"
- onClick={() => handleFormSubmit(true)} // 직접 핸들러 호출
+ onClick={() => setConfirmOpen(true)} // 제출 전 확인 다이얼로그
disabled={loading || !allContractsSigned || isSubmitted || activeTab !== 'attachments'}
>
{!allContractsSigned ? (
@@ -714,6 +723,39 @@ export default function VendorResponseEditor({
</Button>
</div>
+ {/* 최종 제출 확인 다이얼로그 */}
+ <Dialog open={confirmOpen} onOpenChange={setConfirmOpen}>
+ <DialogContent>
+ <DialogHeader>
+ <DialogTitle>최종 제출</DialogTitle>
+ <DialogDescription>
+ 최종 제출하시겠습니까?
+ </DialogDescription>
+ </DialogHeader>
+ <DialogFooter className="flex gap-2 sm:justify-end">
+ <Button
+ type="button"
+ variant="outline"
+ onClick={() => setConfirmOpen(false)}
+ disabled={loading}
+ >
+ 취소
+ </Button>
+ <Button
+ type="button"
+ variant="default"
+ onClick={() => {
+ setConfirmOpen(false)
+ handleFormSubmit(true)
+ }}
+ disabled={loading || !allContractsSigned || isSubmitted || activeTab !== 'attachments'}
+ >
+ 제출하기
+ </Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
+
</div>
</form>
</FormProvider>