Foodops
All docs

Back Office · Updated 2026-08-24 · vfda5cc3

Foodops Reports Catalogue

Audio narration for this article is generated with the weekly docs refresh.

Plain-English summary

The Reports area lives in the back-office admin app (admin.foodops.io). It's a persistent left-rail of pre-built reports — around 28 for the foodops product — each its own route (/reports/<name>). Each report is a self-contained view with filters (date range, outlet, register, order type, category) and a results table. A handful of reports also render live in the counter app (e.g. the end-of-day Day Summary at /reports/day-summary).

New-stack note (July-2026 launch): the legacy app.foodops.io/reports hub (one page, 34 cards across 9 categories, /reports/view/{slug} links) is RETIRED. Reports are now individual React routes in the admin app. Most of the catalogue carried over; the route convention changed from /reports/view/{slug} to /reports/<name> (see the route map below). Brand-Contribution and Order-Quantity reports, Invoice/Sales-Order/Quotes Details, and Purchase-Receive/Expense-Record Details were not ported into the new admin report set; four new reports were added — Sales By Daypart, Sales By Service Type, Voids & Comps, Covers.

When you'd use this

End of day: run Day Summary to tally receipts vs cash for register reconciliation. End of month: Monthly Brand Contribution, Revenue Contribution (%), Output Tax Statement for accounting and GST filing. Weekly operations: Sales By Products (what's selling), Inventory Summary (what's in stock), A/R Aging Summary (who owes us). Ad-hoc: any report can be date-filtered to analyse a specific event, shift, or campaign.

How to access

  • URL: each report is its own route — /reports/<name> on admin.foodops.io (e.g. /reports/day-summary, /reports/sales-by-products, /reports/payments). See the full route map below. (Legacy /reports/view/{slug} is retired.) [code: foodops-admin-app/apps/admin/src/App.tsx:1266-1549]
  • Navigation: the admin app keeps a persistent Reports rail (ReportsSidebar, eagerly imported so it survives across report navigations) — jump between reports without leaving the area. [code: foodops-admin-app/apps/admin/src/pages/reports/ReportsSidebar.tsx]
  • Counter app: the end-of-day Day Summary report also renders inside the counter app at /reports/day-summary for on-the-floor close-out. [code: foodops.counter.app/src/pages/DaySummaryReportPage.tsx]

Legacy reports hub layout — superseded by the per-route admin reports area

KPI tiles at the top of the hub

Unverified on the new stack. The four-KPI strip described below was the legacy /reports hub header. The new admin reports area is per-route with a left rail; whether a KPI strip persists on the admin /reports landing (ReportsPage) is not yet confirmed in code. The live KPI snapshot for foodops is now the admin dashboard stat band (see Dashboard).

Tile Formula Example
Total Sales This Month Sum of closed order totals for current month; % vs previous month MVR 64.80 · 0.00% vs last month
Total Orders Count of closed orders this month 1
Average Order Value Total Sales ÷ Total Orders MVR 64.80
Conversion Rate (Closed orders ÷ initiated orders) × 100 0.00%

The tiles refresh on page load — no manual submit needed.

Category 1 — Sales Reports

Report Route (admin.foodops.io) Primary use
Day Summary — listed in the rail as End of Day /reports/day-summary End-of-day register close-out: receipts, payments, voids, cash drawer tally per session. See End of Day Report (Day Summary). [code: foodops-admin-app/apps/admin/src/config/reportsConfig.ts:154-166 — 'Daily Operations' → item title 'End of Day' @ main]
Sales Details /reports/sales-details Real-time transaction-level view with filters for outlet, register, payment method
Sales By Customer /reports/sales-by-customer Top customers by spend; spotting regulars vs one-timers
Sales By Products /reports/sales-by-products Best-sellers: quantity sold, sub-total, tax, total, average price per product. "Top N%" filters built in
Sales By Daypart /reports/sales-by-daypart NEW — sales broken down by part of day (breakfast/lunch/dinner peaks)
Sales By Service Type /reports/sales-by-service-type NEW — sales split by dine-in / takeout / delivery
Voids & Comps /reports/voids-and-comps NEW — voided and complimentary (FOC) items/orders audit
Covers /reports/covers NEW — covers (guests served) per period

Example — Day Summary sections

Day Summary report showing Receipt Summary (no. of customers/payments/receipts, per-customer/receipt averages, voids), Sales Summary (discounts, FOC, refunds, tax, service charge, delivery charge, totals, outstanding, received), Category Summary, Cash Summary by register session

Day Summary is organised into four blocks:

  • Receipt Summary — No. of customers, payments, receipts, sales per customer, sales per receipt, no. of void receipts
  • Sales Summary — Discounts, FOC, Refunds, Tax, Service Charge, Delivery Charge, Total Amount, Total Sales, Outstanding, Received Amount
  • Category Summary — Per menu category: sales count and total
  • Cash Summary — Register-level cash accounting

Filters: Date Range, Outlet, Register, Report By (Register Session / Order Time).

Example — Sales By Products

Sales By Products with Date Range, Order Type multi-select (Dine-In/Takeout/Delivery), Category multi-select, Outlet, Register filters. Main table columns: PRODUCT, QUANTITY SOLD, SUB TOTAL, TAX TOTAL, TOTAL, AVERAGE PRICE. Top-N% filter (Show All/Top 10%/15%/20%/30%/40%)

Filters combine: Date Range + Order Type (multi) + Category (multi) + Outlet + Register. Results show each product's quantity sold, sub-total, tax total, total, average price.

Category 2 — Brand Reports (NOT in the new admin report set)

These two Brand-Contribution reports were in the legacy monolith catalogue but have not been ported into the new admin reports area (no brand-contribution route exists at foodops-admin-app@4173811). There is no dedicated brand report at launch; multi-brand orgs should filter Sales By Products / Sales Details by category in the meantime.

Report (legacy) Legacy slug Status
Brand Contribution By Numbers brand-contribution-totals Not ported to new admin
Monthly Brand Contribution brand-contribution-by-product-category Not ported to new admin

Category 3 — Revenue Reports

Report Route (admin.foodops.io) Primary use
Monthly Revenue by Product Category /reports/monthly-revenue-by-product-category Matrix: rows = categories, columns = months
Revenue by Product Category /reports/revenue-by-product-category Single-period breakdown by category
Revenue breakdown by Month /reports/revenue-by-month Year-over-year monthly trend
Revenue Contribution (%) /reports/revenue-contribution % share of each category/product in total revenue

Category 4 — Order Reports (NOT in the new admin report set)

The two Order-Quantity-by-category reports were not ported into the new admin reports area (no matching route at foodops-admin-app@4173811). The closest replacements in the new set are Sales By Products and the new Covers / Sales By Service Type reports.

Report (legacy) Legacy slug Status
Order Quantity breakdown by Product Category order-quantity-breakdown-by-product-category Not ported to new admin
Order Quantity totals by Product Category order-quantity-total-by-product-category Not ported to new admin

Category 5 — Accounts Receivable

Report Route (admin.foodops.io) Primary use
A/R Aging Summary /reports/aging-summary Who owes you, bucketed by age (0-30/31-60/61-90/90+ days)
A/R Aging Details /reports/aging-details Same, but with per-invoice detail rows
Customer Balances /reports/customer-balances Current outstanding per customer
Receivable Summary /reports/receivable-summary High-level A/R metrics
Receivable Details /reports/receivable-details Transaction-level receivable records
Invoice Details — (not ported) Legacy report; in the new app, browse invoices at /sales/invoices
Sales Order Details — (not ported) Legacy report; orders live under /sales/orders
Quotes Details — (not ported) Legacy report; quotes live under /quotes

Category 6 — Payments Received

Report Route (admin.foodops.io) Primary use
Payments Report (Payments Received) /reports/payments All received payments with method, date, amount
Refund History /reports/refund-history All refunds issued
Credit Note Details /reports/credit-note-details Credit notes issued to customers

Category 7 — Tax Reports

Report Route (admin.foodops.io) Primary use
Input Tax Statement /reports/input-tax-statement Taxes paid on purchases/expenses — GST-claim-back prep
Output Tax Statement /reports/output-tax-statement Taxes collected on sales — monthly GST filing

Note: a filing-grade VAT / GST Return report (/reports/vat-return) also exists, but it sits in the finance module — it is tree-shaken out of the foodops build and only appears on finance-v2. The two tax statements above are in the foodops report set.

Category 8 — Inventory Reports

