Skip to main content
Version: 3.0

Encounter

An Encounter records a single interaction between a patient and a facility — a visit, admission, or consultation — and maps to the FHIR Encounter resource. You create one when a patient presents, drive it through its status lifecycle, and read it back to render the clinical timeline.

The Django model is only the storage layer: several fields are opaque JSONFields whose real shape lives in the Pydantic resource specs, so the model alone won't tell you what a write must look like. The spec tables below carry that detail.

Source:

Models

ModelPurpose
EncounterA single interaction between a patient and a facility (visit, admission, consultation)
EncounterOrganizationLinks an Encounter to a FacilityOrganization for access control

Both extend EMRBaseModel, the shared Care EMR base that provides external_id, audit fields, and soft-delete semantics.

Encounter fields

Classification & status

FieldTypeNotes
statusCharField(100)Nullable in DB; required in API. One of StatusChoices (see below)
status_historyJSONFieldDefault {}. Server-maintained. Shape: { "history": [{ "status": str, "moved_at": str (ISO datetime) }] }
encounter_classCharField(100)Nullable in DB; required in API. One of ClassChoices (see below)
encounter_class_historyJSONFieldDefault {}. Server-maintained. Shape: { "history": [{ "status": str, "moved_at": str (ISO datetime) }] }
priorityCharField(100)Nullable in DB; required in API. One of EncounterPriorityChoices (see below)
external_identifierCharField(100)Nullable. Identifier from an external/source system

Subject & facility

FieldTypeNotes
patientFK → PatientCASCADE. The patient this encounter is for. Set on create only
facilityFK → FacilityPROTECT. The facility where the encounter takes place. Set on create only
appointmentFK → TokenBookingSET_NULL, nullable. Originating scheduling booking, if any
current_locationFK → FacilityLocationSET_NULL, nullable. Cached current location for easier querying

Timing & disposition

FieldTypeNotes
periodJSONFieldDefault {}. PeriodSpec { start: datetime (tz-aware, optional), end: datetime (tz-aware, optional) }. If both set, start ≤ end
hospitalizationJSONFieldDefault {}. HospitalizationSpec (see shape below). Nullable
discharge_summary_adviceTextFieldNullable. Free-text discharge advice. Explicitly cleared to None on update when omitted

Care team

FieldTypeNotes
care_teamJSONFieldDefault {}. Stored as a list of { "user_id": int, "role": Coding } entries. Not writable on create/update (excluded); managed via the dedicated care-team write spec
care_team_usersArrayField[int]Platform-maintained denormalized cache of user IDs derived from care_team via sync_care_team_users_cache() on every save

Organization cache, tags & extensions

FieldTypeNotes
facility_organization_cacheArrayField[int]Platform-maintained cache of facility-organization IDs (incl. parent chains). Rebuilt by sync_organization_cache()
tagsArrayField[int]Tag IDs applied to the encounter. Serialized via SingleFacilityTagManager().render_tags()
extensionsJSONFieldDefault {}. Open extension bag for deployment-specific metadata. Validated by ExtensionValidator against ExtensionResource.encounter

Nested JSON shapes

PeriodSpec (period)

Defined in resources/base.py.

FieldTypeNotes
startdatetime | NoneMust be timezone-aware if provided
enddatetime | NoneMust be timezone-aware if provided

validate_period rejects naive datetimes, and when both start and end are set it requires start not to exceed end.

HospitalizationSpec (hospitalization)

Defined in spec.py. All fields optional/nullable.

FieldTypeValues
re_admissionbool | None
admit_sourceAdmitSourcesChoices | Nonesee Admit source values
discharge_dispositionDischargeDispositionChoices | Nonesee Discharge disposition values
diet_preferenceDietPreferenceChoices | Nonesee Diet preference values

Enum values

Status values (StatusChoices)

Value
planned
in_progress
on_hold
discharged
completedterminal (in COMPLETED_CHOICES)
cancelledterminal (in COMPLETED_CHOICES)
discontinuedterminal (in COMPLETED_CHOICES)
entered_in_errorterminal (in COMPLETED_CHOICES and ERROR_CHOICES)
unknown

COMPLETED_CHOICES = [completed, cancelled, entered_in_error, discontinued]; ERROR_CHOICES = [entered_in_error].

Class values (ClassChoices)

ValueMeaning
impinpatient
ambambulatory
obsencobservation
emeremergency
vrvirtual
hhhome health

Priority values (EncounterPriorityChoices)

Value
ASAP
callback_results
callback_for_scheduling
elective
emergency
preop
as_needed
routine
rush_reporting
stat
timing_critical
use_as_directed
urgent

Admit source values (AdmitSourcesChoices)

ValueDisplay (get_admit_source_display)
hosp_transTransferred from other hospital
emdFrom accident/emergency department
outpFrom outpatient department
bornBorn in hospital
gpGeneral Practitioner referral
mpMedical Practitioner/physician referral
nursingFrom nursing home
psychFrom psychiatric hospital
rehabFrom rehabilitation facility
otherOther

Discharge disposition values (DischargeDispositionChoices)

ValueDisplay (get_discharge_disposition_display)
homeHome
alt_homeAlternate Home
other_hcfOther Health Care Facility
hospHospital
longLong-term Care Facility
aadviceAgainst Medical Advice
expExpired
psyPsychiatric Hospital
rehabRehabilitation Facility
snfSkilled Nursing Facility
othOther

