summaryrefslogtreecommitdiff
path: root/lib/basic-contract/actions/check-gtc-comments.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/basic-contract/actions/check-gtc-comments.ts')
-rw-r--r--lib/basic-contract/actions/check-gtc-comments.ts239
1 files changed, 239 insertions, 0 deletions
diff --git a/lib/basic-contract/actions/check-gtc-comments.ts b/lib/basic-contract/actions/check-gtc-comments.ts
new file mode 100644
index 00000000..1ce998bc
--- /dev/null
+++ b/lib/basic-contract/actions/check-gtc-comments.ts
@@ -0,0 +1,239 @@
+"use server";
+
+import db from "@/db/db";
+import { eq, and, desc, isNull, isNotNull, ne, or } from "drizzle-orm";
+import {
+ gtcDocuments,
+ gtcVendorDocuments,
+ gtcVendorClauses,
+ gtcNegotiationHistory,
+ projects,
+ BasicContractView
+} from "@/db/schema";
+
+/**
+ * 템플릿 이름에서 프로젝트 코드 추출
+ */
+function extractProjectCodeFromTemplateName(templateName: string): string | null {
+ // "SN2319 GTC" -> "SN2319"
+ // "General GTC" -> null
+ const words = templateName.trim().split(/\s+/);
+ if (words.length === 0) return null;
+
+ const firstWord = words[0];
+ if (firstWord.toLowerCase() === 'general' || firstWord.toLowerCase() === 'gtc') {
+ return null;
+ }
+
+ // 프로젝트 코드는 보통 첫 번째 단어
+ if (words.length > 1 && words[words.length - 1].toLowerCase() === 'gtc') {
+ return words[words.length - 1];
+ }
+
+ return null;
+}
+
+/**
+ * 단일 contract에 대한 GTC 정보 확인
+ */
+async function checkGTCCommentsForContract(
+ templateName: string,
+ vendorId: number,
+ basicContractId?: number
+): Promise<{ gtcDocumentId: number | null; hasComments: boolean }> {
+ try {
+ const projectCode = extractProjectCodeFromTemplateName(templateName);
+ let gtcDocumentId: number | null = null;
+
+ console.log(projectCode,"projectCode")
+
+ // 1. GTC Document ID 찾기
+ if (projectCode && projectCode.trim() !== '') {
+ // Project GTC인 경우
+ const project = await db
+ .select({ id: projects.id })
+ .from(projects)
+ .where(eq(projects.code, projectCode.trim()))
+ .limit(1)
+
+ if (project.length > 0) {
+ const projectGtcDoc = await db
+ .select({ id: gtcDocuments.id })
+ .from(gtcDocuments)
+ .where(
+ and(
+ eq(gtcDocuments.projectId, project[0].id),
+ eq(gtcDocuments.isActive, true)
+ )
+ )
+ .orderBy(desc(gtcDocuments.revision))
+ .limit(1)
+
+ if (projectGtcDoc.length > 0) {
+ gtcDocumentId = projectGtcDoc[0].id
+ }
+ }
+ } else {
+ // Standard GTC인 경우 (general 포함하거나 project code가 없는 경우)
+ console.log(`🔍 [checkGTCCommentsForContract] Standard GTC 조회 중...`);
+ const standardGtcDoc = await db
+ .select({ id: gtcDocuments.id })
+ .from(gtcDocuments)
+ .where(
+ and(
+ eq(gtcDocuments.type, "standard"),
+ eq(gtcDocuments.isActive, true),
+ isNull(gtcDocuments.projectId)
+ )
+ )
+ .orderBy(desc(gtcDocuments.revision))
+ .limit(1)
+
+ console.log(`📊 [checkGTCCommentsForContract] Standard GTC 조회 결과: ${standardGtcDoc.length}개`);
+
+ if (standardGtcDoc.length > 0) {
+ gtcDocumentId = standardGtcDoc[0].id
+ console.log(`✅ [checkGTCCommentsForContract] Standard GTC 찾음: ${gtcDocumentId}`);
+ } else {
+ console.log(`❌ [checkGTCCommentsForContract] Standard GTC 없음 - gtc_documents 테이블에 standard 타입의 활성 문서가 없습니다`);
+ }
+ }
+
+ console.log(`🎯 [checkGTCCommentsForContract] 최종 gtcDocumentId: ${gtcDocumentId}`)
+
+ // 🔥 중요: basicContractId가 있으면 먼저 agreement_comments 테이블 확인
+ // gtcDocumentId가 없어도 새로운 코멘트 시스템은 작동해야 함
+ if (basicContractId) {
+ console.log(`🔍 [checkGTCCommentsForContract] basicContractId: ${basicContractId} 로 코멘트 조회`);
+ const { agreementComments } = await import("@/db/schema");
+ const newComments = await db
+ .select({ id: agreementComments.id })
+ .from(agreementComments)
+ .where(
+ and(
+ eq(agreementComments.basicContractId, basicContractId),
+ eq(agreementComments.isDeleted, false)
+ )
+ )
+ .limit(1);
+
+ console.log(`📊 [checkGTCCommentsForContract] basicContractId ${basicContractId}: 코멘트 ${newComments.length}개 발견`);
+
+ if (newComments.length > 0) {
+ console.log(`✅ [checkGTCCommentsForContract] basicContractId ${basicContractId}: hasComments = true 반환`);
+ return {
+ gtcDocumentId, // null일 수 있음
+ hasComments: true
+ };
+ }
+
+ console.log(`⚠️ [checkGTCCommentsForContract] basicContractId ${basicContractId}: agreementComments 없음`);
+ }
+
+ // GTC Document를 찾지 못한 경우 (기존 방식도 체크할 수 없음)
+ if (!gtcDocumentId) {
+ console.log(`⚠️ [checkGTCCommentsForContract] gtcDocumentId null - 기존 방식 체크 불가`);
+ return { gtcDocumentId: null, hasComments: false };
+ }
+
+ // 2-2. 기존 방식: gtcDocumentId로 해당 벤더의 vendor documents 찾기
+ const vendorDocuments = await db
+ .select({ id: gtcVendorDocuments.id })
+ .from(gtcVendorDocuments)
+ .where(
+ and(
+ eq(gtcVendorDocuments.baseDocumentId, gtcDocumentId),
+ eq(gtcVendorDocuments.vendorId, vendorId),
+ eq(gtcVendorDocuments.isActive, true)
+ )
+ )
+ .limit(1)
+
+ if (vendorDocuments.length === 0) {
+ return { gtcDocumentId, hasComments: false };
+ }
+
+ // vendor document에 연결된 clauses에서 negotiation history 확인
+ const commentsExist = await db
+ .select({ count: gtcNegotiationHistory.id })
+ .from(gtcNegotiationHistory)
+ .innerJoin(
+ gtcVendorClauses,
+ eq(gtcNegotiationHistory.vendorClauseId, gtcVendorClauses.id)
+ )
+ .where(
+ and(
+ eq(gtcVendorClauses.vendorDocumentId, vendorDocuments[0].id),
+ eq(gtcVendorClauses.isActive, true),
+ isNotNull(gtcNegotiationHistory.comment),
+ ne(gtcNegotiationHistory.comment, '')
+ )
+ )
+ .limit(1)
+
+ return {
+ gtcDocumentId,
+ hasComments: commentsExist.length > 0
+ };
+
+ } catch (error) {
+ console.error('Error checking GTC comments for contract:', error);
+ return { gtcDocumentId: null, hasComments: false };
+ }
+}
+
+/**
+ * 전체 contract 리스트에 대해 GTC document ID와 comment 정보 수집
+ */
+export async function checkGTCCommentsForContracts(
+ contracts: BasicContractView[]
+): Promise<Record<number, { gtcDocumentId: number | null; hasComments: boolean }>> {
+ const gtcData: Record<number, { gtcDocumentId: number | null; hasComments: boolean }> = {};
+
+ // GTC가 포함된 contract만 필터링
+ const gtcContracts = contracts.filter(contract =>
+ contract.templateName?.includes('GTC')
+ );
+
+ if (gtcContracts.length === 0) {
+ return gtcData;
+ }
+
+ // Promise.all을 사용해서 병렬 처리
+ const checkPromises = gtcContracts.map(async (contract) => {
+ try {
+ console.log(`🔄 [checkGTCCommentsForContracts] 계약서 ${contract.id} 체크 시작 - 템플릿: ${contract.templateName}, 벤더: ${contract.vendorName} (${contract.vendorId})`);
+ const result = await checkGTCCommentsForContract(
+ contract.templateName!,
+ contract.vendorId!,
+ contract.id // basicContractId 전달
+ );
+
+ console.log(`📌 [checkGTCCommentsForContracts] 계약서 ${contract.id} 결과 - hasComments: ${result.hasComments}, gtcDocumentId: ${result.gtcDocumentId}`);
+
+ return {
+ contractId: contract.id,
+ gtcDocumentId: result.gtcDocumentId,
+ hasComments: result.hasComments
+ };
+ } catch (error) {
+ console.error(`Error checking GTC for contract ${contract.id}:`, error);
+ return {
+ contractId: contract.id,
+ gtcDocumentId: null,
+ hasComments: false
+ };
+ }
+ });
+
+ const results = await Promise.all(checkPromises);
+
+ // 결과를 Record 형태로 변환
+ results.forEach(({ contractId, gtcDocumentId, hasComments }) => {
+ gtcData[contractId] = { gtcDocumentId, hasComments };
+ });
+
+ return gtcData;
+}
+
+