Skip to content

Locality

Locality is the core context management mechanism in Retail API. It identifies the current request’s organization, advisor, and household, then restricts data access accordingly.

  1. Identify current context - Determine which organization/advisor/household the request operates on
  2. Reduce permission resolution overhead - Avoid recalculating accessible IDs for every operation
Federation
└── Organization
└── Advisor
└── Household
Type Inheritance Purpose
UserLocality Locality Default when no route parameters present
AdvisorLocality Locality Advisor context, no impersonation support
ImpersonatableAdvisorLocality AdvisorLocality Supports team collaboration impersonation
HouseholdLocality ImpersonatableAdvisorLocality Household context
OrganizationLocality Locality Organization context, cannot get advisor
FederationLocality Locality Federation context

Code location: retail-api/app/Locality/

Context is initialized from route parameters via InitializeLocality middleware. See Locality Middleware for details.

Resolution priority in LocalityManager::initializeLocalityFromRoute():

  1. federation parameter → FederationLocality
  2. advisor + household parameters → HouseholdLocality
  3. advisor parameter → ImpersonatableAdvisorLocality
  4. organization parameter → OrganizationLocality
  5. No parameters → UserLocality

Route parameters use encrypted IDs:

// retail-api/app/Locality/Services/LocalityManager.php:323
$id = Crypt::decryptId($encrypted_id);
Operation Restricted to
Read context organization/advisor/household
Write context household only

Models under Advisor (excluding Household sub-models)

Section titled “Models under Advisor (excluding Household sub-models)”
Operation Restricted to
Read context organization/advisor
Write context advisor only

Access restricted to context organization.

// Get current context
LocalityManager::getAdvisor();
LocalityManager::getHousehold();
LocalityManager::getOrganization();
// Temporarily switch household
LocalityManager::switchHousehold($household, function() {
// Uses new household context within this closure
});
// Temporarily switch entire locality
LocalityManager::switchLocality($locality, function() {
// ...
});
// Ignore license status check
LocalityManager::switchIgnoreLicenseStatus(true, function() {
// ...
});

Code location: retail-api/app/Locality/Services/LocalityManager.php

Advisors can impersonate other advisors on the same team:

// retail-api/app/Locality/ImpersonatableAdvisorLocality.php:130-135
match ($login_user->type) {
UserType::ADVISOR => (
$login_user->id === $advisor->id
|| in_array($advisor->id, AdvisorPolicy::getReadableIdsForDependent(), true)
) || throw new AccessDeniedHttpException(...),
};

Permission is checked via AdvisorPolicy::getReadableIdsForDependent().

The advisor parameter in the route may be an impersonated advisor, not necessarily the logged-in user.

Context comes entirely from route parameters. If the route has no organization/household parameter, calling getHousehold() will throw LogicException.

OrganizationLocality cannot retrieve an Advisor. Calling getAdvisor() throws an exception.