Skip to main content
Version: 2.0

Reports

Feature NameReports
Feature IDCrestApps.OrchardCore.Reports
DependencyCrestApps.OrchardCore.Resources

The Reports module is a reusable reporting framework. It provides a single admin Reports area and a small contract that any module can implement to surface an industry-standard report — with a shared from/to date-range filter, extensible filters, a uniform renderer (metric cards, tables, bars, and interactive charts), and pluggable exports (CSV built in). The feature depends on CrestApps Resources and loads its named chart.js resource only when the current report contains a chart section. Modules such as Omnichannel and Phone Number Verifications contribute their reports through this framework so every report looks and behaves the same.

Add-on Feature NameReports (OpenXml)
Add-on Feature IDCrestApps.OrchardCore.Reports.OpenXml

The optional Reports (OpenXml) add-on extends the Reports area with Excel workbook (.xlsx) exports using the DocumentFormat.OpenXml library. When the add-on is enabled alongside other exporters, report pages collapse those formats into a single Export dropdown so operators can choose the file type they want.

The implementation is split into three layers:

  • CrestApps.OrchardCore.Reports.Abstractions defines the shared report contracts and document models, including IReport, IReportExportFormat, IReportManager, and IReportExportManager.
  • CrestApps.OrchardCore.Reports.Core contains the default non-Orchard-specific implementations such as the report/export registries and the built-in CSV export formatter.
  • CrestApps.OrchardCore.Reports contains Orchard-specific wiring such as the admin menu, controller, views, and the display-driver-based filter UI.

Concepts

  • IReport — a report definition. It declares a technical Name, a DisplayName, a Description, a Category (used to group reports in the menu), a Permission, and a RunAsync method that returns a ReportDocument for a given ReportContext.
  • ReportFilter — the filter applied when a report runs. Every report shares a tenant-local from/to date and time range; additional, report-specific filters are contributed with display drivers and flow through exports unchanged.
  • ReportDocument — the uniform result. It is an ordered list of sections, where each section is a set of metric cards, a table (with optional emphasized totals rows for aggregated reports), horizontal bars, or a responsive Chart.js line, bar, stacked-bar, or doughnut chart. The same document is rendered in the browser and serialized by every exporter; chart exports use a label-and-dataset table so the underlying values remain portable.
  • IReportExportFormat — an export format. CSV ships in the box; the optional Reports (OpenXml) add-on adds Excel (.xlsx); and any module can add more formats by registering another implementation.

Reports area

Enabling the feature adds a top-level Reports item to the admin menu. Reports are alphabetized within consistently ordered role-based groups: Executive, Operations, Queue & Routing, Agent Performance, Workforce & Payroll, Billing & Usage, CRM & Campaigns, Compliance & Audit, Technical & IT, and General. Each entry is gated by the report's own permission, so a user only sees the reports they are allowed to run. Selecting a report opens a page with the filter form, the rendered document, and export actions for the current filter. A single enabled exporter renders as a normal button, while multiple enabled exporters render as an Export dropdown that can download CSV and, when the add-on is enabled, Excel (.xlsx).

Date range filter

Every report shares a single tenant-local Date range control rendered by the built-in filter. Instead of two separate date inputs, it is a dropdown that offers common presets grouped into Relative days (Today, Yesterday, Last 7 Days, Last 30 Days, Last 90 Days), Calendar periods (This Week, Last Week, This Month, Last Month, This Quarter, Last Quarter, This Year, Last Year), and Rolling months (Last 3 Months, Last 6 Months, Last 12 Months) — plus:

  • Custom Range — two date-time inputs (from and to) editable with Flatpickr.
  • On or before — a single date-time picker that sets only the upper bound, leaving the start open.
  • On or after — a single date-time picker that sets only the lower bound, leaving the end open.

The dropdown button always shows the current selection as readable text (for example, From Jan 1, 2026 to Jan 31, 2026, On or before Jan 31, 2026, or a preset name followed by its resolved range). Presets are computed in the browser using the current culture's first day of the week, and the selected range is written into the underlying from/to fields, which are converted to UTC before the report runs. The control is rendered by the reusable DateRangePicker view component (backed by the date-range-picker resource) provided by the CrestApps Resources feature, so every report presents the same date-range experience and any module can reuse it. See the Resources documentation for details.

Extensible filters

Every report automatically gets the shared date range described above, converted to UTC before execution. To add a report-specific filter (for example a queue, campaign, or channel selector), register a display driver for ReportFilter and gate it to your report by checking filter.ReportName:

public sealed class MyQueueFilterDisplayDriver : DisplayDriver<ReportFilter>
{
public override IDisplayResult Edit(ReportFilter filter, BuildEditorContext context)
{
if (!string.Equals(filter.ReportName, "my-report", StringComparison.Ordinal))
{
return null;
}

return Initialize<MyQueueFilterViewModel>("MyQueueFilter_Edit", model => { /* ... */ })
.Location("Content:2");
}

public override async Task<IDisplayResult> UpdateAsync(ReportFilter filter, UpdateEditorContext context)
{
if (!string.Equals(filter.ReportName, "my-report", StringComparison.Ordinal))
{
return null;
}

var model = new MyQueueFilterViewModel();
await context.Updater.TryUpdateModelAsync(model, Prefix);
filter.Properties["QueueId"] = model.QueueId;

return Edit(filter, context);
}
}

The report reads the bound value from context.Filter.Properties when it runs. Because browser display and export use the same filter-building path, custom filter values must be applied consistently in both outputs.

Contributing a report

Implement IReport and register it as a scoped service:

services.AddScoped<IReport, MyReport>();

RunAsync builds a ReportDocument from the resolved period (context.FromUtc / context.ToUtc) and any report-specific filter values. Use ReportSection.ForMetrics, ReportSection.ForTable, ReportSection.ForBars, and ReportSection.ForChart to compose the document, and ReportFormat to format numbers, durations, and percentages consistently. Charts accept ordered labels plus one or more numeric datasets; Width places sections on the shared responsive twelve-column layout.

Enable via recipe

{
"steps": [
{
"name": "Feature",
"enable": [
"CrestApps.OrchardCore.Reports",
"CrestApps.OrchardCore.Reports.OpenXml"
]
}
]
}

Enable CrestApps.OrchardCore.Reports.OpenXml only when you want Excel workbook exports. Enabled modules such as Omnichannel Management and Phone Number Verifications contribute their reports automatically once CrestApps.OrchardCore.Reports is enabled.