refactor(invoices): extract payload processing to InvoiceProcessor service#359
Merged
nikhilb2 merged 1 commit intoMay 6, 2026
Merged
Conversation
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
Moved invoice payload application and inventory management logic out of the route handler into a dedicated
InvoiceProcessorservice class inbackend/src/services/invoice_processor.py.The previous
_apply_payload_to_invoice()function was ~275 lines of deeply nested logic with multiple responsibilities — ledger lookup, item validation, inventory checks, tax calculation, and ORM persistence — all living in the route file, making it untestable in isolation.Type of change
How to test
cd backend DATABASE_URL=postgresql://... python -m pytest tests/ -vAll 165 existing tests continue to pass, and 27 new unit tests cover the extracted service.
Checklist
What changed
New file:
backend/src/services/invoice_processor.pyInvoiceProcessorclass withdb: Sessioninjected in__init__apply_payload(invoice, payload, active_company, ...)— main entry point (replaces_apply_payload_to_invoice)validate_items(items, company_id, voucher_type, ...)— item validation extractedcalculate_totals(validated_items, tax_inclusive)— tax/total calculation extractedapply_inventory_delta_for_update(invoice, payload, *, company_id)— net delta for editsreverse_inventory(invoice)— used by cancel routerestore_inventory(invoice, *, company_id)— used by restore routeinventory_effect_for_voucher_type(),change_inventory_quantity()Modified:
backend/src/api/routes/invoices.pyInvoiceProcessor(db)and delegateNew test file:
backend/tests/services/test_invoice_processor.pyUpdated tests (patch targets moved from
invoices._generate_next_number→invoice_processor.generate_next_number):test_invoice_update_inventory.pytest_invoice_reference_notes.pytest_invoice_dues_allocations.pytest_invoice_tax_split.py(now usesInvoiceProcessor.apply_payload)Related issue
Closes #351