1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
"use server"
import db from "@/db/db"
import { and, eq, inArray } from "drizzle-orm"
import { complianceResponses } from "@/db/schema/compliance"
import { basicContract, basicContractTemplates } from "@/db/schema/basicContractDocumnet"
import { vendors } from "@/db/schema/vendors"
import { getTriggeredRedFlagQuestions, type TriggeredRedFlagInfo } from "./red-flag-notifier"
import { revalidatePath } from "next/cache"
import { requestRedFlagResolutionWithApproval } from "./approval-actions"
import type { ApprovalResult } from "@/lib/approval/types"
export type ContractSummary = {
contractId: number
vendorName: string | null
vendorCode: string | null
templateName: string | null
createdAt: Date | null
triggeredFlags: TriggeredRedFlagInfo[]
}
/**
* RED FLAG 해소요청 - Approval Saga를 통해 상신
*
* @deprecated 이 함수는 호환성을 위해 유지됩니다.
* 새로운 코드는 `requestRedFlagResolutionWithApproval`을 사용하세요.
*/
export async function requestRedFlagResolution(contractIds: number[]): Promise<ApprovalResult> {
return await requestRedFlagResolutionWithApproval({ contractIds })
}
/**
* RED FLAG 해소 처리 (승인 후 실행)
*/
export async function resolveRedFlag(
contractId: number,
options: { approvalId?: string; revalidate?: boolean } = {}
): Promise<{ success: boolean; message: string }> {
const conditions = [eq(complianceResponses.basicContractId, contractId)]
if (options.approvalId) {
conditions.push(eq(complianceResponses.redFlagResolutionApprovalId, options.approvalId))
}
const [updated] = await db
.update(complianceResponses)
.set({
redFlagResolvedAt: new Date(),
updatedAt: new Date(),
})
.where(and(...conditions))
.returning({ id: complianceResponses.id })
if (!updated) {
throw new Error("해소요청 정보를 찾을 수 없습니다.")
}
if (options.revalidate !== false) {
await revalidatePath("/evcp/basic-contract")
await revalidatePath("/evcp/compliance")
}
return {
success: true,
message: "RED FLAG가 해제되었습니다.",
}
}
/**
* 계약서와 RED FLAG 정보를 함께 조회
*/
export async function fetchContractsWithFlags(contractIds: number[]): Promise<ContractSummary[]> {
const contracts = await db
.select({
contractId: basicContract.id,
vendorName: vendors.vendorName,
vendorCode: vendors.vendorCode,
templateName: basicContractTemplates.templateName,
createdAt: basicContract.createdAt,
})
.from(basicContract)
.leftJoin(vendors, eq(basicContract.vendorId, vendors.id))
.leftJoin(basicContractTemplates, eq(basicContract.templateId, basicContractTemplates.id))
.where(inArray(basicContract.id, contractIds))
const withFlags = await Promise.all(
contracts.map(async (contract) => {
const triggeredFlags = await getTriggeredRedFlagQuestions(contract.contractId)
return {
...contract,
triggeredFlags,
}
})
)
return withFlags.filter((contract) => contract.triggeredFlags.length > 0)
}
/**
* RED FLAG 해소요청 검증 (중복 요청 방지)
*/
export async function validateRedFlagResolutionRequest(
validContractIds: number[],
contractSummaries: ContractSummary[]
): Promise<void> {
// 중복 해소요청 방지 (진행 중인 결재가 있는지 확인)
const responses = await db
.select({
basicContractId: complianceResponses.basicContractId,
redFlagResolvedAt: complianceResponses.redFlagResolvedAt,
redFlagResolutionApprovalId: complianceResponses.redFlagResolutionApprovalId,
})
.from(complianceResponses)
.where(inArray(complianceResponses.basicContractId, validContractIds))
const missingResponses = validContractIds.filter(
(contractId) => !responses.some((response) => response.basicContractId === contractId)
)
if (missingResponses.length > 0) {
throw new Error("준법 응답 정보를 찾을 수 없는 계약서가 포함되어 있습니다.")
}
const blockedContracts = responses
.filter((response) => response.redFlagResolutionApprovalId && !response.redFlagResolvedAt)
.map((response) => response.basicContractId)
if (blockedContracts.length > 0) {
const blockedSummaries = contractSummaries
.filter((contract) => blockedContracts.includes(contract.contractId))
.map((contract) => contract.vendorName ?? `계약 ${contract.contractId}`)
const preview =
blockedSummaries.length > 2
? `${blockedSummaries.slice(0, 2).join(", ")} 외 ${blockedSummaries.length - 2}건`
: blockedSummaries.join(", ")
throw new Error(`이미 해소요청이 진행 중인 계약서가 있습니다: ${preview}`)
}
}
|