Overview
Spree provides a flexible reporting system that allows you to generate data exports for sales analytics, product performance, and custom business metrics. Reports are generated asynchronously and delivered as downloadable CSV files.Report System Diagram
Key relationships:- Report is the base model for all report types using Single Table Inheritance (STI)
- ReportLineItem transforms raw database records into formatted report rows
- Reports are scoped to a Store and optionally track the AdminUser who created them
- GenerateJob handles asynchronous CSV generation via the events system
Architecture
The reporting system uses several design patterns:- Single Table Inheritance (STI): All report types inherit from
Spree::Reportand are stored in the same database table with atypecolumn - Presenter Pattern:
Spree::ReportLineItemsubclasses transform raw records into formatted output - Event-Driven Processing: Report generation is triggered asynchronously via Spree’s events system
- Registry Pattern: Reports are registered in
Spree.reportsfor discovery and validation
Built-in Reports
Spree ships with two built-in reports:Sales Total Report
The Sales Total report provides line-item level detail for all completed orders within a date range. Columns:date- Order completion dateorder- Order numberproduct- Variant descriptive namequantity- Line item quantitypre_tax_amount- Amount before taxespromo_total- Promotion discounts appliedshipment_total- Shipping coststax_total- Tax amounttotal- Final amount including all adjustments
Products Performance Report
The Products Performance report aggregates sales metrics by product for the specified period. Columns:sku- Product SKUname- Product namevendor- Vendor name (if multi-vendor enabled)brand- Brand namecategory_lvl0/1/2- Category hierarchy from main taxonprice- Current product priceweeks_online- Weeks since product became availablepre_tax_amount- Total pre-tax salestax_total- Total taxes collectedquantity- Total units soldpromo_total- Total promotion discountstotal- Total revenue
Creating Custom Reports
To create a custom report, you need three components:- A report class that defines the data query
- A line item class that formats each row
- Registration in
Spree.reports
Step 1: Create the Report Class
Create a new report class that inherits fromSpree::Report:
app/models/spree/reports/customer_orders.rb
line_items_scope method must return an ActiveRecord::Relation that defines what records appear in the report. This method has access to:
store- The current storedate_from- Report start datedate_to- Report end datecurrency- Report currencyvendor- Vendor (if multi-vendor is enabled)
Step 2: Create the Line Item Class
Create a corresponding line item class that transforms each record into report columns:app/models/spree/report_line_items/customer_orders.rb
- The class name must match the report class name (e.g.,
Reports::CustomerOrders->ReportLineItems::CustomerOrders) - Use
attributeto define columns with their types - Each attribute needs a corresponding method that extracts/formats data from
record recordis a single item fromline_items_scope- Use
Spree::Moneyfor currency formatting currencyandstoreare delegated from the report
Step 3: Register the Report
Add your report to the registry in an initializer:config/initializers/spree.rb
Step 4: Add Translations
Add the report name translation:config/locales/en.yml
config/locales/en.yml
Advanced Customization
Complex Queries with Aggregations
For reports that aggregate data, you can use SQL directly:app/models/spree/reports/revenue_by_category.rb
Custom Summary Section
Overrideto_partial_path to use a custom template with a summary:
app/models/spree/reports/customer_orders.rb
Multi-Vendor Support
If you’re using Spree Multi-Vendor, filter by vendor:app/models/spree/reports/vendor_sales.rb
How Reports Work Internally
Generation Flow
- User creates report via admin UI with date range, currency, and optional vendor
- Report is saved to database with status tracking
report.createdevent fires viapublishes_lifecycle_eventsconcernSpree::ReportSubscribercatches event and enqueuesGenerateJob- Background job runs
report.generate:- Iterates through
line_items_scopein batches - Transforms each record via
ReportLineItem - Writes CSV to temp file
- Attaches CSV to report via ActiveStorage
- Sends notification email to user
- Iterates through
Key Files
| File | Purpose |
|---|---|
core/app/models/spree/report.rb | Base report model |
core/app/models/spree/report_line_item.rb | Base line item presenter |
core/app/models/spree/reports/*.rb | Built-in report implementations |
core/app/models/spree/report_line_items/*.rb | Built-in line item formatters |
core/app/subscribers/spree/report_subscriber.rb | Event subscriber for async generation |
core/app/jobs/spree/reports/generate_job.rb | Background job for CSV creation |
core/app/mailers/spree/report_mailer.rb | Notification emails |
admin/app/controllers/spree/admin/reports_controller.rb | Admin UI controller |
Report Base Class Methods
ReportLineItem Base Class Methods
Testing Custom Reports
Testing custom reports ensures they correctly query data and format output. Here’s how to write comprehensive specs for your reports.Testing the Report Class
Test that your report’sline_items_scope returns the correct records:
spec/models/spree/reports/customer_orders_spec.rb
Testing the ReportLineItem Class
Test that your line item correctly formats each record:spec/models/spree/report_line_items/customer_orders_spec.rb
Testing Reports with Aggregations
For reports that use SQL aggregations, mock the virtual attributes:spec/models/spree/report_line_items/revenue_by_category_spec.rb
Running the Tests
Run your report specs with:Key Testing Patterns
- Test scope filtering: Verify
line_items_scopereturns only records matching date range, currency, and store - Test line item resolution: Ensure
line_item_classresolves to the correct presenter class - Test registration: Confirm your report is in
Spree.reports - Test attribute formatting: Verify each attribute method returns correctly formatted data
- Test CSV output: Check
headers,csv_headers, andto_csvreturn expected values - Test edge cases: Handle nil values gracefully (e.g., missing addresses)
Configuration
Report Preview Limit
Configure the number of preview rows shown in the admin UI:config/initializers/spree.rb
Background Job Queue
Reports use a dedicated queue. Configure your job processor:Permissions
Report access is controlled by CanCanCan. By default, only users with the:manage ability on Spree::Report can access reports. Configure permissions in your permission sets.

