Coverage for apps/core_los/reports_api.py: 98%
78 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-04 17:24 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-04 17:24 +0000
1"""Reports & MCR: org-wide pipeline/revenue/call-report endpoints (mounted at "").
3The MCR line item (`GET /{guid}/mcr`) and its exclude toggle (`PATCH /{guid}/mcr-exclude`)
4are loan sub-resources and live on the loans router in `apps/core_los/api.py`, which
5imports `_pass_through_fees` from here.
6"""
8from __future__ import annotations
10from decimal import Decimal
12from django.db.models import Sum
13from django.utils import timezone
14from ninja import Router
16from apps.accounts.auth import requires, staff_loan_queryset
17from apps.core_los.reports_schemas import CallReportOut, PipelineReportOut, RevenueReportOut
18from apps.core_los.schemas import _safe_transaction
19from apps.pricing_ppe.models import RateLock
20from domain.enums import LoanStage, PaidToType
21from domain.reports import broker_compensation, is_active_loan, quarter_of
23router = Router(tags=["reports"])
25_PASS_THROUGH_PAID_TO = [PaidToType.THIRD_PARTY.value, PaidToType.GOVERNMENT.value]
28def _pass_through_fees(loan) -> Decimal:
29 """Third-party and government fees not Paid Outside of Closing (reports.types.ts)."""
30 total = loan.fees.filter(
31 paid_to__in=_PASS_THROUGH_PAID_TO, poc_ind=False
32 ).aggregate(Sum("total_amt"))["total_amt__sum"]
33 return total or Decimal("0.00")
36def _is_locked(loan) -> bool:
37 try:
38 lock = loan.rate_lock
39 except RateLock.DoesNotExist:
40 return False
41 return lock.status in ("LOCKED", "RELOCKED") and lock.expires_at > timezone.now()
44def _active_loans(request):
45 qs = staff_loan_queryset(request.principal, "all").select_related(
46 "transaction", "rate_lock"
47 )
48 return [loan for loan in qs if is_active_loan(loan.stage)]
51@router.get("/reports/pipeline", response=PipelineReportOut, auth=requires("LOAN_VIEW"))
52def pipeline_report(request):
53 loans = _active_loans(request)
54 by_stage: dict[str, dict] = {}
55 total_volume = Decimal("0.00")
56 for loan in loans:
57 txn = _safe_transaction(loan)
58 amt = txn.base_loan_amt if txn else Decimal("0.00")
59 bucket = by_stage.setdefault(loan.stage, {"count": 0, "volume": Decimal("0.00")})
60 bucket["count"] += 1
61 bucket["volume"] += amt
62 total_volume += amt
63 return {"total": len(loans), "total_volume": total_volume, "by_stage": by_stage}
66@router.get("/reports/revenue", response=RevenueReportOut, auth=requires("LOAN_VIEW"))
67def revenue_report(request):
68 loans = _active_loans(request)
70 def volume(subset) -> Decimal:
71 total = Decimal("0.00")
72 for loan in subset:
73 txn = _safe_transaction(loan)
74 total += txn.base_loan_amt if txn else Decimal("0.00")
75 return total
77 funded = [loan for loan in loans if loan.stage == LoanStage.LOAN_FUNDED.value]
78 locked_pipeline = [
79 loan for loan in loans if loan.stage == LoanStage.LOAN_FUNDED.value or _is_locked(loan)
80 ]
81 locked_volume = volume(locked_pipeline)
82 return {
83 "funded_count": len(funded),
84 "funded_volume": volume(funded),
85 "locked_pipeline_count": len(locked_pipeline),
86 "est_comp_bps": 200,
87 "est_comp_amt": broker_compensation(locked_volume),
88 "note": "Revenue reflects only rate-locked and funded loans; floating loans are "
89 "excluded until locked.",
90 }
93@router.get("/reports/call-report", response=CallReportOut, auth=requires("LOAN_VIEW"))
94def call_report(request, year: int | None = None, quarter: int | None = None):
95 year = year or timezone.now().year
96 loans = _active_loans(request)
98 by_state: dict[str, dict] = {}
99 applications = 0
100 excluded = 0
101 for loan in loans:
102 created = loan.created_at
103 if created.year != year: 103 ↛ 104line 103 didn't jump to line 104 because the condition on line 103 was never true
104 continue
105 if quarter and quarter_of(created) != quarter:
106 continue
107 if loan.exclude_from_mcr:
108 excluded += 1
109 continue
111 bucket = by_state.setdefault(
112 loan.state_code,
113 {
114 "applications": 0,
115 "closed": 0,
116 "volume": Decimal("0.00"),
117 "broker_comp": Decimal("0.00"),
118 "pass_through": Decimal("0.00"),
119 },
120 )
121 bucket["applications"] += 1
122 applications += 1
123 txn = _safe_transaction(loan)
124 amt = txn.base_loan_amt if txn else Decimal("0.00")
125 if loan.stage == LoanStage.LOAN_FUNDED.value:
126 bucket["closed"] += 1
127 bucket["volume"] += amt
128 bucket["broker_comp"] += broker_compensation(amt)
129 bucket["pass_through"] += _pass_through_fees(loan)
131 return {
132 "period": {"year": year, "quarter": f"Q{quarter}" if quarter else "FY"},
133 "by_state": by_state,
134 "totals": {"applications": applications, "excluded": excluded},
135 }