summaryrefslogtreecommitdiff
path: root/lib/bidding/delete-action.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/bidding/delete-action.ts')
-rw-r--r--lib/bidding/delete-action.ts166
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 : "입찰 삭제 처리 중 오류가 발생했습니다."
+ };
+ }
+}