Skip to content

refactor(invoices): extract payload processing to InvoiceProcessor service#359

Merged
nikhilb2 merged 1 commit into
mainfrom
351-extract-invoice-payload-processing-to-service-module
May 6, 2026
Merged

refactor(invoices): extract payload processing to InvoiceProcessor service#359
nikhilb2 merged 1 commit into
mainfrom
351-extract-invoice-payload-processing-to-service-module

Conversation

@nikhilb2
Copy link
Copy Markdown
Owner

@nikhilb2 nikhilb2 commented May 6, 2026

Summary

Moved invoice payload application and inventory management logic out of the route handler into a dedicated InvoiceProcessor service class in backend/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

  • chore/refactor

How to test

cd backend
DATABASE_URL=postgresql://... python -m pytest tests/ -v

All 165 existing tests continue to pass, and 27 new unit tests cover the extracted service.

Checklist

  • My code follows the project style and conventions
  • I added/updated tests where appropriate
  • I ran relevant checks locally
  • I verified this does not break existing behavior

What changed

New file: backend/src/services/invoice_processor.py

  • InvoiceProcessor class with db: Session injected 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 extracted
  • calculate_totals(validated_items, tax_inclusive) — tax/total calculation extracted
  • apply_inventory_delta_for_update(invoice, payload, *, company_id) — net delta for edits
  • reverse_inventory(invoice) — used by cancel route
  • restore_inventory(invoice, *, company_id) — used by restore route
  • Module-level helpers: inventory_effect_for_voucher_type(), change_inventory_quantity()

Modified: backend/src/api/routes/invoices.py

  • Removed all moved logic (~275 lines)
  • Route handlers now create InvoiceProcessor(db) and delegate

New test file: backend/tests/services/test_invoice_processor.py

  • 27 focused unit tests covering every method and edge case

Updated tests (patch targets moved from invoices._generate_next_numberinvoice_processor.generate_next_number):

  • test_invoice_update_inventory.py
  • test_invoice_reference_notes.py
  • test_invoice_dues_allocations.py
  • test_invoice_tax_split.py (now uses InvoiceProcessor.apply_payload)

Related issue

Closes #351

@nikhilb2 nikhilb2 merged commit 383cb5a into main May 6, 2026
1 check passed
@nikhilb2 nikhilb2 deleted the 351-extract-invoice-payload-processing-to-service-module branch May 23, 2026 21:40
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.

Extract invoice payload processing to service module

1 participant