Coverage for domain/enums.py: 99%

262 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-11-04 17:24 +0000

1"""Enum dictionary (MISMO/ULAD-aligned), ported from ``arive-spec/src/domain/enums.ts``. 

2 

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""" 

6 

7from __future__ import annotations 

8 

9from enum import StrEnum 

10 

11 

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" 

29 

30 

31class LoanPurpose(StrEnum): 

32 PURCHASE = "Purchase" 

33 REFINANCE = "Refinance" 

34 

35 

36class OccupancyType(StrEnum): 

37 PRIMARY_RESIDENCE = "PrimaryResidence" 

38 SECOND_HOME = "SecondHome" 

39 INVESTMENT = "Investment" 

40 

41 

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" 

51 

52 

53class AmortizationType(StrEnum): 

54 FIXED = "Fixed" 

55 ADJUSTABLE_RATE = "AdjustableRate" 

56 

57 

58class RefinanceType(StrEnum): 

59 NO_CASH_OUT = "NoCashOut" 

60 LIMITED_CASH_OUT = "LimitedCashOut" 

61 CASH_OUT = "CashOut" 

62 

63 

64class FeeSection(StrEnum): 

65 A = "A" 

66 B = "B" 

67 C = "C" 

68 E = "E" 

69 F = "F" 

70 G = "G" 

71 H = "H" 

72 

73 

74class ToleranceBucket(StrEnum): 

75 ZERO = "ZERO" 

76 TEN_PERCENT = "TEN_PERCENT" 

77 UNLIMITED = "UNLIMITED" 

78 

79 

80class QmType(StrEnum): 

81 SAFE_HARBOR = "SafeHarborQM" 

82 REBUTTABLE_PRESUMPTION = "RebuttablePresumptionQM" 

83 GENERAL = "GeneralQM" 

84 NON_QM = "NonQM" 

85 

86 

87class PaidByType(StrEnum): 

88 BORROWER = "Borrower" 

89 SELLER = "Seller" 

90 LENDER = "Lender" 

91 BROKER = "Broker" 

92 

93 

94class PaidToType(StrEnum): 

95 LENDER = "Lender" 

96 BROKER = "Broker" 

97 THIRD_PARTY = "ThirdParty" 

98 GOVERNMENT = "Government" 

99 

100 

101class CompType(StrEnum): 

102 BORROWER_PAID = "BorrowerPaid" 

103 LENDER_PAID = "LenderPaid" 

104 

105 

106class PricingChannel(StrEnum): 

107 WHOLESALE = "Wholesale" 

108 CORRESPONDENT = "Correspondent" 

109 RETAIL = "Retail" 

110 BROKER = "Broker" 

111 

112 

113class RateType(StrEnum): 

114 FIXED = "Fixed" 

115 ARM = "ARM" 

116 

117 

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" 

125 

126 

127class TrackerStatus(StrEnum): 

128 NOT_STARTED = "NotStarted" 

129 IN_PROGRESS = "InProgress" 

130 COMPLETED = "Completed" 

131 NA = "NA" 

132 OVERDUE = "Overdue" 

133 

134 

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) 

153 

154 

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} 

179 

180 

181def stage_choices() -> list[tuple[str, str]]: 

182 return [(s.value, STAGE_LABELS[s]) for s in LoanStage] 

183 

184 

185def purpose_choices() -> list[tuple[str, str]]: 

186 return [(p.value, p.value) for p in LoanPurpose] 

187 

188 

189def occupancy_choices() -> list[tuple[str, str]]: 

190 return [(o.value, OCCUPANCY_LABELS[o]) for o in OccupancyType] 

191 

192 

193def fee_section_choices() -> list[tuple[str, str]]: 

194 return [(s.value, s.value) for s in FeeSection] 

195 

196 

197def tolerance_bucket_choices() -> list[tuple[str, str]]: 

198 return [(b.value, b.value) for b in ToleranceBucket] 

199 

200 

201def paid_by_choices() -> list[tuple[str, str]]: 

202 return [(p.value, p.value) for p in PaidByType] 

203 

204 

205def paid_to_choices() -> list[tuple[str, str]]: 

206 return [(p.value, p.value) for p in PaidToType] 

207 

208 

209def comp_type_choices() -> list[tuple[str, str]]: 

210 return [(c.value, c.value) for c in CompType] 

211 

212 

213def pricing_channel_choices() -> list[tuple[str, str]]: 

214 return [(p.value, p.value) for p in PricingChannel] 

215 

216 

217def rate_type_choices() -> list[tuple[str, str]]: 

218 return [(r.value, r.value) for r in RateType] 

219 

220 

221def pricing_goal_choices() -> list[tuple[str, str]]: 

222 return [(g.value, g.value) for g in PricingGoal] 

223 

224 

225def tracker_status_choices() -> list[tuple[str, str]]: 

226 return [(s.value, s.value) for s in TrackerStatus] 

227 

228 

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} 

239 

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} 

275 

276 

277def is_member(enum_cls: type[StrEnum], code: str) -> bool: 

278 return code in {m.value for m in enum_cls} 

279 

280 

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" 

287 

288 

289class BorrowerRoleType(StrEnum): 

