diff options
Diffstat (limited to 'lib/compliance/approval-handlers.ts')
| -rw-r--r-- | lib/compliance/approval-handlers.ts | 183 |
1 files changed, 144 insertions, 39 deletions
diff --git a/lib/compliance/approval-handlers.ts b/lib/compliance/approval-handlers.ts index 05f92a28..11f95a3c 100644 --- a/lib/compliance/approval-handlers.ts +++ b/lib/compliance/approval-handlers.ts @@ -1,64 +1,169 @@ "use server" -import db from "@/db/db" -import { and, inArray, isNull } from "drizzle-orm" -import { complianceResponses } from "@/db/schema/compliance" -import { resolveRedFlag } from "./red-flag-resolution" +import { resolveRedFlag, type ContractSummary } from "./red-flag-resolution" import { revalidatePath } from "next/cache" - -interface RedFlagResolutionPayload { - contractIds: number[] -} +import { debugLog, debugError, debugSuccess } from "@/lib/debug-utils" +import { htmlListConverter, htmlTableConverter } from "@/lib/approval/template-utils" +import db from "@/db/db" +import { eq } from "drizzle-orm" +import { redFlagManagers } from "@/db/schema/compliance" +import { users } from "@/db/schema" /** - * 결재 승인 후 RED FLAG 해제를 처리하는 핸들러 + * RED FLAG 해소 결재 승인 핸들러 * - * approval-workflow에서 자동으로 호출됩니다. + * 결재 승인 후 자동으로 호출되어 RED FLAG를 해제합니다. + * + * @param payload - 결재 상신 시 저장한 actionPayload */ -export async function resolveRedFlagAfterApproval(payload: RedFlagResolutionPayload) { - if (!payload?.contractIds || payload.contractIds.length === 0) { - return { - success: false, - message: "처리할 계약서가 없습니다.", +export async function resolveRedFlagAfterApproval(payload: { + contractIds: number[] + requestedBy: number + requestedAt: string +}) { + debugLog("[RedFlagResolutionHandler] RED FLAG 해소 결재 승인 핸들러 시작", payload) + + try { + if (!payload?.contractIds || payload.contractIds.length === 0) { + debugError("[RedFlagResolutionHandler] 계약서 ID가 없습니다", payload) + return { + success: false, + message: "처리할 계약서가 없습니다.", + } } - } - const uniqueContractIds = Array.from(new Set(payload.contractIds)) + const uniqueContractIds = Array.from(new Set(payload.contractIds)) + debugLog("[RedFlagResolutionHandler] 처리할 계약서 수", { count: uniqueContractIds.length }) - // 이미 해제된 계약을 제외한 대상을 조회 - const targets = await db - .select({ - basicContractId: complianceResponses.basicContractId, - approvalId: complianceResponses.redFlagResolutionApprovalId, - }) - .from(complianceResponses) - .where( - and( - inArray(complianceResponses.basicContractId, uniqueContractIds), - isNull(complianceResponses.redFlagResolvedAt) - ) + // 각 계약서에 대해 RED FLAG 해소 처리 + // approvalId는 resolveRedFlag 내부에서 조회하므로 여기서는 전달하지 않음 + const results = await Promise.allSettled( + uniqueContractIds.map(async (contractId) => { + const result = await resolveRedFlag(contractId, { + revalidate: false, + }) + return { contractId, result } + }) ) - if (targets.length === 0) { + const successful = results.filter((r) => r.status === "fulfilled").length + const failed = results.filter((r) => r.status === "rejected").length + + debugLog("[RedFlagResolutionHandler] 처리 결과", { + total: uniqueContractIds.length, + successful, + failed, + }) + + if (failed > 0) { + const errors = results + .filter((r) => r.status === "rejected") + .map((r) => (r as PromiseRejectedResult).reason) + debugError("[RedFlagResolutionHandler] 일부 계약서 처리 실패", errors) + } + + await revalidatePath("/evcp/basic-contract") + await revalidatePath("/evcp/compliance") + + debugSuccess("[RedFlagResolutionHandler] RED FLAG 해소 완료", { + successful, + failed, + }) + return { success: true, - message: "해제 대상이 없습니다.", + message: `${successful}개 계약서의 RED FLAG가 해제되었습니다.`, + updated: successful, } + } catch (error) { + debugError("[RedFlagResolutionHandler] RED FLAG 해소 처리 중 오류 발생", error) + throw error } +} - for (const target of targets) { - await resolveRedFlag(target.basicContractId, { - approvalId: target.approvalId ?? undefined, - revalidate: false, +/** + * 구매기획 담당자 EP ID 조회 + */ +export async function getPurchasingManagerEpId(): Promise<string | null> { + const [manager] = await db + .select({ + purchasingManagerId: redFlagManagers.purchasingManagerId, }) + .from(redFlagManagers) + .orderBy(redFlagManagers.createdAt) + .limit(1) + + if (!manager?.purchasingManagerId) { + return null } - await revalidatePath("/evcp/basic-contract") - await revalidatePath("/evcp/compliance") + const [user] = await db + .select({ + epId: users.epId, + }) + .from(users) + .where(eq(users.id, manager.purchasingManagerId)) + .limit(1) + + return user?.epId ?? null +} + +/** + * RED FLAG 해소요청 데이터를 결재 템플릿 변수로 매핑 + * + * @param contracts - RED FLAG가 있는 계약서 목록 + * @param meta - 요청자 정보 + * @returns 템플릿 변수 객체 (Record<string, string>) + */ +export async function mapRedFlagResolutionToTemplateVariables( + contracts: ContractSummary[], + meta: { requesterName: string; requestedAt: Date } +): Promise<Record<string, string>> { + const summaryRows = contracts.map((contract) => ({ + contractId: contract.contractId, + vendorName: contract.vendorName ?? "-", + templateName: contract.templateName ?? "-", + redFlagCount: contract.triggeredFlags.length, + })) + + const summaryTable = await htmlTableConverter(summaryRows, [ + { key: "contractId", label: "계약 ID" }, + { key: "vendorName", label: "업체명" }, + { key: "templateName", label: "템플릿" }, + { key: "redFlagCount", label: "RED FLAG 수" }, + ]) + + const detailSections = await Promise.all( + contracts.map(async (contract) => { + const questionList = contract.triggeredFlags.map((flag, index) => { + const prefix = flag.questionNumber || `${index + 1}` + return `${prefix}. ${flag.questionText}` + }) + + const listHtml = await htmlListConverter(questionList) + return ` + <div style="margin-bottom: 24px;"> + <div style="font-weight:600;margin-bottom:8px;"> + 계약 ID: ${contract.contractId} / ${contract.vendorName ?? "-"} + </div> + <div>${listHtml}</div> + </div> + ` + }) + ) + + const detailHtml = detailSections.join("") + const formattedDate = new Intl.DateTimeFormat("ko-KR", { + dateStyle: "medium", + timeStyle: "short", + }).format(meta.requestedAt) return { - success: true, - updated: targets.length, + 요청자이름: meta.requesterName, + 요청일시: formattedDate, + 요청사유: "컴플라이언스 Red Flag 해소를 위해 구매기획 합의를 요청드립니다.", + RedFlag요약테이블: summaryTable, + RedFlag상세내역: detailHtml, } } |
