Sites, Regions & Districts¶
Sonny's Backoffice has two tenant shapes for site management:
- Flat — a tenant with 1-N sites and no hierarchy. The create form uses a simple site checklist.
- Hierarchical — a tenant with Regions → Districts → Sites. The form has a tree of toggles and submits a complex payload.
The library auto-detects which shape your tenant uses by inspecting the /employee/create page and routes the payload accordingly. You never need to know which shape your tenant has.
Referring to sites by name¶
Always use the human-readable site name from the Backoffice UI, not the internal numeric ID:
Under the hood the library parses the site tree from /employee/create into a SiteTree object, resolves each name to its internal site_id, and builds the payload.
The "all" shorthand¶
Pass the literal string "all" to grant access to every site on the tenant:
On a flat tenant this sends employee[isAllSitesAllowed]=1. On a hierarchical tenant it sends employee[isAllRegionsAllowed]=1, which cascades down to every district and site.
Unknown site names¶
Passing a site name the tenant doesn't have raises LookupError immediately — the wrapper does not silently drop unknown names, because that would mean creating an employee with narrower access than the caller asked for. Instead, you get a clear error before any HTTP call is made.
Inspecting the tree¶
If you want to see what sites exist on a tenant (e.g., for a UI dropdown), use the discovery helper:
list_sites() returns a list of Site objects. It's cached on the client after the first call; pass refresh=True to re-fetch.
How hierarchy detection works¶
The parser looks for <input class="boac-permission-region-option"> inputs inside the /employee/create page. If found, the tenant is hierarchical and the parser walks the tree:
- Regions from
<input class="boac-permission-region-option" data-region-id="N"> - Districts from
<input class="boac-permission-district-option" data-district-id="N" data-region-id="M"> - Sites from
<input class="boac-permission-site-option" value="N" data-district-id="M">(region is resolved via the district)
If no region inputs are found, the tenant is flat and the parser just reads <input class="boac-permission-site-option"> as a top-level list.
Wage attribution¶
Even on a hierarchical tenant with a thousand sites, each employee's wage is attributed to a single site on the form. The library picks the first site from your available_sites list (or the first site in the tree if you passed "all") and uses that as the wage attribution. You can see which site was chosen in result.wage_site.
Domain models¶
class Region:
id: int
name: str
class District:
id: int
name: str
region_id: int | None
class Site:
id: int
name: str
district_id: int | None
region_id: int | None
On a flat tenant, district_id and region_id are always None.