Skip to content

Reports

Genie offers two distinct kinds of report, for two different needs:

  1. Code-defined reports — a C# class that loads data, formats it, and returns a downloadable file (typically a PDF). Use these for pixel-formatted, printable documents (invoices, statements).
  2. SQL-view report grids — an ordinary read-only *.view.xml over a SQL view. Use these for interactive, filterable, on-screen reporting tables.

A report is a class deriving from ReportBase (implementing IReportBuilder). It has full access to dependency injection (DbContext, session parameters, configuration) and owns its own data loading, formatting and styling. It implements BuildAsync() to return an IReport (content bytes + file name + content type + size).

[Report(Key = "sales.invoice", DisplayName = "Invoice", Category = "Sales")]
public class InvoiceReport(
ILogger logger,
ISessionParametersService sessionParams,
IServiceProvider services) : ReportBase(logger, sessionParams, services)
{
public override async Task<IReport> BuildAsync()
{
var invoiceId = GetParameter<long>("invoiceId");
// load data, render a PDF, return an IReport …
}
}

Reports are discovered by reflection over the [Report] attribute — no manual wiring:

  • AddReportsFromAssemblies(...) / AddReportsFromCurrentDomain(...) register each report type in the DI container (scoped, so it gets a DbContext).
  • RegisterReportsFromAssemblies(...) / RegisterReportsFromCurrentDomain(...) register them in the ReportRegistry by their Key.

At request time ReportService looks the key up in the registry, resolves a fresh scoped instance, passes the parameters via WithParameters(...), and calls BuildAsync().

Method Route Returns
GET /api/v1/genie/report/{reportName} the generated file (FileResult)

Any query-string values (except reportName) are passed through as report parameters. Read them in the builder with the typed helper GetParameter<T>("key").

GET /api/v1/genie/report/sales.invoice?invoiceId=42

Many “reports” need no code at all — they are just a read-only grid over a database view. Author a normal object view whose <Sql> selects from a SQL view, and turn off CRUD:

<Table Name="LowStockReport" Slug="low-stock-report" Label="Low Stock"
DisableActions="true" DisableControls="true">
<ParentView Name="catalog" />
<Sql PrimaryKey="ProductId" SortBy="Shortfall" SortDirection="DESC" PageSize="25">
<![CDATA[
SELECT v.ProductId, v.Sku, v.Name, v.OnHand, v.ReorderLevel, v.Shortfall
FROM [Inventory].[vw_LowStock] v
WHERE v.CompanyId = @SessionCompanyId
]]>
</Sql>
<Columns>
<Column Name="Shortfall" Label="Shortfall"
StyleClassesExpression="If(Shortfall &gt; 10, 'badge bg-danger', 'badge bg-warning')" />
</Columns>
</Table>

DisableActions/DisableControls make it a pure report — no create/edit/delete. It still gets paging, sorting, per-column search, lookups and expression-driven styling from the standard object pipeline, and it is permission-filtered and tenant-scoped like any other view. The same SQL view can also back a navbar counts badge.

See sample/Inventory/models/views/LowStockReport.view.xml and CategoryStockReport.view.xml.

Need Use
Printable/downloadable document, precise layout Code-defined report (ReportBase + PDF)
Interactive on-screen table, filter/sort/search SQL-view report grid (*.view.xml)