← Knowledge library

Appointments & queues

Schedule & Availability

A schedule is a repeatable block of time during which a resource — a practitioner, location, or healthcare service — can be booked. You attach availabilities to it and exceptions that block out specific dates. Source: - Model:…

3.1 · versioned documentation · 8 min read

A schedule is a repeatable block of time during which a resource — a practitioner, location, or healthcare service — can be booked. You attach availabilities to it (these decide how the block splits into slots) and exceptions that block out specific dates.

Source:

The Django model (care/emr/models/scheduling/schedule.py) is storage only, down to the opaque Availability.availability JSONField. The contract lives in the Pydantic resource specs (care/emr/resources/scheduling/...): the enums, the real shape of that JSON field, the validation rules, and the separate read/write schemas. Everything below ## Models describes the specs.

Models

Model Purpose
SchedulableResource A bookable resource (a user, location, or healthcare service) within a facility
Schedule A named, date-bounded block of availability attached to a resource
Availability Slot configuration for a schedule (slot size, tokens, weekly recurrence)
AvailabilityException A date/time range that blocks a resource (leave, holidays)

All four extend EMRBaseModel, which supplies external_id, created_date/modified_date, soft-delete via deleted, created_by/updated_by, and history/meta JSON.

Schedule fields

Field Type Req. Notes
resource FK → SchedulableResource (CASCADE) server Resolved server-side from resource_type + resource_id (get-or-create). Reject this in the body
name CharField(255) yes Display name (e.g. "Morning OPD")
valid_from DateTimeField yes Start of the effective window. Must be ≥ now and ≤ valid_to
valid_to DateTimeField yes End of the effective window. Must be ≥ now
revisit_allowed_days IntegerField no Nullable. Window within which a follow-up uses the revisit charge. Settable only via the set_charge_item_definition action
is_public BooleanField yes Exposes the schedule for public booking. Model default False

Billing links

Both charge links are set only through the set_charge_item_definition action (see Resource specs) — never the create/update body.

Field Type Notes
charge_item_definition FK → ChargeItemDefinition (PROTECT) Nullable; charge applied to bookings against this schedule. related_name="schedule_charge_item_definition"
revisit_charge_item_definition FK → ChargeItemDefinition (PROTECT) Nullable; charge applied when a booking falls within revisit_allowed_days. related_name="schedule_revisit_charge_item_definition"

Enums

SchedulableResourceTypeOptions

Discriminates the kind of entity a schedule or exception attaches to. On write, send it as resource_type; the matching resource_id is that entity's external_id.

Value Resolves to
practitioner A User who is a member of the facility (serialized via UserSpec)
location A FacilityLocation in the facility (serialized via FacilityLocationListSpec)
healthcare_service A HealthcareService in the facility (serialized via HealthcareServiceReadSpec)

The SchedulableResource.resource_type model column defaults to the string "practitioner".

SlotTypeOptions

The type of an individual Availability block (Availability.slot_type on the model, slot_type in the spec).

Value Meaning
open Open block; slot_size_in_minutes / tokens_per_slot are cleared to null on save
appointment Time-precise appointment block; requires both slot_size_in_minutes and tokens_per_slot
closed Closed block; slot_size_in_minutes / tokens_per_slot are cleared to null on save

Related models

SchedulableResource

The bookable entity within a facility, and the join point that booking hangs off. Exactly one of user, location, or healthcare_service points at the underlying resource; resource_type says which. You never create one directly: Schedule and AvailabilityException writes call get_or_create_resource(resource_type, resource_id, facility) server-side, which checks the target belongs to the facility first.

facility            → FK Facility (CASCADE)
resource_type       → CharField(255), default "practitioner"
user                → FK User (CASCADE, nullable)
location            → FK FacilityLocation (CASCADE, nullable)
healthcare_service  → FK HealthcareService (CASCADE, nullable)

Three UniqueConstraints keep one SchedulableResource per facility, resource_type, and target:

Constraint Fields
unique_facility_resource_user facility, resource_type, user
unique_facility_resource_location facility, resource_type, location
unique_facility_resource_healthcare_service facility, resource_type, healthcare_service

On read, serialize_resource(obj) (resource/spec.py) dispatches on resource_type to UserSpec / HealthcareServiceReadSpec / FacilityLocationListSpec.

Availability

Decides how a schedule's time divides into bookable slots. One schedule can carry several availabilities — say, a morning block and an afternoon block.

