summaryrefslogtreecommitdiff
path: root/lib/bidding/actions.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/bidding/actions.ts')
-rw-r--r--lib/bidding/actions.ts172
1 files changed, 110 insertions, 62 deletions
diff --git a/lib/bidding/actions.ts b/lib/bidding/actions.ts
index 9aabd469..65ff3138 100644
--- a/lib/bidding/actions.ts
+++ b/lib/bidding/actions.ts
@@ -1,26 +1,24 @@
"use server"
import db from "@/db/db"
-import { eq, and } from "drizzle-orm"
+import { eq, and, sql } from "drizzle-orm"
import {
biddings,
biddingCompanies,
prItemsForBidding,
vendors,
generalContracts,
- generalContractItems
+ generalContractItems,
+ biddingConditions
} from "@/db/schema"
import { createPurchaseOrder } from "@/lib/soap/ecc/send/create-po"
import { getCurrentSAPDate } from "@/lib/soap/utils"
+import { generateContractNumber } from "@/lib/general-contracts/service"
-// TO Contract 서버 액션
+// TO Contract
export async function transmitToContract(biddingId: number, userId: number) {
- console.log('=== transmitToContract STARTED ===')
- console.log('biddingId:', biddingId, 'userId:', userId)
-
try {
// 1. 입찰 정보 조회 (단순 쿼리)
- console.log('Querying bidding...')
const bidding = await db.select()
.from(biddings)
.where(eq(biddings.id, biddingId))
@@ -31,14 +29,19 @@ export async function transmitToContract(biddingId: number, userId: number) {
}
const biddingData = bidding[0]
- console.log('biddingData', biddingData)
- // 2. 낙찰된 업체들 조회 (별도 쿼리)
- console.log('Querying bidding companies...')
- let winnerCompaniesData = []
+ // 2. 입찰 조건 정보 조회
+ const biddingConditionData = await db.select()
+ .from(biddingConditions)
+ .where(eq(biddingConditions.biddingId, biddingId))
+ .limit(1)
+
+ const biddingCondition = biddingConditionData.length > 0 ? biddingConditionData[0] : null
+
+ // 3. 낙찰된 업체들 조회 (별도 쿼리)
+ let winnerCompaniesData: { companyId: number; finalQuoteAmount: string | null; vendorCode: string | null; vendorName: string | null; }[] = []
try {
// 2.1 biddingCompanies만 먼저 조회 (join 제거)
- console.log('Step 1: Querying biddingCompanies only...')
const biddingCompaniesRaw = await db.select()
.from(biddingCompanies)
.where(
@@ -48,12 +51,9 @@ export async function transmitToContract(biddingId: number, userId: number) {
)
)
- console.log('biddingCompaniesRaw:', biddingCompaniesRaw)
// 2.2 각 company에 대한 vendor 정보 개별 조회
for (const bc of biddingCompaniesRaw) {
- console.log('Processing companyId:', bc.companyId)
-
try {
const vendorData = await db.select()
.from(vendors)
@@ -61,13 +61,11 @@ export async function transmitToContract(biddingId: number, userId: number) {
.limit(1)
const vendor = vendorData.length > 0 ? vendorData[0] : null
- console.log('Vendor data for', bc.companyId, ':', vendor)
-
winnerCompaniesData.push({
companyId: bc.companyId,
finalQuoteAmount: bc.finalQuoteAmount,
vendorCode: vendor?.vendorCode || null,
- vendorName: vendor?.vendorName || null,
+ vendorName: vendor?.vendorName || null as string | null,
})
} catch (vendorError) {
console.error('Vendor query error for', bc.companyId, ':', vendorError)
@@ -75,22 +73,18 @@ export async function transmitToContract(biddingId: number, userId: number) {
winnerCompaniesData.push({
companyId: bc.companyId,
finalQuoteAmount: bc.finalQuoteAmount,
- vendorCode: null,
- vendorName: null,
+ vendorCode: null as string | null,
+ vendorName: null as string | null,
})
}
}
- console.log('winnerCompaniesData type:', typeof winnerCompaniesData)
- console.log('winnerCompaniesData length:', winnerCompaniesData?.length)
- console.log('winnerCompaniesData:', winnerCompaniesData)
} catch (queryError) {
console.error('Query error:', queryError)
throw new Error(`biddingCompanies 쿼리 실패: ${queryError}`)
}
// 상태 검증
- console.log('biddingData.status', biddingData.status)
if (biddingData.status !== 'vendor_selected') {
throw new Error("업체 선정이 완료되지 않은 입찰입니다.")
}
@@ -100,32 +94,64 @@ export async function transmitToContract(biddingId: number, userId: number) {
throw new Error("낙찰된 업체가 없습니다.")
}
- console.log('Processing', winnerCompaniesData.length, 'winner companies')
for (const winnerCompany of winnerCompaniesData) {
- // 계약 번호 자동 생성 (현재 시간 기반)
- const contractNumber = `CONTRACT-BID-${Date.now()}-${winnerCompany.companyId}`
- console.log('contractNumber', contractNumber)
+ // 계약 번호 자동 생성 (실제 규칙에 맞게)
+ const contractNumber = await generateContractNumber(userId.toString(), biddingData.contractType)
+ console.log('Generated contractNumber:', contractNumber)
// general-contract 생성
const contractResult = await db.insert(generalContracts).values({
contractNumber,
revision: 0,
contractSourceType: 'bid', // 입찰에서 생성됨
status: 'Draft',
- category: biddingData.contractType as any, // 단가계약, 일반계약, 매각계약
+ category: biddingData.contractType || 'general',
name: biddingData.title,
- selectionMethod: '입찰',
vendorId: winnerCompany.companyId,
linkedBidNumber: biddingData.biddingNumber,
- contractAmount: winnerCompany.finalQuoteAmount || undefined,
+ contractAmount: winnerCompany.finalQuoteAmount || null,
+ contractStartDate: biddingData.contractStartDate || null,
+ contractEndDate: biddingData.contractEndDate || null,
currency: biddingData.currency || 'KRW',
- registeredById: userId, // TODO: 현재 사용자 ID로 변경 필요
- lastUpdatedById: userId, // TODO: 현재 사용자 ID로 변경 필요
+ // 계약 조건 정보 추가
+ paymentTerm: biddingCondition?.paymentTerms || null,
+ taxType: biddingCondition?.taxConditions || 'V0',
+ deliveryTerm: biddingCondition?.incoterms || 'FOB',
+ shippingLocation: biddingCondition?.shippingPort || null,
+ dischargeLocation: biddingCondition?.destinationPort || null,
+ registeredById: userId,
+ lastUpdatedById: userId,
}).returning({ id: generalContracts.id })
console.log('contractResult', contractResult)
const contractId = contractResult[0].id
- // 3. PR 아이템들로 general-contract-items 생성 (일단 생략)
- console.log('Skipping PR items creation for now')
+ // 4. PR 아이템들로 general-contract-items 생성
+ const prItems = await db.select()
+ .from(prItemsForBidding)
+ .where(eq(prItemsForBidding.biddingId, biddingId))
+
+ if (prItems.length > 0) {
+ console.log(`Creating ${prItems.length} contract items for contract ${contractId}`)
+ for (const prItem of prItems) {
+ await db.insert(generalContractItems).values({
+ contractId,
+ project: prItem.projectInfo || '',
+ itemCode: prItem.itemNumber || '',
+ itemInfo: prItem.itemInfo || '',
+ specification: prItem.materialDescription || '',
+ quantity: prItem.quantity || null,
+ quantityUnit: prItem.quantityUnit || '',
+ contractUnitPrice: prItem.annualUnitPrice || null,
+ contractAmount: prItem.annualUnitPrice && prItem.quantity
+ ? (prItem.annualUnitPrice * prItem.quantity)
+ : null,
+ contractCurrency: biddingData.currency || 'KRW',
+ contractDeliveryDate: prItem.requestedDeliveryDate || null,
+ })
+ }
+ console.log(`Created ${prItems.length} contract items`)
+ } else {
+ console.log('No PR items found for this bidding')
+ }
}
return { success: true, message: `${winnerCompaniesData.length}개의 계약서가 생성되었습니다.` }
@@ -136,59 +162,80 @@ export async function transmitToContract(biddingId: number, userId: number) {
}
}
-// TO PO 서버 액션
+// TO PO
export async function transmitToPO(biddingId: number) {
try {
- // 1. 입찰 정보 및 낙찰 업체 조회
- const bidding = await db.query.biddings.findFirst({
- where: eq(biddings.id, biddingId),
- with: {
- biddingCompanies: {
- where: eq(biddingCompanies.isWinner, true), // 낙찰된 업체만
- with: {
- vendor: true
- }
- },
- prItemsForBidding: true
- }
- })
+ // 1. 입찰 정보 조회
+ const biddingData = await db.select()
+ .from(biddings)
+ .where(eq(biddings.id, biddingId))
+ .limit(1)
- if (!bidding) {
+ if (!biddingData || biddingData.length === 0) {
throw new Error("입찰 정보를 찾을 수 없습니다.")
}
+ const bidding = biddingData[0]
+
if (bidding.status !== 'vendor_selected') {
throw new Error("업체 선정이 완료되지 않은 입찰입니다.")
}
- const winnerCompanies = bidding.biddingCompanies.filter(bc => bc.isWinner)
+ // 2. 입찰 조건 정보 조회
+ const biddingConditionData = await db.select()
+ .from(biddingConditions)
+ .where(eq(biddingConditions.biddingId, biddingId))
+ .limit(1)
+
+ const biddingCondition = biddingConditionData.length > 0 ? biddingConditionData[0] : null
- if (winnerCompanies.length === 0) {
+ // 3. 낙찰된 업체들 조회
+ const winnerCompaniesRaw = await db.select({
+ companyId: biddingCompanies.companyId,
+ finalQuoteAmount: biddingCompanies.finalQuoteAmount,
+ vendorCode: vendors.vendorCode,
+ vendorName: vendors.vendorName
+ })
+ .from(biddingCompanies)
+ .leftJoin(vendors, eq(biddingCompanies.companyId, vendors.id))
+ .where(
+ and(
+ eq(biddingCompanies.biddingId, biddingId),
+ eq(biddingCompanies.isWinner, true)
+ )
+ )
+
+ if (winnerCompaniesRaw.length === 0) {
throw new Error("낙찰된 업체가 없습니다.")
}
- // 2. PO 데이터 구성
+ // 4. PR 아이템 조회
+ const prItems = await db.select()
+ .from(prItemsForBidding)
+ .where(eq(prItemsForBidding.biddingId, biddingId))
+
+ // 5. PO 데이터 구성 (bidding condition 정보 사용)
const poData = {
- T_Bidding_HEADER: winnerCompanies.map((company, index) => ({
+ T_Bidding_HEADER: winnerCompaniesRaw.map((company, index) => ({
ANFNR: bidding.biddingNumber,
- LIFNR: company.vendor?.vendorCode || `VENDOR${company.companyId}`,
+ LIFNR: company.vendorCode || `VENDOR${company.companyId}`,
ZPROC_IND: 'A', // 구매 처리 상태
ANGNR: bidding.biddingNumber,
WAERS: bidding.currency || 'KRW',
- ZTERM: '0001', // 기본 지급조건
- INCO1: 'FOB',
- INCO2: 'Seoul, Korea',
- MWSKZ: 'V0', // 세금 코드
+ ZTERM: biddingCondition?.paymentTerms || '0001', // 지급조건
+ INCO1: biddingCondition?.incoterms || 'FOB', // Incoterms
+ INCO2: biddingCondition?.destinationPort || biddingCondition?.shippingPort || 'Seoul, Korea',
+ MWSKZ: biddingCondition?.taxConditions || 'V0', // 세금 코드
LANDS: 'KR',
ZRCV_DT: getCurrentSAPDate(),
ZATTEN_IND: 'Y',
IHRAN: getCurrentSAPDate(),
TEXT: `PO from Bidding: ${bidding.title}`,
})),
- T_Bidding_ITEM: bidding.prItemsForBidding?.map((item, index) => ({
+ T_Bidding_ITEM: prItems.map((item, index) => ({
ANFNR: bidding.biddingNumber,
ANFPS: (index + 1).toString().padStart(5, '0'),
- LIFNR: winnerCompanies[0]?.vendor?.vendorCode || `VENDOR${winnerCompanies[0]?.companyId}`,
+ LIFNR: winnerCompaniesRaw[0]?.vendorCode || `VENDOR${winnerCompaniesRaw[0]?.companyId}`,
NETPR: item.annualUnitPrice?.toString() || '0',
PEINH: '1',
BPRME: item.quantityUnit || 'EA',
@@ -199,7 +246,7 @@ export async function transmitToPO(biddingId: number) {
? ((item.annualUnitPrice * item.quantity) * 1.1).toString() // 10% 부가세 가정
: '0',
LFDAT: item.requestedDeliveryDate?.toISOString().split('T')[0] || getCurrentSAPDate(),
- })) || [],
+ })),
T_PR_RETURN: [{
ANFNR: bidding.biddingNumber,
ANFPS: '00001',
@@ -211,6 +258,7 @@ export async function transmitToPO(biddingId: number) {
}
// 3. SAP으로 PO 전송
+ console.log('SAP으로 PO 전송할 poData', poData)
const result = await createPurchaseOrder(poData)
if (!result.success) {