diff options
Diffstat (limited to 'lib/tbe-last/service.ts')
| -rw-r--r-- | lib/tbe-last/service.ts | 231 |
1 files changed, 201 insertions, 30 deletions
diff --git a/lib/tbe-last/service.ts b/lib/tbe-last/service.ts index 760f66ac..d9046524 100644 --- a/lib/tbe-last/service.ts +++ b/lib/tbe-last/service.ts @@ -6,10 +6,11 @@ import db from "@/db/db"; import { and, desc, asc, eq, sql, or, isNull, isNotNull, ne, inArray } from "drizzle-orm"; import { tbeLastView, tbeDocumentsView } from "@/db/schema"; import { rfqPrItems } from "@/db/schema/rfqLast"; -import { rfqLastTbeDocumentReviews, rfqLastTbePdftronComments, rfqLastTbeVendorDocuments } from "@/db/schema"; +import { rfqLastTbeDocumentReviews, rfqLastTbePdftronComments, rfqLastTbeVendorDocuments,rfqLastTbeSessions } from "@/db/schema"; import { filterColumns } from "@/lib/filter-columns"; import { GetTBELastSchema } from "./validations"; - +import { getServerSession } from "next-auth" +import { authOptions } from "@/app/api/auth/[...nextauth]/route" // ========================================== // 1. TBE 세션 목록 조회 // ========================================== @@ -87,8 +88,8 @@ export async function getAllTBELast(input: GetTBELastSchema) { // 2. TBE 세션 상세 조회 // ========================================== export async function getTBESessionDetail(sessionId: number) { - return unstable_cache( - async () => { + // return unstable_cache( + // async () => { // 세션 기본 정보 const [session] = await db .select() @@ -153,13 +154,13 @@ export async function getTBESessionDetail(sessionId: number) { prItems, documents: documentsWithComments, }; - }, - [`tbe-session-${sessionId}`], - { - revalidate: 60, - tags: [`tbe-session-${sessionId}`], - } - )(); + // }, + // [`tbe-session-${sessionId}`], + // { + // revalidate: 60, + // tags: [`tbe-session-${sessionId}`], + // } + // )(); } // ========================================== @@ -190,25 +191,6 @@ export async function getDocumentComments(documentReviewId: number) { return comments; } -// ========================================== -// 4. TBE 평가 결과 업데이트 -// ========================================== -export async function updateTBEEvaluation( - sessionId: number, - data: { - evaluationResult: "pass" | "conditional_pass" | "non_pass"; - conditionalRequirements?: string; - technicalSummary?: string; - commercialSummary?: string; - overallRemarks?: string; - } -) { - // 실제 업데이트 로직 - // await db.update(rfqLastTbeSessions)... - - // 캐시 무효화 - return { success: true }; -} // ========================================== // 5. 벤더 문서 업로드 @@ -244,4 +226,193 @@ export async function uploadVendorDocument( .returning(); return document; +} + +interface UpdateEvaluationData { + evaluationResult?: "Acceptable" | "Acceptable with Comment" | "Not Acceptable" + conditionalRequirements?: string + conditionsFulfilled?: boolean + technicalSummary?: string + commercialSummary?: string + overallRemarks?: string + approvalRemarks?: string + status?: "준비중" | "진행중" | "검토중" | "보류" | "완료" | "취소" +} + +export async function updateTbeEvaluation( + tbeSessionId: number, + data: UpdateEvaluationData +) { + try { + const session = await getServerSession(authOptions) + if (!session?.user) { + return { success: false, error: "인증이 필요합니다" } + } + + const userId = typeof session.user.id === 'string' ? parseInt(session.user.id) : session.user.id + + // 현재 TBE 세션 조회 + const [currentTbeSession] = await db + .select() + .from(rfqLastTbeSessions) + .where(eq(rfqLastTbeSessions.id, tbeSessionId)) + .limit(1) + + if (!currentTbeSession) { + return { success: false, error: "TBE 세션을 찾을 수 없습니다" } + } + + // 업데이트 데이터 준비 + const updateData: any = { + updatedBy: userId, + updatedAt: new Date() + } + + // 평가 결과 관련 필드 + if (data.evaluationResult !== undefined) { + updateData.evaluationResult = data.evaluationResult + } + + // 조건부 승인 관련 (Acceptable with Comment인 경우) + if (data.evaluationResult === "Acceptable with Comment") { + if (data.conditionalRequirements !== undefined) { + updateData.conditionalRequirements = data.conditionalRequirements + } + if (data.conditionsFulfilled !== undefined) { + updateData.conditionsFulfilled = data.conditionsFulfilled + } + } else if (data.evaluationResult === "Acceptable") { + // Acceptable인 경우 조건부 필드 초기화 + updateData.conditionalRequirements = null + updateData.conditionsFulfilled = true + } else if (data.evaluationResult === "Not Acceptable") { + // Not Acceptable인 경우 조건부 필드 초기화 + updateData.conditionalRequirements = null + updateData.conditionsFulfilled = false + } + + // 평가 요약 필드 + if (data.technicalSummary !== undefined) { + updateData.technicalSummary = data.technicalSummary + } + if (data.commercialSummary !== undefined) { + updateData.commercialSummary = data.commercialSummary + } + if (data.overallRemarks !== undefined) { + updateData.overallRemarks = data.overallRemarks + } + + // 승인 관련 필드 + if (data.approvalRemarks !== undefined) { + updateData.approvalRemarks = data.approvalRemarks + updateData.approvedBy = userId + updateData.approvedAt = new Date() + } + + // 상태 업데이트 + if (data.status !== undefined) { + updateData.status = data.status + + // 완료 상태로 변경 시 종료일 설정 + if (data.status === "완료") { + updateData.actualEndDate = new Date() + } + } + + // TBE 세션 업데이트 + const [updated] = await db + .update(rfqLastTbeSessions) + .set(updateData) + .where(eq(rfqLastTbeSessions.id, tbeSessionId)) + .returning() + + // 캐시 초기화 + revalidateTag(`tbe-session-${tbeSessionId}`) + revalidateTag(`tbe-sessions`) + + // RFQ 관련 캐시도 초기화 + if (currentTbeSession.rfqsLastId) { + revalidateTag(`rfq-${currentTbeSession.rfqsLastId}`) + } + + return { + success: true, + data: updated, + message: "평가가 성공적으로 저장되었습니다" + } + + } catch (error) { + console.error("Failed to update TBE evaluation:", error) + return { + success: false, + error: error instanceof Error ? error.message : "평가 저장에 실패했습니다" + } + } +} + +export async function getTbeVendorDocuments(tbeSessionId: number) { + + try { + const documents = await db + .select({ + id: rfqLastTbeVendorDocuments.id, + documentName: rfqLastTbeVendorDocuments.originalFileName, + documentType: rfqLastTbeVendorDocuments.documentType, + fileName: rfqLastTbeVendorDocuments.fileName, + fileSize: rfqLastTbeVendorDocuments.fileSize, + fileType: rfqLastTbeVendorDocuments.fileType, + documentNo: rfqLastTbeVendorDocuments.documentNo, + revisionNo: rfqLastTbeVendorDocuments.revisionNo, + issueDate: rfqLastTbeVendorDocuments.issueDate, + description: rfqLastTbeVendorDocuments.description, + submittedAt: rfqLastTbeVendorDocuments.submittedAt, + // 검토 정보는 rfqLastTbeDocumentReviews에서 가져옴 + reviewStatus: rfqLastTbeDocumentReviews.reviewStatus, + reviewComments: rfqLastTbeDocumentReviews.reviewComments, + reviewedAt: rfqLastTbeDocumentReviews.reviewedAt, + requiresRevision: rfqLastTbeDocumentReviews.requiresRevision, + technicalCompliance: rfqLastTbeDocumentReviews.technicalCompliance, + qualityAcceptable: rfqLastTbeDocumentReviews.qualityAcceptable, + }) + .from(rfqLastTbeVendorDocuments) + .leftJoin( + rfqLastTbeDocumentReviews, + and( + eq(rfqLastTbeDocumentReviews.vendorAttachmentId, rfqLastTbeVendorDocuments.id), + eq(rfqLastTbeDocumentReviews.documentSource, "vendor") + ) + ) + .where(eq(rfqLastTbeVendorDocuments.tbeSessionId, tbeSessionId)) + .orderBy(rfqLastTbeVendorDocuments.submittedAt) + + // 문서 정보 매핑 (reviewStatus는 이미 한글로 저장되어 있음) + const mappedDocuments = documents.map(doc => ({ + ...doc, + reviewStatus: doc.reviewStatus || "미검토", // null인 경우 기본값 + reviewRequired: doc.requiresRevision || false, // UI 호환성을 위해 필드명 매핑 + })) + + return { + success: true, + documents: mappedDocuments, + } + } catch (error) { + console.error("Failed to fetch vendor documents:", error) + return { + success: false, + error: "벤더 문서를 불러오는데 실패했습니다", + documents: [], + } + } +} +// 리뷰 상태 매핑 함수 +function mapReviewStatus(status: string | null): string { + const statusMap: Record<string, string> = { + "pending": "미검토", + "reviewing": "검토중", + "approved": "승인", + "rejected": "반려", + } + + return status ? (statusMap[status] || status) : "미검토" }
\ No newline at end of file |
