Coverage for domain/enums.py: 99%
262 statements
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-04 14:06 +0000
« prev ^ index » next coverage.py v7.17.0, created at 2026-11-04 14:06 +0000
1"""Enum dictionary (MISMO/ULAD-aligned), ported from ``arive-spec/src/domain/enums.ts``.
3The single source of truth for the domain's enumerated value sets. Django model
4choices and API schemas derive from here so persistence and the API cannot drift.
5"""
7from __future__ import annotations
9from enum import StrEnum
12class LoanStage(StrEnum):
13 APPLICATION_INTAKE = "APPLICATION_INTAKE"
14 PREAPPROVED = "PREAPPROVED"
15 QUALIFICATION = "QUALIFICATION"
16 LOAN_SETUP = "LOAN_SETUP"
17 DISCLOSURE_SENT = "DISCLOSURE_SENT"
18 UNDERWRITING_SUBMITTED = "UNDERWRITING_SUBMITTED"
19 APPROVED_WITH_CONDITION = "APPROVED_WITH_CONDITION"
20 RE_SUBMITTAL = "RE_SUBMITTAL"
21 CLEAR_TO_CLOSE = "CLEAR_TO_CLOSE"
22 DOCS_OUT = "DOCS_OUT"
23 DOCS_SIGNED = "DOCS_SIGNED"
24 LOAN_FUNDED = "LOAN_FUNDED"
25 COMMISSION_PAID = "COMMISSION_PAID"
26 BROKER_CHECK_RECEIVED = "BROKER_CHECK_RECEIVED"
27 ADVERSE = "ADVERSE"
28 SUSPENDED = "SUSPENDED"
31class LoanPurpose(StrEnum):
32 PURCHASE = "Purchase"
33 REFINANCE = "Refinance"
36class OccupancyType(StrEnum):
37 PRIMARY_RESIDENCE = "PrimaryResidence"
38 SECOND_HOME = "SecondHome"
39 INVESTMENT = "Investment"
42class MortgageType(StrEnum):
43 CONVENTIONAL = "Conventional"
44 VA = "VA"
45 FHA = "FHA"
46 USDA_RURAL_DEVELOPMENT = "USDARuralDevelopment"
47 NON_QM = "NonQM"
48 HELOC = "HELOC"
49 HELOAN = "HELOAN"
50 REVERSE = "REVERSE"
53class AmortizationType(StrEnum):
54 FIXED = "Fixed"
55 ADJUSTABLE_RATE = "AdjustableRate"
58class RefinanceType(StrEnum):
59 NO_CASH_OUT = "NoCashOut"
60 LIMITED_CASH_OUT = "LimitedCashOut"
61 CASH_OUT = "CashOut"
64class FeeSection(StrEnum):
65 A = "A"
66 B = "B"
67 C = "C"
68 E = "E"
69 F = "F"
70 G = "G"
71 H = "H"
74class ToleranceBucket(StrEnum):
75 ZERO = "ZERO"
76 TEN_PERCENT = "TEN_PERCENT"
77 UNLIMITED = "UNLIMITED"
80class QmType(StrEnum):
81 SAFE_HARBOR = "SafeHarborQM"
82 REBUTTABLE_PRESUMPTION = "RebuttablePresumptionQM"
83 GENERAL = "GeneralQM"
84 NON_QM = "NonQM"
87class PaidByType(StrEnum):
88 BORROWER = "Borrower"
89 SELLER = "Seller"
90 LENDER = "Lender"
91 BROKER = "Broker"
94class PaidToType(StrEnum):
95 LENDER = "Lender"
96 BROKER = "Broker"
97 THIRD_PARTY = "ThirdParty"
98 GOVERNMENT = "Government"
101class CompType(StrEnum):
102 BORROWER_PAID = "BorrowerPaid"
103 LENDER_PAID = "LenderPaid"
106class PricingChannel(StrEnum):
107 WHOLESALE = "Wholesale"
108 CORRESPONDENT = "Correspondent"
109 RETAIL = "Retail"
110 BROKER = "Broker"
113class RateType(StrEnum):
114 FIXED = "Fixed"
115 ARM = "ARM"
118class PricingGoal(StrEnum):
119 LOWEST_PAYMENT = "lowestPayment"
120 LEAST_CASH_TO_CLOSE = "leastCashToClose"
121 FASTEST_CLOSE = "fastestClose"
122 BEST_LONG_TERM = "bestLongTerm"
123 MAX_CASH_OUT = "maxCashOut"
124 CREDIT_SENSITIVE = "creditSensitive"
127class TrackerStatus(StrEnum):
128 NOT_STARTED = "NotStarted"
129 IN_PROGRESS = "InProgress"
130 COMPLETED = "Completed"
131 NA = "NA"
132 OVERDUE = "Overdue"
135KEY_DATE_KEYS: tuple[str, ...] = (
136 "application_date",
137 "le_disclosed_date",
138 "le_signed_date",
139 "intent_to_proceed_date",
140 "appraisal_ordered_date",
141 "appraisal_received_date",
142 "lock_date",
143 "lock_expiration_date",
144 "submitted_to_uw_date",
145 "approved_date",
146 "clear_to_close_date",
147 "cd_disclosed_date",
148 "cd_signed_date",
149 "closing_date",
150 "funding_date",
151 "first_payment_date",
152)
155# Labels (match the human display names used across the app).
156STAGE_LABELS: dict[LoanStage, str] = {
157 LoanStage.APPLICATION_INTAKE: "Application Intake",
158 LoanStage.PREAPPROVED: "Pre-Approved",
159 LoanStage.QUALIFICATION: "Qualification",
160 LoanStage.LOAN_SETUP: "Loan Setup",
161 LoanStage.DISCLOSURE_SENT: "Disclosure Sent",
162 LoanStage.UNDERWRITING_SUBMITTED: "Submitted to Underwriting",
163 LoanStage.APPROVED_WITH_CONDITION: "Approved with Conditions",
164 LoanStage.RE_SUBMITTAL: "Re-Submittal",
165 LoanStage.CLEAR_TO_CLOSE: "Clear to Close",
166 LoanStage.DOCS_OUT: "Docs Out",
167 LoanStage.DOCS_SIGNED: "Docs Signed",
168 LoanStage.LOAN_FUNDED: "Loan Funded",
169 LoanStage.COMMISSION_PAID: "Commission Paid",
170 LoanStage.BROKER_CHECK_RECEIVED: "Broker Check Received",
171 LoanStage.ADVERSE: "Adverse",
172 LoanStage.SUSPENDED: "Suspended",
173}
174OCCUPANCY_LABELS: dict[OccupancyType, str] = {
175 OccupancyType.PRIMARY_RESIDENCE: "Primary Residence",
176 OccupancyType.SECOND_HOME: "Second Home",
177 OccupancyType.INVESTMENT: "Investment",
178}
181def stage_choices() -> list[tuple[str, str]]:
182 return [(s.value, STAGE_LABELS[s]) for s in LoanStage]
185def purpose_choices() -> list[tuple[str, str]]:
186 return [(p.value, p.value) for p in LoanPurpose]
189def occupancy_choices() -> list[tuple[str, str]]:
190 return [(o.value, OCCUPANCY_LABELS[o]) for o in OccupancyType]
193def fee_section_choices() -> list[tuple[str, str]]:
194 return [(s.value, s.value) for s in FeeSection]
197def tolerance_bucket_choices() -> list[tuple[str, str]]:
198 return [(b.value, b.value) for b in ToleranceBucket]
201def paid_by_choices() -> list[tuple[str, str]]:
202 return [(p.value, p.value) for p in PaidByType]
205def paid_to_choices() -> list[tuple[str, str]]:
206 return [(p.value, p.value) for p in PaidToType]
209def comp_type_choices() -> list[tuple[str, str]]:
210 return [(c.value, c.value) for c in CompType]
213def pricing_channel_choices() -> list[tuple[str, str]]:
214 return [(p.value, p.value) for p in PricingChannel]
217def rate_type_choices() -> list[tuple[str, str]]:
218 return [(r.value, r.value) for r in RateType]
221def pricing_goal_choices() -> list[tuple[str, str]]:
222 return [(g.value, g.value) for g in PricingGoal]
225def tracker_status_choices() -> list[tuple[str, str]]:
226 return [(s.value, s.value) for s in TrackerStatus]
229# Default TRID tolerance bucket per fee section (spec §16.1).
230SECTION_TOLERANCE: dict[FeeSection, ToleranceBucket] = {
231 FeeSection.A: ToleranceBucket.ZERO,
232 FeeSection.B: ToleranceBucket.ZERO,
233 FeeSection.C: ToleranceBucket.TEN_PERCENT,
234 FeeSection.E: ToleranceBucket.ZERO,
235 FeeSection.F: ToleranceBucket.UNLIMITED,
236 FeeSection.G: ToleranceBucket.UNLIMITED,
237 FeeSection.H: ToleranceBucket.UNLIMITED,
238}
240# Broader ULAD/MISMO value sets (ported for reference/validation; grown as needed).
241VALUE_SETS: dict[str, tuple[str, ...]] = {
242 "property_type": (
243 "SingleFamily", "Condominium", "Townhouse", "TwoToFourUnit", "PUD",
244 "ManufacturedHome", "Cooperative",
245 ),
246 "construction_type": ("Existing", "NewConstruction", "ConstructionToPerm", "Renovation"),
247 "income_type": (
248 "Base", "Overtime", "Bonus", "Commission", "SelfEmployment", "Pension",
249 "SocialSecurity", "Dividend", "RentalIncome", "Military", "Other",
250 ),
251 "liability_type": (
252 "Installment", "Revolving", "Mortgage", "HELOC", "Lease", "Open30Day",
253 "ChildSupport", "Alimony", "StudentLoan", "Other",
254 ),
255 "asset_type": (
256 "Checking", "Savings", "MoneyMarket", "CertificateOfDeposit", "Stocks",
257 "Bonds", "MutualFund", "Retirement", "GiftCash", "GiftOfEquity",
258 "ProceedsFromPropertySale", "TrustAccount", "CashOnHand", "OtherAsset",
259 ),
260 "employment_type": ("Primary", "Secondary", "Previous"),
261 "marital_status": ("Married", "Separated", "Unmarried"),
262 "citizenship_type": (
263 "USCitizen", "PermanentResidentAlien", "NonPermanentResidentAlien", "ForeignNational",
264 ),
265 "documentation_type": (
266 "FullDocumentation", "Alternative", "BankStatement", "AssetDepletion",
267 "DSCR", "NoDoc", "StreamlineRefinance",
268 ),
269 "aus_engine": ("DO_DU_FNMA", "LPA_FHLMC", "INV_AUS", "MANUAL_UW"),
270 "condition_status": (
271 "Added", "ReadyToSend", "SentToLender", "ReRequested", "Cleared", "Waived",
272 ),
273 "qm_type": ("SafeHarborQM", "RebuttablePresumptionQM", "GeneralQM", "NonQM"),
274}
277def is_member(enum_cls: type[StrEnum], code: str) -> bool:
278 return code in {m.value for m in enum_cls}
281# --- 1003 extension enums (AUS pre-flight + MISMO 3.4 export; see details/08) -----
282class CitizenResidencyType(StrEnum):
283 US_CITIZEN = "USCitizen"
284 PERMANENT_RESIDENT_ALIEN = "PermanentResidentAlien"
285 NON_PERMANENT_RESIDENT_ALIEN = "NonPermanentResidentAlien"
286 FOREIGN_NATIONAL = "ForeignNational"
289class BorrowerRoleType(StrEnum):
290 BORROWER = "Borrower"
291 CO_BORROWER = "CoBorrower"
294class BorrowerClassificationType(StrEnum):
295 PRIMARY = "Primary"
296 SECONDARY = "Secondary"
299class BankruptcyChapterType(StrEnum):
300 CHAPTER_7 = "Chapter7"
301 CHAPTER_11 = "Chapter11"
302 CHAPTER_12 = "Chapter12"
303 CHAPTER_13 = "Chapter13"
306class IntentToOccupyType(StrEnum):
307 YES = "Yes"
308 NO = "No"
311class PriorPropertyUsageType(StrEnum):
312 PRIMARY_RESIDENCE = "PrimaryResidence"
313 SECOND_HOME = "SecondHome"
314 INVESTMENT = "Investment"
317class ResidenceType(StrEnum):
318 CURRENT = "Current"
319 PRIOR = "Prior"
320 MAILING = "Mailing"
323class ResidenceBasisType(StrEnum):
324 OWN = "Own"
325 RENT = "Rent"
326 LIVING_RENT_FREE = "LivingRentFree"
329class HmdaEthnicityType(StrEnum):
330 HISPANIC_OR_LATINO = "HispanicOrLatino"
331 NOT_HISPANIC_OR_LATINO = "NotHispanicOrLatino"
332 NOT_PROVIDED = "NotProvided"
335class HmdaRaceType(StrEnum):
336 AMERICAN_INDIAN = "AmericanIndianOrAlaskaNative"
337 ASIAN = "Asian"
338 BLACK = "BlackOrAfricanAmerican"
339 PACIFIC_ISLANDER = "NativeHawaiianOrOtherPacificIslander"
340 WHITE = "White"
341 NOT_PROVIDED = "NotProvided"
344class SexType(StrEnum):
345 MALE = "Male"
346 FEMALE = "Female"
347 BOTH = "BothMaleFemale"
348 NOT_PROVIDED = "NotProvided"
351class ApplicationTakenMethodType(StrEnum):
352 FACE_TO_FACE = "FaceToFace"
353 MAIL = "Mail"
354 TELEPHONE = "Telephone"
355 INTERNET = "Internet"
358class FundsSourceType(StrEnum):
359 CHECKING_SAVINGS = "CheckingSavings"
360 GIFT_FUNDS = "GiftFunds"
361 GRANT = "Grant"
362 RETIREMENT_FUNDS = "RetirementFunds"
363 SECONDARY_FINANCING = "SecondaryFinancing"
364 EQUITY_ON_SOLD_PROPERTY = "EquityOnSoldProperty"
365 BRIDGE_LOAN = "BridgeLoan"
366 CASH_ON_HAND = "CashOnHand"
367 STOCKS_AND_BONDS = "StocksAndBonds"
368 OTHER = "Other"
371class GiftSourceType(StrEnum):
372 RELATIVE = "Relative"
373 EMPLOYER = "Employer"
374 GOVERNMENT = "Government"
375 NONPROFIT = "Nonprofit"
376 OTHER = "Other"
379class LoanIdentifierType(StrEnum):
380 ULI = "UniversalLoanIdentifier"
381 MERS_MIN = "MERS_MIN"
382 FHA_CASE = "FHACaseNumber"
383 VA_CASE = "VACaseNumber"
384 AGENCY_CASE = "AgencyCaseNumber"
385 LENDER_LOAN = "LenderLoanIdentifier"
388class PropertyType(StrEnum):
389 SINGLE_FAMILY = "SingleFamily"
390 CONDOMINIUM = "Condominium"
391 COOPERATIVE = "Cooperative"
392 MANUFACTURED_HOME = "ManufacturedHome"
393 PUD = "PUD"
394 TOWNHOUSE = "Townhouse"
395 TWO_TO_FOUR_UNIT = "TwoToFourUnit"
396 VACANT_LOT = "VacantLot"
399class AttachmentType(StrEnum):
400 DETACHED = "Detached"
401 ATTACHED = "Attached"
404class ConstructionMethodType(StrEnum):
405 SITE_BUILT = "SiteBuilt"
406 MANUFACTURED = "Manufactured"
409class ConstructionStatusType(StrEnum):
410 EXISTING = "Existing"
411 PROPOSED = "Proposed"
412 UNDER_CONSTRUCTION = "UnderConstruction"
415class ProjectLegalStructureType(StrEnum):
416 CONDOMINIUM = "Condominium"
417 COOPERATIVE = "Cooperative"
418 PUD = "PlannedUnitDevelopment"
421class EstateType(StrEnum):
422 FEE_SIMPLE = "FeeSimple"
423 LEASEHOLD = "Leasehold"
426class LienPriorityType(StrEnum):
427 FIRST_LIEN = "FirstLien"
428 SECOND_LIEN = "SecondLien"
431class RefinanceCashOutType(StrEnum):
432 NO_CASH_OUT = "NoCashOut"
433 LIMITED_CASH_OUT = "LimitedCashOut"
434 CASH_OUT = "CashOut"
437class ConditionStatus(StrEnum):
438 ADDED = "Added"
439 READY_TO_SEND = "ReadyToSend"
440 SENT_TO_LENDER = "SentToLender"
441 RE_REQUESTED = "ReRequested"
442 CLEARED = "Cleared"
443 WAIVED = "Waived"
446class PriorToCategory(StrEnum):
447 PRIOR_TO_APPROVAL = "PriorToApproval"
448 PRIOR_TO_DOCS = "PriorToDocs"
449 PRIOR_TO_CLOSING_DOCS = "PriorToClosingDocs"
450 PRIOR_TO_FUNDING = "PriorToFunding"
453class ConditionSource(StrEnum):
454 MANUAL = "Manual"
455 LENDER = "Lender"
456 UW = "UW"
459class ClientNeedType(StrEnum):
460 DOC_REQUEST = "DocRequest"
461 SIGNATURE = "Signature"
464class ClientNeedStatus(StrEnum):
465 REQUESTED = "Requested"
466 RECEIVED = "Received"
469class MaritalStatusType(StrEnum):
470 MARRIED = "Married"
471 SEPARATED = "Separated"
472 UNMARRIED = "Unmarried"
475def enum_choices(enum_cls: type[StrEnum]) -> list[tuple[str, str]]:
476 """Django model `choices` for an enum, value as both code and label."""
477 return [(m.value, m.value) for m in enum_cls]
480STATE_NAMES: dict[str, str] = {
481 "AL": "Alabama", "AK": "Alaska", "AZ": "Arizona", "AR": "Arkansas",
482 "CA": "California", "CO": "Colorado", "CT": "Connecticut", "DE": "Delaware",
483 "DC": "District of Columbia", "FL": "Florida", "GA": "Georgia", "HI": "Hawaii",
484 "ID": "Idaho", "IL": "Illinois", "IN": "Indiana", "IA": "Iowa", "KS": "Kansas",
485 "KY": "Kentucky", "LA": "Louisiana", "ME": "Maine", "MD": "Maryland",
486 "MA": "Massachusetts", "MI": "Michigan", "MN": "Minnesota", "MS": "Mississippi",
487 "MO": "Missouri", "MT": "Montana", "NE": "Nebraska", "NV": "Nevada",
488 "NH": "New Hampshire", "NJ": "New Jersey", "NM": "New Mexico", "NY": "New York",
489 "NC": "North Carolina", "ND": "North Dakota", "OH": "Ohio", "OK": "Oklahoma",
490 "OR": "Oregon", "PA": "Pennsylvania", "RI": "Rhode Island",
491 "SC": "South Carolina", "SD": "South Dakota", "TN": "Tennessee", "TX": "Texas",
492 "UT": "Utah", "VT": "Vermont", "VA": "Virginia", "WA": "Washington",
493 "WV": "West Virginia", "WI": "Wisconsin", "WY": "Wyoming",
494}