From defda07c0bb4b0bd444ca8dc4fd3f89322bda0ce Mon Sep 17 00:00:00 2001 From: dujinkim Date: Fri, 3 Oct 2025 04:48:47 +0000 Subject: (대표님) edp, tbe, dolce 등 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pq_new/[vendorId]/[submissionId]/page.tsx | 48 +++++- app/api/revisions/max-serial-no/route.ts | 175 +++++++++++++++++++-- 2 files changed, 199 insertions(+), 24 deletions(-) (limited to 'app') diff --git a/app/[lng]/evcp/(evcp)/(procurement)/pq_new/[vendorId]/[submissionId]/page.tsx b/app/[lng]/evcp/(evcp)/(procurement)/pq_new/[vendorId]/[submissionId]/page.tsx index c8b0e9b8..c63cf4df 100644 --- a/app/[lng]/evcp/(evcp)/(procurement)/pq_new/[vendorId]/[submissionId]/page.tsx +++ b/app/[lng]/evcp/(evcp)/(procurement)/pq_new/[vendorId]/[submissionId]/page.tsx @@ -36,14 +36,37 @@ export default async function PQReviewPage(props: PQReviewPageProps) { const params = await props.params const vendorId = parseInt(params.vendorId, 10) const submissionId = parseInt(params.submissionId, 10) - + + // 파라미터 유효성 검증 + if (isNaN(vendorId) || isNaN(submissionId)) { + return ( +
+
+

잘못된 접근

+

유효하지 않은 파라미터입니다.

+
+
+ ) + } + try { // PQ Submission 정보 조회 const pqSubmission = await getPQById(submissionId, vendorId) // PQ 데이터 조회 (질문과 답변) const pqData = await getPQDataByVendorId(vendorId, pqSubmission.projectId || undefined) - + + // 협력업체 정보 (pqSubmission에 이미 포함되어 있음) + const vendorInfo = { + vendorName: pqSubmission.vendorName, + vendorCode: pqSubmission.vendorCode, + vendorStatus: pqSubmission.vendorStatus, + vendorCountry: pqSubmission.vendorCountry, + vendorEmail: pqSubmission.vendorEmail, + vendorPhone: pqSubmission.vendorPhone, + vendorFax: pqSubmission.vendorFax, + } + // 프로젝트 정보 (프로젝트 PQ인 경우) const projectInfo = pqSubmission.projectId ? { id: pqSubmission.projectId, @@ -60,9 +83,6 @@ export default async function PQReviewPage(props: PQReviewPageProps) { const statusLabel = getStatusLabel(pqSubmission.status) const statusVariant = getStatusVariant(pqSubmission.status) - // 수정 가능 여부 (SUBMITTED 상태일 때만 가능) - const canReview = pqSubmission.status === "SUBMITTED" - return (
@@ -136,7 +156,7 @@ export default async function PQReviewPage(props: PQReviewPageProps) { pqData={pqData} vendorId={vendorId} pqSubmission={pqSubmission} - canReview={canReview} + vendorInfo={vendorInfo} /> @@ -156,7 +176,21 @@ export default async function PQReviewPage(props: PQReviewPageProps) {

상태

{pqSubmission.vendorStatus}

- {/* 필요시 추가 정보 표시 */} +
+

국가

+

{pqSubmission.vendorCountry}

+
+
+

이메일

+

{pqSubmission.vendorEmail}

+
+
+

전화번호

+

{pqSubmission.vendorPhone}

+
+ + + {/* 필요시 추가 정보 표시 */} diff --git a/app/api/revisions/max-serial-no/route.ts b/app/api/revisions/max-serial-no/route.ts index c0bfe5c3..0681b66d 100644 --- a/app/api/revisions/max-serial-no/route.ts +++ b/app/api/revisions/max-serial-no/route.ts @@ -1,10 +1,16 @@ import { NextRequest, NextResponse } from 'next/server' import db from '@/db/db' -import { revisions, issueStages } from '@/db/schema/vendorDocu' +import { revisions, issueStages, documents, projects } from '@/db/schema' import { eq, sql } from 'drizzle-orm' import { alias } from 'drizzle-orm/pg-core' import { debugLog, debugError, debugSuccess } from '@/lib/debug-utils' +// DOLCE Detail Document 타입 정의 +interface DOLCEDetailDocument { + RegisterSerialNoMax?: string | number + // 다른 필드들도 필요시 추가 +} + export async function GET(request: NextRequest) { try { const { searchParams } = new URL(request.url) @@ -23,7 +29,7 @@ export async function GET(request: NextRequest) { const parsedDocumentId = parseInt(documentId) debugLog('3. Parsed documentId:', parsedDocumentId) - // 해당 document의 모든 issueStages와 연결된 revisions에서 최대 serialNo 조회 + // 1. 내부 DB에서 최대 serialNo 조회 const r = alias(revisions, 'r') const is = alias(issueStages, 'is') @@ -45,30 +51,114 @@ export async function GET(request: NextRequest) { ) .where(eq(is.documentId, parsedDocumentId)) - debugLog('5. Query result:', maxSerialResult) - debugLog('6. Query result length:', maxSerialResult.length) - debugLog('7. First result item:', maxSerialResult[0]) + debugLog('5. Internal DB query result:', maxSerialResult) + + const internalMaxSerial = maxSerialResult[0]?.maxSerial || 0 + const internalMaxRegisterSerial = maxSerialResult[0]?.maxRegisterSerial || 0 + const internalMaxSerialNo = Math.max(internalMaxSerial, internalMaxRegisterSerial) + + debugLog('6. Internal maxSerialNo:', internalMaxSerialNo) + + // 2. Document 정보 조회 (DOLCE API 호출을 위해) + const documentInfo = await db + .select({ + docNumber: documents.docNumber, + drawingKind: documents.drawingKind, + discipline: documents.discipline, + projectId: documents.projectId, + projectCode: projects.code + }) + .from(documents) + .leftJoin(projects, eq(documents.projectId, projects.id)) + .where(eq(documents.id, parsedDocumentId)) + .limit(1) + + debugLog('7. Document info:', documentInfo[0]) + + if (!documentInfo[0]) { + debugError('Document not found for id:', parsedDocumentId) + return NextResponse.json( + { error: 'Document not found' }, + { status: 404 } + ) + } + + const { docNumber, drawingKind, discipline, projectCode } = documentInfo[0] + + // 필수 필드 검증 + if (!projectCode || !docNumber || !discipline || !drawingKind) { + debugLog('8. Missing required fields for DOLCE API') + debugLog(' - projectCode:', projectCode) + debugLog(' - docNumber:', docNumber) + debugLog(' - discipline:', discipline) + debugLog(' - drawingKind:', drawingKind) + + // DOLCE API 호출 불가능한 경우, 내부 DB 값만 사용 + return NextResponse.json({ + maxSerialNo: internalMaxSerialNo, + nextSerialNo: internalMaxSerialNo + 1, + documentId: documentId, + source: 'internal_only', + debug: { + parsedDocumentId, + internalMaxSerialNo, + missingFields: { + projectCode: !projectCode, + docNumber: !docNumber, + discipline: !discipline, + drawingKind: !drawingKind + } + } + }) + } + + // 3. DOLCE API 호출 + let dolceMaxSerialNo = 0 + try { + debugLog('9. Calling DOLCE API...') + const dolceDocuments = await fetchDetailFromDOLCE( + projectCode, + docNumber, + discipline, + drawingKind + ) - const maxSerialValue = maxSerialResult[0]?.maxSerial || 0 - const maxRegisterSerialValue = maxSerialResult[0]?.maxRegisterSerial || 0 + debugLog('10. DOLCE API response:', dolceDocuments) - debugLog('8. maxSerial value:', maxSerialValue) - debugLog('9. maxRegisterSerial value:', maxRegisterSerialValue) + if (dolceDocuments && dolceDocuments.length > 0) { + // 첫 번째 문서의 RegisterSerialNoMax 값 사용 + const firstDoc = dolceDocuments[0] + if (firstDoc.RegisterSerialNoMax) { + dolceMaxSerialNo = parseInt(String(firstDoc.RegisterSerialNoMax)) || 0 + debugLog('11. DOLCE maxSerialNo:', dolceMaxSerialNo) + } + } + } catch (error) { + debugError('DOLCE API call failed:', error) + // DOLCE API 실패 시에도 내부 DB 값은 사용 + } - const maxSerialNo = Math.max(maxSerialValue, maxRegisterSerialValue) + // 4. 내부 DB와 DOLCE 값 중 최대값 선택 + const finalMaxSerialNo = Math.max(internalMaxSerialNo, dolceMaxSerialNo) - debugSuccess('10. Final maxSerialNo:', maxSerialNo) - debugSuccess('11. Next serialNo:', maxSerialNo + 1) + debugSuccess('12. Final maxSerialNo:', finalMaxSerialNo) + debugSuccess('13. Next serialNo:', finalMaxSerialNo + 1) return NextResponse.json({ - maxSerialNo, - nextSerialNo: maxSerialNo + 1, + maxSerialNo: finalMaxSerialNo, + nextSerialNo: finalMaxSerialNo + 1, documentId: documentId, + source: dolceMaxSerialNo > internalMaxSerialNo ? 'dolce' : 'internal', debug: { parsedDocumentId, - queryResult: maxSerialResult, - maxSerialValue, - maxRegisterSerialValue + internalMaxSerialNo, + dolceMaxSerialNo, + documentInfo: { + projectCode, + docNumber, + discipline, + drawingKind + } } }) } catch (error) { @@ -79,4 +169,55 @@ export async function GET(request: NextRequest) { { status: 500 } ) } +} + +// DOLCE Detail API 호출 함수 +async function fetchDetailFromDOLCE( + projectCode: string, + drawingNo: string, + discipline: string, + drawingKind: string +): Promise { + const endpoint = process.env.DOLCE_DOC_DETAIL_API_URL || 'http://60.100.99.217:1111/Services/VDCSWebService.svc/DetailDwgReceiptMgmt' + + const requestBody = { + project: projectCode, + drawingNo: drawingNo, + discipline: discipline, + drawingKind: drawingKind + } + + console.log(`Fetching detail from DOLCE: ${projectCode} - ${drawingNo}`) + console.log('Request body:', requestBody) + + try { + const response = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestBody) + }) + + if (!response.ok) { + const errorText = await response.text() + throw new Error(`DOLCE Detail API failed: HTTP ${response.status} - ${errorText}`) + } + + const data = await response.json() + + // DOLCE Detail API 응답 구조에 맞게 처리 + if (data.DetailDwgReceiptMgmtResult) { + const documents = data.DetailDwgReceiptMgmtResult as DOLCEDetailDocument[] + console.log(`Found ${documents.length} detail records for ${drawingNo}`) + return documents + } else { + console.warn(`Unexpected DOLCE Detail response structure:`, data) + return [] + } + + } catch (error) { + console.error(`DOLCE Detail API call failed for ${drawingNo}:`, error) + throw error + } } \ No newline at end of file -- cgit v1.2.3