Coverage for apps/client_needs/services.py: 94%
23 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"""Client-need helpers shared by the staff API and the borrower portal."""
3from __future__ import annotations
5from apps.client_needs.models import ClientNeed
6from domain.enums import ClientNeedStatus, ClientNeedType
9def generate_standard_needs(loan) -> tuple[list[ClientNeed], int]:
10 """Add the standard document requests for ``loan`` (by purpose), skipping any it already
11 has by name. Returns ``(created, skipped_count)``. Used by ``POST /loans/{guid}/
12 generate-needs`` and by borrower-portal submit (add-borrower-portal-api design.md D9)."""
13 existing = set(loan.needs.values_list("name", flat=True))
14 existing_lower = {n.lower() for n in existing}
16 primary_borrower = loan.borrowers.filter(is_primary=True).first() or loan.borrowers.first()
17 borrower_ids = [primary_borrower.id] if primary_borrower else []
19 standard = [
20 {
21 "name": "Government-Issued Photo ID",
22 "type": ClientNeedType.DOC_REQUEST.value,
23 "description": "Driver's license, passport or state ID for all borrowers.",
24 },
25 {
26 "name": "Recent 30-Day Pay Stubs",
27 "type": ClientNeedType.DOC_REQUEST.value,
28 "description": "Most recent 30 consecutive days of paystubs showing YTD earnings.",
29 },
30 {
31 "name": "Last 2 Years W-2 Statements",
32 "type": ClientNeedType.DOC_REQUEST.value,
33 "description": "Official W-2 or 1099 statements for the prior 2 tax years.",
34 },
35 {
36 "name": "Last 2 Months Bank Statements",
37 "type": ClientNeedType.DOC_REQUEST.value,
38 "description": (
39 "All pages of checking and savings accounts used for assets or reserves."
40 ),
41 },
42 ]
43 if loan.purpose == "Purchase": 43 ↛ 52line 43 didn't jump to line 52 because the condition on line 43 was always true
44 standard.append(
45 {
46 "name": "Executed Purchase Agreement",
47 "type": ClientNeedType.DOC_REQUEST.value,
48 "description": "Signed purchase and sales contract including all addenda.",
49 }
50 )
51 else:
52 standard.append(
53 {
54 "name": "Current Mortgage Statement",
55 "type": ClientNeedType.DOC_REQUEST.value,
56 "description": (
57 "Latest mortgage statement for the subject property showing balance "
58 "and escrow."
59 ),
60 }
61 )
63 created = []
64 skipped = 0
65 for item in standard:
66 if item["name"].lower() in existing_lower:
67 skipped += 1
68 continue
69 need = ClientNeed.objects.create(
70 loan=loan,
71 name=item["name"],
72 type=item["type"],
73 description=item["description"],
74 required=True,
75 status=ClientNeedStatus.REQUESTED.value,
76 )
77 if borrower_ids:
78 need.borrowers.set(borrower_ids)
79 created.append(need)
81 return created, skipped