diff --git a/.claude/agent-memory/docs-writer/MEMORY.md b/.claude/agent-memory/docs-writer/MEMORY.md index ab9939f1c..ecb06b9f7 100644 --- a/.claude/agent-memory/docs-writer/MEMORY.md +++ b/.claude/agent-memory/docs-writer/MEMORY.md @@ -30,7 +30,7 @@ - `getting-started/` -- index, docker-setup, first-login, configuration - `guides/work-items/` -- index, creating-work-items, tags, notes-and-subtasks, dependencies, keyboard-shortcuts - `guides/users/` -- index, oidc-setup, admin-panel -- `guides/budget/` -- index, categories, financing-sources, work-item-budgets, vendors-and-invoices, subsidies, budget-overview +- `guides/budget/` -- index, categories, financing-sources, work-item-budgets, vendors-and-invoices, subsidies, budget-overview, bank-reports (added PR #2041, see [Bank Report Wizard docs](bank-report-wizard-docs.md)) - `guides/timeline/` -- index, gantt-chart, milestones, calendar-view - `guides/documents/` -- index, setup, browsing-documents, linking-documents - `guides/household-items/` -- index, creating-editing-items, budget-and-invoices, work-item-linking, delivery-and-dependencies @@ -103,6 +103,12 @@ Three user-facing changes documented (no new pages, no sidebar changes): Pre-existing lint baseline in worktree: ~8 eslint ERRORS in production/test .ts across client/server/e2e (photoService OrientationSummary unused, usePaperless import() type, etc.) -- NOT introduced by docs changes (docs edits are markdown-only; eslint doesn't lint .md). Likely a local `npm install --ignore-scripts` artifact since beta CI requires lint green. Do NOT touch those files as docs-writer. +## Release: PR #2041 (Bank Report Wizard docs gap, subsidies drift fix, .env.example drift fix) + +Full env-var scan (`grep` for `getValue('...')` in `server/src/`) confirmed 3 vars missing/wrong in `.env.example`: `AUTH_RATE_LIMIT_MAX` and `AUTH_RATE_LIMIT_WINDOW` (new, added commented-out under Server section near `TRUST_PROXY`/`EXTERNAL_URL`) and `VAT_RATE` (pre-existing gap, added commented-out under Localization near `CURRENCY`). `CLAUDE.md`'s env-var table was already current for all three (added in an earlier commit within the same 40-commit range) -- always diff `.env.example` against `server/src/plugins/config.ts` directly rather than assuming the two docs surfaces drift together. + +New page `guides/budget/bank-reports.md` closes a total docs gap for the Bank Report Wizard feature -- see [Bank Report Wizard docs](bank-report-wizard-docs.md) for what it covers and why the gap existed. Same pass also rewrote stale portions of `subsidies.md` (multi-category + "No Category" + real 5-status enum) -- same memory file has the detail. + ## Build Note (still true) `npm run docs:build` fails in worktrees with webpack `ProgressPlugin` ValidationError (node_modules corruption, NOT content). Build reaches the webpack bundling stage, so MDX/content/link loading succeeded. Validate internal links/anchors statically with grep instead; CI does the real build. diff --git a/.claude/agent-memory/docs-writer/bank-report-wizard-docs.md b/.claude/agent-memory/docs-writer/bank-report-wizard-docs.md new file mode 100644 index 000000000..dbc4ff05b --- /dev/null +++ b/.claude/agent-memory/docs-writer/bank-report-wizard-docs.md @@ -0,0 +1,40 @@ +--- +name: bank-report-wizard-docs +description: Bank Report Wizard had zero docs-site coverage until PR #2041 (2026-08-06); new guide at guides/budget/bank-reports.md, plus the subsidies.md drift that was fixed in the same pass +metadata: + type: project +--- + +# Bank Report Wizard docs gap (closed 2026-08-06, PR #2041) + +**Why this mattered:** EPIC-07 (Reporting and Export) had been checked off in the roadmap for a long +time, and the feature (`client/src/pages/ReportWizardPage`, route `/budget/reports`) had grown into a +large, actively-developed area (see `product-owner`'s `bank-report-wizard.md` memory for the full +mini-epic history), but `docs/src/` and `docs/sidebars.js` had **no page for it at all** -- not even a +stub. A release task that assumed "extend the existing reports docs" surfaced the gap. + +**How to apply:** The gap is now closed -- `docs/src/guides/budget/bank-reports.md` (sidebar position 9, +registered in `docs/sidebars.js` under the Budget category, cross-linked from `guides/budget/index.md`). +It documents the wizard as a single comprehensive page (index-style, no sub-pages, matching the +`guides/backup/index.md` pattern) covering: the 3 report types (Budget Overview / Claim / Proof of +Funds) and their invoice-status eligibility, the 5 wizard steps (Report Type, Budget Source, Select +Invoices, Settings, Preview & Export), column visibility toggles, AI-assisted generation +("Enhance with AI", gated on `llmEnabled`), marking invoices claimed, and long-content/multi-page PDF +handling. **Before extending this page**, re-derive current UI/copy from +`client/src/i18n/en/budget.json` (`sourceReports` key) and `ReportWizardPage.tsx` rather than trusting +this page alone to stay current -- the feature has a long history of fast iteration (see +`[[release-notes-drift]]` if that file exists, or the product-owner memory directly). + +## Related fix in the same pass: subsidies.md was stale, not just missing "No Category" + +`docs/src/guides/budget/subsidies.md` described a **single** "Budget Category" field and a **4-status** +lifecycle (Pending/Approved/Rejected/Disbursed) that no longer matched the shipped `SubsidyProgram` +type (`shared/src/types/subsidyProgram.ts`): categories are actually **multi-select** +(`applicableCategories: BudgetCategory[]`, empty = universal) with an independent `includesNoCategoryItems` +("No Category") checkbox, and the real status enum is +`eligible | applied | approved | received | rejected` (only `approved`/`received` count toward budget +math). This predated the "No Category" release task -- rewrote the whole Creating/Statuses/How-it-affects +sections rather than just appending the new checkbox, since the old text would have stayed actively +wrong. **Lesson: when a task says "add feature X to this doc", verify the doc's existing claims against +the current type/schema before touching it — drift compounds silently on release cycles that only ever +append.** diff --git a/.env.example b/.env.example index af31cab47..6cc589944 100644 --- a/.env.example +++ b/.env.example @@ -12,6 +12,11 @@ NODE_ENV=production # Used for CalDAV/CardDAV discovery, .mobileconfig generation, and OIDC callback fallback # EXTERNAL_URL=https://myhouse.example.com +# Login endpoint (POST /api/auth/login) rate limiting +# A value that fails to parse causes the server to fail at startup. +# AUTH_RATE_LIMIT_MAX=20 # Max login requests per client per window +# AUTH_RATE_LIMIT_WINDOW=15 minutes # Window length (ms library format, e.g. '1h', '30s') + # ─── Database ────────────────────────────────────────── DATABASE_URL=/app/data/cornerstone.db @@ -21,6 +26,7 @@ SECURE_COOKIES=true # ─── Localization ────────────────────────────────────── # CURRENCY=EUR # ISO 4217 currency code (EUR, USD, CHF, etc.) +# VAT_RATE=0.19 # VAT/sales-tax rate as a fraction (e.g. 0.19 = 19%) # ─── Diary ───────────────────────────────────────────── # DIARY_AUTO_EVENTS=true # Set to false to disable automatic diary event logging diff --git a/README.md b/README.md index 433ef3833..c6d909ce5 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,8 @@ A self-hosted home building project management tool for homeowners. Track work i - **Work Items** -- Manage construction tasks with statuses, dates, area assignments, notes, subtasks, dependencies, and keyboard shortcuts -- every work item shows its full area ancestor path (e.g. `House / Ground Floor / Kitchen`) as a breadcrumb across lists, detail pages, pickers, and every place it is referenced - **Areas & Trades** -- Organize your project with hierarchical areas (rooms, floors, zones) and trade specialties (Electrical, Plumbing, etc.) for vendors; a dedicated "No Area" filter surfaces items that have not been classified yet -- **Budget Management** -- Budget categories, financing sources with inline expansion and mass-move of attached lines, multi-budget-line invoice linking with itemized amounts, inline editing of invoice-linked budget lines (including moving a line to a different work item or household item), staged-payment deposits for invoices paid in instalments (deposit-aware paid/claimed rollups), subsidies with caps, quotation tracking, and an area-grouped overview dashboard with source attribution badges on every line, a per-source filter (URL-persisted, server-side) that updates totals and subsidy math, a Cost Basis filter (All / Paid / Outstanding) that reframes the breakdown around what you have actually spent versus what is left to pay, clickable summary tiles, and print-friendly export +- **Budget Management** -- Budget categories, financing sources with inline expansion and mass-move of attached lines, multi-budget-line invoice linking with itemized amounts, inline editing of invoice-linked budget lines (including moving a line to a different work item or household item), staged-payment deposits for invoices paid in instalments (deposit-aware paid/claimed rollups), subsidies with caps that can target specific categories, uncategorized items, or your whole project, quotation tracking, and an area-grouped overview dashboard with source attribution badges on every line, a per-source filter (URL-persisted, server-side) that updates totals and subsidy math, a Cost Basis filter (All / Paid / Outstanding) that reframes the breakdown around what you have actually spent versus what is left to pay, clickable summary tiles, and print-friendly export +- **Bank Reports** -- Generate a formatted PDF for a financing source -- a claim submission, a proof-of-funds statement, or a budget overview -- with an editable cover letter, configurable table columns, and optional AI-drafted content - **Auto-itemize Invoices** -- Read line items off invoice PDFs (via Paperless OCR) using any OpenAI-compatible LLM provider (Gemini, Anthropic, OpenAI, Ollama) on a dedicated review page with side-by-side PDF preview, per-row category/funding/assignment pickers, and the ability to merge several related rows into one consolidated line -- or create a whole invoice straight from a Paperless document, with metadata, vendor, and VAT-grossed-up line items extracted in one step -- opt-in, no vendor lock-in, costs pennies per invoice - **Timeline & Gantt Chart** -- Interactive Gantt chart with dependency arrows, critical path, zoom controls, milestones, and CPM-based auto-scheduling - **Calendar View** -- Monthly and weekly calendar grids with work items and milestones diff --git a/RELEASE_SUMMARY.md b/RELEASE_SUMMARY.md index 7acce2bea..a926829b6 100644 --- a/RELEASE_SUMMARY.md +++ b/RELEASE_SUMMARY.md @@ -2,18 +2,26 @@ ## What's New -This release sharpens two of the areas you spend the most time in: the budget overview and invoice auto-itemization. You can now see your budget through a "how much have we spent versus how much is left" lens, and consolidate messy multi-row invoices into clean budget lines before saving. No new configuration or migration steps are required. +This release is a reliability and polish pass on the bank report wizard -- the PDFs you hand to a lender now render correctly in every case that was previously fragile, from long German descriptions to multi-page tables. It also adds a configurable login rate limit, a "No Category" option for subsidies, and three security fixes. ### Highlights -- **Cost Basis filter on the Budget Overview** -- The Cost Breakdown table gains a **Cost Basis** dropdown with three views: **All** (the full blended projection, as before), **Paid** (only money that has actually left your account), and **Outstanding** (everything still to pay -- unpaid invoice balances plus not-yet-invoiced projections). It is deposit-aware, so invoices paid in instalments split correctly between Paid and Outstanding, and your choice is saved in the URL for bookmarking and sharing. -- **Merge line items when auto-itemizing** -- On both the auto-itemize review page and the "create invoice from a Paperless document" flow, you can now select two or more extracted rows and merge them into one consolidated line. Amounts are summed on your own server for exact arithmetic; only the descriptions are sent to the language model to propose a combined name and category. A failed merge can be retried or fully undone -- nothing is destructive until you save. -- **Diary opens on your own entries** -- The construction diary now defaults to the **Manual** filter, so you land on the entries you actually wrote instead of a feed dominated by auto-generated system events. Switch to **All** or **Automatic** anytime; the choice is reflected in the URL. - -### Behind the Scenes - -- Auto-itemization now shares a single, harmonized code path between the existing-invoice and new-invoice flows -- no change in behavior, but a more consistent and reliable review experience. -- Dependency and toolchain updates for security and maintenance, with no user-facing changes. +- **Report table columns now flow through to the exported PDF.** The Show/Hide columns you toggle in the wizard preview are respected in the downloaded document, not just the on-screen preview. +- **Long rows are handled cleanly.** Descriptions that need to continue onto the next page are now clearly marked as continuations instead of reading like truncated or broken rows. +- **Split invoices are footnoted correctly.** The report now distinguishes an invoice split across budget lines from one split via a deposit tagged to a different source, so the footnote on each row explains the right reason. +- **Editable fields in the report editor have sensible length limits**, so cover letters and usage descriptions stay within what the PDF layout can safely render. +- **Fixed German header word-breaks, a missing timestamp on later report pages, and the page footer's locale**, so multi-page German-language reports read correctly throughout. +- **AI-assisted report generation is now guarded** against switching report type or source while a generation is still in progress, preventing content written for the wrong report from landing in your draft. +- **Configurable login rate limiting.** New `AUTH_RATE_LIMIT_MAX` and `AUTH_RATE_LIMIT_WINDOW` settings let you tune the login endpoint's rate limit for your household's network setup -- see the [Configuration guide](https://cornerstone.steiler.dev/getting-started/configuration#authentication-rate-limiting). +- **Subsidies can now include uncategorized items.** A subsidy program's applicable-categories picker gained a "No Category" option, so a subsidy can cover budget lines that have no category assigned. +- **Budget source drill-down is deposit-aware.** Instalment-paid invoices now show the correct paid and outstanding split when viewed from a financing source. +- **Failed column-preference saves now surface an error toast** in list views instead of failing silently. + +### Security + +- Fixed an IPv6 address-normalization bypass in the login rate limiter (CVE-2026-15144). +- Remediated a credential-leak/SSRF vulnerability in the `undici` HTTP client (GHSA-g4rg-993r-mgx8). +- Remediated a vulnerability in the `brace-expansion` dependency (GHSA-rhx6-c78j-4q9w). ## Upgrade diff --git a/docs/sidebars.js b/docs/sidebars.js index f4fac7d9b..a99fce5e6 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -45,6 +45,7 @@ const sidebars = { 'guides/budget/auto-itemize', 'guides/budget/subsidies', 'guides/budget/budget-overview', + 'guides/budget/bank-reports', ], }, { diff --git a/docs/src/guides/budget/bank-reports.md b/docs/src/guides/budget/bank-reports.md new file mode 100644 index 000000000..198adf9fc --- /dev/null +++ b/docs/src/guides/budget/bank-reports.md @@ -0,0 +1,75 @@ +--- +sidebar_position: 9 +title: Bank Reports +--- + +# Bank Reports + +Most construction loans and subsidy programs require you to prove how money was spent -- a claim submission to your bank, a proof-of-funds statement, or a general budget overview for your own records. The **Report Wizard** (**Budget > Reports**) turns your invoices and budget lines into a formatted PDF you can hand to a lender, submit for a subsidy claim, or file away, without you having to assemble it by hand. + +## When to Use It + +The wizard supports three report types, chosen in step 1: + +| Report Type | Purpose | Invoices Included | +|---|---|---| +| **Budget Overview** | A summary of all invoices and their allocations for a source, for your own records | All invoices for the selected source | +| **Claim** | Document invoices marked as paid or claimed, for submitting a reimbursement claim to a bank or subsidy program | Invoices in `pending` or `paid` status | +| **Proof of Funds** | Show only claimed invoices, as evidence that previously claimed funds were actually spent | Invoices in `claimed` status | + +Each report is generated for a single **budget source** at a time (a specific loan, credit line, or savings account) -- see [Financing Sources](financing-sources). + +## The Five Steps + +The wizard walks through five steps, shown in a stepper at the top of the page. You can jump back to any step you have already reached. + +### 1. Report Type + +Choose Budget Overview, Claim, or Proof of Funds. This determines which invoices are eligible in step 3 and which document types qualify to be attached in step 4. + +### 2. Budget Source + +Select the financing source the report is for. Each source shows the relevant amount for the chosen report type (Total Amount, Pending Amount, or Claimed Amount). + +### 3. Select Invoices + +Every eligible invoice for the source and report type is listed, pre-selected. You can: + +- Deselect individual invoices, or use **Select all** / **Reset selection** +- Expand an invoice to see its individual budget lines and deposits/refunds, and exclude specific line items from the report without excluding the whole invoice +- See a **partial** badge on invoices whose funding is split across more than one budget source -- both invoices split across budget lines and invoices with a source-tagged deposit are flagged, and the PDF's footnotes distinguish which kind of split applies to each row +- See invoices with no allocation for this source grouped separately under **Unallocated Invoices** + +Excluding a line item narrows the amount shown in the report but does not change the invoice's claim status -- see [Marking Invoices as Claimed](#marking-invoices-as-claimed) below. + +### 4. Settings + +- **Report language** -- choose the language the exported PDF is written in, independent of your own UI language. This only affects the report content (table captions, cover letter, footnotes); the wizard's own controls stay in your UI language. +- **Attach invoice PDFs** -- append each selected invoice's source document as a PDF appendix. Which document types qualify depends on the report type (quotations qualify for a Budget Overview, but not for a Claim or Proof of Funds report -- so a document that would undercut the report's evidentiary value is never silently attached). +- **Include cover letter** -- adds a formal letter ahead of the report table, addressed using the source's contact details. Disabled if the source has no contact address or reference number configured. + +### 5. Preview & Export + +The final step shows an editable HTML preview of the report -- this is what generates the PDF, so what you see here is what you get. + +- **Cover letter fields** (sender, recipient, reference, subject, body, closing, signature) are all editable text, each with a maximum length shown as you type. Editing a field marks it as edited; use the reset button next to a field to discard your edit and fall back to the generated text. +- **Show/hide columns** -- toggle which table columns appear in the exported PDF (Vendor, Invoice No., Date, Status, Invoice Amount, Allocated Amount, Usage). The Allocated Amount column is always required and cannot be hidden. If you hide the Usage column while **Attach invoice PDFs** is enabled, a warning explains that readers lose the text linking each row to its attached document. +- **Enhance with AI** -- if your instance has an LLM provider configured (see [Auto-itemize Invoices](auto-itemize)), a button generates usage descriptions and a full cover letter draft in one batched call, in the report language you selected. Regenerating -- or changing report type, source, or invoice selection while a generation is in progress -- asks for confirmation before discarding your edits or cancelling the in-flight request. +- **Preview PDF** opens the exact rendered PDF in a modal before you commit to downloading it. + +From here you can: + +- **Download PDF** -- saves the report to your device +- **Upload to Paperless** -- if [Paperless-ngx integration](../documents/setup) is configured, uploads the generated PDF directly into your document library + +## Marking Invoices as Claimed + +For **Claim** reports, after exporting you are offered **Mark N invoices as claimed** or **Finish without marking**. Marking as claimed flips the included invoices (and any of their deposits swept into this report) to `claimed` status, so they stop appearing in future Claim reports and become eligible for a Proof of Funds report instead. If any selected invoice has excluded line items, it keeps its current claim status so the excluded portion stays claimable in a later report. + +:::caution +Marking invoices as claimed cannot be undone from the wizard. If you are not ready to close out these invoices yet -- for example, you generated the PDF just to double-check totals -- choose **Finish without marking**. +::: + +## Long Descriptions and Multi-Page Tables + +Usage text and other free-form fields can run long, especially with German construction terminology. The report table wraps and paginates automatically to keep every character intact rather than truncating or dropping content; where a row's text must continue onto a following page, the continuation is marked so it doesn't read as a truncated or broken row. Split and deposit-reduced amounts each get their own footnote so it's clear at a glance *why* a row's allocated amount differs from its invoice amount. diff --git a/docs/src/guides/budget/index.md b/docs/src/guides/budget/index.md index a172d23c7..41dd158ca 100644 --- a/docs/src/guides/budget/index.md +++ b/docs/src/guides/budget/index.md @@ -17,6 +17,7 @@ The budget system provides: - **Invoices** -- Track vendor invoices and their line-item links to budget lines (vendor records now live under the Settings section -- see [Invoices & Vendors](vendors-and-invoices)) - **Subsidy Programs** -- Manage percentage-based or fixed-amount subsidies that reduce your effective costs - **Budget Overview Dashboard** -- See your project's financial health grouped by **area hierarchy**, with clickable summary tiles, expandable nested rows, source attribution badges on every line, a per-source filter that updates the entire breakdown, and print-friendly styling +- **Bank Reports** -- Generate a formatted PDF for a budget source -- a claim submission, a proof-of-funds statement, or a general overview -- with an editable cover letter, optional AI-drafted content, and configurable columns ## How It Fits Together @@ -37,3 +38,4 @@ When a vendor submits an **invoice**, you link the relevant budget lines to the - [Auto-itemize Invoices](auto-itemize) -- Extract line items from invoice PDFs using your own LLM provider - [Subsidies](subsidies) -- Manage subsidy programs - [Budget Overview](budget-overview) -- Area-grouped cost breakdown, source attribution badges, per-source filter, clickable tiles, and print output +- [Bank Reports](bank-reports) -- Generate claim, proof-of-funds, and budget overview PDFs for a financing source diff --git a/docs/src/guides/budget/subsidies.md b/docs/src/guides/budget/subsidies.md index 894e658f1..f197437cc 100644 --- a/docs/src/guides/budget/subsidies.md +++ b/docs/src/guides/budget/subsidies.md @@ -16,34 +16,43 @@ Subsidy programs reduce the effective cost of your construction project. Corners ## Creating a Subsidy -Navigate to **Budget > Subsidies** in the sidebar. Click **New Subsidy** and provide: +Navigate to **Budget > Subsidies** in the sidebar. Click **Add Program** and provide: -- **Name** -- A descriptive label (e.g., "Energy Efficiency Rebate") -- **Type** -- Percentage or Fixed Amount -- **Amount / Rate** -- The fixed amount or percentage rate -- **Budget Category** -- Which category this subsidy applies to -- **Maximum Amount** -- Optional cap on the total subsidy payout (for percentage subsidies) -- **Status** -- The current status of the subsidy application +- **Name** -- A descriptive label (e.g., "Energy Efficiency Rebate Program") +- **Reduction Type** -- Percentage or Fixed Amount +- **Reduction Value** -- The percentage rate or fixed amount +- **Applicable Budget Categories** -- Which categories this program applies to (see [Applicable Categories](#applicable-categories) below) +- **Maximum Amount** -- Optional cap on the total subsidy payout +- **Application Status** -- The current status of the subsidy application +- **Deadline**, **Eligibility Requirements**, **Notes** -- Optional reference fields -## Subsidy Statuses +## Applicable Categories + +A subsidy program can target one or more [budget categories](categories) at once -- tick the categories it applies to, or use **Select All** / **Deselect All**. Two special cases: + +- **No categories selected** -- the subsidy is treated as universal and applies to every budget line, regardless of category. +- **No Category** -- a separate checkbox (independent of the category list) that includes budget lines that have no category assigned at all. Leave it unchecked to exclude uncategorized lines even when the program targets specific categories. + +## Application Statuses | Status | Meaning | |--------|---------| -| **Pending** | Application submitted, awaiting decision | -| **Approved** | Subsidy approved but not yet received | -| **Rejected** | Subsidy application was denied | -| **Disbursed** | Subsidy funds have been received | +| **Eligible** | Identified as a program you qualify for, not yet applied | +| **Applied** | Application has been submitted, awaiting decision | +| **Approved** | Subsidy approved but funds not yet received | +| **Received** | Subsidy funds have been received | +| **Rejected** | Application was denied | -Only subsidies with **Approved** or **Disbursed** status are applied to budget calculations. Pending and rejected subsidies are tracked but do not affect the budget overview. +Only subsidies with **Approved** or **Received** status are applied to budget calculations. Eligible, applied, and rejected subsidies are tracked but do not affect the budget overview. ## How Subsidies Affect the Budget -Subsidies reduce the total cost shown in the [Budget Overview](budget-overview). A subsidy applies to all budget lines in its linked category across all work items and [household items](/guides/household-items): +Subsidies reduce the total cost shown in the [Budget Overview](budget-overview). A subsidy applies to every budget line that matches its applicable categories (see above), across all work items and [household items](/guides/household-items): -- **Percentage subsidy**: Reduces the category total by the specified percentage -- **Fixed-amount subsidy**: Subtracts the flat amount from the category total +- **Percentage subsidy**: Reduces the total of matching lines by the specified percentage +- **Fixed-amount subsidy**: Subtracts the flat amount from the total of matching lines -Multiple subsidies can apply to the same category, and their reductions stack. +Multiple subsidies can apply to the same budget line, and their reductions stack. ### Maximum Amount Cap