Files
tehran_taj/docs/parity/PROTECTED.md
T
s.esmaeiliandClaude Opus 5 c71574b1d2 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>
2026-09-22 07:23:48 +03:30

14 KiB
Raw Blame History

PROTECTED — intentional monorepo additions (additive-only)

CONFIRMED by the user at the Phase 0 gate (2026-09-21). Rulings are recorded in DECISIONS.md (D0–D12).

Items here are intentional changes in gold-banking-monorepo. Per CLAUDE.md §4 they must not be removed, restyled, restructured, or have their behaviour changed. You may only ADD what the reference has and they lack, in a way that doesn't break them. Any fix that touches a protected item needs explicit approval with a diff plan first.

Legend: [brief] = named as protected in the kickoff brief · [found] = discovered in Phase 0, proposed for protection · [?] = needs your ruling.


A. Admin panel — Dashboard · [brief]

Location: apps/admin-panel/src/views/Dashboard.vue (327 lines) — one file serving both /dashboard/ecommerce and /dashboard/analytics. Gate: rid dashboard.view; lacking it redirects to my-data (not a 403 page). Data: a single GET admin/dashboard/overview on mount, then client-side sorts (reagent desc by grams_sell; lastOrder desc by w).

Contents, in render order:

  1. 4 KPI tiles — مشتریان جدید (نفر) · طلای فروش‌رفته (گرم) · تعداد سفارشات · نرخ هر گرم طلای ۱۸ عیار
  2. 2 KPI tiles — سفارشات تحویل‌شده · مجموع بدهی طلای مشتریان (گرم)
  3. Area chart «روند سفارش‌ها» — 2 series (تک / عمده); x-labels rotated -45° with rotateAlways + hideOverlappingLabels (a deliberate overlap fix — do not "tidy" it)
  4. 2 donuts — وضعیت سفارشات (8-entry colour array) · تفکیک عمده/تک
  5. Horizontal bar + an always-rendered agent table (کارشناس فروش / میزان فروش گرم / تعداد سفارش)
  6. Vertical bar «محصولات پرفروش» with an a-empty fallback
  7. a-descriptions «میزان فروش» — 3 columns incl. a computed average
  8. Table «سفارشات هفته جاری» — pageSize 10, sts_str as a coloured a-tag, caj via jdate

Fixed palette #1c2d56 / #28c76f / #ff9f43 / #ea5455. No filters, no date range, no export, no refresh — that is the intended design, not a gap.

Why this matters for parity: the reference's two dashboards (DashboardECommerce.vue, DashboardAnalytics.vue) were found in the earlier port to be mostly commented-out Vuexy demo markup, with ApexCharts imported but never used in live markup (docs/CHANGELOG-PORT-HISTORY.md). So there is essentially nothing to port into this dashboard. Phase 1 will confirm and record that, and will not touch this file.


B. Admin panel — the extra option in product Variants · [brief]

Location: apps/admin-panel/src/views/apps/product/AddProduct.vue:151-270, 315-361, 519-568 (Create/Edit Product). Gate: the card is gated product.list.variants; add/edit/remove on .add / .edit / .remove.

Variant table columns: تصویر | عنوان | وزن | اجرت درصدی | تعداد | فعال (tag) | « پیش سفارش» (tag) | عملیات.

The protected extra field is تصویر تنوع (variant image). It is a picker, not an uploader: a 64×64 flex grid over the product's already-saved gallery (data.gallery, each {id, url, role, sortOrder}), whose first tile is a بدون تصویر option and whose selected tile gets a blue .is-on border. pickVariantImage() sets image_id (posted) plus image_url (thumbnail only, stripped before POST).

It is populated only in edit mode — on create the gallery is empty and an a-alert shows a context-dependent message (ابتدا محصول را ذخیره کنید… vs برای این محصول تصویری ثبت نشده است.).

Variants are edited purely in memory (row key __key = v-<ts>-<rand>) and saved with the product via PUT product/update/{id} / POST product/create under a variants[] array — there is no per-variant endpoint. Only validation is عنوان تنوع الزامی است; delete has no confirm dialog.

Related, and NOT protected by itself: the same imageId/image contract on the user-app side. user-app consumes it; user-app-v2 does not (gap V2-03). Closing that gap in v2 is additive and does not touch this admin code.


C. User apps — new designs and new features · [brief]

Both apps/user-app and apps/user-app-v2 keep their own visual design. The following were identified in Phase 0 as design/feature additions with no henza_pwa equivalent.

C.1 Shared by both apps

Item Location
/notifications route + screen (self-documented "no legacy equivalent") views/user/NotificationsView.vue
ui/PageBar.vue replacing the legacy AppHeader components/ui/
Redesigned CSS bottom nav (5 tabs; legacy was a 4-tab GSAP notch) layout/
GallerySlider.vue rewrite + teleported full-screen image preview components/product/
Variant chips with per-variant images, stock badges, «نمایش همه» components/product/SingleProduct.vue
The whole stock / pre-order rule (qty, isPreOrder, capQty, maxQty) stores/cartStore.ts, composables/useProduct.ts — v1 only; porting to v2 is gap MON-01
vant pull-to-refresh on 5 screens various views
IntersectionObserver incremental rendering (20-item window) list views
Ledger-based «اسناد» screen views/user/TransactionsView.vue
Order-detail drawer + 4-step tracker + useOrderItemProduct join views/user/OrdersView.vue
9-chip OrderStatus enum types/
Dotlottie update dialog app shell
New API contracts: GET /home/slider, GoldPriceQuote, productCode, collections[], gold-bank:* session keys api/client.ts

C.2 user-app (design v1) only

