Skip to content

refactor(finance): split finance utils into focused modules - #331

Merged
paruff merged 1 commit into
mainfrom
refactor/finance-utils-split
Jul 31, 2026
Merged

refactor(finance): split finance utils into focused modules#331
paruff merged 1 commit into
mainfrom
refactor/finance-utils-split

Conversation

@paruff

@paruff paruff commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Summary

Split the 2,347-line investor_app/finance/utils.py into focused submodules and resolve LIMIT-20 (duplicate/divergent finance implementations).

Changes

  • New modules: investor_app/finance/{mortgage,taxes,scoring,strategies}.py
    • mortgage.py — monthly mortgage, carrying costs, break-even rent, paydown, appreciation, ROI components
    • taxes.py — depreciation, tax benefits, after-tax IRR/CF, hold-period projections, sale proceeds, recapture
    • scoring.py — 1% rule, GRM, price-to-rent, market normalization helpers
    • strategies.py — flip, buy-and-hold, vacation rental, BRRRR calculators
  • utils.py now holds only core KPI primitives (noi, cap_rate, cash_on_cash, dscr, irr, build_cashflows) plus Django-coupled analysis
  • All 17 importers updated to canonical module locations (no re-export shim — nothing imports moved names from utils.py)
  • Dead code deleted (LIMIT-20):
    • calculate_noi/calculate_cap_rate/calculate_cash_on_cash/calculate_irr aliases
    • dead score_listing_v1 chain and deprecated score_listing_v1_deprecated
    • duplicate pure score_listing_v2 (production version stays in core/services/scoring.py)
    • unused service-layer calculate_noi in core/services/property_service.py (+ its test file)
  • Docs: LIMIT-20 marked resolved in docs/KNOWN_LIMITATIONS.md

Verification

  • Full suite: 1803 passed, 1 skipped
  • Ruff: clean on all touched files
  • mypy: clean on all touched files (pre-existing error in tests/acceptance/conftest.py:69 unrelated, file unmodified)
  • Pre-commit: all hooks pass

Notes

  • estimate_rehab_cost now takes a cost_per_sqft dict parameter instead of importing settings.REHAB_COST_PER_SQFT — keeps the strategies module Django-free. Callers updated.
  • A moved copy of total_return_summary initially dropped the purchase_price return key; restored to match original contract (caught by test_dict_keys_present).
  • Dual-pipeline (pydantic prei vs Django) investigation documented in the build report: Django is load-bearing; pydantic state machine removal deferred as a separate reviewed change.

Split the 2,347-line investor_app/finance/utils.py into mortgage,
taxes, scoring, and strategies submodules; utils.py now holds only
core KPI primitives and the Django-coupled analysis. Update all 17
importers to canonical locations.

Resolve LIMIT-20: delete the calculate_* aliases, the dead
score_listing_v1 chain, the duplicate pure score_listing_v2, and the
unused service-layer calculate_noi in property_service.py. Mark
LIMIT-20 resolved in docs/KNOWN_LIMITATIONS.md.

Full suite: 1803 passed. Ruff + mypy clean on touched files.
@paruff
paruff merged commit d70e70f into main Jul 31, 2026
21 checks passed
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