Users - User Management & Roles
Domain: Users
Location: api/nextango/apps/users/
Last Updated: 2026-07-08
Overview
The Users app is the platform's identity and role system. A single User record holds Sanity-synced identity data plus user_roles_data, an array of role objects that is the source of truth for what a person is allowed to do. Django-side role models (Employee, Customer, Manager, Influencer, Vip) are rebuilt from that array on every save, giving the domain both Sanity's flexible JSON structure and Django's relational query power. On top of identity and roles, the domain covers email/password and PIN-based authentication, store-scoped permissions, wishlists and curated collections, saved checkout addresses, and a loyalty points ledger.
Documentation Quick Links
| File | Description |
|---|---|
| models.md | User, Employee, Customer, Manager, Influencer, Vip, and the collection/address/loyalty models |
| serializers.md | API serializers for users and roles |
| views.md | User management API endpoints |
| services.md | User business logic, Sanity sync, PIN authentication |
| signals.md | Role-sync and outbound-sync signal handlers |
| authentication.md | PIN login, token refresh, logout |
| permissions.md | Role-based and store-scoped permission classes |
| backends.md | Token authentication backend |
| analytics_service.md | Customer and employee analytics |
| examples.md | Usage examples |
Role Model
A user can hold any combination of five roles at once: employee, customer, manager, influencer, VIP. Sanity's schema enforces at most one role record of each type per user, so a user is never "employee twice." Roles are not a Django-owned concept; they are extracted from the user_roles_data JSONField, which arrives from Sanity in the shape Sanity uses (roleEmployee, roleCustomer, and so on) and is stored verbatim.
user.user_roles_data
# [
# {"_type": "roleEmployee", "employeeId": "EMP-001", "pinCode": "1234"},
# {"_type": "roleCustomer", "loyaltyPoints": 100},
# ]
user.get_all_roles() # ['employee', 'customer']
user.get_primary_role() # 'employee' (the first role in the array)
Whenever a User is saved, sync_roles_to_model_records() reconciles the Employee/Customer/Manager/Influencer/Vip rows against the current user_roles_data: a role present in the array gets its row created or updated, and a role no longer present has its row deleted. See Models and Signals.
Authentication Methods
- Email/password: standard Django auth, enforced through
CustomTokenAuthentication(see Backends), which rejects a valid token if the owning user'sstatusis notactive. - PIN login: a 4 or 6-digit PIN scoped to a store, used by POS terminals.
PINLoginSerializervalidates the employee ID, PIN, and store assignment together (see Authentication). - Token authentication: DRF token-based, via
CustomTokenAuthentication.
Permissions
Role-based permission classes (IsEmployee, IsManager, IsCustomer, and their combinations) gate access by checking user.get_all_roles(). Store-scoped access goes through store_ref_matches() and user_can_access_store(), which tolerate both a store's Sanity ID and its Django UUID in a stored reference. A manager row with an empty store_assignments array is treated as having unrestricted store access rather than none. See Permissions.
Sanity Sync
User is the only model in this domain synced independently outbound; the five role models set _outbound_sync_enabled = False and sync only as part of the parent User document. Inbound sync runs every arriving Sanity user document through an identity-disposition check before writing it: a document with no email is quarantined unless every role on it is a non-customer role (employee, manager, influencer, VIP), and a document whose email matches an existing user under a different sanity_id is linked to that user rather than creating a duplicate. See Services for sync_user_from_sanity() and the identity-disposition outcomes, and the Integrations domain for the shared identity-reconciliation module.
Related Domains
- Web Auth: web dashboard login, registration, and session handling
- POS Gateway: POS terminal sessions built on PIN authentication
- Stores: store references used in
store_assignmentsandpreferred_store - Transactions: employee and customer references on sale transactions
- Integrations: Sanity sync, identity reconciliation, quarantine handling
Common Use Cases
- Employee onboarding: a
roleEmployeeentry lands onuser_roles_data, the signal creates the Employee row, a manager sets the PIN via the admin editorial form. - Customer registration: a
roleCustomerentry creates the Customer row; loyalty and store credit accrue via LoyaltyLedger andStoreCredit. - POS PIN login:
PINTokenLoginViewvalidates employee ID, PIN, and store assignment, then issues a DRF token. - Store-scoped employee listing: filter
UserViewSetby?store=<id>to list employees and managers assigned to a store. - Wishlist / curation: customers, influencers, and staff create
UserProductCollectionrecords; the storefront surfaces featured ones.