diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-10-03 04:48:47 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-10-03 04:48:47 +0000 |
| commit | defda07c0bb4b0bd444ca8dc4fd3f89322bda0ce (patch) | |
| tree | d7f257781f107d7ec2fd4ef76cb4f840f5065a06 /app | |
| parent | 00743c8b4190fac9117c2d9c08981bbfdce576de (diff) | |
(대표님) edp, tbe, dolce 등
Diffstat (limited to 'app')
| -rw-r--r-- | app/[lng]/evcp/(evcp)/(procurement)/pq_new/[vendorId]/[submissionId]/page.tsx | 48 | ||||
| -rw-r--r-- | app/api/revisions/max-serial-no/route.ts | 175 |
2 files changed, 199 insertions, 24 deletions
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 (
+ <div className="container mx-auto p-6">
+ <div className="text-center">
+ <h1 className="text-2xl font-bold text-red-600">잘못된 접근</h1>
+ <p className="text-gray-600 mt-2">유효하지 않은 파라미터입니다.</p>
+ </div>
+ </div>
+ )
+ }
+
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 (
<Shell className="gap-6 max-w-5xl">
<div className="flex items-center justify-between">
@@ -136,7 +156,7 @@ export default async function PQReviewPage(props: PQReviewPageProps) { pqData={pqData}
vendorId={vendorId}
pqSubmission={pqSubmission}
- canReview={canReview}
+ vendorInfo={vendorInfo}
/>
</TabsContent>
@@ -156,7 +176,21 @@ export default async function PQReviewPage(props: PQReviewPageProps) { <p className="text-sm font-medium text-muted-foreground">상태</p>
<p>{pqSubmission.vendorStatus}</p>
</div>
- {/* 필요시 추가 정보 표시 */}
+ <div>
+ <p className="text-sm font-medium text-muted-foreground">국가</p>
+ <p>{pqSubmission.vendorCountry}</p>
+ </div>
+ <div>
+ <p className="text-sm font-medium text-muted-foreground">이메일</p>
+ <p>{pqSubmission.vendorEmail}</p>
+ </div>
+ <div>
+ <p className="text-sm font-medium text-muted-foreground">전화번호</p>
+ <p>{pqSubmission.vendorPhone}</p>
+ </div>
+
+
+ {/* 필요시 추가 정보 표시 */}
</div>
</div>
</TabsContent>
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<DOLCEDetailDocument[]> { + 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 |
