Files
edr-platform/apps
ghost2023 91c9c3e513 feat(reports): list unbilled categories at zero in revenue-by-category
A category with no invoice lines in a period simply had no row, so a
category going quiet was indistinguishable from one that never existed,
and filtering to a category that was never billed returned an empty table.

The query is now three levels. The aggregate groups as before. A grid
crosses every period that saw revenue with every category the filter
allows, and LEFT JOINs the aggregate onto it so a missing combination
lands at zero. The wrapper does the display rounding and the labelling.

Two things had to move for that to be correct:

- The lag() window is now in the wrapper. A window function only sees the
  rows its own query level produces, so left on the aggregate it would
  skip a category's silent periods — billed in January and March, it
  would read March's prior as January and report flat growth.
- The category filter is off the aggregate and enforced by the grid's
  category list. Filtering the aggregate too would make the period axis
  depend on the selection, which is what left the table empty when the
  selected category had never been billed.

Periods come from the data, not generate_series over the date filter: a
twelve-month range over one billed month would otherwise publish eleven
months of pure zeros, and daily granularity would multiply that by thirty.

The Categories KPI is now "Categories with revenue" — a bare count of live
categories reads as a contradiction next to a table listing all fourteen.

EXPLAIN-validated against the dev database across seven filter shapes,
including the empty-array case (hence unnest(ARRAY[...]) over VALUES,
which is a syntax error when empty).

Claude-Session: https://claude.ai/code/session_01LoY3hNWqcaAC1pYmGPN7jr
2026-08-21 15:51:18 +03:00
..