diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-17 07:06:52 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-17 07:06:52 +0000 |
| commit | 1532c1bf4a3236ce9056f33e51ddfebeff79ed5a (patch) | |
| tree | c3f3c43d0f73c4b7df4e34e9b1aa308604c562c9 /lib/bidding | |
| parent | a5be73b70e7d8e6be1724252e6923c664c3771f4 (diff) | |
(최겸) 구매 피드백 처리
Diffstat (limited to 'lib/bidding')
| -rw-r--r-- | lib/bidding/actions.ts | 6 | ||||
| -rw-r--r-- | lib/bidding/detail/table/bidding-detail-vendor-toolbar-actions.tsx | 4 | ||||
| -rw-r--r-- | lib/bidding/pre-quote/service.ts | 17 | ||||
| -rw-r--r-- | lib/bidding/service.ts | 137 |
4 files changed, 103 insertions, 61 deletions
diff --git a/lib/bidding/actions.ts b/lib/bidding/actions.ts index d0c7a0cd..5909cd62 100644 --- a/lib/bidding/actions.ts +++ b/lib/bidding/actions.ts @@ -613,11 +613,11 @@ export async function cancelDisposalAction( } } - // 3. 입찰 상태를 입찰 진행중으로 변경 + // 3. 입찰 상태를 입찰생성으로 변경 await tx .update(biddings) .set({ - status: 'evaluation_of_bidding', + status: 'bidding_generated', updatedAt: new Date(), updatedBy: userName, }) @@ -734,7 +734,7 @@ export async function earlyOpenBiddingAction(biddingId: number) { // 5. 참여협력사 중 최종응찰 버튼을 클릭한 업체들만 있는지 검증 // bidding_submitted 상태인 업체들이 있는지 확인 (이미 위에서 검증됨) - // 6. 조기개찰 상태로 변경 + // 6. 입찰평가중 상태로 변경 await tx .update(biddings) .set({ 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 e3db8861..491f29f7 100644 --- a/lib/bidding/detail/table/bidding-detail-vendor-toolbar-actions.tsx +++ b/lib/bidding/detail/table/bidding-detail-vendor-toolbar-actions.tsx @@ -180,8 +180,8 @@ export function BiddingDetailVendorToolbarActions({ <> <div className="flex items-center gap-2"> {/* 상태별 액션 버튼 */} - {/* 차수증가: 입찰공고 또는 입찰 진행중 상태 */} - {(bidding.status === 'bidding_generated' || bidding.status === 'bidding_opened') && ( + {/* 차수증가: 입찰평가중 또는 입찰 진행중 상태 */} + {(bidding.status === 'evaluation_of_bidding ' || bidding.status === 'bidding_opened') && ( <Button variant="outline" size="sm" diff --git a/lib/bidding/pre-quote/service.ts b/lib/bidding/pre-quote/service.ts index ea92f294..19b418ae 100644 --- a/lib/bidding/pre-quote/service.ts +++ b/lib/bidding/pre-quote/service.ts @@ -1492,18 +1492,15 @@ export async function sendBiddingBasicContracts( try {
await sendEmail({
to: vendor.selectedMainEmail,
- template: 'basic-contract-notification',
+ subject: `[eVCP] 기본계약서 서명 요청`,
+ template: "contract-sign-request",
context: {
vendorName: vendor.vendorName,
- biddingId: biddingId,
- contractCount: contractTypes.length,
- deadline: new Date(Date.now() + 10 * 24 * 60 * 60 * 1000).toLocaleDateString('ko-KR'),
- loginUrl: `${process.env.NEXT_PUBLIC_APP_URL}/partners/bid/${biddingId}`,
- message: message || '',
- currentYear: new Date().getFullYear(),
- language: 'ko'
- }
- })
+ templateName: contractTypes.map(ct => ct.templateName).join(', '),
+ loginUrl: `${process.env.NEXT_PUBLIC_APP_URL}/partners/basic-contract`,
+ language:'ko'
+ },
+ });
} catch (emailError) {
console.error(`이메일 발송 실패 (${vendor.selectedMainEmail}):`, emailError)
// 이메일 발송 실패해도 계약 생성은 유지
diff --git a/lib/bidding/service.ts b/lib/bidding/service.ts index a7cd8286..b60fc73d 100644 --- a/lib/bidding/service.ts +++ b/lib/bidding/service.ts @@ -33,6 +33,11 @@ import { like, notInArray } from 'drizzle-orm' +import { revalidatePath } from 'next/cache' +import { filterColumns } from '@/lib/filter-columns' +import { GetBiddingsSchema } from './validation' + + // 사용자 이메일로 사용자 코드 조회 export async function getUserCodeByEmail(email: string): Promise<string | null> { @@ -49,9 +54,6 @@ export async function getUserCodeByEmail(email: string): Promise<string | null> return null } } -import { revalidatePath } from 'next/cache' -import { filterColumns } from '@/lib/filter-columns' -import { CreateBiddingSchema, GetBiddingsSchema, UpdateBiddingSchema } from './validation' import { saveFile } from '../file-stroage' // userId를 user.name으로 변환하는 유틸리티 함수 @@ -973,6 +975,74 @@ function generateNextSequence(currentMax: string | null): string { return result.padStart(4, '0'); } +// 입찰 참여 현황 카운트 계산 함수 +export async function getParticipantCountsForBidding(biddingId: number) { + try { + // 전체 참여자 수 (예상 참여자) + const expectedResult = await db + .select({ count: count() }) + .from(biddingCompanies) + .where(eq(biddingCompanies.biddingId, biddingId)) + + const expected = expectedResult[0]?.count || 0 + + // 참여 완료 수 + const participatedResult = await db + .select({ count: count() }) + .from(biddingCompanies) + .where(and( + eq(biddingCompanies.biddingId, biddingId), + eq(biddingCompanies.invitationStatus, 'bidding_submitted') + )) + + const participated = participatedResult[0]?.count || 0 + + // 거부/취소 수 + const declinedResult = await db + .select({ count: count() }) + .from(biddingCompanies) + .where(and( + eq(biddingCompanies.biddingId, biddingId), + or( + eq(biddingCompanies.invitationStatus, 'bidding_declined'), + eq(biddingCompanies.invitationStatus, 'bidding_cancelled') + ) + )) + + const declined = declinedResult[0]?.count || 0 + + // 대기중 수 + const pendingResult = await db + .select({ count: count() }) + .from(biddingCompanies) + .where(and( + eq(biddingCompanies.biddingId, biddingId), + or( + eq(biddingCompanies.invitationStatus, 'pending'), + eq(biddingCompanies.invitationStatus, 'bidding_sent'), + eq(biddingCompanies.invitationStatus, 'bidding_accepted') + ) + )) + + const pending = pendingResult[0]?.count || 0 + + return { + participantExpected: expected, + participantParticipated: participated, + participantDeclined: declined, + participantPending: pending + } + } catch (error) { + console.error('Error in getParticipantCountsForBidding:', error) + return { + participantExpected: 0, + participantParticipated: 0, + participantDeclined: 0, + participantPending: 0 + } + } +} + // 자동 입찰번호 생성 export async function generateBiddingNumber( contractType: string, @@ -982,9 +1052,9 @@ export async function generateBiddingNumber( ): Promise<string> { // 계약 타입별 접두사 설정 const typePrefix = { - 'general': 'E', - 'unit_price': 'F', - 'sale': 'G' + 'general': 'E', // 일반계약 + 'unit_price': 'F', // 단가계약 + 'sale': 'G', // 매각계약 }; const prefix = typePrefix[contractType as keyof typeof typePrefix] || 'E'; @@ -3163,8 +3233,8 @@ export async function increaseRoundOrRebid(biddingId: number, userId: string | u prNumber: existingBidding.prNumber, hasPrDocument: existingBidding.hasPrDocument, - // 상태는 내정가 산정으로 초기화 - status: 'set_target_price', + // 상태는 입찰생성으로 초기화 + status: 'bidding_generated', isPublic: existingBidding.isPublic, isUrgent: existingBidding.isUrgent, @@ -3530,8 +3600,8 @@ export async function getBiddingsForReceive(input: GetBiddingsSchema) { orderByColumns.push(desc(biddings.createdAt)) } - // bid-receive 페이지용 데이터 조회 (필요한 컬럼만 선택) - const data = await db + // bid-receive 페이지용 데이터 조회 (참여 현황 제외하고 기본 정보만) + const biddingData = await db .select({ // 기본 입찰 정보 id: biddings.id, @@ -3549,42 +3619,6 @@ export async function getBiddingsForReceive(input: GetBiddingsSchema) { createdAt: biddings.createdAt, updatedAt: biddings.updatedAt, - // 참여 현황 집계 - participantExpected: sql<number>` - COALESCE(( - SELECT count(*) - FROM bidding_companies - WHERE bidding_id = ${biddings.id} - ), 0) - `.as('participant_expected'), - - participantParticipated: sql<number>` - COALESCE(( - SELECT count(*) - FROM bidding_companies - WHERE bidding_id = ${biddings.id} - AND invitation_status = 'bidding_submitted' - ), 0) - `.as('participant_participated'), - - participantDeclined: sql<number>` - COALESCE(( - SELECT count(*) - FROM bidding_companies - WHERE bidding_id = ${biddings.id} - AND invitation_status IN ('bidding_declined', 'bidding_cancelled') - ), 0) - `.as('participant_declined'), - - participantPending: sql<number>` - COALESCE(( - SELECT count(*) - FROM bidding_companies - WHERE bidding_id = ${biddings.id} - AND invitation_status IN ('pending', 'bidding_sent', 'bidding_accepted') - ), 0) - `.as('participant_pending'), - // 개찰 정보 openedAt: biddings.openedAt, openedBy: biddings.openedBy, @@ -3595,6 +3629,17 @@ export async function getBiddingsForReceive(input: GetBiddingsSchema) { .limit(input.perPage) .offset(offset) + // 각 입찰에 대한 참여 현황 카운트 계산 + const data = await Promise.all( + biddingData.map(async (bidding) => { + const participantCounts = await getParticipantCountsForBidding(bidding.id) + return { + ...bidding, + ...participantCounts + } + }) + ) + const pageCount = Math.ceil(total / input.perPage) return { data, pageCount, total } |
