summaryrefslogtreecommitdiff
path: root/lib/bidding/handlers.ts
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-11-25 11:51:27 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-11-25 11:51:27 +0000
commit835df8ddc115ffa74414db2a4fab7efc0d0056a9 (patch)
treebfe814c7b51ee1541d84b6e2dee01f28594763ac /lib/bidding/handlers.ts
parent6160e8bd61360ada9e8e0574671c38292eaba9e7 (diff)
(최겸) 구매 입찰 수정v2
Diffstat (limited to 'lib/bidding/handlers.ts')
-rw-r--r--lib/bidding/handlers.ts130
1 files changed, 46 insertions, 84 deletions
diff --git a/lib/bidding/handlers.ts b/lib/bidding/handlers.ts
index d55107c0..a5cc72ae 100644
--- a/lib/bidding/handlers.ts
+++ b/lib/bidding/handlers.ts
@@ -289,83 +289,47 @@ export async function mapBiddingInvitationToTemplateVariables(payload: {
* @returns 템플릿 변수 객체 (Record<string, string>)
*/
export async function mapBiddingClosureToTemplateVariables(payload: {
- biddingId: number;
+ bidding: {
+ id: number;
+ title: string;
+ biddingNumber: string;
+ projectName: string | null;
+ itemName: string | null;
+ biddingType: string;
+ bidPicName: string | null;
+ supplyPicName: string | null;
+ targetPrice: string | null;
+ };
+ biddingItems: Array<{
+ id: number;
+ materialCode: string | null;
+ materialCodeName: string | null;
+ quantity: string | null;
+ quantityUnit: string | null;
+ targetUnitPrice: string | null;
+ currency: string | null;
+ }>;
+ vendorSubmissions: Array<{
+ vendorId: number | null;
+ vendorName: string | null;
+ vendorCode: string | null;
+ finalQuoteAmount: string | null;
+ submitted: boolean | null;
+ targetPrice: string | null;
+ }>;
description: string;
requestedAt: Date;
}): Promise<Record<string, string>> {
- const { biddingId, description, requestedAt } = payload;
-
- // 1. 입찰 정보 조회
- debugLog('[BiddingClosureMapper] 입찰 정보 조회 시작');
- const { default: db } = await import('@/db/db');
- const { biddings, prItemsForBidding, biddingCompanies, biddingVendorSubmissions } = await import('@/db/schema');
- const { eq, leftJoin } = await import('drizzle-orm');
-
- const biddingInfo = await db
- .select({
- id: biddings.id,
- title: biddings.title,
- biddingNumber: biddings.biddingNumber,
- projectName: biddings.projectName,
- itemName: biddings.itemName,
- biddingType: biddings.biddingType,
- bidPicName: biddings.bidPicName,
- supplyPicName: biddings.supplyPicName,
- targetPrice: biddings.targetPrice,
- winnerCount: biddings.winnerCount,
- })
- .from(biddings)
- .where(eq(biddings.id, biddingId))
- .limit(1);
-
- if (biddingInfo.length === 0) {
- debugError('[BiddingClosureMapper] 입찰 정보를 찾을 수 없음');
- throw new Error('입찰 정보를 찾을 수 없습니다');
- }
-
- const bidding = biddingInfo[0];
-
- // 2. 입찰 대상 자재 정보 조회
- const biddingItemsInfo = await db
- .select({
- id: prItemsForBidding.id,
- materialCode: prItemsForBidding.materialNumber,
- materialCodeName: prItemsForBidding.materialInfo,
- quantity: prItemsForBidding.quantity,
- quantityUnit: prItemsForBidding.quantityUnit,
- targetUnitPrice: prItemsForBidding.targetUnitPrice,
- currency: prItemsForBidding.targetCurrency,
- })
- .from(prItemsForBidding)
- .where(eq(prItemsForBidding.biddingId, biddingId));
-
- // 3. 입찰 참여 업체 및 제출 정보 조회
- const vendorSubmissions = await db
- .select({
- vendorId: biddingCompanies.vendorId,
- vendorName: biddingCompanies.vendorName,
- vendorCode: biddingCompanies.vendorCode,
- targetPrice: biddingVendorSubmissions.targetPrice,
- bidPrice: biddingVendorSubmissions.bidPrice,
- submitted: biddingVendorSubmissions.submitted,
- })
- .from(biddingCompanies)
- .leftJoin(biddingVendorSubmissions, eq(biddingCompanies.id, biddingVendorSubmissions.biddingCompanyId))
- .where(eq(biddingCompanies.biddingId, biddingId));
-
- debugLog('[BiddingClosureMapper] 입찰 정보 조회 완료', {
- biddingId,
- itemCount: biddingItemsInfo.length,
- vendorCount: vendorSubmissions.length,
- });
+ const { bidding, biddingItems, vendorSubmissions, description, requestedAt } = payload;
// 기본 정보 매핑
const title = bidding.title || '폐찰';
const biddingTitle = bidding.title || '';
const biddingNumber = bidding.biddingNumber || '';
- const winnerCount = (bidding.winnerCount || 1).toString();
+ const winnerCount = '1'; // 기본값
const contractType = bidding.biddingType || '';
- const targetPrice = bidding.targetPrice ? bidding.targetPrice.toLocaleString() : '';
+ const targetPriceNum = bidding.targetPrice ? parseFloat(bidding.targetPrice) : 0;
+ const targetPrice = targetPriceNum ? targetPriceNum.toLocaleString() : '';
const biddingManager = bidding.bidPicName || bidding.supplyPicName || '';
const biddingOverview = bidding.itemName || '';
@@ -374,36 +338,34 @@ export async function mapBiddingClosureToTemplateVariables(payload: {
// 협력사별 입찰 현황 매핑
const vendorVariables: Record<string, string> = {};
- vendorSubmissions.forEach((vendor, index) => {
+ vendorSubmissions.filter(vendor => vendor.vendorId && vendor.vendorName).forEach((vendor, index) => {
const num = index + 1;
+ const targetPriceNum = vendor.targetPrice ? parseFloat(vendor.targetPrice) : 0;
+ const finalQuoteAmountNum = vendor.finalQuoteAmount ? parseFloat(vendor.finalQuoteAmount) : 0;
+
vendorVariables[`협력사_코드_${num}`] = vendor.vendorCode || '';
vendorVariables[`협력사명_${num}`] = vendor.vendorName || '';
vendorVariables[`응찰유무_${num}`] = vendor.submitted ? '응찰' : '미응찰';
- vendorVariables[`내정가_${num}`] = vendor.targetPrice ? vendor.targetPrice.toLocaleString() : '';
- vendorVariables[`입찰가_${num}`] = vendor.bidPrice ? vendor.bidPrice.toLocaleString() : '';
- vendorVariables[`비율_${num}`] = (vendor.targetPrice && vendor.bidPrice && vendor.targetPrice > 0)
- ? ((vendor.bidPrice / vendor.targetPrice) * 100).toFixed(2) + '%'
+ vendorVariables[`내정가_${num}`] = targetPriceNum ? targetPriceNum.toLocaleString() : '';
+ vendorVariables[`입찰가_${num}`] = finalQuoteAmountNum ? finalQuoteAmountNum.toLocaleString() : '';
+ vendorVariables[`비율_${num}`] = (targetPriceNum && finalQuoteAmountNum && targetPriceNum > 0)
+ ? ((finalQuoteAmountNum / targetPriceNum) * 100).toFixed(2) + '%'
: '';
});
// 품목별 입찰 정보 매핑 (간소화 - 첫 번째 품목 기준으로 매핑)
const materialVariables: Record<string, string> = {};
- biddingItemsInfo.forEach((item, index) => {
+ biddingItems.forEach((item, index) => {
const num = index + 1;
+ const quantityNum = item.quantity ? parseFloat(item.quantity) : 0;
+ const targetUnitPriceNum = item.targetUnitPrice ? parseFloat(item.targetUnitPrice) : 0;
+
materialVariables[`품목코드_${num}`] = item.materialCode || '';
materialVariables[`품목명_${num}`] = item.materialCodeName || '';
- materialVariables[`수량_${num}`] = item.quantity ? item.quantity.toLocaleString() : '';
+ materialVariables[`수량_${num}`] = quantityNum ? quantityNum.toString() : '';
materialVariables[`단위_${num}`] = item.quantityUnit || '';
materialVariables[`통화_${num}`] = item.currency || '';
- materialVariables[`내정가_${num}`] = item.targetUnitPrice ? item.targetUnitPrice.toLocaleString() : '';
-
- // 각 품목에 대한 협력사별 입찰가 (간소화: 동일 품목에 대한 모든 업체 입찰가 표시)
- vendorSubmissions.forEach((vendor, vendorIndex) => {
- const vendorNum = vendorIndex + 1;
- materialVariables[`협력사코드_${num}`] = vendor.vendorCode || '';
- materialVariables[`협력사명_${num}`] = vendor.vendorName || '';
- materialVariables[`입찰가_${num}`] = vendor.bidPrice ? vendor.bidPrice.toLocaleString() : '';
- });
+ materialVariables[`내정가_${num}`] = targetUnitPriceNum ? targetUnitPriceNum.toString() : '';
});
return {