From e467b3b7905a200b98daa3787565c08a309a6dda Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Fri, 5 Dec 2025 09:18:28 +0000
Subject: (최겸) 계약 승인 요청 시 결재 상신 개발, 입찰 화학물질 soap 개발
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../general-contract-approval-request-dialog.tsx   | 163 ++++++++++++++++++---
 1 file changed, 139 insertions(+), 24 deletions(-)

(limited to 'lib/general-contracts/detail/general-contract-approval-request-dialog.tsx')

diff --git a/lib/general-contracts/detail/general-contract-approval-request-dialog.tsx b/lib/general-contracts/detail/general-contract-approval-request-dialog.tsx
index 46251c71..d44f4290 100644
--- a/lib/general-contracts/detail/general-contract-approval-request-dialog.tsx
+++ b/lib/general-contracts/detail/general-contract-approval-request-dialog.tsx
@@ -35,6 +35,9 @@ import {
   getStorageInfo
 } from '../service'
 import { mapContractDataToTemplateVariables } from '../utils'
+import { ApprovalPreviewDialog } from '@/lib/approval/client'
+import { requestContractApprovalWithApproval } from '../approval-actions'
+import { mapContractToApprovalTemplateVariables } from '../approval-template-variables'
 
 interface ContractApprovalRequestDialogProps {
   contract: Record<string, unknown>
@@ -47,6 +50,8 @@ interface ContractSummary {
   items: Record<string, unknown>[]
   subcontractChecklist: Record<string, unknown> | null
   storageInfo?: Record<string, unknown>[]
+  pdfPath?: string
+  basicContractPdfs?: Array<{ key: string; buffer: number[]; fileName: string }>
 }
 
 export function ContractApprovalRequestDialog({ 
@@ -72,6 +77,12 @@ export function ContractApprovalRequestDialog({
   }>>([])
   const [isLoadingBasicContracts, setIsLoadingBasicContracts] = useState(false)
 
+  // 결재 관련 상태
+  const [approvalDialogOpen, setApprovalDialogOpen] = useState(false)
+  const [approvalVariables, setApprovalVariables] = useState<Record<string, string>>({})
+  const [savedPdfPath, setSavedPdfPath] = useState<string | null>(null)
+  const [savedBasicContractPdfs, setSavedBasicContractPdfs] = useState<Array<{ key: string; buffer: number[]; fileName: string }>>([])
+
   const contractId = contract.id as number
   const userId = session?.user?.id || ''
 
@@ -143,7 +154,7 @@ export function ContractApprovalRequestDialog({
         await new Promise(resolve => setTimeout(resolve, 3000));
 
         const fileData = await templateDoc.getFileData();
-        const pdfBuffer = await Core.officeToPDFBuffer(fileData, { extension: 'docx' });
+        const pdfBuffer = await (Core as any).officeToPDFBuffer(fileData, { extension: 'docx' });
 
         const fileName = `${contractType}_${contractSummary?.basicInfo?.vendorCode || vendorId}_${Date.now()}.pdf`;
 
@@ -542,7 +553,42 @@ export function ContractApprovalRequestDialog({
     console.log("🔄 PDF 미리보기 닫기 완료")
   }
 
-  // 최종 전송
+  // PDF를 서버에 저장하는 함수 (API route 사용)
+  const savePdfToServer = async (pdfBuffer: Uint8Array, fileName: string): Promise<string | null> => {
+    try {
+      // PDF 버퍼를 Blob으로 변환
+      const pdfBlob = new Blob([pdfBuffer], { type: 'application/pdf' });
+      
+      // FormData 생성
+      const formData = new FormData();
+      formData.append('file', pdfBlob, fileName);
+      formData.append('contractId', String(contractId));
+
+      // API route로 업로드
+      const response = await fetch('/api/general-contracts/upload-pdf', {
+        method: 'POST',
+        body: formData,
+      });
+
+      if (!response.ok) {
+        const errorData = await response.json();
+        throw new Error(errorData.error || 'PDF 파일 저장에 실패했습니다.');
+      }
+
+      const result = await response.json();
+      
+      if (!result.success) {
+        throw new Error(result.error || 'PDF 파일 저장에 실패했습니다.');
+      }
+
+      return result.filePath;
+    } catch (error) {
+      console.error('PDF 저장 실패:', error);
+      return null;
+    }
+  };
+
+  // 최종 전송 - 결재 프로세스 시작
   const handleFinalSubmit = async () => {
     if (!generatedPdfUrl || !contractSummary || !generatedPdfBuffer) {
       toast.error('생성된 PDF가 필요합니다.')
@@ -594,34 +640,77 @@ export function ContractApprovalRequestDialog({
         }
       }
 
-      // 서버액션을 사용하여 계약승인요청 전송
-      const result = await sendContractApprovalRequest(
-        contractSummary,
+      // PDF를 서버에 저장
+      toast.info('PDF를 서버에 저장하는 중입니다...');
+      const pdfPath = await savePdfToServer(
         generatedPdfBuffer,
-        'contractDocument',
-        userId,
-        generatedBasicContractPdfs
-      )
+        `contract_${contractId}_${Date.now()}.pdf`
+      );
 
-      if (result.success) {
-        toast.success('계약승인요청이 전송되었습니다.')
-        onOpenChange(false)
-      } else {
-        // 서버에서 이미 처리된 에러 메시지 표시
-        toast.error(result.error || '계약승인요청 전송 실패')
-        return
+      if (!pdfPath) {
+        toast.error('PDF 저장에 실패했습니다.');
+        return;
       }
+
+      setSavedPdfPath(pdfPath);
+      setSavedBasicContractPdfs(generatedBasicContractPdfs);
+
+      // 결재 템플릿 변수 매핑
+      const approvalVars = await mapContractToApprovalTemplateVariables(contractSummary);
+      setApprovalVariables(approvalVars);
+
+      // 계약승인요청 dialog close
+      onOpenChange(false);
+
+      // 결재 템플릿 dialog open
+      setApprovalDialogOpen(true);
     } catch (error: any) {
-      console.error('Error submitting approval request:', error)
+      console.error('Error preparing approval:', error);
+      toast.error('결재 준비 중 오류가 발생했습니다.')
+    } finally {
+      setIsLoading(false)
+    }
+  }
 
-      // 데이터베이스 중복 키 오류 처리
-      if (error.message && error.message.includes('duplicate key value violates unique constraint')) {
-        toast.error('이미 존재하는 계약번호입니다. 다른 계약번호를 사용해주세요.')
-        return
-      }
+  // 결재 등록 처리
+  const handleApprovalSubmit = async (data: {
+    approvers: string[];
+    title: string;
+    attachments?: File[];
+  }) => {
+    if (!contractSummary || !savedPdfPath) {
+      toast.error('계약 정보가 필요합니다.')
+      return
+    }
 
-      // 다른 오류에 대한 일반적인 처리
-      toast.error('계약승인요청 전송 중 오류가 발생했습니다.')
+    setIsLoading(true)
+    try {
+      const result = await requestContractApprovalWithApproval({
+        contractId,
+        contractSummary: {
+          ...contractSummary,
+          // PDF 경로를 contractSummary에 추가
+          pdfPath: savedPdfPath || undefined,
+          basicContractPdfs: savedBasicContractPdfs.length > 0 ? savedBasicContractPdfs : undefined,
+        } as ContractSummary,
+        currentUser: {
+          id: Number(userId),
+          epId: session?.user?.epId || null,
+          email: session?.user?.email || undefined,
+        },
+        approvers: data.approvers,
+        title: data.title,
+      });
+
+      if (result.status === 'pending_approval') {
+        toast.success('결재가 등록되었습니다.')
+        setApprovalDialogOpen(false);
+      } else {
+        toast.error('결재 등록에 실패했습니다.')
+      }
+    } catch (error: any) {
+      console.error('Error submitting approval:', error);
+      toast.error(`결재 등록 중 오류가 발생했습니다: ${error.message || '알 수 없는 오류'}`);
     } finally {
       setIsLoading(false)
     }
@@ -1064,5 +1153,31 @@ export function ContractApprovalRequestDialog({
           </TabsContent>
         </Tabs>
       </DialogContent>
+
+      {/* 결재 미리보기 Dialog */}
+      {session?.user && session.user.epId && contractSummary && (
+        <ApprovalPreviewDialog
+          open={approvalDialogOpen}
+          onOpenChange={(open) => {
+            setApprovalDialogOpen(open);
+            if (!open) {
+              setApprovalVariables({});
+              setSavedPdfPath(null);
+              setSavedBasicContractPdfs([]);
+            }
+          }}
+          templateName="일반계약 결재"
+          variables={approvalVariables}
+          title={`계약 체결 진행 품의 요청서 - ${contractSummary.basicInfo?.contractNumber || contractId}`}
+          currentUser={{
+            id: Number(session.user.id),
+            epId: session.user.epId,
+            name: session.user.name || undefined,
+            email: session.user.email || undefined,
+          }}
+          onConfirm={handleApprovalSubmit}
+          enableAttachments={false}
+        />
+      )}
     </Dialog>
   )}
\ No newline at end of file
-- 
cgit v1.2.3

