Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

References/Booking

From OHC Network Wiki
referenceschedulingCARE 3.0+

A booking puts a patient into a concrete time slot generated from a Schedule. TokenSlot is a bookable unit of availability; TokenBooking is a patient's appointment against that slot, carrying its status through the lifecycle. You touch both whenever an integration creates, cancels, reschedules, or reads an appointment.

Source:

These models live in two layers. The Django model is storage: status is an unconstrained CharField and several relations are opaque at the DB level. The Pydantic resource specs (care/emr/resources/scheduling/slot/) are the API layer — they pin status to a fixed enum, validate writes, and define the read schemas that embed slot, resource, facility, token, charge item, encounter, and patient sub-objects.

Models

Model Purpose
TokenSlot A concrete, bookable time window generated from a schedulable resource's availability
TokenBooking A patient's appointment against a TokenSlot, with status, notes, and optional links to an encounter, token, and charge item

Both extend EMRBaseModel, the shared Care EMR base providing external_id, created_date/modified_date, soft-delete via deleted, created_by/updated_by, and history/meta JSON.

TokenBooking fields

Field Type Notes
token_slot FK → TokenSlot (PROTECT) The slot this appointment occupies; required. PROTECT prevents deleting a slot that has bookings
patient FK → emr.Patient (CASCADE) The booked patient; required
booked_on DateTimeField auto_now_add — stamped once at creation, never accepted from clients
booked_by FK → User (CASCADE) User who created the booking; nullable for self-service or system-created bookings

