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/docs/06-DECISIONS.md

210 lines
14 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 06 — Open Decisions (founder calls)
Each has a recommendation; none is blocking to *start* Phase 0, all must be locked to *exit* it.
## D1 — Product name & positioning vs "SiMS Classic"
New name, or "SiMS" with a generation marker? **Recommendation:** keep the SiMS brand
(it carries your existing base's trust), market the rebuild as *SiMS Next* or *SiMS 2*
the migration pitch is easier when it's "your SiMS, reborn" rather than a stranger.
## D2 — POS shell ✅ REVISED (2026-07-09): web app first
Founder call: **the product is a web app.** POS and back office are browser apps served
by the store-server on LAN (later the same apps served from cloud). Zero installs at
counters — the Oracle Forms deployment model, modernized. Consequences:
- **Hardware routes through the store-server**: web POS → HTTP → store-server → ESC/POS
over TCP 9100 (printers + drawer). Scanners are keyboard-wedge (browser-native).
Weighing scales: Web Serial API (Chrome/Edge) or a store-server serial bridge — spike item.
- **Electron shell is kept as an optional kiosk wrapper** (~100 lines, already built) for
counters that need true offline billing or locked-down kiosk mode; same web bundle inside.
- **Browser key conflicts** (F11 fullscreen, F12 devtools): keymaps are DB config —
the web preset leans on Numpad+ for cash-close and Chrome kiosk mode at counters.
- Offline story for browser POS (PWA + IndexedDB bill queue) is a Phase-2 hardening item;
v1 counters depend on the store-server over LAN, exactly like Classic depends on the
Oracle server today.
## D3 — Sync: buy (PowerSync / ElectricSQL) vs build (custom outbox)
**Recommendation:** spike both in Phase 0; prefer buying the transport if it handles
per-tenant partial sync cleanly. The domain rules (immutable documents, per-counter
series, derived stock) are ours regardless.
## D4 — GSP vendor for e-Invoice / e-Way
ClearTax vs Masters India vs others. Decide on: API reliability, per-document pricing at
your volume, sandbox quality. **Recommendation:** shortlist two, integrate behind our own
interface so switching is a config change.
## D5 — Pricing tiers ✅ RESOLVED (direction)
Founder call (2026-07-08): prices aren't fixed yet and must be **addable/changeable in the
DB** so they reflect in the app without a release. Design adopted: `plan`, `plan_feature`,
and `price_book` tables (see [07-DB-AND-CONFIG.md](07-DB-AND-CONFIG.md) §2) — new plans and
₹ amounts are rows, the license token is a signed snapshot of them. Actual ₹ amounts get
decided whenever you're ready; nothing blocks on it.
## D6 — Accounting depth at launch
Full books (P&L/BS) in-app from Phase 2, or ledgers + **Tally export** first?
**Recommendation:** ledgers + Tally export in Phase 2, full statements later. Most shops'
CAs live in Tally; export buys goodwill for a fraction of the effort. Classic parity data
should confirm how many customers actually used full accounting.
## D7 — Store Hub (LAN sync box) as requirement or option
Multi-counter stores need counters to see shared stock/bills when internet is down.
**Recommendation:** ship Store Hub as an optional role of the main billing PC
(a checkbox, not a separate device) from Phase 2.
## D8 — Android POS terminals (Sunmi-style) in scope? ✅ RESOLVED
Settled by the market-architecture brainstorm (08-MARKET-ARCHITECTURES.md, in the Store repo):
every player winning high-throughput counters is desktop + local-first; Android wins
micro-merchants and mobility. **Decision:** v1 = Windows desktop POS; Phase 34 = Android
*companion* app (line-busting, stock-take, approvals) sharing the billing-engine package;
full Android POS only if we later go down-market deliberately. D2 (Electron) stands.
## D9 — Purchase Inbox channels at launch
Email-in + manual upload first; WhatsApp-forward intake later (needs WhatsApp Business
API setup). **Recommendation:** email + upload in Phase 3, WhatsApp intake fast-follow.
## D10 — Cloud region & provider
Any major provider with a Mumbai region works. Deciding inputs: managed-Postgres quality,
egress pricing, your team's familiarity. **Recommendation:** pick boring; revisit never.
## D13 — Support ticketing: build vs integrate (raised by 11-ADMIN-SUPPORT-CONSOLE.md)
The diagnostic bundle, error-code playbooks, and Store 360° are ours; the ticket queue
itself is commodity. **Recommendation:** integrate a boring helpdesk in Phase 2; build
only if the integration fights the error-code workflow.
## D14 — Cloud staging ✅ RESOLVED: local-only first, cloud layered on later
Founder call (2026-07-09): v1 runs fully local (counters + in-store Oracle 12c per D12),
then the cloud tier is added and switched on per store. Accepted consequences: owner app,
Purchase Inbox, WhatsApp, central backup, silent auto-update, and HQ fleet features all
wait for the cloud workstream. Guardrail (non-negotiable): client UUIDs + dormant outbox
rows are written from the first local build, so cloud enablement is switch-on, not
migration. See the staging note atop 04-ROADMAP.md (in the Store repo).
## D11 — Existing team & hiring plan (partially answered)
Founder input (2026-07-08): a support team exists for Classic and continues; the product
principle is a **stable frontend** with changes flowing from backend/DB, handled by the
team — adopted as the config-driven design in [07-DB-AND-CONFIG.md](07-DB-AND-CONFIG.md).
Still needed to calibrate the roadmap: builder headcount and skill mix for the new stack.
## D12 — Local DB engine ✅ RESOLVED (for now): Oracle 12c stays at the store tier
Founder call (2026-07-09): the in-store DB remains **Oracle 12c** — it is already deployed
shared infrastructure with multiple other systems running on it, so for the existing base
the license and operations cost is sunk. Analysis and the adapted design are in
[07-DB-AND-CONFIG.md](07-DB-AND-CONFIG.md) §3.
**Consequences accepted with this call:**
- **D3 resolves to "build"**: off-the-shelf sync engines (PowerSync/ElectricSQL) speak
Postgres/SQLite only — sync is a custom outbox, with outbox tables living in Oracle and
a store-side sync agent draining them to the cloud.
- **Counters still carry a local SQLite buffer** (offline bill queue + item-search cache)
so a counter survives store-server or LAN death — the "counter never stops" goal is
independent of the store engine.
- Backend connects via node-oracledb **thin mode** (no Oracle client install needed;
supports 12.1+).
- **New code stays engine-portable**: repository/DAL layer, portable SQL by default,
Oracle-only features only with a written justification — "for now" must stay cheap to
revisit.
**Revisit triggers (any one reopens this decision):** onboarding NEW customers who don't
already have Oracle in-store (license cost is no longer sunk); the other systems sharing
the DB migrate off 12c or upgrade (19c/23ai); a security/compliance audit flags the
unpatched engine; or sync friction in Phase 01 proves materially costlier than an
engine port. Risk register entries in 07 §3 remain on record.
## D15 — Internal HQ ops console: build now ✅ RESOLVED (2026-07-09; split to its own repo 2026-07-16)
Founder call (2026-07-09): an **internal tool for running our own ~300-client business**
quotations/proforma/invoices in minutes, recurring billing with auto/manual email
reminders, per-module payment + AMC tracking, per-client AWS usage & cost, and the full
client interaction memory (installation, training, visits, calls, meetings, sales
outcomes) in a system instead of heads. **Decision:** start the
[doc-11 HQ Console](11-ADMIN-SUPPORT-CONSOLE.md) **early**, as `apps/hq` (built in the shared
monorepo at the time; **split into its own repo `C:/SiMS/hq` on 2026-07-16**) —
spec in [14-SPEC-HQ-CONSOLE.md](14-SPEC-HQ-CONSOLE.md). Cloud-hosted **vendor-side**
(D14 untouched — the store product stays local); SQLite behind portable repositories with
S3 backup; **Gmail API** sending from the company mailbox; automated AWS cost attribution;
**full replacement of the internal Oracle APEX app** — one-time verified migration, then
APEX goes read-only, no double entry. Reuses `@sims/billing-engine` (our invoices are GST
documents), `@sims/domain` (paise money, FY, doc series), `@sims/ui`, and the `@sims/auth`
patterns.
**Alternatives rejected:** fully separate codebase (rebuilds the engine/UI/patterns and
builds doc-11 twice); off-the-shelf CRM (no module-level tracking against our catalog, no
AWS attribution, no path to the fleet console).
**Consequences accepted:**
- This shipped as **two deployables with different release rhythms**. On **2026-07-16 they
were split into two independent repos** (`C:/SiMS/hq` and `C:/SiMS/store`) — separate
histories, deploy scripts, and versioning; the shared packages were forked into each.
- Doc-11's phase mapping **splits**: the ops slice ships early (HQ-1…3 in
[14-SPEC-HQ-CONSOLE.md](14-SPEC-HQ-CONSOLE.md) §6); the fleet half is unchanged and
waits for the cloud tier per D14.
- **One repo, two runtime-independent applications** (founder re-confirmed 2026-07-10
after weighing a full split): separate servers, DBs, builds and deploys; only source
packages are shared. Guardrails: a bundle-isolation check in the test suite (store
bundle contains no HQ code and vice versa), per-app version tags, and the rule that
**HQ's DB is the cloud tier's nucleus** — the tenant registry is born from HQ's client
table (same UUIDs, promoted once), never built beside it. **Postgres locked as the
production engine (founder, 2026-07-10)** — swap runs as its own task after HQ-2,
before the real-data import, so live data never migrates engines; SQLite stays the
dev/test engine behind the same repositories. Physical repo split stays a cheap,
reversible option if practice ever demands it.
## D16 — Quote-to-close funnel slice (2026-07-17)
Founder-approved design in
`superpowers/specs/2026-07-17-quote-to-close-funnel-design.md`; decisions locked
with it:
- **Employee foundation rides `staff_user`.** The Employee entity is **backed by the
existing `staff_user` table** — renaming it would churn `session.staff_id`,
`interaction.staff_id` and audit entity names for zero gain; "Employee" lives in the
repo/API/UI vocabulary only. Roles widened to `owner | manager | staff` via a guarded,
transactional table rebuild (SQLite cannot ALTER a CHECK); the enum is validated in
the repo. Session hardening rode along: `verifySession` requires `active=1`, and
deactivation / password reset purge sessions in the same transaction.
- **Chase-list over kanban (option B).** The pipeline is **one cross-client ranked
list** — most-overdue on top, colour bands, an explicit next action per row,
`All | Mine | Overdue | Lost` filters — not a kanban board.
- **Derived pipeline stages, zero new pipeline storage.** Stage (Enquiry / New
Project / Quoted-Waiting / Won / Lost) is **never stored** — computed from
`client.status` + the client's latest live quotation; age anchors on the first
`sent` document event. The only new column anywhere in the slice is
`client.owner_id` (a bare lead has no creator to derive an owner from — A4).
- **Escalating owner+client reminders on the dated `reminder_schedule` table.** New
sendable `quote_followup` rule kind: the queued reminder row **is** the owning
employee's nudge (owner derived `doc_id → document.created_by`), and its send **is**
the client email with a public share link. Cadence **and** message text are dated
config rows (rule 3) resolved by `resolveSchedule` — defaults 3/7/14 (quote) and
7/15/30 (invoice) ship as seeded rows plus code-constant fallbacks, so a cadence
change is a new dated row, never a release. Catch-up fires only the highest crossed
milestone; accept / lose / convert / cancel dismisses open nudges per-row (audited)
in the same transaction; previews resolve shares read-only — minting happens only in
the send path (60-day expiry, live links reused).
- **Convert recompute + duplicate-invoice guard (F3/F4).** PROFORMA → INVOICE rebuilds
the carried lines through `computeBill` on the **invoice's own `doc_date`** (a tax
invoice uses the rate that is law on its issue date), and `convertDocument` rejects
any source with a live (non-cancelled) forward child — one sale, one invoice number.
- **Proforma supersede matrix.** Corrections stay firewalled by document type: a draft
PI is simply re-drafted; an issued, unallocated PI may cancel (number stays consumed)
and be recreated as a `ref_doc_id`-linked supersede; an issued INVOICE is **never**
recreated — cancel-if-unpaid or credit note only.
- **Assumptions A1A7 (defaulted, flagged in the spec):** A1 module roster balances
support and revenue; A2 employees are a plain table (no org chart); A3 document
owner = `created_by` for v1 (no `document.owner_id` until a handoff feature is
real); A4 `client.owner_id` is the one genuinely new owner column; A5 notify-all
text is owner-typed ad-hoc, not dated config; A6 roster visible to all staff,
notify-all owner/manager only; A7 invoice-overdue anchors on `doc_date` (no
due-date column exists).
**Recorded deviation from the funnel spec's blanket pagination rule (§5 / rule 8):**
`GET /employees` returns the full set with `total` and **no `page`/`pageSize`**
console users are a small bounded set (single-digit headcount), the count is displayed,
and nothing is silently truncated, which is the intent of rule 8. If the team ever
grows past a screenful, paginate it like every other list.
**Delivery state (2026-07-17, final):** all ten phases landed. Phases 16 (employee
foundation + management surface, owner plumbing, dated schedule, pipeline chase-list,
escalating quote follow-up) plus the F3/F4 convert hardening, then 710 in the same
day's second pass: one-click `POST /documents/:id/convert-and-send` (guards → one txn
convert+issue → post-commit email), proforma `POST /documents/:id/supersede`, invoice
`dN` milestone escalation (d7/d15/d30 from the dated schedule, F7 highest-only
catch-up), and the module → client roster (`GET /modules/:id/clients` + CSV export +
owner/manager Notify-all with per-recipient audit). Suite at 293 tests / 57 files.