Report Route (admin.foodops.io) Primary use
Inventory Summary /reports/inventory-summary Current stock levels + availability status
Committed Stock Details /reports/committed-stock-details Stock allocated to open orders or production
Inventory Valuation Summary /reports/inventory-valuation-summary Balance-sheet view: stock × cost = asset value
Inventory Aging Summary /reports/inventory-aging-summary How long each batch has been in stock — flags slow movers
Product Sales Report /reports/product-sales Sales performance with inventory turnover metrics
Stock Summary Report /reports/stock-summary Consolidated stock levels, locations, availability

Category 10 — Kitchen & Food Cost (restaurant brand)

These are the costing/kitchen reports, grouped under Kitchen & Food Cost in the reports hub. They're restaurant-oriented and read from your recipes, ingredient costs and wastage logs. [code: foodops-admin-app/apps/admin/src/config/reportsConfig.ts — group 'Kitchen & Food Cost' @ main]

Report Route (admin.foodops.io) Primary use
Food Cost % /costing/food-cost Food cost as a share of sales — the single most important kitchen KPI
Cost Variance /costing/variance Theoretical vs actual ingredient usage — where money is leaking
Recipe Variance /restaurant/recipe-variance Theoretical vs actual cost per dish — spots kitchen drift and over-portioning
Recipe Costing /costing/recipes Plate cost per item, gross margin and target food-cost analysis
Wastage Log /costing/wastage-logs Logged spoilage, breakage and prep loss, broken down by reason
Yield Analysis /costing/yield Trim and prep yield variance against expected recipe yields

If these numbers look wrong, the usual cause is upstream data, not the report: Recipe Costing / Food Cost / Recipe Variance depend on each item having a recipe with ingredient quantities and ingredient costs — an item with no recipe, or ingredients with no cost, reads as zero or missing. Wastage Log only shows what was actually logged as wastage. Yield Analysis compares against the expected yield set on the recipe, so a missing or wrong expected-yield makes the variance meaningless. Check the item's recipe and ingredient costs first.

Category 11 — Restaurant Analytics (restaurant brand)

Report Route (admin.foodops.io) Primary use
Outlet Performance /restaurant/outlet-performance Compare net sales, COGS, gross profit and margin across outlets
Supplier Price Alerts /restaurant/supplier-price-alerts Suppliers raising prices on items you buy — catches quiet hikes

Outlet Performance numbers off? It compares outlets on net sales, COGS, gross profit and margin — the COGS/margin columns depend on the same recipe-and-cost data as the Kitchen reports above, so an outlet whose items lack costed recipes will show a distorted margin. [code: reportsConfig.ts — 'Outlet Performance' description: "Compare net sales, COGS, gross profit and margin across outlets" @ main]

Category 9 — Purchase (NOT in the new admin report set)

The two Purchase reports were not ported into the new admin reports area as dedicated report routes. In the new admin app, browse this data operationally under Expenses/expenses/purchase-receipts (stock receiving) and /bills / /expenses/suppliers.

Report (legacy) Legacy slug Status
Purchase Receive Details purchase-receive-details Not ported; see /expenses/purchase-receipts
Expense Record Details expense-record-details Not ported; see /bills

The standard report view

Every report has the same three-part structure:

  1. Top bar — Report title, Reports nav, filter controls (date range, outlet, register, etc.), Submit button to re-run. (Day Summary's dedicated History button/modal was removed 2026-07 in favor of a shared list-view export dialog + bottom-right ExportProgressTray — foodops-admin-app@6642e57; whether other reports still expose a per-report History nav is unverified.)
  2. Header metadata — Organization name ("KB Test Restaurant Pvt Ltd"), report name, date range
  3. Body — Metric blocks or tabular data, with totals row

Common filters across reports

Filter Appears on Type
Date Range All reports Date picker with presets (Today, Yesterday, This Month, Last Month, Custom)
Outlet Multi-outlet orgs Dropdown
Register Sales reports Dropdown
Report By Day Summary Register Session / Order Time
Order Type Sales By Products, Sales Details Dine-In / Takeout / Delivery multi-select
Category Sales-by-product family Multi-select from product categories

Key concepts

  • Route — each report has a stable route (e.g., /reports/day-summary) on admin.foodops.io. Link to reports by route, not by ephemeral ID. (The legacy /reports/view/{slug} pattern is retired with the monolith.)
  • Report By (Day Summary) — switches the bucketing axis. "Register Session" groups cash/totals by each open-close cycle of a register; "Order Time" groups by calendar day regardless of session.
  • FOC — "Free Of Charge" — comped items/orders. Tracked separately from discounts because the tax treatment differs.
  • Committed Stock — stock reserved for orders that haven't shipped yet. Unlike sold stock, it's not deducted from on-hand until the order closes.
  • Brand — an organizational layer above categories, used when one Foodops org runs multiple concepts. Brand Reports only show content for orgs that have configured brands.
  • Top N% — the Sales By Products filter lets you show only the top-performing products (by revenue). Useful for 80/20-type analysis.

Common questions

Q: Which report should I run at end of day? A: Day Summary. Filter by today's date, pick the register(s) you closed, set Report By = Register Session. Reconcile the Cash Summary against the physical cash in the drawer.

Q: How do I see each day's sales? / Where is the daily sales report? / How do I know the sales of the day? A: Run the Day Summary report (/reports/day-summary). Set the Date Range to the day (or a preset like Today / Yesterday) and Submit — it tallies that day's sales (Total Sales, Total Amount, Received, Outstanding), payments by method, category breakdown, and per-register cash. To compare several days side by side, use Sales Details (/reports/sales-details) with a multi-day date range, or Revenue breakdown by Month (/reports/revenue-by-month) for a longer daily/monthly trend. The counter app also shows the same Day Summary at /reports/day-summary for an on-the-floor read without leaving the till.

Q: How do I add / record expenses? Where do daily costs go? A: Expenses are recorded in the admin back-office, not on the till. Use Expenses → Records (/expenses/records/new) to log a one-off or reimbursable expense, or Expenses → Bills (/expenses/bills) for a supplier invoice you'll pay later. See Recording Expenses for the full flow. (Cash physically taken out of the register drawer mid-shift is a different thing — that's a cash withdrawal / pickup on the register session, not a business expense; see the cash-handling entries.)

Q: How do I view the sales order report? A: There is no report literally titled "Sales Order" in the Reports rail — the legacy monolith's Sales Order Details report was not ported to the new admin report set. In the new admin app, sales orders are a document type you view and manage operationally at Sales → Sales Orders (/sales/orders): a filterable, searchable list where you open each order (/sales/orders/:id), create/edit/duplicate them, and export the list to CSV or Excel from the Export dialog. [code: foodops-admin-app/apps/admin/src/pages/sales/SalesOrdersPage.tsx:168,417 (CSV/XLSX export); App.tsx:782-852 (routes)] For order-level sales analytics (what sold, by product/customer/day), use Sales Details, Sales By Products, or Day Summary in the Reports rail instead.

Q: Where's the monthly revenue-vs-target comparison? A: No built-in target-vs-actual report. Use Monthly Brand Contribution or Revenue breakdown by Month and overlay target externally. The top-of-hub KPI tile shows the % change vs last month, which covers the common case.

Q: How do I export a report to Excel? A: Every report view has an Export option (usually top-right of the results area — the three-dot or download icon). Exports CSV for tabular reports and PDF for formatted summaries. Large exports may take a moment.

Q: Can I schedule a report to email me every morning? A: Not from the Reports UI. Scheduled reports are on the roadmap — for now, use the History link on each report to revisit a previously-run version.

Q: The numbers on Day Summary don't match Sales Details — why? A: Day Summary uses register-session buckets by default. Sales Details uses order time. If a register session spans midnight (opened 10pm, closed 2am), Sales Details splits the sales across two calendar days while Day Summary keeps them together. Switch Day Summary's "Report By" to "Order Time" to align.

Q: What's the difference between "Receivable Summary" and "A/R Aging Summary"? A: Receivable Summary is a flat list of outstanding amounts. A/R Aging bucketizes them by age (0-30, 31-60, 61-90, 90+). Use aging to decide who to chase first.

Q: Inventory Valuation Summary — is that current cost or historical cost? A: Current cost, multiplied by current stock. For historical valuation (e.g., "what was this worth on 31 Dec?"), you need to pair with the Inventory Aging Summary or use a date-specific export.

