diff options
Diffstat (limited to 'lib/bidding/selection/actions.ts')
| -rw-r--r-- | lib/bidding/selection/actions.ts | 118 |
1 files changed, 54 insertions, 64 deletions
diff --git a/lib/bidding/selection/actions.ts b/lib/bidding/selection/actions.ts index 91550960..06dcbea1 100644 --- a/lib/bidding/selection/actions.ts +++ b/lib/bidding/selection/actions.ts @@ -237,12 +237,14 @@ export async function getQuotationHistory(biddingId: number, vendorId: number) { .where(eq(biddings.originalBiddingNumber, baseNumber)) .orderBy(biddings.createdAt) - // 각 bidding에 대한 벤더의 견적 정보 조회 + // 각 bidding에 대한 벤더의 견적 정보 및 상세 아이템 조회 const historyPromises = relatedBiddings.map(async (bidding) => { + // 1. 견적 헤더 정보 조회 (ID 포함) const biddingCompanyData = await db .select({ + id: biddingCompanies.id, finalQuoteAmount: biddingCompanies.finalQuoteAmount, - responseSubmittedAt: biddingCompanies.responseSubmittedAt, + responseSubmittedAt: biddingCompanies.finalQuoteSubmittedAt, isFinalSubmission: biddingCompanies.isFinalSubmission }) .from(biddingCompanies) @@ -256,84 +258,72 @@ export async function getQuotationHistory(biddingId: number, vendorId: number) { return null } - return { - biddingId: bidding.id, - biddingNumber: bidding.biddingNumber, - finalQuoteAmount: biddingCompanyData[0].finalQuoteAmount, - responseSubmittedAt: biddingCompanyData[0].responseSubmittedAt, - isFinalSubmission: biddingCompanyData[0].isFinalSubmission, - targetPrice: bidding.targetPrice, - currency: bidding.currency - } - }) - - const historyData = (await Promise.all(historyPromises)).filter(Boolean) - - // biddingNumber의 suffix를 기준으로 정렬 (-01, -02, -03 등) - const sortedHistory = historyData.sort((a, b) => { - const aSuffix = a!.biddingNumber.split('-')[1] ? parseInt(a!.biddingNumber.split('-')[1]) : 0 - const bSuffix = b!.biddingNumber.split('-')[1] ? parseInt(b!.biddingNumber.split('-')[1]) : 0 - return aSuffix - bSuffix - }) - - // PR 항목 정보 조회 (현재 bidding 기준) - const prItems = await db - .select({ - id: prItemsForBidding.id, - itemNumber: prItemsForBidding.itemNumber, - itemInfo: prItemsForBidding.itemInfo, - quantity: prItemsForBidding.quantity, - quantityUnit: prItemsForBidding.quantityUnit, - requestedDeliveryDate: prItemsForBidding.requestedDeliveryDate - }) - .from(prItemsForBidding) - .where(eq(prItemsForBidding.biddingId, biddingId)) - - // 각 히스토리 항목에 대한 PR 아이템 견적 조회 - const history = await Promise.all(sortedHistory.map(async (item, index) => { - // 각 bidding에 대한 PR 아이템 견적 조회 + // 2. 아이템별 견적 및 상세 정보 조회 (Join 사용) const prItemBids = await db .select({ - prItemId: companyPrItemBids.prItemId, + // 견적 정보 bidUnitPrice: companyPrItemBids.bidUnitPrice, bidAmount: companyPrItemBids.bidAmount, - proposedDeliveryDate: companyPrItemBids.proposedDeliveryDate + proposedDeliveryDate: companyPrItemBids.proposedDeliveryDate, + // 아이템 상세 정보 + prItemId: prItemsForBidding.id, + itemNumber: prItemsForBidding.itemNumber, + itemInfo: prItemsForBidding.itemInfo, + quantity: prItemsForBidding.quantity, + quantityUnit: prItemsForBidding.quantityUnit, + requestedDeliveryDate: prItemsForBidding.requestedDeliveryDate }) .from(companyPrItemBids) - .where(and( - eq(companyPrItemBids.biddingId, item!.biddingId), - eq(companyPrItemBids.companyId, vendorId) - )) - - const targetPrice = item!.targetPrice ? parseFloat(item!.targetPrice.toString()) : null - const totalAmount = parseFloat(item!.finalQuoteAmount.toString()) + .innerJoin(prItemsForBidding, eq(companyPrItemBids.prItemId, prItemsForBidding.id)) + .where(eq(companyPrItemBids.biddingCompanyId, biddingCompanyData[0].id)) + + // 아이템 매핑 + const items = prItemBids.map(bid => ({ + itemCode: bid.itemNumber || `ITEM${bid.prItemId}`, + itemName: bid.itemInfo || '품목 정보 없음', + quantity: bid.quantity ? parseFloat(bid.quantity.toString()) : 0, + unit: bid.quantityUnit || 'EA', + unitPrice: bid.bidUnitPrice ? parseFloat(bid.bidUnitPrice.toString()) : 0, + totalPrice: bid.bidAmount ? parseFloat(bid.bidAmount.toString()) : 0, + deliveryDate: bid.proposedDeliveryDate + ? new Date(bid.proposedDeliveryDate) + : bid.requestedDeliveryDate + ? new Date(bid.requestedDeliveryDate) + : new Date() + })) + + const targetPrice = bidding.targetPrice ? parseFloat(bidding.targetPrice.toString()) : null + const totalAmount = parseFloat(biddingCompanyData[0].finalQuoteAmount.toString()) const vsTargetPrice = targetPrice && targetPrice > 0 ? ((totalAmount - targetPrice) / targetPrice) * 100 : 0 - const items = prItemBids.map(bid => { - const prItem = prItems.find(p => p.id === bid.prItemId) - return { - itemCode: prItem?.itemNumber || `ITEM${bid.prItemId}`, - itemName: prItem?.itemInfo || '품목 정보 없음', - quantity: prItem?.quantity || 0, - unit: prItem?.quantityUnit || 'EA', - unitPrice: parseFloat(bid.bidUnitPrice.toString()), - totalPrice: parseFloat(bid.bidAmount.toString()), - deliveryDate: bid.proposedDeliveryDate ? new Date(bid.proposedDeliveryDate) : prItem?.requestedDeliveryDate ? new Date(prItem.requestedDeliveryDate) : new Date() - } - }) - return { - id: item!.biddingId, - round: index + 1, // 1차, 2차, 3차... - submittedAt: new Date(item!.responseSubmittedAt), + biddingId: bidding.id, + biddingNumber: bidding.biddingNumber, + submittedAt: new Date(biddingCompanyData[0].responseSubmittedAt), totalAmount, - currency: item!.currency || 'KRW', + currency: bidding.currency || 'KRW', vsTargetPrice: parseFloat(vsTargetPrice.toFixed(2)), items } + }) + + const historyData = (await Promise.all(historyPromises)).filter(Boolean) + + // biddingNumber의 suffix를 기준으로 정렬 (-01, -02, -03 등) + const sortedHistory = historyData.sort((a, b) => { + const aSuffix = a!.biddingNumber.split('-')[1] ? parseInt(a!.biddingNumber.split('-')[1]) : 0 + const bSuffix = b!.biddingNumber.split('-')[1] ? parseInt(b!.biddingNumber.split('-')[1]) : 0 + return aSuffix - bSuffix + }) + + // 회차 정보 추가 + const history = sortedHistory.map((item, index) => ({ + id: item!.biddingId, + round: index + 1, // 1차, 2차, 3차... + ...item! })) return { |
