Skip to content

release: promote beta to main (cost basis filter, diary fix, auto-itemize refactor)#1789

Closed
steilerDev wants to merge 4 commits into
mainfrom
beta
Closed

release: promote beta to main (cost basis filter, diary fix, auto-itemize refactor)#1789
steilerDev wants to merge 4 commits into
mainfrom
beta

Conversation

@steilerDev

Copy link
Copy Markdown
Owner

Release Summary

This release ships a Cost Basis payment-status filter for the Cost Breakdown table, a diary UX fix, and an auto-itemize workflow harmonization.

Changes

Features

Fixes

Refactoring

Change Inventory

Backend (server/, shared/)

  • shared/src/types/budgetBreakdown.ts — new actualCostPaid, actualCostPending fields on all breakdown entities
  • server/src/services/budgetBreakdownService.ts — deposit-aware paid/pending rollup via computeDepositAwareAggregates
  • server/src/services/budgetBreakdownService.paid.test.ts — 11 new integration tests

Frontend (client/)

  • client/src/components/CostBreakdownTable/CostBreakdownTable.tsxCostBasisSelect component, resolveBudgetLineCost/resolveAggregateCost helpers
  • client/src/components/CostBreakdownTable/CostBreakdownTable.module.css — new .controlBar, .costBasisField, .costBasisSelect styles
  • client/src/pages/BudgetOverviewPage/BudgetOverviewPage.tsx — URL state via ?paymentStatus=paid|outstanding
  • client/src/i18n/en/budget.json, client/src/i18n/de/budget.json — Cost Basis i18n keys
  • client/src/pages/DiaryPage/DiaryPage.tsx — default filter set to manual
  • Auto-itemize flow harmonization files

E2E Tests (e2e/)

  • e2e/tests/budget/budget-cost-basis.spec.ts — 16 new E2E scenarios for Cost Basis filter
  • e2e/pages/BudgetOverviewPage.ts — new costBasisSelect and costBasisLabel locators
  • Fixture updates across 4 existing budget E2E specs

Manual Validation Checklist

