Tenant Analytics Reference Public

Research Evidence

Research evidence for the ERPat Tenant Analytics guide: every file shipped under modules/TenantAnalytics and what each controller, model, library, job, view, migration, and config does.

Guide version: r1 Module version: 1.1.0 Updated: 2026-07-27 Estimated time: 6 min 3 views
Reference

Research Evidence

The evidence base for this guide: every file shipped under modules/TenantAnalytics/ and exactly what it does. Everything documented in the other pages traces back to a file listed here — nothing is invented.

????
How to use this page. It maps the module's directory tree to plain-language descriptions so a reviewer can verify a claim against its source, and a new maintainer can find their way around quickly. Paths are relative to modules/TenantAnalytics/.

Manifest & module docs

FileWhat it is
module.jsonThe manifest. Declares the slug tenant_analytics, version, the three permissions, the three tables, the cron job, and — importantly — availability.tenant_database = false (main-database-only). Also documents the deliberate absence of an end-user API.
README.mdModule overview rendered in Settings → Manage Modules.
CHANGELOG.mdPer-module change history.
docs/This offline operator guide (the pages you're reading, plus assets/guide.css and assets/guide.js).
composer.jsonModule composer metadata and the Modules\TenantAnalytics\ PSR-4 root (src/).

config/

FileWhat it does
module_config.phpRegisters the module in Manage Modules (module_key, display name, System category, icon).
permissions.phpThe three RBAC permissions injected into the Roles editor under System: Tenant Analytics.
routes.phpThe staff UI routes: tenant_analytics → the tab, and tenant_analytics/(:any) → the controller's AJAX actions.
api_routes.phpThe three public API routes on the api/v1/analytics/* stack (Route DSL), so they land in the generated OpenAPI spec.
api_public_routes.phpDeclares those three routes as anonymous to the OpenAPI generator (no bearer, no company key).
public_api_limits.phpPer-route rate-limit caps (requests/minute per IP) for the public API, read directly by the rate-limit middleware.
system_logs.phpRegisters the two audit events (run:tenant_analytics, update:tenant_analytics_settings).
settings_lists.phpGroups the module under System in the Manage Modules list.
menu.phpEmpty by design — the module ships no left-sidebar entry; its UI is a tab on the Tenants page.
default_menu.phpEmpty by design — the required default-menu slice, empty because there is no sidebar surface.

controllers/

FileWhat it does
Tenant_analytics.phpThe operator UI + AJAX endpoints (tab, KPIs, snapshots DataTable, aggregate panel, snapshot modal, the sweep console flow, re-sweep, rollup rebuild, settings). Gated main-DB-only + read permission; heavier per-action gates for run and configure.
Tenant_analytics_api.phpThe public, keyless API (groups, summary, group). Reads only the rollups table, enforces the kill switch, validates the date, and applies small-n money suppression.

models/

FileWhat it does
Tenant_analytics_base_model.phpStorage base that pins the primary (default) DB connection, so writes always land in the main database even while the active connection points at a tenant mid-sweep.
Tenant_analytics_snapshots_model.phpPer-tenant snapshot storage: upsert by natural key, the DataTable source, date/coverage counts, and retention pruning.
Tenant_analytics_rollups_model.phpThe identifier-free platform aggregate: read/upsert plus latest-date, available-dates and nearest-date helpers.
Tenant_analytics_runs_model.phpThe sweep audit ledger and resume cursor; also resolves the fleet's sweep targets from the main clients registry (optionally excluding inactive tenants).
Tenant_analytics_probe_model.phpThe measurement engine — the seventeen metric collectors. Runs against the active (tenant) connection and guards every table and column so a drifted tenant degrades to zeros rather than erroring.

libraries/ & src/

FileWhat it does
libraries/Tenant_analytics_collector.phpPer-tenant orchestration: switch into the tenant DB, probe each requested group, upsert results to the main snapshot table, and always restore the main connection in a finally block (the isolation invariant).
libraries/Tenant_analytics_rollup.phpBuilds the platform aggregate for a date: deep-sums numeric leaves, unions scalar lists, and drops every string/boolean leaf — the structural guarantee that no identifier reaches a rollup. Overview also gets a tenant-status histogram (counts only).
src/Middleware/PublicAnalyticsRateLimitMiddleware.phpA dedicated per-route token-bucket rate limiter for the anonymous public API, swapped in for the default client-API limiter.

jobs/

FileWhat it does
TenantAnalyticsSnapshotJob.phpThe nightly (03:00) global-scope cron. Self-gates on the module flag, guards the three tables, sweeps every targeted tenant, rebuilds the rollups, prunes past-retention snapshots, and closes the run. Slug tenant_analytics_snapshot.

views/tenant_analytics/

FileWhat it renders
tab_index.phpThe embedded Analytics tab body: KPI cards, the date/group toolbar, the per-tenant snapshots DataTable, and the platform-aggregate panel.
_kpi_cards.phpThe four KPI cards (tenants swept, coverage, failures, last run).
run_sweep_modal_form.phpThe manual-sweep console modal: options, progress bar, counters, live log, per-tenant status list, and Retry Failed.
settings_modal.phpThe settings form (retention, include-inactive, batch size, public API, small-n floor).
snapshot_detail_modal.phpOne tenant's full snapshot payload for a group.
Where the tab is mounted. The Analytics tab is surfaced on the Tenants list page (in the Tenancy module's tenant-manager view), which is why the module ships no menu entry of its own.

migrations/ & seeders/

FileWhat it does
migrations/20260726065412_create_tenant_analytics_tables.php Creates the three main-database tables (snapshots, rollups, runs). Idempotent and reversible; a no-op on tenant connections.
seeders/TenantAnalyticsDemoSeeder.phpLoads demo data (three pseudo-tenants × three groups of snapshots, a completed run, matching rollups) and soft-deletes it on --remove. Idempotent and clearly marked so it never touches real analytics.

helpers/, language/ & tests/

FileWhat it does
helpers/tenant_analytics_helper.phpA small module helper (a loaded-marker function).
language/english/tenant_analytics_lang.phpAll user-facing strings for the tab, modals, KPIs, statuses and messages.
tests/bootstrap.phpReuses the core test bootstrap for the module suite.
tests/TenantAnalyticsTest.phpThe module's PHPUnit suite, run with php erpat module:test TenantAnalytics.

Verified design invariants

These properties are enforced in the source and underpin the safety claims elsewhere in this guide:

  • Connection isolation. The collector restores the main connection in a finally block after every tenant, so a failed tenant can't send subsequent writes to the wrong database.
  • Main-DB storage while reading tenants. Storage models pin the primary connection, so snapshot/rollup writes land in the main database even while the active connection points at a tenant.
  • No identifiers in the rollup. The rollup builder keeps only numeric leaves and drops every string/boolean, so tenant slugs, names, statuses and module maps cannot reach the table the public API reads.
  • Fail-soft probing. Every collector guards table and column existence, so a tenant on a different schema revision yields zeros / omitted metrics instead of an error.
  • Idempotent sweeps. Snapshots upsert by their natural key, so re-running a date replaces rather than double-counts.

Next steps

Was this guide helpful?

Report a content problem