|
|
# Build Status — 2026-07-10 (post-slice)
|
|
|
|
|
|
**54 screens across 2 surfaces** (46 back office + 8 POS), on 8 tested logic packages
|
|
|
(89 tests green), and a store-server **with a real data layer** (SQLite behind portable
|
|
|
repositories; Oracle 12c adapter slots in when the Classic DDL arrives — D12).
|
|
|
Product form: **web app** served by the store-server (D2) — back office at `/`,
|
|
|
POS at `/pos/`, printing via `/api/print`.
|
|
|
|
|
|
**The vertical slice is LIVE and verified end-to-end**: PIN login (server-side scrypt +
|
|
|
lockout) → item cache from DB → bill commit in one transaction (GST series allocation,
|
|
|
bill + lines, stock movements, dormant outbox row, audit entry) with **server-side
|
|
|
engine re-verification** (client and server must agree to the paisa) → bill visible in
|
|
|
Back Office Bills, stock derived correctly, audit trail written. Verified by driving
|
|
|
both UIs in a real browser: bill `ST1C2/26-000001`, ₹600.00.
|
|
|
|
|
|
## The foundation (packages/ — real, tested code, not wireframe)
|
|
|
|
|
|
| Package | What it does | Tests |
|
|
|
|---|---|---|
|
|
|
| `@sims/domain` | Tenancy (tenant→store→counter→user), integer-paise money + ₹ Indian grouping, UUIDv7 ids, Day-Begin business day + FY, per-counter GST doc series (16-char rule), GSTIN checksum validation, immutable document types, dormant outbox (D14) | ✅ |
|
|
|
| `@sims/billing-engine` | Dated GST rates, inclusive/exclusive prices, CGST/SGST vs IGST, cess, pro-rata bill discounts, rupee round-off — golden-tested to the paisa | ✅ |
|
|
|
| `@sims/auth` | PIN policy + scrypt hashing, lockout reducer, role→action matrix with supervisor-PIN gates, POS session bound to business date | ✅ |
|
|
|
| `@sims/scanning` | EAN-13 validation, weighing-scale (2x) barcodes, scan-box grammar (`3*` qty, code, search), wedge scanner vs human-typing detector | ✅ |
|
|
|
| `@sims/search-core` | Catalog search (pure TS, no deps): Indic phonetic folding (chawal≈chaval, paneer≈panir, jeera≈zeera), in-memory index, tiered ranking (whole-prefix → token-prefix → phonetic → substring) | ✅ |
|
|
|
| `@sims/printing` | ESC/POS byte builder, receipt renderer (42/32 col), drawer kick; **A4 GST invoice HTML + amount-in-words, EAN-13 label sheets, GS v 0 Indic raster (S2)** | ✅ |
|
|
|
| `@sims/config` | Settings hierarchy resolver (system→tenant→store→counter→user, effective-dated), message catalog with language fallback | ✅ |
|
|
|
| `@sims/ui` | Design system: light/dark themes, 6 accents, Inter + JetBrains Mono bundled, wireframe component kit, ThemeSwitcher | — |
|
|
|
|
|
|
## POS — 7 screens (`/pos/`) — FUNCTIONAL
|
|
|
|
|
|
| # | Screen | State |
|
|
|
|---|--------|-------|
|
|
|
| 1 | Login — user tiles + PIN pad | Functional (seed users) |
|
|
|
| 2 | Shift Open — denomination count → opening float | Functional |
|
|
|
| 3 | **Billing screen** — scan box, lines grid, totals, quick keys, status bar | **Functional end-to-end**: real engine, barcode/multiplier/scale-barcode entry, hold/resume, per-counter GST series, one-key cash close (F12/Numpad+) |
|
|
|
| 4 | Payment overlay (F11) — Cash/UPI/Card/Khata | Functional (modes recorded; QR/split later) |
|
|
|
| 5 | Bill history (F10) | Functional (day log) |
|
|
|
| 6 | Day-End summary — totals by payment mode | Functional (variance calc later) |
|
|
|
| 7 | Settings (Ctrl+,) — printer IP/port/width, test print, drawer kick | Functional (prints via store-server) |
|
|
|
|
|
|
Also live: customer attach (F6); server-backed history (F10); commit failures keep the
|
|
|
cart; **live search suggestions** (visible-before-add, ↑/↓+Enter); **F2 qty / F3 price
|
|
|
inline cell edits** (price>MRP blocked); **unknown-code quick-add** (draft item created
|
|
|
server-side, billable immediately). Verified: bill ST1C2/26-000002.
|
|
|
**P1 pass done (verified)**: tendered-cash change flash (type `100` F12 → CHANGE ₹20),
|
|
|
Ctrl+B repeat-last-bill (current prices, never resurrected), instant `*` multiplier with
|
|
|
qty chip, F4 line discount (`4` = ₹4, `5%` = percent; Disc column appears on demand),
|
|
|
audio feedback (ok/warn/err beeps), ok-notices auto-clear, stock chips in suggestions
|
|
|
(live counts, out-of-stock warn-not-block). Bill ST1C2/26-000003 proves the chain.
|
|
|
Purchase Entry P1: crash-proof localStorage draft with resume banner; glance strip on
|
|
|
cell focus (stock · sale · MRP · live margin · last-3 costs via /purchases/cost-history).
|
|
|
Still pending: returns flow, supervisor PIN overlay (needs /auth/verify-pin),
|
|
|
purchase schema P1s (free-qty, disc%, interstate split — one migration pass together),
|
|
|
history-R repeat, customer display, offline queue.
|
|
|
|
|
|
**Catalog scale (S1 + S6) — done 2026-07-10.** New `@sims/search-core` (pure TS, no
|
|
|
deps) replaces BillingScreen's linear name filter: `phoneticKey` folds the Indic
|
|
|
spelling variants the spec names (aa→a, ee→i, oo→u, w→v, ph→f, bh→b, chh→ch, sh→s,
|
|
|
th→t, dh→d, z→j, drop doubled letters), and `buildIndex`/`querySearch` rank matches in
|
|
|
tiers (whole-prefix → token-prefix → phonetic → substring, shorter name breaks ties).
|
|
|
The POS memoizes the index over its live item cache (grows with quick-adds); the ≤8-row
|
|
|
dropdown and arrow/Enter behaviour are unchanged. S6/R13: the silent `LIMIT 2000` in
|
|
|
`listItems` is gone — `GET /api/items?all=1` returns the whole non-inactive catalog for
|
|
|
the POS cache (which now fetches with `?all=1`), every other path paginates
|
|
|
(limit/offset, default 5000); `listBills`/`listAudit` and their routes paginate too
|
|
|
(default 100), and the back-office Bills and Audit pages grew a "Load more" button.
|
|
|
Verified 2026-07-10: 66/66 tests green (11 new folding/tier cases), `npm run typecheck`
|
|
|
clean on root + all three apps, all three apps build, and the store-server was rebuilt
|
|
|
and restarted — `/api/health` ok, `/api/items?all=1` returns the full 12-item catalog
|
|
|
with no cap, limit/offset confirmed on items/bills/audit, and a phonetic query
|
|
|
`ashirwad` resolves to `Aashirvaad Atta 5kg`. Integer paise, existing tests, and the
|
|
|
zero-runtime-dep rule are untouched.
|
|
|
|
|
|
**Print coverage (S2) — done 2026-07-10.** Three new pure modules in `@sims/printing`:
|
|
|
`invoice-html.ts` (`renderInvoiceHtml` → a self-contained, printable **A4 GST tax
|
|
|
invoice**: seller name/GSTIN/address header, buyer block, HSN-wise line table, a
|
|
|
CGST/SGST **or** IGST + cess breakup grouped by HSN+rate, round-off, payable, and
|
|
|
**amount-in-words** via a new `amountInWordsINR(paise)` domain util — integer paise,
|
|
|
Indian lakh/crore numbering, singular "Paisa" — plus a bilingual English/Hindi footer);
|
|
|
`labels.ts` (`ean13Svg` — standards-correct EAN-13 with L/G/R element patterns and
|
|
|
first-digit parity, checksum via `@sims/scanning`'s `ean13CheckDigit`; `renderLabelSheetHtml`
|
|
|
— a printable barcode/MRP label grid); and `raster.ts` (pure `packRasterRow` /
|
|
|
`bitonalFromRGBA` / `rasterToEscPos` for **GS v 0** 1-bit bit-image receipts). Back
|
|
|
office: **Bills rows are clickable** → the A4 invoice opens in a print-ready tab (Blob
|
|
|
URL, own "Print A4" button); **Catalog → Label Printing is now LIVE** (prints labels for
|
|
|
current items, columns + label size configurable); Purchase Entry offers "Print labels"
|
|
|
for the received lines after a post. POS: a **"Receipt script: ASCII / Raster (Indic)"**
|
|
|
setting (persisted in the printer cfg) routes the commit-time receipt through a
|
|
|
canvas→GS v 0 raster path (`apps/pos/src/raster.ts`) so Devanagari/Malayalam item names
|
|
|
print, sharing one `receiptLines` content model with the ASCII path. Verified 2026-07-10:
|
|
|
**97 tests green** (20 new S2 cases: amountInWordsINR 0/paisa/1/lakh/crore, invoice fields
|
|
|
+ amount-in-words + IGST switch, EAN-13 structure + checksum, label sheet, raster
|
|
|
bit-packing), typecheck clean on root + all three apps, all three apps build, store-server
|
|
|
rebuilt and restarted. Drove the back office in a real browser (thomas/sims): Bills → click
|
|
|
`ST1C2/26-000001` → A4 invoice rendered with **"Rupees Six Hundred Only"**, CGST/SGST
|
|
|
breakup and place-of-supply Kerala; Catalog → Label Printing → sheet rendered **7 real
|
|
|
EAN-13 SVG symbols**; and the raster canvas pipeline was exercised in-browser (384-px 2"
|
|
|
head, Indic glyphs drew 1,575 ink pixels via the OS font, GS v 0 header bytes correct).
|
|
|
**Honest caveats:** raster receipts are unit- and canvas-verified but the physical thermal
|
|
|
output needs a real 80 mm printer; canvas Indic glyphs fall back to the shop PC's OS fonts
|
|
|
(Windows ships Nirmala UI) — bundled canvas webfonts are a follow-up; the demo seed
|
|
|
barcodes carry placeholder check digits, so `ean13Svg` encodes stored 13-digit codes
|
|
|
as-is (a strict `normalizeEan13` validator is exported for real catalog input).
|
|
|
|
|
|
**B2B at the counter (S3) — done 2026-07-10.** A registered buyer's GSTIN now flows from
|
|
|
attach → engine → document. New pure `deriveSupply(customer, storeState)` in `@sims/domain`
|
|
|
is the single place-of-supply rule both surfaces run: no GSTIN → B2C, place of supply = the
|
|
|
store (CGST/SGST); a GSTIN → B2B, place of supply = the buyer's state (its first two digits,
|
|
|
or an explicit stateCode), inter-state ⇒ IGST. **Parties gained B2B identity**: `createCustomer`
|
|
|
takes an optional GSTIN, **validated server-side** with `validateGstin` (bad checksum/shape
|
|
|
rejected with E-1401/E-1402), the state code derived from the GSTIN when absent; the back
|
|
|
office **Customers page has a GSTIN column** and the POS **F6 attach modal has an optional
|
|
|
"GSTIN (B2B)" field with live checksum/state feedback**. **Billing is now place-of-supply
|
|
|
aware**: the POS `computeBill` ctx uses the attached customer's state (falls back to the
|
|
|
store), and `commitBill` **does not trust the client** — it re-reads the customer row, derives
|
|
|
place of supply itself, recomputes, and the paisa-agreement check (R5) stays. The **immutable
|
|
|
bill snapshots the buyer** (`buyer_gstin`, `place_of_supply` — added to the CREATE TABLE and
|
|
|
back-filled on the running dev DB by a PRAGMA-guarded `ALTER TABLE` migrate step in `db.ts`);
|
|
|
`listBills` returns them. **Documents follow**: the thermal receipt prints a buyer GSTIN line
|
|
|
(IGST rows on the existing inter-state path), the A4 invoice buyer block fills GSTIN +
|
|
|
place-of-supply from the bill (closing the S2 caveat), and the POS totals panel shows an
|
|
|
**"IGST · inter-state"** badge plus a B2B chip on the customer strip. Verified 2026-07-10:
|
|
|
**110 tests pass** (4 new: `deriveSupply` no-GSTIN/intra/inter, B2B receipt buyer line),
|
|
|
typecheck clean on root + all three apps, all three apps build, store-server rebuilt and
|
|
|
restarted (`/api/health` ok, migration added both columns to the 3-bill dev DB). Drove both
|
|
|
UIs in a real browser: POS login (Ramesh/4728, Skip count) → F6 → created **Anand Traders**
|
|
|
(phone 9876543210) with Maharashtra GSTIN **`27ABCDE1234F1Z0`** (body `27ABCDE1234F1Z` +
|
|
|
`gstinCheckDigit` = `0`) → the modal showed "place of supply state 27 — GSTIN valid", the
|
|
|
strip showed the B2B chip, the totals showed **IGST · inter-state** → billed Surf Excel
|
|
|
(GST ₹22.12 as IGST) → committed **ST1C2/26-000004**. Server row confirms `buyer_gstin`,
|
|
|
`place_of_supply='27'`, `igst_paise=2212`, `cgst=sgst=0`, payable ₹145.00 (client/server
|
|
|
agreed to the paisa). Back office → Bills → the row opened its A4 **Tax Invoice** with
|
|
|
**Billed To Anand Traders / GSTIN 27ABCDE1234F1Z0 / State 27 — Maharashtra**, Place of Supply
|
|
|
**"27 — Maharashtra"**, and an **IGST** tax row (no CGST/SGST); Customers shows the GSTIN column.
|
|
|
**Honest caveats:** the buyer GSTIN receipt line is unit-tested only (no physical printer here);
|
|
|
the seed store's state 32 resolves as "Kerala" in the invoice state map though it is named
|
|
|
T.Nagar — a seed-data quirk unrelated to S3; the e-Invoice/IRN (GSP) hook remains P2 (S3 stops
|
|
|
at the document, per doc 17 §S3). Unrelated pre-existing failure: `apps/hq/test/gmail.test.ts`
|
|
|
imports a not-yet-added `../src/gmail` (HQ-console in-flight work, untouched by S3).
|
|
|
|
|
|
## Purchase Entry — LIVE (the second crown jewel)
|
|
|
|
|
|
`/purchases/entry`: supplier type-ahead → invoice no/date → scan-grammar line entry
|
|
|
(barcode / code / name / `48*surf`) → qty→cost cell flow (Enter-advances) → last-cost
|
|
|
prefill + cost-changed price-revision prompt → printed-total cross-check → F12 post.
|
|
|
Server: transactional commit (stock IN, price/MRP updates + audit, duplicate-invoice
|
|
|
rejection, totals re-verification). Purchase List page live too. Verified end-to-end:
|
|
|
HD/2651 ₹6,988.83 posted, duplicate rejected, stock 118→166. Full P1/P2 backlog:
|
|
|
docs/13-SPEC-PURCHASE-ENTRY.md.
|
|
|
|
|
|
## Back office — 46 screens (`/`) — WIREFRAME (sample data)
|
|
|
|
|
|
Login (1) + Dashboard (1, custom) + 44 module pages:
|
|
|
|
|
|
| Module | Pages | Count |
|
|
|
|--------|-------|------:|
|
|
|
| Catalog | Items · Categories · Price Lists · Schemes & Offers · Label Printing | 5 |
|
|
|
| Sales | Bills · Returns & Credit Notes · Customers · Khata & Collections · Loyalty | 5 |
|
|
|
| Purchases | Purchase List · Purchase Entry · **Purchase Inbox** (custom split-review) · Suppliers · GSTR-2B Reconciliation | 5 |
|
|
|
| Inventory | Stock · Adjustments · Transfers · Stock Take · Expiry Dashboard | 5 |
|
|
|
| Accounting | Vouchers · Ledgers · Day Book · P&L/Balance Sheet · Tally Export | 5 |
|
|
|
| GST | GSTR-1 · GSTR-3B · e-Invoice Queue · e-Way Bills · HSN Summary | 5 |
|
|
|
| Reports | Sales Reports · Margins · Stock Aging · Day-End Digest | 4 |
|
|
|
| Admin | **Users & Roles** (custom matrix) · Stores & Counters · **Settings** (custom hierarchy) · Print Templates · Integrations · Subscription & Plan · Audit Log · Sync & Devices · Backups · Data Import | 10 |
|
|
|
|
|
|
**6 pages are now LIVE on real data**: Items (list + search + New Item — billable at the
|
|
|
counter immediately), Customers, Bills (rows open the printable A4 GST invoice — S2),
|
|
|
Stock (derived from movements), Audit Log, and Label Printing (barcode/MRP sheets — S2).
|
|
|
Login is real (server-verified). 35 pages render from the data-driven registry; 4 are
|
|
|
custom wireframes (Dashboard, Purchase Inbox, Settings hierarchy, Users & Roles).
|
|
|
Edition-locked pages render as upsells; everything themed and role/edition-aware.
|
|
|
|
|
|
## Store-server (`apps/store-server`) — RUNNING SERVICE with DATA LAYER
|
|
|
|
|
|
Serves both web apps + `POST /api/print` + `GET /api/health`, and now the data API:
|
|
|
`/api/auth/pin`, `/api/auth/login` (scrypt + lockout), `/api/bootstrap`, `/api/items`
|
|
|
(GET/POST), `/api/parties` (GET/POST), `/api/bills` (GET/POST with engine
|
|
|
re-verification), `/api/stock`, `/api/audit`. SQLite (WAL) at `apps/store-server/data/`
|
|
|
behind portable repositories (`src/repos.ts`) — **the Oracle 12c adapter reimplements
|
|
|
those signatures when the Classic DDL arrives; nothing above them changes.**
|
|
|
Port 5181 (8080–85 are Windows-reserved).
|
|
|
|
|
|
## Not built yet (per roadmap)
|
|
|
|
|
|
- Store-server data layer: Oracle repository, real auth, item/bill APIs → needs the
|
|
|
**Classic Oracle DDL** (→ docs/12-DATA-MODEL.md, to be written once the DDL arrives)
|
|
|
- Owner mobile app (Phase 3) · Customer display · HQ console (doc 11): ops slice now spec'd to **start early** as `apps/hq` (docs/14-SPEC-HQ-CONSOLE.md, D15); fleet half still waits for the cloud tier (D14)
|
|
|
- Purchase Inbox backend (AI parsing) · e-Invoice/e-Way GSP · WhatsApp/UPI integrations
|
|
|
- Sync agent + cloud tier (D14: deliberately later) · Classic migration tool
|
|
|
- Onboarding wizard · i18n content (mechanism exists, Hindi/Malayalam strings pending)
|
|
|
|
|
|
## Decisions: resolved vs open
|
|
|
|
|
|
Resolved: D2 web-first · D5 DB-driven plans/prices · D8 Android later · D12 Oracle 12c
|
|
|
for now · D14 local-first-then-cloud · D15 HQ ops console early, in-monorepo.
|
|
|
**Open: D1 product name · D4 GSP vendor · D5 ₹ amounts · D11 builder headcount · D13 helpdesk.**
|
|
|
|
|
|
## How to run
|
|
|
|
|
|
```
|
|
|
cd apps/store-server && npm start # http://localhost:5181 (/ and /pos/)
|
|
|
npm test && npm run typecheck # from repo root
|
|
|
```
|