You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sims-hq/STATUS.md

15 KiB

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 (808085 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