Field Type Req. Spec detail
schedule FK Schedule (CASCADE) server Excluded from the availability spec; set from the URL / parent schedule
name CharField(255) yes Block name
slot_type CharField yes One of SlotTypeOptions
slot_size_in_minutes IntegerField (nullable) conditional int | None, ge=1. Required when slot_type == appointment; forced to null otherwise
tokens_per_slot IntegerField (nullable) conditional int | None, ge=1. Capacity per slot. Required when slot_type == appointment; forced to null otherwise
create_tokens BooleanField no Default False. When True, each booking gets a token (queue/token workflows)
reason TextField (nullable) no Spec default ""
availability JSONField, default dict yes Despite the dict default, the spec models it as a list[AvailabilityDateTimeSpec] (weekly recurrence). See below

availability JSON shape — AvailabilityDateTimeSpec

Each entry is one weekly recurrence window. The field stores a list of these objects, not a single dict.

AvailabilityDateTimeSpec {
  day_of_week: int   # 0–6, validated le=6 (Monday=0 … Sunday=6, ISO-style)
  start_time: time   # HH:MM:SS
  end_time:   time   # HH:MM:SS
}

The list is validated by AvailabilityForScheduleSpec.validate_availability + validate_for_slot_type:

AvailabilityException

Blocks a resource for a date/time range regardless of its schedules — leave, holidays, one-off closures.

Field Type Req. Spec detail
resource FK SchedulableResource (CASCADE) server Resolved from resource_type + resource_id (get-or-create); excluded from the spec body
name CharField(255) yes Exception name
reason TextField (nullable) no str | None
valid_from DateField yes date. Must be ≥ today and ≤ valid_to
valid_to DateField yes date. Must be ≥ today
start_time TimeField yes time
end_time TimeField yes time

Resource specs (API schema)

Every spec extends EMRResource (base.py), which provides serialize() (DB → pydantic), de_serialize() (pydantic → DB), and the perform_extra_serialization / perform_extra_deserialization hooks. __exclude__ lists fields skipped during (de)serialization.

Spec Role Exposes / behaviour
ScheduleBaseSpec shared id, is_public. __exclude__ = ["resource", "facility"]
ScheduleCreateSpec write · create facility, name, valid_from, valid_to, availabilities: list[AvailabilityForScheduleSpec], resource_type, resource_id, is_public. Validates dates ≥ now, valid_from ≤ valid_to, and cross-availability non-overlap. perform_extra_deserialization stashes facility, _resource_id, _resource_type, and availabilities on the instance for the viewset
ScheduleUpdateSpec write · update name, valid_from, valid_to, is_public. perform_extra_deserialization blocks narrowing validity that would drop allocated slots — it compares Sum(TokenSlot.allocated) across the old vs new range and raises if they differ
ScheduleReadSpec read · list + detail All of the above plus availabilities (re-serialized from Availability rows), resource_type, charge_item_definition / revisit_charge_item_definition (full ChargeItemDefinitionReadSpec or null), revisit_allowed_days, created_by, updated_by
AvailabilityBaseSpec shared id. __exclude__ = ["schedule"]
AvailabilityForScheduleSpec write/read (nested in schedule) name, slot_type, slot_size_in_minutes, tokens_per_slot, create_tokens, reason, availability: list[AvailabilityDateTimeSpec]. Carries the availability + slot-type validators
AvailabilityCreateSpec write · create (standalone) Adds schedule: UUID4; on create, re-checks overlap against all existing availabilities of that schedule
AvailabilityDateTimeSpec nested day_of_week (le=6), start_time, end_time — the shape of the availability JSON field
AvailabilityExceptionBaseSpec shared id, reason, valid_from, valid_to, start_time, end_time. __exclude__ = ["resource", "facility"]
AvailabilityExceptionWriteSpec write · create/upsert Adds facility, resource_type, resource_id. Validates dates ≥ today and valid_from ≤ valid_to; stashes _resource_type/_resource_id
AvailabilityExceptionReadSpec read · list + detail Base fields; perform_extra_serialization maps id → external_id
ChargeItemDefinitionSetSpec write (action body) charge_item_definition: str | None, re_visit_allowed_days: int, re_visit_charge_item_definition: str | None — body for POST .../set_charge_item_definition (charge fields resolved by slug within the facility)

Server-maintained behaviour (viewset hooks)

Integration notes

Related

Source evidence

References are pinned to the private MANSOQ source repository. Repository access is required to open these links.