From b67861fbb424c7ad47ad1538f75e2945bd8890c5 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Fri, 19 Sep 2025 04:25:04 +0000 Subject: (최겸) 구매 입찰 to contract 수정 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/bidding/actions.ts | 372 +++++++++++++++------ lib/bidding/list/biddings-transmission-dialog.tsx | 144 +++++++- .../table/bidding-pre-quote-invitation-dialog.tsx | 5 +- lib/bidding/vendor/partners-bidding-pre-quote.tsx | 51 ++- 4 files changed, 446 insertions(+), 126 deletions(-) (limited to 'lib') diff --git a/lib/bidding/actions.ts b/lib/bidding/actions.ts index 65ff3138..ae1fb54f 100644 --- a/lib/bidding/actions.ts +++ b/lib/bidding/actions.ts @@ -1,11 +1,12 @@ "use server" import db from "@/db/db" -import { eq, and, sql } from "drizzle-orm" +import { eq, and } from "drizzle-orm" import { biddings, biddingCompanies, prItemsForBidding, + companyPrItemBids, vendors, generalContracts, generalContractItems, @@ -38,51 +39,23 @@ export async function transmitToContract(biddingId: number, userId: number) { const biddingCondition = biddingConditionData.length > 0 ? biddingConditionData[0] : null - // 3. 낙찰된 업체들 조회 (별도 쿼리) - let winnerCompaniesData: { companyId: number; finalQuoteAmount: string | null; vendorCode: string | null; vendorName: string | null; }[] = [] - try { - // 2.1 biddingCompanies만 먼저 조회 (join 제거) - const biddingCompaniesRaw = await db.select() - .from(biddingCompanies) - .where( - and( - eq(biddingCompanies.biddingId, biddingId), - eq(biddingCompanies.isWinner, true) - ) - ) - - - // 2.2 각 company에 대한 vendor 정보 개별 조회 - for (const bc of biddingCompaniesRaw) { - try { - const vendorData = await db.select() - .from(vendors) - .where(eq(vendors.id, bc.companyId)) - .limit(1) - - const vendor = vendorData.length > 0 ? vendorData[0] : null - winnerCompaniesData.push({ - companyId: bc.companyId, - finalQuoteAmount: bc.finalQuoteAmount, - vendorCode: vendor?.vendorCode || null, - vendorName: vendor?.vendorName || null as string | null, - }) - } catch (vendorError) { - console.error('Vendor query error for', bc.companyId, ':', vendorError) - // vendor 정보 없이도 진행 - winnerCompaniesData.push({ - companyId: bc.companyId, - finalQuoteAmount: bc.finalQuoteAmount, - vendorCode: null as string | null, - vendorName: null as string | null, - }) - } - } - - } catch (queryError) { - console.error('Query error:', queryError) - throw new Error(`biddingCompanies 쿼리 실패: ${queryError}`) - } + // 3. 낙찰된 업체들 조회 (biddingCompanies.id 포함) + const winnerCompaniesData = await db.select({ + id: biddingCompanies.id, + companyId: biddingCompanies.companyId, + finalQuoteAmount: biddingCompanies.finalQuoteAmount, + awardRatio: biddingCompanies.awardRatio, + vendorCode: vendors.vendorCode, + vendorName: vendors.vendorName + }) + .from(biddingCompanies) + .leftJoin(vendors, eq(biddingCompanies.companyId, vendors.id)) + .where( + and( + eq(biddingCompanies.biddingId, biddingId), + eq(biddingCompanies.isWinner, true) + ) + ) // 상태 검증 if (biddingData.status !== 'vendor_selected') { @@ -95,10 +68,47 @@ export async function transmitToContract(biddingId: number, userId: number) { } for (const winnerCompany of winnerCompaniesData) { + // winnerCompany에서 직접 정보 사용 + const awardRatio = (Number(winnerCompany.awardRatio) || 100) / 100 + const biddingCompanyId = winnerCompany.id + + // 현재 winnerCompany의 입찰 데이터 조회 + const companyBids = await db.select({ + prItemId: companyPrItemBids.prItemId, + proposedDeliveryDate: companyPrItemBids.proposedDeliveryDate, + bidUnitPrice: companyPrItemBids.bidUnitPrice, + bidAmount: companyPrItemBids.bidAmount, + currency: companyPrItemBids.currency, + // PR 아이템 정보도 함께 조회 + itemNumber: prItemsForBidding.itemNumber, + itemInfo: prItemsForBidding.itemInfo, + materialDescription: prItemsForBidding.materialDescription, + quantity: prItemsForBidding.quantity, + quantityUnit: prItemsForBidding.quantityUnit, + totalWeight: prItemsForBidding.totalWeight, + weightUnit: prItemsForBidding.weightUnit, + }) + .from(companyPrItemBids) + .leftJoin(prItemsForBidding, eq(companyPrItemBids.prItemId, prItemsForBidding.id)) + .where(eq(companyPrItemBids.biddingCompanyId, biddingCompanyId)) + + // 발주비율에 따른 최종 계약금액 계산 + let totalContractAmount = 0 + if (companyBids.length > 0) { + for (const bid of companyBids) { + const originalQuantity = Number(bid.quantity) || 0 + const bidUnitPrice = Number(bid.bidUnitPrice) || 0 + const finalQuantity = originalQuantity * awardRatio + const finalAmount = finalQuantity * bidUnitPrice + totalContractAmount += finalAmount + } + } + // 계약 번호 자동 생성 (실제 규칙에 맞게) const contractNumber = await generateContractNumber(userId.toString(), biddingData.contractType) console.log('Generated contractNumber:', contractNumber) - // general-contract 생성 + + // general-contract 생성 (발주비율 계산된 최종 금액 사용) const contractResult = await db.insert(generalContracts).values({ contractNumber, revision: 0, @@ -108,9 +118,9 @@ export async function transmitToContract(biddingId: number, userId: number) { name: biddingData.title, vendorId: winnerCompany.companyId, linkedBidNumber: biddingData.biddingNumber, - contractAmount: winnerCompany.finalQuoteAmount || null, - contractStartDate: biddingData.contractStartDate || null, - contractEndDate: biddingData.contractEndDate || null, + contractAmount: totalContractAmount || null, // 발주비율 계산된 최종 금액 사용 + startDate: biddingData.contractStartDate || null, + endDate: biddingData.contractEndDate || null, currency: biddingData.currency || 'KRW', // 계약 조건 정보 추가 paymentTerm: biddingCondition?.paymentTerms || null, @@ -124,33 +134,39 @@ export async function transmitToContract(biddingId: number, userId: number) { console.log('contractResult', contractResult) const contractId = contractResult[0].id - // 4. PR 아이템들로 general-contract-items 생성 - const prItems = await db.select() - .from(prItemsForBidding) - .where(eq(prItemsForBidding.biddingId, biddingId)) + // 현재 winnerCompany의 품목정보 생성 (발주비율 적용) + if (companyBids.length > 0) { + console.log(`Creating ${companyBids.length} contract items for winner company ${winnerCompany.companyId} with award ratio ${awardRatio}`) + for (const bid of companyBids) { + // 발주비율에 따른 최종 수량/중량 계산 + const originalQuantity = Number(bid.quantity) || 0 + const originalWeight = Number(bid.totalWeight) || 0 + const bidUnitPrice = Number(bid.bidUnitPrice) || 0 + + const finalQuantity = originalQuantity * awardRatio + const finalWeight = originalWeight * awardRatio + const finalAmount = finalQuantity > 0 + ? finalQuantity * bidUnitPrice + : finalWeight * bidUnitPrice - if (prItems.length > 0) { - console.log(`Creating ${prItems.length} contract items for contract ${contractId}`) - for (const prItem of prItems) { await db.insert(generalContractItems).values({ - contractId, - project: prItem.projectInfo || '', - itemCode: prItem.itemNumber || '', - itemInfo: prItem.itemInfo || '', - specification: prItem.materialDescription || '', - quantity: prItem.quantity || null, - quantityUnit: prItem.quantityUnit || '', - contractUnitPrice: prItem.annualUnitPrice || null, - contractAmount: prItem.annualUnitPrice && prItem.quantity - ? (prItem.annualUnitPrice * prItem.quantity) - : null, - contractCurrency: biddingData.currency || 'KRW', - contractDeliveryDate: prItem.requestedDeliveryDate || null, - }) + contractId: contractId, + itemCode: bid.itemNumber || '', + itemInfo: bid.itemInfo || '', + specification: bid.materialDescription || '', + quantity: finalQuantity || null, + quantityUnit: bid.quantityUnit || '', + totalWeight: finalWeight || null, + weightUnit: bid.weightUnit || '', + contractDeliveryDate: bid.proposedDeliveryDate || null, + contractUnitPrice: bid.bidUnitPrice || null, + contractAmount: finalAmount || null, + contractCurrency: bid.currency || biddingData.currency || 'KRW', + } as any) } - console.log(`Created ${prItems.length} contract items`) + console.log(`Created ${companyBids.length} contract items for winner company ${winnerCompany.companyId}`) } else { - console.log('No PR items found for this bidding') + console.log(`No bid data found for winner company ${winnerCompany.companyId}`) } } @@ -189,34 +205,101 @@ export async function transmitToPO(biddingId: number) { const biddingCondition = biddingConditionData.length > 0 ? biddingConditionData[0] : null - // 3. 낙찰된 업체들 조회 + // 3. 낙찰된 업체들 조회 (발주비율 포함) const winnerCompaniesRaw = await db.select({ + id: biddingCompanies.id, companyId: biddingCompanies.companyId, finalQuoteAmount: biddingCompanies.finalQuoteAmount, + awardRatio: biddingCompanies.awardRatio, vendorCode: vendors.vendorCode, vendorName: vendors.vendorName }) - .from(biddingCompanies) - .leftJoin(vendors, eq(biddingCompanies.companyId, vendors.id)) - .where( - and( - eq(biddingCompanies.biddingId, biddingId), - eq(biddingCompanies.isWinner, true) - ) + .from(biddingCompanies) + .leftJoin(vendors, eq(biddingCompanies.companyId, vendors.id)) + .where( + and( + eq(biddingCompanies.biddingId, biddingId), + eq(biddingCompanies.isWinner, true) ) + ) if (winnerCompaniesRaw.length === 0) { throw new Error("낙찰된 업체가 없습니다.") } - // 4. PR 아이템 조회 - const prItems = await db.select() - .from(prItemsForBidding) - .where(eq(prItemsForBidding.biddingId, biddingId)) + // 4. 낙찰된 업체들의 입찰 데이터 조회 (발주비율 적용) + type POItem = { + prItemId: number + proposedDeliveryDate: string | null + bidUnitPrice: string | null + bidAmount: string | null + currency: string | null + itemNumber: string | null + itemInfo: string | null + materialDescription: string | null + quantity: string | null + quantityUnit: string | null + totalWeight: string | null + weightUnit: string | null + finalQuantity: number + finalWeight: number + finalAmount: number + awardRatio: number + vendorCode: string | null + vendorName: string | null + companyId: number + } + const poItems: POItem[] = [] + for (const winner of winnerCompaniesRaw) { + const awardRatio = (Number(winner.awardRatio) || 100) / 100 + + const companyBids = await db.select({ + prItemId: companyPrItemBids.prItemId, + proposedDeliveryDate: companyPrItemBids.proposedDeliveryDate, + bidUnitPrice: companyPrItemBids.bidUnitPrice, + bidAmount: companyPrItemBids.bidAmount, + currency: companyPrItemBids.currency, + // PR 아이템 정보 + itemNumber: prItemsForBidding.itemNumber, + itemInfo: prItemsForBidding.itemInfo, + materialDescription: prItemsForBidding.materialDescription, + quantity: prItemsForBidding.quantity, + quantityUnit: prItemsForBidding.quantityUnit, + totalWeight: prItemsForBidding.totalWeight, + weightUnit: prItemsForBidding.weightUnit, + }) + .from(companyPrItemBids) + .leftJoin(prItemsForBidding, eq(companyPrItemBids.prItemId, prItemsForBidding.id)) + .where(eq(companyPrItemBids.biddingCompanyId, winner.id)) + + // 발주비율 적용하여 PO 아이템 생성 + for (const bid of companyBids) { + const originalQuantity = Number(bid.quantity) || 0 + const originalWeight = Number(bid.totalWeight) || 0 + const bidUnitPrice = Number(bid.bidUnitPrice) || 0 + + const finalQuantity = originalQuantity * awardRatio + const finalWeight = originalWeight * awardRatio + const finalAmount = finalQuantity > 0 + ? finalQuantity * bidUnitPrice + : finalWeight * bidUnitPrice + + poItems.push({ + ...bid, + finalQuantity, + finalWeight, + finalAmount, + awardRatio, + vendorCode: winner.vendorCode, + vendorName: winner.vendorName, + companyId: winner.companyId, + } as POItem) + } + } - // 5. PO 데이터 구성 (bidding condition 정보 사용) + // 5. PO 데이터 구성 (bidding condition 정보와 발주비율 적용된 데이터 사용) const poData = { - T_Bidding_HEADER: winnerCompaniesRaw.map((company, index) => ({ + T_Bidding_HEADER: winnerCompaniesRaw.map((company) => ({ ANFNR: bidding.biddingNumber, LIFNR: company.vendorCode || `VENDOR${company.companyId}`, ZPROC_IND: 'A', // 구매 처리 상태 @@ -232,20 +315,16 @@ export async function transmitToPO(biddingId: number) { IHRAN: getCurrentSAPDate(), TEXT: `PO from Bidding: ${bidding.title}`, })), - T_Bidding_ITEM: prItems.map((item, index) => ({ + T_Bidding_ITEM: poItems.map((item, index) => ({ ANFNR: bidding.biddingNumber, ANFPS: (index + 1).toString().padStart(5, '0'), - LIFNR: winnerCompaniesRaw[0]?.vendorCode || `VENDOR${winnerCompaniesRaw[0]?.companyId}`, - NETPR: item.annualUnitPrice?.toString() || '0', + LIFNR: item.vendorCode || `VENDOR${item.companyId}`, + NETPR: item.bidUnitPrice?.toString() || '0', PEINH: '1', BPRME: item.quantityUnit || 'EA', - NETWR: item.annualUnitPrice && item.quantity - ? (item.annualUnitPrice * item.quantity).toString() - : '0', - BRTWR: item.annualUnitPrice && item.quantity - ? ((item.annualUnitPrice * item.quantity) * 1.1).toString() // 10% 부가세 가정 - : '0', - LFDAT: item.requestedDeliveryDate?.toISOString().split('T')[0] || getCurrentSAPDate(), + NETWR: item.finalAmount?.toString() || '0', + BRTWR: (Number(item.finalAmount || 0) * 1.1).toString(), // 10% 부가세 가정 + LFDAT: item.proposedDeliveryDate ? new Date(item.proposedDeliveryDate).toISOString().split('T')[0] : getCurrentSAPDate(), })), T_PR_RETURN: [{ ANFNR: bidding.biddingNumber, @@ -272,3 +351,102 @@ export async function transmitToPO(biddingId: number) { throw new Error(error instanceof Error ? error.message : 'PO 전송에 실패했습니다.') } } + +// 낙찰된 업체들의 상세 정보 조회 (발주비율에 따른 계산 포함) +export async function getWinnerDetails(biddingId: number) { + try { + // 1. 낙찰된 업체들 조회 + const winnerCompanies = await db.select({ + id: biddingCompanies.id, + companyId: biddingCompanies.companyId, + finalQuoteAmount: biddingCompanies.finalQuoteAmount, + awardRatio: biddingCompanies.awardRatio, + vendorName: vendors.vendorName, + vendorCode: vendors.vendorCode, + }) + .from(biddingCompanies) + .leftJoin(vendors, eq(biddingCompanies.companyId, vendors.id)) + .where( + and( + eq(biddingCompanies.biddingId, biddingId), + eq(biddingCompanies.isWinner, true) + ) + ) + + if (winnerCompanies.length === 0) { + return { success: false, error: '낙찰된 업체가 없습니다.' } + } + + // 2. 각 낙찰 업체의 입찰 품목 정보 조회 + const winnerDetails = [] + + for (const winner of winnerCompanies) { + // 업체의 입찰 품목 정보 조회 + const companyBids = await db.select({ + prItemId: companyPrItemBids.prItemId, + proposedDeliveryDate: companyPrItemBids.proposedDeliveryDate, + bidUnitPrice: companyPrItemBids.bidUnitPrice, + bidAmount: companyPrItemBids.bidAmount, + currency: companyPrItemBids.currency, + // PR 아이템 정보 + itemNumber: prItemsForBidding.itemNumber, + itemInfo: prItemsForBidding.itemInfo, + materialDescription: prItemsForBidding.materialDescription, + quantity: prItemsForBidding.quantity, + quantityUnit: prItemsForBidding.quantityUnit, + totalWeight: prItemsForBidding.totalWeight, + weightUnit: prItemsForBidding.weightUnit, + }) + .from(companyPrItemBids) + .leftJoin(prItemsForBidding, eq(companyPrItemBids.prItemId, prItemsForBidding.id)) + .where(eq(companyPrItemBids.biddingCompanyId, winner.id)) + + // 발주비율에 따른 계산 (백분율을 실제 비율로 변환) + const awardRatio = (Number(winner.awardRatio) || 100) / 100 + const calculatedItems = companyBids.map(bid => { + const originalQuantity = Number(bid.quantity) || 0 + const originalWeight = Number(bid.totalWeight) || 0 + const bidUnitPrice = Number(bid.bidUnitPrice) || 0 + + // 발주비율에 따른 최종 수량/중량 계산 + const finalQuantity = originalQuantity * awardRatio + const finalWeight = originalWeight * awardRatio + + // 최종 견적가 계산 (수량 또는 중량에 따른) + const finalAmount = finalQuantity > 0 + ? finalQuantity * bidUnitPrice + : finalWeight * bidUnitPrice + + return { + ...bid, + finalQuantity, + finalWeight, + finalAmount, + awardRatio, + } + }) + + // 업체 총 견적가 계산 + const totalFinalAmount = calculatedItems.reduce((sum, item) => sum + item.finalAmount, 0) + + winnerDetails.push({ + ...winner, + items: calculatedItems, + totalFinalAmount, + awardRatio: Number(winner.awardRatio) || 1, + }) + } + + return { + success: true, + data: winnerDetails + } + + } catch (error) { + console.error('Winner details 조회 실패:', error) + return { + success: false, + error: '낙찰 업체 상세 정보 조회에 실패했습니다.' + } + } +} diff --git a/lib/bidding/list/biddings-transmission-dialog.tsx b/lib/bidding/list/biddings-transmission-dialog.tsx index 035ab583..61207327 100644 --- a/lib/bidding/list/biddings-transmission-dialog.tsx +++ b/lib/bidding/list/biddings-transmission-dialog.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { - Send, CheckCircle, FileText, Truck + Send, CheckCircle, FileText, Truck, Calculator, Package } from "lucide-react" import { toast } from "sonner" import { Button } from "@/components/ui/button" @@ -16,8 +16,10 @@ import { } from "@/components/ui/dialog" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Label } from "@/components/ui/label" +import { Badge } from "@/components/ui/badge" +import { Separator } from "@/components/ui/separator" import { BiddingListItem } from "@/db/schema" -import { transmitToContract, transmitToPO } from "@/lib/bidding/actions" +import { transmitToContract, transmitToPO, getWinnerDetails } from "@/lib/bidding/actions" interface TransmissionDialogProps { open: boolean @@ -26,8 +28,60 @@ interface TransmissionDialogProps { userId: number } +interface WinnerDetail { + id: number + companyId: number + vendorName: string | null + vendorCode: string | null + awardRatio: number + totalFinalAmount: number + items: Array<{ + itemNumber: string | null + itemInfo: string | null + materialDescription: string | null + quantity: string | null + quantityUnit: string | null + totalWeight: string | null + weightUnit: string | null + bidUnitPrice: string | null + currency: string | null + finalQuantity: number + finalWeight: number + finalAmount: number + awardRatio: number + }> +} + export function TransmissionDialog({ open, onOpenChange, bidding, userId }: TransmissionDialogProps) { const [isLoading, setIsLoading] = React.useState(false) + const [winnerDetails, setWinnerDetails] = React.useState([]) + const [isLoadingDetails, setIsLoadingDetails] = React.useState(false) + + // 낙찰 업체 상세 정보 로드 + const loadWinnerDetails = React.useCallback(async () => { + if (!bidding) return + + try { + setIsLoadingDetails(true) + const result = await getWinnerDetails(bidding.id) + if (result.success) { + setWinnerDetails(result.data || []) + } else { + toast.error(result.error || '낙찰 업체 정보를 불러오는데 실패했습니다.') + } + } catch (error) { + console.error('Failed to load winner details:', error) + toast.error('낙찰 업체 정보를 불러오는데 실패했습니다.') + } finally { + setIsLoadingDetails(false) + } + }, [bidding]) + + React.useEffect(() => { + if (open && bidding) { + loadWinnerDetails() + } + }, [open, bidding, loadWinnerDetails]) if (!bidding) return null @@ -102,19 +156,85 @@ export function TransmissionDialog({ open, onOpenChange, bidding, userId }: Tran - {/* 선정된 업체 정보 (임시로 표시) */} + {/* 선정된 업체 상세 정보 */} - 선정된 업체 - - -
+ - 업체 선정이 완료되었습니다. -
-

- 자세한 업체 정보는 전송 후 확인할 수 있습니다. -

+ 선정된 업체 ({winnerDetails.length}개) + + + + {isLoadingDetails ? ( +
+
+

업체 정보를 불러오는 중...

+
+ ) : winnerDetails.length === 0 ? ( +

선정된 업체가 없습니다.

+ ) : ( + winnerDetails.map((winner) => ( +
+
+
+ + {winner.vendorName || `업체 ${winner.companyId}`} + {winner.vendorCode} +
+
+
+ 발주비율: {winner.awardRatio.toFixed(1)}% +
+
+ 최종 견적가: {winner.totalFinalAmount.toLocaleString()} {winner.items[0]?.currency || 'KRW'} +
+
+
+ + + +
+
+ + 품목별 상세 ({winner.items.length}개 품목) +
+ +
+ {winner.items.map((item, itemIndex) => ( +
+
+
+ 품목: {item.itemInfo || item.itemNumber} +
+
+ 규격: {item.materialDescription} +
+
+ 원래 수량: {Number(item.quantity).toLocaleString()} {item.quantityUnit} +
+
+ 발주 수량: {item.finalQuantity.toLocaleString()} {item.quantityUnit} +
+
+ 원래 중량: {Number(item.totalWeight).toLocaleString()} {item.weightUnit} +
+
+ 발주 중량: {item.finalWeight.toLocaleString()} {item.weightUnit} +
+
+ 단가: {Number(item.bidUnitPrice).toLocaleString()} {item.currency} +
+
+ 최종 금액: {item.finalAmount.toLocaleString()} {item.currency} +
+
+
+ ))} +
+
+
+ )) + )}
diff --git a/lib/bidding/pre-quote/table/bidding-pre-quote-invitation-dialog.tsx b/lib/bidding/pre-quote/table/bidding-pre-quote-invitation-dialog.tsx index 5fc0a0ee..3205df08 100644 --- a/lib/bidding/pre-quote/table/bidding-pre-quote-invitation-dialog.tsx +++ b/lib/bidding/pre-quote/table/bidding-pre-quote-invitation-dialog.tsx @@ -474,7 +474,7 @@ export function BiddingPreQuoteInvitationDialog({
setPreQuoteDeadline(e.target.value)} className="w-full" /> -

- 설정하지 않으면 마감일 없이 초대가 발송됩니다. -

{/* 기존 계약 정보 알림 */} diff --git a/lib/bidding/vendor/partners-bidding-pre-quote.tsx b/lib/bidding/vendor/partners-bidding-pre-quote.tsx index 6a76ffa1..6b9f956b 100644 --- a/lib/bidding/vendor/partners-bidding-pre-quote.tsx +++ b/lib/bidding/vendor/partners-bidding-pre-quote.tsx @@ -325,6 +325,17 @@ export function PartnersBiddingPreQuote({ biddingId, companyId }: PartnersBiddin return } + // 사전견적 상태 체크 + const isPreQuoteStatus = biddingStatus === 'request_for_quotation' || biddingStatus === 'received_quotation' + if (!isPreQuoteStatus) { + toast({ + title: "접근 제한", + description: "사전견적 단계가 아니므로 임시저장이 불가능합니다.", + variant: "destructive", + }) + return + } + if (!userId) { toast({ title: '임시저장 실패', @@ -347,11 +358,11 @@ export function PartnersBiddingPreQuote({ biddingId, companyId }: PartnersBiddin proposedContractDeliveryDate: responseData.proposedContractDeliveryDate, proposedShippingPort: responseData.proposedShippingPort, proposedDestinationPort: responseData.proposedDestinationPort, - priceAdjustmentResponse: responseData.priceAdjustmentResponse, - isInitialResponse: responseData.isInitialResponse, + priceAdjustmentResponse: responseData.priceAdjustmentResponse || false, // 체크 안하면 false로 설정 + isInitialResponse: responseData.isInitialResponse || false, // 체크 안하면 false로 설정 sparePartResponse: responseData.sparePartResponse, additionalProposals: responseData.additionalProposals, - priceAdjustmentForm: responseData.priceAdjustmentResponse ? { + priceAdjustmentForm: (responseData.priceAdjustmentResponse || false) ? { itemName: priceAdjustmentForm.itemName, adjustmentReflectionPoint: priceAdjustmentForm.adjustmentReflectionPoint, majorApplicableRawMaterial: priceAdjustmentForm.majorApplicableRawMaterial, @@ -440,6 +451,17 @@ export function PartnersBiddingPreQuote({ biddingId, companyId }: PartnersBiddin return } + // 사전견적 상태 체크 + const isPreQuoteStatus = biddingStatus === 'request_for_quotation' || biddingStatus === 'received_quotation' + if (!isPreQuoteStatus) { + toast({ + title: "접근 제한", + description: "사전견적 단계가 아니므로 견적 제출이 불가능합니다.", + variant: "destructive", + }) + return + } + // 견적마감일 체크 if (biddingDetail.preQuoteDeadline) { const now = new Date() @@ -509,11 +531,11 @@ export function PartnersBiddingPreQuote({ biddingId, companyId }: PartnersBiddin proposedContractDeliveryDate: responseData.proposedContractDeliveryDate, proposedShippingPort: responseData.proposedShippingPort, proposedDestinationPort: responseData.proposedDestinationPort, - priceAdjustmentResponse: responseData.priceAdjustmentResponse, - isInitialResponse: responseData.isInitialResponse, + priceAdjustmentResponse: responseData.priceAdjustmentResponse || false, // 체크 안하면 false로 설정 + isInitialResponse: responseData.isInitialResponse || false, // 체크 안하면 false로 설정 sparePartResponse: responseData.sparePartResponse, additionalProposals: responseData.additionalProposals, - priceAdjustmentForm: responseData.priceAdjustmentResponse ? { + priceAdjustmentForm: (responseData.priceAdjustmentResponse || false) ? { itemName: priceAdjustmentForm.itemName, adjustmentReflectionPoint: priceAdjustmentForm.adjustmentReflectionPoint, majorApplicableRawMaterial: priceAdjustmentForm.majorApplicableRawMaterial, @@ -1145,12 +1167,12 @@ export function PartnersBiddingPreQuote({ biddingId, companyId }: PartnersBiddin
- +