Cost Basis Filter (#1786)

  • Navigate to Budget Overview → Cost Breakdown table. Confirm a "Cost Basis" dropdown is visible next to the perspective toggle (min/avg/max).
  • Default is "All" and the table shows the existing cost view.
  • Select "Paid" — all cost columns show only paid/claimed amounts. Budget lines with no paid invoices show €0. URL updates to ?paymentStatus=paid.
  • Select "Outstanding" — invoiced lines show pending amounts; uninvoiced lines show projected costs. URL updates to ?paymentStatus=outstanding.
  • Reload the page with ?paymentStatus=paid in the URL — "Paid" is pre-selected.
  • Switch perspective toggle (min/avg/max) while "Paid" or "Outstanding" is active — both controls work independently.
  • Deselect a budget source while "Paid" is active — source filter and cost basis filter work together.

Diary Default Filter (#1782)

  • Navigate to the Construction Diary. Confirm the initial filter defaults to "Manual" (not "All"), hiding auto-generated events on load.
  • Switch filter to "All" — auto-generated events appear. Switch back to "Manual" — they hide.

Auto-itemize Harmonization (#1780)

  • Open an invoice in the auto-itemize flow for an existing invoice (one already linked to a work item). Confirm the flow works as before — items can be appended or replaced.
  • Open an invoice in the auto-itemize flow for a new invoice. Confirm the flow works as before.

Testing

  • DockerHub beta image: docker pull steilerdev/cornerstone:beta
  • PR-specific image: docker pull steilerdev/cornerstone:pr-1789

Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com

steilerDev and others added 3 commits June 22, 2026 10:35
…ows (#1780)

* refactor(auto-itemize): harmonize existing-invoice and new-invoice flows

Extract shared logic duplicated between AutoItemizePage and
PaperlessInvoiceReviewPage into three reusable units:

- `materializeInlineDrafts` (autoItemizeDraftUtils.ts) — pure async helper
  that resolves inline budget-line drafts at save time. Financial fields
  (includesVat, quantity, unitPrice, totalAmount) now come from the LIVE
  line state rather than the draft snapshot, so the created budget line
  always matches the invoice line item as the user sees it at save time.

- `AutoItemizePdfPreview` component — shared PDF preview column (iframe +
  loading overlay + fallback) previously copy-pasted verbatim.

- `useAutoItemizeLines` hook — encapsulates all line-state management and
  picker integration (useBudgetLinePicker, all CRUD handlers, initializeStaticData
  effect, budgetSourceId re-defaulting). Both pages now call one hook instead
  of ~150 lines of duplicated boilerplate each.

Also aligns confidence derivation: both flows now derive confidence from the
Paperless documentType (Invoice/Quotation) before falling back to the ML score,
closing a behavioral divergence that previously only PaperlessInvoiceReviewPage
implemented.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(auto-itemize): absorb two-way VAT sync and fix mock types in tests

- Absorb beta's two-way VAT sync (from #1775/#1779) into useAutoItemizeLines:
  - onFieldChange: when field=includesVat, mirrors to inlineCreatedBudgetLineDraft
  - onInlineDraftChange: when updates.includesVat set, mirrors back to line
- Fix jest.fn() mock type annotations in autoItemizeDraftUtils.test.ts
  (TS2345: never inference with untyped mocks in Jest 30)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Frank Steiler <frank@steilerdev.de>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(diary): update E2E tests for default filter mode change from 'all' to 'manual'

- In diary-r2-uat.spec.ts: rename test and flip assertions so Manual chip
  is expected aria-pressed=true by default, All and Automatic are false
- In diary-list.spec.ts: add Scenario 12 (@smoke) asserting the Manual mode
  chip is the default when navigating to /diary with no params, and that the
  initial API request includes the daily_log type param

Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.6) <noreply@anthropic.com>

* fix(diary): default filter to 'manual' instead of 'all'

Change the diary page initial filter mode from 'all' to 'manual' so that
users land on a focused view of manually authored entries by default, rather
than seeing all auto-generated events mixed in.

- DiaryPage.tsx: default filterMode state reads 'manual' when no URL param
  is present
- DiaryPage.tsx: handleClearAll resets filterMode to 'manual' instead of 'all'
- DiaryPage.test.tsx: add 4 unit tests covering default mode, explicit URL
  param overrides (all, automatic), and Clear All reset behaviour

Fixes #1781

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 qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>
Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com>

* test(diary): fix handleClearAll test to use search query so clear button renders

Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com>
Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com>

* chore(diary): fix comment typo budget_update -> budget_breach in E2E spec

Co-Authored-By: Claude e2e-test-engineer (Sonnet) <noreply@anthropic.com>

---------

Co-authored-by: Frank Steiler <frank@steilerdev.de>
Co-authored-by: Claude e2e-test-engineer (Sonnet 4.6) <noreply@anthropic.com>
…1786)

Adds a "Cost Basis" dropdown filter (All / Paid / Outstanding) to the Cost Breakdown table, separate from the existing min/avg/max perspective toggle.

Fixes #1786
@steilerDev

Copy link
Copy Markdown
Owner Author

Detailed Validation Steps

1. Cost Basis Filter — Budget Overview (#1786)

Setup: Open the Budget Overview page with some invoices in different states (pending/paid/claimed).

All (default)

  1. Visit /budget — the Cost Breakdown table loads with "All" selected in the Cost Basis dropdown.
  2. Verify cost columns show: actual cost for invoiced lines, projected cost for uninvoiced lines (same as before this feature).

Paid mode

  1. Select "Paid" from the Cost Basis dropdown.
  2. Verify URL changes to include ?paymentStatus=paid.
  3. Budget lines with fully paid invoices → show paid amount.
  4. Budget lines with pending invoices → show €0.
  5. Budget lines with no invoice → show €0.
  6. Deposit-split invoices (claimed status) → show proportional paid+claimed amount.
  7. Area subtotals and grand totals update to reflect only paid amounts.

Outstanding mode

  1. Select "Outstanding" from the Cost Basis dropdown.
  2. Verify URL changes to include ?paymentStatus=outstanding.
  3. Budget lines with pending invoice amounts → show pending amount.
  4. Budget lines with no invoice → show projected (planned) cost.
  5. Area subtotals and grand totals update correctly.

URL persistence

  1. Reload page with ?paymentStatus=paid → "Paid" is pre-selected, table shows paid view.
  2. Reload page with ?paymentStatus=outstanding → "Outstanding" is pre-selected.
  3. Reload with ?paymentStatus=invalid → falls back to "All".

Independence from perspective toggle

  1. With "Paid" selected, switch perspective to "Min" then "Max" — the perspective controls projected amounts; in paid mode this has minimal visible effect.
  2. With "Outstanding" selected, switch perspective to "Min" → projected (uninvoiced) lines use minimum estimate.

2. Diary Default Filter (#1782)

  1. Navigate to Construction Diary.
  2. Verify the filter shows "Manual" selected by default (not "All").
  3. Auto-generated events (e.g. status-change events) should NOT appear on initial load.
  4. Switch to "All" → auto-generated events appear.
  5. Switch back to "Manual" → auto-generated events disappear.

3. Auto-itemize Harmonization (#1780)

  1. Upload a PDF invoice and trigger auto-itemize on it (Paperless integration required, or use mock).
  2. If the invoice already has linked items → verify "Append" and "Replace" modes work.
  3. If the invoice is new → verify the standard auto-itemize flow completes normally.
  4. No regressions in the auto-itemize workflow.

Diary default changed from 'all' to 'manual' in #1782. Three test scenarios
in diary-r2-uat.spec.ts were asserting the old default ('All' chip pressed)
or trying to click 'Manual' when it was already active (causing timeout).

Co-authored-by: Frank Steiler <frank@steilerdev.de>
Co-authored-by: Claude e2e-test-engineer (claude-sonnet-4-6) <noreply@anthropic.com>
@steilerDev

Copy link
Copy Markdown
Owner Author

Closing — E2E test fix (#1790) needed to be merged to beta first. Reopening as PR #1791.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant