Laravel · POS · Business systems
Building a POS System in Laravel: Architecture and Lessons Learned
How to architect a POS system in Laravel: data model, stock ledger, transactions, multi-branch inventory, receipts, reports and lessons from real builds.
By Ehsan Ullah · Published · Last updated · 7 min read
What does a POS system need to do?
A point-of-sale (POS) system must record a sale in seconds, print a receipt, keep stock accurate and give the owner reliable reports. Speed at the counter and correct stock numbers matter more than any fancy feature. This is the architecture I use when building POS systems in Laravel.
The core data model
- Products and variants: SKU, barcode, price, tax class.
- Branches / locations: every stock number belongs to a location.
- Sales and sale items: a header row plus line items with price and discount at the time of sale.
- Payments: a sale can have several payments (cash, card, wallet, credit).
- Stock movements: an append-only ledger of every change.
- Customers, suppliers and purchase orders.
Lesson 1: use a stock ledger, not a single quantity field
Instead of only updating products.quantity, write every change to a stock_movements table (sale, return, purchase, transfer, adjustment) with the quantity, location and reference. The current stock is the sum of movements, which you can cache in a stock_levels table. This gives a complete audit trail and makes errors traceable.
Lesson 2: wrap every sale in a transaction
Creating the sale, its items, the payments and the stock movements must succeed or fail together. Use DB::transaction() and lock the affected stock rows (lockForUpdate()) to avoid two tills selling the last item at the same time.
Lesson 3: store prices at the time of sale
Copy the unit price, discount and tax onto each sale item. Product prices will change; old receipts and reports must not.
Lesson 4: design the sales screen for speed
- Barcode scanner input focused by default.
- Keyboard shortcuts for quantity, discount and payment.
- Search that responds instantly (indexed columns, small payloads).
- Printing handled without blocking the next sale.
Lesson 5: multi-branch from the start
Even if the business has one shop today, make location part of every stock record. Adding branches later is painful otherwise.
Lesson 6: reports from summary tables
Daily sales, product performance and staff reports should read from summary tables updated by queued jobs, not from huge live queries during business hours.
Lesson 7: plan for integrations
Expose a clean internal API so the POS can share inventory with an online store, sync to Google Sheets or feed accounting software later.
Summary
| Decision | Why |
|---|---|
| Stock ledger | Audit trail and accurate stock |
| Transactions + row locks | No half-saved sales or overselling |
| Prices copied to sale items | Historical accuracy |
| Location on every record | Painless multi-branch |
| Summary tables | Fast reports |
Planning a POS? See POS system development.