Diet preference values (DietPreferenceChoices)

Value
vegetarian
dairy_free
nut_free
gluten_free
vegan
halal
kosher
none

Resource specs (API schema)

All specs build on EMRResource, which provides serialize / de_serialize plus the perform_extra_serialization / perform_extra_deserialization hooks. EncounterSpecBase sets __exclude__ = [patient, organizations, facility, appointment, current_location, care_team]; those fields move through hooks or dedicated endpoints rather than direct assignment, which is why the create and read schemas diverge from the raw model.

SpecRoleNotes
EncounterSpecBasesharedFields: id, status, encounter_class, period (PeriodSpec, default {}), hospitalization (HospitalizationSpec | None, default {}), priority, external_identifier, discharge_summary_advice. __model__ = Encounter
EncounterCreateSpecwrite · createExtends base + ExtensionValidator. Adds patient: UUID4, facility: UUID4, organizations: list[UUID4] = [], appointment: UUID4 | None. Resolves FKs by external_id; validates appointment belongs to the patient and facility
EncounterUpdateSpecwrite · updateExtends base + ExtensionValidator. Does not accept patient/facility/organizations/appointment — only mutable fields. Appends to status/class history on change
EncounterListSpecread · listAdds serialized patient (PatientListSpec), facility (FacilityBareMinimumSpec), status_history, encounter_class_history, created_date, modified_date, tags, current_location (FacilityLocationMinimalListSpec | None), care_team ([{ member, role }])
EncounterRetrieveSpecread · detailExtends list + EncounterPermissionsMixin. Adds appointment (TokenBookingReadSpec), created_by/updated_by (UserSpec), organizations (FacilityOrganizationReadSpec[]), location_history (FacilityLocationEncounter[], newest first), extensions; patient via PatientRetrieveSpec
HospitalizationSpecnestedPlain BaseModel; see shape above
EncounterCareTeamMemberSpecnested (write){ user_id: UUID4, role: Coding }, role bound to the system-practitioner-role-code value set
EncounterCareTeamMemberWriteSpecwrite · care team{ members: list[EncounterCareTeamMemberSpec] }. Dedicated payload for replacing the care team

Server-maintained behaviour

  • Status history: on create, status_history starts as { "history": [{ status, moved_at }] } carrying the initial status; on update, a new entry is appended only when status changes. encounter_class_history / encounter_class follow the same rule.
  • FK resolution (create): patient, facility, and appointment resolve from external_id (404 if not found). The appointment must belong to the same patient and to a slot whose resource sits in the same facility.
  • Organizations (create): organizations, deduplicated to a list of UUIDs, is stashed on obj._organizations, and the resulting EncounterOrganization rows drive facility_organization_cache.
  • Discharge advice (update): omitting discharge_summary_advice on update clears it to None rather than leaving the prior value.
  • Care team: role binds to the PRACTITIONER_ROLE_VALUESET (slug system-practitioner-role-code), a SNOMED CT value set of descendants of 223366009 (healthcare professional) and 224930009 (healthcare-related occupation).

EncounterOrganization

Associates an encounter with a facility-scoped organization to drive organization-based access control on encounters.

encounter → FK Encounter (CASCADE)
organization → FK FacilityOrganization (CASCADE)

Saving an EncounterOrganization calls encounter.sync_organization_cache(), which rebuilds the encounter's facility_organization_cache.

Methods & save behaviour

sync_care_team_users_cache()

When care_team is a list, fills care_team_users with int(entry["user_id"]) for each entry, falling back to -1 when a user_id is missing. The flat integer array lets queries skip JSON traversal.

sync_organization_cache()

Recomputes facility_organization_cache as the union of:

  • every linked EncounterOrganization organization plus its parent_cache chain, and
  • the facility's default_internal_organization_id.

Persists the result with super().save(update_fields=["facility_organization_cache"]).

save() side effects

On every save:

  1. sync_care_team_users_cache() runs, refreshing care_team_users.
  2. A record with no pk is flagged as newly created.
  3. Inside a single transaction.atomic() block, the base save() runs. For newly created encounters, evaluate_patient_facility_default_values(patient, facility) then generates facility-scoped patient identifier defaults.
  4. After the transaction, sync_organization_cache() runs, performing a second write (update_fields=["facility_organization_cache"]).

Expect two write passes per create or update through the ORM, plus identifier-default generation on creation.

API integration notes

  • Reads use EncounterListSpec / EncounterRetrieveSpec; writes use EncounterCreateSpec / EncounterUpdateSpec.
  • status, encounter_class, and priority are required on write and validated against their enums, even though the DB columns are nullable.
  • Never set status_history, encounter_class_history, care_team_users, or facility_organization_cache from a client; the server appends history on create and update and derives the caches from care_team and EncounterOrganization.
  • Create and update don't touch the care team. Replace it through the dedicated care-team endpoint with EncounterCareTeamMemberWriteSpec, where each member's role must resolve within the system-practitioner-role-code value set.
  • period and hospitalization carry structured JSON matching the nested-spec shapes above; period.start and period.end must be timezone-aware.
  • current_location is a cached convenience field. The authoritative record is location_history, from FacilityLocationEncounter.
  • extensions holds custom key-value data without a schema migration, validated by ExtensionValidator.