summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/api/(S-ERP)/(ECC)/IF_ECC_EVCP_PR_INFORMATION/route.ts199
-rw-r--r--app/api/mdg/send-vendor-xml/route.ts28
2 files changed, 199 insertions, 28 deletions
diff --git a/app/api/(S-ERP)/(ECC)/IF_ECC_EVCP_PR_INFORMATION/route.ts b/app/api/(S-ERP)/(ECC)/IF_ECC_EVCP_PR_INFORMATION/route.ts
new file mode 100644
index 00000000..3b7636f9
--- /dev/null
+++ b/app/api/(S-ERP)/(ECC)/IF_ECC_EVCP_PR_INFORMATION/route.ts
@@ -0,0 +1,199 @@
+import { NextRequest } from 'next/server';
+import db from '@/db/db';
+
+import {
+ ToXMLFields,
+ serveWsdl,
+ createXMLParser,
+ extractRequestData,
+ convertXMLToDBData,
+ processNestedArray,
+ createErrorResponse,
+ createSuccessResponse,
+ createSoapResponse,
+ replaceSubTableData,
+ withSoapLogging,
+} from '@/lib/soap/mdg/utils';
+
+import {
+ PR_INFORMATION_T_BID_HEADER,
+ PR_INFORMATION_T_BID_ITEM,
+} from '@/db/schema/ECC/ecc';
+
+// 스키마에서 타입 추론
+type BidHeaderData = typeof PR_INFORMATION_T_BID_HEADER.$inferInsert;
+type BidItemData = typeof PR_INFORMATION_T_BID_ITEM.$inferInsert;
+
+// XML 구조 타입 정의
+type BidHeaderXML = ToXMLFields<Omit<BidHeaderData, 'id' | 'createdAt' | 'updatedAt'>>;
+type BidItemXML = ToXMLFields<Omit<BidItemData, 'id' | 'createdAt' | 'updatedAt'>>;
+
+// 처리된 데이터 구조
+interface ProcessedPRData {
+ bidHeader: BidHeaderData;
+ bidItems: BidItemData[];
+}
+
+export async function GET(request: NextRequest) {
+ const url = new URL(request.url);
+ if (url.searchParams.has('wsdl')) {
+ return serveWsdl('IF_ECC_EVCP_PR_INFORMATION.wsdl');
+ }
+
+ return new Response('Method Not Allowed', { status: 405 });
+}
+
+export async function POST(request: NextRequest) {
+ const url = new URL(request.url);
+ if (url.searchParams.has('wsdl')) {
+ return serveWsdl('IF_ECC_EVCP_PR_INFORMATION.wsdl');
+ }
+
+ const body = await request.text();
+
+ // SOAP 로깅 래퍼 함수 사용
+ return withSoapLogging(
+ 'INBOUND',
+ 'S-ERP',
+ 'IF_ECC_EVCP_PR_INFORMATION',
+ body,
+ async () => {
+ console.log('🚀 PR_INFORMATION 수신 시작, 데이터 길이:', body.length);
+
+ // 1) XML 파싱
+ const parser = createXMLParser(['T_BID_HEADER', 'T_BID_ITEM']);
+ const parsedData = parser.parse(body);
+
+ // 2) SOAP Body 또는 루트에서 요청 데이터 추출
+ const requestData = extractRequestData(parsedData, 'IF_ECC_EVCP_PR_INFORMATIONReq');
+ if (!requestData) {
+ console.error('유효한 요청 데이터를 찾을 수 없습니다');
+ throw new Error('Missing request data - IF_ECC_EVCP_PR_INFORMATIONReq not found');
+ }
+
+ // 3) XML 데이터를 DB 삽입 가능한 형태로 변환
+ const processedData = transformPRData(requestData as PRRequestXML);
+
+ // 4) 필수 필드 검증
+ for (const prData of processedData) {
+ if (!prData.bidHeader.ANFNR) {
+ throw new Error('Missing required field: ANFNR in Bid Header');
+ }
+ for (const item of prData.bidItems) {
+ if (!item.ANFNR || !item.ANFPS) {
+ throw new Error('Missing required fields in Bid Item: ANFNR, ANFPS');
+ }
+ }
+ }
+
+ // 5) 데이터베이스 저장
+ await saveToDatabase(processedData);
+
+ console.log(`🎉 처리 완료: ${processedData.length}개 PR 데이터`);
+
+ // 6) 성공 응답 반환
+ return createSoapResponse('http://60.101.108.100/', {
+ 'tns:IF_ECC_EVCP_PR_INFORMATIONRes': {
+ EV_TYPE: 'S',
+ },
+ });
+ }
+ ).catch((error) => {
+ // withSoapLogging에서 이미 에러 로그를 처리하므로, 여기서는 응답만 생성
+ return createSoapResponse('http://60.101.108.100/', {
+ 'tns:IF_ECC_EVCP_PR_INFORMATIONRes': {
+ EV_TYPE: 'E',
+ EV_MESSAGE:
+ error instanceof Error ? error.message.slice(0, 100) : 'Unknown error',
+ },
+ });
+ });
+}
+
+// -----------------------------------------------------------------------------
+// 데이터 변환 및 저장 관련 유틸리티
+// -----------------------------------------------------------------------------
+
+// Root XML Request 타입
+type PRRequestXML = {
+ CHG_GB?: string;
+ T_BID_HEADER?: BidHeaderXML[];
+ T_BID_ITEM?: BidItemXML[];
+};
+
+// XML -> DB 데이터 변환 함수
+function transformPRData(requestData: PRRequestXML): ProcessedPRData[] {
+ const headers = requestData.T_BID_HEADER || [];
+ const items = requestData.T_BID_ITEM || [];
+
+ return headers.map((header) => {
+ const headerKey = header.ANFNR || '';
+ const fkData = { ANFNR: headerKey };
+
+ // Header 변환
+ const bidHeaderConverted = convertXMLToDBData<BidHeaderData>(
+ header as Record<string, string | undefined>,
+ undefined // Header는 자체 필드만 사용
+ );
+
+ // 해당 Header의 Item들 필터 후 변환
+ const relatedItems = items.filter((item) => item.ANFNR === headerKey);
+
+ const bidItemsConverted = processNestedArray(
+ relatedItems,
+ (item) =>
+ convertXMLToDBData<BidItemData>(item as Record<string, string | undefined>, fkData),
+ fkData
+ );
+
+ return {
+ bidHeader: bidHeaderConverted,
+ bidItems: bidItemsConverted,
+ };
+ });
+}
+
+// 데이터베이스 저장 함수
+async function saveToDatabase(processedPRs: ProcessedPRData[]) {
+ console.log(`데이터베이스 저장 시작: ${processedPRs.length}개 PR 데이터`);
+
+ try {
+ await db.transaction(async (tx) => {
+ for (const prData of processedPRs) {
+ const { bidHeader, bidItems } = prData;
+
+ if (!bidHeader.ANFNR) {
+ console.warn('ANFNR가 없는 헤더 발견, 건너뜁니다.');
+ continue;
+ }
+
+ // 1. 헤더 테이블 Upsert (ANFNR 기준)
+ await tx
+ .insert(PR_INFORMATION_T_BID_HEADER)
+ .values(bidHeader)
+ .onConflictDoUpdate({
+ target: PR_INFORMATION_T_BID_HEADER.ANFNR,
+ set: {
+ ...bidHeader,
+ updatedAt: new Date(),
+ },
+ });
+
+ // 2. 아이템 테이블 전체 교체 (ANFNR FK 기준)
+ await replaceSubTableData(
+ tx,
+ PR_INFORMATION_T_BID_ITEM,
+ bidItems,
+ 'ANFNR',
+ bidHeader.ANFNR
+ );
+ }
+ });
+
+ console.log(`데이터베이스 저장 완료: ${processedPRs.length}개 PR`);
+ return true;
+ } catch (error) {
+ console.error('데이터베이스 저장 중 오류 발생:', error);
+ throw error;
+ }
+} \ No newline at end of file
diff --git a/app/api/mdg/send-vendor-xml/route.ts b/app/api/mdg/send-vendor-xml/route.ts
deleted file mode 100644
index 7f8d1daf..00000000
--- a/app/api/mdg/send-vendor-xml/route.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { NextRequest, NextResponse } from 'next/server';
-import { sendVendorEnvelopeToMDG } from '@/lib/soap/mdg/send/vendor-master/action';
-
-export async function POST(request: NextRequest) {
- try {
- const { envelope } = await request.json();
-
- if (!envelope || typeof envelope !== 'string') {
- return NextResponse.json(
- { success: false, message: 'envelope(XML) is required' },
- { status: 400 }
- );
- }
-
- const result = await sendVendorEnvelopeToMDG(envelope);
-
- return NextResponse.json(result);
- } catch (error) {
- console.error('[send-vendor-xml] error:', error);
- return NextResponse.json(
- {
- success: false,
- message: error instanceof Error ? error.message : 'Unknown error',
- },
- { status: 500 }
- );
- }
-} \ No newline at end of file