summaryrefslogtreecommitdiff
path: root/lib/bidding/detail/service.ts
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-09-03 10:35:57 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-09-03 10:35:57 +0000
commita2bc455f654e011c53968b0d3a14389d7259847e (patch)
tree6ff60b8ef0880aaa4cf2c9d4f234772fb0a74537 /lib/bidding/detail/service.ts
parentbfe354f7633f62350e61eb784cbf1926079339d1 (diff)
(최겸) 구매 입찰 개발(벤더 응찰 개발 및 기본계약 요청 개발 필)
Diffstat (limited to 'lib/bidding/detail/service.ts')
-rw-r--r--lib/bidding/detail/service.ts174
1 files changed, 162 insertions, 12 deletions
diff --git a/lib/bidding/detail/service.ts b/lib/bidding/detail/service.ts
index 2ce17713..c811f46d 100644
--- a/lib/bidding/detail/service.ts
+++ b/lib/bidding/detail/service.ts
@@ -1,7 +1,8 @@
'use server'
import db from '@/db/db'
-import { biddings, prItemsForBidding, biddingDocuments, biddingCompanies, vendors, companyPrItemBids, companyConditionResponses, vendorSelectionResults, BiddingListItem, biddingConditions } from '@/db/schema'
+import { biddings, prItemsForBidding, biddingDocuments, biddingCompanies, vendors, companyPrItemBids, companyConditionResponses, vendorSelectionResults, BiddingListItem, biddingConditions, priceAdjustmentForms } from '@/db/schema'
+import { specificationMeetings } from '@/db/schema/bidding'
import { eq, and, sql, desc, ne } from 'drizzle-orm'
import { revalidatePath } from 'next/cache'
@@ -224,6 +225,7 @@ export async function getQuotationVendors(biddingId: number): Promise<QuotationV
proposedShippingPort: companyConditionResponses.proposedShippingPort,
proposedDestinationPort: companyConditionResponses.proposedDestinationPort,
priceAdjustmentResponse: companyConditionResponses.priceAdjustmentResponse,
+ isInitialResponse: companyConditionResponses.isInitialResponse,
sparePartResponse: companyConditionResponses.sparePartResponse,
additionalProposals: companyConditionResponses.additionalProposals,
})
@@ -256,6 +258,7 @@ export async function getQuotationVendors(biddingId: number): Promise<QuotationV
proposedShippingPort: vendor.proposedShippingPort || '',
proposedDestinationPort: vendor.proposedDestinationPort || '',
priceAdjustmentResponse: vendor.priceAdjustmentResponse || false,
+ isInitialResponse: vendor.isInitialResponse || false,
sparePartResponse: vendor.sparePartResponse || '',
additionalProposals: vendor.additionalProposals || '',
documents: [] // TODO: 문서 정보 조회 로직 추가
@@ -828,6 +831,7 @@ export async function getBiddingDetailsForPartners(biddingId: number, companyId:
proposedShippingPort: companyConditionResponses.proposedShippingPort,
proposedDestinationPort: companyConditionResponses.proposedDestinationPort,
priceAdjustmentResponse: companyConditionResponses.priceAdjustmentResponse,
+ isInitialResponse: companyConditionResponses.isInitialResponse,
sparePartResponse: companyConditionResponses.sparePartResponse,
additionalProposals: companyConditionResponses.additionalProposals,
responseSubmittedAt: companyConditionResponses.submittedAt,
@@ -859,9 +863,27 @@ export async function submitPartnerResponse(
proposedShippingPort?: string
proposedDestinationPort?: string
priceAdjustmentResponse?: boolean
+ isInitialResponse?: boolean
sparePartResponse?: string
additionalProposals?: string
finalQuoteAmount?: number
+ priceAdjustmentForm?: {
+ itemName?: string
+ adjustmentReflectionPoint?: string
+ majorApplicableRawMaterial?: string
+ adjustmentFormula?: string
+ rawMaterialPriceIndex?: string
+ referenceDate?: string
+ comparisonDate?: string
+ adjustmentRatio?: number
+ notes?: string
+ adjustmentConditions?: string
+ majorNonApplicableRawMaterial?: string
+ adjustmentPeriod?: string
+ contractorWriter?: string
+ adjustmentDate?: string
+ nonApplicableReason?: string
+ }
},
userId: string
) {
@@ -876,6 +898,7 @@ export async function submitPartnerResponse(
proposedShippingPort: response.proposedShippingPort,
proposedDestinationPort: response.proposedDestinationPort,
priceAdjustmentResponse: response.priceAdjustmentResponse,
+ isInitialResponse: response.isInitialResponse,
sparePartResponse: response.sparePartResponse,
additionalProposals: response.additionalProposals,
submittedAt: new Date(),
@@ -889,20 +912,67 @@ export async function submitPartnerResponse(
.where(eq(companyConditionResponses.biddingCompanyId, biddingCompanyId))
.limit(1)
+ let companyConditionResponseId: number
+
if (existingResponse.length > 0) {
// 업데이트
await tx
.update(companyConditionResponses)
.set(responseData)
.where(eq(companyConditionResponses.biddingCompanyId, biddingCompanyId))
+
+ companyConditionResponseId = existingResponse[0].id
} else {
// 새로 생성
- await tx
+ const [newResponse] = await tx
.insert(companyConditionResponses)
.values({
biddingCompanyId,
...responseData,
})
+ .returning({ id: companyConditionResponses.id })
+
+ companyConditionResponseId = newResponse.id
+ }
+
+ // 3. 연동제 정보 저장 (연동제 적용이 true이고 연동제 정보가 있는 경우)
+ if (response.priceAdjustmentResponse && response.priceAdjustmentForm) {
+ const priceAdjustmentData = {
+ companyConditionResponsesId: companyConditionResponseId,
+ itemName: response.priceAdjustmentForm.itemName,
+ adjustmentReflectionPoint: response.priceAdjustmentForm.adjustmentReflectionPoint,
+ majorApplicableRawMaterial: response.priceAdjustmentForm.majorApplicableRawMaterial,
+ adjustmentFormula: response.priceAdjustmentForm.adjustmentFormula,
+ rawMaterialPriceIndex: response.priceAdjustmentForm.rawMaterialPriceIndex,
+ referenceDate: response.priceAdjustmentForm.referenceDate ? new Date(response.priceAdjustmentForm.referenceDate) : null,
+ comparisonDate: response.priceAdjustmentForm.comparisonDate ? new Date(response.priceAdjustmentForm.comparisonDate) : null,
+ adjustmentRatio: response.priceAdjustmentForm.adjustmentRatio,
+ notes: response.priceAdjustmentForm.notes,
+ adjustmentConditions: response.priceAdjustmentForm.adjustmentConditions,
+ majorNonApplicableRawMaterial: response.priceAdjustmentForm.majorNonApplicableRawMaterial,
+ adjustmentPeriod: response.priceAdjustmentForm.adjustmentPeriod,
+ contractorWriter: response.priceAdjustmentForm.contractorWriter,
+ adjustmentDate: response.priceAdjustmentForm.adjustmentDate ? new Date(response.priceAdjustmentForm.adjustmentDate) : null,
+ nonApplicableReason: response.priceAdjustmentForm.nonApplicableReason,
+ }
+
+ // 기존 연동제 정보가 있는지 확인
+ const existingPriceAdjustment = await tx
+ .select()
+ .from(priceAdjustmentForms)
+ .where(eq(priceAdjustmentForms.companyConditionResponsesId, companyConditionResponseId))
+ .limit(1)
+
+ if (existingPriceAdjustment.length > 0) {
+ // 업데이트
+ await tx
+ .update(priceAdjustmentForms)
+ .set(priceAdjustmentData)
+ .where(eq(priceAdjustmentForms.companyConditionResponsesId, companyConditionResponseId))
+ } else {
+ // 새로 생성
+ await tx.insert(priceAdjustmentForms).values(priceAdjustmentData)
+ }
}
// 2. biddingCompanies 테이블에 견적 금액과 상태 업데이트
@@ -940,6 +1010,26 @@ export async function submitPartnerResponse(
// 사양설명회 정보 조회 (협력업체용)
export async function getSpecificationMeetingForPartners(biddingId: number) {
try {
+ // specification_meetings 테이블에서 사양설명회 정보 조회
+ const specMeeting = await db
+ .select({
+ id: specificationMeetings.id,
+ meetingDate: specificationMeetings.meetingDate,
+ meetingTime: specificationMeetings.meetingTime,
+ location: specificationMeetings.location,
+ address: specificationMeetings.address,
+ contactPerson: specificationMeetings.contactPerson,
+ contactPhone: specificationMeetings.contactPhone,
+ contactEmail: specificationMeetings.contactEmail,
+ agenda: specificationMeetings.agenda,
+ materials: specificationMeetings.materials,
+ notes: specificationMeetings.notes,
+ isRequired: specificationMeetings.isRequired,
+ })
+ .from(specificationMeetings)
+ .where(eq(specificationMeetings.biddingId, biddingId))
+ .limit(1)
+
// bidding_documents에서 사양설명회 관련 문서 조회
const documents = await db
.select({
@@ -956,17 +1046,12 @@ export async function getSpecificationMeetingForPartners(biddingId: number) {
eq(biddingDocuments.documentType, 'specification_meeting')
))
- // biddings 테이블에서 사양설명회 기본 정보 조회
+ // 기본 입찰 정보도 가져오기 (제목, 입찰번호 등)
const bidding = await db
.select({
id: biddings.id,
title: biddings.title,
biddingNumber: biddings.biddingNumber,
- preQuoteDate: biddings.preQuoteDate,
- biddingRegistrationDate: biddings.biddingRegistrationDate,
- managerName: biddings.managerName,
- managerEmail: biddings.managerEmail,
- managerPhone: biddings.managerPhone,
})
.from(biddings)
.where(eq(biddings.id, biddingId))
@@ -976,15 +1061,44 @@ export async function getSpecificationMeetingForPartners(biddingId: number) {
return { success: false, error: '입찰 정보를 찾을 수 없습니다.' }
}
+ // 사양설명회 정보가 없는 경우
+ if (specMeeting.length === 0) {
+ return {
+ success: true,
+ data: {
+ ...bidding[0],
+ documents,
+ meetingDate: null,
+ meetingTime: null,
+ location: null,
+ address: null,
+ contactPerson: null,
+ contactPhone: null,
+ contactEmail: null,
+ agenda: null,
+ materials: null,
+ notes: null,
+ isRequired: false,
+ }
+ }
+ }
+
return {
success: true,
data: {
...bidding[0],
documents,
- meetingDate: bidding[0].preQuoteDate ? bidding[0].preQuoteDate.toISOString().split('T')[0] : null,
- contactPerson: bidding[0].managerName,
- contactEmail: bidding[0].managerEmail,
- contactPhone: bidding[0].managerPhone,
+ meetingDate: specMeeting[0].meetingDate ? specMeeting[0].meetingDate.toISOString().split('T')[0] : null,
+ meetingTime: specMeeting[0].meetingTime,
+ location: specMeeting[0].location,
+ address: specMeeting[0].address,
+ contactPerson: specMeeting[0].contactPerson,
+ contactPhone: specMeeting[0].contactPhone,
+ contactEmail: specMeeting[0].contactEmail,
+ agenda: specMeeting[0].agenda,
+ materials: specMeeting[0].materials,
+ notes: specMeeting[0].notes,
+ isRequired: specMeeting[0].isRequired,
}
}
} catch (error) {
@@ -1094,3 +1208,39 @@ export async function updatePartnerAttendance(
return { success: false, error: '참석 여부 업데이트에 실패했습니다.' }
}
}
+
+// 연동제 정보 조회
+export async function getPriceAdjustmentForm(companyConditionResponseId: number) {
+ try {
+ const priceAdjustment = await db
+ .select()
+ .from(priceAdjustmentForms)
+ .where(eq(priceAdjustmentForms.companyConditionResponsesId, companyConditionResponseId))
+ .limit(1)
+
+ return priceAdjustment[0] || null
+ } catch (error) {
+ console.error('Failed to get price adjustment form:', error)
+ return null
+ }
+}
+
+// 입찰업체 ID로 연동제 정보 조회
+export async function getPriceAdjustmentFormByBiddingCompanyId(biddingCompanyId: number) {
+ try {
+ const result = await db
+ .select({
+ priceAdjustmentForm: priceAdjustmentForms,
+ companyConditionResponse: companyConditionResponses,
+ })
+ .from(companyConditionResponses)
+ .leftJoin(priceAdjustmentForms, eq(companyConditionResponses.id, priceAdjustmentForms.companyConditionResponsesId))
+ .where(eq(companyConditionResponses.biddingCompanyId, biddingCompanyId))
+ .limit(1)
+
+ return result[0]?.priceAdjustmentForm || null
+ } catch (error) {
+ console.error('Failed to get price adjustment form by bidding company id:', error)
+ return null
+ }
+}