fix(orders): 수기 출고 — 라벨 변경 + 현재고 표시 + 삭제 + admin-panel 정리
Deploy momo-erp / deploy (push) Successful in 2m13s
Deploy momo-erp / deploy (push) Successful in 2m13s
1) '수기 발주' → '수기 출고' 라벨 변경 (버튼/타이틀). 2) detail STOCK_QTY: 거래처 default_wh_objid 분기 제거 → 항상 STOCK 류 전체 합산 표시. customer=admin 또는 김포 시장 등 default 가 빈 창고일 때 현재고 0 으로 표시되던 버그 fix. 실제 출고 차감은 approve 시 default_wh_objid 또는 STOCK 첫 창고 기준 그대로. 3) /api/m/orders/delete (admin) — REQUESTED 상태 발주만 hard delete. 수기 출고로 잘못 생성한 빈 발주 정리용. einvoice/items/orders 일괄. 4) 출고관리 detail (REQUESTED) 에 '삭제' 버튼 추가 — 반려 옆. 5) admin-panel 의 '공급업체관리' 메뉴 제거 (m/admin/vendors 별도 메뉴 사용).
This commit is contained in:
@@ -804,13 +804,39 @@ function StatementPreview({
|
||||
<span className="text-[11px] text-slate-400">
|
||||
※ 다중 발주를 일괄 처리하려면 왼쪽 리스트에서 체크박스 선택 후 상단 [출고] 버튼을 사용하세요.
|
||||
</span>
|
||||
<button
|
||||
onClick={() => onCancel(order)}
|
||||
disabled={busy || shipping}
|
||||
className="px-3 h-9 rounded-lg border border-rose-200 text-rose-700 text-xs font-semibold hover:bg-rose-50 disabled:opacity-50 inline-flex items-center gap-1"
|
||||
>
|
||||
<X size={12} /> 반려
|
||||
</button>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={async () => {
|
||||
const ok = await Swal.fire({
|
||||
icon: "warning",
|
||||
title: `발주 ${order.ORDER_NO} 삭제`,
|
||||
text: "수기 출고 등 잘못 만든 출고건을 완전히 삭제합니다. (취소 이력 없음)",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#dc2626",
|
||||
confirmButtonText: "삭제",
|
||||
});
|
||||
if (!ok.isConfirmed) return;
|
||||
const res = await fetch("/api/m/orders/delete", {
|
||||
method: "POST", headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ objid: order.OBJID }),
|
||||
});
|
||||
const j = await res.json();
|
||||
if (j.success) { onReloadList(); }
|
||||
else Swal.fire({ icon: "error", title: "삭제 실패", text: j.message });
|
||||
}}
|
||||
disabled={busy || shipping}
|
||||
className="px-3 h-9 rounded-lg border border-rose-300 bg-white text-rose-700 text-xs font-bold hover:bg-rose-50 disabled:opacity-50 inline-flex items-center gap-1"
|
||||
>
|
||||
<X size={12} /> 삭제
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onCancel(order)}
|
||||
disabled={busy || shipping}
|
||||
className="px-3 h-9 rounded-lg border border-rose-200 text-rose-700 text-xs font-semibold hover:bg-rose-50 disabled:opacity-50 inline-flex items-center gap-1"
|
||||
>
|
||||
<X size={12} /> 반려
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{(order.STATUS === "APPROVED" || order.STATUS === "PAID") && (
|
||||
@@ -1186,9 +1212,9 @@ function ManualOrderButton({ onCreated }: { onCreated: (newObjid: string) => voi
|
||||
onClick={onClick}
|
||||
disabled={busy}
|
||||
className="h-9 px-3 rounded-lg bg-white border border-amber-300 text-amber-700 text-sm font-bold hover:bg-amber-50 disabled:opacity-50 inline-flex items-center gap-1.5"
|
||||
title="빈 발주를 생성하여 오른쪽에서 거래처/품목 채워가는 흐름"
|
||||
title="빈 출고건을 생성하여 오른쪽에서 거래처/품목 채워가는 흐름"
|
||||
>
|
||||
<PhoneCall size={14} /> 수기 발주
|
||||
<PhoneCall size={14} /> 수기 출고
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ const ADMIN_MENUS = [
|
||||
{
|
||||
label: "기준정보관리", icon: Database,
|
||||
items: [
|
||||
{ key: "supply" as AdminTab, label: "공급업체관리" },
|
||||
{ key: "template" as AdminTab, label: "템플릿 관리" },
|
||||
{ key: "exchange" as AdminTab, label: "환율관리" },
|
||||
],
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
// 발주 hard delete — admin 만, REQUESTED 상태만 (수기 출고 정리용).
|
||||
// 출고 처리(APPROVED) 이후는 재고/이력 정합성 때문에 cancel(CANCELED) 흐름 사용.
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { pool } 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 { objid } = await req.json() as { objid?: string };
|
||||
if (!objid) return NextResponse.json({ success: false, message: "objid 누락" }, { status: 400 });
|
||||
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query("BEGIN");
|
||||
const cur = await client.query(`SELECT status FROM momo_orders WHERE objid = $1 FOR UPDATE`, [objid]);
|
||||
if (cur.rowCount === 0) {
|
||||
await client.query("ROLLBACK");
|
||||
return NextResponse.json({ success: false, message: "발주를 찾을 수 없습니다." }, { status: 404 });
|
||||
}
|
||||
if (cur.rows[0].status !== "REQUESTED") {
|
||||
await client.query("ROLLBACK");
|
||||
return NextResponse.json({ success: false, message: "출고요청 상태만 삭제 가능합니다. 출고된 발주는 취소 처리하세요." }, { status: 400 });
|
||||
}
|
||||
// 연관 데이터 정리 (REQUESTED 단계라 stock_moves/payments 없음)
|
||||
await client.query(`DELETE FROM momo_einvoice_items WHERE einvoice_objid IN (SELECT objid FROM momo_einvoices WHERE order_objid = $1)`, [objid]);
|
||||
await client.query(`DELETE FROM momo_einvoices WHERE order_objid = $1`, [objid]);
|
||||
await client.query(`DELETE FROM momo_order_items WHERE order_objid = $1`, [objid]);
|
||||
await client.query(`DELETE FROM momo_orders WHERE objid = $1`, [objid]);
|
||||
await client.query("COMMIT");
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (err) {
|
||||
await client.query("ROLLBACK");
|
||||
console.error("[orders/delete]", err);
|
||||
return NextResponse.json({ success: false, message: err instanceof Error ? err.message : "오류" }, { status: 500 });
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
@@ -65,20 +65,14 @@ export async function POST(req: NextRequest) {
|
||||
I.unit AS "UNIT",
|
||||
I.image_url AS "IMAGE_URL",
|
||||
-- 현재고: 거래처에 default_wh_objid 가 있으면 그 창고 재고, 없으면 STOCK 류 전체 합
|
||||
-- (출고가 실제로 일어날 창고 기준 — approve 로직과 일관)
|
||||
-- 거래처 default 창고가 비어있을 수 있어, 항상 STOCK 류 전체 합산 표시.
|
||||
-- (실제 출고는 approve 시 default 창고 또는 STOCK 첫 창고에서 차감)
|
||||
COALESCE(
|
||||
CASE
|
||||
WHEN UD.default_wh_objid IS NOT NULL THEN
|
||||
(SELECT SUM(S.qty) FROM momo_stocks S
|
||||
WHERE S.item_objid = OI.item_objid
|
||||
AND S.wh_objid = UD.default_wh_objid::text)
|
||||
ELSE
|
||||
(SELECT SUM(S.qty) FROM momo_stocks S
|
||||
JOIN momo_warehouses W ON W.objid = S.wh_objid
|
||||
WHERE S.item_objid = OI.item_objid
|
||||
AND W.wh_type IN ('STOCK','HQ_STOCK','KIMPO_STOCK')
|
||||
AND COALESCE(W.is_del,'N') != 'Y')
|
||||
END,
|
||||
(SELECT SUM(S.qty) FROM momo_stocks S
|
||||
JOIN momo_warehouses W ON W.objid = S.wh_objid
|
||||
WHERE S.item_objid = OI.item_objid
|
||||
AND W.wh_type IN ('STOCK','HQ_STOCK','KIMPO_STOCK')
|
||||
AND COALESCE(W.is_del,'N') != 'Y'),
|
||||
0
|
||||
) AS "STOCK_QTY"
|
||||
FROM momo_order_items OI
|
||||
|
||||
Reference in New Issue
Block a user