The owner ruled that the reference also ran a retail storefront (B2C
single-customer shop) and that the wallet, cashback and
ConfigStatic['percent'] belong to it. These are preserved exactly as they
are: nothing deleted or disabled on either side, no parity work, listed in
the matrices with status "preserved — out of current scope" and no gap or
work row. Shared surfaces (product, variant, price, order, customer) need
the owner's approval before a change that could reach the storefront.
Classifying the endpoints for that split surfaced a counting error in the
Phase 0 inventory: routes/admin.php has 151 live explicit routes, not the
117 recorded, so the reference total is 412 and not 369 (logged DOC-01).
get-analytics-tak reads SingleCustomerOrders, so it is a storefront route
despite its neutral name (logged DOC-02).
Of the 412 reference endpoints, 109 are retail storefront, 15 are web.php
dev/ops closures, and 288 remain in scope (218 admin + 70 B2B app).
[ref: henza_server/routes/{admin,api,customer,site,web}.php]
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
149 lines
8.7 KiB
Markdown
149 lines
8.7 KiB
Markdown
# PROGRESS — Reference Parity Project
|
||
|
||
> Read this file FIRST at the start of every session and after every `/compact`.
|
||
> Rules live in `CLAUDE.md` (workspace root). Method lives in the `reference-parity-audit` skill.
|
||
|
||
## Current state
|
||
|
||
| Field | Value |
|
||
|---|---|
|
||
| **Phase** | Security hotfix — **stages 1 and 2 ✅ complete** |
|
||
| **Branch** | `parity/security-hotfix` |
|
||
| **Status** | Both stages done and verified live: **46/46** smoke assertions, **7/7** jest unit tests, typecheck + build clean on all three projects. |
|
||
| **Next step** | Show the user the **D0 (firstName/lastName)** and **`percent`** migrations for approval, then start `01-ADMIN-MATRIX.md` field-by-field (D12) |
|
||
| **Blocked on** | user approval of those two migrations before they run |
|
||
| **Last updated** | 2026-09-22 |
|
||
|
||
### Security hotfix — final result
|
||
|
||
| Route | Was | Now |
|
||
|---|---|---|
|
||
| `GET accounting/customers/:id/balance` | public | admin, rid `wallet.list` |
|
||
| `POST accounting/customers/:id/reconcile` | public **write** | admin, rid `wallet.charge` |
|
||
| `POST accounting/repayments/gold` | public **write** | admin, rid `document.create` |
|
||
| `POST accounting/repayments/rial` | public **write** | admin, rid `document.create` |
|
||
| `POST accounting/entries/:id/confirm` | public **write** | admin, rid `document.list.confirm` |
|
||
| `PATCH orders/:id/status` | public **write** | admin, rid `order.list.sts.submit` |
|
||
| `GET customers` | every customer, 18 fields | **route deleted** |
|
||
| `GET customers/:id` | public | **route deleted** → `GET customer/me` (token) |
|
||
| `GET customers/:id/orders` | public | **route deleted** → `GET customer/me/orders` (token) |
|
||
| `GET customers/:id/ledger-entries` | public | **route deleted** → `GET customer/me/ledger-entries` (token) |
|
||
| — | — | **new** `POST customer/auth/request-otp` · `verify-otp` |
|
||
|
||
Still public by design: `catalog/products`, `pricing/latest`, `home/slider`,
|
||
`uploads/:filename` (ruling T2), and `POST /orders` + `GET /orders/:id` — the
|
||
last two are logged as SEC-02/B1-B2 and want a customer-token pass of their own.
|
||
|
||
**Tests**
|
||
- `apps/api/scripts/auth-smoke.sh` (live server): **46 passed, 0 failed**.
|
||
Run it with `API=http://localhost:3000/api CODE_LOG=/path/to/api.log`.
|
||
- `pnpm test` in `apps/api` (jest, newly installed): **7 passed**.
|
||
- Typecheck: api ✅ · user-app ✅ · user-app-v2 ✅. Both apps build ✅.
|
||
|
||
### ⚠️ Correction worth remembering
|
||
|
||
Stage 1 reported the customer-book leak as "mitigated" after narrowing the
|
||
`GET /customers` field list. That was wrong: the response still returned every
|
||
customer **id**, and the three `/customers/:id*` routes were still public, so the
|
||
full data was still two requests away. The user caught it. Stage 2 deleted the
|
||
whole id-bearing surface instead. **Narrowing a response is not closing a leak
|
||
while the id and the id-addressed routes remain.**
|
||
|
||
## ⚠️ Read `DECISIONS.md` before doing anything
|
||
|
||
All user rulings live there (D0–D13). Highlights that change how work is done:
|
||
|
||
- **D12 — the matrix is FIELD-BY-FIELD, not page-by-page**, and a 4-step
|
||
round-trip test per entity is mandatory. Both rules were added to the
|
||
`reference-parity-audit` skill (Step C rule C0, Step E rule E0).
|
||
- **D0** — split `fullName` into `firstName`/`lastName` via an additive migration;
|
||
**show the migration and get approval before running it**.
|
||
- **D4** — never port broken behaviour, but match calculations/rounding/messages
|
||
exactly; annotate each non-replication «عمداً تکرار نشد — ISSUE #n».
|
||
- **D2** — target-only features get status ➕ and are left alone.
|
||
- **D13** — the **retail storefront** (48 `customer.php` + 30 `site.php` + 31
|
||
storefront-management admin routes = **109 of the reference's 412 endpoints**)
|
||
is **preserved but out of scope**: status ⏸️, no gap row, no work row, nothing
|
||
deleted or disabled. **288 endpoints stay in scope** (218 admin + 70 B2B app).
|
||
Note: the reference total is **412, not 369** — `admin.php` was undercounted
|
||
in Phase 0; corrected in `00-INVENTORY.md` §2.1.
|
||
Ask before changing shared surfaces (product, variant, price, order, customer).
|
||
|
||
## Phase checklist
|
||
|
||
- [x] P0.1 — All 3 reference folders readable (`henza_server`, `henza_admin`, `henza_pwa`)
|
||
- [x] P0.2 — Branch `parity/phase-0` + `docs/parity/` created
|
||
- [x] P0.3 — Inventory all 7 projects → `_inv-*.md` (8111 lines total) → consolidated in `00-INVENTORY.md`
|
||
- [x] P0.4 — Mapping tables, missing list, extra list (`00-INVENTORY.md` §2–4)
|
||
- [x] P0.5 — Draft `PROTECTED.md`
|
||
- [x] **P0.6 — GATE passed** — `PROTECTED.md` confirmed, D0–D12 recorded
|
||
- [x] P0.7 — Urgent traces delivered: `percent`, `@Public` list, login leak
|
||
- [x] P0.8 — Skill updated with rules C0 (field-by-field) and E0 (round-trip test)
|
||
- [ ] **P1.0 — user approves: 5 ledger routes, `percent` column, D0 migration** ← HERE
|
||
- [ ] Phase 1 — Admin parity (branch `parity/phase-1-admin`, `01-ADMIN-MATRIX.md`)
|
||
- [ ] **GATE:** "Admin parity is complete and tested. Shall we move on to the apps (user-app & user-app-v2)?"
|
||
- [ ] Phase 2 — Apps parity (`02-APPS-MATRIX.md`, both user apps)
|
||
- [ ] Phase 3 — Work through `ISSUES.md` (only when user asks)
|
||
|
||
## Answered — see `DECISIONS.md` for the full text
|
||
|
||
| # | Ruling |
|
||
|---|---|
|
||
| D0 | Split `fullName` → `firstName`/`lastName`, additive migration, backfill, sync, report multi-token rows. Approval needed before running. |
|
||
| D1 | Target's stricter auth stands. Build the "reference operation ↔ rid" matrix; explain 172 vs 174. |
|
||
| D2 | Audit only what the reference has. Target-only features → ➕, untouched. |
|
||
| D3 | Build the 5 missing screens in Phase 2, both apps, plus analytics, `isLoaded`/`sts_verify_step_two` guards, guest toast+home, and v2's cart ceiling. |
|
||
| D4 | Don't port breakage; do match calculations/rounding/messages. Unit-test the price formula. |
|
||
| D5 | Leave the stray folder; delete last, own commit. |
|
||
| D6 | **OTP must verify** — parity gap, fix it (plus expiry + attempt limits, which exceed the reference). |
|
||
| D7 | `@Public` ledger routes: list delivered, awaiting approval to protect. |
|
||
| D8 | Login leak: **target**, full customer book incl. national IDs and debts. |
|
||
| D9 | `percent`: trace delivered — **the target's implementation is wrong**. |
|
||
| D10 | Sales expert: keep `AdminUser`; investigate the 5 missing fields; propose `SalesExpertProfile`. |
|
||
| D11 | `singleCustomer.vue` → diff-send. |
|
||
| D12 | **Field-by-field matrix + mandatory round-trip test.** Added to the skill. |
|
||
| D13 | **Retail storefront (wallet, cashback, `ConfigStatic['percent']`) preserved but OUT OF SCOPE.** Nothing deleted or disabled; no parity work; matrices list it ⏸️ with no gap/work row; ask before touching shared surfaces. |
|
||
|
||
## Open — awaiting user approval
|
||
|
||
1. **Protect the 5 `@Public` ledger-write routes** (`DECISIONS.md` group A).
|
||
2. **`percent` schema change** — add `makingChargeOffsetPercent` to `Customer`.
|
||
3. **D0 migration file** — must be shown and approved before it runs.
|
||
|
||
## Key context (survives context loss)
|
||
|
||
- **Domain correction:** the reference is a **gold-jewellery B2B/B2C e-commerce**
|
||
system, not a gold bank. No buy/sell order book, no spread, no deposit/withdraw,
|
||
no P2P transfer, no KYC levels, no daily limits (only `gram_level`), no payment
|
||
callback route, no cron, no websockets. See `00-INVENTORY.md` §0.
|
||
- **The gold price formula** (Phase 1 row #1) —
|
||
`henza_server/app/Models/ProductVariant.php:183-190`: `+extra%` → `+7%` margin →
|
||
`+9%` VAT **on the non-metal portion only** → `×grams` → `floor(/1000)*1000`.
|
||
Three superseded commented-out variants above it apply VAT to the whole price —
|
||
do **not** port those.
|
||
- **A large prior port already happened** — see `docs/ADMIN-PORT-PLAN.md` and
|
||
`docs/CHANGELOG-PORT-HISTORY.md` (80 views, 25 Nest modules, 29 Prisma models,
|
||
154 admin routes, 144/144 rids). Prior port notes **no view was ever visually
|
||
verified in a browser**.
|
||
- **Stack changed across the port:** Laravel→NestJS/Prisma, Vue2/Vuesax→Vue3/antd.
|
||
Port behaviour, not code style.
|
||
- **Reference scale is inflated:** 541 admin `.vue` files but only ~69 reachable
|
||
(rest are Vuexy template demos); 9 reference router files are dead (not imported);
|
||
~28 legacy Django-era DB tables have no migrations.
|
||
- **v1 ⇄ v2 logic gaps** (5, all v2 behind v1) are parity work, not design:
|
||
`ISSUES.md` MON-01, V2-01..05.
|
||
|
||
## Inventory files (Phase 0 working files)
|
||
|
||
| File | Scope | Lines |
|
||
|---|---|---|
|
||
| `_inv-ref-server.md` | henza_server | 1313 |
|
||
| `_inv-tgt-api.md` | apps/api | 1179 |
|
||
| `_inv-ref-admin.md` | henza_admin | 1705 |
|
||
| `_inv-tgt-admin.md` | apps/admin-panel | 1261 |
|
||
| `_inv-ref-pwa.md` | henza_pwa | 1178 |
|
||
| `_inv-tgt-userapp.md` | apps/user-app | 612 |
|
||
| `_inv-tgt-userappv2.md` | apps/user-app-v2 | 700 |
|
||
|
||
Consolidated into `00-INVENTORY.md`. Issues found: `ISSUES.md` (26 entries).
|