summaryrefslogtreecommitdiff
path: root/lib/soap/ecc/send/create-po.ts
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-11-19 16:57:14 +0900
committerjoonhoekim <26rote@gmail.com>2025-11-19 16:57:14 +0900
commit3284b70bacb7074d86170b38b7ec6b01e2aa6954 (patch)
treea8e5520e2463c57c1dc3f271875de2b14bbbccb5 /lib/soap/ecc/send/create-po.ts
parenta07cad76810349096d768116d3d38ca7ad664e33 (diff)
(김준회) RFQ PO 생성 I/F 변경 (IF_EVCP_ECC_PO_CREATE 는 입찰 전용 PO 생성, IF_EVCP_ECC_RFQ_INFORMATION 은 RFQ PO 생성) - RFQ_INFORMATION은 MAINTAIN PO/VO, PO_INFORMATION은 PO List Management (SAP 메뉴)
Diffstat (limited to 'lib/soap/ecc/send/create-po.ts')
-rw-r--r--lib/soap/ecc/send/create-po.ts367
1 files changed, 0 insertions, 367 deletions
diff --git a/lib/soap/ecc/send/create-po.ts b/lib/soap/ecc/send/create-po.ts
deleted file mode 100644
index 1e21d39c..00000000
--- a/lib/soap/ecc/send/create-po.ts
+++ /dev/null
@@ -1,367 +0,0 @@
-'use server'
-
-import { sendSoapXml, type SoapSendConfig, type SoapLogInfo, type SoapSendResult } from "@/lib/soap/sender";
-import { getCurrentSAPDate, getCurrentSAPTime } from "@/lib/soap/utils";
-
-// ECC PO 생성 엔드포인트 (WSDL에 명시된 P2038_D 사용)
-const ECC_PO_ENDPOINT = "http://shii8dvddb01.hec.serp.shi.samsung.net:50000/sap/xi/engine?type=entry&version=3.0&Sender.Service=P2038_Q&Interface=http%3A%2F%2Fshi.samsung.co.kr%2FP2_MM%2FMMM%5EP2MM3015_SO";
-
-// PO 헤더 데이터 타입
-export interface POHeaderData {
- ANFNR: string; // Bidding Number (M) // BiddingNumber이지만, RFQ/Bidding 모두 받은 ANFNR 사용하면 됩니다. (RFQ/Bidding Header Key)
- LIFNR: string; // Vendor Account Number (M)
- ZPROC_IND: string; // Purchasing Processing State (M)
- ANGNR?: string; // Bidding Number // 더 이상 사용하지 않음. 보내지 않으면 됨.
- WAERS: string; // Currency Key (M)
- ZTERM: string; // Terms of Payment Key (M)
- INCO1: string; // Incoterms (Part 1) (M) //인코텀즈코드 3자리
- INCO2: string; // Incoterms (Part 2) (M) //자유입력28자리, 인코텀즈 풀네임 28자리로 잘라 넣으면 될 듯... 데이터예시: 시방서에 따름
- VSTEL?: string; // Shipping Point
- LSTEL?: string; // loading Point
- MWSKZ: string; // Tax on sales/purchases code (M)
- LANDS: string; // Country for Tax (M)
- ZRCV_DT: string; // Bidding Receive Date (M)
- ZATTEN_IND: string; // Attendance Indicator (M)
- IHRAN: string; // Bidding Submission Date (M)
- TEXT?: string; // PO Header note
- ZDLV_CNTLR?: string; // Delivery Controller
- ZDLV_PRICE_T?: string; // 납품대금연동제대상여부
- ZDLV_PRICE_NOTE?: string; // 연동제 노트
-}
-
-// PO 아이템 데이터 타입
-export interface POItemData {
- ANFNR: string; // Bidding Number (M)
- ANFPS: string; // Item Number of Bidding (M)
- LIFNR: string; // Vendor Account Number (M)
- NETPR: string; // Net Price (M)
- PEINH: string; // Price Unit (M)
- BPRME: string; // Order Unit of Measure (M)
- NETWR: string; // Net Order Value (M)
- BRTWR: string; // Gross Order Value (M)
- LFDAT: string; // Item Delivery Date (M)
- ZCON_NO_PO?: string; // PR Consolidation Number
- EBELP?: string; // Series PO Item Seq
-}
-
-// PO 생성 요청 데이터 타입
-// 참고: T_PR_RETURN, EV_ERDAT, EV_ERZET는 응답용 필드이므로 요청에 포함하지 않음
-export interface POCreateRequest {
- T_Bidding_HEADER: POHeaderData[];
- T_Bidding_ITEM: POItemData[];
-}
-
-
-
-
-
-// SOAP Body Content 생성 함수
-function createPOSoapBodyContent(poData: POCreateRequest): Record<string, unknown> {
- return {
- 'p1:MT_P2MM3015_S': { // WSDL에서 사용하는 p1 접두사 적용
- 'T_Bidding_HEADER': poData.T_Bidding_HEADER,
- 'T_Bidding_ITEM': poData.T_Bidding_ITEM
- // T_PR_RETURN, EV_ERDAT, EV_ERZET는 응답용 필드이므로 요청에 포함하지 않음
- }
- };
-}
-
-// PO 데이터 검증 함수
-function validatePOData(poData: POCreateRequest): { isValid: boolean; errors: string[] } {
- const errors: string[] = [];
-
- // 헤더 데이터 검증
- if (!poData.T_Bidding_HEADER || poData.T_Bidding_HEADER.length === 0) {
- errors.push('T_Bidding_HEADER는 필수입니다.');
- } else {
- poData.T_Bidding_HEADER.forEach((header, index) => {
- const requiredFields = ['ANFNR', 'LIFNR', 'ZPROC_IND', 'WAERS', 'ZTERM', 'INCO1', 'INCO2', 'MWSKZ', 'LANDS', 'ZRCV_DT', 'ZATTEN_IND', 'IHRAN'];
- requiredFields.forEach(field => {
- if (!header[field as keyof POHeaderData]) {
- errors.push(`T_Bidding_HEADER[${index}].${field}는 필수입니다.`);
- }
- });
- });
- }
-
- // 아이템 데이터 검증
- if (!poData.T_Bidding_ITEM || poData.T_Bidding_ITEM.length === 0) {
- errors.push('T_Bidding_ITEM은 필수입니다.');
- } else {
- poData.T_Bidding_ITEM.forEach((item, index) => {
- const requiredFields = ['ANFNR', 'ANFPS', 'LIFNR', 'NETPR', 'PEINH', 'BPRME', 'NETWR', 'BRTWR', 'LFDAT'];
- requiredFields.forEach(field => {
- if (!item[field as keyof POItemData]) {
- errors.push(`T_Bidding_ITEM[${index}].${field}는 필수입니다.`);
- }
- });
- });
- }
-
- // T_PR_RETURN은 응답용 필드이므로 검증하지 않음
-
- return {
- isValid: errors.length === 0,
- errors
- };
-}
-
-// ECC로 PO 생성 SOAP XML 전송하는 함수
-async function sendPOToECC(poData: POCreateRequest): Promise<SoapSendResult> {
- try {
- // 데이터 검증 (경고만 출력, 전송은 계속 진행)
- const validation = validatePOData(poData);
- if (!validation.isValid) {
- console.warn('⚠️ PO 데이터 검증 경고 (전송은 계속 진행):', validation.errors.join(', '));
- console.warn('⚠️ 검증 실패한 PO 데이터:', JSON.stringify(poData, null, 2));
- }
-
- // SOAP Body Content 생성
- const soapBodyContent = createPOSoapBodyContent(poData);
-
- // SOAP 전송 설정
- const config: SoapSendConfig = {
- endpoint: ECC_PO_ENDPOINT,
- envelope: soapBodyContent,
- soapAction: 'http://sap.com/xi/WebService/soap1.1',
- timeout: 60000, // PO 생성은 60초 타임아웃
- retryCount: 3,
- retryDelay: 2000,
- namespace: 'http://shi.samsung.co.kr/P2_MM/MMM', // ECC MM 모듈 네임스페이스
- prefix: 'p1' // WSDL에서 사용하는 p1 접두사
- };
-
- // 로그 정보
- const logInfo: SoapLogInfo = {
- direction: 'OUTBOUND',
- system: 'S-ERP ECC',
- interface: 'IF_ECC_EVCP_PO_CREATE'
- };
-
- console.log(`📤 PO 생성 요청 전송 시작 - ANFNR: ${poData.T_Bidding_HEADER[0]?.ANFNR}`);
- console.log(`🔍 헤더 ${poData.T_Bidding_HEADER.length}개, 아이템 ${poData.T_Bidding_ITEM.length}개`);
-
- // SOAP XML 전송
- const result = await sendSoapXml(config, logInfo);
-
- if (result.success) {
- console.log(`✅ PO 생성 요청 전송 성공 - ANFNR: ${poData.T_Bidding_HEADER[0]?.ANFNR}`);
- } else {
- console.error(`❌ PO 생성 요청 전송 실패 - ANFNR: ${poData.T_Bidding_HEADER[0]?.ANFNR}, 오류: ${result.message}`);
- }
-
- return result;
-
- } catch (error) {
- console.error('❌ PO 생성 전송 중 오류 발생:', error);
- return {
- success: false,
- message: error instanceof Error ? error.message : 'Unknown error'
- };
- }
-}
-
-// ========================================
-// 메인 PO 생성 및 전송 함수들
-// ========================================
-
-// 단일 PO 생성 요청 처리
-export async function createPurchaseOrder(poData: POCreateRequest): Promise<{
- success: boolean;
- message: string;
- responseData?: string;
- statusCode?: number;
- headers?: Record<string, string>;
- endpoint?: string;
- requestXml?: string;
- bidding_number?: string;
-}> {
- try {
- console.log(`🚀 PO 생성 요청 시작 - ANFNR: ${poData.T_Bidding_HEADER[0]?.ANFNR}`);
-
- const result = await sendPOToECC(poData);
-
- return {
- success: result.success,
- message: result.success ? 'PO 생성 요청이 성공적으로 전송되었습니다.' : result.message,
- responseData: result.responseText,
- statusCode: result.statusCode,
- headers: result.headers,
- endpoint: result.endpoint,
- requestXml: result.requestXml,
- bidding_number: poData.T_Bidding_HEADER[0]?.ANFNR
- };
-
- } catch (error) {
- console.error('❌ PO 생성 요청 처리 실패:', error);
- return {
- success: false,
- message: error instanceof Error ? error.message : 'Unknown error'
- };
- }
-}
-
-// 여러 PO 배치 생성 요청 처리
-export async function createMultiplePurchaseOrders(poDataList: POCreateRequest[]): Promise<{
- success: boolean;
- message: string;
- results?: Array<{ bidding_number: string; success: boolean; error?: string }>;
-}> {
- try {
- console.log(`🚀 배치 PO 생성 요청 시작: ${poDataList.length}개`);
-
- const results: Array<{ bidding_number: string; success: boolean; error?: string }> = [];
-
- for (const poData of poDataList) {
- try {
- const bidding_number = poData.T_Bidding_HEADER[0]?.ANFNR || 'Unknown';
- console.log(`📤 PO 생성 처리 중: ${bidding_number}`);
-
- const result = await sendPOToECC(poData);
-
- if (result.success) {
- console.log(`✅ PO 생성 성공: ${bidding_number}`);
- results.push({
- bidding_number,
- success: true
- });
- } else {
- console.error(`❌ PO 생성 실패: ${bidding_number}, 오류: ${result.message}`);
- results.push({
- bidding_number,
- success: false,
- error: result.message
- });
- }
-
- // 배치 처리간 지연 (시스템 부하 방지)
- if (poDataList.length > 1) {
- await new Promise(resolve => setTimeout(resolve, 1000));
- }
-
- } catch (error) {
- const bidding_number = poData.T_Bidding_HEADER[0]?.ANFNR || 'Unknown';
- console.error(`❌ PO 생성 처리 실패: ${bidding_number}`, error);
- results.push({
- bidding_number,
- success: false,
- error: error instanceof Error ? error.message : 'Unknown error'
- });
- }
- }
-
- const successCount = results.filter(r => r.success).length;
- const failCount = results.length - successCount;
-
- console.log(`🎉 배치 PO 생성 완료: 성공 ${successCount}개, 실패 ${failCount}개`);
-
- return {
- success: failCount === 0,
- message: `배치 PO 생성 완료: 성공 ${successCount}개, 실패 ${failCount}개`,
- results
- };
-
- } catch (error) {
- console.error('❌ 배치 PO 생성 중 전체 오류 발생:', error);
- return {
- success: false,
- message: error instanceof Error ? error.message : 'Unknown error'
- };
- }
-}
-
-// 테스트용 PO 생성 함수 (샘플 데이터 포함)
-export async function createTestPurchaseOrder(): Promise<{
- success: boolean;
- message: string;
- responseData?: string;
- testData?: POCreateRequest;
-}> {
- try {
- console.log('🧪 테스트용 PO 생성 시작');
-
- // 테스트용 샘플 데이터 생성
- const testPOData: POCreateRequest = {
- T_Bidding_HEADER: [{
- ANFNR: 'TEST001',
- LIFNR: '1000000001',
- ZPROC_IND: 'A',
- ANGNR: 'TEST001',
- WAERS: 'KRW',
- ZTERM: '0001',
- INCO1: 'FOB',
- INCO2: 'Seoul, Korea',
- VSTEL: '001',
- LSTEL: '001',
- MWSKZ: 'V0',
- LANDS: 'KR',
- ZRCV_DT: getCurrentSAPDate(),
- ZATTEN_IND: 'Y',
- IHRAN: getCurrentSAPDate(),
- TEXT: 'Test PO Creation',
- ZDLV_CNTLR: '001',
- ZDLV_PRICE_T: 'N',
- ZDLV_PRICE_NOTE: 'Test Note'
- }],
- T_Bidding_ITEM: [{
- ANFNR: 'TEST001',
- ANFPS: '00001',
- LIFNR: '1000000001',
- NETPR: '1000.00',
- PEINH: '1',
- BPRME: 'EA',
- NETWR: '1000.00',
- BRTWR: '1100.00',
- LFDAT: getCurrentSAPDate(),
- ZCON_NO_PO: 'CON001',
- EBELP: '00001'
- }]
- // T_PR_RETURN, EV_ERDAT, EV_ERZET는 응답용 필드이므로 요청에 포함하지 않음
- };
-
- const result = await sendPOToECC(testPOData);
-
- return {
- success: result.success,
- message: result.success ? '테스트 PO 생성이 성공적으로 전송되었습니다.' : result.message,
- responseData: result.responseText,
- testData: testPOData
- };
-
- } catch (error) {
- console.error('❌ 테스트 PO 생성 실패:', error);
- return {
- success: false,
- message: error instanceof Error ? error.message : 'Unknown error'
- };
- }
-}
-
-// PO 생성 요청 상태 확인 함수 (향후 확장용)
-export async function checkPOCreationStatus(biddingNumber: string): Promise<{
- success: boolean;
- message: string;
- status?: string;
-}> {
- try {
- console.log(`🔍 PO 생성 상태 확인: ${biddingNumber}`);
-
- // 향후 ECC에서 상태 조회 API가 제공될 경우 구현
- // 현재는 기본 응답만 반환
-
- return {
- success: true,
- message: 'PO 생성 상태 확인 기능은 향후 구현 예정입니다.',
- status: 'PENDING'
- };
-
- } catch (error) {
- console.error('❌ PO 상태 확인 실패:', error);
- return {
- success: false,
- message: error instanceof Error ? error.message : 'Unknown error'
- };
- }
-}
-
-// 사용하지 않는 유틸리티 함수들 삭제 (linter 오류 해결)