diff options
Diffstat (limited to 'lib/bidding/delete-action.ts')
| -rw-r--r-- | lib/bidding/delete-action.ts | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/lib/bidding/delete-action.ts b/lib/bidding/delete-action.ts new file mode 100644 index 00000000..32dc32ad --- /dev/null +++ b/lib/bidding/delete-action.ts @@ -0,0 +1,166 @@ +'use server' + +import { revalidatePath } from "next/cache"; +import db from "@/db/db"; +import { biddings, biddingCompanies } from "@/db/schema"; +import { eq, and, inArray } from "drizzle-orm"; +import { getServerSession } from "next-auth/next"; +import { authOptions } from "@/app/api/auth/[...nextauth]/route"; +import { cancelRFQ } from "@/lib/soap/ecc/send/delete-rfq"; + +/** + * 입찰(Bidding) 삭제 서버 액션 + * 조건: + * 1. SAP 연동 건(ANFNR 존재)이어야 함 (필수) + * 2. 입찰 상태가 '입찰생성(bidding_generated)'이어야 함 + * + * 동작: + * 1. SAP 취소 I/F 전송 (필수) + * 2. 성공 시 입찰 상태를 '삭제(deleted)'로 변경 + * 3. 연관된 데이터(업체 응답 등) 상태 변경 + */ +export async function deleteBidding(biddingIds: number[], deleteReason?: string): Promise<{ + success: boolean; + message: string; + results?: Array<{ biddingId: number; success: boolean; error?: string }>; +}> { + try { + const session = await getServerSession(authOptions); + + if (!session?.user?.id) { + return { + success: false, + message: "인증이 필요합니다." + }; + } + + const userName = session.user.name || session.user.email || "Unknown"; + + // 1. Bidding 정보 조회 + const targets = await db.select({ + id: biddings.id, + biddingNumber: biddings.biddingNumber, + ANFNR: biddings.ANFNR, + status: biddings.status, + title: biddings.title + }) + .from(biddings) + .where(inArray(biddings.id, biddingIds)); + + // 2. 유효성 검증 + // 조건 1: ANFNR이 있어야 함 + // 조건 2: 상태가 'bidding_generated'(입찰생성) 이어야 함 + const validTargets = targets.filter(b => + b.status === 'bidding_generated' && + b.ANFNR && + b.ANFNR.trim() !== "" + ); + + if (validTargets.length === 0) { + // 실패 사유 분석 + const noAnfnr = targets.filter(b => !b.ANFNR || b.ANFNR.trim() === ""); + const wrongStatus = targets.filter(b => b.status !== 'bidding_generated'); + + let errorMsg = "삭제 가능한 입찰이 없습니다."; + if (noAnfnr.length > 0) errorMsg += " (SAP 연동 건(ANFNR)이 아님)"; + if (wrongStatus.length > 0) errorMsg += " ('입찰생성' 상태가 아님)"; + + return { + success: false, + message: errorMsg + }; + } + + const results: Array<{ biddingId: number; success: boolean; error?: string }> = []; + + // 3. 각 Bidding에 대해 처리 + for (const bidding of validTargets) { + try { + // 3-1. SAP 취소 요청 전송 (ANFNR 필수이므로 바로 호출) + const cancelResult = await cancelRFQ(bidding.ANFNR!); + + if (!cancelResult.success) { + results.push({ + biddingId: bidding.id, + success: false, + error: `SAP 전송 실패: ${cancelResult.message}` + }); + continue; + } + + // 3-2. DB 상태 변경 및 연관 데이터 정리 + await db.transaction(async (tx) => { + // 입찰 상태 변경 + await tx + .update(biddings) + .set({ + status: 'deleted', // 삭제 상태 처리 + remarks: deleteReason ? `[삭제사유] ${deleteReason}` : `[삭제됨] 사용자에 의한 삭제`, + updatedBy: userName, + updatedAt: new Date() + }) + .where(eq(biddings.id, bidding.id)); + + }); + + results.push({ + biddingId: bidding.id, + success: true + }); + + } catch (error) { + console.error(`입찰 삭제 실패 (ID: ${bidding.id}, ANFNR: ${bidding.ANFNR}):`, error); + results.push({ + biddingId: bidding.id, + success: false, + error: error instanceof Error ? error.message : "알 수 없는 오류" + }); + } + } + + const successCount = results.filter(r => r.success).length; + const failCount = results.length - successCount; + + // 캐시 갱신 + revalidatePath("/evcp/bid"); + + // 결과 메시지 조합 + let message = ""; + const invalidStatusCount = targets.filter(b => b.status !== 'bidding_generated').length; + const noAnfnrCount = targets.filter(b => !b.ANFNR || b.ANFNR.trim() === "").length; + + if (invalidStatusCount > 0) { + message += `'입찰생성' 상태가 아닌 건(${invalidStatusCount}건) 제외. `; + } + if (noAnfnrCount > 0) { + message += `SAP 미연동 건(${noAnfnrCount}건) 제외. `; + } + + if (failCount === 0 && successCount > 0) { + return { + success: true, + message: message + `입찰 삭제가 완료되었습니다. (${successCount}건)`, + results + }; + } else if (successCount > 0) { + return { + success: false, // 부분 성공 + message: message + `일부 삭제 실패 (성공: ${successCount}건, 실패: ${failCount}건)`, + results + }; + } else { + return { + success: false, + message: message + (failCount > 0 ? "삭제 처리에 실패했습니다." : "처리할 대상이 없습니다."), + results + }; + } + + } catch (error) { + console.error("입찰 삭제 처리 중 오류:", error); + return { + success: false, + message: error instanceof Error ? error.message : "입찰 삭제 처리 중 오류가 발생했습니다." + }; + } +} |