Q: Can non-admin users see reports? A: Per role. By default only admin and manager roles have report access. Configurable in Settings → Users & Roles → Permissions → Reports.

Q: The Conversion Rate KPI shows 0.00% — what conversion is this measuring? A: Initiated orders that reached Closed status vs orders that went through cancellation / abandonment. Low conversion often means a flow issue (payment failed, customer walked out) rather than an inventory problem.

Q: Where are staff/payroll reports? A: Not in this catalogue — staff/payroll reports are either handled in a separate Accounting module (if enabled on plan) or via integration with external payroll software. Users & Roles screen has a basic activity feed per user.

Edge cases and known issues

Report shows 0.00 across the board

  • Symptom: Every cell is 0.00 even though orders exist
  • Cause: Date range defaults to today; running before any orders for the day
  • Workaround: Click the Date Range filter, pick a range you know has sales, click Submit

"No records found" on a report that should have data

  • Symptom: Blank result despite matching orders existing
  • Cause: Filter combination is too narrow — e.g., Outlet + Register + Order Type excludes everything
  • Workaround: Start with just Date Range, confirm you get results, then layer filters one at a time

Slow load on large date ranges

  • Symptom: Spinner for 30+ seconds on a year-wide Sales Details
  • Cause: Report joins transaction tables without pagination
  • Workaround: Narrow the date range; use a monthly summary report (Monthly Revenue by Product Category) for broad trends, Sales Details only for specific investigations

KPI tiles show "0.00% vs last month" even with data

  • Symptom: Percentage always reads 0.00%
  • Cause: No data in the comparison month (newly onboarded org, or a skipped month)
  • Workaround: Once you have two full months of data, the comparison starts working. Before that, ignore the % delta and use absolute numbers

Tax statements don't match accounting software

  • Symptom: Output Tax Statement total differs from what your external accountant calculated
  • Cause: Tax is computed per-line at order close, so mid-period tax rate changes or after-the-fact discount adjustments can cause drift
  • Workaround: Reconcile against Sales Details with tax columns enabled; investigate any order where the line tax doesn't match rate × sub-total