Home family: components/home/RateStrip.vue, ShowcaseSlider.vue, ShowcaseCard.vue, CategoryRail.vue, SectionHead.vue, AccountStrip.vue; admin banner via GET /home/slider + AdminSlide; the «برگزیده منتزر» and «تازه‌ها» home sections; trust-badge rows.

C.3 user-app-v2 (design v2) only

components/home/HeroCarousel.vue, ProductCarousel.vue, CategoryGrid.vue, ProductPanel.vue; the IRANSans FaNum font family (12 files, src/assets/styles/fonts/iransans/); redesigned HomeView structure; redesigned DashboardView (hero card + 4 settings rows, replacing card + tile grid); the staggered first-paint reveal system (module-scope hasRevealed) in HomeView/CartView/DashboardView; the compact RateStrip pill variant; SingleProduct's scroll-driven solid header + category deep-link pill; the pill-shaped SearchInput.

Important boundary. C.2 and C.3 are design differences and stay separate. The five items in ISSUES.md §V2-01..05 are logic differences and are not protected — CLAUDE.md §2 requires logic to be identical across both apps.


D. Backend — stricter auth than the reference · CONFIRMED (D1)

Protected. The user ruled the target's stricter auth stands as an accepted deviation. Additional obligation from D1: build a full "reference admin operation ↔ target rid" matrix so no operation lacks a permission, and explain the 172-vs-174 key difference.

⚠️ Not yet satisfied: 5 ledger-write routes are still @Public() (DECISIONS.md §"@Public audit" group A). They contradict D1 and are awaiting the user's go-ahead to be protected.

The reference leaves every admin route unauthenticated (henza_server/routes/admin.php:12 and :272 — the auth:api group wrapper is commented out at both ends, verified). apps/api instead applies a global JwtAuthGuard + PermissionsGuard with 144 server-enforced rids, and only 15 routes opt out via @Public().

Strict parity would mean removing authentication from ~215 admin endpoints. CLAUDE.md §3 already carves out exactly this case ("if matching the reference would cause … a clear security hole in money flows → STOP and ask").

Proposed: protect the target's stricter auth as an accepted deviation; log the reference behaviour as SEC-01 and the residual @Public() routes as SEC-02.


E. Backend — Decimal money discipline · CONFIRMED

The reference uses PHP floats throughout. apps/api has zero Float columns in prisma/schema.prisma; ledger and order math run on Prisma.Decimal.

Reverting to floats for "parity" would introduce rounding error into money and gram values. Proposed: protect the Decimal discipline. The one leak (GRAMS_PER_MESGHAL as a JS float, ledger.service.ts:189) is logged as MON-03, to be fixed in Phase 3 — not now.

Note this does not exempt the target from matching the reference's arithmetic, including the floor(price/1000)*1000 rounding in ProductVariant.php:183-190. Same result, better representation.


F. Prior deliberate non-ports · CONFIRMED (D4)

Per D4: broken/destructive reference behaviour is not replicated, only logged — but business behaviour, calculations, rounding and messages must match the reference exactly, and every deliberate non-replication carries a matrix note «عمداً تکرار نشد — ISSUE #n».

Recorded in docs/CHANGELOG-PORT-HISTORY.md as intentionally not ported:

  1. Unauthenticated file upload (file/create had no middleware in the reference)
  2. SMS sent inside toggleActive
  3. Six destructive data behaviours — deleting a confirmed document, editing a confirmed amount, deleting a category that has dependents, creating a category cycle, orphaning the audit trail — each replaced with a Persian 400 message
  4. The reference's avatar-load crash (ReagentCustomer.vue:280 dereferenced data.image.id unguarded)
  5. The branch-mode horizontal layout (HorizontalNavMenu, 4 files never ported; mainLayoutType narrowed to 'vertical')

Items 1–4 are security/data-loss carve-outs consistent with CLAUDE.md §3 and are proposed for protection. Item 5 is different — it is a missing feature, not a safety carve-out, and Phase 1 should treat it as a real gap unless you say otherwise.



G. Target-only capabilities · CONFIRMED (D2) — status ➕, do not touch

Per D2, features that exist only in the target are listed with status ➕ and left alone. Ask before touching any of them.

Capability Where Note
Dual-currency ledger (LedgerEntry, gold grams + rial) apps/api/src/modules/accounting/ replaces the reference's single mixed-unit balance column
Gram / rial debt split (goldDebtGrams, fiatDebtRial) prisma/schema.prisma reference mixed both in one field
Prisma.Decimal money types throughout schema + services see §E
Server-enforced rid permissions auth/acl.constants.ts see §D
UploadedFile model + authenticated upload admin-uploads/ reference upload was anonymous
Status-label / transition helpers admin-orders.service.ts reference had two divergent enums

This list grows as Phase 1 and Phase 2 identify more. It is not a to-do list — it is a keep-out list.


Summary — confirmed status

Section Item Status
A Admin Dashboard ✅ protected (brief)
B Product Variants image picker ✅ protected (brief)
C User-app designs + new features ✅ protected (brief), excluding the 5 v1↔v2 logic gaps (D3)
D Stricter admin auth in apps/api ✅ protected (D1) — 5 ledger routes still open, pending approval
E Decimal money types ✅ protected
F.1–4 Prior security / data-loss non-ports ✅ protected (D4)
F.5 Branch horizontal layout ❌ a real gap, not protected
G Target-only capabilities ✅ ➕ leave untouched (D2)

Explicitly NOT protected (parity work, per DECISIONS.md)

  • The fullName collapse — being reversed under D0
  • percent read from the referring agent — wrong, being corrected under D9
  • The 5 v1↔v2 logic gaps — D3
  • 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.