"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 { 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 { 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 { // 중복 해소요청 방지 (진행 중인 결재가 있는지 확인) 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}`) } }