summaryrefslogtreecommitdiff
path: root/lib/bidding/detail/service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/bidding/detail/service.ts')
-rw-r--r--lib/bidding/detail/service.ts225
1 files changed, 10 insertions, 215 deletions
diff --git a/lib/bidding/detail/service.ts b/lib/bidding/detail/service.ts
index a603834c..9fb3d87f 100644
--- a/lib/bidding/detail/service.ts
+++ b/lib/bidding/detail/service.ts
@@ -1,7 +1,7 @@
'use server'
import db from '@/db/db'
-import { biddings, prItemsForBidding, biddingDocuments, biddingCompanies, vendors, companyPrItemBids, companyConditionResponses, vendorSelectionResults, BiddingListItem, biddingConditions, priceAdjustmentForms, users } from '@/db/schema'
+import { biddings, prItemsForBidding, biddingDocuments, biddingCompanies, vendors, companyPrItemBids, companyConditionResponses, vendorSelectionResults, priceAdjustmentForms, users } from '@/db/schema'
import { specificationMeetings } from '@/db/schema/bidding'
import { eq, and, sql, desc, ne } from 'drizzle-orm'
import { revalidatePath, revalidateTag } from 'next/cache'
@@ -34,7 +34,7 @@ export interface BiddingDetailData {
}
// getBiddingById 함수 임포트 (기존 함수 재사용)
-import { getBiddingById, getPRDetailsAction } from '@/lib/bidding/service'
+import { getBiddingById } from '@/lib/bidding/service'
// Promise.all을 사용하여 모든 데이터를 병렬로 조회 (캐시 적용)
export async function getBiddingDetailData(biddingId: number): Promise<BiddingDetailData> {
@@ -674,216 +674,6 @@ export async function createBiddingDetailVendor(
}
}
-// 협력업체 정보 저장 - biddingCompanies와 companyConditionResponses 테이블에 레코드 생성
-export async function createQuotationVendor(input: any, userId: string) {
- try {
- const userName = await getUserNameById(userId)
- const result = await db.transaction(async (tx) => {
- // 0. 중복 체크 - 이미 해당 입찰에 참여중인 업체인지 확인
- const existingCompany = await tx
- .select()
- .from(biddingCompanies)
- .where(sql`${biddingCompanies.biddingId} = ${input.biddingId} AND ${biddingCompanies.companyId} = ${input.companyId}`)
-
- if (existingCompany.length > 0) {
- throw new Error('이미 등록된 업체입니다')
- }
-
- // 1. biddingCompanies에 레코드 생성
- const biddingCompanyResult = await tx.insert(biddingCompanies).values({
- biddingId: input.biddingId,
- companyId: input.vendorId,
- finalQuoteAmount: input.quotationAmount?.toString(),
- awardRatio: input.awardRatio?.toString(),
- isWinner: null, // 미정 상태로 초기화
- contactPerson: input.contactPerson,
- contactEmail: input.contactEmail,
- contactPhone: input.contactPhone,
- finalQuoteSubmittedAt: new Date(),
- // 스키마에 createdBy, updatedBy 필드가 없으므로 제거
- }).returning({ id: biddingCompanies.id })
-
- if (biddingCompanyResult.length === 0) {
- throw new Error('협력업체 정보 저장에 실패했습니다.')
- }
-
- const biddingCompanyId = biddingCompanyResult[0].id
-
- // 2. companyConditionResponses에 입찰 조건 생성
- await tx.insert(companyConditionResponses).values({
- biddingCompanyId: biddingCompanyId,
- paymentTermsResponse: input.paymentTermsResponse || '',
- taxConditionsResponse: input.taxConditionsResponse || '',
- proposedContractDeliveryDate: input.proposedContractDeliveryDate || null,
- priceAdjustmentResponse: input.priceAdjustmentResponse || false,
- incotermsResponse: input.incotermsResponse || '',
- proposedShippingPort: input.proposedShippingPort || '',
- proposedDestinationPort: input.proposedDestinationPort || '',
- sparePartResponse: input.sparePartResponse || '',
- additionalProposals: input.additionalProposals || '',
- isPreQuote: false,
- submittedAt: new Date(),
- createdAt: new Date(),
- updatedAt: new Date(),
- })
-
- return biddingCompanyId
- })
-
- // 캐시 무효화
- revalidateTag(`bidding-${input.biddingId}`)
- revalidateTag('quotation-vendors')
- revalidateTag('quotation-details')
- revalidatePath(`/evcp/bid/[id]`)
- return {
- success: true,
- message: '협력업체 정보가 성공적으로 저장되었습니다.',
- data: { id: result }
- }
- } catch (error) {
- console.error('Failed to create quotation vendor:', error)
- return { success: false, error: '협력업체 정보 저장에 실패했습니다.' }
- }
-}
-
-// 협력업체 정보 업데이트
-export async function updateQuotationVendor(id: number, input: any, userId: string) {
- try {
- const userName = await getUserNameById(userId)
- const result = await db.transaction(async (tx) => {
- // 1. biddingCompanies 테이블 업데이트
- const updateData: any = {}
- if (input.quotationAmount !== undefined) updateData.finalQuoteAmount = input.quotationAmount
- if (input.contactPerson !== undefined) updateData.contactPerson = input.contactPerson
- if (input.contactEmail !== undefined) updateData.contactEmail = input.contactEmail
- if (input.contactPhone !== undefined) updateData.contactPhone = input.contactPhone
- if (input.awardRatio !== undefined) updateData.awardRatio = input.awardRatio?.toString()
- // status 필드가 스키마에 없으므로 제거
- // updatedBy 필드가 스키마에 없으므로 제거
- updateData.updatedAt = new Date()
-
- if (Object.keys(updateData).length > 0) {
- await tx.update(biddingCompanies)
- .set(updateData)
- .where(eq(biddingCompanies.id, id))
- }
-
- // 2. companyConditionResponses 테이블 업데이트 (입찰 조건들)
- if (input.paymentTermsResponse !== undefined ||
- input.taxConditionsResponse !== undefined ||
- input.incotermsResponse !== undefined ||
- input.proposedContractDeliveryDate !== undefined ||
- input.proposedShippingPort !== undefined ||
- input.proposedDestinationPort !== undefined ||
- input.priceAdjustmentResponse !== undefined ||
- input.sparePartResponse !== undefined ||
- input.additionalProposals !== undefined) {
-
- const conditionsUpdateData: any = {}
- if (input.paymentTermsResponse !== undefined) conditionsUpdateData.paymentTermsResponse = input.paymentTermsResponse
- if (input.taxConditionsResponse !== undefined) conditionsUpdateData.taxConditionsResponse = input.taxConditionsResponse
- if (input.incotermsResponse !== undefined) conditionsUpdateData.incotermsResponse = input.incotermsResponse
- if (input.proposedContractDeliveryDate !== undefined) conditionsUpdateData.proposedContractDeliveryDate = input.proposedContractDeliveryDate || null
- if (input.proposedShippingPort !== undefined) conditionsUpdateData.proposedShippingPort = input.proposedShippingPort
- if (input.proposedDestinationPort !== undefined) conditionsUpdateData.proposedDestinationPort = input.proposedDestinationPort
- if (input.priceAdjustmentResponse !== undefined) conditionsUpdateData.priceAdjustmentResponse = input.priceAdjustmentResponse
- if (input.sparePartResponse !== undefined) conditionsUpdateData.sparePartResponse = input.sparePartResponse
- if (input.additionalProposals !== undefined) conditionsUpdateData.additionalProposals = input.additionalProposals
- conditionsUpdateData.updatedAt = new Date()
-
- await tx.update(companyConditionResponses)
- .set(conditionsUpdateData)
- .where(eq(companyConditionResponses.biddingCompanyId, id))
- }
-
- return true
- })
-
- // 캐시 무효화 (모든 입찰 관련 데이터 무효화)
- revalidateTag('quotation-vendors')
- revalidateTag('quotation-details')
- revalidatePath(`/evcp/bid/[id]`)
- return {
- success: true,
- message: '협력업체 정보가 성공적으로 업데이트되었습니다.',
- }
- } catch (error) {
- console.error('Failed to update quotation vendor:', error)
- return { success: false, error: '협력업체 정보 업데이트에 실패했습니다.' }
- }
-}
-
-// 협력업체 정보 삭제
-export async function deleteQuotationVendor(id: number) {
- try {
- // TODO: 실제로는 견적 시스템의 테이블에서 삭제
- console.log(`[TODO] 견적 시스템에서 협력업체 정보 ${id} 삭제 예정`)
-
- // 임시로 성공 응답
- return { success: true, message: '협력업체 정보가 성공적으로 삭제되었습니다.' }
- } catch (error) {
- console.error('Failed to delete quotation vendor:', error)
- return { success: false, error: '협력업체 정보 삭제에 실패했습니다.' }
- }
-}
-
-// 낙찰 처리
-export async function selectWinner(biddingId: number, vendorId: number, awardRatio: number, userId: string) {
- try {
- // 트랜잭션으로 처리
- await db.transaction(async (tx) => {
- // 기존 낙찰자 초기화
- await tx
- .update(biddingCompanies)
- .set({
- isWinner: false,
- updatedAt: new Date()
- })
- .where(eq(biddingCompanies.biddingId, biddingId))
-
- // 새로운 낙찰자 설정
- const biddingCompany = await tx
- .select()
- .from(biddingCompanies)
- .where(and(
- eq(biddingCompanies.biddingId, biddingId),
- eq(biddingCompanies.companyId, vendorId)
- ))
- .limit(1)
-
- if (biddingCompany.length > 0) {
- await tx
- .update(biddingCompanies)
- .set({
- isWinner: true,
- updatedAt: new Date()
- })
- .where(eq(biddingCompanies.id, biddingCompany[0].id))
- }
-
- // biddings 테이블의 상태 업데이트
- await tx
- .update(biddings)
- .set({
- status: 'vendor_selected',
- finalBidPrice: undefined, // TODO: 낙찰가 설정 로직 추가
- updatedAt: new Date()
- })
- .where(eq(biddings.id, biddingId))
- })
-
- // 캐시 무효화
- revalidateTag(`bidding-${biddingId}`)
- revalidateTag('quotation-vendors')
- revalidateTag('quotation-details')
- revalidatePath(`/evcp/bid/${biddingId}`)
- return { success: true, message: '낙찰 처리가 완료되었습니다.' }
- } catch (error) {
- console.error('Failed to select winner:', error)
- return { success: false, error: '낙찰 처리에 실패했습니다.' }
- }
-}
-
// 유찰 처리
export async function markAsDisposal(biddingId: number, userId: string) {
try {
@@ -913,12 +703,14 @@ export async function markAsDisposal(biddingId: number, userId: string) {
eq(biddingCompanies.biddingId, biddingId),
eq(biddingCompanies.isBiddingParticipated, true)
))
+ const userName = await getUserNameById(userId)
// 입찰 상태를 유찰로 변경
await db
.update(biddings)
.set({
status: 'bidding_disposal',
+ updatedBy: userName,
updatedAt: new Date()
})
.where(eq(biddings.id, biddingId))
@@ -996,12 +788,15 @@ export async function registerBidding(biddingId: number, userId: string) {
const bidding = biddingInfo[0]
+ const userName = await getUserNameById(userId)
+
await db.transaction(async (tx) => {
// 1. 입찰 상태를 오픈으로 변경
await tx
.update(biddings)
.set({
status: 'bidding_opened',
+ updatedBy: userName,
updatedAt: new Date()
})
.where(eq(biddings.id, biddingId))
@@ -1098,13 +893,14 @@ export async function createRebidding(biddingId: number, userId: string) {
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))
@@ -1721,6 +1517,7 @@ export interface PartnersBiddingListItem {
submissionDate: Date | null // 입찰제출일 (submissionEndDate)
}
+// 협력업체용 입찰 목록 조회 (bidding_companies 기준)
export async function getBiddingListForPartners(companyId: number): Promise<PartnersBiddingListItem[]> {
try {
const result = await db
@@ -1821,7 +1618,6 @@ export async function getBiddingDetailsForPartners(biddingId: number, companyId:
contractEndDate: biddings.contractEndDate,
// 일정 정보
- preQuoteDate: biddings.preQuoteDate,
biddingRegistrationDate: biddings.biddingRegistrationDate,
submissionStartDate: biddings.submissionStartDate,
submissionEndDate: biddings.submissionEndDate,
@@ -1849,7 +1645,6 @@ export async function getBiddingDetailsForPartners(biddingId: number, companyId:
isPreQuoteSelected: biddingCompanies.isPreQuoteSelected,
isBiddingParticipated: biddingCompanies.isBiddingParticipated,
isPreQuoteParticipated: biddingCompanies.isPreQuoteParticipated,
- isBiddingParticipated: biddingCompanies.isBiddingParticipated,
hasSpecificationMeeting: biddings.hasSpecificationMeeting,
// 응답한 조건들 (company_condition_responses) - 제시된 조건과 응답 모두 여기서 관리
paymentTermsResponse: companyConditionResponses.paymentTermsResponse,