fix(pos): keep the receipt printable after paying an order
CI/CD / CI · API (dotnet build + test) (push) Successful in 56s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 31s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 39s
CI/CD / CI · Website (tsc) (push) Successful in 47s
CI/CD / CI · Koja (tsc) (push) Successful in 1m1s
CI/CD / Deploy · all services (push) Successful in 3m0s
CI/CD / CI · API (dotnet build + test) (push) Successful in 56s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 31s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 39s
CI/CD / CI · Website (tsc) (push) Successful in 47s
CI/CD / CI · Koja (tsc) (push) Successful in 1m1s
CI/CD / Deploy · all services (push) Successful in 3m0s
Bug: confirming payment ran backToBoard() → clearSession(), wiping the cart's activeOrderId, so the order vanished from the POS and the "print receipt" button (which keys off activeOrderId) went dead — the only escape was a 4s toast. Fix: capture the just-paid order id in local state (survives the session clear) and show a persistent payment-success sheet with a "چاپ فاکتور" button so the cashier can print/reprint the customer receipt, then "سفارش جدید" to continue. Shared printReceiptById() backs both the in-order button and the success sheet. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -121,6 +121,8 @@ export function Pos2Screen() {
|
|||||||
const [busy, setBusy] = useState(false);
|
const [busy, setBusy] = useState(false);
|
||||||
const [payTarget, setPayTarget] = useState<Order | null>(null);
|
const [payTarget, setPayTarget] = useState<Order | null>(null);
|
||||||
const [payLoyalty, setPayLoyalty] = useState(0);
|
const [payLoyalty, setPayLoyalty] = useState(0);
|
||||||
|
// Order just paid — kept after the cart is cleared so the receipt stays printable.
|
||||||
|
const [paidOrderId, setPaidOrderId] = useState<string | null>(null);
|
||||||
|
|
||||||
const [online, setOnline] = useState(true);
|
const [online, setOnline] = useState(true);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -287,13 +289,14 @@ export function Pos2Screen() {
|
|||||||
loyaltyPointsToRedeem: loyaltyRedeem > 0 ? loyaltyRedeem : undefined,
|
loyaltyPointsToRedeem: loyaltyRedeem > 0 ? loyaltyRedeem : undefined,
|
||||||
});
|
});
|
||||||
const paid = payments.reduce((s, p) => s + p.amount, 0);
|
const paid = payments.reduce((s, p) => s + p.amount, 0);
|
||||||
const paidOrderId = payTarget.id;
|
const justPaidOrderId = payTarget.id;
|
||||||
notify.success(`پرداخت ${fmt(paid)} تومان ثبت شد`, {
|
notify.success(`پرداخت ${fmt(paid)} تومان ثبت شد`);
|
||||||
action: { label: "چاپ فاکتور", onClick: () => void printReceipt(cafeId as string, paidOrderId) },
|
|
||||||
});
|
|
||||||
queryClient.invalidateQueries({ queryKey: ["tables-board", cafeId] });
|
queryClient.invalidateQueries({ queryKey: ["tables-board", cafeId] });
|
||||||
queryClient.invalidateQueries({ queryKey: ["orders-open", cafeId] });
|
queryClient.invalidateQueries({ queryKey: ["orders-open", cafeId] });
|
||||||
backToBoard();
|
backToBoard();
|
||||||
|
// Keep the paid order id so the cashier can still print the receipt after
|
||||||
|
// the cart is cleared (the success sheet below uses it).
|
||||||
|
if (!isLocalOrder(justPaidOrderId)) setPaidOrderId(justPaidOrderId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof ApiClientError && e.code.startsWith("POS_DEVICE")) {
|
if (e instanceof ApiClientError && e.code.startsWith("POS_DEVICE")) {
|
||||||
notify.error(posDeviceMsg(e));
|
notify.error(posDeviceMsg(e));
|
||||||
@@ -307,14 +310,10 @@ export function Pos2Screen() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Print (or reprint) the customer receipt for the active, already-saved order.
|
// Print (or reprint) the customer receipt for a saved server order.
|
||||||
const printActiveReceipt = async () => {
|
const printReceiptById = async (orderId: string) => {
|
||||||
if (!activeOrderId || isLocalOrder(activeOrderId)) {
|
|
||||||
notify.error("ابتدا سفارش را ثبت کنید");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
await printReceipt(cafeId as string, activeOrderId);
|
await printReceipt(cafeId as string, orderId);
|
||||||
notify.success("فاکتور برای چاپ ارسال شد");
|
notify.success("فاکتور برای چاپ ارسال شد");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const code = e instanceof ApiClientError ? e.code : "";
|
const code = e instanceof ApiClientError ? e.code : "";
|
||||||
@@ -328,6 +327,14 @@ export function Pos2Screen() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const printActiveReceipt = async () => {
|
||||||
|
if (!activeOrderId || isLocalOrder(activeOrderId)) {
|
||||||
|
notify.error("ابتدا سفارش را ثبت کنید");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await printReceiptById(activeOrderId);
|
||||||
|
};
|
||||||
|
|
||||||
// ── guards ───────────────────────────────────────────────────────────────
|
// ── guards ───────────────────────────────────────────────────────────────
|
||||||
if (!cafeId) {
|
if (!cafeId) {
|
||||||
return (
|
return (
|
||||||
@@ -613,6 +620,35 @@ export function Pos2Screen() {
|
|||||||
onConfirm={confirmPay}
|
onConfirm={confirmPay}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{paidOrderId && (
|
||||||
|
<div dir="rtl" className="fixed inset-0 z-[65] flex items-center justify-center p-4">
|
||||||
|
<div className="absolute inset-0 bg-black/50" onClick={() => setPaidOrderId(null)} />
|
||||||
|
<div className="relative w-full max-w-sm rounded-2xl bg-card p-6 text-center shadow-2xl">
|
||||||
|
<div className="mx-auto mb-3 flex size-14 items-center justify-center rounded-full bg-emerald-100 text-emerald-600">
|
||||||
|
<Check className="size-7" />
|
||||||
|
</div>
|
||||||
|
<p className="text-lg font-bold">پرداخت با موفقیت ثبت شد</p>
|
||||||
|
<p className="mt-1 text-sm text-muted-foreground">فاکتور مشتری را میتوانید چاپ کنید</p>
|
||||||
|
<div className="mt-5 space-y-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => void printReceiptById(paidOrderId)}
|
||||||
|
className="flex min-h-[52px] w-full items-center justify-center gap-2 rounded-xl bg-primary text-base font-bold text-primary-foreground transition-colors hover:bg-primary/90 active:scale-[0.99]"
|
||||||
|
>
|
||||||
|
<ReceiptText className="size-5" /> چاپ فاکتور
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setPaidOrderId(null)}
|
||||||
|
className="flex min-h-[48px] w-full items-center justify-center rounded-xl bg-muted text-sm font-medium text-foreground hover:bg-accent"
|
||||||
|
>
|
||||||
|
سفارش جدید
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user