feat: add work order dropdown and receipt linking to expense detail edit (#235)#242
Merged
daveharmswebdev merged 5 commits intomainfrom Feb 21, 2026
Merged
Conversation
…dit view Implements Story 16.4 (Issue #235) — allows property owners to link expenses to work orders and receipts from the detail edit page. Backend: new LinkReceiptToExpense command/handler sets both FK sides of the 1:1 relationship (expense.ReceiptId + receipt.ExpenseId) and marks the receipt as processed, following the Story 15.4 pattern. Includes FluentValidation validator and POST /api/v1/expenses/{id}/link-receipt endpoint with 204/404/409 responses. Frontend: expense detail edit form now includes a work order dropdown (filtered by property, clears on property change) and an inline receipt picker with thumbnail selection for unprocessed receipts. 12 new backend unit tests, 10 new frontend component tests, 7 E2E acceptance tests (red-phase, ready for full-stack validation). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…behind toggle Work order section now displays description and status badge instead of just a link. Receipt picker in edit mode no longer auto-loads thumbnails — users click "Browse Receipts to Link" to reveal available receipts, preventing confusion with the "No receipt linked" state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ly view Info, Receipt, and Work Order now each have their own mat-card in edit mode, providing visual symmetry with the readonly detail view. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Mock PUT response directly in AC1 save test (fake workOrderId FK fails on real server) - Add browse receipts toggle step in AC3 tests (picker is behind button, not auto-shown) - Fix AC4 unlink mock from POST /unlink-receipt to DELETE /receipt (matching actual API) - Update page object with browseReceiptsButton locator and correct receipt section testid Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…se detail Inject LinkReceiptToExpenseValidator via IValidator<T> instead of manual instantiation, matching the established pattern across all controllers. Track propertyId valueChanges subscription to prevent stacking on repeated Edit/Cancel cycles. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.
Summary
Implements Story 16.4 — Expense-WorkOrder & Expense-Receipt Linking (Issue #235).
Allows property owners editing an expense at
/expenses/:idto:Changes
Backend (new)
LinkReceiptToExpense.cs— MediatR command + handler that sets both FK sides of the 1:1 receipt-expense relationship (expense.ReceiptId,receipt.ExpenseId) and marksreceipt.ProcessedAt = UtcNow. Follows the Story 15.4 UnlinkReceipt pattern in reverse. Includes property sync when receipt has no property.LinkReceiptToExpenseValidator.cs— FluentValidation:ExpenseIdandReceiptIdmust be non-empty GUIDs.ExpensesController.cs— NewPOST /api/v1/expenses/{id}/link-receiptendpoint returning 204/404/409. IncludesLinkReceiptRequestrecord.Frontend (modified)
expense-detail.component.ts— Added work order dropdown (workOrderIdform control,loadWorkOrders(),propertyIdvalueChanges listener for AC2 reset), receipt picker UI (thumbnail grid,loadUnprocessedReceipts()via ApiClient,linkReceipt()method, unlink button in edit mode). AddedSlicePipefor description truncation.expense.service.ts— AddedlinkReceipt(expenseId, receiptId)method (manual, pending NSwag regen).expense-detail.component.spec.ts— Fixed TEA red-phase test by providing mock unprocessed receipts to component signal.Tests & E2E (pre-created by TEA agent)
LinkReceiptToExpenseHandlerTests.cs— 9 unit tests: happy path (both FKs, ProcessedAt, property sync, SaveChanges) + error cases (404 expense/receipt, 409 conflict already linked/processed)LinkReceiptToExpenseValidatorTests.cs— 3 validation testsexpense-detail.component.spec.ts— 10 new component tests (5 work order dropdown AC1/AC2, 5 receipt linking AC3/AC4)expense-linking.spec.ts— 7 E2E acceptance tests (require full stack for green)expense-detail.page.ts— Page object with receipt linking locators and methodsSprint artifacts
dev-completeTest Results
dotnet test)npm test)API Design
Test plan
dotnet test --filter "LinkReceiptToExpense")npm testfrom/frontend)npm run generate-api)🤖 Generated with Claude Code