Skip to content
Ercan Labs
← All workDESKTOP · Own product · in-development

Corner Shop Register

Offline-first register, stock and credit ledger for a neighbourhood grocer. No internet, no server.

233
Tests
18
Tables
150
Source files
18.783
Lines of code

How this was measured: Test count from vitest output, tables from migration files, LOC via wc over tracked source files.

Architecture233 tests + 4 smoke
  1. WINDOW
    • kasa ekranıReact + Vite
    • PIN kilidiscrypt
  2. MAIN
    • Electron main
    • fiş şablonu
  3. LEDGER
    • stok defterisatır, sayaç değil
  4. FILE
    • SQLiteWAL · 18 tablo
No server: register, reports and backup read the same file. No sync layer, so no reconciliation bug.

Problem

A neighbourhood grocer does not run on subscriptions. The till must work when the line is down, the day must close with a printable Z report, and the credit notebook — who owes what, since when — is the real accounting system.

Every cloud POS I looked at assumed a connection and a monthly fee. This one assumes neither.

Constraints

  • Fully offline: no API, no account, no telemetry.
  • One machine, one operator: a 4-8 digit PIN with scrypt, auto-lock after two idle minutes.
  • Data lives in a single SQLite file under the OS user data directory, WAL mode.

Architecture decisions

01

SQLite as the source of truth, not a cache

The register writes to the same file the reports read. No sync layer, so no reconciliation bugs — and no multi-device story, which is a deliberate trade.

Cost: No second till without a redesign of the write path.

02

Stock is a ledger, not a counter

Purchases, sales and manual corrections are rows; the on-hand number is derived. A counter column drifts the first time someone cancels a receipt.

Cost: Every stock query aggregates; mitigated with indexes and a reconciliation check.

03

Receipts are templates, not screenshots

The Z report and the customer receipt render from a template that knows the paper width, so a 58mm and an 80mm printer both work.

Cost: A small layout engine instead of print CSS.

04

Four smoke tests against the real Electron app

Unit tests cover the ledger; CDP-driven smoke tests open the actual window and click through register, stock, credit and closing.

Cost: Slower CI, but the failure mode that matters — a broken window — is caught.

Proof

  • Unit tests233 (31 files)
  • Schema tables18
  • Smoke tests4 (POS, stock, credit, Z report)
  • Source files150

Known limits

  • Windows packaging and backup/restore are not finished; the app runs from source today.
  • No real shop has used it for a month — there is no field data in this case.
  • Single-terminal by design; sync is out of scope, not forgotten.

What I learned

Offline is not a feature flag. Once you remove the server, every assumption about identity, time and conflict comes back as a local problem — the PIN, the clock and the file lock all become part of the architecture.

Stack

ElectronViteReactTypeScriptSQLiteDrizzle