Compliance - Utilities
Location: api/nextango/apps/compliance/services/credential_utils.py, api/nextango/apps/compliance/services/permit_utils.py
Last Updated: 2026-07-06
Overview
Read helpers for the two JSONField slots the compliance domain cares about: User.credentials and StoreComplianceProfile.permits. All compliance services that need to inspect a person's credentials or a store's permits import from these modules; no service re-implements the lookup logic.
Both modules share an active-entry definition: status must be 'active', and expiryDate, when present, must be today or later. ISO-8601 dates (YYYY-MM-DD) compare correctly as strings, so the credential helper uses lexical comparison; the permit helper parses via date.fromisoformat() to ignore malformed dates.
Credential Utilities
Location: compliance/services/credential_utils.py
Every credential entry in User.credentials mirrors the credential object type in Sanity. The core shape:
| Field | Purpose |
|---|---|
_key | UUID4 hex, stable per-credential identifier. Server writes always generate one. |
credentialType | One of the registered credential type strings |
status | active / suspended / revoked / rejected / pending_verification / pending_employee_review / pending_vendor_response |
issuedDate, expiryDate | ISO-8601 date strings (YYYY-MM-DD); expiryDate optional |
payload | Nested dict with type-specific fields, written by the credential validators |
get_active_credential(user, credential_type)
Returns the first credential on user whose credentialType matches, status == 'active', and expiryDate is absent or not before today. Returns None if no such credential exists. Used by EmployeeComplianceService to gate cannabis sales and by AgeVerificationService for medical-card checks.
get_credential_by_key(user, key)
Returns the credential dict with the matching _key, or None. Does not filter on status or expiry; the approve/reject workflow needs to reach pending and rejected entries.
find_user_and_credential_by_key(key)
Scans every User with a non-empty credentials array and returns (user, credential) for the first match. Returns (None, None) if no user owns that key. The webhook and approve/reject endpoints receive a credential key from the client and use this helper to locate the owning user for the state change. Expected volume is small; linear scan is acceptable.
Permit Utilities
Location: compliance/services/permit_utils.py
Permits live on store.compliance_profile.permits (JSONField, mirror of Sanity's store.regulatedCompliance.permits). Each entry has: _key, permitType, permitNumber, status, issuedDate, expiryDate, payload, documentUrl, notes.
Each helper safely handles stores without a StoreComplianceProfile by treating them as having an empty permit list.
get_active_permit(store, permit_type)
Returns the first permit on store whose permitType matches, status == 'active', and expiryDate is absent or parseable as a non-past date. Returns None otherwise. A malformed expiryDate causes the entry to be skipped rather than raise.
get_permit_by_key(store, key)
Returns the permit with the matching _key, or None.
find_store_and_permit_by_key(key)
Scans every Store whose compliance_profile.permits is non-empty and returns (store, permit) for the first match. Returns (None, None) if no store owns that key. Mirror of find_user_and_credential_by_key for store-level permits.