Test Plan: Reporting & Analytics
♻️ Reconciled against current code — 2026-07-01. Fixed a broken "Test Deliverables" link that pointed at a non-existent
TC_RPT_001_REPORTS.md — it now links the real pages on disk (TC-RPT-001, TC-RPT-002, TC-RPT-003, plus the new TC-RPT-004). Export format is SCOPED, not uniform CSV-only: the old "everything is CSV, no XLSX anywhere" note is partially wrong — real .xlsx (?format=xlsx, via streamXlsxToResponse / exceljs) now exists on exactly 4 exports: invoices (GET /api/billing/admin/invoices/export), payments (GET /api/billing/admin/payments/export), treasury (GET /api/treasury/export), and event registrations (GET /api/admin/events/:eventId/registrations/export). Everything else stays as before: the attendance report/audit exports are CSV-only (no xlsx), the members report is JSON-only with no export at all (GET /api/reporting/reports/members — the old TC-RPT-002/010 "Export CSV" steps below describe a button that does not exist in code), and the donations report is also JSON-only. Do not claim xlsx for reporting/attendance, and do not claim CSV/xlsx for the members or donations reports. See TC-RPT-004 for full export-integrity coverage.
🆕 New on stage — 2026-06-19 → 2026-07-01.
- Real
.xlsxexport on 4 money/registration surfaces (#228): invoices, payments, treasury, and event-registrations exports now accept?format=xlsxand stream a genuine Excel workbook (viastreamXlsxToResponse/exceljs), not a renamed CSV. See TC-RPT-004. - CSV/formula-injection neutralized platform-wide (#336/#340, Jun 25 release): the shared
utils/csvSanitize.tsanchors on a cell's leading character (/^[=+\-@\t\r]/) and prepends a single'when triggered — e.g.=cmd|'/c calc'!A1becomes'=cmd|'/c calc'!A1in the downloaded file. A value with a mid-string-/+(a date like2024-01-01, an amount) is untouched. Guest export (/api/admin/guests/export.csv) is now server-side sanitized (#340). See TC-RPT-004. - Canonical
projectInvoiceTotalsparity — with a known reporting-side gap (#443/#450, Jun 25 release): invoice detail/listing/finance-summary/invoice+guest exports all derive totals from the one canonical projector now. ⚠️ Known divergence: the event attendance report'stotalCollected(attendanceHandlers.ts) still sums rawinvoice.amountCentsforPAIDinvoices directly — it bypasses the projector and does not read allocations. If you see this figure disagree with the finance summary for the same event's invoices, that is a known, already-flagged gap — do not re-file it as a new bug; note the exact mismatch if you want to escalate the fix priority. See TC-RPT-004. - NO_SHOW handling reconciled between the stats endpoint and the export (#397):
GET /events/:eventId/attendance/stats(getAttendanceStats) allowlists onlyCONFIRMED/PENDING/CHECKED_INregistrations for its checked-in/total counts — NO_SHOW and WAITLISTED registrations are excluded from that ratio (NO_SHOW is counted separately). The attendance export (GET /admin/events/:eventId/attendance,?format=csv,getAttendanceReport) applies no status filter — NO_SHOW/CANCELLED/WAITLISTED rows are all present in the exported rows. These are two different, both-correct views: don't expect the export row count to equal the stats "total". See TC-RPT-004. - Consolidated reconciliation block on the attendance report (#416):
GET /admin/events/:eventId/attendancenow returns adata.report.reconciliationobject with fieldsregistered, checkedIn, walkIns, noShow, cancelled, stillPending, capacity, attendanceRate, eventEnded.noShowis only populated once the event has ended (eventsAt < now) — before that, non-checked-in non-cancelled registrants count towardstillPendinginstead, by design (registrants who haven't arrived yet aren't no-shows). See TC-RPT-004.
Tenant-local timezone dates (#450) apply ONLY to the invoice export.
billingHandlers.ts' exportInvoicesCSVHandler has a local toTenantLocalDate helper that renders issuedDate/dueDate/paidDate using orgProfile.timezone (fallback Asia/Manila). Reporting and attendance exports emit raw UTC (.toISOString()) for every date column, including CSV. There is no shared formatInTenantTimezone util reused across modules — do not assume attendance/members/donations dates are tenant-local; assert them as UTC.
1. Introduction
This document outlines the test strategy for the Reporting & Analytics module, which provides dashboards, reports, and data exports across all system areas.
2. Scope
- Executive Dashboard: High-level organization metrics.
- Member Reports: Membership statistics and trends —
GET /api/reporting/reports/members, JSON-only, no export. - Financial Reports: Revenue, collections, outstanding —
GET /api/billing/admin/finance/summary. - Event Attendance Reports: Registrations, check-ins, no-shows, reconciliation — CSV export only.
- Export Functionality: scoped — real
.xlsxon invoices/payments/treasury/event-registrations; CSV-only on attendance/audit/guest exports; no export on members/donations reports.
3. Test Strategy
- Data Accuracy Testing: Verify calculated metrics and cross-surface money parity.
- Export Format Testing: Confirm each export is available in the formats the code actually supports — not more, not less.
- Security Testing: Formula-injection neutralization in every CSV/XLSX cell that echoes user input.
- E2E Testing: Dashboard navigation and filtering.
4. Environment
- Frontend Pages:
ExecutiveDashboardPage,AdminFinanceDashboardPage,AdminEventAttendanceReportPage - Backend Endpoints:
/api/reporting/*,/api/billing/admin/finance/summary,/api/admin/events/:eventId/attendance,/api/events/:eventId/attendance/stats
5. Roles & Permissions
| Role | Permissions |
|---|---|
| ADMIN | Full report access |
| FINANCE_MANAGER | Finance reports (FINANCE:VIEW:*) |
| EVENT_MANAGER | Event reports (EVENTS:VIEW:registrations) |
| MEMBER | Limited/no report access |
6. Test Deliverables
| TC | Topic | Detailed |
|---|---|---|
| TC-RPT-001 | Financial Reports | detailed |
| TC-RPT-002 | Membership Reports | detailed |
| TC-RPT-003 | Event Attendance Reports | detailed |
| TC-RPT-004 | 🆕 Export Integrity & Total Parity | detailed |
7. Risk Areas
- Money parity across invoice detail / listing / finance summary / exports (see #443/#450 note above, and the known attendance
totalCollecteddrift). - Formula-injection in any export that echoes free-text member/event data.
- Overclaiming export format — testers assuming xlsx exists on a CSV-only or JSON-only surface (see the reconciliation banner above).
- NO_SHOW inclusion/exclusion mismatch between the stats endpoint and the full export.