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.

FileDescription
models.mdUser, Employee, Customer, Manager, Influencer, Vip, and the collection/address/loyalty models
serializers.mdAPI serializers for users and roles
views.mdUser management API endpoints
services.mdUser business logic, Sanity sync, PIN authentication
signals.mdRole-sync and outbound-sync signal handlers
authentication.mdPIN login, token refresh, logout
permissions.mdRole-based and store-scoped permission classes
backends.mdToken authentication backend
analytics_service.mdCustomer and employee analytics
examples.mdUsage 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's status is not active.
  • PIN login: a 4 or 6-digit PIN scoped to a store, used by POS terminals. PINLoginSerializer validates 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.

  • Web Auth: web dashboard login, registration, and session handling
  • POS Gateway: POS terminal sessions built on PIN authentication
  • Stores: store references used in store_assignments and preferred_store
  • Transactions: employee and customer references on sale transactions
  • Integrations: Sanity sync, identity reconciliation, quarantine handling

Common Use Cases

  • Employee onboarding: a roleEmployee entry lands on user_roles_data, the signal creates the Employee row, a manager sets the PIN via the admin editorial form.
  • Customer registration: a roleCustomer entry creates the Customer row; loyalty and store credit accrue via LoyaltyLedger and StoreCredit.
  • POS PIN login: PINTokenLoginView validates employee ID, PIN, and store assignment, then issues a DRF token.
  • Store-scoped employee listing: filter UserViewSet by ?store=<id> to list employees and managers assigned to a store.
  • Wishlist / curation: customers, influencers, and staff create UserProductCollection records; the storefront surfaces featured ones.

Was this page helpful?