From 2aeb3bb430185cfe64983e501d0e5d85bca57394 Mon Sep 17 00:00:00 2001 From: delchev Date: Thu, 2 Jul 2026 18:37:16 +0300 Subject: [PATCH] Four management reports over the invoice data - InvoicesByCustomer: count, revenue, paid, balance per customer (cross-model dimension - the customers model's table joined for the name) - InvoicesByStatus: pipeline overview per status - OverdueInvoices: unpaid invoices past due (compound filter due <= CURRENT_DATE AND balance > 0) - SalesByProduct: quantity and revenue per product (cross-model dimension) Rendered by the generated Harmonia report pages (sidebar Reports + dashboard tiles) with typed per-column filters (date/number ranges, text contains) applied server-side. README gains a Reports section. Requires the Dirigible platform report upgrades (eclipse-dirigible/dirigible#6126, stacked on #6125) - a pre-feature platform joins a cross-model dimension against a non-existent local table. Co-Authored-By: Claude Fable 5 --- README.md | 17 +++++++++++++++++ sales-invoices/sales-invoices.intent | 26 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/README.md b/README.md index a39d86b..6ba413b 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,23 @@ primary key - so a plain cascade only declares `filterBy` and a narrow-to-refere auto-allocation settlement leave it empty; it exists to guide **manual** allocation (pick the customer -> see only their payments -> amount pre-filled). +## Reports + +Four management reports over the invoice data ([`sales-invoices.intent`](sales-invoices/sales-invoices.intent) +`reports:`), rendered by the generated Harmonia report pages (sidebar **Reports** + dashboard tiles): + +| Report | Shows | +|---|---| +| `InvoicesByCustomer` | invoice count, revenue, paid and outstanding balance per customer | +| `InvoicesByStatus` | pipeline overview - count and value per status | +| `OverdueInvoices` | unpaid invoices past their due date (listing with a compound filter) | +| `SalesByProduct` | quantity sold and revenue per product | + +`Customer` and `Product` are **cross-model dimensions**: the report joins the owning model's table +and shows the name instead of the raw FK id. Every report table offers **typed per-column filters** +(date ranges, number ranges, text contains) applied server-side - pagination, count and CSV export +all reflect them. + ## One shared shell (no app-hopping) Each domain project generates its own standalone app shell (handy for running or testing a single diff --git a/sales-invoices/sales-invoices.intent b/sales-invoices/sales-invoices.intent index 0147778..2513efc 100644 --- a/sales-invoices/sales-invoices.intent +++ b/sales-invoices/sales-invoices.intent @@ -151,6 +151,32 @@ processes: - { name: cancel, kind: serviceTask, args: { setRelationField: Status, value: 5, next: end } } - { name: end, kind: end } +# Management reports over the invoice data. Dimensions may be plain fields, same-model relations +# (Status - shows the status name) or cross-model relations (Customer / Product - joined against the +# owning model's table, showing the name instead of the raw FK id). The generated report page offers +# typed per-column filters (date/number ranges, text contains) applied server-side. +reports: + - name: InvoicesByCustomer + source: SalesInvoice + description: Invoice count, revenue, paid and outstanding balance per customer + dimensions: [Customer] + measures: ["count(*)", "sum(total)", "sum(paid)", "sum(balance)"] + - name: InvoicesByStatus + source: SalesInvoice + description: Pipeline overview - invoice count and value per status + dimensions: [Status] + measures: ["count(*)", "sum(total)"] + - name: OverdueInvoices + source: SalesInvoice + description: Unpaid invoices past their due date + dimensions: [number, date, due, Customer, total, balance] + filter: "due <= CURRENT_DATE AND balance > 0" + - name: SalesByProduct + source: SalesInvoiceItem + description: Quantity sold and revenue per product + dimensions: [Product] + measures: ["sum(quantity)", "count(*)", "sum(total)"] + forms: - { name: ApproveSalesInvoice, forEntity: SalesInvoice, fields: [number, date, due, Customer, total, Status], actions: [approve, reject] } - { name: IssueSalesInvoice, forEntity: SalesInvoice, fields: [number, date, Customer, total, Status], actions: [issue] }