PRD: Sales reports & access
| Module | Reports (CORE-11) | PRD ID | PRD-SLS-001 |
| Status | Shipped | Owner | Reports squad |
| Date | 2026-05-28 | Version | v1.0 |
| Packages | @nx/sale · @nx/commerce · @nx/finance · apps/client | URD | SLS · ACC |
TL;DR
Gives a merchant owner read-only views over what was actually sold: how much per day, which products and categories drive revenue, and how purchase spending compares against it. Every report is confined to the user's merchant, requires a date range, and counts only completed orders - so the numbers an owner runs the shop on are always correct and never leak across merchants.
1. Context & Problem
A merchant owner needs trustworthy read-only views over the business: daily sales, top products and categories, and how purchasing spend compares against revenue. Orders and purchase orders already exist as transactional records, but there is no aggregated read-side over them - an owner cannot answer "how much did I sell yesterday", "what sells best", or "how does cost compare to revenue" without manual counting.
This increment builds the sales reporting read-side on top of completed sale orders and purchase orders, scoping every aggregation to the requesting user's merchant, and surfaces it through revenue-report screens in the client. Aggregation is pushed down to the database over completed orders; reports never mutate data.
2. Goals & Non-Goals
Goals
- Daily sales summary - per-day gross, tax, discount, net, and order count over a from-to date range.
- Product sales report - top products ranked by revenue and quantity.
- Category sales report with single-category drill-down.
- Purchase summary - supplier / purchase-order spending for cost comparison.
- Strict scoping - every report confined to the user's merchant access, requiring a date range, counting only completed orders, with empty result sets rendering as zero totals rather than errors.
Non-Goals
- Shift X / Z reports and cash reconciliation - owned by the separate
SHFfeature (URD-SHF), still in-progress. - Profit & loss / margin analytics, inventory valuation, customer retention analytics - owned by
ADV(URD-ADV). - Export to PDF / CSV / Excel and scheduled auto-generated reports.
- Tax declaration reports (owned by Tax & Invoice).
3. Success Metrics
| Metric | Target / signal |
|---|---|
| Tenancy isolation | 0 cross-merchant rows in any report response; every query keyed by merchantId |
| Figure correctness | Daily totals reconcile against completed orders in range; end date is included in the window |
| Completed-only accuracy | Cancelled / draft orders never contribute to sales figures |
| Empty-state robustness | A range with no matching orders returns zero totals, never an error |
4. Personas & Use Cases
| Persona | Goal in this feature |
|---|---|
| Owner | See daily sales, best sellers, category mix, and cost-vs-revenue across the merchant |
| Manager | Review sales / product / category / purchase reports for the merchant |
Core scenarios: pick a from-to date range → view the daily sales summary → rank products by revenue and quantity → break down by category and drill into one → review purchase spending for cost comparison - all scoped to the user's merchant.
5. User Stories
- As an owner, I want a daily sales summary over a date range, so that I can see gross, tax, discount, net, and order count per day.
- As a manager, I want top products ranked by revenue and quantity, so that I know what sells best.
- As a manager, I want sales broken down by category, so that I can compare category performance.
- As a manager, I want to drill into a single category, so that I can inspect its detail.
- As an owner, I want a purchase summary by supplier / purchase order, so that I can compare cost against revenue.
- As an owner, I want every report scoped to my merchant and to count only completed orders, so that the figures are always correct and never leak across merchants.
6. Functional Requirements
| # | Requirement | URD ref |
|---|---|---|
| FR-1 | Daily sales summary: per-day gross / tax / discount / net / order count over a from-to range | URD-SLS-001 |
| FR-2 | Product sales: top products ranked by revenue and quantity | URD-SLS-002 |
| FR-3 | Category sales: totals broken down by product category | URD-SLS-003 |
| FR-4 | Category drill-down: detail for a single chosen category | URD-SLS-004 |
| FR-5 | Purchase summary: supplier / purchase-order spending for cost comparison | URD-SLS-005 |
| FR-6 | Every query scoped to the user's merchant access; no cross-merchant leakage | URD-ACC-001 |
| FR-7 | Sales reports require a from-to date range; the end date is included in the window | URD-ACC-002 |
| FR-8 | Only completed sale / purchase orders are aggregated; cancelled / draft are excluded | URD-ACC-003 |
| FR-9 | Empty result sets render gracefully as zero totals, not an error | URD-ACC-004 |
Full requirement text and acceptance criteria live in the Reports URD. This PRD references them rather than restating them.
7. Non-Functional Requirements
| Area | Requirement |
|---|---|
| Read-only | Reports aggregate over completed orders and never mutate data |
| Tenancy & authz | Every aggregation keyed by merchantId; scoped to the user's merchant access; no cross-merchant rows |
| Date semantics | A from-to range is mandatory; the window is inclusive of the end date |
| Performance / scale | Aggregation pushed down to indexed SQL over completed orders rather than in-memory loops |
| Robustness | Empty result sets resolve to zero totals, not errors |
| i18n | User-facing labels are bilingual ({ en, vi }) |
8. UX & Flows
Key screens (in apps/client): the revenue-report dashboard with charts and the report filters (date-range, reset state), on its own report route.
9. Data & Domain
| Entity | Role |
|---|---|
SalesReportService | Backend service orchestrating the sales / product / category / purchase aggregations |
SalesSummaryRepository | Per-day gross / tax / discount / net / order-count aggregation over completed sale orders |
SalesProductRepository | Product ranking by revenue and quantity (reads commerce product data) |
SalesCategoryRepository | Category breakdown and single-category drill-down (reads commerce category data) |
PurchaseSummaryRepository | Supplier / purchase-order spend aggregation for cost comparison |
Conceptual only - full schema and query detail in the sale domain model.
10. Dependencies & Assumptions
Depends on
- Orders (
@nx/sale) - completed sale orders are the source of every sales figure. - Product / category data (
@nx/commerce) - product and category names drive the product / category breakdowns. - Finance order totals (
@nx/finance) - order monetary totals feed the summaries. - Inventory purchase orders - completed purchase orders feed the purchase summary.
Assumptions
- The requesting user has a resolvable merchant scope.
- Completed orders carry the monetary fields (gross, tax, discount, net) the summaries aggregate.
11. Risks & Open Questions
| Risk / question | Mitigation / status |
|---|---|
| Cross-merchant leakage in aggregation | Every query keyed by merchantId; scoping enforced on the read path |
| Off-by-one on the date window | Window is inclusive of the end date for completed sale and purchase orders |
| Large date ranges scanning many orders | Aggregation pushed down to indexed SQL rather than in-memory loops |
| Inconsistent status filtering across report types | Only completed orders counted uniformly across sales and purchase reports |
12. Release Plan & Launch Criteria
| Aspect | Plan |
|---|---|
| Phase | P1 - see URD feature catalog (SLS and ACC Built) |
| Rollout | All merchants; no feature flag |
| Migration | None (read-only aggregation over existing orders) |
| Launch criteria | Daily / product / category / purchase reports return correct totals; merchant scoping verified; empty ranges return zero totals; end date included |
| Monitoring | Report request volume, query latency over large ranges, cross-merchant isolation checks |
13. FAQ
Which orders count toward sales figures? Only completed orders - cancelled and draft orders are excluded from every sales report.
Is the date range optional? No - sales reports require a from-to range, and the window includes the end date.
Can I see another merchant's figures? No - every report is scoped to the user's merchant access; cross-merchant data is never returned.
What happens when there are no matching orders? The report returns zero totals rather than raising an error.
Where are shift X / Z reports? Those belong to the separate SHF Shift Reports feature, not this increment.
References
- URD: Reports - Sales Reports · Access & Scoping
- Related: Shift Reports · Advanced Analytics
- Module: Reports - overview + traceability
- Developer: @nx/sale · Sales Report