summaryrefslogtreecommitdiff
path: root/lib/compliance/approval-handlers.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/compliance/approval-handlers.ts')
-rw-r--r--lib/compliance/approval-handlers.ts183
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,
}
}