8. Report Entitlements
8.1 Source files inspected
Shumoul.Infrastructure/MultiTenancy/Seeders/JsonFiles/Reports.json(BackEnd) — the report catalog: 6 systems (Sales/Purchases/Inventory/Accounts/Tax/Administrative Reports), 29 menus, 307 leaf reports. Zero platform/root-admin/SaaS-management entries — confirmed by a case-insensitive search fortenant|hosting|saas|platform|rootacross the whole file (no matches). This makes sense structurally: this catalog is seeded into the tenant's own report tables, not a cross-tenant one.Shumoul.Infrastructure/MultiTenancy/Seeders/JsonFiles/ReportActions.json— the fixed 6-verb action vocabulary (Display,ExportExcel,ExportCs,Print,SentBySMS,SentByEmail) applied to every leaf report.Shumoul.Infrastructure/MultiTenancy/Seeders/JsonFiles/SubscriptionFeatureReportActions.json— the existing feature→report-action linking file (5 rows before this task, coveringFeatures.SalesManagement,Features.PurchaseInvoices,Features.AccountManagement).
8.2 How reports are linked — confirmed architecture
ReportSeeder derives each report's stable ActionKey from Reports.json/ReportActions.json:
{ReportKey}.{ActionKey} where ReportKey = Permission.{SystemFName}.{MenuFName}.{ReportFName}
(every FName has its spaces stripped)
e.g. Permission.SalesReports.SalesInvoicesReports.Sequential.Display. SubscriptionFeatureReportAction
links a SubscriptionFeature.Key to one of these derived AppReportNameAction.ActionKey strings — never
a numeric report Id, and never invented outside this exact derivation (the seeder skips + logs a
warning for any key that doesn't resolve).
This is the architecture already in use — no new report mapping model was created. A new seeder
(ShumoulFeatureReportActionSeeder, see Chapter 4) was required only
because the existing SubscriptionFeatureReportActionSeeder is SeedHistory-gated and would never
pick up new rows in an already-seeded environment — the underlying SubscriptionFeatureReportAction
entity and its identity (SubscriptionFeatureId, AppReportNameActionId) are unchanged.
8.3 Why ProjectManagement carries the advanced finance reports
Both Finance Starter and Finance Advanced include Features.AccountManagement. Since report grants are
feature-keyed, giving AccountManagement every report both packages should ever show would make them
identical. Instead, AccountManagement carries only the Starter-appropriate baseline (Accounts
Balances Report, Account Statement Report, Trial Balance Report, Tax Declaration Report), and the
richer "Final Reports" set (Income Statement / Financial Position / Cash Flow) plus the Cost Center and
Project account-statement reports are attached to Features.ProjectManagement — the one feature only
Finance Advanced includes. This is a deliberate, documented reuse of an existing feature for a new
purpose (advanced-tier report gating), not a new parallel model.
8.4 21 new report-action links added, by feature
| FeatureKey | Reports added |
|---|---|
Features.ProductsManagement | Products Data (Display, ExportExcel) |
Features.PointofSalesApp | Daily Closing Report, Shifts Report |
Features.InventoryManagement | Items Data, Items Balances Report |
Features.SalesManagement | Sales Invoices — Grouped by Customers; Tax Reports — Sales Invoices Report |
Features.AccountManagement | Account Statement Report, Trial Balance Report, Tax Declaration Report |
Features.PayablesManagement | Supplier Account Statement Report, Suppliers Accounts Balances Report |
Features.ReceivablesManagement | Customer Account Statement Report, Customers Accounts Balances Report |
Features.ProjectManagement | Income Statement, Financial Position, Cash Flow, Cost Center Account Statement Report, Cost Centers Balances Report, Project Account Statement Report |
8.5 Provisioning vs. runtime gating
SubscriptionFeatureReportAction is real, live-wired data — but it controls what gets provisioned
into a tenant's own database at provisioning time (TenantEntitlementProvisioningService copies the
entitled AppReportSystem/Menu/Name/Action/NameAction rows and grants the tenant's Admin role a
RoleReportPermission per report action), not an in-request "is this report entitled" check. The
actual per-request report-execution gate is the older, separate RoleReportPermission /
ReportClaimService mechanism, keyed by AppReportName.ReportKey, entirely independent of subscription
data. Confirming a new package's reports "work" for a live tenant means confirming provisioning ran and
RoleReportPermission rows landed — not just that SubscriptionFeatureReportAction rows exist.
8.6 Idempotency and deployability
ShumoulFeatureReportActionSeeder has no SeedHistory gate — it re-runs every startup, resolves
FeatureKey/ReportNameActionKey fresh each time, and only inserts a (FeatureId, AppReportNameActionId)
pair that doesn't already exist. Live-verified: first run inserted 21/21 rows with 0 skips; every
subsequent startup re-run inserted 0. This means the mappings ship automatically after any git pull/deploy/startup — no manual production step is required.
