summaryrefslogtreecommitdiff
path: root/lib/bidding
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-11-17 10:00:07 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-11-17 10:00:07 +0000
commit213b995271edfbe7604d07ba4b71fcc20038a894 (patch)
tree41540fafa74d18755bccd00d4368e5652d8f6092 /lib/bidding
parentf1676f41e6edadd5841bff6a097dc93fbd195b92 (diff)
(최겸) 구매 입찰 수정
Diffstat (limited to 'lib/bidding')
-rw-r--r--lib/bidding/detail/table/bidding-detail-vendor-columns.tsx2
-rw-r--r--lib/bidding/detail/table/bidding-detail-vendor-table.tsx3
-rw-r--r--lib/bidding/detail/table/bidding-detail-vendor-toolbar-actions.tsx10
-rw-r--r--lib/bidding/list/biddings-table-columns.tsx1
-rw-r--r--lib/bidding/receive/biddings-receive-table.tsx2
-rw-r--r--lib/bidding/selection/actions.ts49
-rw-r--r--lib/bidding/selection/biddings-selection-columns.tsx10
-rw-r--r--lib/bidding/service.ts7
-rw-r--r--lib/bidding/vendor/partners-bidding-list-columns.tsx2
9 files changed, 59 insertions, 27 deletions
diff --git a/lib/bidding/detail/table/bidding-detail-vendor-columns.tsx b/lib/bidding/detail/table/bidding-detail-vendor-columns.tsx
index af7d70e1..6f35405d 100644
--- a/lib/bidding/detail/table/bidding-detail-vendor-columns.tsx
+++ b/lib/bidding/detail/table/bidding-detail-vendor-columns.tsx
@@ -79,7 +79,7 @@ export function getBiddingDetailVendorColumns({
<div className="text-right font-mono">
{hasAmount ? (
<button
- onClick={() => onViewItemDetails?.(row.original)}
+ onClick={() => onViewQuotationHistory?.(row.original)}
className="text-primary hover:text-primary/80 hover:underline cursor-pointer"
title="품목별 견적 상세 보기"
>
diff --git a/lib/bidding/detail/table/bidding-detail-vendor-table.tsx b/lib/bidding/detail/table/bidding-detail-vendor-table.tsx
index 315c2aac..cfdab9c6 100644
--- a/lib/bidding/detail/table/bidding-detail-vendor-table.tsx
+++ b/lib/bidding/detail/table/bidding-detail-vendor-table.tsx
@@ -133,11 +133,12 @@ export function BiddingDetailVendorTableContent({
try {
const { getQuotationHistory } = await import('@/lib/bidding/selection/actions')
const result = await getQuotationHistory(biddingId, vendor.vendorId)
+ console.log(result)
if (result.success) {
setQuotationHistoryData({
vendorName: vendor.vendorName,
- history: result.data.history,
+ history: result.data?.history || [],
biddingCurrency: bidding.currency || 'KRW',
targetPrice: bidding.targetPrice ? parseFloat(bidding.targetPrice.toString()) : undefined
})
diff --git a/lib/bidding/detail/table/bidding-detail-vendor-toolbar-actions.tsx b/lib/bidding/detail/table/bidding-detail-vendor-toolbar-actions.tsx
index 491f29f7..c1677ae7 100644
--- a/lib/bidding/detail/table/bidding-detail-vendor-toolbar-actions.tsx
+++ b/lib/bidding/detail/table/bidding-detail-vendor-toolbar-actions.tsx
@@ -164,7 +164,7 @@ export function BiddingDetailVendorToolbarActions({
title: "성공",
description: result.message,
})
- router.refresh()
+ router.push(`/evcp/bid`)
onSuccess()
} else {
toast({
@@ -180,8 +180,8 @@ export function BiddingDetailVendorToolbarActions({
<>
<div className="flex items-center gap-2">
{/* 상태별 액션 버튼 */}
- {/* 차수증가: 입찰평가중 또는 입찰 진행중 상태 */}
- {(bidding.status === 'evaluation_of_bidding ' || bidding.status === 'bidding_opened') && (
+ {/* 차수증가: 입찰공고 또는 입찰평가중 상태에서만 */}
+ {(bidding.status === 'evaluation_of_bidding' || bidding.status === 'bidding_opened') && (
<Button
variant="outline"
size="sm"
@@ -193,8 +193,8 @@ export function BiddingDetailVendorToolbarActions({
</Button>
)}
- {/* 유찰/낙찰: 입찰 진행중 상태에서만 */}
- {bidding.status === 'bidding_opened' && (
+ {/* 유찰/낙찰: 입찰공고 또는 입찰평가중 상태에서만 */}
+ {(bidding.status === 'bidding_opened' || bidding.status === 'evaluation_of_bidding') && (
<>
<Button
variant="destructive"
diff --git a/lib/bidding/list/biddings-table-columns.tsx b/lib/bidding/list/biddings-table-columns.tsx
index f5e77d03..92b2fe42 100644
--- a/lib/bidding/list/biddings-table-columns.tsx
+++ b/lib/bidding/list/biddings-table-columns.tsx
@@ -29,6 +29,7 @@ import {
biddingStatusLabels,
contractTypeLabels,
biddingTypeLabels,
+ biddingNoticeTypeLabels,
} from "@/db/schema"
import { formatDate } from "@/lib/utils"
diff --git a/lib/bidding/receive/biddings-receive-table.tsx b/lib/bidding/receive/biddings-receive-table.tsx
index 873f3fa4..1c5dd16b 100644
--- a/lib/bidding/receive/biddings-receive-table.tsx
+++ b/lib/bidding/receive/biddings-receive-table.tsx
@@ -283,7 +283,7 @@ export function BiddingsReceiveTable({ promises }: BiddingsReceiveTableProps) {
variant="outline"
size="sm"
onClick={handleOpenBidding}
- disabled={!selectedBiddingForAction || !canOpen || isOpeningBidding}
+ disabled={!selectedBiddingForAction || isOpeningBidding}
>
{isOpeningBidding && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
개찰
diff --git a/lib/bidding/selection/actions.ts b/lib/bidding/selection/actions.ts
index e17e9292..16b2c083 100644
--- a/lib/bidding/selection/actions.ts
+++ b/lib/bidding/selection/actions.ts
@@ -127,7 +127,8 @@ export async function getQuotationHistory(biddingId: number, vendorId: number) {
))
.limit(1)
- if (!companyData.length || !companyData[0].quotationSnapshots) {
+ // 데이터 존재 여부 및 유효성 체크
+ if (!companyData.length || !companyData[0]?.quotationSnapshots) {
return {
success: true,
data: {
@@ -136,7 +137,33 @@ export async function getQuotationHistory(biddingId: number, vendorId: number) {
}
}
- const snapshots = companyData[0].quotationSnapshots as any[]
+ let snapshots = companyData[0].quotationSnapshots
+
+ // quotationSnapshots가 JSONB 타입이므로 파싱이 필요할 수 있음
+ if (typeof snapshots === 'string') {
+ try {
+ snapshots = JSON.parse(snapshots)
+ } catch (parseError) {
+ console.error('Failed to parse quotationSnapshots:', parseError)
+ return {
+ success: true,
+ data: {
+ history: []
+ }
+ }
+ }
+ }
+
+ // snapshots가 배열인지 확인
+ if (!Array.isArray(snapshots)) {
+ console.error('quotationSnapshots is not an array:', typeof snapshots)
+ return {
+ success: true,
+ data: {
+ history: []
+ }
+ }
+ }
// PR 항목 정보 조회 (스냅샷의 prItemId로 매핑하기 위해)
const prItemIds = snapshots.flatMap(snapshot =>
@@ -146,12 +173,11 @@ export async function getQuotationHistory(biddingId: number, vendorId: number) {
const prItems = prItemIds.length > 0 ? await db
.select({
id: prItemsForBidding.id,
- itemCode: prItemsForBidding.itemCode,
- itemName: prItemsForBidding.itemName,
- specification: prItemsForBidding.specification,
+ itemNumber: prItemsForBidding.itemNumber,
+ itemInfo: prItemsForBidding.itemInfo,
quantity: prItemsForBidding.quantity,
- unit: prItemsForBidding.unit,
- deliveryDate: prItemsForBidding.deliveryDate
+ quantityUnit: prItemsForBidding.quantityUnit,
+ requestedDeliveryDate: prItemsForBidding.requestedDeliveryDate
})
.from(prItemsForBidding)
.where(sql`${prItemsForBidding.id} IN ${prItemIds}`) : []
@@ -181,14 +207,13 @@ export async function getQuotationHistory(biddingId: number, vendorId: number) {
const items = snapshot.items?.map((item: any) => {
const prItem = prItemMap.get(item.prItemId)
return {
- itemCode: prItem?.itemCode || `ITEM${item.prItemId}`,
- itemName: prItem?.itemName || '품목 정보 없음',
- specification: prItem?.specification || item.technicalSpecification || '-',
+ itemCode: prItem?.itemNumber || `ITEM${item.prItemId}`,
+ itemName: prItem?.itemInfo || '품목 정보 없음',
quantity: prItem?.quantity || 0,
- unit: prItem?.unit || 'EA',
+ unit: prItem?.quantityUnit || 'EA',
unitPrice: item.bidUnitPrice,
totalPrice: item.bidAmount,
- deliveryDate: item.proposedDeliveryDate ? new Date(item.proposedDeliveryDate) : prItem?.deliveryDate ? new Date(prItem.deliveryDate) : new Date()
+ deliveryDate: item.proposedDeliveryDate ? new Date(item.proposedDeliveryDate) : prItem?.requestedDeliveryDate ? new Date(prItem.requestedDeliveryDate) : new Date()
}
}) || []
diff --git a/lib/bidding/selection/biddings-selection-columns.tsx b/lib/bidding/selection/biddings-selection-columns.tsx
index 0d1a8c9d..8351a0dd 100644
--- a/lib/bidding/selection/biddings-selection-columns.tsx
+++ b/lib/bidding/selection/biddings-selection-columns.tsx
@@ -5,7 +5,7 @@ import { type ColumnDef } from "@tanstack/react-table"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
- Eye, Calendar, FileText, DollarSign, TrendingUp, TrendingDown
+ Eye, Calendar, FileText, DollarSign, TrendingUp, TrendingDown, MoreHorizontal
} from "lucide-react"
import {
Tooltip,
@@ -242,13 +242,13 @@ export function getBiddingsSelectionColumns({ setRowAction }: GetColumnsProps):
// ═══════════════════════════════════════════════════════════════
{
id: "actions",
- header: "액션",
+ header: "작업",
cell: ({ row }) => (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">메뉴 열기</span>
- <FileText className="h-4 w-4" />
+ <MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
@@ -256,7 +256,7 @@ export function getBiddingsSelectionColumns({ setRowAction }: GetColumnsProps):
<Eye className="mr-2 h-4 w-4" />
상세보기
</DropdownMenuItem>
- {row.original.status === 'bidding_opened' && (
+ {/* {row.original.status === 'bidding_opened' && (
<>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => setRowAction({ row, type: "close_bidding" })}>
@@ -264,7 +264,7 @@ export function getBiddingsSelectionColumns({ setRowAction }: GetColumnsProps):
입찰마감
</DropdownMenuItem>
</>
- )}
+ )} */}
{row.original.status === 'bidding_closed' && (
<>
<DropdownMenuSeparator />
diff --git a/lib/bidding/service.ts b/lib/bidding/service.ts
index b60fc73d..cbeeb24a 100644
--- a/lib/bidding/service.ts
+++ b/lib/bidding/service.ts
@@ -35,7 +35,7 @@ import {
} from 'drizzle-orm'
import { revalidatePath } from 'next/cache'
import { filterColumns } from '@/lib/filter-columns'
-import { GetBiddingsSchema } from './validation'
+import { GetBiddingsSchema, CreateBiddingSchema } from './validation'
@@ -865,6 +865,9 @@ export interface CreateBiddingInput extends CreateBiddingSchema {
meetingFiles: File[]
} | null
+ // noticeType 필드 명시적 추가 (CreateBiddingSchema에 포함되어 있지만 타입 추론 문제 해결)
+ noticeType?: 'standard' | 'facility' | 'unit_price'
+
// PR 아이템들 (선택사항)
prItems?: Array<{
id: string
@@ -1174,6 +1177,7 @@ export async function createBidding(input: CreateBiddingInput, userId: string) {
description: input.description,
contractType: input.contractType,
+ noticeType: input.noticeType || 'standard',
biddingType: input.biddingType,
awardCount: input.awardCount,
contractStartDate: input.contractStartDate ? parseDate(input.contractStartDate) : new Date(),
@@ -1494,6 +1498,7 @@ export async function updateBidding(input: UpdateBiddingInput, userId: string) {
if (input.content !== undefined) updateData.content = input.content
if (input.contractType !== undefined) updateData.contractType = input.contractType
+ if (input.noticeType !== undefined) updateData.noticeType = input.noticeType
if (input.biddingType !== undefined) updateData.biddingType = input.biddingType
if (input.awardCount !== undefined) updateData.awardCount = input.awardCount
if (input.contractStartDate !== undefined) updateData.contractStartDate = parseDate(input.contractStartDate)
diff --git a/lib/bidding/vendor/partners-bidding-list-columns.tsx b/lib/bidding/vendor/partners-bidding-list-columns.tsx
index 5870067a..6e8591c2 100644
--- a/lib/bidding/vendor/partners-bidding-list-columns.tsx
+++ b/lib/bidding/vendor/partners-bidding-list-columns.tsx
@@ -67,7 +67,7 @@ export function getPartnersBiddingListColumns({ setRowAction }: PartnersBiddingL
return (
<div className="font-mono text-sm">
<div>{biddingNumber}</div>
- <div className="text-muted-foreground text-xs">Rev. {revision ?? 0}</div>
+ {/* <div className="text-muted-foreground text-xs">Rev. {revision ?? 0}</div> */}
{originalBiddingNumber && (
<div className="text-xs text-muted-foreground">원: {originalBiddingNumber}</div>
)}