summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/bidding/actions.ts2
-rw-r--r--lib/bidding/approval-actions.ts16
-rw-r--r--lib/bidding/detail/service.ts14
-rw-r--r--lib/bidding/handlers.ts21
-rw-r--r--lib/bidding/manage/import-bidding-items-from-excel.ts4
-rw-r--r--lib/bidding/pre-quote/service.ts10
-rw-r--r--lib/bidding/service.ts61
-rw-r--r--lib/bidding/validation.ts2
-rw-r--r--lib/bidding/vendor/partners-bidding-attendance-dialog.tsx1
-rw-r--r--lib/bidding/vendor/partners-bidding-detail.tsx7
-rw-r--r--lib/bidding/vendor/partners-bidding-list.tsx1
11 files changed, 99 insertions, 40 deletions
diff --git a/lib/bidding/actions.ts b/lib/bidding/actions.ts
index cc246ee7..6bedbab5 100644
--- a/lib/bidding/actions.ts
+++ b/lib/bidding/actions.ts
@@ -652,7 +652,7 @@ export async function cancelDisposalAction(
}
// 사용자 이름 조회 헬퍼 함수
-async function getUserNameById(userId: string): Promise<string> {
+export async function getUserNameById(userId: string): Promise<string> {
try {
const user = await db
.select({ name: users.name })
diff --git a/lib/bidding/approval-actions.ts b/lib/bidding/approval-actions.ts
index 0fb16439..b4f6f297 100644
--- a/lib/bidding/approval-actions.ts
+++ b/lib/bidding/approval-actions.ts
@@ -266,12 +266,14 @@ export async function requestBiddingInvitationWithApproval(data: {
const { default: db } = await import('@/db/db');
const { biddings, biddingCompanies, prItemsForBidding } = await import('@/db/schema');
const { eq } = await import('drizzle-orm');
-
+ const { getUserNameById } = await import('@/lib/bidding/actions');
+ const userName = await getUserNameById(data.currentUser.id.toString());
+
await db
.update(biddings)
.set({
status: 'approval_pending', // 결재 진행중 상태
- // updatedBy: String(data.currentUser.id), // 기존 등록자 유지를 위해 주석 처리
+ updatedBy: userName,
updatedAt: new Date()
})
.where(eq(biddings.id, data.biddingId));
@@ -465,6 +467,7 @@ export async function requestBiddingClosureWithApproval(data: {
const { default: db } = await import('@/db/db');
const { biddings } = await import('@/db/schema');
const { eq } = await import('drizzle-orm');
+ const { getUserNameById } = await import('@/lib/bidding/actions');
// 유찰상태인지 확인
const biddingResult = await db
@@ -487,12 +490,12 @@ export async function requestBiddingClosureWithApproval(data: {
// 3. 입찰 상태를 결재 진행중으로 변경
debugLog('[BiddingClosureApproval] 입찰 상태 변경 시작');
-
+ const userName = await getUserNameById(data.currentUser.id.toString());
await db
.update(biddings)
.set({
status: 'approval_pending', // 폐찰 결재 진행중 상태
- // updatedBy: Number(data.currentUser.id), // 기존 등록자 유지를 위해 주석 처리
+ updatedBy: userName,
updatedAt: new Date()
})
.where(eq(biddings.id, data.biddingId));
@@ -693,12 +696,13 @@ export async function requestBiddingAwardWithApproval(data: {
const { default: db } = await import('@/db/db');
const { biddings } = await import('@/db/schema');
const { eq } = await import('drizzle-orm');
-
+ const { getUserNameById } = await import('@/lib/bidding/actions');
+ const userName = await getUserNameById(data.currentUser.id.toString());
await db
.update(biddings)
.set({
status: 'approval_pending', // 낙찰 결재 진행중 상태
- // updatedBy: Number(data.currentUser.id), // 기존 등록자 유지를 위해 주석 처리
+ updatedBy: userName,
updatedAt: new Date()
})
.where(eq(biddings.id, data.biddingId));
diff --git a/lib/bidding/detail/service.ts b/lib/bidding/detail/service.ts
index 68c55fb0..17ea8f28 100644
--- a/lib/bidding/detail/service.ts
+++ b/lib/bidding/detail/service.ts
@@ -1288,10 +1288,14 @@ export async function getAwardedCompanies(biddingId: number) {
companyId: biddingCompanies.companyId,
companyName: vendors.vendorName,
finalQuoteAmount: biddingCompanies.finalQuoteAmount,
- awardRatio: biddingCompanies.awardRatio
+ awardRatio: biddingCompanies.awardRatio,
+ vendorCode: vendors.vendorCode,
+ companySize: vendors.businessSize,
+ targetPrice: biddings.targetPrice
})
.from(biddingCompanies)
.leftJoin(vendors, eq(biddingCompanies.companyId, vendors.id))
+ .leftJoin(biddings, eq(biddingCompanies.biddingId, biddings.id))
.where(and(
eq(biddingCompanies.biddingId, biddingId),
eq(biddingCompanies.isWinner, true)
@@ -1301,7 +1305,10 @@ export async function getAwardedCompanies(biddingId: number) {
companyId: company.companyId,
companyName: company.companyName,
finalQuoteAmount: parseFloat(company.finalQuoteAmount?.toString() || '0'),
- awardRatio: parseFloat(company.awardRatio?.toString() || '0')
+ awardRatio: parseFloat(company.awardRatio?.toString() || '0'),
+ vendorCode: company.vendorCode,
+ companySize: company.companySize,
+ targetPrice: company.targetPrice ? parseFloat(company.targetPrice.toString()) : 0
}))
} catch (error) {
console.error('Failed to get awarded companies:', error)
@@ -1330,7 +1337,7 @@ async function updateBiddingAmounts(biddingId: number) {
.set({
targetPrice: totalTargetAmount.toString(),
budget: totalBudgetAmount.toString(),
- finalBidPrice: totalActualAmount.toString(),
+ actualPrice: totalActualAmount.toString(),
updatedAt: new Date()
})
.where(eq(biddings.id, biddingId))
@@ -1745,7 +1752,6 @@ export async function getBiddingDetailsForPartners(biddingId: number, companyId:
biddingRegistrationDate: biddings.biddingRegistrationDate,
submissionStartDate: biddings.submissionStartDate,
submissionEndDate: biddings.submissionEndDate,
- evaluationDate: biddings.evaluationDate,
// 가격 정보
currency: biddings.currency,
diff --git a/lib/bidding/handlers.ts b/lib/bidding/handlers.ts
index d56a083a..03a85bb6 100644
--- a/lib/bidding/handlers.ts
+++ b/lib/bidding/handlers.ts
@@ -422,12 +422,13 @@ export async function requestBiddingClosureInternal(payload: {
const { default: db } = await import('@/db/db');
const { biddings } = await import('@/db/schema');
const { eq } = await import('drizzle-orm');
-
+ const { getUserNameById } = await import('@/lib/bidding/actions');
+ const userName = await getUserNameById(payload.currentUserId.toString());
await db
.update(biddings)
.set({
status: 'bid_closure',
- updatedBy: payload.currentUserId.toString(),
+ updatedBy: userName,
updatedAt: new Date(),
remarks: payload.description, // 폐찰 사유를 remarks에 저장
})
@@ -614,6 +615,15 @@ export async function mapBiddingAwardToTemplateVariables(payload: {
biddingId: number;
selectionReason: string;
requestedAt: Date;
+ awardedCompanies?: Array<{
+ companyId: number;
+ companyName: string | null;
+ finalQuoteAmount: number;
+ awardRatio: number;
+ vendorCode?: string | null;
+ companySize?: string | null;
+ targetPrice?: number | null;
+ }>;
}): Promise<Record<string, string>> {
const { biddingId, selectionReason, requestedAt } = payload;
@@ -649,8 +659,11 @@ export async function mapBiddingAwardToTemplateVariables(payload: {
const bidding = biddingInfo[0];
// 2. 낙찰된 업체 정보 조회
- const { getAwardedCompanies } = await import('@/lib/bidding/detail/service');
- const awardedCompanies = await getAwardedCompanies(biddingId);
+ let awardedCompanies = payload.awardedCompanies;
+ if (!awardedCompanies) {
+ const { getAwardedCompanies } = await import('@/lib/bidding/detail/service');
+ awardedCompanies = await getAwardedCompanies(biddingId);
+ }
// 3. 입찰 대상 자재 정보 조회
const biddingItemsInfo = await db
diff --git a/lib/bidding/manage/import-bidding-items-from-excel.ts b/lib/bidding/manage/import-bidding-items-from-excel.ts
index 2e0dfe33..fe5b17a9 100644
--- a/lib/bidding/manage/import-bidding-items-from-excel.ts
+++ b/lib/bidding/manage/import-bidding-items-from-excel.ts
@@ -1,6 +1,7 @@
import ExcelJS from "exceljs"
import { PRItemInfo } from "@/components/bidding/manage/bidding-items-editor"
import { getProjectIdByCodeAndName } from "./project-utils"
+import { decryptWithServerAction } from "@/components/drm/drmUtils"
export interface ImportBiddingItemsResult {
success: boolean
@@ -19,7 +20,8 @@ export async function importBiddingItemsFromExcel(
try {
const workbook = new ExcelJS.Workbook()
- const arrayBuffer = await file.arrayBuffer()
+ // DRM 해제 후 ArrayBuffer 획득 (DRM 서버 미연결 시 원본 반환)
+ const arrayBuffer = await decryptWithServerAction(file)
await workbook.xlsx.load(arrayBuffer)
const worksheet = workbook.worksheets[0]
diff --git a/lib/bidding/pre-quote/service.ts b/lib/bidding/pre-quote/service.ts
index e1152abe..6fef228c 100644
--- a/lib/bidding/pre-quote/service.ts
+++ b/lib/bidding/pre-quote/service.ts
@@ -859,8 +859,8 @@ export async function getSelectedVendorsForBidding(biddingId: number) {
interface CreatePreQuoteRfqInput {
rfqType: string;
rfqTitle: string;
- dueDate: Date;
- picUserId: number;
+ dueDate?: Date;
+ picUserId: number | string | undefined;
projectId?: number;
remark?: string;
biddingNumber?: string;
@@ -875,6 +875,8 @@ interface CreatePreQuoteRfqInput {
remark?: string;
materialCode?: string;
materialName?: string;
+ totalWeight?: number | string | null; // 중량 추가
+ weightUnit?: string | null; // 중량단위 추가
}>;
biddingConditions?: {
paymentTerms?: string | null
@@ -976,6 +978,10 @@ export async function createPreQuoteRfqAction(input: CreatePreQuoteRfqInput) {
quantity: item.quantity, // 수량
uom: item.uom, // 단위
+ // 중량 정보
+ grossWeight: item.totalWeight ? (typeof item.totalWeight === 'string' ? parseFloat(item.totalWeight) : item.totalWeight) : null,
+ gwUom: item.weightUnit || null,
+
majorYn: index === 0, // 첫 번째 아이템을 주요 아이템으로 설정
remark: item.remark || null, // 비고
}));
diff --git a/lib/bidding/service.ts b/lib/bidding/service.ts
index 77a0b1b4..76cd31f7 100644
--- a/lib/bidding/service.ts
+++ b/lib/bidding/service.ts
@@ -61,6 +61,27 @@ export async function getUserCodeByEmail(email: string): Promise<string | null>
}
}
+// 사용자 ID로 상세 정보 조회 (이름, 코드 등)
+export async function getUserDetails(userId: number) {
+ try {
+ const user = await db
+ .select({
+ id: users.id,
+ name: users.name,
+ userCode: users.userCode,
+ employeeNumber: users.employeeNumber
+ })
+ .from(users)
+ .where(eq(users.id, userId))
+ .limit(1)
+
+ return user[0] || null
+ } catch (error) {
+ console.error('Failed to get user details:', error)
+ return null
+ }
+}
+
// userId를 user.name으로 변환하는 유틸리티 함수
async function getUserNameById(userId: string): Promise<string> {
try {
@@ -421,9 +442,10 @@ export async function getBiddings(input: GetBiddingsSchema) {
// 메타 정보
remarks: biddings.remarks,
updatedAt: biddings.updatedAt,
- updatedBy: biddings.updatedBy,
+ updatedBy: users.name,
})
.from(biddings)
+ .leftJoin(users, sql`${biddings.updatedBy} = ${users.id}::varchar`)
.where(finalWhere)
.orderBy(...orderByColumns)
.limit(input.perPage)
@@ -874,7 +896,6 @@ export async function createBidding(input: CreateBiddingInput, userId: string) {
biddingRegistrationDate: new Date(),
submissionStartDate: parseDate(input.submissionStartDate),
submissionEndDate: parseDate(input.submissionEndDate),
- evaluationDate: parseDate(input.evaluationDate),
hasSpecificationMeeting: input.hasSpecificationMeeting || false,
hasPrDocument: input.hasPrDocument || false,
@@ -913,6 +934,7 @@ export async function createBidding(input: CreateBiddingInput, userId: string) {
await tx.insert(biddingNoticeTemplate).values({
biddingId,
title: input.title + ' 입찰공고',
+ type: input.noticeType || 'standard',
content: input.content || standardContent,
isTemplate: false,
})
@@ -1723,7 +1745,6 @@ export async function updateBiddingBasicInfo(
contractEndDate?: string
submissionStartDate?: string
submissionEndDate?: string
- evaluationDate?: string
hasSpecificationMeeting?: boolean
hasPrDocument?: boolean
currency?: string
@@ -1781,9 +1802,23 @@ export async function updateBiddingBasicInfo(
// 정의된 필드들만 업데이트
if (updates.title !== undefined) updateData.title = updates.title
if (updates.description !== undefined) updateData.description = updates.description
- if (updates.content !== undefined) updateData.content = updates.content
+ // content는 bidding 테이블에 컬럼이 없음, notice content는 별도로 저장해야 함
+ // if (updates.content !== undefined) updateData.content = updates.content
if (updates.noticeType !== undefined) updateData.noticeType = updates.noticeType
if (updates.contractType !== undefined) updateData.contractType = updates.contractType
+
+ // 입찰공고 내용 저장
+ if (updates.content !== undefined) {
+ try {
+ await saveBiddingNotice(biddingId, {
+ title: (updates.title || '') + ' 입찰공고', // 제목이 없으면 기존 제목을 가져오거나 해야하는데, 여기서는 업데이트된 제목 사용
+ content: updates.content
+ })
+ } catch (e) {
+ console.error('Failed to save bidding notice content:', e)
+ // 공고 저장 실패는 전체 업데이트 실패로 처리하지 않음 (로그만 남김)
+ }
+ }
if (updates.biddingType !== undefined) updateData.biddingType = updates.biddingType
if (updates.biddingTypeCustom !== undefined) updateData.biddingTypeCustom = updates.biddingTypeCustom
if (updates.awardCount !== undefined) updateData.awardCount = updates.awardCount
@@ -1795,7 +1830,6 @@ export async function updateBiddingBasicInfo(
if (updates.contractEndDate !== undefined) updateData.contractEndDate = parseDate(updates.contractEndDate)
if (updates.submissionStartDate !== undefined) updateData.submissionStartDate = parseDate(updates.submissionStartDate)
if (updates.submissionEndDate !== undefined) updateData.submissionEndDate = parseDate(updates.submissionEndDate)
- if (updates.evaluationDate !== undefined) updateData.evaluationDate = parseDate(updates.evaluationDate)
if (updates.hasSpecificationMeeting !== undefined) updateData.hasSpecificationMeeting = updates.hasSpecificationMeeting
if (updates.hasPrDocument !== undefined) updateData.hasPrDocument = updates.hasPrDocument
if (updates.currency !== undefined) updateData.currency = updates.currency
@@ -2889,7 +2923,7 @@ export async function increaseRoundOrRebid(biddingId: number, userId: string | u
let currentRound = match ? parseInt(match[1]) : 1
if (currentRound >= 3) {
- // -03 이상이면 새로운 번호 생성
+ // -03 이상이면 재입찰이며, 새로운 번호 생성
newBiddingNumber = await generateBiddingNumber(existingBidding.contractType, userId, tx)
// 새로 생성한 입찰번호를 원입찰번호로 셋팅
originalBiddingNumber = newBiddingNumber.split('-')[0]
@@ -2913,13 +2947,15 @@ export async function increaseRoundOrRebid(biddingId: number, userId: string | u
// 기본 정보 복제
projectName: existingBidding.projectName,
+ projectCode: existingBidding.projectCode, // 프로젝트 코드 복제
itemName: existingBidding.itemName,
title: existingBidding.title,
description: existingBidding.description,
// 계약 정보 복제
contractType: existingBidding.contractType,
- biddingType: existingBidding.biddingType,
+ noticeType: existingBidding.noticeType, // 공고타입 복제
+ biddingType: existingBidding.biddingType, // 구매유형 복제
awardCount: existingBidding.awardCount,
contractStartDate: existingBidding.contractStartDate,
contractEndDate: existingBidding.contractEndDate,
@@ -2929,7 +2965,6 @@ export async function increaseRoundOrRebid(biddingId: number, userId: string | u
biddingRegistrationDate: new Date(),
submissionStartDate: null,
submissionEndDate: null,
- evaluationDate: null,
// 사양설명회
hasSpecificationMeeting: existingBidding.hasSpecificationMeeting,
@@ -2939,6 +2974,7 @@ export async function increaseRoundOrRebid(biddingId: number, userId: string | u
budget: existingBidding.budget,
targetPrice: existingBidding.targetPrice,
targetPriceCalculationCriteria: existingBidding.targetPriceCalculationCriteria,
+ actualPrice: existingBidding.actualPrice,
finalBidPrice: null, // 최종입찰가는 초기화
// PR 정보 복제
@@ -3194,8 +3230,6 @@ export async function increaseRoundOrRebid(biddingId: number, userId: string | u
.from(biddingDocuments)
.where(and(
eq(biddingDocuments.biddingId, biddingId),
- // PR 아이템에 연결된 첨부파일은 제외 (SHI용과 협력업체용만 복제)
- isNull(biddingDocuments.prItemId),
// SHI용(evaluation_doc) 또는 협력업체용(company_proposal) 문서만 복제
or(
eq(biddingDocuments.documentType, 'evaluation_doc'),
@@ -3266,6 +3300,8 @@ export async function increaseRoundOrRebid(biddingId: number, userId: string | u
}
revalidatePath('/bid-receive')
+ revalidatePath('/evcp/bid-receive')
+ revalidatePath('/evcp/bid')
revalidatePath(`/bid-receive/${biddingId}`) // 기존 입찰 페이지도 갱신
revalidatePath(`/bid-receive/${newBidding.id}`)
@@ -3825,7 +3861,7 @@ export async function getBiddingsForFailure(input: GetBiddingsSchema) {
// 유찰 정보 (업데이트 일시를 유찰일로 사용)
disposalDate: biddings.updatedAt, // 유찰일
disposalUpdatedAt: biddings.updatedAt, // 폐찰수정일
- disposalUpdatedBy: biddings.updatedBy, // 폐찰수정자
+ disposalUpdatedBy: users.name, // 폐찰수정자
// 폐찰 정보
closureReason: biddings.description, // 폐찰사유
@@ -3840,9 +3876,10 @@ export async function getBiddingsForFailure(input: GetBiddingsSchema) {
createdBy: biddings.createdBy,
createdAt: biddings.createdAt,
updatedAt: biddings.updatedAt,
- updatedBy: biddings.updatedBy,
+ updatedBy: users.name,
})
.from(biddings)
+ .leftJoin(users, sql`${biddings.updatedBy} = ${users.id}::varchar`)
.leftJoin(biddingDocuments, and(
eq(biddingDocuments.biddingId, biddings.id),
eq(biddingDocuments.documentType, 'evaluation_doc'), // 폐찰 문서
diff --git a/lib/bidding/validation.ts b/lib/bidding/validation.ts
index 73c2fe21..3254ae7e 100644
--- a/lib/bidding/validation.ts
+++ b/lib/bidding/validation.ts
@@ -99,7 +99,6 @@ export const createBiddingSchema = z.object({
submissionEndDate: z.string().optional(),
- evaluationDate: z.string().optional(),
// 회의 및 문서
hasSpecificationMeeting: z.boolean().default(false),
@@ -220,7 +219,6 @@ export const createBiddingSchema = z.object({
submissionStartDate: z.string().optional(),
submissionEndDate: z.string().optional(),
- evaluationDate: z.string().optional(),
hasSpecificationMeeting: z.boolean().optional(),
hasPrDocument: z.boolean().optional(),
diff --git a/lib/bidding/vendor/partners-bidding-attendance-dialog.tsx b/lib/bidding/vendor/partners-bidding-attendance-dialog.tsx
index d0ef97f1..8d6cb82d 100644
--- a/lib/bidding/vendor/partners-bidding-attendance-dialog.tsx
+++ b/lib/bidding/vendor/partners-bidding-attendance-dialog.tsx
@@ -37,7 +37,6 @@ interface PartnersSpecificationMeetingDialogProps {
title: string
preQuoteDate: string | null
biddingRegistrationDate: string | null
- evaluationDate: string | null
hasSpecificationMeeting?: boolean // 사양설명회 여부 추가
} | null
biddingCompanyId: number
diff --git a/lib/bidding/vendor/partners-bidding-detail.tsx b/lib/bidding/vendor/partners-bidding-detail.tsx
index bf76de62..087648ab 100644
--- a/lib/bidding/vendor/partners-bidding-detail.tsx
+++ b/lib/bidding/vendor/partners-bidding-detail.tsx
@@ -75,7 +75,6 @@ interface BiddingDetail {
biddingRegistrationDate: Date | string | null
submissionStartDate: Date | string | null
submissionEndDate: Date | string | null
- evaluationDate: Date | string | null
currency: string
budget: number | null
targetPrice: number | null
@@ -927,11 +926,7 @@ export function PartnersBiddingDetail({ biddingId, companyId }: PartnersBiddingD
})()}
</div>
)}
- {biddingDetail.evaluationDate && (
- <div>
- <span className="font-medium">평가일:</span> {format(new Date(biddingDetail.evaluationDate), "yyyy-MM-dd HH:mm")}
- </div>
- )}
+
</div>
</div>
</CardContent>
diff --git a/lib/bidding/vendor/partners-bidding-list.tsx b/lib/bidding/vendor/partners-bidding-list.tsx
index 0f68ed68..f1cb0bdc 100644
--- a/lib/bidding/vendor/partners-bidding-list.tsx
+++ b/lib/bidding/vendor/partners-bidding-list.tsx
@@ -181,7 +181,6 @@ export function PartnersBiddingList({ promises }: PartnersBiddingListProps) {
title: rowAction.row.original.title,
preQuoteDate: null,
biddingRegistrationDate: rowAction.row.original.submissionStartDate?.toISOString() || null,
- evaluationDate: null,
hasSpecificationMeeting: rowAction.row.original.hasSpecificationMeeting || false,
} : null}
biddingCompanyId={rowAction?.row.original?.biddingCompanyId || 0}