summaryrefslogtreecommitdiff
path: root/lib/bidding/selection/actions.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/bidding/selection/actions.ts')
-rw-r--r--lib/bidding/selection/actions.ts118
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 {