- Global MutationCache.onError safety net so mutations without their own onError
no longer fail silently (skips ones that handle errors → no double toast).
- Notifications feed no longer opens its own SignalR connection; it reuses the
one in useOrderAlerts (was double sockets + double cache churn per session).
- "Send test notification" now works on the settings page (force flag bypasses
the tab-visible guard) instead of silently doing nothing.
- POS: re-entry guard on payment confirm (no duplicate payment on double-tap);
notes on already-sent lines are read-only (a note-only edit was silently lost);
ORDER_ALREADY_CLOSED surfaced with a clear Persian message.
- Reservation Confirm/Cancel/Complete buttons disabled while pending.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The payment-success sheet with the "چاپ فاکتور" button lives in the order-view
return, but confirmPay called backToBoard() which switched to the board view —
so the sheet never rendered and the cashier couldn't print after paying.
Now payment clears the cart + closes the pay sheet but STAYS on the order view,
so the success sheet shows; returning to the board happens when the cashier taps
"سفارش جدید" or the backdrop. Offline/local orders still go straight to the board.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The note control was an unlabeled icon next to the trash on each cart line, so
cashiers couldn't tell where to add a note. Replaced it with a clear
"افزودن یادداشت" (add note) text button under each item; clicking it reveals the
note input, which collapses again on blur when left empty. Existing notes still
show the editable field.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
POS v2 has been the default at /pos for a while; this deletes the old classic
POS entirely:
- removed the /pos-classic route and all classic-only components
(pos-screen, pos-pay-panel, pos-table-board, pos-queue-bar, pos-receipt-modal,
pos-slip-modal, pos-receipt-print.css)
- relocated the two modules POS v2 still shared into the pos2 tree
(lib/pos/submit-order → lib/pos2, components/pos/pos-customer-picker → pos2),
so the components/pos and lib/pos folders are gone
- dropped the now-dead "نسخه کلاسیک" (classic version) button + RotateCcw import
from the POS v2 header, and updated stale comments
POS v2 (/pos) is unchanged and fully self-contained. Typecheck clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
POS v2 auto-printed the receipt on full payment but had no manual button. Adds a
"چاپ فاکتور" (print receipt) action in the order panel that prints/reprints the
active saved order's customer receipt, plus a "print receipt" action on the
payment-success toast. Replaces the dead disabled "hold" placeholder button.
Backend print endpoint unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The cart store, order payload (create + add-items + offline), KDS ticket and
receipt already supported per-item notes — but POS v2 had no way to enter one.
Adds a note button on each cart line that toggles an inline input (e.g. "no
sugar"); the note shows highlighted when set and rides along to the kitchen/bar
ticket. No backend change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Nav already hides pages a role can't view (NAV_REQUIRED_PERMISSION). This wraps
the sensitive/CRUD action controls in <Can permission> so users only see what
they can do (server still enforces):
- POS/orders: void → VoidOrder, cancel → VoidOrder, transfer → EditOrder,
pay/split → HandlePayments
- menu/inventory/coupons/customers/reservations/expenses/taxes/branches:
add/edit/delete buttons → the matching Create/Edit/Delete permission
- reports CSV export → ExportReports; SMS send → SendSms, settings → ManageSmsSettings
- home dashboard: revenue/orders KPI queries gated on ViewReports so non-report
roles don't 403 on the landing page
(Refund/discount/comp/cash-drawer have no UI control yet — no buttons to gate.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
POS runs in the (fullscreen) layout which strips the sidebar.
Adds a Home → داشبورد button at the top-left of the table board so
users can navigate back to the dashboard without being stuck.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Race fix: orderBranchId now returns `undefined` (not null) while the /branches
query is in flight. usePos2Menu treats undefined as "not yet determined" and
skips the fetch, preventing getBranchMenu(cafeId, null) → empty array.
Once branchesFetched=true, orderBranchId resolves to the correct branchId
(or null for café-wide fallback).
Layout: desktop order screen now shows a left vertical category sidebar
(116 px, md+) instead of horizontal chips, giving the classic POS sidebar
feel. Horizontal chips kept for mobile (<md). Menu grid columns adjusted.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The menu/tables are branch-scoped. v2 used the raw stored branchId, which is
null or stale for users who never opened the classic POS (it has no branch
picker), so getBranchMenu returned an empty menu. Now v2 fetches /branches,
auto-selects the first valid branch (self-healing the stored id), and loads the
branch menu + tables + order submission against that resolved branch — matching
the classic POS exactly. Also adds a visible "menu failed to load / retry"
state instead of a silent empty grid.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Card is now the pre-selected payment method (and split rows default to Card),
matching Iran's card-dominant payments. Card already sits first in the selector.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reorder the payment method tabs to کارت / نقدی / تقسیم (Card first, most common
in Iran) while keeping Cash as the pre-selected default method.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Completes the four POS v2 roadmap items:
1. Real split payments — split tab records N separate payment rows (equal split,
last row takes the remainder), each row toggles Cash/Card; posts payments[].
2. Card-terminal push — confirmPay sums Card amounts and calls requestPosPayment
(POS device) before recording; surfaces POS_DEVICE_* errors.
3. Customer + coupons + loyalty — reuses PosCustomerPicker (attach/search/create)
and validates coupons via /coupons/validate (discount in totals). Pay sheet
offers loyalty redemption (1 point = 100 toman) when a customer is attached.
4. Promote to default — /pos now renders POS v2 (full-screen, café-themed); the
classic terminal moves to /pos-classic with its sidebar+topbar chrome. The
"نسخه کلاسیک" link points there.
Order submission already carried customerId/guestName/guestPhone/couponId via the
shared cart store, so customer + coupon flow straight through send + pay.
tsc --noEmit clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
POS v2 is now a real, working point of sale at /[locale]/pos2 (was a static
mock). It reuses the existing data layer so it shares the React Query cache and
offline pipeline with the classic POS:
- Table board ← fetchCafeTableBoard (Free/Busy/Reserved/Cleaning, live totals,
guest-QR badge); polls every 15s. Open a free table to start an order; open a
busy table to hydrate its existing order (GET order → cart hydrateFromOrder).
- Order screen ← real branch/café menu + categories, bound to useCartStore
(add/qty/remove). Send via submitOrderToApi (online + offline outbox) then
re-hydrate; "ارسال (n)" shows the pending (unsynced) line count.
- Pay sheet ← POST /orders/{id}/payments. Cash (numpad + change), Card, and a
Split helper (records the full amount; split is cashier guidance for now).
- Online/offline badge, loading/empty states, toasts, busy overlay, and a
"نسخه کلاسیک" link back to /pos.
The static design mock stays at /[locale]/pos2-preview (dev-only, 404 in prod).
tsc --noEmit clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>