# A custom POS system that doesn't slow down the queue

Canonical: https://erpfly.com/modules/point-of-sale/
Last updated: August 28, 2026

Category: Sales & CRM

Your stores run on the ERPNext or Odoo point of sale, and it mostly works. Then head office wants refund controls, a different receipt and a price rule for one location. A custom POS system has to add those without making a cashier wait while a queue builds. erpfly is built for exactly that job.

### Features

- **Controls that run after the sale.** Refund reviews, discount audits and cash checks happen at session close or overnight. The cashier never sees a server error with a customer standing there.
- **Per-store settings.** Limits, managers and receipt footers live on the POS Profile or pos.config, so one store can differ without a code change.
- **Receipts you can actually edit.** Add a return policy, a loyalty balance or a tax line your region requires. Receipt templates are extended, not copied, so upgrades don't wipe them.
- **Till screen changes, kept small.** A required field on refunds or an extra button goes in as a focused front-end patch. We keep these minimal because they're the most version-sensitive part of any POS.
- **Back office that matches the till.** Session totals, payment methods and refunds line up with the journal entries finance sees, store by store.

### What gets generated

**ERPNext / Frappe app:**
- Custom fields on POS Profile and POS Invoice
- POS Closing Entry on_submit hook for refund review
- page_js patch for the point-of-sale page in hooks.py
- Receipt Print Format for POS Invoice
- Refunds by Store Script Report

**Odoo addon:**
- pos.config fields for refund limit and store manager
- pos.session computed refund totals
- Review activity created on session close
- OWL patch for the payment screen
- QWeb receipt template extension

### The till is the worst place for surprises

Back-office users will tolerate a slow form or a confusing error. A cashier with six people waiting won't, and neither will the customer at the front of the line. So the first question we ask about any POS change isn't "can we build it?" It's "where should this rule run?"

Most requests from head office are really audit requests. They want to know about big refunds, odd discounts and cash that doesn't add up. None of that has to happen during the sale. It can happen when the session closes, when the day ends, or on a schedule. Rules that genuinely must stop a sale, like an age check on certain products, belong in the till screen, where they can prompt the cashier politely instead of failing on the server.

### What a custom POS system from erpfly changes, and what it leaves alone

We change the rules around the till. We don't rebuild the till.

On **Odoo**, the POS interface is an OWL application. Front-end changes go in as patches to existing components, while settings, computed totals and follow-up tasks live in Python on `pos.config`, `pos.session` and `pos.order`. Receipts are QWeb templates that we extend.

On **ERPNext**, the Point of Sale page works with POS Profile, POS Opening Entry, POS Invoice and POS Closing Entry. At closing, POS Invoices are merged into regular Sales Invoices. That makes closing the natural hook for reviews and reconciliation. Changes to the page itself are loaded through `page_js` in `hooks.py`, from your own app.

If you haven't picked a platform yet, POS is one of the areas where the two differ most in how they feel to customise. Our [ERPNext vs Odoo comparison](https://erpfly.com/compare/erpnext-vs-odoo/) covers that in more detail.

### Example: refunds at three garden centres

Take a small chain of three garden centres on Odoo. Refunds are a normal part of the business, since plants die and customers bring back the wrong size of pot. Head office doesn't want cashiers phoning a manager for every one. It does want to know when a single session runs heavy on refunds.

The rule they settled on: if a session closes with more than $150 in refunds, the store manager gets a to-do listing them. On a Saturday, store two closes a session with six refunds totalling $410. The session closes normally, the cashier goes home, and on Monday the store manager finds a task linked to that session. Four of the refunds are dead hedging plants from one bad delivery. The store manager ends up calling the supplier, not questioning the cashier.

Store one sets its limit to $250 because it's the busiest site. That's a setting on the POS configuration, not a code change. The snippet above is most of the module.

On ERPNext the same rule would hang off the POS Closing Entry. When the cashier submits the closing, a hook totals the return invoices in that period and creates a ToDo for the manager named on the POS Profile. Same idea, different nouns. Either way, the review happens once per session instead of once per refund, which is roughly the difference between a manager who reads the alerts and one who mutes them.

### Requests that cause trouble at the till

- **Server-side validation that rejects synced orders.** If an offline till creates an order your Python rule refuses, that sale is stuck between the till and the database. Put hard stops in the till screen, or flag afterwards.
- **A home-built till interface.** The standard POS on both platforms handles offline mode, payments and session control. Replacing it is a large project that you then maintain forever.
- **Loyalty schemes with a dozen tiers.** Start with points and one reward. Complicated schemes confuse cashiers first, then customers.

### Stock, pricing and the rest of retail

A till is only as good as the stock and prices behind it. Store-level counts and transfers belong in [inventory management](https://erpfly.com/modules/inventory-management/), and wholesale or trade orders that start in-store usually want a proper [sales order flow](https://erpfly.com/modules/sales-order-management/). For the wider picture of running shops on either platform, see our page on [ERP for retail](https://erpfly.com/industries/retail/). Cafés and restaurants have a different set of problems, covered under [food and beverage](https://erpfly.com/industries/food-and-beverage/).

### FAQ

### Will the tills still work when the internet drops?

Both POS apps keep selling offline and sync orders later, and we design around that. It's the reason we avoid server-side checks that could reject an order during sync. Rules that must stop a sale go into the till screen itself.

### Do receipt printers, scanners and scales keep working?

Customisation doesn't change how hardware connects. Odoo connects devices through its IoT box or supported network printers, and ERPNext relies on the browser and its print settings. If you need a new device integration, we'll want the actual device on a desk before promising anything.

### Is the Odoo point of sale available in Community edition?

Yes, the core point_of_sale module is in Community. Some add-ons around it, such as certain hardware and restaurant features, depend on your version and edition, so check the specific feature before you plan around it.

### Which part is most likely to break on upgrade?

Front-end patches to the till screen. Both POS interfaces change more between major versions than the back-office models do. We keep those patches small and documented, and the Python side usually carries over with little work.

### Can we move from Square or Lightspeed?

Products, customers, gift card balances and opening stock can be imported with a generated script. Old transaction history usually comes across as summarised journal entries per day or per month, because replaying years of receipts rarely pays for itself.

### Who owns the POS code?

You do. It's a normal Odoo addon or Frappe app in your own repository, and it keeps running if you stop using erpfly.