summaryrefslogtreecommitdiff
path: root/lib/soap
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-11-27 09:43:55 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-11-27 09:43:55 +0000
commitdaabc02e9ae54f216ada77aa826b349f37c3281a (patch)
tree74c6f94e0c66ee31dfeac2f355c5156431cd42e3 /lib/soap
parent5870b73785715d1585531e655c06d8c068eb64ac (diff)
(최겸) 구매 입찰 피드백 반영(80%완)
Diffstat (limited to 'lib/soap')
-rw-r--r--lib/soap/ecc/mapper/bidding-and-pr-mapper.ts50
1 files changed, 37 insertions, 13 deletions
diff --git a/lib/soap/ecc/mapper/bidding-and-pr-mapper.ts b/lib/soap/ecc/mapper/bidding-and-pr-mapper.ts
index 1a5089eb..9bdd238d 100644
--- a/lib/soap/ecc/mapper/bidding-and-pr-mapper.ts
+++ b/lib/soap/ecc/mapper/bidding-and-pr-mapper.ts
@@ -215,6 +215,17 @@ export async function mapECCBiddingHeaderToBidding(
// 첫번째 PR Item 가져오기 (관련 아이템들 중 첫번째)
const firstItem = eccItems.find(item => item.ANFNR === eccHeader.ANFNR);
+
+ // 대표 PR Item 찾기 (ZCON_NO_PO와 BANFN이 같은 아이템 - RFQ 매퍼 로직 반영)
+ const representativeItem = eccItems.find(item =>
+ item.ANFNR === eccHeader.ANFNR &&
+ item.ZCON_NO_PO &&
+ item.ZCON_NO_PO.trim() &&
+ item.BANFN === item.ZCON_NO_PO.trim()
+ );
+
+ // 타겟 아이템 설정 (대표 아이템 우선, 없으면 첫번째 아이템)
+ const targetItem = representativeItem || firstItem;
// 날짜 파싱 (실패시 현재 Date 들어감)
const createdAt = parseSAPDateTime(eccHeader.ZRFQ_TRS_DT || null, eccHeader.ZRFQ_TRS_TM || null);
@@ -222,23 +233,27 @@ export async function mapECCBiddingHeaderToBidding(
// 담당자 찾기
const inChargeUserInfo = await findUserInfoByEKGRP(eccHeader.EKGRP || null);
- // 첫번째 PR Item 기반으로 projectName, itemName 설정
+ // 타겟 PR Item 기반으로 projectName, itemName 설정
let projectName: string | null = null;
let itemName: string | null = null;
let prNumber: string | null = null;
- if (firstItem) {
- // projectName: 첫번째 PR Item의 PSPID와 projects.code 매칭
- const projectInfo = await findProjectInfoByPSPID(firstItem.PSPID || null);
+ if (targetItem) {
+ // projectName: 타겟 PR Item의 PSPID와 projects.code 매칭
+ const projectInfo = await findProjectInfoByPSPID(targetItem.PSPID || null);
if (projectInfo) {
projectName = projectInfo.name;
}
- // itemName: 첫번째 PR Item의 MATNR로 MDG에서 ZZNAME 조회
- itemName = await findMaterialNameByMATNR(firstItem.MATNR || null);
+ // itemName: 타겟 PR Item의 TXZ01(내역) 우선 사용, 없으면 MATNR로 조회
+ if (targetItem.TXZ01) {
+ itemName = targetItem.TXZ01;
+ } else {
+ itemName = await findMaterialNameByMATNR(targetItem.MATNR || null);
+ }
- // prNumber: 첫번째 PR의 ZREQ_FN 값
- prNumber = firstItem.ZREQ_FN || null;
+ // prNumber: 대표 PR의 BANFN 또는 타겟 PR의 ZREQ_FN 값
+ prNumber = representativeItem?.BANFN || targetItem.ZREQ_FN || null;
}
// 매핑
@@ -246,9 +261,9 @@ export async function mapECCBiddingHeaderToBidding(
biddingNumber, // 생성된 Bidding 코드
originalBiddingNumber: eccHeader.ANFNR || null, // 원입찰번호
revision: 0, // 기본 리비전 0 (I/F 해서 가져온 건 보낸 적 없으므로 0 고정)
- projectName, // 첫번째 PR Item의 PSPID로 찾은 프로젝트 이름
- itemName, // 첫번째 PR Item의 MATNR로 조회한 자재명
- title: `${firstItem?.PSPID || ''}${itemName || ''}입찰`, // PSPID+자재그룹명+계약구분+입찰, 계약구분은 없으니까 제외했음
+ projectName, // 타겟 PR Item의 PSPID로 찾은 프로젝트 이름
+ itemName, // 타겟 PR Item 정보로 조회한 자재명/내역
+ title: `${targetItem?.PSPID || ''}${itemName || ''}입찰`, // PSPID+자재명+입찰
description: null,
// 계약 정보 - ECC에서 제공되지 않으므로 기본값 설정
@@ -269,7 +284,7 @@ export async function mapECCBiddingHeaderToBidding(
hasSpecificationMeeting: false, // 기본값 처리하고, 입찰관리상세에서 사용자가 관리
// 예산 및 가격 정보
- currency: firstItem?.WAERS1,
+ currency: targetItem?.WAERS1,
budget: null,
targetPrice: null,
targetPriceCalculationCriteria: null,
@@ -286,6 +301,7 @@ export async function mapECCBiddingHeaderToBidding(
// 담당자 정보 - EKGRP 기반으로 설정 (입찰담당자로 매핑)
bidPicId: inChargeUserInfo?.userId || null,
bidPicName: inChargeUserInfo?.userName || null,
+ bidPicCode: eccHeader.EKGRP || null,
// 메타 정보
remarks: `ECC ANFNR: ${eccHeader.ANFNR}`,
@@ -409,7 +425,15 @@ export async function mapAndSaveECCBiddingData(
// 2) 헤더별 관련 아이템 그룹핑 + 헤더 매핑을 병렬로 수행 - 유효한 헤더만 사용
const biddingGroups = await Promise.all(
validHeaders.map(async (eccHeader) => {
- const relatedItems = eccItems.filter((item) => item.ANFNR === eccHeader.ANFNR);
+ // 아이템 필터링 및 정렬 (ANFPS 오름차순)
+ const relatedItems = eccItems
+ .filter((item) => item.ANFNR === eccHeader.ANFNR)
+ .sort((a, b) => {
+ const anfpsA = parseInt(a.ANFPS || '0', 10);
+ const anfpsB = parseInt(b.ANFPS || '0', 10);
+ return anfpsA - anfpsB;
+ });
+
const biddingNumber = biddingCodeMap.get(eccHeader.ANFNR) || `BID${eccHeader.EKGRP || 'UNKNOWN'}00001`;
// 헤더 매핑 시 아이템 정보와 생성된 Bidding 코드 전달
const biddingData = await mapECCBiddingHeaderToBidding(eccHeader, relatedItems, biddingNumber);