diff --git a/src/app/(main)/m/admin/orders/page.tsx b/src/app/(main)/m/admin/orders/page.tsx index fa75b7c..81dfc83 100644 --- a/src/app/(main)/m/admin/orders/page.tsx +++ b/src/app/(main)/m/admin/orders/page.tsx @@ -22,7 +22,7 @@ interface DetailLine { SEQ: number; ITEM_NAME: string; UNIT: string; UNIT_PRICE: number; QTY: number; IS_TAX_FREE: string; SUPPLY_AMOUNT: number; VAT_AMOUNT: number; TOTAL_AMOUNT: number; STOCK_QTY: number; - KIND: "ITEM" | "DELIVERY" | "CHARTER"; + KIND: "ITEM" | "DELIVERY" | "CHARTER" | "REFUND"; EXTRA_LABEL?: string; REMARK?: string; } @@ -550,7 +550,7 @@ function StatementPreview({ else Swal.fire({ icon: "error", title: "삭제 실패", text: j.message }); }; - const upsertExtra = async (line: { objid?: string; kind: "DELIVERY" | "CHARTER"; label: string; unitPrice: number; qty: number }) => { + const upsertExtra = async (line: { objid?: string; kind: "DELIVERY" | "CHARTER" | "REFUND"; label: string; unitPrice: number; qty: number }) => { const res = await fetch("/api/m/orders/lines/save", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ orderObjid: order.OBJID, lines: [line] }), @@ -588,6 +588,10 @@ function StatementPreview({ : { unitPrice: 5000, qty: 1, label: "용차" }; upsertExtra({ kind, ...defaults }); }; + // 환불 라인 추가 — admin 전용. 수량 1 고정, 단가는 양수로 받아 내부 음수 저장. + const addRefund = () => { + upsertExtra({ kind: "REFUND", label: "환불", unitPrice: 0, qty: 1 }); + }; // ITEM 라인 추가 (피커 모달) const [pickerOpen, setPickerOpen] = useState(false); @@ -750,6 +754,14 @@ function StatementPreview({ > + 용차 추가 + )} @@ -772,10 +784,10 @@ function StatementPreview({
{items.map((it, idx) => { const displaySeq = idx + 1; - const isExtra = it.KIND === "DELIVERY" || it.KIND === "CHARTER"; + const isExtra = it.KIND === "DELIVERY" || it.KIND === "CHARTER" || it.KIND === "REFUND"; const lack = !isExtra && Number(it.STOCK_QTY) < Number(it.QTY); - const kindBadge = it.KIND === "DELIVERY" ? "택배" : it.KIND === "CHARTER" ? "용차" : null; - const kindBg = it.KIND === "DELIVERY" ? "bg-orange-50" : it.KIND === "CHARTER" ? "bg-sky-50" : ""; + const kindBadge = it.KIND === "DELIVERY" ? "택배" : it.KIND === "CHARTER" ? "용차" : it.KIND === "REFUND" ? "환불" : null; + const kindBg = it.KIND === "DELIVERY" ? "bg-orange-50" : it.KIND === "CHARTER" ? "bg-sky-50" : it.KIND === "REFUND" ? "bg-rose-50" : ""; if (isExtra && editable) { return ( @@ -1005,37 +1017,42 @@ function ExtraRow({ line, displaySeq, editable, onSave, onDelete, onSaveRemark } onDelete: () => void; onSaveRemark: (r: string) => void; }) { + const isRefund = line.KIND === "REFUND"; + const isDelivery = line.KIND === "DELIVERY"; + // 환불 라인은 DB에 음수로 저장됨 — UI 에선 양수로 표시/입력 후 저장 시 부호는 API 가 처리. + const initialUnit = isRefund ? Math.abs(Number(line.UNIT_PRICE) || 0) : (Number(line.UNIT_PRICE) || 0); const [label, setLabel] = useState(line.EXTRA_LABEL || line.ITEM_NAME); - const [unitPrice, setUnitPrice] = useState(Number(line.UNIT_PRICE) || 0); - const [qty, setQty] = useState(Number(line.QTY) || 1); + const [unitPrice, setUnitPrice] = useState(initialUnit); + const [qty, setQty] = useState(isRefund ? 1 : (Number(line.QTY) || 1)); - // 외부에서 line 이 갱신되면(예: + 택배 추가 → 서버 재조회) 인풋도 동기화. - // 같은 OBJID 라서 컴포넌트가 unmount 되지 않아 useState 초기값이 무시되는 문제 방지. useEffect(() => { setLabel(line.EXTRA_LABEL || line.ITEM_NAME); - setUnitPrice(Number(line.UNIT_PRICE) || 0); - setQty(Number(line.QTY) || 1); - }, [line.OBJID, line.EXTRA_LABEL, line.ITEM_NAME, line.UNIT_PRICE, line.QTY]); - const total = Math.round(unitPrice * qty); - const supply = Math.round(total / 1.1); - const vat = total - supply; - const isDelivery = line.KIND === "DELIVERY"; + setUnitPrice(isRefund ? Math.abs(Number(line.UNIT_PRICE) || 0) : (Number(line.UNIT_PRICE) || 0)); + setQty(isRefund ? 1 : (Number(line.QTY) || 1)); + }, [line.OBJID, line.EXTRA_LABEL, line.ITEM_NAME, line.UNIT_PRICE, line.QTY, isRefund]); + + // 표시용 금액 — 환불은 음수 + const displayTotal = isRefund ? -unitPrice * qty : Math.round(unitPrice * qty); + const displaySupply = isRefund ? displayTotal : Math.round(displayTotal / 1.1); + const displayVat = isRefund ? 0 : displayTotal - displaySupply; + + const bg = isRefund ? "bg-rose-50" : isDelivery ? "bg-orange-50" : "bg-sky-50"; + const badgeCls = isRefund ? "bg-rose-200 text-rose-800" : isDelivery ? "bg-orange-200 text-orange-800" : "bg-sky-200 text-sky-800"; + const badgeText = isRefund ? "환불" : isDelivery ? "택배" : "용차"; - // onBlur 시 자동 저장 (값이 바뀐 경우만). V 버튼 제거. const commit = () => { + const originalUnit = isRefund ? Math.abs(Number(line.UNIT_PRICE) || 0) : Number(line.UNIT_PRICE); const dirty = label !== (line.EXTRA_LABEL || line.ITEM_NAME) - || unitPrice !== Number(line.UNIT_PRICE) + || unitPrice !== originalUnit || qty !== Number(line.QTY); if (dirty && qty > 0 && unitPrice >= 0) onSave({ label, unitPrice, qty }); }; return ( -