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:

FieldPurpose
_keyUUID4 hex, stable per-credential identifier. Server writes always generate one.
credentialTypeOne of the registered credential type strings
statusactive / suspended / revoked / rejected / pending_verification / pending_employee_review / pending_vendor_response
issuedDate, expiryDateISO-8601 date strings (YYYY-MM-DD); expiryDate optional
payloadNested 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.


Was this page helpful?