"use server"; import db from "@/db/db"; import { eq, and, desc, isNull, isNotNull, ne, or } from "drizzle-orm"; import { gtcDocuments, projects, BasicContractView, agreementComments } 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[0]; // 첫 번째 단어가 프로젝트 코드 } 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 newComments = await db .select({ id: agreementComments.id }) .from(agreementComments) .where( and( eq(agreementComments.basicContractId, basicContractId), eq(agreementComments.isDeleted, false), isNotNull(agreementComments.comment), ne(agreementComments.comment, '') ) ) .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 조항 기능은 더 이상 사용하지 않으므로, agreement_comments만 체크 // agreement_comments에 코멘트가 없으면 hasComments = false 반환 return { gtcDocumentId, hasComments: false }; } 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> { const gtcData: Record = {}; // 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; }