{{Field|model=Booking|section=TokenBooking fields|name=status|type=CharField (storage) → BookingStatusChoices (API)|notes=Appointment lifecycle status. Unbounded on the model; the write spec restricts it to the [[#bookingstatuschoices-values}}

note TextField Free-text note; nullable in storage but required (str) on write
tags ArrayField[int] Tag IDs, default empty list. Managed via SingleFacilityTagManager; reads return rendered tag objects, not raw IDs
associated_encounter FK → emr.Encounter (PROTECT) Encounter linked to the appointment; nullable, defaults to None. Surfaced only by TokenBookingRetrieveSpec
token FK → emr.Token (PROTECT) Queue token issued for this booking; nullable, defaults to None, related_name="token_booking". Created by the generate_token action
charge_item FK → emr.ChargeItem (CASCADE) Billing charge item for the appointment; nullable. Auto-created on booking when the schedule has a charge_item_definition

BookingStatusChoices values

BookingStatusChoices (str, Enum) is the full set of status values the write spec accepts:

Value Notes
proposed
pending
booked Default applied server-side when a booking is created via lock_create_appointment
arrived
fulfilled Terminal; in COMPLETED_STATUS_CHOICES
cancelled Set only via the cancel endpoint; in CANCELLED_STATUS_CHOICES + COMPLETED_STATUS_CHOICES
noshow Terminal; in COMPLETED_STATUS_CHOICES
entered_in_error Set only via the cancel endpoint; in CANCELLED_STATUS_CHOICES + COMPLETED_STATUS_CHOICES
checked_in
waitlist
in_consultation Cannot be cancelled — the cancel handler rejects it
rescheduled Set only via the reschedule endpoint; in CANCELLED_STATUS_CHOICES + COMPLETED_STATUS_CHOICES

Two derived sets (from slot/spec.py) drive the lifecycle rules:

Set Values
CANCELLED_STATUS_CHOICES entered_in_error, cancelled, rescheduled
COMPLETED_STATUS_CHOICES fulfilled, noshow, entered_in_error, cancelled, rescheduled

TokenBookingWriteSpec.perform_extra_deserialization rejects any write whose status is in CANCELLED_STATUS_CHOICES with "Cannot cancel a booking. Use the cancel endpoint". Cancellation and rescheduling go through dedicated endpoints (see below), never a plain update.

TokenSlot

A bookable window derived from a resource's availability. Bookings reference it via PROTECT, so a slot with active bookings cannot be deleted.

Field Type Notes
resource FK → SchedulableResource (CASCADE) The schedulable resource — practitioner, location, or healthcare service — the slot belongs to; required
availability FK → Availability (CASCADE) The availability rule that generated this slot; nullable
start_datetime DateTimeField Slot start; required, tz-aware
end_datetime DateTimeField Slot end; required, tz-aware
allocated IntegerField Count of bookings currently allocated to the slot, used to enforce capacity. Defaults to 0. Platform-maintained — never written by clients

SchedulableResource and Availability live in the Schedule module. SchedulableResource.resource_type is one of SchedulableResourceTypeOptions: practitioner, location, healthcare_service.

Resource specs (API schema)

Every spec extends EMRResource ([https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/base.py base.py]): serialize (DB → pydantic, read) and de_serialize (pydantic → DB, write), with perform_extra_serialization / perform_extra_deserialization hooks.

Spec class Role Exposes / behaviour
TokenBookingBaseSpec shared __model__ = TokenBooking, __exclude__ = ["token_slot", "patient"]
TokenBookingWriteSpec write · create + update Fields: status: BookingStatusChoices, note: str. Rejects status in CANCELLED_STATUS_CHOICES (use the cancel/reschedule endpoints). Serves as both pydantic_model and pydantic_update_model
TokenBookingMinimumReadSpec read · embed Lightweight read for when a booking is embedded in another resource, such as a token. Fields: id, token_slot (embedded TokenSlotBaseSpec), booked_on, status: str, note, created_date, modified_date
TokenBookingBaseReadSpec read · shared Adds booked_by: UserSpec, resource_type, resource (serialized resource), facility, created_by/updated_by, token: TokenReadSpec, tags: list[dict] (rendered), charge_item: dict
TokenBookingReadSpec read · list/default TokenBookingBaseReadSpec + patient: PatientRetrieveSpec. The viewset's pydantic_read_model for list responses
TokenBookingOTPReadSpec read · OTP flow TokenBookingBaseReadSpec + patient: PatientOTPReadSpec for the public/OTP booking flow
TokenBookingRetrieveSpec read · detail TokenBookingReadSpec + associated_encounter: dict (serialized EncounterListSpec). The viewset's pydantic_retrieve_model
TokenSlotBaseSpec read · embed __model__ = TokenSlot, __exclude__ = ["resource", "availability"]. Fields: id, availability (embedded as {name, tokens_per_slot, id, schedule:{name,id}}), start_datetime, end_datetime, allocated

Read serialization details (perform_extra_serialization)

  • id always comes from obj.external_id (UUID), not the integer PK.
  • token_slot is embedded via TokenSlotBaseSpec.serialize(...).
  • resource_type comes from token_slot.resource.resource_type; resource is built by serialize_resource() — a UserSpec for practitioner, HealthcareServiceReadSpec for healthcare_service, FacilityLocationListSpec for location.
  • facility is a cached FacilityBareMinimumSpec; booked_by / created_by / updated_by are cached UserSpecs.
  • tags are rendered through SingleFacilityTagManager().render_tags(obj) as objects, not raw IDs.
  • token (TokenReadSpec), charge_item (ChargeItemReadSpec), and associated_encounter (EncounterListSpec) appear only when present.

Methods & save behaviour

status has no model-level choices; the lifecycle lives entirely in the spec/viewset layer (TokenBookingViewSet). The server-maintained behaviour:

  • Creation (lock_create_appointment) — runs under a per-resource lock and a transaction. Rejects past slots (end_datetime < now) and full slots (allocated >= availability.tokens_per_slot), rejects a duplicate active booking for the same patient/slot (any status not in COMPLETED_STATUS_CHOICES), then increments token_slot.allocated, creates the booking with status="booked", and — if the schedule has a charge_item_definition — auto-creates the linked charge_item.
  • Cancel (cancel action) — accepts CancelBookingSpec { reason: cancelled | entered_in_error | rescheduled, note?: str }. Rejects cancelling an in_consultation booking. Decrements token_slot.allocated (unless already cancelled), sets status = reason, optionally overwrites note, sets updated_by, and aborts any linked charge_item (handle_charge_item_cancel + status aborted).
  • Reschedule (reschedule action) — accepts RescheduleBookingSpec { new_slot: UUID, new_booking_note: str, previous_booking_note?: str, tags: list[UUID] } and requires can_reschedule_booking. Cancels the existing booking with reason rescheduled, then runs lock_create_appointment to create a fresh booking on new_slot for the same patient. Rejects rescheduling to the same slot.
  • Generate token (generate_token action) — accepts TokenGenerationSpec { category: UUID, note?: str, queue?: UUID }. Rejects if a token already exists. Resolves or creates a TokenQueue for the slot's date and resource, allocates the next number under a queue lock, creates a Token (status="CREATED"), and links it to booking.token.
  • booked_on is auto_now_add — never set by clients.

API integration notes

  • TokenSlot and TokenBooking are exposed through Care's scheduling REST API and align with the FHIR Slot and Appointment resources; payload field names may differ from Django model names.
  • Writes use TokenBookingWriteSpec (status constrained to BookingStatusChoices, note required). cancelled / entered_in_error / rescheduled cannot be set through a plain update — use the cancel / reschedule actions.
  • List responses use TokenBookingReadSpec; detail responses use TokenBookingRetrieveSpec, which adds associated_encounter.
  • allocated on TokenSlot is a capacity counter maintained by lock_create_appointment and cancel; do not write it directly.
  • tags is stored as an array of integer tag IDs but returned as rendered tag objects on read.
  • List requires a resource_type query param and authorization scoped to organizations or resource IDs.

Pages that link here: