diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-10-01 06:26:44 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-10-01 06:26:44 +0000 |
| commit | d689608ca2a54cab2cd12a12f0b6007a1be39ab2 (patch) | |
| tree | bb93e630c18b3028322f7f7aee87547e893f5df7 /lib/rfq-last/service.ts | |
| parent | 7021eca8f53e398f55f775c6dc431bca9670fabe (diff) | |
(대표님, 최겸) 구매 견적 첨부파일 type 오류 수정, 문서확정, short list 기능 수정
Diffstat (limited to 'lib/rfq-last/service.ts')
| -rw-r--r-- | lib/rfq-last/service.ts | 188 |
1 files changed, 183 insertions, 5 deletions
diff --git a/lib/rfq-last/service.ts b/lib/rfq-last/service.ts index 09d707d7..78d2479a 100644 --- a/lib/rfq-last/service.ts +++ b/lib/rfq-last/service.ts @@ -3,7 +3,7 @@ import { revalidatePath, unstable_cache, unstable_noStore } from "next/cache"; import db from "@/db/db"; -import { avlVendorInfo, paymentTerms, incoterms, rfqLastVendorQuotationItems, rfqLastVendorAttachments, rfqLastVendorResponses, RfqsLastView, rfqLastAttachmentRevisions, rfqLastAttachments, rfqsLast, rfqsLastView, users, rfqPrItems, prItemsLastView, vendors, rfqLastDetails, rfqLastVendorResponseHistory, rfqLastDetailsView, vendorContacts, projects, basicContract, basicContractTemplates, rfqLastTbeSessions, rfqLastTbeDocumentReviews, templateDetailView } from "@/db/schema"; +import { avlVendorInfo, paymentTerms, incoterms, rfqLastVendorQuotationItems, rfqLastVendorAttachments, rfqLastVendorResponses, RfqsLastView, rfqLastAttachmentRevisions, rfqLastAttachments, rfqsLast, rfqsLastView, users, rfqPrItems, prItemsLastView, vendors, rfqLastDetails, rfqLastVendorResponseHistory, rfqLastDetailsView, vendorContacts, projects, basicContract, basicContractTemplates, rfqLastTbeSessions, rfqLastTbeDocumentReviews, templateDetailView, RfqStatus } from "@/db/schema"; import { sql, and, desc, asc, like, ilike, or, eq, SQL, count, gte, lte, isNotNull, ne, inArray } from "drizzle-orm"; import { filterColumns } from "@/lib/filter-columns"; import { GetRfqLastAttachmentsSchema, GetRfqsSchema } from "./validations"; @@ -1527,6 +1527,9 @@ export async function getRfqVendorResponses(rfqId: number) { submittedBy: rfqLastVendorResponses.submittedBy, submittedByName: users.name, + isDocumentConfirmed: rfqLastVendorResponses.isDocumentConfirmed, + + // 금액 정보 totalAmount: rfqLastVendorResponses.totalAmount, currency: rfqLastVendorResponses.currency, @@ -1709,6 +1712,7 @@ export async function getRfqVendorResponses(rfqId: number) { responseVersion: response.responseVersion, isLatest: response.isLatest, status: response.status, + isDocumentConfirmed: response.isDocumentConfirmed, // 벤더 정보 vendor: { @@ -2980,7 +2984,7 @@ export async function sendRfqToVendors({ // 6. RFQ 상태 업데이트 if (results.length > 0) { - await updateRfqStatus(rfqId, currentUser.id); + await updateRfqStatus(rfqId, Number(currentUser.id)); } return { @@ -4812,13 +4816,24 @@ export async function updateShortList( .update(rfqLastTbeSessions) .set({ status: "준비중", - updatedBy: session.user.id, + updatedBy: Number(session.user.id), updatedAt: new Date() }) .where(eq(rfqLastTbeSessions.id, existingSession[0].id)); } }) ); + + // 2-3. RFQ 상태를 "Short List 확정"으로 업데이트 + await tx + .update(rfqsLast) + .set({ + status: "Short List 확정" as RfqStatus, + updatedBy: Number(session.user.id), + updatedAt: new Date() + }) + .where(eq(rfqsLast.id, rfqId)); + } else { // shortList가 false인 경우, 해당 벤더들의 활성 TBE 세션을 취소 상태로 변경 await Promise.all( @@ -4839,21 +4854,60 @@ export async function updateShortList( ) ) ); + + // shortList를 해제하는 경우의 상태 처리 + // 모든 벤더의 shortList가 false인지 확인 + const remainingShortlisted = await tx + .select() + .from(rfqLastDetails) + .where( + and( + eq(rfqLastDetails.rfqsLastId, rfqId), + eq(rfqLastDetails.isLatest, true), + eq(rfqLastDetails.shortList, true) + ) + ) + .limit(1); + + // 남은 shortList 벤더가 없으면 RFQ 상태를 이전 상태로 되돌림 + // if (remainingShortlisted.length === 0) { + // await tx + // .update(rfqsLast) + // .set({ + // status: "견적 접수" as RfqStatus, // 또는 적절한 이전 상태 + // updatedBy: Number(session.user.id), + // updatedAt: new Date() + // }) + // .where(eq(rfqsLast.id, rfqId)); + // } } return { success: true, updatedCount: updatedDetails.length, vendorIds, - tbeSessionsUpdated: shortListStatus + tbeSessionsUpdated: shortListStatus, + rfqStatusUpdated: true }; } + // 벤더가 없는 경우 (모든 shortList를 false로만 설정) + // RFQ 상태를 이전 상태로 되돌림 + await tx + .update(rfqsLast) + .set({ + status: "견적 접수" as RfqStatus, // 또는 적절한 이전 상태 + updatedBy: Number(session.user.id), + updatedAt: new Date() + }) + .where(eq(rfqsLast.id, rfqId)); + return { success: true, updatedCount: 0, vendorIds: [], - tbeSessionsUpdated: false + tbeSessionsUpdated: false, + rfqStatusUpdated: true }; }); @@ -5209,3 +5263,127 @@ export async function addAvlVendorsToRfq({ }; } } + + + +interface ConfirmVendorDocumentsResult { + success: boolean; + message: string; + updatedCount?: number; +} + +/** + * 특정 벤더의 모든 문서를 확정 처리 + * @param rfqId RFQ ID + * @param vendorId 벤더 ID + * @returns 처리 결과 + */ +export async function confirmVendorDocuments( + rfqId: number, + vendorId: number +): Promise<ConfirmVendorDocumentsResult> { + try { + // 데이터 유효성 검증 + if (!rfqId || !vendorId) { + return { + success: false, + message: "RFQ ID와 벤더 ID가 필요합니다.", + }; + } + + // 트랜잭션으로 두 테이블 동시 업데이트 + const result = await db.transaction(async (tx) => { + // 1. rfqLastVendorResponses 테이블 업데이트 + const vendorResponseResult = await tx + .update(rfqLastVendorResponses) + .set({ + isDocumentConfirmed: true, + updatedAt: new Date(), + }) + .where( + and( + eq(rfqLastVendorResponses.rfqsLastId, rfqId), + eq(rfqLastVendorResponses.vendorId, vendorId) + ) + ) + .returning({ id: rfqLastVendorResponses.id }); + + // 업데이트된 레코드 수 확인 + const updatedCount = vendorResponseResult.length; + + if (updatedCount === 0) { + throw new Error("해당 조건에 맞는 문서를 찾을 수 없습니다."); + } + + // 2. rfqsLast 테이블 status 업데이트 + const rfqUpdateResult = await tx + .update(rfqsLast) + .set({ + status: "견적요청문서 확정" as RfqStatus, + updatedAt: new Date(), + }) + .where(eq(rfqsLast.id, rfqId)) + .returning({ id: rfqsLast.id }); + + if (rfqUpdateResult.length === 0) { + throw new Error("RFQ 상태 업데이트에 실패했습니다."); + } + + return updatedCount; + }); + + // 캐시 무효화 (필요한 경우) + revalidatePath(`/rfq-last/${rfqId}`); + + return { + success: true, + message: `문서가 확정되었습니다.`, + updatedCount: result, + }; + } catch (error) { + console.log("문서 확정 중 오류 발생:", error); + + return { + success: false, + message: error instanceof Error + ? `문서 확정 실패: ${error.message}` + : "문서 확정 중 알 수 없는 오류가 발생했습니다.", + }; + } +} + +/** + * 특정 벤더의 문서 확정 상태 조회 + * @param rfqId RFQ ID + * @param vendorId 벤더 ID + * @returns 확정 상태 + */ +export async function getVendorDocumentConfirmStatus( + rfqId: number, + vendorId: number +): Promise<{ isConfirmed: boolean; count: number }> { + try { + const results = await db + .select({ + isDocumentConfirmed: rfqLastVendorResponses.isDocumentConfirmed, + }) + .from(rfqLastVendorResponses) + .where( + and( + eq(rfqLastVendorResponses.rfqsLastId, rfqId), + eq(rfqLastVendorResponses.vendorId, vendorId) + ) + ); + + const confirmedCount = results.filter(r => r.isDocumentConfirmed).length; + const totalCount = results.length; + + return { + isConfirmed: totalCount > 0 && confirmedCount === totalCount, + count: totalCount, + }; + } catch (error) { + console.error("문서 확정 상태 조회 중 오류:", error); + return { isConfirmed: false, count: 0 }; + } +}
\ No newline at end of file |
