release: promote beta to main (cost basis filter, diary fix, auto-itemize refactor)#1791
Open
steilerDev wants to merge 6 commits into
Open
release: promote beta to main (cost basis filter, diary fix, auto-itemize refactor)#1791steilerDev wants to merge 6 commits into
steilerDev wants to merge 6 commits into
Conversation
…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>
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>
Contributor
|
🎉 This PR is included in version 2.12.0-beta.2 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
…ter (#1792) * fix(e2e): update diary-r2-uat Scenario 2 for manual-default diary filter Scenario 2 ("Clicking Manual chip hides automatic type chips") was clicking the Manual mode chip when Manual is already the default — a no-op that fires no API request. waitForResponse would time out. Apply the same pattern already used for Scenario 10: switch to "All" first, wait for that API response, then click Manual and await the second response. Co-Authored-By: Claude e2e-test-engineer (Sonnet) <noreply@anthropic.com> * fix(e2e): update diary-automatic-events Scenario 2 for manual-default diary filter The work_item_status type chip is hidden in manual mode (the new default from #1782). Switch to "All" mode first before clicking it to make automatic type chips visible, then reset request capture so only the chip-click request is measured. Co-Authored-By: Claude e2e-test-engineer (Sonnet) <noreply@anthropic.com> * fix(e2e): fix queueCreateNewBudgetLine assertion to properly await picker close The previous assertion checked pickerModal (filtered by step-1 h2) which was already absent once the flow moved to step 2, causing it to pass immediately without confirming the picker actually closed. This left a timing gap where the test's next assertion (getCreatingNewBadge) ran before React had batched and applied the setLines + closePicker state updates. Changed to pickerStep2Modal().not.toBeVisible() which polls until the step-2 modal disappears — this happens in the same React render cycle that also sets inlineCreatedBudgetLineDraft on the line card, ensuring the badge is visible by the time the assertion resolves. Fixes scenarios 16-18 in paperless-first-invoice.spec.ts (shard 5). Co-Authored-By: Claude e2e-test-engineer (Sonnet) <noreply@anthropic.com> * fix(e2e): fix deterministic E2E test failures in shards 5 and 12 **Shard 12: budget-cost-basis.spec.ts (mobile)** Replace non-retrying getAttribute('class')+toMatch() assertions with retrying toHaveClass(/Active/i). On mobile WebKit, React's state update after selectOption() may not have committed when getAttribute is called. toHaveClass retries until the DOM reflects the new class. **Shard 5: paperless-first-invoice.spec.ts Scenario 18** Fix the invalid-amount guard setup. materializeInlineDrafts reads financial state from the LIVE LINE (not the inline draft form), so clearing the inline form's unitPrice field had no effect — the save proceeded and returned a Paperless config error instead of inlineDraftInvalid. Fix: manipulate the LIVE LINE's inputs BEFORE queuing the draft (while the cardMetricGrid is still visible): - Clear unitPrice → disables unit-pricing mode (hasUnitPricing=false) - Set totalAmount = -1 → netBase = -1 < 0 → inlineDraftInvalid fires 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) <noreply@anthropic.com>
Contributor
|
🎉 This PR is included in version 2.12.0-beta.3 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
3 tasks
…witch step (#1793) In diary-r2-uat.spec.ts Scenarios 2 and 10 and diary-automatic-events.spec.ts Scenario 2, the "switch to All first" step used waitForResponse() to wait for the All-click's API response. This can race: the DiaryPage's useEffect fires asynchronously (state update → re-render → aria-pressed changes → useEffect → API call → response), so in high-contention CI environments (fail-fast mode with maxFailures:1) the response can arrive after the next waitForResponse() listener is registered, causing the subsequent test step to be fulfilled by the wrong response. Replace with diaryPage.waitForLoaded(), which races between timeline.visible, emptyState.visible, and errorBanner.visible. These elements are controlled by isLoading which is set true at the start of loadEntries() and cleared in finally. Returning waitForLoaded() guarantees the All-mode response cycle is complete before the next waitForResponse() listener is registered. Co-authored-by: Frank Steiler <frank@steilerdev.de> Co-authored-by: Claude e2e-test-engineer (Sonnet) <noreply@anthropic.com>
Contributor
|
🎉 This PR is included in version 2.12.0-beta.4 🎉 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
This release ships a Cost Basis payment-status filter for the Cost Breakdown table, a diary UX fix, an auto-itemize workflow harmonization, and the corresponding E2E test corrections.
Changes
Features
Fixes
Refactoring
Change Inventory
Backend (
server/,shared/)shared/src/types/budgetBreakdown.ts— newactualCostPaid,actualCostPendingfields on all breakdown entitiesserver/src/services/budgetBreakdownService.ts— deposit-aware paid/pending rollup viacomputeDepositAwareAggregatesserver/src/services/budgetBreakdownService.paid.test.ts— 11 new integration testsFrontend (
client/)client/src/components/CostBreakdownTable/CostBreakdownTable.tsx—CostBasisSelectcomponent,resolveBudgetLineCost/resolveAggregateCosthelpersclient/src/components/CostBreakdownTable/CostBreakdownTable.module.css— new.controlBar,.costBasisField,.costBasisSelectstylesclient/src/pages/BudgetOverviewPage/BudgetOverviewPage.tsx— URL state via?paymentStatus=paid|outstandingclient/src/i18n/en/budget.json,client/src/i18n/de/budget.json— Cost Basis i18n keysclient/src/pages/DiaryPage/DiaryPage.tsx— default filter set tomanualE2E Tests (
e2e/)e2e/tests/budget/budget-cost-basis.spec.ts— 16 new E2E scenarios for Cost Basis filtere2e/pages/BudgetOverviewPage.ts— newcostBasisSelectandcostBasisLabellocatorse2e/tests/diary/diary-r2-uat.spec.ts— 3 scenarios updated for manual-default filterManual Validation Checklist
Cost Basis Filter (#1786)
?paymentStatus=paid.?paymentStatus=outstanding.?paymentStatus=paidin the URL — "Paid" is pre-selected.Diary Default Filter (#1782)
Auto-itemize Harmonization (#1780)
Testing
docker pull steilerDev/cornerstone:betadocker pull steilerDev/cornerstone:pr-1791Supersedes #1789
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com