Facility administration
Location
FacilityLocation is a node in a facility's building/ward/room/bed tree — where resources sit and where care happens. You work with it when you place an encounter somewhere, scope organization access to part of a facility, or model a site's…
3.1 · versioned documentation · 9 min read
FacilityLocation is a node in a facility's building/ward/room/bed tree — where resources sit and where care happens. You work with it when you place an encounter somewhere, scope organization access to part of a facility, or model a site's physical layout.
Source:
- Model:
care/emr/models/location.py - Resource spec:
care/emr/resources/location/spec.py - Base resource:
care/emr/resources/base.py - Coding type:
care/emr/resources/common/coding.py
Nodes nest without limit: every node may have a parent and children, so one tree can run from a campus down to a single bed. Encounters and organizations attach to nodes, and access granted at a node cascades to its descendants asynchronously rather than resolving on each request. The model maps to the FHIR Location resource.
The Django model is only the storage layer. location_type, metadata, and cached_parent_json are opaque JSONFields; their real shape lives in the Pydantic resource specs (care/emr/resources/location/spec.py), which also define every enum and the read/write schema split. See Resource specs (API schema).
Models
| Model | Purpose |
|---|---|
FacilityLocation |
A node in a facility's location tree (building, ward, room, bed, …) |
FacilityLocationOrganization |
Grants a FacilityOrganization access to a location |
FacilityLocationEncounter |
Records how/when an encounter occupied a location (e.g. a bed) |
All three extend EMRBaseModel, which supplies external_id, audit fields, and soft-delete semantics.
FacilityLocation fields
Descriptive
| Field | Type | Spec values / shape | Notes |
|---|---|---|---|
name |
CharField(255) |
str, required |
Must be unique among siblings at the same level under the same root (see validation) |
description |
CharField(255) |
str, required |
Free-text |
status |
CharField(255) |
StatusChoices enum |
One of StatusChoices |
operational_status |
CharField(255) |
FacilityLocationOperationalStatusChoices enum |
FHIR operational status code; one of FacilityLocationOperationalStatusChoices |
system_availability_status |
CharField(255) |
str (read-only) |
Server-maintained, derived from encounter association. Resolves to one of LocationAvailabilityStatusChoices (available / reserved) |
mode |
CharField(255) |
FacilityLocationModeChoices enum |
kind (a type, e.g. a building/ward) or instance (a single location, e.g. Bed 1). Set on create, then frozen. See FacilityLocationModeChoices |
location_type |
JSONField (default=dict, nullable) |
Coding | None (default None) |
A single FHIR Coding, not a CodeableConcept. Unbound — any coding is accepted |
form |
CharField(255) |
FacilityLocationFormChoices enum |
FHIR physical form; one of FacilityLocationFormChoices |
Hierarchy & tree caches
These fields denormalize the tree so descendants are queryable without recursive joins. save() and the cascade task own them; never set them from a client.
| Field | Type | Notes |
|---|---|---|
parent |
FK → FacilityLocation |
SET_NULL, nullable. Immediate parent. Accepted on write as a UUID (parent); on read it's dropped in favour of the serialized parent JSON |
root_location |
FK → self |
CASCADE, related_name="root", nullable. Top of this node's tree. Excluded from specs |
has_children |
BooleanField |
default=False. Set True on the parent when a child is created. Read-only in list/retrieve specs |
level_cache |
IntegerField |
default=0. Depth in the tree (parent.level_cache + 1) |
parent_cache |
ArrayField[int] |
Ordered ancestor IDs (parent.parent_cache + [parent.id]) |
cached_parent_json |
JSONField (default=dict) |
Serialized parent chain (FacilityLocationListSpec) plus a cache_expiry ISO timestamp. Rebuilt lazily by get_parent_json(), invalidated by the cascade task. cache_expiry_days = 15 |
sort_index |
IntegerField |
default=0. In specs int | None, default 0, constrained 0 ≤ sort_index ≤ 10000 (MIN_SORT_INDEX/MAX_SORT_INDEX). Auto-assigned as max(sibling sort_index) + 1 when left unset |
Access & encounter
| Field | Type | Notes |
|---|---|---|
facility |
FK → facility.Facility |
PROTECT. Owning facility. Excluded from specs — resolved from route/context |
facility_organization_cache |
ArrayField[int] |
default=list. Org IDs that can reach this location; rebuilt by sync_organization_cache() |
current_encounter |
FK → Encounter |
SET_NULL, nullable, default=None. Populated from FacilityLocationEncounter. Excluded from the base spec; surfaced as serialized JSON in list/retrieve specs |
metadata |
JSONField |
default=dict. Open extension bag for deployment-specific data |
Enum values
Every enum is a str Enum in care/emr/resources/location/spec.py. Values below are character-for-character from source.
StatusChoices values
| Value | Meaning |
|---|---|
active |
Location is active |
inactive |
Location is inactive |
unknown |
Status unknown |
FacilityLocationOperationalStatusChoices values
FHIR HL7 v2 bed/location operational status codes.
| Code | Meaning |
|---|---|
C |
Closed |
H |
Housekeeping |
O |
Occupied |
U |
Unoccupied |
K |
Contaminated |
I |
Isolated |
FacilityLocationModeChoices values
| Value | Meaning |
|---|---|
instance |
A single, concrete location (e.g. Bed 1). Instances cannot have children |
kind |
A class/type of location (e.g. a ward or building) |
FacilityLocationFormChoices values
FHIR location-physical-type codes.
| Code | Meaning |
|---|---|
si |
Site |
bu |
Building |
wi |
Wing |
wa |
Ward |
lvl |
Level |
co |
Corridor |
ro |
Room |
bd |
Bed |
ve |
Vehicle |
ho |
House |
ca |
Cabinet |
rd |
Road |
area |
Area |
jdn |
Jurisdiction |
vi |
Virtual |
LocationEncounterAvailabilityStatusChoices values
Used by FacilityLocationEncounter.status — how an encounter occupies a location.
| Value | Meaning |
|---|---|
planned |
Occupancy planned |
active |
Currently occupied |
reserved |
Reserved |
completed |
Occupancy ended |
LocationAvailabilityStatusChoices values
The server-derived values that system_availability_status resolves to.
| Value | Meaning |
|---|---|
available |
Not actively tied to an encounter |
reserved |
Tied to an encounter |
Coding shape
location_type is a single Coding object (extra="forbid"):
Coding {
system: str | None = None
version: str | None = None
code: str # required
display: str | None = None
}
Related models
FacilityLocationOrganization
Links an organization to a location. Users under that organization inherit access to the location and its encounters through whatever role they hold on the organization.
location → FK FacilityLocation (CASCADE)
organization → FK FacilityOrganization (CASCADE)
Saving one of these rows calls location.save() (rebuilding facility_organization_cache), then location.cascade_changes() to propagate the grant down to descendants asynchronously. On write, the organization set comes from FacilityLocationWriteSpec.organizations, a list of UUIDs.
FacilityLocationEncounter
A bed assignment, in effect: it records how an encounter occupied a location over a time window.
| Field | Type | Spec | Notes |
|---|---|---|---|
status |
CharField(25) |
LocationEncounterAvailabilityStatusChoices |
One of planned / active / reserved / completed |
location |
FK → FacilityLocation |
excluded / nested | CASCADE. Surfaced as JSON in ...ListSpecWithLocation |
encounter |
FK → Encounter |
UUID on write, JSON on read | CASCADE. Validated to exist on create |
start_datetime |
DateTimeField |
datetime (required) |
Occupancy start |
end_datetime |
DateTimeField (nullable) |
datetime | None |
Occupancy end; open-ended when null |
FacilityLocation.current_encounter is populated from these rows.
Methods & save behaviour
save() side effects
On insert (no id yet):
- If
parentis set,level_cache,root_location, andparent_cacheare derived from the parent, and the parent'shas_childrenis flipped toTrueif needed. - If
sort_indexis unset, it's assignedmax(sibling sort_index) + 1.
On update, cached_parent_json is cleared so it rebuilds lazily. After every super().save(), sync_organization_cache() runs and persists facility_organization_cache through a second save(update_fields=[...]), so each write touches the row twice.
Caches & cascade
sync_organization_cache()— unions the parent's org cache, the org chains of every linkedFacilityLocationOrganization, and the facility'sdefault_internal_organization_id, then writesfacility_organization_cache.get_parent_json()— returns the cached parent chain, refreshingcached_parent_json(viaFacilityLocationListSpec.serialize(parent)) once it expires (cache_expiry_days = 15).cascade_changes()/handle_cascade(base_location)— a Celery task that walks every descendant and re-saves it to invalidate each node'scached_parent_json. It fires when a location's organizations change, so propagation is eventually consistent, not immediate.
validate_uniqueness()
Classmethod that enforces name uniqueness among siblings at the same level_cache within the same root_location — or among root-level locations when there's no root. For a new instance with a parent, level_cache and root_location are computed from that parent before the check runs.
Resource specs (API schema)
The Pydantic specs in care/emr/resources/location/spec.py define the request/response shapes. All extend EMRResource, which provides serialize/de_serialize plus the perform_extra_serialization/perform_extra_deserialization hooks (see base model and base.py).
Location specs
| Spec class | Role | Exposes / notes |
|---|---|---|
FacilityLocationBaseSpec |
shared base | __model__ = FacilityLocation; id: UUID4 | None. __exclude__ = [parent, facility, organizations, root_location, current_encounter] |
FacilityLocationSpec |
shared fields | status, operational_status, name, description, location_type (Coding | None), form, sort_index (0..10000, default 0) |
FacilityLocationWriteSpec |
write · create | Adds parent: UUID4 | None, organizations: list[UUID4] (required), mode: FacilityLocationModeChoices. Validator: a set parent must exist and must not be mode = instance ("Instances cannot have children"). perform_extra_deserialization resolves the parent UUID to an FK |
FacilityLocationUpdateSpec |
write · update | Same fields as FacilityLocationSpec. Drops parent/organizations/mode — hierarchy and mode are fixed after creation |
FacilityLocationMinimalListSpec |
read · minimal | Adds parent: dict, mode: str, has_children: bool, system_availability_status: str. perform_extra_serialization sets id = external_id and parent = obj.get_parent_json() |
FacilityLocationListSpec |
read · list | Extends minimal; adds current_encounter: dict | None, serialized via EncounterListSpec when present |
FacilityLocationRetrieveSpec |
read · detail | Extends list; adds created_by/updated_by (via serialize_audit_users) |
Encounter-association specs
| Spec class | Role | Exposes / notes |
|---|---|---|
FacilityLocationEncounterBaseSpec |
shared base | __model__ = FacilityLocationEncounter; id: UUID4 | None. __exclude__ = [encounter, location] |
FacilityLocationEncounterCreateSpec |
write · create | status (LocationEncounterAvailabilityStatusChoices), encounter: UUID4, start_datetime, end_datetime: datetime | None. Validator: encounter must exist. perform_extra_deserialization resolves the encounter UUID to an FK |
FacilityLocationEncounterUpdateSpec |
write · update | status, start_datetime, end_datetime. Encounter is not re-assignable |
FacilityLocationEncounterListSpec |
read · list | encounter: UUID4, start_datetime, end_datetime, status: str; id = external_id |
FacilityLocationEncounterListSpecWithLocation |
read · list | Extends list; adds location: dict (serialized via FacilityLocationListSpec) |
FacilityLocationEncounterReadSpec |
read · detail | encounter: dict (serialized via EncounterRetrieveSpec), start_datetime, end_datetime, status, created_by/updated_by |
Server-maintained behaviour
- These are platform-maintained — don't write them from a client:
system_availability_status,has_children,level_cache,parent_cache,cached_parent_json,facility_organization_cache,current_encounter, andsort_index(when left unset). - On read,
parentcomes back as the serialized parent chain (get_parent_json()), never the raw FK. - Access granted through
organizations/FacilityLocationOrganizationlands asynchronously; the org cache and descendant caches settle only after the cascade task runs. location_typetakes a freeCoding(no value-set slug) andmetadatais an open bag, so both add coded or extension data without a schema migration.
API integration notes
- The API is modelled on the FHIR
Locationresource, so payload field names may differ from these Django names. modeseparateskind(a location type) frominstance(a concrete location such as a bed). Beds areFacilityLocationinstances, not a separate model, andinstancelocations cannot have children.parent,organizations, andmodeare settable only at creation (FacilityLocationWriteSpec). Updates go throughFacilityLocationUpdateSpec, which omits all three.- For fields you can't write, see Server-maintained behaviour.
Related
- Reference: Facility
- Reference: Organization
- Reference: Encounter
- Reference: Base model
- Source: location.py on GitHub
- Source: location/spec.py on GitHub
Source evidence
References are pinned to the private MANSOQ source repository. Repository access is required to open these links.