diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/bidding/actions.ts | 2 | ||||
| -rw-r--r-- | lib/bidding/approval-actions.ts | 16 | ||||
| -rw-r--r-- | lib/bidding/detail/service.ts | 14 | ||||
| -rw-r--r-- | lib/bidding/handlers.ts | 21 | ||||
| -rw-r--r-- | lib/bidding/manage/import-bidding-items-from-excel.ts | 4 | ||||
| -rw-r--r-- | lib/bidding/pre-quote/service.ts | 10 | ||||
| -rw-r--r-- | lib/bidding/service.ts | 61 | ||||
| -rw-r--r-- | lib/bidding/validation.ts | 2 | ||||
| -rw-r--r-- | lib/bidding/vendor/partners-bidding-attendance-dialog.tsx | 1 | ||||
| -rw-r--r-- | lib/bidding/vendor/partners-bidding-detail.tsx | 7 | ||||
| -rw-r--r-- | lib/bidding/vendor/partners-bidding-list.tsx | 1 |
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} |
