From f2499df6a90cb401b4112f59e162464b75196634 Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Sun, 26 Jul 2026 22:22:22 +0400 Subject: [PATCH] docs: Backoffice readiness audit for future Seller Management Audit only, no code changed - every fact gathered by reading current facades/gateways/components on this branch, not assumed. Covers all 13 admin modules (Dashboard, Products, Categories, Orders, Customers, Users, Analytics, Reviews/Moderation, Media, CMS, Builder, Settings, Monitoring, Transactions). Per module: answers the 3 readiness questions (does Marketplace Owner see everything / would Seller see only their own / would Seller Staff be limited), documents where a future scope would be injected (an existing method/interface parameter to extend - no "if seller" checks introduced anywhere), lists components that currently assume global ownership, and classifies Ready / Needs scope / Needs permissions / Needs API change. Key findings: - Only 3 of 13 gateways (Categories, Dashboard-metrics, Media) are DI-token-swappable today; everything else needs that seam added first, independent of seller scoping. - Orders is the load-bearing blocker: Customers, Transactions, and half of Analytics all derive from its same unscoped full-fetch order list, and AdminOrderItem has no per-item seller attribution at all - the concrete gap behind Seller-Management.md's open Unified-vs-Split- Orders question. - Users already carries an AdminUserScope/AdminRole concept (label- only today) - the natural future home for the Marketplace Owner/ Seller/Seller Staff/Platform Admin role vocabulary. - CMS/Static Pages and Builder/Project Editor are structurally not about data scoping at all (marketplace-wide content, single global config document respectively) - seller-level work there is new product surface, not an extension. - No admin module anywhere does role-based hiding of buttons or data today - confirmed, not assumed. Linked from docs/architecture/foundation/README.md alongside the other Seller Management docs. --- docs/architecture/foundation/README.md | 1 + ...r-Management-Backoffice-Readiness-Audit.md | 319 ++++++++++++++++++ 2 files changed, 320 insertions(+) create mode 100644 docs/architecture/foundation/Seller-Management-Backoffice-Readiness-Audit.md diff --git a/docs/architecture/foundation/README.md b/docs/architecture/foundation/README.md index 558dc34..febaff9 100644 --- a/docs/architecture/foundation/README.md +++ b/docs/architecture/foundation/README.md @@ -54,6 +54,7 @@ It is a platform runtime that must support unlimited tenants from one Angular ap - [Seller-Management-Diagrams.md](Seller-Management-Diagrams.md) — hierarchy, bootstrap gate, type diagram - [Seller-Management-Domain-Models.md](Seller-Management-Domain-Models.md) — typed models, optional sellerId fields - [Seller-Management-UX-Review.md](Seller-Management-UX-Review.md) — UX/accessibility review of the Phase 1 UI +- [Seller-Management-Backoffice-Readiness-Audit.md](Seller-Management-Backoffice-Readiness-Audit.md) — per-module scoping/permissions readiness audit ### Engineering Rule Documents diff --git a/docs/architecture/foundation/Seller-Management-Backoffice-Readiness-Audit.md b/docs/architecture/foundation/Seller-Management-Backoffice-Readiness-Audit.md new file mode 100644 index 0000000..295d2af --- /dev/null +++ b/docs/architecture/foundation/Seller-Management-Backoffice-Readiness-Audit.md @@ -0,0 +1,319 @@ +# Seller Management — Backoffice Readiness Audit + +Audit only. **No code changed by this pass** — every fact below was gathered +by reading current source (facades, gateway interfaces, local +implementations, components) on `feature/seller-management-foundation`, not +inferred or assumed. Companion to +[Seller-Management.md](Seller-Management.md) §5 (Roles & Permissions) and §6 +(Seller Ownership). + +## How to read this + +For each module: three readiness questions, then **where** a future scope +would be injected (not "if seller" conditionals — an injection point in an +existing method signature or facade call), then components that currently +assume there is exactly one owner of the whole dataset, then a +classification. + +**Classification legend:** +- **Ready** — either already scope-injectable with no structural change, or + conceptually marketplace-wide data that a seller scope shouldn't apply to + at all. +- **Needs scope** — a filters object or DI-token seam already exists; adding + a scope field is additive, not structural. +- **Needs permissions** — the open question is *who sees this at all* + (Marketplace Owner vs Seller vs Seller Staff vs Platform Admin), not data + filtering. +- **Needs API change** — the method signature itself has no parameter to + extend (bare no-arg calls), the data model has no owner attribution field + to filter on, or the module's whole premise assumes one global record. + +Repo-wide baseline established by this audit: **only Categories, Dashboard- +metrics, and Media have a DI-token-swappable gateway today** (per +`BACKEND.md` §8's pattern — interface + `*LocalGateway` + `*ApiGateway` + +`InjectionToken`). Every other admin domain's facade injects its +`*LocalGateway` concretely; that seam has to be added before any real +scoping work lands, independent of the scoping question itself. No module +currently does role-based hiding of any button or data — Users displays +role/permission *labels* only, nothing gates on them. + +## Module-by-module + +### Dashboard +- Owner sees everything? Yes — today's only mode. +- Seller sees only their own? Not possible today — `loadMetrics()` has no + parameters at all. +- Seller Staff limited? Undetermined — no permission model touches this yet. +- **Injection point:** `AdminDashboardMetricsGateway.loadMetrics()` would + need a new parameter (interface change, not a filters-object field — + there is no object to extend). +- **Assumes global ownership:** `admin-dashboard-metrics.local.gateway.ts` + computes `categoriesCount`/`productsCount` as raw `.length` over the + entire catalog. +- **Classification: Needs API change.** + +### Products +- Owner sees everything? Yes. +- Seller sees only their own? Not yet, but the shape is close — filters + object already exists. +- Seller Staff limited? Undetermined. +- **Injection point:** `AdminProductListFilters` (already has + `search/categoryId/visibility/stock/includeArchived/sort/page/pageSize`) + — an optional `sellerId` field slots in next to the existing ones; one + more `.filter()` line in `AdminProductsLocalGateway`. +- **Assumes global ownership:** `loadDashboardStats()` calls + `loadProducts({page:1, pageSize:100000, includeArchived:true, ...})` to + sum stock/health stats with no per-seller split. +- **Classification: Needs scope** (small, once the DI-token seam this + module still lacks is added — see baseline note above). + +### Categories +- Owner sees everything? Yes. +- Seller sees only their own? Categories are conceptually a **shared, + marketplace-wide taxonomy** — the real future question is "which products + in category X belong to seller Y," not "which categories belong to seller + Y." Likely Owner-only editable regardless of seller scope. +- Seller Staff limited? Undetermined. +- **Injection point:** `AdminCategoryListFilters` already has + `search/visibility/includeDeleted` and the gateway is already DI-token- + swappable (`ADMIN_CATEGORIES_GATEWAY`) — **but** the facade's `loadList()` + currently calls the gateway with a hardcoded + `{search:'', visibility:'all', includeDeleted:true}` and does all real + filtering client-side in `filteredCategories()`/`visibleTreeRows()` to + preserve tree parent/child chains. A `sellerId` filter passed to the + gateway would be silently bypassed unless this hardcoded call is updated + too. +- **Assumes global ownership:** `loadDashboardStats()` sums the whole + category tree. +- **Classification: Needs scope** — gateway/interface layer is Ready, the + facade's full-fetch-then-client-filter pattern is the actual gap. + +### Orders +- Owner sees everything? Yes. +- Seller sees only their own? **Not modeled at all** — `AdminOrderItem` has + no seller/vendor attribution field; a multi-vendor order (one order, + items from several sellers) has no representation today. This is the + concrete blocker behind the Unified-vs-Split-Orders open question in + `Seller-Management.md` §6. +- Seller Staff limited? Undetermined. +- **Injection point:** `AdminOrderListFilters` has `search/status/page/ + pageSize` — a `sellerId` field is syntactically cheap to add, but + filtering by it means nothing until orders/order-items carry seller + attribution in the data model. +- **Assumes global ownership:** `loadDashboardStats()` fetches + `pageSize:100000` and sums orders/revenue/customers globally; this same + full-fetch feeds Customers, Transactions, and Analytics (see below), + compounding the single-owner assumption across four modules. +- **Classification: Needs API change** — the data-model gap (per-item + seller attribution, unified-vs-split decision) is the real blocker, not + the filter object. + +### Customers +- Owner sees everything? Yes. +- Seller sees only their own? Not possible today — "customer" is a + **derived aggregate** grouping the full order list by email in-memory; + there is no first-class Customers gateway to add a filter to at all. +- Seller Staff limited? Undetermined. +- **Injection point:** none exists yet. Either (a) derive from an + already-scoped Orders call once Orders itself supports `sellerId`, or (b) + introduce a first-class `AdminCustomersGateway` — `BACKEND.md` already + recommends the latter independent of Seller Management. +- **Assumes global ownership:** `buildCustomers()` groups the entire + unscoped order list; hardcoded `pageSize:100000` fetch, "search" is + client-side only. +- **Classification: Needs API change.** + +### Users +- Owner sees everything? Yes. +- Seller sees only their own? Not modeled — `loadUsers()` is bare no-arg, + no filters object exists to extend. +- Seller Staff limited? **This is where the answer will actually live** — + `AdminUser` already carries an `AdminUserScope` field (`'marketplace' | + 'office'` in seed data) and an `AdminRole` with a permission-array shape. + This is the closest existing hook to the future `SellerPermissionRole` + vocabulary (`marketplaceOwner`/`seller`/`sellerStaff`/`platformAdmin`, + `Seller-Management.md` §5) — but today it's used for labels only + (`roleLabel`/`permissionLabel` in the page component), nothing gates + actions or visibility on it anywhere in the app. +- **Injection point:** `loadUsers()` needs a parameter added (interface + change — no object to extend); `AdminUserScope` is the natural place a + seller-scope value would eventually live. +- **Assumes global ownership:** `loadAll()` fetches every user + unconditionally, no per-seller user set exists. +- **Classification: Needs API change** (data fetch) **+ Needs permissions** + (this module is the eventual home of the Marketplace Owner / Seller / + Seller Staff / Platform Admin distinction — right now it's purely + informational). + +### Analytics +- Owner sees everything? Yes. +- Seller sees only their own? Not possible today — every number (revenue, + top products, health, recommendations) is summed across the *entire* + orders+products+categories+reviews corpus with no per-owner dimension + anywhere, and there's no gateway of its own to add a filter to (it + composes five other gateways/facades directly). +- Seller Staff limited? Undetermined. +- **Injection point:** none today — depends entirely on Orders/Products/ + Categories/Moderation each supporting `sellerId` first, then every one of + this facade's ~6 nested subscribe calls would need the field passed + through, plus every aggregate builder (`buildSeries`, `buildTopProducts`, + `buildCustomerAnalytics`) reworked to partition by seller instead of + summing globally. +- **Assumes global ownership:** the strongest case in the audit — literally + every displayed number. +- **Classification: Needs API change** — `BACKEND.md` already independently + flags this as the last domain to get a real backend; Seller Management + scoping compounds on top of that, not ahead of it. + +### Reviews / Moderation +- Owner sees everything? Yes. +- Seller sees only their own? Reviews carry `productId`/`productName` — a + seller would see reviews on *their own products*, which means joining + through product ownership (once Products has `sellerId`), not a direct + seller field on the review itself. Reports (`loadReports()`) has no + filters object at all today, separate code path from reviews. +- Seller Staff limited? Undetermined. +- **Injection point:** `AdminReviewListFilters` + (`search/status/rating/page/pageSize`) — cheap field add, correctness + depends on a product-ownership join. `loadReports()` needs a signature + change first (no params exist). +- **Assumes global ownership:** `loadDashboardStats()` sums the full review + queue with `pageSize:100000`, no per-product/per-seller split. +- **Classification: Needs scope** (reviews, small, blocked on Products) + **+ Needs API change** (reports, no params today). + +### Media +- Owner sees everything? Yes. +- Seller sees only their own? Not modeled — `MediaAsset` has no + uploader/owner field at all (`filename/mimeType/size/tags/folder` only); + "folder" is the closest existing scoping primitive, used generically + today. +- Seller Staff limited? Undetermined. +- **Injection point:** `MediaListParams` (`page/pageSize/search/folder/ + kind/sort`) is already a rich optional-params object — a `sellerId` field + is a cheap addition, and the repository is already DI-token-bound + (`MediaRepository` abstract class, mock vs API swap already established). + `MediaAsset` itself needs an owner field added for the filter to mean + anything. +- **Assumes global ownership:** none beyond the missing owner field itself. +- **Classification: Needs scope** (small — best-positioned module in the + audit alongside Categories). + +### CMS / Static Pages +- Owner sees everything? Yes — and likely always will. +- Seller sees only their own? **Conceptually doesn't apply.** Static pages + (legal, about, etc.) are inherently marketplace-wide; there is no + per-seller "static page" concept in the domain, and no fetch method + exists to add a filter to in the first place — this module reads/writes + `BootstrapConfig.staticPages` in-memory, no gateway, no HTTP call + (confirmed by `BACKEND.md`: "has no backend call today"). +- Seller Staff limited? Not applicable. +- **Injection point:** none — would require inventing an entirely new data + source, not adding a filter to an existing one. +- **Assumes global ownership:** the whole module's premise, but + appropriately so — this is marketplace-wide content by nature. +- **Classification: Ready** — no scoping work belongs here; flag if product + strategy later decides sellers need their own static pages, which is a + new capability, not a gap in this one. + +### Builder / Project Editor +- Owner sees everything? Yes — the entire module edits one global + `BootstrapConfig` document. +- Seller sees only their own? Not modeled, and not a filter question at + all — there is no `load*`/`list*` gateway method anywhere in this module + to extend; `save()`/`publish()`/`resetDraft()` all operate on the single + in-memory config object, with no backend write path yet either + (`BACKEND.md`: "no client write call exists today"). +- Seller Staff limited? Not applicable at this structural level. +- **Injection point:** none exists. A seller-scoped builder (per-seller + storefront layout/branding) would be a **different product concept**, not + an extension of this module's current single-document model. +- **Assumes global ownership:** total and structural — the single strongest + one-owner assumption in the codebase. +- **Classification: Needs API change** — biggest structural gap in the + audit if seller-level storefront customization is ever wanted; this is + new work, not scoping. + +### Settings +No route exists — `admin-nav.model.ts` marks it `comingSoon: true`, no +component backs it. **Skipped, nothing to audit.** + +### Monitoring +- Owner sees everything? Yes. +- Seller sees only their own? Likely **shouldn't** — events/queues/webhooks + (logins, API health, queue depth) are platform-operational data. A seller + has no legitimate reason to see other users' login events or system + queue depth regardless of any future scoping. +- Seller Staff limited? Same reasoning — this looks like a Marketplace + Owner / Platform Admin-only surface once roles exist, not something a + Seller role should reach at all. +- **Injection point:** `AdminMonitoringEventFilters` (`category/search`) + exists for events; `loadQueues()`/`loadWebhooks()` are bare no-arg. +- **Assumes global ownership:** appropriately so — this is genuinely + system-wide data. +- **Classification: Needs permissions** — the open question is route-level + visibility per role, not data filtering. + +### Transactions +- Owner sees everything? Yes. +- Seller sees only their own? Not modeled — transactions are derived 1:1 + from the same unscoped global order list as Customers, inheriting the + same seller-attribution gap. +- Seller Staff limited? Undetermined. +- **Injection point:** `AdminTransactionListFilters` + (`search/status/type/page/pageSize`) exists — cheap field syntactically, + but real correctness is blocked on Orders resolving per-item seller + attribution first. +- **Assumes global ownership:** derives wholesale from + `AdminOrdersLocalGateway`, same pattern as Customers. +- **Classification: Needs scope** (small syntactically, blocked on Orders' + **Needs API change** classification for real correctness). + +## Summary table + +| Module | Classification | +|---|---| +| Dashboard | Needs API change | +| Products | Needs scope | +| Categories | Needs scope | +| Orders | Needs API change | +| Customers | Needs API change | +| Users | Needs API change + Needs permissions | +| Analytics | Needs API change | +| Reviews / Moderation | Needs scope (reviews) + Needs API change (reports) | +| Media | Needs scope | +| CMS / Static Pages | Ready (not applicable) | +| Builder / Project Editor | Needs API change | +| Settings | N/A — no route | +| Monitoring | Needs permissions | +| Transactions | Needs scope (blocked on Orders) | + +**Nothing in this repo is currently classified "Ready" for actual seller +data scoping** — CMS/Static Pages is "Ready" only in the sense that it +correctly needs no scoping at all. Categories and Media are the +best-positioned modules for a future scope field (filters object + DI seam +either fully or mostly in place already). Orders is the load-bearing +blocker — Customers, Transactions, and half of Analytics all derive from +it, so its data-model gap (no per-item seller attribution, unified-vs-split +undecided) should be resolved before scoping any of its three dependents. + +## Cross-cutting findings + +- **No admin module anywhere does role-based hiding of buttons or data + today.** Users is the only module with a role *concept* in its data + (`AdminRole`, permission arrays) and even there it's label-only. +- **Only 3 of 13 audited gateways are DI-token-swappable today** + (Categories, Dashboard-metrics, Media) — everything else needs that seam + added before any scoping work, independent of Seller Management. +- **The heaviest single dependency chain**: Orders → Customers, + Transactions, and Analytics all derive from the same unscoped, full-fetch + order list. Fixing Orders' data model is the one change with the largest + downstream effect. +- **Two modules are structurally not about data scoping at all**: CMS/ + Static Pages (marketplace-wide by nature) and Builder/Project Editor + (single global document, no query surface) — seller-level work here + would be new product surface, not an extension. +- **No "if seller" conditional exists anywhere in the codebase** — this + audit deliberately did not introduce any. Every injection point above is + described as a parameter/field addition to an existing method or + interface, never a runtime branch.