Skip to content

fix(pos): print receipts for sales completed offline - #148

Draft
phronesis wants to merge 3 commits into
kodlyft:developfrom
VigesimalTech:fix/offline-receipt-printing
Draft

phronesis wants to merge 3 commits into
kodlyft:developfrom
VigesimalTech:fix/offline-receipt-printing

Conversation

@phronesis

Copy link
Copy Markdown

What

Prints the receipt for a sale completed offline in the browser POS, from local data, when the cashier chooses Save & Print.

Builds on #147: without it the offline sale isn't saved, and the receipt snapshot also contains reactive arrays. Only the last two commits are new here.

Why

In the browser, both offline branches of PaymentDialog.submitPayment (the !isOnline() branch and the network-error fallback) queue the invoice and clear the cart, but never print, even for Save & Print. Online printing opens /printview, which isn't reachable offline. The receipt layout (get_receipt_context) was cached only in Electron, and the receipt snapshot was kept only by the Electron path. A till that loses its connection can take payment but can't hand the customer a receipt.

Changes

  • frontend/src/stores/posStore.ts: refreshReceiptContext also caches the receipt layout in the browser for profiles with use_offline_mode, when a shift is opened or resumed (never while offline).
  • Receipt stored with the invoice: offlineStore.saveOffline and dbBridge.addPendingInvoice store a receipt snapshot next to data (new optional PendingInvoice.receipt). Sync still sends only data to create_invoice.
  • frontend/src/composables/usePrintInvoice.ts:
    • new printReceiptOffline(snapshot): builds the receipt with buildReceiptHtml and the cached layout, and makes no network call. If no layout is cached, it shows a clear message instead.
    • new printHtml(html): prints from a hidden iframe, which popup blockers don't catch after an await, then removes the iframe.
  • New frontend/src/composables/useOfflineSale.ts: completeOfflineSale queues the invoice with its snapshot, prints it as OFFLINE-<id> when asked, records that number as the last invoice, and clears the cart. A failed print doesn't lose the sale; a failed save keeps the cart.
  • frontend/src/components/dialogs/PaymentDialog.vue: both offline branches call completeOfflineSale.
  • Tests (20 new):
    • offlinePrint.spec.ts: the iframe printing, snapshot and layout, no network calls, and the missing-layout message.
    • offlineSale.spec.ts: queueing and printing order, Save without print, Print Last, and the failure cases.
    • offlineReceiptStorage.spec.ts: the receipt stored but never synced, and the layout cached only for offline-mode profiles and not while offline.
    • dbBridge.spec.ts: the receipt stored beside the invoice data.

The cart passes its payments and change legs as live Vue arrays.
dbBridge.addPendingInvoice and addPendingPurchase wrote them to IndexedDB
(or sent them over IPC) as-is, and structured clone throws DataCloneError
on a Proxy, so a cash sale made offline failed with "Failed to save
invoice offline".

Run both records through sanitizeForIdb first, which idbService already
used for everything else. It moves to its own module so dbBridge can use
it without pulling in Dexie.
An offline sale in the browser was queued and the cart cleared, but
nothing printed: the offline branches of the payment dialog never called
a print function, the server's /printview is unreachable offline, and the
receipt layout was only cached in Electron.

- Cache the receipt layout for offline-mode profiles in the browser too,
  when a shift opens or is resumed.
- Keep a receipt snapshot with each queued invoice, beside the invoice
  data so it is never sent to the server.
- Complete offline sales through useOfflineSale: queue, print from the
  snapshot and cached layout with a hidden iframe (OFFLINE-<id> until it
  syncs), then clear the cart. A failed print does not lose the sale; a
  failed save keeps the cart.
- printHtml prints from a hidden iframe and cleans up after printing.
- printReceiptOffline uses the snapshot and cached layout, makes no
  network call, and explains when no layout is cached.
- completeOfflineSale queues with the receipt, prints only when asked,
  keeps the cart when saving fails.
- The receipt is stored beside the invoice and never synced.
- The layout cache is filled for offline-mode profiles only, and not
  while offline.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant