Skip to main content

Overview

Spree’s reporting system is designed for extension. Each report is a pair of classes — a Report that defines the data query and a ReportLineItem that formats each row — registered in Spree.reports so it appears in the admin UI. This guide walks you through building a custom report from scratch, including advanced patterns for SQL aggregations and multi-vendor support. Before starting, make sure you understand how the reporting system works.

Creating a Custom Report

Step 1: Create the Report Class

Create a new report class inheriting from Spree::Report. The key method to implement is line_items_scope, which returns an ActiveRecord::Relation defining the records in your report:
app/models/spree/reports/customer_orders.rb
The line_items_scope method has access to:

Step 2: Create the Line Item Class

Create a corresponding line item class that transforms each record into report columns. The class name must match the report class name (e.g., Reports::CustomerOrdersReportLineItems::CustomerOrders):
app/models/spree/report_line_items/customer_orders.rb
Important notes:
  • Use attribute to define columns with their types — these become CSV headers
  • Each attribute needs a corresponding method that extracts/formats data from record
  • record is a single item from line_items_scope
  • Use Spree::Money for currency formatting
  • currency and store are delegated from the report

Available Base Class Methods

Spree::ReportLineItem provides:

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 and column header translations:
config/locales/en.yml
After restarting your application, the new report will be available in Admin > Reports.

Advanced Patterns

Complex Queries with Aggregations

For reports that aggregate data across records, use SQL directly in line_items_scope:
app/models/spree/reports/revenue_by_category.rb
When using aggregated queries, the record in your line item class will have virtual attributes (like total_quantity, total_revenue) available as methods.

Custom Summary Section

Override summary to provide aggregate metrics alongside the line items:
app/models/spree/reports/customer_orders.rb

Multi-Vendor Support

If you’re using Spree Multi-Vendor, filter by vendor when one is selected:
app/models/spree/reports/vendor_sales.rb

Testing Custom Reports

Testing the Report Class

Test that your report’s line_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

Key Testing Patterns

  1. Test scope filtering — verify line_items_scope returns only records matching date range, currency, and store
  2. Test attribute formatting — verify each attribute method returns correctly formatted data
  3. Test CSV output — check headers, csv_headers, and to_csv return expected values
  4. Test edge cases — handle nil values gracefully (e.g., missing addresses)
  • Reports - Report architecture and built-in reports
  • Events - How report generation uses the events system