Creating a report
:::info Status Outline — expand with a full worked example (e.g. adding a new parameterized report with a grouping/aggregate column). :::
:::tip Use the shipped skill
Prefer the create-report skill (in your scaffolded repo's .claude/skills/) over
hand-rolling this. Adding or changing a CSV/PDF export on the result is a separate skill,
create-print-form.
:::
A Report is a parameterized, read-only query — no posting, no persistence of its own. It's
also the one entity kind with a fully generated Blazor UI end to end (ReportUiEmitter) —
there are no hand-written report pages left in the reference configuration.
The pieces
*Base/[KandraReportEntity], *Dto/[KandraReportForm], a filter/parameters shape, and a
*Validator. Reports don't have a *Behavior in the same sense as Dictionaries/Documents —
the query logic lives in the report's own execution service instead.
Shared filter shape
Use DateRangeDto for any From/To date-range parameter rather than ad-hoc separate
properties — it's the shared convention across every report and rippled into Document list
pages too.
Grouping and aggregates
[Aggregate]/AggregateKind/ICustomAggregate drive grouped-column totals in the generated
MudDataGrid. ReportGroupingState carries the current grouping across renders.
Gotchas worth knowing before you start
IsCollectionOfGuidfield discovery previously missedIReadOnlyList<Guid>(fixed, but worth knowing the discovery logic's shape if a collection field doesn't render as expected).- A nullable-reference type's
ToDisplayString()includes a trailing?— strip it if you're building a custom display formatter. ReportPageBaseneeds aDataProcessorPageBase-style null-Filtersrender guard if your report's filter object can be null before first load.