summaryrefslogtreecommitdiff
path: root/lib/basic-contract/sslvw-service.ts
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-11-19 06:15:43 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-11-19 06:15:43 +0000
commitc92bd1b8caa6ddabe6acee42018262febd5d91fb (patch)
tree833a62c9577894b0f77d3677d4d0274e1cb99385 /lib/basic-contract/sslvw-service.ts
parent9bf5b15734cdf87a02c68b2d2a25046a0678a037 (diff)
(임수민) 기본계약 코멘트, 법무검토 수정
Diffstat (limited to 'lib/basic-contract/sslvw-service.ts')
-rw-r--r--lib/basic-contract/sslvw-service.ts191
1 files changed, 189 insertions, 2 deletions
diff --git a/lib/basic-contract/sslvw-service.ts b/lib/basic-contract/sslvw-service.ts
index 9650d43a..5a35fb99 100644
--- a/lib/basic-contract/sslvw-service.ts
+++ b/lib/basic-contract/sslvw-service.ts
@@ -1,6 +1,11 @@
"use server"
import { oracleKnex } from '@/lib/oracle-db/db'
+import db from '@/db/db'
+import { basicContract, agreementComments } from '@/db/schema'
+import { eq, inArray, and } from 'drizzle-orm'
+import { revalidateTag } from 'next/cache'
+import { sendEmail } from '@/lib/mail/sendEmail'
// SSLVW_PUR_INQ_REQ 테이블 데이터 타입 (실제 테이블 구조에 맞게 조정 필요)
export interface SSLVWPurInqReq {
@@ -39,10 +44,28 @@ export async function getSSLVWPurInqReqData(): Promise<{
console.log('📋 [getSSLVWPurInqReqData] SSLVW_PUR_INQ_REQ 테이블 조회 시작...')
const result = await oracleKnex.raw(`
- SELECT *
+ SELECT
+ ID,
+ PRGS_STAT_DSC,
+ REQ_DT,
+ REQ_NO,
+ REQ_TIT,
+ REQ_CONT,
+ VEND_CD,
+ VEND_NM,
+ CNTR_CTGR_DSC,
+ CNTR_AMT,
+ CNTR_STRT_DT,
+ CNTR_END_DT,
+ RPLY_DT,
+ RPLY_CONT,
+ RPLY_USER_NM,
+ RPLY_USER_ID,
+ CREATED_AT,
+ UPDATED_AT
FROM SSLVW_PUR_INQ_REQ
WHERE ROWNUM < 100
- ORDER BY 1
+ ORDER BY REQ_DT DESC
`)
// Oracle raw query의 결과는 rows 배열에 들어있음
@@ -80,3 +103,167 @@ export async function getSSLVWPurInqReqData(): Promise<{
}
}
}
+
+/**
+ * 법무검토 요청
+ * @param contractIds 계약서 ID 배열
+ * @returns 성공 여부 및 메시지
+ */
+export async function requestLegalReview(contractIds: number[]): Promise<{
+ success: boolean
+ message: string
+ requested: number
+ skipped: number
+ errors: string[]
+}> {
+ console.log(`📋 [requestLegalReview] 법무검토 요청 시작: ${contractIds.length}건`)
+
+ if (!contractIds || contractIds.length === 0) {
+ return {
+ success: false,
+ message: '선택된 계약서가 없습니다.',
+ requested: 0,
+ skipped: 0,
+ errors: []
+ }
+ }
+
+ try {
+ // 계약서 정보 조회
+ const contracts = await db
+ .select()
+ .from(basicContract)
+ .where(inArray(basicContract.id, contractIds))
+
+ if (!contracts || contracts.length === 0) {
+ return {
+ success: false,
+ message: '계약서를 찾을 수 없습니다.',
+ requested: 0,
+ skipped: 0,
+ errors: []
+ }
+ }
+
+ let requestedCount = 0
+ let skippedCount = 0
+ const errors: string[] = []
+
+ for (const contract of contracts) {
+ try {
+ // 유효성 검사
+ if (contract.legalReviewRequestedAt) {
+ console.log(`⚠️ [requestLegalReview] 계약서 ${contract.id}: 이미 법무검토 요청됨`)
+ skippedCount++
+ errors.push(`${contract.id}: 이미 법무검토 요청됨`)
+ continue
+ }
+
+ // 협의 완료 여부 확인
+ // 1. 협의 완료됨 (negotiationCompletedAt 있음) → 가능
+ // 2. 협의 없음 (코멘트 없음) → 가능
+ // 3. 협의 중 (negotiationCompletedAt 없고 코멘트 있음) → 불가
+
+ if (!contract.negotiationCompletedAt) {
+ // 협의 완료되지 않은 경우, 코멘트 존재 여부 확인
+ // 삭제되지 않은 코멘트가 있으면 협의 중이므로 불가
+ const comments = await db
+ .select()
+ .from(agreementComments)
+ .where(
+ and(
+ eq(agreementComments.basicContractId, contract.id),
+ eq(agreementComments.isDeleted, false)
+ )
+ )
+ .limit(1);
+
+ // 삭제되지 않은 코멘트가 있으면 협의 중이므로 불가
+ if (comments.length > 0) {
+ console.log(`⚠️ [requestLegalReview] 계약서 ${contract.id}: 협의 진행 중`)
+ skippedCount++
+ errors.push(`${contract.id}: 협의가 진행 중입니다`)
+ continue
+ }
+
+ // 코멘트가 없으면 협의 없음으로 간주하고 가능
+ console.log(`ℹ️ [requestLegalReview] 계약서 ${contract.id}: 협의 없음, 법무검토 요청 가능`)
+ }
+
+ // 법무검토 요청 상태로 업데이트
+ await db
+ .update(basicContract)
+ .set({
+ legalReviewRequestedAt: new Date(),
+ updatedAt: new Date(),
+ } as any)
+ .where(eq(basicContract.id, contract.id))
+
+ requestedCount++
+ console.log(`✅ [requestLegalReview] 계약서 ${contract.id}: 법무검토 요청 완료`)
+
+ // 법무팀에 이메일 알림 발송 (선택사항)
+ try {
+ // TODO: 법무팀 이메일 주소를 설정에서 가져오기
+ const legalTeamEmail = process.env.LEGAL_TEAM_EMAIL || 'legal@example.com'
+
+ await sendEmail({
+ to: legalTeamEmail,
+ subject: `[eVCP] 기본계약서 법무검토 요청 - ${contract.id}`,
+ template: 'legal-review-request',
+ context: {
+ language: 'ko',
+ contractId: contract.id,
+ vendorName: contract.vendorId || '업체명 없음',
+ contractUrl: `${process.env.NEXT_PUBLIC_APP_URL}/evcp/basic-contract/${contract.id}`,
+ systemUrl: process.env.NEXT_PUBLIC_APP_URL || 'https://evcp.com',
+ currentYear: new Date().getFullYear(),
+ },
+ })
+ } catch (emailError) {
+ console.error(`⚠️ [requestLegalReview] 이메일 발송 실패 (계약서 ${contract.id}):`, emailError)
+ // 이메일 실패는 법무검토 요청 성공에 영향을 주지 않음
+ }
+
+ } catch (error) {
+ console.error(`❌ [requestLegalReview] 계약서 ${contract.id} 처리 실패:`, error)
+ errors.push(`${contract.id}: 처리 중 오류 발생`)
+ skippedCount++
+ }
+ }
+
+ // 캐시 무효화
+ revalidateTag('basic-contracts')
+
+ const totalProcessed = requestedCount + skippedCount
+ let message = ''
+
+ if (requestedCount === contracts.length) {
+ message = `${requestedCount}건의 계약서에 대한 법무검토가 요청되었습니다.`
+ } else if (requestedCount > 0) {
+ message = `${requestedCount}건 요청 완료, ${skippedCount}건 건너뜀`
+ } else {
+ message = `모든 계약서를 건너뛰었습니다. (${skippedCount}건)`
+ }
+
+ console.log(`✅ [requestLegalReview] 법무검토 요청 완료: ${message}`)
+
+ return {
+ success: requestedCount > 0,
+ message,
+ requested: requestedCount,
+ skipped: skippedCount,
+ errors
+ }
+
+ } catch (error) {
+ console.error('❌ [requestLegalReview] 법무검토 요청 실패:', error)
+ return {
+ success: false,
+ message: '법무검토 요청 중 오류가 발생했습니다.',
+ requested: 0,
+ skipped: contractIds.length,
+ errors: [error instanceof Error ? error.message : '알 수 없는 오류']
+ }
+ }
+}