parity(docs): record D13 — retail storefront preserved but out of scope

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>
This commit is contained in:
s.esmaeili
2026-09-22 07:23:48 +03:30
co-authored by Claude Opus 5
parent 024e3dd045
commit c71574b1d2
5 changed files with 158 additions and 6 deletions
+38 -5
View File
@@ -73,17 +73,50 @@ the *target* has something bank-like, it is an intentional addition, not a gap.
| Metric | `henza_server` | `apps/api` |
|---|---|---|
| Routes / endpoints | **369** (admin 215, api 70, customer 44, site 30, web 10) | **179** |
| Routes / endpoints | **412** (admin 249, api 70, customer 48, site 30, web 15) — corrected, see below | **179** |
| Controllers | 46 | 25 |
| Models | 57 files (34 migrated tables + ~28 legacy Django-era tables) | 29 Prisma models |
| Enums | PHP static arrays | 14 Prisma enums |
| DTO / validation | **zero FormRequests**, all inline, several endpoints unvalidated | 8 DTO classes; most admin bodies untyped `Record<string, unknown>` |
| Seeders | 1, **empty** | `prisma/seed.ts` — user types, 144 rids, admins |
The 369 → 179 drop is expected, not automatically a gap: the reference counts
The 412 → 179 drop is expected, not automatically a gap: the reference counts
`Route::resource` expansions, ~28 dead legacy tables, a `site.php` storefront and
a `web.php` that the monorepo replaces with its own apps. **Which of the 369 are
genuinely unmapped is Phase 1 / Phase 2 matrix work**, per route group.
a `web.php` that the monorepo replaces with its own apps. **Which of the in-scope
routes are genuinely unmapped is Phase 1 / Phase 2 matrix work**, per route group.
**⚠️ Count correction (2026-09-22).** The **369** reported throughout Phase 0 is
**wrong**; the real total is **412**. Recounting the
live routes (excluding the 6 commented-out ones, expanding `Route::resource` ×7
and `Route::match(['get','post'])` ×2) gives **412**. The error was in
`admin.php`: §3.6 of `_inv-ref-server.md` recorded 117 explicit routes where the
file actually has **151**, so admin is **249**, not 215. Verified with
`grep -E "^\s*Route::(get|post|…)" | grep -v "^\s*//"` per file.
| File | Explicit | `match` ×2 | `resource` ×7 | Total |
|---|---|---|---|---|
| `admin.php` | 151 | 0 | 14 → 98 | **249** |
| `api.php` | 61 | 1 → 2 | 1 → 7 | **70** |
| `customer.php` | 18 | 1 → 2 | 4 → 28 | **48** |
| `site.php` | 30 | 0 | 0 | **30** |
| `web.php` | 13 | 1 → 2 | 0 | **15** |
| **Total** | | | | **412** |
**Scope split (D13, 2026-09-22).** The retail storefront is preserved but out of
scope, so the 412 divide as follows:
| Group | Endpoints | Scope |
|---|---|---|
| Admin panel — B2B | 218 | ✅ Phase 1 |
| B2B mobile app (`api.php`) | 70 | ✅ Phase 2 |
| Retail storefront — customer API (`customer.php`) | 48 | ⏸️ preserved, out of scope |
| Retail storefront — public site API (`site.php`) | 30 | ⏸️ preserved, out of scope |
| Retail storefront — admin management (wallet 6, `single` 15, `comment` 9, `get-analytics-tak` 1) | 31 | ⏸️ preserved, out of scope |
| `web.php` dev/ops closures, price cron, redirects | 15 | ⏸️ out of scope (dead/dev) |
| **Total** | **412** | **288 in scope · 124 out** |
**109 of the 412 are retail storefront**; 15 more are `web.php` dev/ops. Full
classification with evidence: `DECISIONS.md` § D13.
### 2.2 Core business logic — where each rule lives
@@ -238,7 +271,7 @@ in `HomeView` and `DashboardView` only.
## 5. What Phase 0 did NOT determine (goes to Phase 1 / Phase 2)
- Route-by-route mapping of the reference's 369 backend routes onto the target's 179.
- Route-by-route mapping of the reference's 412 backend routes (288 in scope after D13) onto the target's 179.
- Field-by-field comparison of request/response shapes per endpoint.
- Per-view form/validation/table comparison for the ~69 live admin views.
- Whether the target's gold-price implementation matches `ProductVariant.php:183-190`
+70
View File
@@ -459,3 +459,73 @@ now exists, so they can be closed, but `CreateOrderDto` carries `customerId` in
the body and changing that is an API-contract change touching both apps' cart
flow. Left for the Phase 2 apps work rather than widened into this hotfix.
Tracked as SEC-02 (B1/B2) in `ISSUES.md`.
---
# D13 — Retail storefront: preserved, but out of scope (2026-09-22)
**Owner's ruling.** The reference also contained a **retail storefront**
(single-customer / B2C shop). The **wallet**, **cashback** and
`ConfigStatic['percent']` belong to that storefront, not to the B2B gold
business this project is porting.
## The rule
1. **Nothing is deleted or disabled.** No code, table, column, endpoint, setting
or data belonging to the storefront may be removed or switched off — neither
in the target nor in the reference. Whatever exists in the target today stays
exactly as it is.
2. **No work is done on it.** No parity, no fixes, no refactoring.
3. **In the matrices** these items are listed with status
**⏸️ حفظ‌شده — خارج از کار فعلی** (preserved — out of current scope) so they
remain visible, but **no gap row and no work row is created for them**.
4. **Shared surfaces need a heads-up.** If fixing something shared — product,
variant, price, order, customer — would affect the storefront, **ask the
owner before changing it**, so nothing in the storefront breaks.
5. **No guessing.** If it is unclear whether something is storefront-only or
shared, **list it and ask**.
## Status legend addition
| Symbol | Meaning |
|---|---|
| ⏸️ | Preserved — out of current scope (D13). Listed for visibility only; no gap row, no work row. |
## Endpoint classification — where the reference endpoints go
**The previously reported total of 369 was wrong; the real figure is 412.**
Recounting the live routes per file (excluding the 6 commented-out ones,
expanding `Route::resource` ×7 and `Route::match(['get','post'])` ×2) shows
`admin.php` has **151** explicit routes, not the 117 recorded in
`_inv-ref-server.md` §3.6 — so admin is **249**, not 215. Corrected in
`00-INVENTORY.md` §2.1.
| Surface | Route file(s) | Endpoints | In scope? |
|---|---|---|---|
| Admin panel — B2B | `admin.php` minus the storefront block below | **218** | ✅ in scope (Phase 1) |
| Admin panel — storefront management | `admin.php`: wallet 6, `single*` 8, comment 2, `get-analytics-tak` 1 explicit + `single` resource 7 + `comment` resource 7 | **31** | ⏸️ preserved, out of scope |
| B2B mobile app | `api.php` | **70** | ✅ in scope (Phase 2) |
| Retail storefront — customer API | `customer.php` | **48** | ⏸️ preserved, out of scope |
| Retail storefront — public site API | `site.php` | **30** | ⏸️ preserved, out of scope |
| Dev/ops closures, price cron, redirects | `web.php` | **15** | ⏸️ out of scope (dead/dev — see DEAD-\* in `ISSUES.md`) |
| **Total** | | **412** | |
**Result: 109 of the 412 endpoints belong to the retail storefront**
(48 `customer.php` + 30 `site.php` + 30 storefront management inside
`admin.php`), **15 more are dev/ops closures in `web.php`**, and
**288 remain in scope** (218 admin + 70 B2B app).
Evidence for the storefront classification:
| Block | Where | Why it is storefront |
|---|---|---|
| `routes/customer.php` (prefix `cu`) | whole file | guarded by `auth:api-singleCustomer`; cart, fav, address, comment, payment gateway, `customer/wallet` |
| `routes/site.php` (prefix `site`) | whole file | public shop front: `Site\SiteController`, `Site\CollectionController`, `Api\SiteController` |
| `admin/wallet/*` | `admin.php:186-191` | `Adm\WalletController` — cashback percent, charge, config |
| `admin/single*` | `admin.php:35,59,60,194-199` | `Adm\SingleCustomerController` — the retail customer's orders, transactions, wallet, report |
| `admin/comment*` | `admin.php:150-152` | `Adm\CommentController` — product reviews, a storefront feature |
The three unrelated `percent` meanings were already separated in
`MIGRATION-STATE.md`. D13 confirms that **`ConfigStatic['percent']` (wallet
cashback) is storefront and out of scope**, while **`customers.percent`
(→ `makingChargeOffsetPercent`) is B2B and stays in scope**.
+7
View File
@@ -97,3 +97,10 @@ Severity: `Critical` (money loss / security / data loss) · `High` · `Medium`
|---|---|---|---|---|---|
| SEC-11 | High | TGT `apps/api/src/modules/customer-auth/customer-auth.service.ts` (deliver) | The OTP message text, **code included**, was persisted to `SmsMessage.body`, which the admin SMS grid lists and full-text searches (`admin-misc.service.ts:135-136`). Any operator holding the `sms` rid could read live codes — defeating the bcrypt hashing of `codeHash`. | Read both files; grid `allowed` includes `body`. | ✅ Fixed in stage 3: the persisted row is a redacted audit line. |
| MON-08 | Medium | TGT `apps/api/src/modules/orders/orders.service.ts:40-52` | `wagePercent`, `discountPercent` and `flatRialCharge` are taken from the **client** on order creation, so a caller can set their own making charge. `rashWeightGrams` is correctly read from the variant. **The reference is worse** — `ApiMobileController.php:538-539,619` takes `rash_weight` from the client too — so this is a partial improvement already. Reported per the user's instruction to flag client-supplied pricing. | REF `:536-545,615-632` vs TGT `:40-52` | Log only (D4). Fix in Phase 2 alongside the price-formula work. |
## Found while classifying scope for D13
| ID | Severity | Where | Description | Evidence | Status |
|---|---|---|---|---|---|
| DOC-01 | Medium | DOC `docs/parity/_inv-ref-server.md:588` (§3.6) | The Phase 0 inventory undercounted `routes/admin.php`: it recorded **117** explicit routes where the file has **151** live ones (6 more are commented out). Every downstream total inherited the error, so the reference endpoint count was reported as **369** when it is **412** (admin 249, not 215). No code is affected — only the planning figures. | `grep -E "^\s*Route::(get\|post\|put\|patch\|delete\|any)" routes/admin.php \| grep -v "^\s*//" \| wc -l` → 151; resources 14×7=98; 151+98=249 | ✅ Corrected in `00-INVENTORY.md` §2.1 and `DECISIONS.md` §D13. `_inv-ref-server.md` left as the raw subagent record. |
| DOC-02 | Low | REF `routes/admin.php:38` | `GET admin/get-analytics-tak` reads `SingleCustomerOrders` (`Adm/AdminController.php:557-579`), so it is a **retail-storefront** endpoint despite its neutral name. A keyword scan for `wallet\|single\|comment` misses it. Noted so the D13 scope split stays correct — anything else named neutrally but querying `SingleCustomerOrders`, `Fav`, `Address`, `Comment` or `Transaction` is storefront too. | `AdminController.php:563` — `SingleCustomerOrders::select(...)` | ✅ Classified as ⏸️ storefront in D13 (the 31st admin storefront route). |
+9 -1
View File
@@ -51,7 +51,7 @@ while the id and the id-addressed routes remain.**
## ⚠️ Read `DECISIONS.md` before doing anything
All user rulings live there (D0–D12). Highlights that change how work is done:
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
@@ -61,6 +61,13 @@ All user rulings live there (D0–D12). Highlights that change how work is done:
- **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
@@ -95,6 +102,7 @@ All user rulings live there (D0–D12). Highlights that change how work is done:
| 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
+34
View File
@@ -235,3 +235,37 @@ it is a keep-out list.
- Unverified OTP — D6
- The public customer list — D8
- `singleCustomer.vue`'s full-form send — D11
---
## H — Retail storefront (D13) — preserved, out of current scope
Owner's ruling of 2026-09-22. The reference also ran a **retail storefront**
(B2C single-customer shop). The **wallet**, **cashback** and
`ConfigStatic['percent']` belong to it.
**Keep-out rule — stronger than "protected":** these are neither fixed nor
touched. Nothing is deleted or disabled on either side; no parity work is done
on them; they appear in the matrices only with status **⏸️ حفظ‌شده — خارج از
کار فعلی**, with no gap row and no work row.
| Surface | Reference | Endpoints |
|---|---|---|
| Customer API (prefix `cu`) | `routes/customer.php` | 48 |
| Public site API (prefix `site`) | `routes/site.php` | 30 |
| Wallet / cashback admin | `admin.php:186-191`, `Adm\WalletController` | 6 |
| Retail-customer admin | `admin.php:35,59,60,194-199` + `single` resource | 15 |
| Retail analytics | `admin.php:38` `get-analytics-tak` → `SingleCustomerOrders` | 1 |
| Product comments admin | `admin.php:150-152` + `comment` resource | 9 |
| **Total** | | **109** |
Whatever of this already exists in `gold-banking-monorepo` stays exactly as it
is — it is not restyled, not refactored, not removed.
**Shared-surface warning.** Product, variant, price, order and customer are
shared between the B2B business and the storefront. Before changing any of
them, check whether the change reaches the storefront and **ask the owner
first** (D13 rule 4). When in doubt whether something is storefront-only or
shared, **list it and ask — never guess** (D13 rule 5).
Full reasoning and the endpoint classification: `DECISIONS.md` § D13.