From 087fc383a662d45a69b5971a6ad821209bcbaf5b Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Mon, 22 Sep 2025 12:04:24 +0900 Subject: (김준회) 수신부 UPSERT 제거 및 ANFNR 유니크 처리 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/soap/ecc/mapper/bidding-and-pr-mapper.ts | 56 +++------------------------- lib/soap/ecc/mapper/rfq-and-pr-mapper.ts | 41 ++------------------ 2 files changed, 9 insertions(+), 88 deletions(-) (limited to 'lib/soap/ecc') diff --git a/lib/soap/ecc/mapper/bidding-and-pr-mapper.ts b/lib/soap/ecc/mapper/bidding-and-pr-mapper.ts index 0ec9af0f..4db8d451 100644 --- a/lib/soap/ecc/mapper/bidding-and-pr-mapper.ts +++ b/lib/soap/ecc/mapper/bidding-and-pr-mapper.ts @@ -195,7 +195,8 @@ export async function mapECCBiddingHeaderToBidding( contractType: 'general', // 일반계약 기본값 (notNull) biddingType: 'equipment', // 입찰유형 기본값 (notNull) awardCount: 'single', // 낙찰수 기본값 (notNull) - contractPeriod: null, // ECC에서 제공 X + contractStartDate: null, // ECC에서 제공 X + contractEndDate: null, // ECC에서 제공 X // 일정 관리 - ECC에서 제공되지 않음 preQuoteDate: null, @@ -328,30 +329,10 @@ export async function mapAndSaveECCBiddingData( const biddingRecords = biddingGroups.map((g) => g.biddingData); - // 3) Bidding 다건 삽입 (중복은 무시). 반환된 레코드로 일부 ID 매핑 - // ANFNR 기반으로 중복 방지 + // 3) Bidding 다건 삽입 const inserted = await tx .insert(biddings) .values(biddingRecords) - .onConflictDoUpdate({ - target: biddings.ANFNR, - set: { - updatedAt: new Date(), - // ANFNR이 같으면 기존 데이터의 주요 필드들을 업데이트 - biddingNumber: sql`EXCLUDED."bidding_number"`, - projectId: sql`EXCLUDED."project_id"`, - projectName: sql`EXCLUDED."project_name"`, - itemName: sql`EXCLUDED."item_name"`, - title: sql`EXCLUDED."title"`, - currency: sql`EXCLUDED."currency"`, - prNumber: sql`EXCLUDED."pr_number"`, - managerName: sql`EXCLUDED."manager_name"`, - managerPhone: sql`EXCLUDED."manager_phone"`, - remarks: sql`EXCLUDED."remarks"`, - createdBy: sql`EXCLUDED."created_by"`, - updatedBy: sql`EXCLUDED."updated_by"`, - } - }) .returning({ id: biddings.id, biddingNumber: biddings.biddingNumber }); const biddingNumberToId = new Map(); @@ -361,22 +342,7 @@ export async function mapAndSaveECCBiddingData( } } - // 4) 모든 Bidding 코드에 대한 ID 매핑 보완 (업데이트된 경우 포함) - const allCodes = biddingRecords - .map((r) => r.biddingNumber) - .filter((c): c is string => typeof c === 'string' && c.length > 0); - const missingCodes = allCodes.filter((c) => !biddingNumberToId.has(c)); - if (missingCodes.length > 0) { - const existing = await tx - .select({ id: biddings.id, biddingNumber: biddings.biddingNumber }) - .from(biddings) - .where(inArray(biddings.biddingNumber, missingCodes)); - for (const row of existing) { - if (row.biddingNumber) { - biddingNumberToId.set(row.biddingNumber, row.id); - } - } - } + // 4) 모든 새로 삽입된 레코드의 ID 매핑은 이미 완료됨 // 5) 모든 아이템을 한 번에 생성할 데이터로 변환 const allItemsToInsert: PrItemForBiddingData[] = []; @@ -395,19 +361,7 @@ export async function mapAndSaveECCBiddingData( } } - // 6) 기존 아이템들 삭제 후 새로 삽입 (upsert 대신 replace 방식) - const updatedBiddingIds = Array.from(new Set( - allItemsToInsert.map(item => item.biddingId).filter(id => id !== undefined) - )); - - if (updatedBiddingIds.length > 0) { - // 기존 아이템들 삭제 - await tx - .delete(prItemsForBidding) - .where(inArray(prItemsForBidding.biddingId, updatedBiddingIds)); - } - - // 아이템 일괄 삽입 (chunk 처리로 파라미터 제한 회피) + // 6) 아이템 일괄 삽입 (chunk 처리로 파라미터 제한 회피) const ITEM_CHUNK_SIZE = 1000; for (let i = 0; i < allItemsToInsert.length; i += ITEM_CHUNK_SIZE) { const chunk = allItemsToInsert.slice(i, i + ITEM_CHUNK_SIZE); diff --git a/lib/soap/ecc/mapper/rfq-and-pr-mapper.ts b/lib/soap/ecc/mapper/rfq-and-pr-mapper.ts index 7d1c2ab8..8748e244 100644 --- a/lib/soap/ecc/mapper/rfq-and-pr-mapper.ts +++ b/lib/soap/ecc/mapper/rfq-and-pr-mapper.ts @@ -14,14 +14,12 @@ import { PR_INFORMATION_T_BID_HEADER, PR_INFORMATION_T_BID_ITEM, } from '@/db/schema/ECC/ecc'; -import { eq, inArray, max, sql } from 'drizzle-orm'; +import { eq, max } from 'drizzle-orm'; import { findUserInfoByEKGRP, findProjectInfoByPSPID, findMaterialNameByMATNR, parseSAPDateTime, - type UserInfo, - type ProjectInfo, } from './common-mapper-utils'; // ECC 데이터 타입 정의 @@ -309,26 +307,10 @@ export async function mapAndSaveECCRfqDataToRfqLast( const rfqRecords = rfqGroups.map((g) => g.rfqData); - // 3) RFQ 다건 삽입 (중복은 무시). 반환된 레코드로 일부 ID 매핑 - // ANFNR 기반으로 중복 방지 (ECC에서 오는 실제 비즈니스 키) + // 3) RFQ 다건 삽입 const inserted = await tx .insert(rfqsLast) .values(rfqRecords) - .onConflictDoUpdate({ - target: rfqsLast.ANFNR, - set: { - updatedAt: new Date(), - // ANFNR이 같으면 기존 데이터를 업데이트 - projectId: sql`EXCLUDED."project_id"`, - series: sql`EXCLUDED."series"`, - itemCode: sql`EXCLUDED."item_code"`, - itemName: sql`EXCLUDED."item_name"`, - picCode: sql`EXCLUDED."pic_code"`, - pic: sql`EXCLUDED."pic"`, - prNumber: sql`EXCLUDED."pr_number"`, - prIssueDate: sql`EXCLUDED."pr_issue_date"`, - } - }) .returning({ id: rfqsLast.id, rfqCode: rfqsLast.rfqCode }); const rfqCodeToId = new Map(); @@ -338,22 +320,7 @@ export async function mapAndSaveECCRfqDataToRfqLast( } } - // 4) 모든 RFQ 코드에 대한 ID 매핑 보완 (업데이트된 경우 포함) - const allCodes = rfqRecords - .map((r) => r.rfqCode) - .filter((c): c is string => typeof c === 'string' && c.length > 0); - const missingCodes = allCodes.filter((c) => !rfqCodeToId.has(c)); - if (missingCodes.length > 0) { - const existing = await tx - .select({ id: rfqsLast.id, rfqCode: rfqsLast.rfqCode }) - .from(rfqsLast) - .where(inArray(rfqsLast.rfqCode, missingCodes)); - for (const row of existing) { - if (row.rfqCode) { - rfqCodeToId.set(row.rfqCode, row.id); - } - } - } + // 4) 모든 새로 삽입된 레코드의 ID 매핑은 이미 완료됨 // 5) 모든 아이템을 한 번에 생성할 데이터로 변환 const allItemsToInsert: RfqPrItemData[] = []; @@ -372,7 +339,7 @@ export async function mapAndSaveECCRfqDataToRfqLast( } } - // 6) 아이템 일괄 삽입 (chunk 처리로 파라미터 제한 회피) + // 5) 아이템 일괄 삽입 (chunk 처리로 파라미터 제한 회피) const ITEM_CHUNK_SIZE = 1000; for (let i = 0; i < allItemsToInsert.length; i += ITEM_CHUNK_SIZE) { const chunk = allItemsToInsert.slice(i, i + ITEM_CHUNK_SIZE); -- cgit v1.2.3