diff --git a/src/app/(main)/m/admin/proc-payments/page.tsx b/src/app/(main)/m/admin/proc-payments/page.tsx
index 8b3e26f..2644244 100644
--- a/src/app/(main)/m/admin/proc-payments/page.tsx
+++ b/src/app/(main)/m/admin/proc-payments/page.tsx
@@ -125,6 +125,74 @@ export default function ProcPaymentsPage() {
}
};
+ // 입금완료(PAID) 건 수정 — 입금액/입금일/방법/메모 수정 또는 입금 취소
+ const onEdit = async (p: Proc) => {
+ const today = new Date().toISOString().slice(0, 10);
+ const result = await Swal.fire({
+ title: "입금 정보 수정",
+ html: `
+
+
발주번호 ${p.PROC_NO}
+
공급업체 ${p.VENDOR_NAME ?? "-"}
+
발주금액 ₩${fmt(p.TOTAL_AMOUNT)}
+
+
{busy &&
}
@@ -187,9 +255,17 @@ export default function ProcPaymentsPage() {
{p.STATUS === "REQUESTED" ? (
+ ) : p.STATUS === "PAID" ? (
+
+
+ 입금일 {p.PAID_DATE} · ₩{fmt(p.PAID_AMOUNT)} {p.PAID_METHOD && `· ${p.PAID_METHOD}`}
+
+
+
) : (
-
- 입금일 {p.PAID_DATE} · ₩{fmt(p.PAID_AMOUNT)} {p.PAID_METHOD && `· ${p.PAID_METHOD}`}
+
+ 입금일 {p.PAID_DATE ?? "-"} · ₩{fmt(p.PAID_AMOUNT)} {p.PAID_METHOD && `· ${p.PAID_METHOD}`}
)}
@@ -233,6 +309,11 @@ export default function ProcPaymentsPage() {
className="h-7 px-2 rounded bg-emerald-700 text-white text-[11px] font-bold disabled:opacity-50">
입금 처리
+ ) : p.STATUS === "PAID" ? (
+
) :
완료}
diff --git a/src/app/api/m/admin/proc-payments/update/route.ts b/src/app/api/m/admin/proc-payments/update/route.ts
new file mode 100644
index 0000000..e4bad77
--- /dev/null
+++ b/src/app/api/m/admin/proc-payments/update/route.ts
@@ -0,0 +1,61 @@
+// 입금완료(PAID) 건 수정 — 입금액/입금일/방법/메모 수정 또는 입금 취소(상태 복원)
+import { NextRequest, NextResponse } from "next/server";
+import { execute, queryOne } from "@/lib/db";
+import { requireMomoAdmin } from "@/lib/momo-guard";
+
+export async function POST(req: NextRequest) {
+ const g = await requireMomoAdmin();
+ if (g instanceof NextResponse) return g;
+
+ const body = await req.json().catch(() => ({}));
+ const { objid, action, amount, method, memo, paidDate } = body as {
+ objid?: string;
+ action?: "edit" | "cancel";
+ amount?: number; method?: string; memo?: string; paidDate?: string;
+ };
+ if (!objid) return NextResponse.json({ success: false, message: "objid 필수" }, { status: 400 });
+
+ const proc = await queryOne<{ status: string; total_amount: number }>(
+ `SELECT status, total_amount FROM momo_procurements WHERE objid::text = $1`,
+ [objid]
+ );
+ if (!proc) return NextResponse.json({ success: false, message: "발주를 찾을 수 없습니다." }, { status: 404 });
+ if (proc.status !== "PAID") {
+ return NextResponse.json({ success: false, message: "입금완료 건만 수정할 수 있습니다." }, { status: 400 });
+ }
+
+ // 입금 취소 — 입고 진행 상태로 복원하고 입금 정보 제거
+ if (action === "cancel") {
+ const st = await queryOne<{ pending: string; started: string; cnt: string }>(
+ `SELECT COUNT(*) FILTER (WHERE COALESCE(received_qty,0) < qty) AS pending,
+ COUNT(*) FILTER (WHERE COALESCE(received_qty,0) > 0) AS started,
+ COUNT(*) AS cnt
+ FROM momo_procurement_items WHERE proc_objid::text = $1`,
+ [objid]
+ );
+ const pending = Number(st?.pending ?? 0);
+ const started = Number(st?.started ?? 0);
+ const cnt = Number(st?.cnt ?? 0);
+ const restored = cnt > 0 && pending === 0 ? "RECEIVED" : started > 0 ? "PARTIAL" : "REQUESTED";
+ await execute(
+ `UPDATE momo_procurements
+ SET status = $1, paid_date = NULL, paid_amount = NULL, paid_method = NULL, paid_memo = NULL
+ WHERE objid::text = $2`,
+ [restored, objid]
+ );
+ return NextResponse.json({ success: true, status: restored });
+ }
+
+ // 입금 정보 수정 (상태는 PAID 유지)
+ const paidAmount = Number(amount) > 0 ? Number(amount) : Number(proc.total_amount);
+ await execute(
+ `UPDATE momo_procurements
+ SET paid_amount = $1,
+ paid_method = $2,
+ paid_memo = $3,
+ paid_date = COALESCE($4::timestamp, paid_date)
+ WHERE objid::text = $5`,
+ [paidAmount, method || null, memo || null, paidDate || null, objid]
+ );
+ return NextResponse.json({ success: true });
+}