Skip to content

Schedule Profile

Canonical../StructureDefinition/nexus-emr-core-schedule
Statusdraft · 1.26.0
BaseSchedule (constraint)
CQL primary code pathserviceTypewhat this means
SourceFSH · JSON

Minimal Nexus EMR profile for Schedule resources, representing a clinic or provider's bookable calendar. Slots reference back to a Schedule via Slot.schedule. Phase 1 sources OscarPro provider templates (one Schedule per provider) but the profile supports room/equipment (Location) and service-line (HealthcareService) schedules as well.

FSH

The authored source of truth: resources/StructureDefinition-nexus-emr-core-schedule.fsh (edit on GitHub). One FSH file may define several related artifacts.

// NexusEmrCoreSchedule.fsh

Alias: $NexusEmrCoreSchedule = ../StructureDefinition/nexus-emr-core-schedule


// A provider actor may be named by EITHER a PractitionerRole (the clinic/role binding) or a
// raw Practitioner. Practitioner is the primary provider identity in this IG; PractitionerRole
// is used where the clinic/role binding is the assertion. Schedule is the one place a provider
// reference was a HARD requirement rather than a target-type constraint, so before this change
// a Practitioner-shaped Schedule -- the shape a primary-care writer would naturally emit --
// failed `schedule-actor-required` outright. Alternation is ordered longest-first so
// `PractitionerRole/` is never mis-read as `Practitioner/`.
Invariant:     schedule-actor-required
Description:   "Schedule.actor must contain at least one of a provider (PractitionerRole or Practitioner), Location, or HealthcareService"
Severity:      #error
Expression:    "actor.where(reference.matches('(^|/)(PractitionerRole|Practitioner|Location|HealthcareService)/')).exists()"

// Replaces `schedule-actor-practitionerrole-max`: caps the PROVIDER actor at one, whichever
// resource names them. The old rule counted only PractitionerRole, so it was silently
// unenforced against a Practitioner-shaped actor.
Invariant:     schedule-actor-provider-max
Description:   "At most one actor referencing a provider (PractitionerRole or Practitioner)"
Severity:      #error
Expression:    "actor.where(reference.matches('(^|/)PractitionerRole/') or reference.matches('(^|/)Practitioner/')).count() <= 1"

Invariant:     schedule-actor-location-max
Description:   "At most one actor referencing a Location"
Severity:      #error
Expression:    "actor.where(reference.matches('(^|/)Location/')).count() <= 1"

Invariant:     schedule-actor-healthcareservice-max
Description:   "At most one actor referencing a HealthcareService"
Severity:      #error
Expression:    "actor.where(reference.matches('(^|/)HealthcareService/')).count() <= 1"


// --- Profile Definition ---

Profile:        NexusEmrCoreSchedule
Parent:         Schedule
Id:             nexus-emr-core-schedule
Title:          "Schedule Profile"
Description:    "Minimal Nexus EMR profile for Schedule resources, representing a clinic or provider's bookable calendar. Slots reference back to a Schedule via Slot.schedule. Phase 1 sources OscarPro provider templates (one Schedule per provider) but the profile supports room/equipment (Location) and service-line (HealthcareService) schedules as well."
// THE effective date for this profile (see the EffectiveDate / NoEffectiveDate
// RuleSets): one date concept per resource, declared here rather than guessed.
* insert EffectiveDate([[planningHorizon.start]])

// SMART on FHIR exposure -- READ-ONLY. An app may read and search this resource but not
// create, update or delete it: this is reference/directory data, maintained by the EMR
// rather than by third-party apps, and a delete here is data loss rather than workflow.
* insert SmartExposedReadOnly(schedule)

* ^url = $NexusEmrCoreSchedule
* ^status = #draft
* ^publisher = "WELLSTAR Technologies Corp."
* obeys schedule-actor-required

// --- Element Constraints ---

* . ^short      = "Nexus EMR Schedule Core"
* . ^definition = """
  Represents a bookable calendar — typically owned by a provider (PractitionerRole),
  but may instead represent a room/equipment (Location) or a service line
  (HealthcareService). Slots are produced from a Schedule and reference it via
  Slot.schedule (1..1).
  """

* meta.source 0..1 MS
* meta.source ^short = "Identifies EMR instance & pipeline version the resource came from"
* meta.source ^definition = "A URI that identifies the EMR pipeline and version from which this resource originated. Tells you which EMR instance, and the version of the pipeline code/transformations."

