diff options
Diffstat (limited to 'lib/soap/ecc')
| -rw-r--r-- | lib/soap/ecc/mapper/common-mapper-utils.ts | 51 | ||||
| -rw-r--r-- | lib/soap/ecc/mapper/rfq-and-pr-mapper.ts | 17 | ||||
| -rw-r--r-- | lib/soap/ecc/send/create-po.ts | 49 |
3 files changed, 73 insertions, 44 deletions
diff --git a/lib/soap/ecc/mapper/common-mapper-utils.ts b/lib/soap/ecc/mapper/common-mapper-utils.ts index 2199490a..ed655e0e 100644 --- a/lib/soap/ecc/mapper/common-mapper-utils.ts +++ b/lib/soap/ecc/mapper/common-mapper-utils.ts @@ -307,6 +307,57 @@ export interface ProjectInfo { } /** + * 사번(PERNR)으로 사용자 정보 조회 함수 + * PERNR(사번)으로 users 테이블의 employeeNumber와 매칭하여 사용자 정보 조회 + */ +export async function findUserInfoByPERNR(PERNR: string | null): Promise<{ + userId: number; + userName: string; + userEmail: string | null; + userPhone: string | null; + employeeNumber: string | null; +} | null> { + try { + debugLog('사번으로 사용자 조회 시작', { PERNR }); + + if (!PERNR) { + debugError('PERNR이 null 또는 undefined', { PERNR }); + return null; + } + + const userResult = await db + .select({ + id: users.id, + name: users.name, + email: users.email, + phone: users.phone, + employeeNumber: users.employeeNumber + }) + .from(users) + .where(eq(users.employeeNumber, PERNR)) + .limit(1); + + if (userResult.length === 0) { + debugError('PERNR에 해당하는 사용자를 찾을 수 없음', { PERNR }); + return null; + } + + const userInfo = { + userId: userResult[0].id, + userName: userResult[0].name, + userEmail: userResult[0].email, + userPhone: userResult[0].phone, + employeeNumber: userResult[0].employeeNumber + }; + debugSuccess('사번으로 사용자 정보 찾음', { PERNR, userInfo }); + return userInfo; + } catch (error) { + debugError('사번으로 사용자 조회 중 오류 발생', { PERNR, error }); + return null; + } +} + +/** * 협력업체 코드(LIFNR)로 vendorId 찾기 * LIFNR = 벤더코드 (ex. A0001234) * vendors 테이블의 vendorCode 필드와 비교하여 vendorId를 찾음 diff --git a/lib/soap/ecc/mapper/rfq-and-pr-mapper.ts b/lib/soap/ecc/mapper/rfq-and-pr-mapper.ts index 85fbb918..d08bc5fb 100644 --- a/lib/soap/ecc/mapper/rfq-and-pr-mapper.ts +++ b/lib/soap/ecc/mapper/rfq-and-pr-mapper.ts @@ -24,6 +24,7 @@ import { findUserInfoByEKGRP, findProjectInfoByPSPID, parseSAPDateTime, + findUserInfoByPERNR, } from './common-mapper-utils'; // ECC 데이터 타입 정의 @@ -154,11 +155,13 @@ export async function mapECCRfqHeaderToRfqLast( const inChargeUserInfo = await findUserInfoByEKGRP(eccHeader.EKGRP || null); const inChargeUserId = inChargeUserInfo?.userId || null; - // 대표 PR Item 기반으로 projectId, itemCode, itemName 설정 (없으면 첫번째 PR Item 사용) + // 대표 PR Item 기반으로 projectId, itemCode, itemName, 설계담당자 설정 (없으면 첫번째 PR Item 사용) let projectId: number | null = null; let itemCode: string | null = null; let itemName: string | null = null; let prNumber: string | null = null; + let engPicName: string | null = null; + let engPicEmployeeNumber: string | null = null; let representativeItem: ECCBidItem | undefined; if (firstItem) { @@ -185,6 +188,15 @@ export async function mapECCRfqHeaderToRfqLast( // prNumber: 대표 PR의 BANFN 또는 첫번째 PR의 ZREQ_FN 값 prNumber = representativeItem?.BANFN || firstItem.ZREQ_FN || null; + + // 설계담당자 정보: 대표 PR Item의 PERNR로 조회 + if (targetItem.PERNR) { + const engPicInfo = await findUserInfoByPERNR(targetItem.PERNR); + if (engPicInfo) { + engPicName = engPicInfo.userName; + engPicEmployeeNumber = engPicInfo.employeeNumber; + } + } } // 매핑 @@ -199,7 +211,8 @@ export async function mapECCRfqHeaderToRfqLast( rfqSendDate: null, // ECC에서 제공되지 않음 status: 'RFQ 생성', // 한글 상태로 변경 rfqSealedYn: false, - EngPicName: null, // ECC에서 제공되지 않음 + EngPicName: engPicName, // 대표 PR Item의 PERNR로 조회한 설계담당자명 + EngPicEmployeeNumber: engPicEmployeeNumber, // 대표 PR Item의 PERNR(사번) packageNo: null, // ECC에서 제공되지 않음 packageName: null, // ECC에서 제공되지 않음 remark: null, diff --git a/lib/soap/ecc/send/create-po.ts b/lib/soap/ecc/send/create-po.ts index 0984a208..1e21d39c 100644 --- a/lib/soap/ecc/send/create-po.ts +++ b/lib/soap/ecc/send/create-po.ts @@ -44,23 +44,11 @@ export interface POItemData { EBELP?: string; // Series PO Item Seq } -// PR 반환 데이터 타입 -export interface PRReturnData { - ANFNR: string; // PR Request Number (M) - ANFPS: string; // Item Number of PR Request (M) - EBELN: string; // Purchase Requisition Number (M) - EBELP: string; // Item Number of Purchase Requisition (M) - MSGTY: string; // Message Type (M) - MSGTXT?: string; // Message Text -} - // PO 생성 요청 데이터 타입 +// 참고: T_PR_RETURN, EV_ERDAT, EV_ERZET는 응답용 필드이므로 요청에 포함하지 않음 export interface POCreateRequest { T_Bidding_HEADER: POHeaderData[]; T_Bidding_ITEM: POItemData[]; - T_PR_RETURN: PRReturnData[]; - EV_ERDAT?: string; // Extract Date - EV_ERZET?: string; // Extract Time } @@ -72,10 +60,8 @@ function createPOSoapBodyContent(poData: POCreateRequest): Record<string, unknow return { 'p1:MT_P2MM3015_S': { // WSDL에서 사용하는 p1 접두사 적용 'T_Bidding_HEADER': poData.T_Bidding_HEADER, - 'T_Bidding_ITEM': poData.T_Bidding_ITEM, - 'T_PR_RETURN': poData.T_PR_RETURN, - ...(poData.EV_ERDAT && { 'EV_ERDAT': poData.EV_ERDAT }), - ...(poData.EV_ERZET && { 'EV_ERZET': poData.EV_ERZET }) + 'T_Bidding_ITEM': poData.T_Bidding_ITEM + // T_PR_RETURN, EV_ERDAT, EV_ERZET는 응답용 필드이므로 요청에 포함하지 않음 } }; } @@ -112,19 +98,7 @@ function validatePOData(poData: POCreateRequest): { isValid: boolean; errors: st }); } - // PR 반환 데이터 검증 - if (!poData.T_PR_RETURN || poData.T_PR_RETURN.length === 0) { - errors.push('T_PR_RETURN은 필수입니다.'); - } else { - poData.T_PR_RETURN.forEach((prReturn, index) => { - const requiredFields = ['ANFNR', 'ANFPS', 'EBELN', 'EBELP', 'MSGTY']; - requiredFields.forEach(field => { - if (!prReturn[field as keyof PRReturnData]) { - errors.push(`T_PR_RETURN[${index}].${field}는 필수입니다.`); - } - }); - }); - } + // T_PR_RETURN은 응답용 필드이므로 검증하지 않음 return { isValid: errors.length === 0, @@ -165,7 +139,7 @@ async function sendPOToECC(poData: POCreateRequest): Promise<SoapSendResult> { }; console.log(`📤 PO 생성 요청 전송 시작 - ANFNR: ${poData.T_Bidding_HEADER[0]?.ANFNR}`); - console.log(`🔍 헤더 ${poData.T_Bidding_HEADER.length}개, 아이템 ${poData.T_Bidding_ITEM.length}개, PR 반환 ${poData.T_PR_RETURN.length}개`); + console.log(`🔍 헤더 ${poData.T_Bidding_HEADER.length}개, 아이템 ${poData.T_Bidding_ITEM.length}개`); // SOAP XML 전송 const result = await sendSoapXml(config, logInfo); @@ -341,17 +315,8 @@ export async function createTestPurchaseOrder(): Promise<{ LFDAT: getCurrentSAPDate(), ZCON_NO_PO: 'CON001', EBELP: '00001' - }], - T_PR_RETURN: [{ - ANFNR: 'TEST001', - ANFPS: '00001', - EBELN: 'PR001', - EBELP: '00001', - MSGTY: 'S', - MSGTXT: 'Test message' - }], - EV_ERDAT: getCurrentSAPDate(), - EV_ERZET: getCurrentSAPTime() + }] + // T_PR_RETURN, EV_ERDAT, EV_ERZET는 응답용 필드이므로 요청에 포함하지 않음 }; const result = await sendPOToECC(testPOData); |
