// Flagship product
Leehog POS.
A multi-tenant operations platform for brick-and-mortar specialty retail. I designed and built the whole thing: the web dashboard, the desktop register, and everything between.
It covers the full shop loop. Catalog and batch inventory, warehouse-to-store transfers, a weight-aware point of sale, loyalty membership with check-in and store credit, shift audits, expenses, and reporting.








Point of sale
2
apps, one system (web + desktop)
14
SQL reporting views
4
scale protocols supported
EN/TH
fully localized
// The full shop loop
Every part of running a shop, in one system.
Everything runs in one platform, so inventory, sales, members, and reporting always stay in sync with each other.
Catalog & batch inventory
Products tracked in batches with cost and expiry. Display and back-room stock live in separate buckets, so the floor count always matches what is actually out.
Warehouse-to-store transfers
Two-phase transfers with send, receive, and loss accounting. Everything that moves between locations leaves a record.
Weight-aware point of sale
The register reads USB and serial scales live. Gram and unit products share one checkout, with billing-threshold snapping on weights.
Loyalty membership
Members check in and out with capacity limits, carry store credit, and can keep an open tab per location. PII is encrypted at rest.
Shift audits
Shift weight-checks, withdrawals, and shrinkage tracking. Every shift closes against actual counted numbers.
Expenses
Store-level expense tracking that flows into the same reporting layer as sales, so reports show actual margins.
Reporting
Analytics run on 14 PostgreSQL views instead of ad-hoc app aggregations. The numbers are consistent everywhere they appear.
Customer display
A second screen faces the customer with live cart, member greeting, and store credit. Driven over BroadcastChannel from the register.
// Architecture
How the system is built.
The web app
A Next.js 16 App Router full-stack product in TypeScript on PostgreSQL 17 via Prisma 7. NextAuth v5 JWT sessions with action-based authorization: roles are permission presets, not hard-coded role checks. Client state with TanStack Query, forms with react-hook-form and Zod, UI in Tailwind CSS 4 and shadcn/ui, localized with next-intl. WebSockets push live updates so the register, dashboard, and inventory stay in sync without a refresh.
The desktop POS
A companion Electron app wraps the register and the customer display. It talks to USB and serial scales through node-hid and serialport, with drivers for generic HID, KERN, Mettler SICS, and CAS. An offline SQLite cache queues mutations and replays them when the network returns, so a dropped connection never stops a sale.
Data & infrastructure
Everything is containerized and deployed on AWS ECS Fargate with RDS PostgreSQL, SES for mail, and S3 for media. Prisma migrations manage the schema, Vitest covers the API with mocked auth, and Playwright runs end-to-end flows. Docker Compose mirrors production locally.
// Engineering deep dive
The parts I am proud of.
Eight areas where I spent extra time getting the details right.
Multi-tenant SaaS
- tenantId on every tenant-owned model with composite unique indexes
- Row-level tenant isolation, no shared queries across tenants
- Platform-admin impersonation for support without password sharing
- Internal subscription billing with price and location caps
Authorization
- Action catalog: dashboard.view, sales.checkout, inventory.transfers.manage, and so on
- OWNER, MANAGER, STAFF, and ACCOUNTANT as permission presets
- Per-user grant and deny overrides on top of any preset
- No hard-coded role checks anywhere in the codebase
POS & pricing
- Gram and unit products in one checkout, scale-driven weight with billing-threshold snap
- Deal engine: weight tiers, buy-X-get-Y, and fixed bundles
- FIFO batch allocation so cost of goods is always traceable
- Cash, card, and store-credit ledger with no card processor on the register
- Member open tabs as a single pending sale per member and location
Inventory
- Batches carry cost and expiry through their whole lifecycle
- Two-phase transfers with send, receive, and loss states
- Cross-product stock conversions with weighted-average cost
- Display versus back-room stock buckets
- Withdrawals, shrinkage, and shift weight-checks
Membership
- Member PII encrypted at rest with AES-256-GCM and tenant-scoped key derivation
- Searchable HMAC lookup indexes without storing plaintext
- Per-location access and check-in/out with capacity limits
- Purchase caps and a store-credit ledger: top-up, redeem, refund, adjustment
Desktop
- Electron register plus second-screen customer display over BroadcastChannel
- HID and serial scale drivers: generic HID, KERN, Mettler SICS, CAS
- Offline mutation queue in SQLite with replay on reconnect
Data & ops
- Prisma migrations and 14 SQL reporting views
- WebSockets for live register, dashboard, and inventory updates
- Vitest API tests with mocked auth, Playwright end-to-end tests
- Docker Compose locally, ECS and RDS in AWS (eu-south-2), SES in eu-central-1
Hardening
- AES-256-GCM for PII and HMAC lookup hashes
- Optional TOTP two-factor with backup codes
- IP allowlists and a full audit log
- Emergency location lockdown switch
// Built with
Need something similar?
This is the level of work I bring to client projects. Tell me about your business and I will tell you how I would approach it.