Files
tehran_taj/docs/parity/PROTECTED.md
T
s.esmaeiliandClaude Opus 5 004a3945c2 parity(docs): record user decisions D0-D12 and urgent investigation results
Phase 0 gate passed. All rulings recorded in docs/parity/DECISIONS.md and
applied to PROTECTED.md, PROGRESS.md and ISSUES.md. No source code changed.

Urgent investigations requested by the user, all evidence-cited:

- percent trace: the target's implementation is WRONG. `percent` is the
  customer's OWN column in the reference (customer_add.php:32), a per-customer
  making-charge offset used to shift catalogue filter bounds
  (ApiMobileController.php:391-396,76-77). The target reads the referring
  agent's wagePercent instead (admin-customers.service.ts:245) and never
  writes the field at all. It is NOT part of the price formula -- that extra
  percent comes from a global ConfigStatic row (ProductVariant.php:50,183).
  Two unrelated `percent` keys exist and must not be confused with it.

- @Public audit: 15 routes across 7 controllers, grouped by risk. Group A is
  5 ledger routes (4 writes) that contradict decision D1; group B is 3 order
  routes incl. a public status change accepting a client-supplied
  actorAdminUserId; group C is 8 reads, of which only the catalogue, pricing,
  slider and uploads are legitimately public.

- Login leak: it is in the TARGET. GET /customers is public and returns every
  active customer with national ID numbers, debts and ID-document URLs.
  passwordHash is correctly excluded. Not possible in the reference.

Also logged 5 customer-module defects found in the deep check the user asked
for (CUST-01..05), including the fullName collapse the user reported.

Skill updated per D12: Step C gains rule C0 (field-by-field matrix, never
page-by-page) and Step E gains rule E0 (mandatory 4-step round-trip test).

[ref: henza_server, henza_admin]

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 23:26:35 +03:30

12 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