summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/bidding/detail/service.ts109
-rw-r--r--lib/bidding/list/biddings-table-columns.tsx4
-rw-r--r--lib/bidding/service.ts2
-rw-r--r--lib/bidding/vendor/partners-bidding-detail.tsx10
-rw-r--r--lib/bidding/vendor/partners-bidding-list-columns.tsx40
5 files changed, 31 insertions, 134 deletions
diff --git a/lib/bidding/detail/service.ts b/lib/bidding/detail/service.ts
index 6ab9270e..39bf0c46 100644
--- a/lib/bidding/detail/service.ts
+++ b/lib/bidding/detail/service.ts
@@ -918,114 +918,6 @@ export async function registerBidding(biddingId: number, userId: string) {
}
}
-// 재입찰 생성 (기존 입찰의 revision 업데이트 + 메일 발송)
-export async function createRebidding(biddingId: number, userId: string) {
- try {
- // 기존 입찰 정보 조회
- const bidding = await db
- .select()
- .from(biddings)
- .where(eq(biddings.id, biddingId))
- .limit(1)
-
- if (bidding.length === 0) {
- return { success: false, error: '입찰을 찾을 수 없습니다.' }
- }
-
- const originalBidding = bidding[0]
-
- // 기존 입찰 참여 업체들 조회
- const participantCompanies = await db
- .select({
- companyId: biddingCompanies.companyId,
- companyName: vendors.vendorName,
- contactEmail: vendors.email
- })
- .from(biddingCompanies)
- .leftJoin(vendors, eq(biddingCompanies.companyId, vendors.id))
- .where(and(
- eq(biddingCompanies.biddingId, biddingId),
- eq(biddingCompanies.isBiddingParticipated, true)
- ))
- const userName = await getUserNameById(userId)
- // 기존 입찰의 revision 증가 및 상태 변경
- const updatedBidding = await db
- .update(biddings)
- .set({
- revision: (originalBidding.revision || 0) + 1,
- status: 'bidding_opened', // 재입찰 시 다시 오픈 상태로
- updatedBy: userName,
- updatedAt: new Date()
- })
- .where(eq(biddings.id, biddingId))
- .returning({
- id: biddings.id,
- biddingNumber: biddings.biddingNumber,
- revision: biddings.revision
- })
-
- if (updatedBidding.length === 0) {
- return { success: false, error: '재입찰 업데이트에 실패했습니다.' }
- }
-
- // // 참여 업체들의 상태를 대기로 변경
- // await db
- // .update(biddingCompanies)
- // .set({
- // isBiddingParticipated: null, // 대기 상태로 변경
- // invitationStatus: 'sent',
- // updatedAt: new Date()
- // })
- // .where(and(
- // eq(biddingCompanies.biddingId, biddingId),
- // eq(biddingCompanies.isBiddingParticipated, true)
- // ))
-
- // 재입찰 안내 메일 발송
- for (const company of participantCompanies) {
- if (company.contactEmail) {
- try {
- await sendEmail({
- to: company.contactEmail,
- template: 'rebidding-invitation',
- context: {
- companyName: company.companyName,
- biddingNumber: updatedBidding[0].biddingNumber,
- title: originalBidding.title,
- projectName: originalBidding.projectName,
- itemName: originalBidding.itemName,
- biddingType: originalBidding.biddingType,
- revision: updatedBidding[0].revision || 1,
- submissionStartDate: originalBidding.submissionStartDate,
- submissionEndDate: originalBidding.submissionEndDate,
- biddingUrl: `${process.env.NEXT_PUBLIC_BASE_URL}/partners/bid/${biddingId}`,
- bidPicName: originalBidding.bidPicName,
- supplyPicName: originalBidding.supplyPicName,
- language: 'ko'
- }
- })
- } catch (emailError) {
- console.error(`Failed to send rebidding email to ${company.contactEmail}:`, emailError)
- }
- }
- }
-
- // 캐시 무효화
- revalidateTag(`bidding-${biddingId}`)
- revalidateTag('quotation-vendors')
- revalidateTag('quotation-details')
- revalidatePath('/evcp/bid')
- revalidatePath(`/evcp/bid/${biddingId}`)
-
- return {
- success: true,
- message: `재입찰이 성공적으로 처리되었습니다. ${participantCompanies.length}개 업체에 안내 메일을 발송했습니다.`
- }
- } catch (error) {
- console.error('Failed to create rebidding:', error)
- return { success: false, error: '재입찰 처리에 실패했습니다.' }
- }
-}
// 업체 선정 사유 업데이트
export async function updateVendorSelectionReason(biddingId: number, selectedCompanyId: number, selectionReason: string, userId: string) {
@@ -1559,6 +1451,7 @@ export interface PartnersBiddingListItem {
respondedAt: string | null
finalQuoteAmount: number | null
finalQuoteSubmittedAt: string | null
+ isFinalSubmission: boolean | null
isWinner: boolean | null
isAttendingMeeting: boolean | null
isPreQuoteSelected: boolean | null
diff --git a/lib/bidding/list/biddings-table-columns.tsx b/lib/bidding/list/biddings-table-columns.tsx
index 48c32302..40c7f271 100644
--- a/lib/bidding/list/biddings-table-columns.tsx
+++ b/lib/bidding/list/biddings-table-columns.tsx
@@ -246,7 +246,7 @@ export function getBiddingsColumns({ setRowAction }: GetColumnsProps): ColumnDef
size: 100,
meta: { excelHeader: "입찰등록일" },
},
-
+ // ░░░ 입찰서제출기간 ░░░
{
id: "submissionPeriod",
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="입찰서제출기간" />,
@@ -263,7 +263,7 @@ export function getBiddingsColumns({ setRowAction }: GetColumnsProps): ColumnDef
return (
<div className="text-xs">
<div className={`${isActive ? 'text-green-600 font-medium' : isPast ? 'text-red-600' : 'text-gray-600'}`}>
- {formatDate(startDate, "KR")} ~ {formatDate(endDate, "KR")}
+ {new Date(startDate).toISOString().slice(0, 16).replace('T', ' ')} ~ {new Date(endDate).toISOString().slice(0, 16).replace('T', ' ')}
</div>
{isActive && (
<Badge variant="default" className="text-xs mt-1">진행중</Badge>
diff --git a/lib/bidding/service.ts b/lib/bidding/service.ts
index 68ae016e..14bed105 100644
--- a/lib/bidding/service.ts
+++ b/lib/bidding/service.ts
@@ -1166,7 +1166,7 @@ export async function createBidding(input: CreateBiddingInput, userId: string) {
.insert(biddings)
.values({
biddingNumber,
- originalBiddingNumber: null, // 원입찰번호는 단순 정보이므로 null
+ originalBiddingNumber: null, // 원입찰번호는 초기 생성이므로 아직 없음
revision: input.revision || 0,
// 프로젝트 정보 (PR 아이템에서 설정됨)
diff --git a/lib/bidding/vendor/partners-bidding-detail.tsx b/lib/bidding/vendor/partners-bidding-detail.tsx
index fe254dad..66c90eaf 100644
--- a/lib/bidding/vendor/partners-bidding-detail.tsx
+++ b/lib/bidding/vendor/partners-bidding-detail.tsx
@@ -25,7 +25,7 @@ import {
Calendar,
ChevronDown
} from 'lucide-react'
-
+import { format } from 'date-fns'
import { formatDate } from '@/lib/utils'
import {
getBiddingDetailsForPartners,
@@ -806,9 +806,9 @@ export function PartnersBiddingDetail({ biddingId, companyId }: PartnersBiddingD
<Label className="text-sm font-medium text-muted-foreground mb-2 block">계약기간</Label>
<div className="p-3 bg-muted/50 rounded-lg">
<div className="flex items-center gap-2 text-sm">
- <span className="font-medium">{formatDate(biddingDetail.contractStartDate, 'KR')}</span>
+ <span className="font-medium">{format(new Date(biddingDetail.contractStartDate), "yyyy-MM-dd")}</span>
<span className="text-muted-foreground">~</span>
- <span className="font-medium">{formatDate(biddingDetail.contractEndDate, 'KR')}</span>
+ <span className="font-medium">{format(new Date(biddingDetail.contractEndDate), "yyyy-MM-dd")}</span>
</div>
</div>
</div>
@@ -874,12 +874,12 @@ export function PartnersBiddingDetail({ biddingId, companyId }: PartnersBiddingD
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-sm">
{biddingDetail.submissionStartDate && biddingDetail.submissionEndDate && (
<div>
- <span className="font-medium">응찰기간:</span> {formatDate(biddingDetail.submissionStartDate, 'KR')} ~ {formatDate(biddingDetail.submissionEndDate, 'KR')}
+ <span className="font-medium">입찰서 제출기간:</span> {new Date(biddingDetail.submissionStartDate).toISOString().slice(0, 16).replace('T', ' ')} ~ {new Date(biddingDetail.submissionEndDate).toISOString().slice(0, 16).replace('T', ' ')}
</div>
)}
{biddingDetail.evaluationDate && (
<div>
- <span className="font-medium">평가일:</span> {formatDate(biddingDetail.evaluationDate, 'KR')}
+ <span className="font-medium">평가일:</span> {format(new Date(biddingDetail.evaluationDate), "yyyy-MM-dd HH:mm")}
</div>
)}
</div>
diff --git a/lib/bidding/vendor/partners-bidding-list-columns.tsx b/lib/bidding/vendor/partners-bidding-list-columns.tsx
index d9058e97..ba8efae6 100644
--- a/lib/bidding/vendor/partners-bidding-list-columns.tsx
+++ b/lib/bidding/vendor/partners-bidding-list-columns.tsx
@@ -240,22 +240,6 @@ export function getPartnersBiddingListColumns({ setRowAction }: PartnersBiddingL
},
}),
- // 사전견적 참여의사
- columnHelper.accessor('isPreQuoteParticipated', {
- header: '사전견적 참여의사',
- cell: ({ row }) => {
- const participated = row.original.isPreQuoteParticipated
- if (participated === null) {
- return <Badge variant="outline">미결정</Badge>
- }
- return (
- <Badge variant={participated ? 'default' : 'destructive'}>
- {participated ? '참여' : '미참여'}
- </Badge>
- )
- },
- }),
-
// 입찰 참여의사
columnHelper.accessor('isBiddingParticipated', {
header: '입찰 참여의사',
@@ -272,6 +256,26 @@ export function getPartnersBiddingListColumns({ setRowAction }: PartnersBiddingL
},
}),
+ // 입찰 제출여부
+ columnHelper.display({
+ id: 'biddingSubmissionStatus',
+ header: '입찰 제출여부',
+ cell: ({ row }) => {
+ const finalQuoteAmount = row.original.finalQuoteAmount
+ const isFinalSubmission = row.original.isFinalSubmission
+
+ if (!finalQuoteAmount) {
+ return <Badge variant="outline">미제출</Badge>
+ }
+
+ if (isFinalSubmission) {
+ return <Badge variant="default">최종제출</Badge>
+ }
+
+ return <Badge variant="secondary">제출</Badge>
+ },
+ }),
+
// 계약구분
columnHelper.accessor('contractType', {
header: '계약구분',
@@ -291,9 +295,9 @@ export function getPartnersBiddingListColumns({ setRowAction }: PartnersBiddingL
}
return (
<div className="text-sm">
- <div>{format(new Date(startDate), "yyyy-MM-dd")}</div>
+ <div>{new Date(startDate).toISOString().slice(0, 16).replace('T', ' ')}</div>
<div className="text-muted-foreground">~</div>
- <div>{format(new Date(endDate), "yyyy-MM-dd")}</div>
+ <div>{new Date(endDate).toISOString().slice(0, 16).replace('T', ' ')}</div>
</div>
)
},