Layaway
Hyvä POS is in closed beta
Hyvä POS is currently in a closed beta (pilot phase) with a small group of merchants. It is not yet generally available: the App Store release follows the pilot, and features and configuration may still change - possibly in backwards-incompatible ways - before the general release. Want to take part? Sign up at hyva.io/pos.
A customer wants the item held: they pay a deposit now and the balance later. Layaway makes that a first-class order instead of a note under the counter - with a real payment schedule, real stock reservation, and a drawer that reconciles.
How a Layaway Works
At checkout the cashier places the sale as a layaway and takes a deposit - any amount at or above the configured minimum percentage. From there:
- The order books in Magento immediately, for the full cart, in a pending-payment state. The goods are reserved: Magento's inventory reservation holds the stock until the layaway settles or is cancelled.
- Each payment - the deposit, any top-up, the final balance - is taken at the register like any tender and prints its own receipt showing the amount captured, the running total paid, the balance owed, and the expiry date.
- When the balance reaches zero the layaway flips to ready to settle: the goods hand over, and the order continues through Magento's normal invoice and fulfillment flow.
Because sharing happens through Magento, any register at the location can take the next payment or settle the balance - the customer is not tied to the counter where they started. Every payment lands in the register report for the day it was taken, on its own layaway payments line: the money joins the cash and card totals (so the drawer always reconciles), while the day's sales figure stays untouched - the revenue was already booked when the layaway was placed.
How It Books in Magento
The order places under a dedicated pos_layaway payment method, and that choice matters: under a stock offline method Magento would auto-capture the full total at placement. Alongside the order, placement creates a full invoice in the open state - so accounting integrations that drive on Magento invoices book the revenue and VAT at placement, matching the signed POS receipt exactly, and the open invoice shows the outstanding balance as a real receivable in the admin. (Advance payments make VAT chargeable when received; invoicing at placement declares it once, on the conservative side, instead of trickling it per payment - which Magento's item-based invoices could not represent anyway.)
Each payment then updates the order's own paid and due totals and adds an order comment recording the amount, the tender the customer actually used, and the payment reference (the terminal transaction id for card payments, the POS receipt number for cash) - a per-payment audit trail the register and the back office both read. That reference also makes retries safe: if a register resends a payment after a dropped connection, Magento recognises it and books nothing twice. The running numbers (deposit, balance owed, expiry, status) live as pos_layaway_* attributes on the order itself; there is no separate layaway table to reconcile.
When the balance reaches zero, the invoice is marked paid - by the register's final payment, or from the admin: Capture Offline on the open invoice is the admin-side settlement action for a bank-transferred balance, and it flips the layaway to ready to settle in the register's list without anyone at the counter touching it. Settlement then ships the goods through Magento's normal fulfillment flow. The registers and the back office each take their half of the work without stepping on the other.
Statuses and Expiry
A layaway carries its own status, independent of the Magento order state:
- Open - balance outstanding, payments being taken.
- Ready to settle - fully paid, awaiting handover.
- Stale - the expiry date passed before the customer settled. A scheduled job checks hourly and flags overdue layaways, adding a comment to the order; the register's Stale filter lists them so staff can chase or cancel. Nothing is cancelled automatically.
- Cancelled - closed by staff, deposit handled per your rules (below).
Each layaway gets an expiry date at placement, from the configured expiry period (default 30 days).
Cancelling a Layaway
Cancelling from the layaway detail asks for an optional reason and shows the money math before confirming:
- A cancellation fee - the configured percentage of what the customer has paid so far (default 0) - is retained. The cashier can adjust the percentage per cancellation. For an abandoned layaway the merchant's policy might set it to 100%: deposit forfeited, nothing handed back.
- Magento issues a credit memo for the order total minus the retained fee: the goods return to stock through the memo, the memo is the merchant's VAT-reclaim document for the never-delivered items, and the fee survives as the memo's adjustment - so the forfeited amount lands in the books as exactly what it is, not as a note in a comment. The order closes; the fee and refund breakdown also land in the order's comment history.
- The remainder is refunded from the cash drawer: a cash refund is paid out of the drawer and logged (the drawer count and the Z report's cash-refunds line both see it), a card deposit is reversed on the terminal by the cashier. A cancellation receipt prints either way - refunded amount, fee retained, and the credit memo reference - fiscally signed where signing applies.
Note that the VAT on the retained fee is the merchant's accountant's call: EU case law generally treats a genuine cancellation fee as compensation outside VAT scope, but a "fee" that equals essentially the full price is treated as taxable consideration. The credit memo gives the accountant a real document to adjust from, whichever way they rule.
A fully paid layaway cannot be cancelled this way; once it is ready to settle, use the normal returns flow instead.
Where the Data Lives
A layaway is not a parallel record - it is a Magento order:
- The order sits in
sales_orderlike any sale, with the layaway specifics stored as extra columns on the order: deposit amount, balance owed, expiry date, status, and the tender used for the deposit (which is how the refund on cancellation knows where the money should go back to). - The amounts paid and due use the order's own paid/due totals, so the admin order view, accounting exports and ERP integrations see standard Magento numbers - no side ledger to reconcile.
- Every payment produces a receipt on the device, marked as deposit, top-up or settlement; with receipt sync those receipts mirror to Magento with the layaway amounts on them. Top-up and settlement receipts are titled Payment Receipt rather than Customer Receipt, carry no VAT rows, and say so: the full order's VAT sits on the deposit receipt, printed when the layaway was placed. Reprinting the VAT on every payment would declare the same tax twice - a payment receipt is proof of money received against the balance, not a second tax document.
Configuration
The guardrails are Magento settings under Stores → Configuration → Hyvä POS → Terminal Defaults → Layaway Defaults:
| Setting | Default | What it does |
|---|---|---|
| Minimum Deposit (%) | 25 | Smallest deposit a cashier may take when placing a sale as layaway. |
| Expiry (days) | 30 | Days until an unsettled layaway is flagged stale. |
| Cancellation Fee (%) | 0 | Percentage of the captured payments retained on cancellation. |
Each location can override these, and the values sync to the registers automatically - the deposit sheet validates against them at the register. There is no per-device version of these numbers to drift out of line; the register enforces what Magento says.
Layaway is currently experimental: enable it per device under Settings → Advanced before Place as Layaway appears at checkout. The scheduled stale-flagging job rides Magento's default cron, so cron health matters here too.
What the Cashier Sees
- At checkout, Place as Layaway opens the deposit sheet: cart total, the minimum deposit, the resulting balance, and the expiry date the layaway will get. The deposit is then paid with a normal tender.
- The Layaways tab lists every layaway with filter pills for Open, Stale, Ready and All - the stale filter is the morning to-do list.
- The detail pane shows total, paid so far, balance owed and days until expiry, lists every payment taken so far (the deposit and each top-up, with tender and payment reference), links straight to the full order and the customer record, and offers cancellation with the fee and refund amounts spelled out before anything happens.
- Take payment asks how much (full balance in one tap, or 25% / 50% chips for a quick top-up) and then opens the register: the customer pays by cash - with change calculated - or by card on the terminal, exactly like any sale. The receipt prints through the configured printer, lands in the Receipts tab for reprints, and the payment shows up in the day's register report.
- The order detail shows the same story from the other side: a layaway order carries a Layaway card with the status, the balance, and the payment trail - and payments taken on that register open their receipt with a tap. Open in Layaways jumps to that layaway to take the next payment.
Offline
Placing a layaway works offline like any sale: the deposit is taken at the register and the order joins the offline queue. Taking further payments and cancelling need the connection, because the balance lives on the Magento order - the register will say so rather than guess at a balance it cannot verify.
If the connection drops mid-payment - money taken, but Magento unreachable before the balance updated - nothing is lost: the receipt and register totals are already recorded on the device, and the payment queues on the Layaways tab with a retry button. Retrying is safe even when the original request did land; Magento recognises the payment reference and books nothing twice.