|
|
|
@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
# Split SiMS into two independent projects: HQ and Store
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Date:** 2026-07-16
|
|
|
|
|
|
|
|
**Status:** Approved, pending implementation plan
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Why
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
`C:/SiMS/Store Software` currently holds two products that are not one product.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**HQ** (`apps/hq`, `apps/hq-web`) is internal: our client book, their projects,
|
|
|
|
|
|
|
|
proforma-invoice billing, call tracking, AMC contracts, AWS cost recovery,
|
|
|
|
|
|
|
|
payments and reminders. It is the commercial record of the business.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Store** (`apps/pos`, `apps/store-server`, `apps/backoffice`) is the product we
|
|
|
|
|
|
|
|
sell: GST retail management deployed on client premises.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
They are independent projects and should be developed, deployed and released as
|
|
|
|
|
|
|
|
such:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **Different audiences.** HQ is internal-only; Store ships to ~300 clients.
|
|
|
|
|
|
|
|
- **Different databases.** Two SQLite schemas with no conceptual overlap. HQ:
|
|
|
|
|
|
|
|
`client`, `module`, `document`, `interaction`, `amc_contract`, `aws_usage`,
|
|
|
|
|
|
|
|
`payment`, `reminder`, `staff_user`. Store: `item`, `batch`, `bill`,
|
|
|
|
|
|
|
|
`purchase`, `stock_movement`, `party`, `counter`, `store`, `tenant`. Four
|
|
|
|
|
|
|
|
names collide (`audit_log`, `doc_series`, `setting`, `tax_class`) but they are
|
|
|
|
|
|
|
|
different definitions in different DBs — shared in appearance only.
|
|
|
|
|
|
|
|
- **Different cadences.** Of 75 commits, 60 touch HQ and 2 touch Store. The repo
|
|
|
|
|
|
|
|
is already HQ wearing a Store name.
|
|
|
|
|
|
|
|
- **Confidentiality.** A single tree means every Store branch, contractor
|
|
|
|
|
|
|
|
handout, or repo copy carries our client book and revenue data in its history.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Decisions
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| # | Decision | Choice |
|
|
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
| 1 | Split shape | Two separate git repos |
|
|
|
|
|
|
|
|
| 2 | Shared code | Fork into each repo, trimmed to what each uses |
|
|
|
|
|
|
|
|
| 3 | History | HQ inherits the existing 60 commits; Store starts fresh |
|
|
|
|
|
|
|
|
| 4 | Docs | Trim each side to only what that repo needs |
|
|
|
|
|
|
|
|
| 5 | Repo name | `C:/SiMS/hq` (keep — matches docs/14, DEPLOY-HQ.md, `@sims/hq`, 60 commits) |
|
|
|
|
|
|
|
|
| 6 | Project tracking | Out of scope — split first, build after |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Rejected: a third shared repo and a private npm registry. Both add versioning
|
|
|
|
|
|
|
|
overhead to solve a divergence risk the evidence says does not exist (see below).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Target layout
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
C:/SiMS/
|
|
|
|
|
|
|
|
├── hq/ ← current repo, 60 commits preserved
|
|
|
|
|
|
|
|
│ ├── apps/{hq, hq-web}
|
|
|
|
|
|
|
|
│ ├── packages/{domain, auth, billing-engine, ui} ← trimmed, names kept
|
|
|
|
|
|
|
|
│ ├── docs/ ← HQ-only, renumbered
|
|
|
|
|
|
|
|
│ ├── DEPLOY-HQ.md
|
|
|
|
|
|
|
|
│ └── README.md, STATUS.md ← STATUS.md is new
|
|
|
|
|
|
|
|
└── store/ ← fresh git init
|
|
|
|
|
|
|
|
├── apps/{pos, store-server, backoffice}
|
|
|
|
|
|
|
|
├── packages/{domain, billing-engine, auth, ui, config, scanning, search-core}
|
|
|
|
|
|
|
|
├── docs/ ← the remaining 15 docs
|
|
|
|
|
|
|
|
└── README.md, STATUS.md, BUILDING.md
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Each repo gets its own `package.json` (`sims-hq` / `sims-store`), `tsconfig`,
|
|
|
|
|
|
|
|
`vitest.config`, `.gitignore`, and `CLAUDE.md`. Neither references the other at
|
|
|
|
|
|
|
|
any point — no workspace links, no relative paths across roots, no doc citing a
|
|
|
|
|
|
|
|
file it does not contain.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Shared code: fork, trimmed
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HQ's dependency on shared packages is shallow and fully verified — utilities and
|
|
|
|
|
|
|
|
a design system, no shared schema or business logic.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**HQ keeps the package names** (`@sims/domain`, `@sims/auth`,
|
|
|
|
|
|
|
|
`@sims/billing-engine`, `@sims/ui`) and removes files underneath. Import
|
|
|
|
|
|
|
|
specifiers are unchanged, so **no import statement in HQ's app source changes** —
|
|
|
|
|
|
|
|
not one of `apps/hq`'s 75 files or `apps/hq-web`'s 14. What changes is narrow and
|
|
|
|
|
|
|
|
enumerable: the four `index.ts` barrels (drop the removed `export *` lines), and
|
|
|
|
|
|
|
|
`apps/hq-web`'s `package.json` + `vite.config.ts` (drop the dead `@sims/printing`
|
|
|
|
|
|
|
|
dependency and alias).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Package | HQ keeps | HQ drops |
|
|
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
| `domain` | `ids`, `money`, `business-day`, `doc-series`, `gstin`, `documents` | `tenancy`, `masters`, `batch`, `outbox` |
|
|
|
|
|
|
|
|
| `auth` | `pin` | `lockout`, `permissions`, `session` |
|
|
|
|
|
|
|
|
| `billing-engine` | `compute`, `tax` | `returns`, `gst-returns` (588 lines) |
|
|
|
|
|
|
|
|
| `ui` | all 5 files | — |
|
|
|
|
|
|
|
|
| `printing` | — | whole package |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Store's `packages/` is untouched.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Closure verification (done, 2026-07-16)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Every trim is a clean cut — verified by reading the imports, not by assumption:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- `compute.ts` imports `./tax` and four **types** from `@sims/domain`
|
|
|
|
|
|
|
|
(`BillLine`, `BillTotals`, `Paise`, `IsoDate`) — all in files HQ keeps.
|
|
|
|
|
|
|
|
- `tax.ts` imports only `IsoDate`.
|
|
|
|
|
|
|
|
- `documents.ts` imports only from `money` and `business-day`.
|
|
|
|
|
|
|
|
- `pin.ts` imports only `node:crypto` — no `@sims` deps.
|
|
|
|
|
|
|
|
- `ui` has no `@sims` deps at all (`react` + its own `theme`).
|
|
|
|
|
|
|
|
- Nothing HQ retains imports `tenancy`, `masters`, `batch`, or `outbox`. The
|
|
|
|
|
|
|
|
reverse edges (`batch → business-day`, `masters → money`) are one-way and
|
|
|
|
|
|
|
|
drop cleanly.
|
|
|
|
|
|
|
|
- `@sims/printing` is declared in `apps/hq-web/package.json` and aliased in its
|
|
|
|
|
|
|
|
`vite.config.ts`, but **imported by no source file** — a dead dependency.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Why forking is safe
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The divergence risk for forked code is GST computation. It does not apply:
|
|
|
|
|
|
|
|
**tax rates are dated DB rows, not code.** `tax_class` rows carry
|
|
|
|
|
|
|
|
`effective_from`/`effective_to` and resolve by the bill's business date, with
|
|
|
|
|
|
|
|
the Sept-2025 GST 2.0 slab change cited in `tax.ts` as the precedent. HQ reads
|
|
|
|
|
|
|
|
its own `tax_class` table; Store reads its own. A rate change is a row insert
|
|
|
|
|
|
|
|
per DB, never a code edit.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
What remains in `compute.ts` is stable arithmetic — CGST/SGST/IGST split,
|
|
|
|
|
|
|
|
rounding. It has no reason to change often, and the two products are entitled to
|
|
|
|
|
|
|
|
diverge: HQ invoices software modules, Store bills retail goods.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## History
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The current repo becomes `C:/SiMS/hq`: all 60 HQ commits preserved, then one
|
|
|
|
|
|
|
|
commit removing Store paths. No `filter-repo` surgery.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
`C:/SiMS/store` gets `git init` and one clean initial commit. It loses only 2
|
|
|
|
|
|
|
|
commits (one titled `wip`) and gains a guarantee: **zero HQ bytes in its history,
|
|
|
|
|
|
|
|
permanently.** This is the confidentiality boundary, and it is only real if the
|
|
|
|
|
|
|
|
Store history starts empty.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Docs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Trimmed per repo — each doc cut to what that side actually needs.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**HQ** (renumbered `00`–`04`):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| New | From | Treatment |
|
|
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
| `00-HQ-CONSOLE-SPEC.md` | `14-SPEC-HQ-CONSOLE.md` | As-is |
|
|
|
|
|
|
|
|
| `01-ADMIN-SUPPORT-CONSOLE.md` | `11-ADMIN-SUPPORT-CONSOLE.md` | As-is |
|
|
|
|
|
|
|
|
| `02-PROJECT-RULES.md` | `16-PROJECT-RULES.md` | Trimmed: keep money, config-over-code, document rules; drop counter/POS rules |
|
|
|
|
|
|
|
|
| `03-DECISIONS.md` | `06-DECISIONS.md` | Extract D15 + HQ-only calls |
|
|
|
|
|
|
|
|
| `04-DB-AND-CONFIG.md` | `07-DB-AND-CONFIG.md` | Trimmed: D12 portable-repo pattern, dated tax-class config |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Plus `DEPLOY-HQ.md` as-is, a rewritten `README.md`, and a **new `STATUS.md`**.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Store**: the remaining 15 docs, with D15/HQ references pruned from
|
|
|
|
|
|
|
|
`06-DECISIONS.md` and the HQ rows removed from `README.md`'s doc table.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### HQ has no STATUS.md
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The largest app in the repo (75 files, at go-live per `DEPLOY-HQ.md`) has no
|
|
|
|
|
|
|
|
build-state doc. `STATUS.md` covers only the Store product. HQ's current state
|
|
|
|
|
|
|
|
had to be reconstructed from source and `git log`. Write one during the split,
|
|
|
|
|
|
|
|
covering the built surface: clients, modules and price book, documents
|
|
|
|
|
|
|
|
(QT/PI/INV/CN) with templates/PDF/share links, interactions, AMC, AWS usage,
|
|
|
|
|
|
|
|
payments, recurring plans, reminders with Gmail send and bounce handling,
|
|
|
|
|
|
|
|
reports, dashboard, audit, APEX import, scheduler.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Verification
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The split is done when, for each repo independently:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1. `node_modules` removed and `npm install` run fresh
|
|
|
|
|
|
|
|
2. `npm run typecheck` passes
|
|
|
|
|
|
|
|
3. `npm test` passes
|
|
|
|
|
|
|
|
4. No path resolves outside its own root
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Typecheck is the real proof of the fork closure — a missed dependency fails
|
|
|
|
|
|
|
|
immediately and loudly.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Additionally, confirm the confidentiality boundary holds rather than assuming it:
|
|
|
|
|
|
|
|
searching `C:/SiMS/store/.git` for client, AMC, and AWS-cost terms must return
|
|
|
|
|
|
|
|
nothing.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Out of scope
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **Project tracking.** Listed as a core HQ concept but not built — no `project`
|
|
|
|
|
|
|
|
table, no repo module, no UI. It is a feature to design and build, not code to
|
|
|
|
|
|
|
|
move. Deferred so the split stays mechanical: pure move-and-verify, with no new
|
|
|
|
|
|
|
|
feature to confuse a failure with.
|
|
|
|
|
|
|
|
- **Conversion reporting.** `client.status` (`lead`/`active`/`dormant`/`lost`)
|
|
|
|
|
|
|
|
exists but has no lead-to-won reporting on it. Wanted alongside the existing
|
|
|
|
|
|
|
|
conversation log (`interaction`), but after the split.
|
|
|
|
|
|
|
|
- **Oracle 12c adapter** (Store, pending Classic DDL) and the **46 wireframe
|
|
|
|
|
|
|
|
back-office screens** (Store) are unaffected by this work.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Risks
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Risk | Mitigation |
|
|
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
|
|
| A trimmed file is needed by something unread | Typecheck on a fresh install in each repo catches it immediately |
|
|
|
|
|
|
|
|
| Store history retains HQ data | Fresh `git init`, verified by searching `.git` |
|
|
|
|
|
|
|
|
| Docs cite files that no longer exist in their repo | Grep each repo's docs for cross-references to removed paths |
|
|
|
|
|
|
|
|
| HQ's dead `@sims/printing` alias breaks its Vite build when dropped | Remove from both `package.json` and `vite.config.ts` together; build verifies |
|