Koeuk KOS
000%
Loading portfolioWarming up
Back to Projects
POS Retail logo

POS Retail

Offline-first Point of Sale & Back Office

A point-of-sale and back-office built for a real Cambodian retail shop. Prices are Riel-native, a can, a six-pack and a case are sold as one product, customers can run a tab, and the till keeps working without a connection, syncing sales back to the server once it is online again.

Laravel 12Vue 3TypeScriptInertia.jsshadcn-vueTailwind CSSMySQLViteScribePHPUnit + GitHub Actions

3

Roles: Admin, Manager, Cashier

KHR / USD

Dual currency display

Offline

Sales queue and sync

13

Screenshots

Features

πŸ“΄

Offline-first till

Sales rung up without a connection are queued on the device and replayed to the server when it is back online.

αŸ›

Riel-native pricing

Prices are stored in the shop currency with its own minor factor, so Riel never assumes cents. Toggle USD or KHR display with a configurable exchange rate.

πŸ“¦

Pack sizes as one product

A can, a six-pack and a case are the same product with different prices, so the grid shows one card with a price range.

πŸ“’

Customer debt tabs

Put all or part of a bill on a named customer. Record payments as money comes in; a debt settles once paid in full.

🍽

Myself

Stock taken for own use goes down without counting as a sale, valued at shelf price for the week, month and year.

πŸ“±

Public QR menu

A no-login mobile menu customers reach by scanning a QR code, grouped by category with every pack size.

🏬

Per-store stock

Stock lives per store, not on the product. Order numbers encode store and register (S1-R1-…).

πŸ”

One permission path

Every feature is gated by a Permission enum, route middleware and a policy. Roles are only bundles of permissions.

πŸ•΅οΈ

Audit trail

Model changes and money, auth and access events are logged with an explicit field list, so secrets never reach the activity log.

πŸ“Š

Reports and dashboard

Today versus yesterday, average basket, sales for the last 7 days, owed to you, and per-cashier activity.

🎨

Branding and dark mode

Upload a logo and favicon, set the order number prefix, and switch between light and dark themes.

🌐

Khmer and English

Bilingual product and category names throughout the till, menu and back office.

Architecture

Enums first

Permission, Role, Action, OrderStatus, PaymentMethod and SaleType define the vocabulary the whole app agrees on.

Inertia pages

Web controllers return Inertia pages mapped straight to resources/js/pages; shared props carry auth.can to every screen.

Services

OrderTotals, OrderSyncService for offline replay, and SalesReporter keep business rules out of controllers.

Policies

Per-model authorisation, all routed through hasPermission, never a role check.

shadcn-vue UI

A primitive component layer under components/ui with typed composables such as usePermissions and useCurrency.

Tests and CI

PHPUnit suite plus lint run on every push to main and develop via GitHub Actions.

Core Models

users
stores
registers
products
categories
stocks
orders
customers
activities
settings
TOKEN APIdocumented with Scribe

REST API for integrators

The token API reuses the same permission gates as the web app, so there is one authorisation path. Docs generate from controller annotations and ship with an integrator walkthrough.

GETPOSTPUTDELETE

Screenshots β€” Point of Sale

The till: search or scan, pack-size prices, cart, and the three ways a sale can end β€” paid, on a customer tab, or taken for yourself.

POS Retail β€” Putting a sale in debt β€” pick the customer who owes it
Putting a sale in debt β€” pick the customer who owes it
POS Retail β€” Partial payment now, the rest stays on the tab
Partial payment now, the rest stays on the tab
POS Retail β€” Order history from every till, with debt badges
Order history from every till, with debt badges
POS Retail β€” In Debt β€” what each customer still owes, record payments
In Debt β€” what each customer still owes, record payments
POS Retail β€” Myself β€” stock taken for own use, valued at shelf price
Myself β€” stock taken for own use, valued at shelf price

Screenshots β€” Back Office

Dashboard, catalogue and shop settings. Light and dark themes.

POS Retail β€” Dashboard β€” today's sales, owed to you, last 7 days
Dashboard β€” today's sales, owed to you, last 7 days
POS Retail β€” Dashboard in dark mode
Dashboard in dark mode
POS Retail β€” Products with SKU, barcode, per-store stock and status
Products with SKU, barcode, per-store stock and status
POS Retail β€” Categories with product counts and change history
Categories with product counts and change history
POS Retail β€” Shop settings β€” USD / KHR, exchange rate, logo and favicon
Shop settings β€” USD / KHR, exchange rate, logo and favicon

Screenshots β€” Authentication

Staff accounts are created by an administrator. Password reset uses a 6-digit emailed code.

POS Retail β€” Log in
Log in
POS Retail β€” Forgot password β€” 6-digit code
Forgot password β€” 6-digit code

Screenshots β€” Public QR Menu

Customers scan a QR code and browse the shelf on their phone, with every pack size and price.

POS Retail β€” Mobile menu grouped by category
Mobile menu grouped by category

Source code

The full Laravel + Vue codebase, migrations, seeders, tests and API docs are public on GitHub.

github.com/koeuk/POS-retail

Open repository

Β© 2026 Koeuk KOS. All rights reserved.

Built with Nuxt.js, Vue.js & Tailwind CSS