Skip to main content
Version: Latest

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 for tenant|hosting|saas|platform|root across 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, covering Features.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.

FeatureKeyReports added
Features.ProductsManagementProducts Data (Display, ExportExcel)
Features.PointofSalesAppDaily Closing Report, Shifts Report
Features.InventoryManagementItems Data, Items Balances Report
Features.SalesManagementSales Invoices — Grouped by Customers; Tax Reports — Sales Invoices Report
Features.AccountManagementAccount Statement Report, Trial Balance Report, Tax Declaration Report
Features.PayablesManagementSupplier Account Statement Report, Suppliers Accounts Balances Report
Features.ReceivablesManagementCustomer Account Statement Report, Customers Accounts Balances Report
Features.ProjectManagementIncome 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.