API and integration notes

  • Report pages are React routes in the admin app — foodops-admin-app/apps/admin/src/pages/reports/*ReportPage.tsx, registered in apps/admin/src/App.tsx (paths /reports/<name>, lines 1266-1549 for the foodops set), with the persistent rail in apps/admin/src/pages/reports/ReportsSidebar.tsx.
  • Report data is served by the sales API — hubits-api-sales/Hubits.Sales.Api/Controllers/ReportsController.cs (+ CounterReportsController.cs, Services/Counter/Reports/**); inventory-flavoured reports pull from the inventory API. The by-daypart / by-service-type / voids-comps endpoints added in 2026-05 are now surfaced as the four new admin report pages.
  • Finance reports (P&L, Balance Sheet, Trial Balance, VAT/GST Return, Cash Flow, A/R & A/P Aging, Customer/Supplier statements, period-close) are gated behind INCLUDE_FINANCE and tree-shaken out of the foodops build — they render only on finance-v2. [code: foodops-admin-app/apps/admin/src/App.tsx:201-217]
  • Day Summary also renders inside the counter app (foodops.counter.app/src/pages/DaySummaryReportPage.tsx, components in src/components/reports/*).
  • Filter state and CSV/PDF export are handled per-report in React; the legacy Razor ?format=csv|pdf export endpoints are retired with the monolith.

X-Report & Z-Report (register close-out)

The X-Report and Z-Report are printable back-office artifacts generated from a register session, not from the /reports rail:

  • Open a session at Sales → Register Sessions → (a session)/sales/register-sessions/:id (RegisterSessionDetailsPage) — and print the X or Z report from there.
  • The report HTML is built by apps/admin/src/utils/sessionXZReport.ts buildSessionXZReportHtml({ mode }): mode: 'x' renders "X-REPORT (READING)" (a mid-shift read that does NOT close the session), mode: 'z' renders "Z-REPORT (FINAL)" (the close-out, which adds the counted-cash close + variance). [code: foodops-admin-app/apps/admin/src/utils/sessionXZReport.ts:72 — mode === 'z' ? 'Z-REPORT (FINAL)' : 'X-REPORT (READING)']
  • Sections: Period, Sales (Subtotal, Tax, Nontaxable, Total), Payments (by method), Cash drawer (counted close + variance on Z), Footer (printed-at / printed-by). It auto-opens the browser print dialog (window.print()). [code: sessionXZReport.ts:103-111,281]

What changed since 2026-04-15 (NEW 2026-05)

Reports activity in this window was concentrated on the Payments Report + Day Summary.

Legacy monolith — Payments Report layout fix (NEW 2026-05-03)

  • 46a80a3 2026-05-03 fix(PaymentsReport): update layout to use _CounterLayout — the Payments Report Razor view (Haafai.Sales/Views/Reports/PaymentsReport.cshtml) was migrated to the counter-style layout (_CounterLayout), aligning its appearance with the rest of the POS-facing reports.
  • 7b60f3b 2026-05-03 update payments report to match pos app — paired companion commit; touched Haafai.Sales/Views/Reports/PaymentsReport.cshtml, _PaymentsReport.cshtml, _PaymentsReportPrint.cshtml, plus Haafai.Sales/ViewModels/SalesReport/PaymentReceivedReport.cs. View-model + view + print-view all moved to the new layout.
  • 11b1995 2026-04-20 cashwithdrawal/session amount fix — Day Summary view models updated to surface cash withdrawal details and reconcile session-amount inconsistencies on the report (also touched the Restaurant.App SalesSessions views).
  • e9abffb 2026-04-18 day-summary calc fix — Day Summary "counted" + "difference" amounts now correctly account for payment method visibility (some payment methods were being silently filtered, throwing off totals).
  • 621cc02 2026-04-21 report download fix in notificationshaafai.app.common package version bumped to 1.0.57 to fix a "report download failed from notifications" issue.

Counter app — Day Summary report polish

  • ca3029f 2026-04-29 fix(DaySummaryReportPage): remove reportData dependency from export-button disabled state — export buttons no longer disable when reportData is empty (was a UX regression).
  • 9f56b2e 2026-04-28 — fix to "unable to download day summary report".
  • ee4f532 2026-04-28 fix(DaySummaryReportPage): hide export and history buttons for future re-enablement — temporary toggle while the export pipeline is rebuilt; the buttons are present in code but hidden in UI.
  • a19c746 2026-04-24 refactor: remove 'Report By' selection — the "Report By" axis (Register Session vs Order Time) was simplified out of the React DaySummaryReportPage. The Q&A about Report By in this entry references the legacy monolith report; counter.app's React view has dropped this filter. Note this discrepancy when running playwright re-verification.
  • b228396 2026-04-24 layout enhancement — DaySummaryReportPage layout polish.

Verified 2026-05-04 (Playwright):

  • Payments Report layout: the Payments Report at /reports/paymentsreport is now a React page (counter-style layout) with columns: REFERENCE / PAYMENT DATE / PAYMENT REFERENCE / REMARKS / CUSTOMER / PAYMENT METHOD / INVOICES / DEPOSIT TO / TOTAL / UNUSED. Filters: Date Range (start + end) only — no Outlet or Register filter. Export (PDF), export (CSV), and History buttons are all visible at top-right.

    Payments Report — counter-style layout with date-range filter and REFERENCE/PAYMENT DATE/PAYMENT REFERENCE/REMARKS/CUSTOMER/PAYMENT METHOD/INVOICES/DEPOSIT TO/TOTAL/UNUSED columns

  • Day Summary export + History buttons: commit ee4f532 noted them as temporarily hidden, but as of 2026-05-04 they are visible — PDF export, CSV export, and History buttons all appear at the top-right of the Day Summary Report page. The export-button re-enable may have been completed via commit ca3029f (2026-04-29) which removed the disabled-state dependency. The buttons are functional in the current build.

    Day Summary Report — Date Range, Outlet, Register, Report By filters; PDF/CSV export + History buttons visible at top-right

  • Report By selector (Day Summary): the React DaySummaryReportPage (counter.app) shows "Report By" with "Register Session" as the default option — the a19c746 removal noted in codebase was evidently reverted or re-added. The Q&A about Report By = Register Session for end-of-day reporting applies to both surfaces as of 2026-05-04.

What changed since 2026-05-04 (NEW 2026-06)

The user-facing report catalogue (34 reports / 9 categories) is unchanged. Activity this window was backend + display polish:

  • New sales-analysis API endpoints — by daypart, service type, and voids/comps (bcaadac6 2026-05-06) — added to Hubits.Sales.Api/Controllers/ReportsController.cs, but these are API-only: no corresponding report cards exist in the hub (legacy Views/Reports/) or in the counter app, so they don't appear in the catalogue above. Likely groundwork for future report cards. [code: hubits-api-sales/Hubits.Sales.Api/Controllers/ReportsController.cs — bcaadac6]
  • Organization timezone integrated into report date/time formatting (b7b92257, cdd45cee 2026-05-17) — report components (register sessions, day summary) now format dates/times in the org's timezone (persisted to localStorage, routed via parseServerDate). Cosmetic — fixes off-by-timezone display on session timestamps, doesn't change report figures. [code: foodops.counter.app/src/components/reports/RegisterSessions.tsx — cdd45cee]
  • Currency-handling + cash-difference fixes in PaymentMethodSummary / RegisterSessions (c1dd098c, 24d27baf 2026-05-25) — better multi-currency rendering and a corrected cash-difference calculation on the Day Summary cash blocks. [code: foodops.counter.app/src/components/reports/PaymentMethodSummary.tsx — c1dd098c]

Note: the brief's "reports driver" 0198d25 is actually a counter.app commit adding address/contact/customer sub-resource services (not report content) — no report-catalogue impact.

Audit log — who changed what (and change notifications)

This moved to its own entry so it retrieves on its own: Change Notifications & the Audit Log. Short version — there are no push alerts on edits (the notification bell carries export/background-task items only); instead you review changes on demand via Settings → Audit Log and per-record History tabs (pull, not push).

Related

  • Inventory — data source for Inventory Reports

  • Purchasing & Suppliers — data source for Purchase reports

  • Recipes & Ingredients — feeds food-cost side of reports

  • Settings Overview — report permissions are role-based

  • User Stories: Reporting — operational scenarios

  • 2026-08-18: hubits-api-sales (144add7..98c202d) — most of this range's diff hunks against the entry's cited surface duplicate what's already captured verbatim in the 2026-08-03 through 2026-08-11 verification notes above (the export-columns endpoint, the Aging Summary/Details ReportPredicates.SoldInvoice fix, the Payments Received DateField modified/payment filter, PosDefaultViewMode/SoftEnforcePaymentReference session fields, the OpenedTime UTC ISO-8601 fix, the OpenRegister ExpectedAmount-seed fix, and the Day Summary cash-withdrawal ADR-0011 fix) — no new information from those parts. What IS new: GetPaymentsReceived (backs the Payments Report, Category 6, /reports/payments) now excludes voided payments from its summary/totals block (.Where(p => p.PaymentStatusId != ModelConstants.PaymentStatus.PaymentVoid)) even when the caller's IncludeVoided flag put voided rows into the result set — per the inline comment, 'a void moved no money, so summing it reports funds that do not exist,' while the individual rows still display (each carrying its own status/badge, per the 2026-07-23 note about the red 'Void' badge). This doesn't contradict any documented claim — the entry's Category 6 description only says 'all received payments with method, date, amount', with no claim about how totals handle voided rows — but it's a real behavior a user could stumble on: ticking 'Include voided payments' will surface voided rows inline without moving the summary totals. Separately and unrelated to Reports: this same diff range adds an EnforceUniqueBlazeNumber field to the open-sessions payload (SessionsController.GetMySessionsAndRegisters) — per comment, already present on the session-details payload for the Issues tab/close gate, now also stamped on the open-sessions list so the counter can gate a Transfer-Receipt reference-reuse warning at the payment surface — this is POS/session settings plumbing, not report content, no catalogue impact. Worth a Playwright re-check next pass on /reports/payments with a voided payment present and 'Include voided payments' ticked, to confirm the summary/total row excludes it while the row itself still appears with its Void badge. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-08-14: foodops-admin-app (47ff627..f9cc71d) changes the production deploy workflow's health-check target for the finance-brand deploy job from https://finance-v2.hubits.io/health to https://finance.hubits.io/health (.github/workflows/deploy-production.yml, the name: finance-v2 / brand: finance / host_secret: EC2_HOST_ADMIN_FINANCE_V2 entry). This is the same deploy target this entry's VAT/GST-Return note refers to when it says the report 'is tree-shaken out of the foodops build and only appears on finance-v2.' It's unclear from this diff alone whether this reflects a subdomain rename (finance-v2.hubits.io → finance.hubits.io) or a fix to a health check that was already pointed at the wrong URL for a build that was always served at finance.hubits.io — either reading means 'finance-v2' can no longer be treated as a confirmed-current hostname for the finance build. This doesn't strictly contradict the entry's text (which names the deploy target, not a specific URL, and asserts nothing about routing/tree-shaking that this diff touches), so no forced edit — but it's worth a Playwright check next pass on whether https://finance.hubits.io (not finance-v2.hubits.io) is now the correct address to send a user looking for the VAT/GST Return report. The rest of this diff range (the 'Quick range' date-preset chip-row series, and the new App.tsx ErrorBoundary/RouteErrorBoundary/lazyWithReload chunk-resilience layer wrapping the Dashboard/Finance/CustomerStatements/Salesmade/Assistant pages) duplicates what the 2026-08-03, 2026-08-04, and 2026-08-11 verification notes above already captured — no new information there, same open question about whether report pages get the same chunk-404 self-heal treatment. The purchase-receipts.spec.ts test change (Import-from-Document now routes to the Documents inbox instead of opening a modal) sits outside this entry's cited surface (Expenses/Purchase Receipts, not Reports) — no impact here. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-08-11: foodops-admin-app (47ff627..4e51283, quick-date-filter series culminating in the 'Quick range' chip row) also lands a chunk-load resilience layer touching App.tsx — the file this entry cites for both the report-route registration (App.tsx:1266-1549) and the INCLUDE_FINANCE tree-shake gate (App.tsx:201-217). New ErrorBoundary/RouteErrorBoundary components (with tests) catch render-time errors so a crash degrades to a per-route 'Reload page' recovery panel instead of React 18 blanking the entire <div id="root">; RouteErrorBoundary keys on pathname so navigating to a different report self-heals the error state, and per its own comment the surrounding chrome (i.e. the reports rail) stays mounted through a crash. Paired with this, lazy(...) is replaced by a new lazyWithReload(...) wrapper for DashboardPage, FinanceDashboardPage, CustomerStatementsPage, SalesmadeDashboardPage, and AssistantHomePage — per the added comment, this exists because a deploy replaces the container outright, so a tab left open beforehand requests hashed chunks that 404; lazyWithReload turns that into one self-healing reload instead of an uncaught rejection. The visible diff hunks do NOT show the individual /reports/<name> report-page constants (day-summary, sales-by-products, etc.) being converted from lazy to lazyWithReload, nor show RouteErrorBoundary being wired around the report route tree specifically — only the five dashboard-family pages above are confirmed touched here, so it's unconfirmed whether report pages get the same chunk-404 self-heal or crash-recovery treatment yet. Nothing in this diff contradicts any documented claim: the INCLUDE_FINANCE ? … : ExcludedRoute tree-shake pattern this entry cites is structurally unchanged (just wraps lazyWithReload instead of lazy), and report routes/paths are untouched. Two things worth a follow-up pass: (1) confirm via Playwright whether report pages (not just Dashboard/Finance/Salesmade/Assistant) get lazyWithReload + RouteErrorBoundary coverage, since a stale-tab-after-deploy 404 on a report chunk is a plausible real-world failure mode for this entry's audience; (2) App.tsx picked up ~10 more lines (two new imports plus an expanded comment block) on top of the ~7-line drift already flagged in the 2026-07-21 verification note, so the App.tsx:1266-1549 (report routes) and App.tsx:201-217 (INCLUDE_FINANCE gate) line citations in this entry are now further stale and should be re-confirmed next time that section is touched. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-08-11: hubits-api-sales (144add7..0d4d26c, export column registries + typed export pipeline series) adds a genuine correctness fix to A/R Aging Summary and A/R Aging Details (Category 5 — /reports/aging-summary, /reports/aging-details), both backed by ReportsController.GetAgingSummary/GetAgingDetails. Both queries previously filtered outstanding invoices only on RecordStatusId == Active (the soft-delete flag) plus Outstanding > 0 and InvoiceDate < asOfUtc; per the inline comment, RecordStatusId doesn't reflect document status, so a voided invoice or one still in Draft — as long as its Outstanding column wasn't zeroed — used to age into the buckets and overstate the receivable. Both endpoints now add .Where(ReportPredicates.SoldInvoice), explicitly described as 'the same predicate the sibling receivable reports use so they cannot disagree' (i.e. aligning with whatever screen Customer Balances/Receivable Summary already use). This doesn't contradict any documented claim in this entry — the Category 5 table only describes the two reports at the 'who owes you, bucketed by age' / 'per-invoice detail' level, with no claim about voided/draft-invoice handling — but it's a real change to what numbers a user sees on these two reports (any org with voided or draft invoices carrying a nonzero Outstanding value should see their aging totals drop). The rest of this diff range (the export-columns endpoint, the Payments Received DateField/modifiedDate sort wiring, the SessionsController PosDefaultViewMode/SoftEnforcePaymentReference/OpenedTime-UTC/ExpectedAmount-seed changes, and the DaySummaryReportService ADR-0011 cash-withdrawal fix) is identical to what's already captured in the 2026-08-03, 2026-08-04, and 2026-08-08 verification notes above — no new information there. Worth a Playwright re-check next pass on /reports/aging-summary and /reports/aging-details for an org with a voided or draft invoice that still shows nonzero Outstanding, to confirm it now drops out of the aging buckets. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-08-08: hubits-api-sales (144add7..cdb3b32) ships a genuine calculation fix to the Cash Summary this entry documents under Day Summary and 'X-Report & Z-Report'. DaySummaryReportService.BuildReportAsync (ADR-0011, 'single-source Expected Cash: pure reader') stops subtracting cashWithdrawalTotal from each register session's cash ExpectedAmount — per the inline comment, that subtraction was wrong twice over: the stored ExpectedAmount already nets that session's own withdrawals, and cashWithdrawalTotal was the sum across EVERY session in the report period, so on a multi-session day one register's cash drops leaked into every other register's Expected figure, and per the comment 'that is what made the Session Summary's Expected Cash disagree with the session details screen' (the Session Summary artifact added 2026-08-01, previously flagged). This directly firms up the accuracy of the per-session Cash Summary block documented in this entry's Day Summary section and the 2026-07-30 'blind cash counting' note (which gates Expected/Difference visibility but doesn't touch the underlying math) — no visibility/permission behavior changes, just a correctness fix to the number shown once revealed. Companion fix in the same diff: SessionsController.OpenRegister now seeds the cash balance's ExpectedAmount to the opening float at register-open time (both the multi-currency and single-currency branches) instead of leaving it 0 — per comment, previously 'a just-opened register show[ed] Opening correctly and Expected as 0.00 until the first payment settled,' which would have shown a misleadingly-zero Expected on a freshly-opened, no-sales-yet register in Day Summary / Session Summary. Neither fix contradicts any entry text (no documented claim asserted a specific Expected-Cash formula or a 0.00-at-open behavior), so no edit is needed — but worth a Playwright re-check on Day Summary's Cash Summary for a multi-session day and a just-opened register to confirm the corrected figures render. Separately in this same diff range, SessionsController.GetMySessionsAndRegisters fixes an unrelated timezone-display bug — OpenedTime now emits an explicit UTC ISO-8601 string (DateTime.SpecifyKind(...,Utc).ToString("O")) instead of a zone-less yyyy-MM-dd HH:mm:ss, so the client no longer misreads a UTC timestamp as local (was showing a just-opened register hours off) — this is open-session-list plumbing, not report content, no catalogue impact. The GetExportColumns endpoint, the Payments Report DateField/modified-date filter, and the PosDefaultViewMode/SoftEnforcePaymentReference session fields also present in this diff range are identical to what's already covered in the 2026-08-03 and 2026-08-04 verification notes above — no new information there. (auto-applied by the truth pipeline; adversarially gated) (auto-applied by the truth pipeline; adversarially gated)

  • 2026-08-04: foodops-admin-app (47ff627..b288f36, quick-filter design/spec docs + 'add shared org-timezone-aware date presets' + 'add the quick date-range chip row') is largely the same 'Quick range' chip-row rollout already captured in the 2026-08-03 verification note (Covers, Credit Note Details, Day Summary, Input Tax Statement, Output Tax Statement, Payments Received gain the chip; Aging Summary and Customer Balances gain only a reportKey prop + columns export arg) — no new edit needed for that part. What's NOT yet captured: PaymentsReceivedReportPage.tsx in this diff also adds a new DateField = 'payment' | 'modified' state (dateField, default 'payment'), explicitly commented as backing a 'Filter by' dropdown, threaded into applied filters, the report query (dateField: applied.dateField), and the export payload (dateField, columns alongside a newly added reportKey="payments-received"). The same commit also renames the payments-table sort key from createdDate to modifiedDate. This is a genuinely new filter dimension for the Payments Report (Category 6) beyond the already-documented Date Range + Voided checkbox, but the diff hunks shown don't include the actual <Select>/dropdown JSX, so the UI control's existence/label/options aren't directly proven here — only the state, query-wiring, and export-wiring are. Doesn't contradict any existing entry text (Category 6 table and the 2026-05-04/2026-07-23 Payments Report filter notes only describe Date Range + the Voided checkbox as filters, neither of which is falsified). Worth a Playwright re-check next pass on /reports/payments to confirm the 'Filter by: Payment Date / Modified Date' control renders, capture its default and label text, and note whether the export column-picker (reportKey+columns) has a visible dialog yet — mirroring the same open question already flagged for Aging Summary/Customer Balances. (auto-applied by the truth pipeline; adversarially gated) (auto-applied by the truth pipeline; adversarially gated)

  • 2026-08-04: hubits-api-sales (144add7..5889642, export column registries + typed export pipeline series) adds a new server-side gate on SessionsController.CloseRegister — a register can no longer be closed while it has an unreviewed 'void bin' entry (VoidBinEntries with ReviewStatusId == Pending); the close request now fails with Success = false, Message = "Review and clear the void bin before closing the register." This is directly relevant to the register close-out flow this entry documents under 'X-Report & Z-Report (register close-out)' and the Day Summary end-of-day reconciliation guidance — closing a session can now be blocked mid-flow by an unreviewed void, which isn't mentioned anywhere in this entry today. The diff doesn't touch foodops-admin-app or foodops.counter.app, so it's unverified whether the counter UI currently surfaces a void-bin review screen/modal in response to this error, or whether it silently fails — worth a Playwright re-check on the register-close flow next pass, ideally provoking a void first. Separately, GetPaymentsReceived (backs Payments Report, /reports/payments) gains a DateField query option: results now filter by UpdatedDate instead of PaymentDate when the caller passes DateField=modified (default unchanged — still PaymentDate), a new ModifiedDate field is exposed per row, and modifiedDate is added as a sort key; per the inline comment this backs a 'Filter by' dropdown in the UI, but no frontend hunk is included so the dropdown's existence is unverified. This doesn't contradict the entry's existing filter/column description for Payments Report, just extends it once confirmed live. Also unrelated to Reports: SessionsController.GetMySessionsAndRegisters now stamps PosDefaultViewMode (org-level tile/compact default) and SoftEnforcePaymentReference (org-level Transfer-payment reference enforcement) onto open sessions — POS/counter settings plumbing, no intersection with any claim in this entry. The export-columns endpoint hunk in this diff range is identical to the one already covered in the 2026-08-03 verification note (df4179d); no new information there. (auto-applied by the truth pipeline; adversarially gated) (auto-applied by the truth pipeline; adversarially gated)

  • 2026-08-03: foodops-admin-app (47ff627..76df568, 'design for quick date filters on report pages' → 'add shared org-timezone-aware date presets' → 'add the quick date-range chip row') adds a new 'Quick range' preset-chip control (ReportDatePresets, backed by a shared useReportDatePresets hook and presetRange()/ymdToStartOfDay/ymdToEndOfDay helpers in utils/datePresets) next to the From/To date pickers on Covers, Credit Note Details, Day Summary, Input Tax Statement, Output Tax Statement, and Payments Received. (Aging Summary and Customer Balances only gained a reportKey prop and a columns argument on their export handlers in this diff, not the chip row itself — hinting at a column-selection export capability with no export-dialog UI visible in these hunks, so unverified.) Selecting a chip both updates the draft From/To fields and immediately commits/re-fetches (onCommit calls fetchData/updates applied state and resets pagination), unlike the plain date pickers which require a separate Apply click. Presets are computed timezone-aware via useOrgTimeZone, consistent with the org-timezone treatment already documented elsewhere in this entry for report timestamps. Default date range on first load also changed on the retrofitted operational pages: Covers (previously a rolling 7-day window ending today), Credit Note Details and Payments Received (previously 'first of current month' through today) all now default to the 'today' preset; Input Tax Statement and Output Tax Statement — filing/tax-category reports — keep a 'thisMonth' default, matching their existing accounting-cadence framing. This doesn't contradict the entry's 'Common filters across reports' claim that Date Range appears on 'All reports' with presets (Today/Yesterday/This Month/Last Month/Custom) — if anything this diff is a concrete step toward that claim landing — but it's currently a separate 'Quick range' chip control rather than presets built into the date picker, and confirmed live so far only on the six pages above, not the full ~28-report catalogue. It also reinforces this entry's own Edge Cases note ('Report shows 0.00 across the board... Cause: Date range defaults to today') for the pages whose default just changed to 'today', since that note previously assumed a default that wasn't true for Covers/Credit Note Details/Payments Received before this diff. Worth a Playwright re-check next pass to screenshot the new chip row, confirm its exact preset label set, and check whether the remaining report pages (Sales family, A/R, Inventory, etc.) get the same control before updating the Common Filters table with specifics. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-08-03: hubits-api-sales (144add7..df4179d, feat(reports): typed export pipeline with column selection, sales-details first + convert remaining 7 wave-1 report branches) adds a new GET /reports/export-columns?reportKey=... endpoint (ReportsController.cs) returning the available export columns, in default order, for a report — per the inline doc-comment, intended to back a column-picker in the admin app's export dialog. The same comment states the endpoint 404s for reports without column selection ('day-summary, revenue matrices, non-wave-1 keys') and the client treats that as 'export everything' — so Day Summary's export behavior documented above is explicitly unaffected, and this is confirmed to be a rolling 'wave-1' subset (sales-details first, plus 7 more report branches per the commit series), not a catalogue-wide change. This diff is API-only — no foodops-admin-app frontend hunks are included, so it's unverified whether the export dialog currently calls this endpoint or shows a column picker in the UI yet. Doesn't contradict the entry's existing generic export description ('Every report view has an Export option... usually top-right'); worth a Playwright re-check next pass, starting with Sales Details (/reports/sales-details), to see if a column-picker has appeared in report export dialogs. Separately and unrelated to Reports: this diff also touches SessionsController.cs, adding PosDefaultViewMode (org-level POS tile/compact default from Outlets settings) and SoftEnforcePaymentReference (org-level Transfer-payment reference enforcement) fields to the open-sessions response — this is counter/POS session-settings plumbing, not report content, and doesn't intersect any claim documented in this entry. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-08-01: hubits-api-sales (bb69312..0d94f42, tenancy pin / cache+signalr perf / customer-statements series) adds two new endpoints on SessionsControllerGET /{sessionId}/close-summary and GET /{sessionId}/close-summary/pdf — that are NOT the documented X-Report/Z-Report (sessionXZReport.ts/buildSessionXZReportHtml). This is a separate 'Session Summary' artifact: a new Services/Counter/Reports/SessionCloseSummaryMapper.cs calls DaySummaryReportService.GenerateReport(...) scoped to one session (per an inline comment, the date-window args are ignored when a sessionId is supplied — session membership alone defines inclusion) and maps the result 'verbatim' — one tender row per payment-method×currency with Opening/Expected/Counted/Difference, plus the full Day Summary SalesTotals set unchanged — so the new artifact is guaranteed to reconcile numerically with the existing Day Summary report this entry documents. A new SessionCloseSummaryHtmlBuilder.cs renders this DTO as an 80mm monospace thermal-receipt layout (titled 'SESSION SUMMARY', citing Figma frame 2850-20643) for both a ?download=true PDF and (per comment) a client-built thermal print. This diff is API-only — no foodops-admin-app or foodops.counter.app frontend hunks are included, so it's unverified whether/where a UI button calls these endpoints yet (likely a register-session close action on the counter, given the thermal-receipt framing, but not proven here). Does not contradict any existing claim about X-Report/Z-Report or Day Summary — it's an additive, reconciling artifact. Worth a Playwright re-check next pass to see if a 'Session Summary' print/download action has appeared on the register-session close flow, and if so add it as a new subsection alongside the existing X-Report & Z-Report section. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-07-30: foodops.counter.app (c4cf5dc..5468530, 'blind cash counting') changes the Cash Summary / Register Sessions block on the counter-app Day Summary (DaySummaryReportBody.tsx) so the Expected and Difference columns are hidden by default for an open register session — they only appear once every session for the day has closed (allSessionsClosed), unless the viewer holds a new canViewExpectedCash permission, in which case they're always shown. While hidden, a note reads 'Expected cash & variance are available after all sessions for the day are closed.' This is a permission-gated UX change to the reconciliation workflow this entry documents under 'End of day: run Day Summary to tally receipts vs cash for register reconciliation' and the Cash Summary block description — most users doing a mid-shift Day Summary check will now see only Opening/Counted (no Expected/Difference) until close-out. Companion commit subjects ('blind-close variance summary modal, neutral-black figures, per-currency cards') suggest a related close-out variance modal shipped too, but its code isn't in this diff's hunks, so its details are unverified. Worth a Playwright re-check to confirm which roles get canViewExpectedCash by default and to capture the hidden-state note + the variance modal on session close; no existing entry text is contradicted since the entry never specified that Expected/Difference are unconditionally visible. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-07-27: hubits-api-sales (c3662fc..bf7ac10, tenancy/scale series) adds server-side paging, free-text search, and multi-column sort to GetSalesDetails (backs Sales Details, /reports/sales-details, documented in Category 1 as 'Real-time transaction-level view with filters for outlet, register, payment method'). The endpoint keeps its original behavior when page/pageSize aren't supplied (Paging.IsRequested(...) false → same unpaged OrderBy(CreatedDate) query as before, now routed through a new shared ShapeSalesDetailsRowsAsync helper that preserves the existing org-local date conversion and outlet-name resolution logic verbatim). When paging is requested, the endpoint additionally accepts a search term (matches invoice reference or customer name) and a sortBy/sortDir pair (createdDate, invoiceDate, netTotal, taxTotal, serviceCharge, amount, outstanding, reference, customerName, registerSession, statusName), and returns a ReportPagedApiResponse with a summary block (totalAmount/netTotal/taxTotal/outstanding). This is API-only groundwork — the diff doesn't touch foodops-admin-app, so it's unverified whether the Sales Details report page currently sends page/pageSize/search/sortBy params or still calls the legacy unpaged path; the documented filter set (Date Range, Outlet, Register, Order Type) and result columns are unchanged either way. No entry text needs to change now — worth a Playwright re-check next pass to see whether Sales Details has gained a search box, sortable columns, or paginated results in the UI, similar to how the 2026-05-06 daypart/service-type endpoints and 2026-07-13 Sales Details outlet filter shipped API-only before any frontend confirmation. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-07-23: hubits-api-sales (aca3395..34ca3ff, Refine invoice query to filter by ApplicationOrganisationId for improved accuracy) adds an explicit Transaction.ApplicationOrganisationId == orgId predicate to both invoice queries inside DaySummaryReportService.cs — the one in BuildReportAsync that feeds the documented Day Summary Sales/Receipt/Category/Cash Summary blocks, and the one in GenerateInvoiceBreakdownAsync (invoice-level drill-down data). Previously these queries relied solely on SalesRegisterSessionId matching a pre-scoped sessionIds set to exclude other orgs' invoices; the new explicit org filter is a correctness/accuracy hardening (per the commit subject) suggesting the prior filter could, in some edge case, admit invoices from a different ApplicationOrganisationId into a Day Summary run — which would silently inflate the documented Sales Summary / Cash Summary totals for the affected org. This is not a contradiction of any documented Day Summary behavior (sections, filters, routes are all unchanged) and there's no UI signal to verify — it's backend query scoping only. Worth a note in case any org previously saw inexplicably-high Day Summary totals; no entry text needs to change. The companion commit in this same diff range (chore: bump haafai.domain to 1.0.1016, modifier recipe deduction) does not intersect this entry's cited surface (no hunks shown against Reports/Sales files) — unrelated to reports. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-07-23: foodops-admin-app (985570b..35146e3, Fix alcyon issues) wires the frontend for the IncludeVoided API flag flagged as API-only/unverified in the 2026-07-22 verification note. PaymentsReceivedReportPage.tsx (backs Payments Report, /reports/payments) adds a new 'Voided' filter field — a checkbox labeled 'Include voided payments', defaulting to unchecked (includeVoided: false) — so voided payments are now excluded from the results by default, and re-queries the API with includeVoided: true when ticked. Rows where row.isVoided is true get a red 'Void' badge inline next to the REFERENCE cell. This resolves the prior pass's open question: the checkbox does exist, and the entry's Category-6 description ('All received payments...') and the 2026-05-04 Playwright-verified filter list ('Date Range (start + end) only — no Outlet or Register filter') are both now incomplete — the Voided checkbox is a new filter (not Outlet/Register, so not literally contradicted, but the filter list is stale) and by default the table no longer shows literally all received payments. Recommend a Playwright re-check next pass to confirm the live default view and capture a screenshot of the new checkbox + Void badge, then update the Category 6 table and the 2026-05-04 verification note's filter description. Separately and unrelated: SalesOrdersPage.tsx's reference cell now renders an explicit href={/sales/orders/:id} link — this only reinforces the entry's already-documented claim that sales orders open at /sales/orders/:id; no edit needed there. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-07-22: hubits-api-sales (407e704..355de62, Fix alcyon isusues) changes GetPaymentsReceived (backs Payments Report, /reports/payments) to hide voided payments by default: a new IncludeVoided query flag now filters out rows where PaymentStatusId == PaymentVoid unless the caller opts in, and each row gains StatusName/IsVoided fields so a UI 'can badge a voided row when IncludeVoided is on' (per the inline comment). This is API-only groundwork — the diff doesn't touch the admin frontend, so it's unverified whether an 'Include Voided' checkbox actually exists yet in the Payments Report UI, or whether voided rows were previously visible in the documented 10-column table (REFERENCE/PAYMENT DATE/PAYMENT REFERENCE/REMARKS/CUSTOMER/PAYMENT METHOD/INVOICES/DEPOSIT TO/TOTAL/UNUSED, screenshot 167, 2026-05-04). Not a contradiction of the entry's 'All received payments' description yet, since we can't confirm the frontend currently omits voided rows from the default view — but worth a Playwright re-check next pass to see whether voided payments have silently disappeared from the default table (a user-visible behavior change) or whether this is inert pending frontend wiring, similar to the 2026-05-06 daypart/service-type endpoints that shipped API-only with no UI card. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-07-21: foodops-admin-app (53d3af8..799c61f, feat(admin): TanStack Query client + provider, write-invalidation bridge — wave 1) wraps the admin app in a global QueryClientProvider (apps/admin/src/App.tsx, 30s staleness contract) and refactors NotificationBell.tsx (cited in this entry's source_repos and referenced in the 'Audit log' section's notification-bell pointer) to read its unread count from a shared TanStack Query cache (NOTIFICATION_COUNT_KEY) instead of a per-component usePageVisibility-gated poll. User-visible behavior is unchanged — still a 2-minute poll, still pauses while the tab is hidden, still catches up immediately on NOTIFICATIONS_CHANGED_EVENT/window focus (now via invalidateQueries) — this entry's claim that 'the notification bell carries export/background-task items only' and the pull-not-push framing both still hold; the only effect is that multiple mounted bells (rail + mobile drawer) now collapse to one network request instead of one each. Also note: App.tsx gained ~7 lines near the top of the file (QueryClientProvider import/wrap + a dev-only ReactQueryDevtools block), so the line-number citations for the foodops report routes (App.tsx:1266-1549) and the INCLUDE_FINANCE gate (App.tsx:201-217) may have drifted by roughly that many lines — the diff does not show the route/gate content itself changing, so no route or gating behavior is affected, but the exact line numbers are worth re-confirming next time that section is touched. Worth watching: the commit series is labeled 'wave 1,' implying report pages themselves may adopt TanStack Query (with its write-invalidation-marks-all-stale behavior) in a future wave — not evidenced by this diff, no action needed yet. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-07-21: hubits-api-sales (99e6847..95e017e, fix(reports): update customer balance comparison to avoid timezone conversion) fixes a double-timezone-conversion bug in GetCustomerBalances (backs /reports/customer-balances, documented in the A/R category as 'Current outstanding per customer'). The as-of journal-date comparison previously took the already org-local toDateLocal and ran it back through ReportTimeZone.ToUtcFromOrgLocal(toDateLocal, orgTz) before comparing against JournalEntry.JournalDate; that extra conversion could shift the effective as-of boundary near midnight depending on the org's UTC offset, potentially including or excluding journal entries dated right on the boundary day. The fix compares JournalEntry.JournalDate <= toDateLocal directly, dropping the redundant conversion. This does not contradict any documented claim — the entry only describes Customer Balances at the 'current outstanding per customer' level, not its as-of date-boundary mechanics — but it's a correctness fix worth flagging since it changes which balances near a day boundary get counted, i.e. the actual numbers users see. No route, filter, or column change. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-07-17: hubits-api-sales (acfa332..416ee89) follow-up-corrects the 2026-07-13-flagged 'F8' change on GetPaymentsReceived (backs Payments Report, /reports/payments). The prior commit had collapsed the REFERENCE field to p.Reference ?? p.Transaction.Reference; this diff reverts that per an inline comment noting the collapse 'cost the report its sequence number' — Reference now goes back to the system PAY number (p.Transaction.Reference) and a new BankReference field carries the user-entered bank/cheque ref separately, alongside a newly exposed Remarks and UnappliedAmount. This does NOT contradict the entry: the Playwright-verified column list (screenshot 167, 2026-05-04) already documents distinct REFERENCE, PAYMENT REFERENCE, and REMARKS columns, so this backend revert restores consistency with what was already documented rather than breaking it — it just means the 2026-07-13 verification note's description of the collapsed single-Reference behavior is now superseded/stale. Also added: results now order by PaymentDate then CreatedDate (fixes same-day payment ordering, since PaymentDate is often midnight-only), and ChartOfAccount/InvoicePayments are now included (likely feeding the deposit-account / linked-invoice columns already in the documented table). Separately, GetSalesDetails (backs /reports/sales-details) gained an outlet filter (ReportPredicates.OutletMatches) and now resolves an OutletName per row (via Invoice.OutletId, falling back to the register session's outlet) — the entry already generically lists 'outlet' as a Sales Details filter, so this isn't a new claim, but whether Outlet now appears as a visible results column is unverified and worth a Playwright check next pass. (auto-applied by the truth pipeline; adversarially gated) (auto-applied by the truth pipeline; adversarially gated)

  • 2026-07-13: hubits-api-sales (de33dbc..acfa332) touches two endpoints behind documented reports without contradicting any documented claim. ReportsController.GetPaymentsReceived (backs Payments Report, /reports/payments) now returns Reference = p.Reference ?? p.Transaction.Reference instead of always p.Transaction.Reference — per the inline comment ('F8'), the REFERENCE field now shows the user-entered bank/transfer reference when captured, falling back to the system PAY-number only when it wasn't. This changes the actual values users will see in the REFERENCE column of the Playwright-verified Payments Report table (screenshot 167), though the column set itself is unchanged. Separately, ReportsController.GetSalesDetails (likely backing /reports/sales-details) gained a filter restricting results to TransactionTypeId == Invoice, narrowing the transaction-level data returned — worth confirming with Playwright whether this changes visible row counts on Sales Details. Unrelated: CounterReportsController's Day Summary PDF/Excel generation switched date-format strings from dd/MM/yyyy to dd'/'MM'/'yyyy (escaping literal slashes so .NET doesn't substitute a culture-specific date separator) — a display-safety fix, not a content change, and not worth a catalogue edit. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-07-05: foodops.counter.app (9200293..b813f61, feat(reports): adopt admin's unified Day Summary body + date toggle) refactors the counter app's Day Summary into a single shared DaySummaryReportBody.tsx, replacing four previously separate components (CategorySummary.tsx, PaymentMethodSummary.tsx, RegisterSessions.tsx, SummarySection.tsx — all fully deleted). This is a structural consolidation, not a content change: the documented Receipt/Sales/Category/Cash Summary blocks are preserved, and the new body is written to be reusable in a session-overview context too (its showKpiStrip prop is suppressed there because 'its hero card already shows those figures' per an inline comment) — confirming Register Sessions and Credit Sales rows already lived on this page pre-refactor, just via separate components. Two genuinely new pieces of UI ship in the same commit and are not yet in the entry: (1) DaySummaryKpiStrip.tsx — a brand-new KPI tile strip (2/3/6-col responsive grid, currency + count-style tiles from buildKpiTiles(report)) rendered above the Day Summary tables — distinct from the legacy hub-level KPI strip already flagged elsewhere in this entry as unverified/retired; (2) DateTypeToggle.tsx — a brand-new 'Business Date' / 'Transaction Date' toggle component. Given the commit groups this toggle with the Day Summary body work, it may be the next iteration of (or a replacement for) the already-documented 'Report By: Register Session / Order Time' filter — but the diff hunks available don't show where DateTypeToggle is actually wired into DaySummaryReportPage, so the relationship to the existing 'Report By' filter is UNCLEAR and needs a follow-up pass (ideally with Playwright) before editing that section. Recommend re-verifying the Day Summary filters and adding a KPI-strip note next pass rather than editing now, since the wiring isn't proven by this diff alone. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-07-05: foodops-admin-app (8ec1ab1..6642e57, feat(reports): report exports through list-view dialog/tray UX, incl. Day Summary) replaces Day Summary's bespoke PDF/Excel dropdown-export + polling History modal with the shared list-view export flow: ReportPageShell's onExport prop now takes an ExportFormat and resolves { correlationId }, with progress/download surfacing in a shared bottom-right ExportProgressTray (notification bell as fallback) instead of an in-page History modal — DaySummaryReportPage.tsx drops handleShowHistory/handleExportPdf/handleExportExcel and the 5s-polling effect entirely. The commit subject implies this is a cross-report UX unification rather than Day-Summary-only, though this diff only directly proves it for Day Summary + the shared shell; other report pages' History affordance is unverified pending a fuller pass. Separately and unrelated to History: CommittedStockReportPage.tsx and SalesDetailsReportPage.tsx now format timestamps via formatDateTimeOrgTz(value, orgTz) instead of raw parseISO/format, extending the org-timezone treatment already documented for Day Summary/Register Sessions/X-Z reports to two more report pages — cosmetic only, no change to report figures. CoversReportPage.tsx also now passes a dateType param on export, and ScheduledReportRunsPage.test.tsx was refactored to a shared renderWithProviders test helper — both internal/test-only. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-07-04: hubits-api-sales (01cd718..3005390, feat(reports): Day Summary exports through tray pipeline with legacy workbook) adds the concrete plumbing behind Day Summary's PDF/CSV export + History buttons documented above. CounterReportsController.GenerateDaySummaryPdfReport now writes a FileDownloadLink Notification (keyed on the report id, message "Day Summary PDF export ready (...)") after the PDF is uploaded to blob storage, so the admin app's export tray — which polls notifications by EntityId — can surface the download; per the code comment this is 'same contract as ReportExportJob's XLSX exports,' i.e. Day Summary PDF exports now join the same async notification-based delivery already used for XLSX. Separately, the ~300-line inline ClosedXML workbook-building code previously in GenerateDaySummaryExcelReport was extracted verbatim into a new shared Services/Counter/Reports/DaySummaryWorkbook.cs (DaySummaryWorkbook.Build(...)), so the counter app's history-modal pipeline and the admin app's export-tray pipeline render the identical workbook from one renderer fed by DaySummaryReportService — no change to workbook content/layout (extracted verbatim, not rewritten). No documented claim is contradicted: the Export/History buttons, their visibility, and the 'Large exports may take a moment' note all remain accurate — this diff explains why (async Hangfire job + notification poll) rather than changing the user-visible contract. Worth a line in the API/integration notes about the shared DaySummaryWorkbook renderer and the notification-based export-tray delivery mechanism next time that section is touched. (auto-applied by the truth pipeline; adversarially gated)

  • 2026-07-04: foodops-admin-app (88dfde2..f25a41c) extends the org-timezone treatment — previously documented (2026-06-04 entry) as covering Day Summary / Register Sessions display only — to the printed X-Report/Z-Report artifact itself. buildSessionXZReportHtml in apps/admin/src/utils/sessionXZReport.ts now takes a required timeZone param and renders the Period (Opened/Closed) and Footer (Printed at) timestamps via formatInTimeZone against the org's IANA timezone instead of raw local-machine format(parseISO(iso)); RegisterSessionDetailsPage.tsx now passes timeZone: orgTz at the print call site. Structure/sections and the X=READING / Z=FINAL mode labels are unchanged. The rest of this diff (App.tsx additions for ai/autonomy, ai/activity, customisation/import-result Settings routes; finance-report test-harness refactors to renderWithProviders; the invoiceCountSessionSalesSummaryCard internal cleanup; formatDurationSince merged into formatDuration) is either outside the Reports feature area or test/internal-only and doesn't affect any documented report claim. (auto-applied by the truth pipeline; adversarially gated)

Warning: 2026-06-29 — new-stack re-verify for July-10 launch · codebase Verified_by: codebase · Last verified: 2026-06-29 · foodops-admin-app@4173811 (main) + foodops.counter.app@32669a9 (main) + hubits-api-sales@663dae0 (main) Re-pointed the whole catalogue off the retired legacy monolith onto the new admin app. The report surface is now ~28 per-route React pages under apps/admin/src/pages/reports/* (registered App.tsx:1266-1549), with a persistent ReportsSidebar rail; route convention changed /reports/view/{slug}/reports/<name>. Updated every category table's identifier column to the new route and verified each route exists at HEAD. NOT ported to the new admin set (no matching route): Brand-Contribution (2), Order-Quantity-by-category (2), Invoice/Sales-Order/Quotes Details, Purchase-Receive/Expense-Record Details. NEW reports added: Sales By Daypart, Sales By Service Type, Voids & Comps, Covers (/reports/sales-by-daypart|sales-by-service-type|voids-and-comps|covers). Finance reports (P&L/BS/TB/VAT-return/etc.) are INCLUDE_FINANCE-gated → tree-shaken out of the foodops build (only on finance-v2). Documented the X-Report / Z-Report printable register close-out artifact (utils/sessionXZReport.ts buildSessionXZReportHtml, X=READING / Z=FINAL, auto window.print()), reached from /sales/register-sessions/:id. Tax is still computed per-line at order close (counter orderCalculationService.ts). Frontmatter dropped haafai.app.foodops + haafai.app.common legacy globs. Open: KPI-strip-on-/reports-landing and live screenshots still UI-TODO (no Playwright this pass).

Note: Code + Playwright verified 2026-05-04 · codebase + live E2E Verified_by: playwright · Last verified: 2026-05-04 Playwright pass (2026-05-04) confirmed: Payments Report at /reports/paymentsreport is a React counter-style page with date-range-only filter, 10-column table (REFERENCE/PAYMENT DATE/PAYMENT REFERENCE/REMARKS/CUSTOMER/PAYMENT METHOD/INVOICES/DEPOSIT TO/TOTAL/UNUSED), and PDF+CSV export + History buttons (screenshot 167). Day Summary Report shows Date Range + Outlet + Register + "Report By" (Register Session) filters and PDF+CSV export + History buttons at top-right — export buttons were re-enabled (screenshot 166). "Report By" selector reverted — present in both surfaces as of 2026-05-04. Code-verified claims updated accordingly; status downgraded from stable to needs-review is partially resolved — the "Report By" discrepancy no longer exists. Status remains needs-review for the broader entry that hasn't been playwright-verified beyond these two pages.

Note: 2026-06-04 — Content re-verify (mostly no-change) · codebase Verified_by: codebase · Last verified: 2026-06-04 Re-verified against foodops.counter.app@24d27ba, hubits-api-sales@720407c (main), haafai.app.common@master. The brief's driver 0198d25 is a counter.app sub-resource-services commit, not report content — no catalogue impact. The report catalogue (34 reports / 9 categories) is unchanged — no user-facing behaviour change to the documented reports, filters, slugs, or KPI tiles. Backend/polish only: new daypart/service-type/voids-comps API endpoints exist but have no UI cards (bcaadac6); org-timezone now drives report date/time display (b7b92257/cdd45cee, cosmetic); currency + cash-difference fixes on PaymentMethodSummary/RegisterSessions (c1dd098c/24d27baf). Added a "since 2026-05-04" subsection capturing these. No Playwright this pass.

Additional verification events are appended here by the weekly KB sync and by manual re-verification passes. See _meta/failure-log.md for the global failure index.

Go beyond POS.
Choose the total solution.

Focus on creating the best experience for your guests while we handle the rest.