* text 0..1 MS
* text ^short = "Narrative"
* text ^definition = "A human-readable narrative summary of the NexusEmrCoreSchedule resource for human interpretation."

// Identifier
* identifier 0..* MS
* identifier.system 1..1
* identifier.value 1..1
* identifier ^short = "Business identifier(s) for schedule"
* identifier ^definition = """
Unique identifiers for the schedule.

A business identifier is recommended but not required (identifier is 0..*). Where available, carry the source-system identifier using the most appropriate system URI. No specific canonical system is mandated.
"""
* insert SourceIdentifier(identifier)


// active — whether the schedule is currently bookable
* active 0..1 MS
* active ^short = "Whether the schedule is in active use"
* active ^definition = "Whether this schedule record is in active use. Defaults to true if absent (FHIR R4 semantics)."

// actor — a provider (PractitionerRole or Practitioner), a Location, or a HealthcareService
// At least one must be present (enforced by schedule-actor-required invariant)
* actor 1..* MS
* actor ^short = "Who/what the schedule belongs to"
* actor ^definition = """
The resource(s) this schedule represents available booking time for.

Typically the **provider** whose calendar this is. The provider may be named by either a
**PractitionerRole** (where the clinic/role binding is the assertion -- a provider's calendar
*at this clinic, in this role*) or a raw **Practitioner** (the primary provider identity in
this IG). Both are supported and consumers must handle either.

May also be a Location (room or equipment schedule) or a HealthcareService (service-line
schedule, e.g. walk-in clinic). At least one provider / Location / HealthcareService actor
MUST be present (per schedule-actor-required invariant), and at most one provider actor
(per schedule-actor-provider-max).
"""

// Actor composition (at most one provider — PractitionerRole or Practitioner — and at most
// one each of Location and HealthcareService) is enforced by reference-type invariants rather
// than slicing: profile-discriminated slicing on resolve() is unenforceable for Nexus EMR's
// logical (non-resolvable) references — see meta/history 2026-07-06 evaluable-slicing.
* obeys schedule-actor-provider-max and schedule-actor-location-max and schedule-actor-healthcareservice-max

// planningHorizon — the period over which the schedule is bookable
* planningHorizon 0..1 MS
* planningHorizon ^short = "Rolling booking window this schedule supports"
* planningHorizon ^definition = "The period of time that this schedule covers (typically a rolling 90 days from today). Slots SHOULD only exist within this period."

// serviceCategory — broad categorisation of the service this schedule supports
* serviceCategory 0..* MS
* serviceCategory ^short = "Service category with raw code support"
* insert SourceCoding(serviceCategory)
* serviceCategory ^definition = """
A broad categorization of the service that is offered through this schedule.

RECOMMENDED: Standard terminology codes when available
OPTIONAL: Raw EMR codes for source system preservation
"""


// serviceType — specific service this schedule supports
* serviceType 0..* MS
* serviceType ^short = "Service type with raw code support"
* insert SourceCoding(serviceType)
* serviceType ^definition = """
The specific service that is offered through this schedule.

RECOMMENDED: Standard terminology codes when available
OPTIONAL: Raw EMR codes for source system preservation
"""


// comment — free-text scheduling notes (e.g. "Wednesdays are walk-in only")
* comment 0..1 MS
* comment ^short = "Free-text scheduling notes"

// CQL PRIMARY CODE PATH. Names the code-valued element a bare CQL retrieve filters on, so
// `[Schedule: "some-code"]` knows what to compare. Base R4's own CQL ModelInfo declares no
// `primaryCodePath` for this resource, and the consequence is not a compile error: the
// retrieve COMPILES, the translator only warns, and the criterion silently filters on
// nothing while its data-requirements manifest emits a `codeFilter` with no `path` (which
// violates FHIR's own drq-1 invariant). Declaring it here is what makes the natural
// authoring form correct rather than merely accepted.
//
// The extension is HL7's own, from hl7.fhir.uv.extensions.r4 which this IG already depends
// on -- not a Nexus canonical. Read by ts/codegen/cql/gen-modelinfo.ts.
* ^extension[+].url = $cql-primary-code-path
* ^extension[=].valueString = "serviceType"