release: promote beta to main (invoice deposits + cleanups)#1415
Open
steilerDev wants to merge 18 commits into
Open
release: promote beta to main (invoice deposits + cleanups)#1415steilerDev wants to merge 18 commits into
steilerDev wants to merge 18 commits into
Conversation
* feat(invoice): unify budget-line creation with BudgetLineForm component - Replace slim 4-field form with reusable BudgetLineForm component in picker Step 2 - Add vendor fetch to showCreateBudgetLineForm (vendors now fetched alongside categories/sources) - Implement complete VAT math following useBudgetSection.handleSaveBudgetLine pattern: - Direct mode: plannedAmount *= (includesVat ? 1 : 1.19), rounded to 2 decimals - Unit mode: plannedAmount = qty * price (no VAT multiplier) - Auto-link newly-created budget line to invoice using newBudgetLine.plannedAmount - Replace createFormData state with rich BudgetLineFormState - Handle link errors (ITEMIZED_SUM_EXCEEDS_INVOICE, BUDGET_LINE_ALREADY_LINKED): - Transition back to existing-line list with error banner - New line shows as unlinked in the list - Add focus management: focus to #budget-description on form open, back to button on cancel - Add fieldset/visually-hidden legend for screen reader context - Update CSS: .createBudgetLineForm now has --color-bg-primary bg, no padding (BudgetLineForm.container owns it) - Remove .createFormTitle; add .srOnly utility class - Add two i18n keys (English only) to budget.json under invoiceDetail.budgetLines - Conditional rendering: hide existing-line list when create form is shown - Add createBudgetLineButtonRef for focus restoration Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com> * test(invoice): add tests and translations for budget-line auto-link (#1401) - Add 14 unit-test scenarios covering vendor fetch, VAT math, create+link sequence, link error transitions (ITEMIZED_SUM_EXCEEDS_INVOICE / BUDGET_LINE_ALREADY_LINKED), cancel, and regression on select-existing flow - Mock fetchVendors and BudgetLineForm at the module boundary for ESM tests - Add Playwright E2E scenarios: happy path (unit + direct pricing), non-empty list, link-exceeds-invoice error, mobile responsive smoke, Escape-key close - Extend InvoiceDetailPage POM with budget-line picker and create-form locators - Add German translations for the two new budget.invoiceDetail.budgetLines keys using the glossary term "Budgetposition" Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com> Co-Authored-By: Claude translator (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> * fix(e2e): correct invoice budget-line auto-link spec assertions (#1401) - Remove toContainText('Roof materials') on budgetSection in Scenario 1 — the description is inside a collapsed InvoiceGroup accordion; the invoiceLink badge assertion already proves the link - Replace fill('100') with click() + pressSequentially('100') in the mobile Scenario 4 — fill() does not fire React onChange reliably on the mobile viewport for number inputs Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com> * fix(e2e): scroll submit button into view in mobile scenario (#1401) Playwright's auto-scroll fails inside the picker modal (overflow: hidden on the modal container), so the submit button stayed outside the viewport on the mobile run and click() timed out. Explicit scrollIntoViewIfNeeded scrolls the element within its scrollable ancestor. Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com> * fix(invoice,e2e): mobile modal scroll + locale-independent picker locators (#1401) - Make .modalBody scrollable on mobile so the rich BudgetLineForm can be used at viewport widths < 768px (the form is now taller than the slim one it replaced) - Convert picker submit/unit-mode/cancel POM locators to structural selectors so the spec is robust to German locale state leaked by the i18n test suite Fixes #1401 Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com> Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> * style(invoice): move fieldset reset from inline style to CSS module (#1401) Addresses non-blocking nit from product-architect and ux-designer reviews. Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com> --------- Co-authored-by: Frank Steiler <frank@steiler.de> Co-authored-by: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com>
#1406) * feat(invoice): add deposit support (schema, CRUD API, and cascade) Adds invoice_deposits table and CRUD endpoints under both /api/invoices/:invoiceId/deposits and the vendor-scoped variant. Each deposit has its own status (pending → paid → claimed) and contributes to budget rollups based on its own state, while the parent invoice contributes its residual (final payment) amount under its own status. State machine enforced server-side: pending → paid, paid → claimed, paid → pending (correction), claimed → paid (correction). Disallowed transitions return INVALID_DEPOSIT_STATUS_TRANSITION (400). Σ deposit amounts ≤ invoice amount enforced as DEPOSITS_EXCEED_INVOICE_TOTAL. Read-check-write is atomic via drizzle's db.transaction((tx) => {}) to prevent sum-invariant races. Diary auto-events fire only on transitions into paid/claimed, reusing the invoice_status entry type. GET /api/invoices/:id now embeds deposits + finalPaymentAmount; list endpoints intentionally return empty deposits and finalPaymentAmount = invoice.amount for payload optimisation. Fixes #1403 Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude backend-developer (Haiku 4.5) <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com> * test(invoice): add deposits + finalPaymentAmount to existing Invoice mocks Existing client-side test fixtures construct Invoice objects literally; adding the new required fields on the shared Invoice interface broke their typecheck. Patch the factories (InvoiceLinkModal, HouseholdItemDetailPage.budget) and individual literals (others) so existing tests continue to compile under the updated Invoice shape. Production code is unaffected — backend already returns deposits: [] and finalPaymentAmount = invoice.amount for invoices with no deposits. Refs #1403 Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.6) <noreply@anthropic.com> * fix(invoice): finalPaymentAmount sums all deposits; sort by dueDate Two AC-10 violations flagged in PR review: 1. finalPaymentAmount filtered deposits to status='claimed' only, but AC-10 (and the user requirement) specifies the un-itemized residual = invoice.amount - Σ ALL deposits.amount, regardless of status. The claimed-only semantic would double-count pending/paid deposits in the #1405 budget rollup. 2. Deposit ordering was inconsistent: toInvoice()'s embedded fetch had no orderBy at all; listDepositsForInvoice ordered by createdAt only. AC-10 specifies dueDate ASC, createdAt ASC in both paths. Updates the three tests that hardcoded the wrong claimed-only semantic and rewrites scenario 26 to create deposits with out-of-order dueDate so the new ordering is actually exercised (plus a tie-breaker case). Refs #1403 Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude backend-developer (Haiku 4.5) <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com> * test(invoice): fix scenario 26b double-setup collision on users.email UNIQUE The test called setup() twice in the same body, causing the second INSERT into users to fail with a UNIQUE constraint violation on user@example.com. Replace the second setup() call with direct createTestVendor/createTestInvoice helpers so the fresh invoice is created without inserting a duplicate user. Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com> * docs(invoice): correct stale JSDoc on Invoice.finalPaymentAmount Comment used to read "minus sum of claimed deposits" — describing the pre-fix behaviour. After the round-2 fix, finalPaymentAmount subtracts all deposit amounts regardless of status (per AC-10). Update the JSDoc to match. Refs #1403 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude backend-developer (Haiku 4.5) <noreply@anthropic.com> --------- Co-authored-by: Frank Steiler <frank@steiler.de> Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com>
Bumps the github-actions group with 2 updates: [actions/cache](https://github.com/actions/cache) and [github/codeql-action](https://github.com/github/codeql-action). Updates `actions/cache` from 5.0.4 to 5.0.5 - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](actions/cache@6682284...27d5ce7) Updates `github/codeql-action` from 4.35.2 to 4.35.3 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@95e58e9...e46ed2c) --- updated-dependencies: - dependency-name: actions/cache dependency-version: 5.0.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: github/codeql-action dependency-version: 4.35.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the dev-dependencies group with 6 updates: | Package | From | To | | --- | --- | --- | | [eslint](https://github.com/eslint/eslint) | `10.2.0` | `10.3.0` | | [stylelint](https://github.com/stylelint/stylelint) | `17.8.0` | `17.9.1` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.58.2` | `8.59.1` | | [webpack](https://github.com/webpack/webpack) | `5.105.0` | `5.106.2` | | [@docusaurus/core](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus) | `3.10.0` | `3.10.1` | | [@docusaurus/preset-classic](https://github.com/facebook/docusaurus/tree/HEAD/packages/docusaurus-preset-classic) | `3.10.0` | `3.10.1` | Updates `eslint` from 10.2.0 to 10.3.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](eslint/eslint@v10.2.0...v10.3.0) Updates `stylelint` from 17.8.0 to 17.9.1 - [Release notes](https://github.com/stylelint/stylelint/releases) - [Changelog](https://github.com/stylelint/stylelint/blob/main/CHANGELOG.md) - [Commits](stylelint/stylelint@17.8.0...17.9.1) Updates `typescript-eslint` from 8.58.2 to 8.59.1 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.1/packages/typescript-eslint) Updates `webpack` from 5.105.0 to 5.106.2 - [Release notes](https://github.com/webpack/webpack/releases) - [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md) - [Commits](webpack/webpack@v5.105.0...v5.106.2) Updates `@docusaurus/core` from 3.10.0 to 3.10.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.10.1/packages/docusaurus) Updates `@docusaurus/preset-classic` from 3.10.0 to 3.10.1 - [Release notes](https://github.com/facebook/docusaurus/releases) - [Changelog](https://github.com/facebook/docusaurus/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/docusaurus/commits/v3.10.1/packages/docusaurus-preset-classic) --- updated-dependencies: - dependency-name: eslint dependency-version: 10.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: stylelint dependency-version: 17.9.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: typescript-eslint dependency-version: 8.59.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: webpack dependency-version: 5.106.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: dev-dependencies - dependency-name: "@docusaurus/core" dependency-version: 3.10.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies - dependency-name: "@docusaurus/preset-classic" dependency-version: 3.10.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: dev-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the prod-dependencies group with 5 updates: | Package | From | To | | --- | --- | --- | | [@fastify/static](https://github.com/fastify/fastify-static) | `9.1.1` | `9.1.3` | | [openid-client](https://github.com/panva/openid-client) | `6.8.3` | `6.8.4` | | [i18next](https://github.com/i18next/i18next) | `26.0.5` | `26.0.8` | | [react-i18next](https://github.com/i18next/react-i18next) | `17.0.4` | `17.0.6` | | [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) | `7.14.1` | `7.14.2` | Updates `@fastify/static` from 9.1.1 to 9.1.3 - [Release notes](https://github.com/fastify/fastify-static/releases) - [Commits](fastify/fastify-static@v9.1.1...v9.1.3) Updates `openid-client` from 6.8.3 to 6.8.4 - [Release notes](https://github.com/panva/openid-client/releases) - [Changelog](https://github.com/panva/openid-client/blob/main/CHANGELOG.md) - [Commits](panva/openid-client@v6.8.3...v6.8.4) Updates `i18next` from 26.0.5 to 26.0.8 - [Release notes](https://github.com/i18next/i18next/releases) - [Changelog](https://github.com/i18next/i18next/blob/master/CHANGELOG.md) - [Commits](i18next/i18next@v26.0.5...v26.0.8) Updates `react-i18next` from 17.0.4 to 17.0.6 - [Changelog](https://github.com/i18next/react-i18next/blob/master/CHANGELOG.md) - [Commits](i18next/react-i18next@v17.0.4...v17.0.6) Updates `react-router-dom` from 7.14.1 to 7.14.2 - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@7.14.2/packages/react-router-dom) --- updated-dependencies: - dependency-name: "@fastify/static" dependency-version: 9.1.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: openid-client dependency-version: 6.8.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: i18next dependency-version: 26.0.8 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: react-i18next dependency-version: 17.0.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies - dependency-name: react-router-dom dependency-version: 7.14.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: prod-dependencies ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.0 to 3.1.2. - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](fastify/fast-uri@v3.1.0...v3.1.2) --- updated-dependencies: - dependency-name: fast-uri dependency-version: 3.1.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…ups (#1404, #1405) (#1407) * feat(invoice,budget): invoice deposits UI + deposit-aware budget rollups #1404 — Add a Deposits section to the invoice detail page with add / edit / delete + state-toggle controls and a "Final payment" row showing the residual amount. Uses shared Modal, Badge, FormError, and EmptyState components. Responsive: table on desktop/tablet (claimed-date column hidden < 1024 px), card list on mobile. Overflow menu supports full keyboard navigation (ArrowUp/Down/Home/End/ Escape) per the WAI-ARIA Menu Button pattern. New i18n keys under invoiceDetail.deposits.* in EN and DE. Glossary updated: Deposit → Abschlagszahlung, Final payment → Schlusszahlung. #1405 — Budget rollups now split each invoice's contribution between its deposits (under each deposit's status) and the residual (under the parent invoice's status), using a proportional split: deposit contribution_i = ibl.itemizedAmount × (d_i.amount / I.amount) residual contribution = ibl.itemizedAmount × ((I.amount − Σ d) / I.amount) Zero-deposit invoices behave identically to today (regression-tested). All rollup queries use one extra LEFT JOIN onto invoice_deposits — no N+1. Applies to: budget overview, budget sources (paid / unclaimed / claimed / discretionary), work-item + household-item budget summaries (actualCost / actualCostPaid / actualCostClaimed). No new schema, no new endpoints, no response-shape changes. Fixes #1404 Fixes #1405 Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude backend-developer (Haiku 4.5) <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com> Co-Authored-By: Claude translator (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com> * fix(invoice): pass tErrors to translateApiError and Badge label asserts; harden E2E add-deposit locator - InvoiceDepositsSection now imports useTranslation('errors') as tErrors and passes it to translateApiError at both call sites - BadgeVariantMap labels + classNames use non-null assertions (!) per the established UserManagementPage pattern - E2E InvoiceDetailPage POM addDepositButton switched to getByLabel('Add deposit', { exact: true }) so it no longer collides with the EmptyState CTA in strict mode. Added a separate addDepositFromEmptyState locator for future use. Refs #1404 Refs #1405 Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com> * fix(invoice): omit paidDate/claimedDate from deposit payload when status is pending emptyForm() defaults paidDate and claimedDate to today's date, which made the add/edit payloads always include those keys. The server validates `if (data.paidDate !== undefined) { … }` and rejects with INVALID_DEPOSIT_DATE_FOR_STATUS when the status is pending — even when the value is null. Spread-conditionally include paidDate only when status !== 'pending', and claimedDate only when status === 'claimed'. Refs #1404 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com> * build(deps): drop stale webpack override blocking npm ci The root package.json overrides pinned webpack@5.105.0 — left over from before the dep-bump bot upgraded client/package.json to webpack@5.106.2. The lockfile has 5.106.2; the override forces 5.105.0; npm ci then reports the 5.105.0 nested deps (eslint-scope@5.1.1, mime-types@2.1.35, estraverse@4.3.0, mime-db@1.52.0) missing from the lockfile and refuses to install. This blocked Docker builds on both this PR and beta itself. Remove the redundant override; the client workspace already pins the version we want. Verified locally with `npm ci --dry-run`: clean install, no EUSAGE errors. Refs #1404 Refs #1405 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude backend-developer (Haiku 4.5) <noreply@anthropic.com> * fix(invoice): correct availableHeadroom field name and harden E2E locators #1404 follow-up — three issues surfaced by full-E2E shard runs: 1. Wrong error-detail field name: InvoiceDepositsSection read details.available, but the server's DEPOSITS_EXCEED_INVOICE_TOTAL payload uses details.availableHeadroom. Toast showed €0.00 instead of the real headroom. Rename the field reference + the i18n placeholder ({{available}} → {{availableHeadroom}}). 2. Flaky locator chain: the POM used page.locator('[role="dialog"]').getByRole('button', { name: ... }) for Cancel/Confirm buttons, which times out in headless CI. Add stable data-testid attributes to the 6 modal buttons and switch the POM to page.getByTestId(). 3. State-machine violation: two E2E setup paths called createDepositViaApi with status='paid'/'claimed' directly. The server only allows pending→paid (and paid→claimed). Use multi-step PATCH transitions in those setups. Refs #1404 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude translator (Sonnet 4.6) <noreply@anthropic.com> * test(invoice): wire deleteDepositCancelButton locator to delete modal Scenario 4's delete-paid-deposit test clicked the wrong Cancel button: depositModalCancel (data-testid="deposit-modal-cancel") targets the Add/Edit modal Cancel. The Delete modal has its own Cancel button with data-testid="deposit-delete-cancel". The locator existed in the production component but was not yet exposed on the page object. Add deleteDepositCancelButton to the POM and switch the test to it. This unblocks Shard 4 of the full E2E matrix. Refs #1404 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.6) <noreply@anthropic.com> * test(invoice): narrow over-broad locators in Scenarios 3 and 2 Two E2E test fixes from the full-shard CI failures: - Scenario 3 (revert-to-paid lifecycle, ~line 325): drop the section-level not.toContainText('Claimed') assertion. The "Claimed date" column header is always rendered when deposits exist, so a section-level "Claimed" absence check is structurally unpassable. The earlier toContainText('Paid') badge assertion already verifies the revert took effect. - Scenario 2 (add deposit on mobile, ~line 199): filter the depositRows locator to visible elements before .first(). On mobile (≤767 px) the table renders both tableRow elements (display: none) and mobileCard elements (visible); .first() picked the hidden tableRow. Refs #1404 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.6) <noreply@anthropic.com> * test(invoice): filter openDepositMenu locator to visible buttons On mobile (≤767 px) the desktop table is hidden via CSS but its overflow buttons remain in the DOM. openDepositMenu() called .first() without filtering, so it resolved to a hidden table button and timed out waiting for stability. Add .filter({ visible: true }) before .first() in both branches of the helper. Refs #1404 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.6) <noreply@anthropic.com> * test(invoice): filter [role=menu] waitFor to visible elements Mobile (≤767 px) hides the desktop table via CSS, but the table's [role="menu"] elements stay in the DOM. openDepositMenu() waited for the first [role="menu"] without filtering visibility, so on mobile it resolved to the hidden desktop menu and timed out. Add the same .filter({ visible: true }) pattern used for the menu-trigger button. Refs #1404 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.6) <noreply@anthropic.com> * test(invoice): filter clickDepositMenuItem to visible menuitems Mobile/tablet hide the desktop table via CSS but the hidden table keeps its [role="menuitem"] nodes in the DOM. Without a visibility filter, .first() picked the hidden table menuitem on mobile and the click timed out. Filter to visible elements before resolving the label-text match. Refs #1404 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.6) <noreply@anthropic.com> * fix(invoice): use valid CSS tokens for warning banner and dropdown z-index UX review on PR #1407 caught two CSS bugs in InvoiceDepositsSection: - Warning banner referenced --color-warning-border and --color-warning-text, neither of which exist in tokens.css. Browsers silently ignored them, leaving the banner border-less and inheriting the parent text color. Replace with var(--color-warning) and var(--color-warning-text-on-light). - Menu used hardcoded z-index: 10 instead of var(--z-dropdown). Also updates wiki/API-Contract.md to document the deposit-aware proportional-split semantics on actualCostPaid, claimedAmount, and paidAmount, plus a new explainer section, closing the documentation drift flagged by the architect review. Refs #1404 Refs #1405 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com> Co-Authored-By: Claude backend-developer (Haiku 4.5) <noreply@anthropic.com> --------- Co-authored-by: Frank Steiler <frank@steiler.de> Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com>
InvoiceStatusBreakdown.summary (consumed by the InvoicesPage header and the Dashboard InvoicePipelineCard) was missed when #1405 migrated budget rollups to the deposit-aware split. A quotation invoice of €1000 with a pending deposit of €200 showed €1000 under Quotation and €0 under Pending — should be €800 (residual) and €200 (deposit). Adds aggregateInvoiceStatusBreakdown() to depositAggregateUtils.ts and rewrites listAllInvoices() summary to use it with a LEFT JOIN onto invoice_deposits. Per-invoice split: summary[parent].totalAmount accrues max(0, amount - Σ deposits), each summary[deposit.status].totalAmount accrues deposit.amount; count stays per-invoice (not per row). Summary remains GLOBAL (filter-independent) — the existing UX where the header cards stay stable while the user filters the list is preserved. The pre-existing "summary reflects global counts" test is unmodified. Wiki updated: API-Contract.md documents the deposit-aware semantic and adds quotation to the example summary block. Fixes #1411 Co-authored-by: Frank Steiler <frank@steiler.de> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…face revert errors (#1413) (#1414) * chore(invoice,budget): dedupe split helper, extract OverflowMenu, surface revert errors Closes the architect's medium-severity recommendations from the #1407 and #1412 reviews. Four scopes: 1. Extract splitByDeposits() helper in depositAggregateUtils.ts and reuse across the 4 call sites that inlined the proportional-split + dedup pattern (computeDepositAwareAggregates, computeStatusContribution, aggregateInvoiceStatusBreakdown, and computeDiscretionaryInvoiceAmount in budgetSourceService.ts). Behaviour-preserving — existing tests pass unmodified. 2. Extract a shared OverflowMenu component (client/src/components/ OverflowMenu/). DepositRow and DepositCard both consume it instead of duplicating ~330 lines of menu code. Full WAI-ARIA Menu Button keyboard nav, mobile 44px touch targets, design-token-only CSS, dark mode handled by the token cascade. Same aria-haspopup/role attributes as the inline implementation — existing E2E locators still work. 3. Replace inline style={{}} on the <tr> opacity transition with a CSS module class (.tableRowMutating), matching the prior fd73bca fix. 4. Surface API errors in handleRevertToPending, handleRevertToPaid, and handleStateConfirm. Menu-driven reverts show a section-level FormError banner (auto-dismiss 6s). State-confirm modal shows FormError inside the dialog. Two new i18n keys for network-error fallbacks; existing translateApiError() covers coded server errors. Fixes #1413 Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude backend-developer (Haiku 4.5) <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com> Co-Authored-By: Claude translator (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com> * fix(overflow-menu): canonical focus tokens and skip-disabled keyboard nav UX-designer review on PR #1414 found two non-blocking nits in the new OverflowMenu shared component: - Default item focus ring switched from inset 2px var(--color-primary) to inset 3px var(--color-focus-ring) — the canonical menu-item ring used elsewhere in the codebase. - Added missing .itemDanger:focus-visible rule with var(--color-focus-ring-danger) so destructive items have a distinguishable keyboard focus indicator. - Arrow-key / Home / End keyboard handlers and the initial-focus query now use [role="menuitem"]:not(:disabled), so the cursor skips disabled items. Refs #1413 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com> --------- Co-authored-by: Frank Steiler <frank@steiler.de> Co-authored-by: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com>
Owner
Author
Detailed Validation WalkthroughStep-by-step instructions to spot-check each major change against the beta image. Run the beta image locallyOpen http://localhost:3000 and log in. 1. Invoice deposits — basic flow
2. Invoice deposits — state machine
3. Sum invariant + error feedback
4. Delete with warning
5. Budget overview deposit-aware split (#1405 + #1412)
6. Budget overview — work-item / household-item rollups
7. Mobile layout
8. Invoice list row regression check
9. Empty state
10. Revert error feedback (#1413)This is harder to trigger without forcing a server error. If you want to validate:
11. Budget-line creation form (#1402)
12. German locale
13. Dark mode
RollbackIf a critical issue surfaces after promotion, the standard rollback path is:
The |
steilerDev
pushed a commit
that referenced
this pull request
May 12, 2026
Release-cycle housekeeping. Bumps glossary metadata so beta gets a fresh HEAD SHA, which unblocks the promotion PR #1415 — the existing beta HEAD is an auto-fix commit that skipped CI, leaving the promotion PR without associated CI checks. No glossary terms changed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude translator (Sonnet 4.6) <noreply@anthropic.com>
Release-cycle housekeeping. Bumps glossary metadata so beta gets a fresh HEAD SHA, which unblocks the promotion PR #1415 — the existing beta HEAD is an auto-fix commit that skipped CI, leaving the promotion PR without associated CI checks. No glossary terms changed. Co-authored-by: Frank Steiler <frank@steiler.de> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
|
🎉 This PR is included in version 2.6.0-beta.6 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
`npm audit fix` removes two stale transitive lockfile entries that are shadowed by the root serialize-javascript override (serialize-javascript and randombytes nested under @docusaurus/bundler). Pre-applying this in a regular commit so the auto-fix bot won't push another no-CI commit after the next beta merge, which has been blocking CI association on promotion PR #1415. No functional changes. Co-authored-by: Frank Steiler <frank@steiler.de> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
|
🎉 This PR is included in version 2.6.0-beta.7 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release Summary
Promotes the Invoice Deposits feature to stable, along with a budget-line form unification, regular dependency bumps, and a follow-up refactor closing the architect's tech-debt findings from the deposits review cycle.
Changes
Features
pending → paid → claimedwith corrections). Each deposit contributes to budget rollups under its own state; the invoice's residual contributes under the parent's state. Net result: budget overview, budget sources, work-item + household-item budget summaries, and the invoices status breakdown header all show deposit-aware totals.BudgetLineFormcomponent for the inline create flow (matches the rest of the project's create paths).Fixes
InvoicePipelineCardnow reflect the deposit split (Pending/Paid/Claimed/Quotation totals were previously off when invoices had deposits).Chores / Refactoring
splitByDeposits()helper to dedupe the proportional-split pattern across four call sites; new sharedOverflowMenucomponent replaces the duplicated inline menu inDepositRow/DepositCard; replaced inlinestyle={{}}with a CSS module class; surfaced API errors from revert handlers with aFormErrorbanner (auto-dismiss 6 s) and modal-level error in the state-confirm dialog.Change Inventory
Backend (
server/,shared/)server/src/db/migrations/0032_invoice_deposits.sql(new)server/src/db/schema.ts—invoiceDepositsDrizzle tableserver/src/routes/invoiceDeposits.ts(new) + testsserver/src/services/invoiceDepositService.ts(new) + testsserver/src/services/invoiceService.ts+ tests — deposit embedding on detail, deposit-aware status breakdown summary, list-endpoint payload optimisationserver/src/services/budgetOverviewService.ts+ tests — step 8 deposit-aware aggregateserver/src/services/budgetSourceService.ts+ tests — claimed/unclaimed/discretionary now deposit-awareserver/src/services/shared/budgetServiceFactory.ts+ tests —getInvoiceAggregates/resolveRelationsBatchdeposit-awareserver/src/services/shared/depositAggregateUtils.ts(new) + tests —splitByDeposits,computeDepositAwareAggregates,computeStatusContribution,aggregateInvoiceStatusBreakdownserver/src/services/diaryAutoEventService.ts+ tests —onDepositStatusChangedserver/src/errors/AppError.ts—DepositsExceedInvoiceTotalError,InvalidDepositStatusTransitionError,InvalidDepositDateForStatusErrorserver/src/app.ts— deposit route registrationshared/src/types/invoice.ts—InvoiceDeposit,InvoiceDepositStatus,CreateDepositRequest,UpdateDepositRequest,Invoice.deposits/Invoice.finalPaymentAmountshared/src/types/budget.ts+shared/src/types/budgetSource.ts— JSDoc clarifying deposit-aware split semanticsshared/src/types/errors.ts— 3 new error codesFrontend (
client/)client/src/components/OverflowMenu/(new) — shared component (Tsx + CSS + index)client/src/lib/invoiceDepositsApi.ts(new) + tests — typed API client for deposits CRUDclient/src/pages/InvoiceDetailPage/InvoiceDepositsSection.tsx(new) + tests — section with table on desktop/tablet, card list on mobile, add/edit/delete + state-toggle controls, Final-payment row, error surfacingclient/src/pages/InvoiceDetailPage/InvoiceDepositsSection.module.css(new)client/src/pages/InvoiceDetailPage/InvoiceBudgetLinesSection.tsx+ CSS + tests — unified to consume the sharedBudgetLineFormclient/src/pages/InvoiceDetailPage/InvoiceDetailPage.tsx+ tests — wired in the deposits sectionclient/src/i18n/en/budget.json— deposit-related strings + 2 network-error fallbacksclient/src/i18n/de/budget.json— DE mirror with glossary termsAbschlagszahlung/Schlusszahlungclient/src/i18n/glossary.json— new termsDeposit,Final paymentdeposits: []/finalPaymentAmounton Invoice mocksE2E Tests (
e2e/)e2e/tests/invoices/invoice-deposits.spec.ts(new) — 11 tests covering deposit lifecycle, Final-payment row, mobile card layout, error bannere2e/tests/invoices/invoice-budget-line-create-and-link.spec.ts— covers unified budget-line creation flowe2e/pages/InvoiceDetailPage.ts— extended POM (deposit locators, add-from-empty-state, modal cancel/save/delete-confirm/state-confirm bydata-testid)Build / Config / Wiki
package.json+package-lock.json— dep-bump fan-out; dropped stalewebpack: 5.105.0override that was blocking Dockernpm ciafter the bumpsclient/package.json,server/package.json,docs/package.json— version bumps.github/workflows/ci.yml,.github/workflows/release.yml— github-actions group bumpwiki/API-Contract.md— deposit endpoints,Invoiceshape withdeposits/finalPaymentAmount, deposit-aware aggregation explainer, new error codeswiki/Schema.md—invoice_depositstableManual Validation Checklist
Walk through each item against the beta image (or PR-specific image).
total − deposit.Σ deposits > total. The form shows an error with available headroom.pendingdeposit of €200 to aquotationinvoice of €1000. The InvoicesPage header summary should now show €800 under Quotation and €200 under Pending (instead of the old €1000 / €0). Budget overview totals and the Dashboard pipeline card should reflect the same split./budget/invoicesrows still show the parent invoice's full amount in the amount column (per Invoice deposits: detail-page UI (Deposits section, Final payment row, add/edit/delete + state toggles) #1404 AC-5).Testing
docker pull steilerdev/cornerstone:betadocker pull steilerdev/cornerstone:pr-<PR-NUMBER>(replace placeholder after PR creation)