Coverage for apps/client_needs/schemas.py: 100%

61 statements  

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

1"""Ninja schemas for document references and client needs.""" 

2 

3from ninja import ModelSchema, Schema 

4from pydantic import Field, field_validator 

5 

6from apps.client_needs.models import ClientNeed, Document, DocumentClassification 

7from domain.enums import ClientNeedType 

8 

9 

10class DocumentOut(ModelSchema): 

11 class Meta: 

12 model = Document 

13 exclude = ["loan"] 

14 

15 

16class DocumentCreate(Schema): 

17 classification: str 

18 content_hash: str 

19 external_doc_id: str | None = None 

20 

21 

22class ClientNeedOut(ModelSchema): 

23 borrower_ids: list[int] = [] 

24 

25 class Meta: 

26 model = ClientNeed 

27 exclude = ["loan", "borrowers"] 

28 

29 @staticmethod 

30 def resolve_borrower_ids(obj: ClientNeed) -> list[int]: 

31 return list(obj.borrowers.values_list("id", flat=True)) 

32 

33 

34class ClientNeedCreate(Schema): 

35 name: str = Field(..., min_length=1, max_length=255) 

36 type: str = Field(..., min_length=1) 

37 borrower_ids: list[int] = [] 

38 required: bool = True 

39 description: str = "" 

40 due_date: str | None = None 

41 

42 @field_validator("name") 

43 @classmethod 

44 def validate_name(cls, v: str) -> str: 

45 s = v.strip() 

46 if not s: 

47 raise ValueError("Name cannot be blank.") 

48 return s 

49 

50 @field_validator("type") 

51 @classmethod 

52 def validate_type(cls, v: str) -> str: 

53 valid_types = {e.value for e in ClientNeedType} 

54 if v not in valid_types: 

55 raise ValueError( 

56 f"Invalid need type '{v}'. Allowed types: {', '.join(sorted(valid_types))}" 

57 ) 

58 return v 

59 

60 

61class ClientNeedFulfill(Schema): 

62 document_id: int 

63 

64 

65class DocumentClassificationOut(ModelSchema): 

66 class Meta: 

67 model = DocumentClassification 

68 fields = ["code", "name", "category", "system", "is_active"] 

69 

70 

71class DocumentPatch(Schema): 

72 classification: str | None = None 

73 external_doc_id: str | None = None 

74 

75 

76class GeneratedNeedsOut(Schema): 

77 created: list[ClientNeedOut] 

78 skipped: int 

79 

80 

81class DocumentClassificationCreate(Schema): 

82 code: str 

83 name: str 

84 category: str 

85 

86 

87class DocumentClassificationPatch(Schema): 

88 name: str | None = None 

89 category: str | None = None 

90 is_active: bool | None = None