Coverage for apps/core_los/contacts_api.py: 94%

27 statements  

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

1"""Contacts router: business-contact directory + borrower directory (mounted at "").""" 

2 

3from ninja import Router 

4 

5from apps.accounts.auth import requires, staff_loan_queryset 

6from apps.application_1003.models import Borrower 

7from apps.core_los.models import BusinessContact 

8from apps.core_los.schemas import ( 

9 BusinessContactCreate, 

10 BusinessContactOut, 

11 DirectoryBorrowerOut, 

12) 

13from domain.result import AppError, DomainError 

14 

15router = Router(tags=["contacts"]) 

16 

17 

18@router.get( 

19 "/business-contacts", response=list[BusinessContactOut], auth=requires("CONTACT_VIEW") 

20) 

21def list_business_contacts(request): 

22 return list(BusinessContact.objects.all().order_by("name")) 

23 

24 

25@router.post( 

26 "/business-contacts", response={201: BusinessContactOut}, auth=requires("CONTACT_EDIT") 

27) 

28def create_business_contact(request, payload: BusinessContactCreate): 

29 if not payload.name.strip(): 29 ↛ 30line 29 didn't jump to line 30 because the condition on line 29 was never true

30 raise DomainError([AppError("VALIDATION", "Name is required", "name")]) 

31 contact = BusinessContact.objects.create(**payload.model_dump()) 

32 return 201, contact 

33 

34 

35@router.delete( 

36 "/business-contacts/{contact_id}", response={204: None}, auth=requires("CONTACT_EDIT") 

37) 

38def delete_business_contact(request, contact_id: int): 

39 deleted, _ = BusinessContact.objects.filter(pk=contact_id).delete() 

40 if deleted == 0: 

41 raise DomainError([AppError("NOT_FOUND", "Contact not found")]) 

42 return 204, None 

43 

44 

45@router.get( 

46 "/borrower-directory", response=list[DirectoryBorrowerOut], auth=requires("LOAN_VIEW") 

47) 

48def borrower_directory(request): 

49 qs = staff_loan_queryset(request.principal, "all") 

50 borrowers = Borrower.objects.filter(loan__in=qs, is_primary=True).select_related("loan") 

51 return [ 

52 { 

53 "name": f"{b.first_name} {b.last_name}", 

54 "email": b.email, 

55 "phone": None, # Borrower has no phone field today (design.md note). 

56 "loan_guid": str(b.loan.guid), 

57 "loan_number": b.loan.arive_loan_no, 

58 "stage": b.loan.stage, 

59 } 

60 for b in borrowers 

61 ]