From e467b3b7905a200b98daa3787565c08a309a6dda Mon Sep 17 00:00:00 2001 From: dujinkim 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 @@ -47,6 +50,8 @@ interface ContractSummary { items: Record[] subcontractChecklist: Record | null storageInfo?: Record[] + 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>({}) + const [savedPdfPath, setSavedPdfPath] = useState(null) + const [savedBasicContractPdfs, setSavedBasicContractPdfs] = useState>([]) + 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 => { + 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({ + + {/* 결재 미리보기 Dialog */} + {session?.user && session.user.epId && contractSummary && ( + { + 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} + /> + )} )} \ No newline at end of file -- cgit v1.2.3