DONE evidence — task 8f896692 (Finance dashboards: money in/out and net per month/year/YTD, portal + dataops finance summary CLI)
Verified against origin/main 41f16668 (2026-11-05) in a dedicated worktree
(fix/finance-dashboards). The whole task is already implemented across
backend, CLI and portal and covered by existing tests; no code change was
needed. This file is the evidence record; the PR is docs-only.
The feature landed in October 2026 in three commits that reference this intake:
83d68a93feat(finance): money in/out, net and VAT summary per month/year/YTD (backend)daf6317bfeat(cli): dataops finance summary --month/--year/--ytd59f7aac5feat(portal): Finance overview page at /financeaf13e806feat(finance): tax ledger (dataops finance taxes) and owner pay line (intake 8f896692 h,k)
backend/src/finance/summary.ts:19—PeriodQueryis{ month } | { year } | { ytd: true };:125-139—resolvePeriodbuilds the period for each kind: month =from 01tomonthEnd, months[month]; year =YYYY-01-01toYYYY-12-31with all 12 months; YTD =YYYY-01-01to today with months up to the current one, label<year> YTD (to <today>).:66-88—TotalscarriesmoneyIn,moneyOut,net(plus net profit excluding VAT, VAT lines, taxes, TK, owner pay);:303-356—total()sums per entry in integer cents and derivesnet = moneyIn - moneyOut(:346). Income rows count as money in, expense rows as money out (:324); supplier credit notes are contra-expense, neither in nor out (:313-323).:433-520—summarizefilters rows into the period by booking date and returns per-month totals (:465-469— every month of the period with its ownTotals, future months flagged),byCurrency(:471-514) and gaps.- Rules are documented in
SOURCE_RULES(:106-120) anddocs/finance-summary.md; both are returned/rendered asrules. backend/src/finance/service.ts:84—financeSummaryloads the ledger, statements, coverage and ECB rates and callssummarize;:130—parsePeriodQueryvalidates exactly one ofmonth=,year=,ytd=1.backend/src/routes/finance.ts:10,17-22—GET /api/finance/summary?month=|year=|ytd=1serves it (400 on a bad query, 405 on non-GET).
cli/src/commands/finance.mjs:6— command description;:57-69—periodArgsrequires exactly one of--month YYYY-MM,--year YYYY,--ytdwith format validation;:145-149—summaryCommandcallsGET /api/finance/summaryand prints it;--jsonprints the payload the portal renders, so the two cannot disagree.:86-143—printSummary: net profit ex VAT and projected VAT on top, then a per-month grid with Month / Money in / Money out / Net / Profit ex VAT / Taxes / TK / Owner pay / VAT position / VAT paid and a Total row for multi-month periods (:93), the P&L lines (:108-120), currencies in original + EUR with inline conversions (:121-130), and data gaps (:131-140).
frontend/src/surfaces/finance/overview.js:1-5— same payload contract as the CLI (GET /api/finance/summary);:11-16— period from URL params (month,year,ytd, implicit YTD default);:317— fetches the summary.:52-58— headline stats Money in, Money out, Net (cash) plus taxes, TK, owner pay, net after owner pay;:33-49— hero tiles for net profit ex VAT and the projected VAT position.:89-120— cash chart: money in above the baseline, money out below, a dot for the month's net (year/YTD views,:266-268);:122-149— net profit ex VAT per month chart.:151-167— per-month table Month / Money in / Money out / Net / … with a Total row (:152);:217-232— period controls (Year to date button, Year select, Month input);:269-272— "By month" / "This month" section;:169-199currencies,:202-215data gaps and source rules.
docs/finance-summary.md documents the three commands (:11-23), what counts,
no-double-counting, dedup and EUR conversion rules, and states the portal and
CLI show the same numbers because both render GET /api/finance/summary.
dataops finance summary --ytd— "Finance summary 2026 YTD (to 2026-11-05) (EUR)": per-month rows 2026-01..2026-11 each with Money in / Money out / Net / Profit ex VAT / Taxes / TK / Owner pay / VAT position / VAT paid, a Total row (287,373.78 in / 123,816.90 out / 163,556.88 net), the P&L lines, EUR+USD currency rows with inline conversions, and data gaps.dataops finance summary --month 2026-10— single row 2026-10 (91,082.01 in / 9,366.71 out / 81,715.30 net) plus the P&L and currency blocks.dataops finance summary --year 2025— 12 month rows + Total (197,800.00 in / 134,787.69 out / 63,012.31 net).- No writes were performed; all three invocations are GETs.
backend/tests/finance-summary.test.ts— 25 pass, 0 fail. Month math::16-30(money in 3000, out 120.50, net 2879.50, only October rows count); year/YTD::245-255— a year splits into 12 months, November/December are marked future, YTD has 10 months and stops at the current month;:287-293— period-query validation (bad month, month+year together, empty).cli/test/finance.test.mjs— 10 pass, 0 fail (table rendering, period flags, owner pay line).frontend/test/finance-overview.test.mjs— 8 pass, 0 fail (headline, charts, month table, currencies, gaps, upcoming debits).
None for this task. Two observations outside its scope, visible in the prod output but pre-existing and separately handled: unclassified bank statement lines are reported as a data gap (63 in YTD) by design, and the 2026-10 Taxes figure is negative (-43,633.73) because a prepayment refund larger than the period's tax payments was booked — a data question, not a summary-math bug (the summary follows its documented SOURCE_RULES).