Coverage for apps/accounts/admin_schemas.py: 100%
271 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"""Ninja schemas for the accounts self (/me) + RBAC admin API.
3Hand-written ``Schema``s for the principal and role views (the principal is not an ORM row);
4safe-field user output never carries the password hash or the raw Cognito subject.
5"""
7from __future__ import annotations
9from typing import Any
11from ninja import ModelSchema, Schema
13from apps.accounts.models import License
16class RoleBindingOut(Schema):
17 role: str
18 org_unit_id: int | None = None
21class MeOut(Schema):
22 user_id: int
23 email: str
24 first_name: str
25 last_name: str
26 is_hq: bool
27 permissions: list[str]
28 org_unit_ids: list[int]
29 roles: list[RoleBindingOut]
32class OrgUnitOut(Schema):
33 id: int
34 name: str
35 parent_id: int | None = None
36 nmls_id: str = ""
37 is_headquarters: bool = False
40class OrgUnitCreate(Schema):
41 name: str
42 parent_id: int | None = None
43 nmls_id: str = ""
44 is_headquarters: bool = False
47class RoleOut(Schema):
48 name: str
49 permissions: list[str]
52class PermissionOut(Schema):
53 code: str
54 label: str = ""
57class PermissionRowOut(Schema):
58 permission: str
59 roles: list[str]
60 branch_override: bool
61 admin_role_check_reqd: bool
64class RoleMatrixOut(Schema):
65 roles: list[str]
66 permissions: list[PermissionRowOut]
69class RoleAssignmentOut(Schema):
70 id: int
71 role: str
72 org_unit_id: int | None = None
75class RoleAssignmentCreate(Schema):
76 role_name: str
77 org_unit_id: int | None = None
80class LicenseOut(ModelSchema):
81 class Meta:
82 model = License
83 exclude = ["user"]
86class LicenseCreate(Schema):
87 state_code: str
88 nmls_id: str = ""
89 active: bool = True
90 sponsored: bool = False
93class UserSummaryOut(Schema):
94 id: int
95 email: str
96 first_name: str
97 last_name: str
98 title: str = ""
99 phone: str = ""
100 nmls_id: str = ""
101 org_unit_id: int | None = None
102 is_active: bool
105class UserDetailOut(UserSummaryOut):
106 roles: list[RoleAssignmentOut] = []
107 licenses: list[LicenseOut] = []
110class UserActiveUpdate(Schema):
111 is_active: bool
114class RoleAssignmentReplace(Schema):
115 roles: list[RoleAssignmentCreate]
118class UserInviteInput(Schema):
119 name: str
120 email: str
121 role_name: str
122 org_unit_id: int | None = None
123 nmls_id: str = ""
126class UserInviteOut(Schema):
127 user: UserDetailOut
128 # The temporary password goes only to the new user by email (add-sendgrid-email).
129 email_sent: bool
130 # Development (DEBUG) only, for local testing without opening the email.
131 devTempPassword: str | None = None
134class CompanyOut(Schema):
135 name: str
136 nmls_id: str
137 type: str
138 dba: str | None = None
139 phone: str | None = None
140 mers_id: str | None = None
141 address_line: str | None = None
142 city: str | None = None
143 state_code: str | None = None
144 postal_code: str | None = None
145 business_hours: str | None = None
148class CompanyPatch(Schema):
149 name: str | None = None
150 nmls_id: str | None = None
151 type: str | None = None
152 dba: str | None = None
153 phone: str | None = None
154 mers_id: str | None = None
155 address_line: str | None = None
156 city: str | None = None
157 state_code: str | None = None
158 postal_code: str | None = None
159 business_hours: str | None = None
162class OrgUnitSummaryOut(Schema):
163 id: str
164 label: str
167class LoanOfficerSummaryOut(Schema):
168 id: int
169 name: str
170 nmls_id: str
171 states: list[str]
174class OrgInfoOut(Schema):
175 company: CompanyOut
176 org_units: list[OrgUnitSummaryOut]
177 user_count: int
178 loan_officers: list[LoanOfficerSummaryOut]
181class FieldConfigOut(Schema):
182 name: str
183 display_name: str
184 value_type: str
185 required: bool
186 visible: bool
187 order: int
188 category: str
189 default_value: Any | None = None
192class RequiredToProcessingOut(Schema):
193 Purchase: list[str]
194 Refinance: list[str]
197class FieldConfigsOut(Schema):
198 stages: list[str]
199 required_to_processing: RequiredToProcessingOut
200 fields: list[FieldConfigOut]
203class PosFeatures(Schema):
204 credit_auth_required: bool = True
205 allow_submit_without_credit_consent: bool = False
206 generate_needs_on_submit: bool = True
207 co_borrower_invite: bool = True
208 doc_upload: bool = True
209 e_consent: bool = True
212class PosBranding(Schema):
213 dba: str | None = None
214 use_dba_in_disclosures: bool = False
217class PosConfigOut(Schema):
218 host: str = "posapi.my1003app.com"
219 sections: list[str]
220 languages: list[str]
221 default_language: str = "English"
222 programs: list[str]
223 sections_by_program: dict[str, list[str]]
224 features: PosFeatures
225 branding: PosBranding
228class PosConfigPatch(Schema):
229 default_language: str | None = None
230 credit_auth_required: bool | None = None
231 allow_submit_without_credit_consent: bool | None = None
232 generate_needs_on_submit: bool | None = None
235class LeadFormConfigOut(Schema):
236 sources: list[str]
237 statuses: list[str]
238 tags: list[str]
239 required_fields: list[str]
242class LeadFormConfigPatch(Schema):
243 sources: list[str] | None = None
244 statuses: list[str] | None = None
245 tags: list[str] | None = None
246 required_fields: list[str] | None = None
249class AgentConfigOut(Schema):
250 agent_portal_enabled: bool = True
251 co_brand_letters: bool = False
252 show_agent_on_letter: bool = True
253 realtor_domain: str | None = None
256class AgentConfigPatch(Schema):
257 agent_portal_enabled: bool | None = None
258 co_brand_letters: bool | None = None
259 show_agent_on_letter: bool | None = None
260 realtor_domain: str | None = None
263class NonDelegatedConfigOut(Schema):
264 enabled: bool = False
265 sponsor_name: str | None = None
266 sponsor_ein: str | None = None
267 underwriting_contact: str | None = None
270class NonDelegatedConfigPatch(Schema):
271 enabled: bool | None = None
272 sponsor_name: str | None = None
273 sponsor_ein: str | None = None
274 underwriting_contact: str | None = None
277class ApiClientOut(Schema):
278 client_id: str
279 org_id: str
280 label: str | None = None
281 has_key: bool = True
284class ApiClientCreate(Schema):
285 label: str | None = "Zapier"
288class NewApiCredentialsOut(Schema):
289 client_id: str
290 client_secret: str
291 api_key: str
294class StateComplianceOut(Schema):
295 state_code: str
296 state_name: str
297 regulator: str
298 company_license: str
299 mlo_license: str
300 mlo_state_education_hours: int
301 ce_hours_annual: int
302 surety_bond_min: int
303 mcr_frequency: str
304 renewal_window: str
305 independent_processor_licensed: bool
306 w2_processor_exempt: bool
307 state_disclosures: list[str]
308 notes: str | None = None
311class ComplianceIssueOut(Schema):
312 severity: str
313 code: str
314 message: str
317class LoanOfficerComplianceOut(Schema):
318 id: int | None = None
319 name: str | None = None
320 issues: list[ComplianceIssueOut] = []
323class ProcessorComplianceOut(Schema):
324 type: str | None = None
325 issues: list[ComplianceIssueOut] = []
328class ComplianceCheckOut(Schema):
329 state_code: str
330 state_name: str
331 compliant: bool
332 loan_officer: LoanOfficerComplianceOut
333 processor: ProcessorComplianceOut
334 rule: StateComplianceOut
337class ProcessorInput(Schema):
338 type: str
339 licensed_states: list[str] = []
342class ComplianceCheckInput(Schema):
343 state_code: str
344 loan_officer_id: int | None = None
345 processor: ProcessorInput | None = None
348class BillingOut(Schema):
349 provider: str
350 plan: str
351 status: str
352 seats: int
353 price_per_seat: int
354 mrr: int
355 next_invoice_date: str
356 features: list[str]
359class ProvisionPlanOut(Schema):
360 company: str
361 nmls_id: str
362 branches: int
363 staff: int
364 loan_officers: int
365 licenses: int
366 licensed_states: list[str]
367 products: int
368 rules: int
369 templates: int
370 api_client: bool
371 warnings: list[str]
374class ProvisionPreviewInput(Schema):
375 config: dict[str, Any]
378class ProvisionApplyInput(Schema):
379 config: dict[str, Any]
380 clear_loans: bool = False
383class ProvisionApplyOut(Schema):
384 plan: ProvisionPlanOut