Skip to content

User Creation Flows

How each user role is created, what triggers it, and what side effects follow. All logic lives in novahomecareapi/users/managers.py (creation) and role-specific views.


Summary

RoleHow createdEntry point
ADMINAdmin creates another admin via portal UIPOST /users/
EMPLOYEE1) Admin manually createsPOST /users/create-employee/
2) Admin approves an applicationPATCH /applications/{id}/approve
CLIENT1) Admin manually createsPOST /users/create-client/
2) Referral pipeline completes → admin approvesReferralStep.done()PATCH /users/{id}/approve-client
MARKETERAdmin creates via portal UIPOST /users/ with role=MARKETER
MASTERDatabase / management command onlyNo API flow

ADMIN

Trigger: An existing admin (or master) posts to POST /users/ with role=ADMIN.

View: UserViewSet.create()AdministratorSerializer

What happens:

  1. User record created with role=ADMIN
  2. User is added to the requesting admin's organization
  3. NEW_ACCOUNT notification sent to the new admin (email + portal)

No profile sub-model — admins have only the base User record.


EMPLOYEE

Path 1 — Admin manually creates an employee

Trigger: Admin fills out the "Add Employee" form in the portal.

View: POST /users/create-employee/EmployeeUserSerializerUser.employee.create()

What happens:

  1. User record created with role=EMPLOYEE, manually_created=True
  2. EmployeeProfile created (manager, position, salary, SOS contact, license info)
  3. A dummy Application record is created with status=APPROVED and placeholder data — this satisfies the data model's assumption that every employee has an application
  4. FormRequest records created for all applicable onboarding forms (based on position and org)
  5. NEW_ACCOUNT notification sent

Path 2 — Application approved

Trigger: Admin reviews a job application and approves it via PATCH /applications/{id}/approve.

View: ApplicationViewSet.approve()User.employee.create_from_application()

What happens:

  1. Application fields (name, DOB, SSN, address, phone, signature, license/CPR dates) are copied from the Application to create the User
  2. User record created with role=EMPLOYEE
  3. EmployeeProfile created with the submitted manager, position, salary, external_id from the approval form
  4. Application.approve(employee) called — links the Application to the new User and sets status=APPROVED
  5. FormRequest records created for onboarding forms via FormRequest.objects.create_for_onboarding()
  6. If position has is_hcc=True: CompetencyTestRequest records created for onboarding tests
  7. Approval email sent to the employee

Key difference from Path 1: No dummy Application — the real submitted application is used.


CLIENT

Path 1 — Admin manually creates a client

Trigger: Admin fills out the "Add Client" form in the portal.

View: POST /users/create-client/ClientUserSerializerUser.clients.create()

What happens:

  1. User record created with role=CLIENT, manually_created=True
  2. ClientProfile created with status=ACTIVE (immediately active)
  3. Client added to the org
  4. client_types M2M set (HCC / HHP / both) — this drives the dynamic nav
  5. FormRequest records created for all applicable onboarding forms
  6. If manual_upload_documents=True: a manual-upload form request is also created
  7. New client notifications sent

Path 2 — Referral converts to client

This is a two-step process: the referral pipeline completes, then an admin approves the resulting client record.

Step 2a — Pipeline completion

Trigger: The final ReferralStep is marked done (ReferralStep.done()), which calls Referral.to_client().

What happens:

  1. User.clients.create_from_referral() called — creates User with role=CLIENT, manually_created=False
  2. ClientProfile created with status=NEW (not yet active — pending admin approval)
  3. User added to org
  4. No onboarding forms created yet — held until admin approval
  5. Referral.status set to PD (Pending for Approval)
  6. REFERRAL_COMPLETED notification sent to all org admins
  7. Marketer metrics update scheduled (Celery task)

Step 2b — Admin approves the client

Trigger: Admin reviews the pending client and approves via PATCH /users/{id}/approve-client.

What happens:

  1. ClientProfile updated with case manager details (pre-populated from referral data)
  2. Referral.approve() called — sets referral status=CL (Closed/Converted)
  3. FormRequest records now created for onboarding forms
  4. New client notifications sent
  5. Marketer metrics updated (Celery task)

MARKETER

Trigger: Admin posts to POST /users/ with role=MARKETER.

View: UserViewSet.create()AdministratorSerializer (same serializer as ADMIN)

What happens:

  1. User record created with role=MARKETER
  2. User added to the requesting admin's organization
  3. MarketerMetric record created — tracks active_referrals, clients, days_by_referral
  4. NEW_ACCOUNT notification sent to the new marketer

No profile sub-model — marketers have only the base User record plus MarketerMetric.


MASTER

No API flow. Created directly in the database or via Django management commands:

bash
docker compose run --rm api python manage.py createsuperuser

MASTER users are platform-level — they can access all organizations. Created once at platform setup, not through the portal UI.


Common Side Effects Reference

Side effectADMINEMPLOYEECLIENTMARKETER
NEW_ACCOUNT notification
EmployeeProfile created
ClientProfile created
Onboarding FormRequest records
CompetencyTestRequest recordsHCC only
Dummy Application createdManual path only
MarketerMetric created

Key Files

FileRole
users/views.pyUserViewSet — endpoints for all role creation
users/managers.pyUserManager, EmployeeManager, ClientManager — creation logic
users/serializers/Per-role serializers (AdministratorSerializer, EmployeeUserSerializer, ClientUserSerializer)
applications/views.pyApplicationViewSet.approve() — employee creation from application
referrals/models.pyReferralStep.done(), Referral.to_client() — client creation from referral
referrals/tasks.pyCelery task: update_marketer_metrics
job_forms/managers.pyFormRequest.objects.create_for_onboarding() — assigns onboarding forms

Nova Home Care — Internal Developer Docs