290 BORROWER = "Borrower" 

291 CO_BORROWER = "CoBorrower" 

292 

293 

294class BorrowerClassificationType(StrEnum): 

295 PRIMARY = "Primary" 

296 SECONDARY = "Secondary" 

297 

298 

299class BankruptcyChapterType(StrEnum): 

300 CHAPTER_7 = "Chapter7" 

301 CHAPTER_11 = "Chapter11" 

302 CHAPTER_12 = "Chapter12" 

303 CHAPTER_13 = "Chapter13" 

304 

305 

306class IntentToOccupyType(StrEnum): 

307 YES = "Yes" 

308 NO = "No" 

309 

310 

311class PriorPropertyUsageType(StrEnum): 

312 PRIMARY_RESIDENCE = "PrimaryResidence" 

313 SECOND_HOME = "SecondHome" 

314 INVESTMENT = "Investment" 

315 

316 

317class ResidenceType(StrEnum): 

318 CURRENT = "Current" 

319 PRIOR = "Prior" 

320 MAILING = "Mailing" 

321 

322 

323class ResidenceBasisType(StrEnum): 

324 OWN = "Own" 

325 RENT = "Rent" 

326 LIVING_RENT_FREE = "LivingRentFree" 

327 

328 

329class HmdaEthnicityType(StrEnum): 

330 HISPANIC_OR_LATINO = "HispanicOrLatino" 

331 NOT_HISPANIC_OR_LATINO = "NotHispanicOrLatino" 

332 NOT_PROVIDED = "NotProvided" 

333 

334 

335class HmdaRaceType(StrEnum): 

336 AMERICAN_INDIAN = "AmericanIndianOrAlaskaNative" 

337 ASIAN = "Asian" 

338 BLACK = "BlackOrAfricanAmerican" 

339 PACIFIC_ISLANDER = "NativeHawaiianOrOtherPacificIslander" 

340 WHITE = "White" 

341 NOT_PROVIDED = "NotProvided" 

342 

343 

344class SexType(StrEnum): 

345 MALE = "Male" 

346 FEMALE = "Female" 

347 BOTH = "BothMaleFemale" 

348 NOT_PROVIDED = "NotProvided" 

349 

350 

351class ApplicationTakenMethodType(StrEnum): 

352 FACE_TO_FACE = "FaceToFace" 

353 MAIL = "Mail" 

354 TELEPHONE = "Telephone" 

355 INTERNET = "Internet" 

356 

357 

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" 

369 

370 

371class GiftSourceType(StrEnum): 

372 RELATIVE = "Relative" 

373 EMPLOYER = "Employer" 

374 GOVERNMENT = "Government" 

375 NONPROFIT = "Nonprofit" 

376 OTHER = "Other" 

377 

378 

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" 

386 

387 

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" 

397 

398 

399class AttachmentType(StrEnum): 

400 DETACHED = "Detached" 

401 ATTACHED = "Attached" 

402 

403 

404class ConstructionMethodType(StrEnum): 

405 SITE_BUILT = "SiteBuilt" 

406 MANUFACTURED = "Manufactured" 

407 

408 

409class ConstructionStatusType(StrEnum): 

410 EXISTING = "Existing" 

411 PROPOSED = "Proposed" 

412 UNDER_CONSTRUCTION = "UnderConstruction" 

413 

414 

415class ProjectLegalStructureType(StrEnum): 

416 CONDOMINIUM = "Condominium" 

417 COOPERATIVE = "Cooperative" 

418 PUD = "PlannedUnitDevelopment" 

419 

420 

421class EstateType(StrEnum): 

422 FEE_SIMPLE = "FeeSimple" 

423 LEASEHOLD = "Leasehold" 

424 

425 

426class LienPriorityType(StrEnum): 

427 FIRST_LIEN = "FirstLien" 

428 SECOND_LIEN = "SecondLien" 

429 

430 

431class RefinanceCashOutType(StrEnum): 

432 NO_CASH_OUT = "NoCashOut" 

433 LIMITED_CASH_OUT = "LimitedCashOut" 

434 CASH_OUT = "CashOut" 

435 

436 

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" 

444 

445 

446class PriorToCategory(StrEnum): 

447 PRIOR_TO_APPROVAL = "PriorToApproval" 

448 PRIOR_TO_DOCS = "PriorToDocs" 

449 PRIOR_TO_CLOSING_DOCS = "PriorToClosingDocs" 

450 PRIOR_TO_FUNDING = "PriorToFunding" 

451 

452 

453class ConditionSource(StrEnum): 

454 MANUAL = "Manual" 

455 LENDER = "Lender" 

456 UW = "UW" 

457 

458 

459class ClientNeedType(StrEnum): 

460 DOC_REQUEST = "DocRequest" 

461 SIGNATURE = "Signature" 

462 

463 

464class ClientNeedStatus(StrEnum): 

465 REQUESTED = "Requested" 

466 RECEIVED = "Received" 

467 

468 

469class MaritalStatusType(StrEnum): 

470 MARRIED = "Married" 

471 SEPARATED = "Separated" 

472 UNMARRIED = "Unmarried" 

473 

474 

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] 

478 

479 

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}