summaryrefslogtreecommitdiff
path: root/lib/tbe-last/service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tbe-last/service.ts')
-rw-r--r--lib/tbe-last/service.ts231
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