Coverage for domain/conditions.py: 100%

13 statements  

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

1"""Underwriting condition status state machine. 

2 

3Pure, mirroring ``domain/lifecycle.py``'s pattern for ``LoanStage`` — a separate small 

4graph because conditions and loan stages have different states, different terminal 

5states, and no shared behavior beyond "it's a graph walk." 

6""" 

7 

8from __future__ import annotations 

9 

10from .enums import ConditionStatus 

11from .result import AppError, Result, err, ok 

12 

13CONDITION_TRANSITIONS: dict[ConditionStatus, tuple[ConditionStatus, ...]] = { 

14 ConditionStatus.ADDED: (ConditionStatus.READY_TO_SEND,), 

15 ConditionStatus.READY_TO_SEND: (ConditionStatus.SENT_TO_LENDER,), 

16 ConditionStatus.SENT_TO_LENDER: ( 

17 ConditionStatus.CLEARED, 

18 ConditionStatus.WAIVED, 

19 ConditionStatus.RE_REQUESTED, 

20 ), 

21 ConditionStatus.RE_REQUESTED: (ConditionStatus.READY_TO_SEND,), 

22 ConditionStatus.CLEARED: (), 

23 ConditionStatus.WAIVED: (), 

24} 

25 

26# Terminal statuses, plus everything else: a condition is "open" (not yet resolved) 

27# unless it's Cleared or Waived. 

28OPEN_CONDITION_STATUSES: frozenset[ConditionStatus] = frozenset( 

29 s for s in ConditionStatus if s not in (ConditionStatus.CLEARED, ConditionStatus.WAIVED) 

30) 

31 

32 

33def can_transition_condition(frm: ConditionStatus, to: ConditionStatus) -> bool: 

34 return to in CONDITION_TRANSITIONS[frm] 

35 

36 

37def assert_condition_transition(frm: ConditionStatus, to: ConditionStatus) -> Result: 

38 if frm == to: 

39 return ok(to) 

40 if not can_transition_condition(frm, to): 

41 return err( 

42 AppError( 

43 code="INVALID_TRANSITION", 

44 message=f"Condition cannot move from {frm} to {to}.", 

45 ) 

46 ) 

47 return ok(to)