diff options
Diffstat (limited to 'lib/bidding/actions.ts')
| -rw-r--r-- | lib/bidding/actions.ts | 139 |
1 files changed, 30 insertions, 109 deletions
diff --git a/lib/bidding/actions.ts b/lib/bidding/actions.ts index 78f07219..0bf2af57 100644 --- a/lib/bidding/actions.ts +++ b/lib/bidding/actions.ts @@ -655,111 +655,7 @@ async function getUserNameById(userId: string): Promise<string> { } } -// 조기개찰 액션 -export async function earlyOpenBiddingAction(biddingId: number) { - try { - const session = await getServerSession(authOptions) - if (!session?.user?.name) { - return { success: false, message: '인증이 필요합니다.' } - } - - const userName = session.user.name - - return await db.transaction(async (tx) => { - // 1. 입찰 정보 확인 - const [bidding] = await tx - .select({ - id: biddings.id, - status: biddings.status, - submissionEndDate: biddings.submissionEndDate, - title: biddings.title - }) - .from(biddings) - .where(eq(biddings.id, biddingId)) - .limit(1) - - if (!bidding) { - return { success: false, message: '입찰 정보를 찾을 수 없습니다.' } - } - - // 2. 입찰서 제출기간 내인지 확인 - const now = new Date() - if (bidding.submissionEndDate && now > bidding.submissionEndDate) { - return { success: false, message: '입찰서 제출기간이 종료되었습니다.' } - } - - // 3. 참여 현황 확인 - const [participationStats] = await tx - .select({ - participantExpected: db.$count(biddingCompanies), - participantParticipated: db.$count(biddingCompanies, eq(biddingCompanies.invitationStatus, 'bidding_submitted')), - participantDeclined: db.$count(biddingCompanies, and( - eq(biddingCompanies.invitationStatus, 'bidding_declined'), - eq(biddingCompanies.biddingId, biddingId) - )), - participantPending: db.$count(biddingCompanies, and( - eq(biddingCompanies.invitationStatus, 'pending'), - eq(biddingCompanies.biddingId, biddingId) - )), - }) - .from(biddingCompanies) - .where(eq(biddingCompanies.biddingId, biddingId)) - - // 실제 SQL 쿼리로 변경 - const [stats] = await tx - .select({ - participantExpected: sql<number>`COUNT(*)`.as('participant_expected'), - participantParticipated: sql<number>`COUNT(CASE WHEN invitation_status = 'bidding_submitted' THEN 1 END)`.as('participant_participated'), - participantDeclined: sql<number>`COUNT(CASE WHEN invitation_status IN ('bidding_declined', 'bidding_cancelled') THEN 1 END)`.as('participant_declined'), - participantPending: sql<number>`COUNT(CASE WHEN invitation_status IN ('pending', 'bidding_sent', 'bidding_accepted') THEN 1 END)`.as('participant_pending'), - }) - .from(biddingCompanies) - .where(eq(biddingCompanies.biddingId, biddingId)) - - const participantExpected = Number(stats.participantExpected) || 0 - const participantParticipated = Number(stats.participantParticipated) || 0 - const participantDeclined = Number(stats.participantDeclined) || 0 - const participantPending = Number(stats.participantPending) || 0 - - // 4. 조기개찰 조건 검증 - // - 미제출 협력사 = 0 - if (participantPending > 0) { - return { success: false, message: `미제출 협력사가 ${participantPending}명 있어 조기개찰이 불가능합니다.` } - } - - // - 참여협력사 + 포기협력사 = 참여예정협력사 - if (participantParticipated + participantDeclined !== participantExpected) { - return { success: false, message: '모든 협력사가 참여 또는 포기하지 않아 조기개찰이 불가능합니다.' } - } - - // 5. 참여협력사 중 최종응찰 버튼을 클릭한 업체들만 있는지 검증 - // bidding_submitted 상태인 업체들이 있는지 확인 (이미 위에서 검증됨) - - // 6. 입찰평가중 상태로 변경 - await tx - .update(biddings) - .set({ - status: 'evaluation_of_bidding', - openedAt: new Date(), - openedBy: userName, - updatedAt: new Date(), - updatedBy: userName, - }) - .where(eq(biddings.id, biddingId)) - - return { success: true, message: '조기개찰이 완료되었습니다.' } - }) - - } catch (error) { - console.error('조기개찰 실패:', error) - return { - success: false, - message: error instanceof Error ? error.message : '조기개찰 중 오류가 발생했습니다.' - } - } -} - -// 개찰 액션 +// 개찰 액션 (조기개찰 포함) export async function openBiddingAction(biddingId: number) { try { const session = await getServerSession(authOptions) @@ -786,10 +682,35 @@ export async function openBiddingAction(biddingId: number) { return { success: false, message: '입찰 정보를 찾을 수 없습니다.' } } - // 2. 입찰서 제출기간이 종료되었는지 확인 const now = new Date() - if (bidding.submissionEndDate && now <= bidding.submissionEndDate) { - return { success: false, message: '입찰서 제출기간이 아직 종료되지 않았습니다.' } + const isDeadlinePassed = bidding.submissionEndDate && now > bidding.submissionEndDate + + // 2. 개찰 가능 여부 확인 + if (!isDeadlinePassed) { + // 마감일이 지나지 않았으면 조기개찰 조건 확인 + // 조기개찰 조건: 모든 대상 업체가 응찰(최종제출)했거나 포기했는지 확인 (미제출 0) + + const [stats] = await tx + .select({ + participantExpected: sql<number>`COUNT(*)`.as('participant_expected'), + participantFinalSubmitted: sql<number>`COUNT(CASE WHEN invitation_status = 'bidding_submitted' THEN 1 END)`.as('participant_final_submitted'), + participantDeclined: sql<number>`COUNT(CASE WHEN invitation_status IN ('bidding_declined', 'bidding_cancelled') THEN 1 END)`.as('participant_declined'), + }) + .from(biddingCompanies) + .where(eq(biddingCompanies.biddingId, biddingId)) + + const participantExpected = Number(stats.participantExpected) || 0 + const participantFinalSubmitted = Number(stats.participantFinalSubmitted) || 0 + const participantDeclined = Number(stats.participantDeclined) || 0 + + // 조건: 전체 대상 = 최종제출 + 포기 + if (participantExpected !== participantFinalSubmitted + participantDeclined) { + const pending = participantExpected - (participantFinalSubmitted + participantDeclined); + return { + success: false, + message: `입찰서 제출기간이 종료되지 않았으며, 최종제출하지 않은 업체가 ${pending}곳 있어 조기개찰할 수 없습니다.` + } + } } // 3. 입찰평가중 상태로 변경 @@ -804,7 +725,7 @@ export async function openBiddingAction(biddingId: number) { }) .where(eq(biddings.id, biddingId)) - return { success: true, message: '개찰이 완료되었습니다.' } + return { success: true, message: isDeadlinePassed ? '개찰이 완료되었습니다.' : '조기개찰이 완료되었습니다.' } }) } catch (error) { |
