Skip to main content
Version: 3.1

Facility

A Facility is a physical or virtual care site — hospital, clinic, lab, telemedicine endpoint — and the root of a deployment's administrative hierarchy. You create one before any patient, organization, or device can attach to it.

A Facility spans two layers. The Django model is the storage layer, where columns like features and print_templates are opaque blobs. The API contract — enums, validation, JSON-field shapes, and the read/write schema split — lives in the Pydantic resource specs. When the two disagree (a stored integer vs. a wire label, an object key vs. a resolved URL), the spec wins on the wire.

Source:

Models

ModelPurpose
FacilityA care site and the root of a deployment's administrative org hierarchy
FacilityMonetoryConfigPer-facility billing config (one-to-one): discount codes, discount monetary components, discount configuration, invoice-number expression. Backs the discount/invoice fields of FacilityRetrieveSpec. See Facility config

Facility extends BaseModel, not EMRBaseModel. It gets external_id, created_date/modified_date, and soft-delete via deleted, but none of the EMRBaseModel audit/history/meta machinery: it declares its own created_by FK and has no updated_by, history, or meta columns. FacilityMonetoryConfig does extend EMRBaseModel.

The choice sets and enums it uses are defined alongside the model — see Choices & enums.

Facility fields

The Type column is the Django storage type. Spec / Notes records where the API representation, validation, or bound enum from the Pydantic specs diverges from storage.

Identity & classification

FieldTypeSpec / Notes
nameCharField(1000)Required (blank=False, null=False). Spec enforces case-insensitive uniqueness across all facilities (Lower(Trim(name))); on update the current row is excluded.
descriptionTextFieldblank=True, null=False; defaults to empty string. Required (str) in FacilityBaseSpec.
facility_typeIntegerFieldRequired; choices from FACILITY_TYPES (see below). Wire format is the label string, not the integer: create validates the label against REVERSE_REVERSE_FACILITY_TYPES then maps to the int; read maps the int back to the label via REVERSE_FACILITY_TYPES.
featuresArrayField[SmallIntegerField]Nullable; each element a FacilityFeature choice. Spec type list[int].
is_activeBooleanFielddefault=True. Server-managed; the specs don't expose it.
verifiedBooleanFielddefault=False. Server-managed; the specs don't expose it.
is_publicBooleanFielddefault=False; controls public visibility. Exposed as bool on FacilityBaseSpec.

Location & contact

FieldTypeSpec / Notes
addressTextFieldFree-text address. Required (str).
pincodeIntegerFielddefault=None, null=True on the model; required int in FacilityBaseSpec.
longitudeDecimalField(22, 16)Nullable. Spec type Longitude | None (pydantic_extra_types, range −180…180).
latitudeDecimalField(22, 16)Nullable. Spec type Latitude | None (pydantic_extra_types, range −90…90).
phone_numberCharField(14)blank=True; validated with mobile_or_landline_number_validator. Spec type str.
middleware_addressCharField(200)Nullable, default=None. Hostname of an external device/middleware integration. Spec type str | None.
FieldTypeSpec / Notes
geo_organizationFK → emr.Organizationon_delete=SET_NULL, nullable. Geographic/administrative org placement. On write (FacilityCreateSpec): a required UUID4 (the org's external_id), resolved on deserialization to an Organization with org_type="govt". On read: a nested OrganizationReadSpec dict (empty {} when unset).
geo_organization_cacheArrayField[int]default=list. Denormalized cache of geo_organization's parent_cache chain plus its own id; rebuilt by sync_cache(). Not exposed by specs.
default_internal_organizationFK → emr.FacilityOrganizationon_delete=SET_NULL, nullable, related_name="default_facilities". The auto-created root Administration org. Not exposed by specs.
internal_organization_cacheArrayField[int]default=list. Denormalized cache of all FacilityOrganization ids (with parent chains) under this facility; rebuilt by sync_cache(). Not exposed by specs.

Both cache fields are platform-maintained denormalizations, rebuilt on every save() so org-scoped filtering avoids deep joins. Never write them directly. See Methods & save behaviour.

Media & presentation

FieldTypeSpec / Notes
cover_image_urlCharField(500)Nullable, default=None. Stores an object key, not a URL. Read specs expose both the raw cover_image_url and a resolved read_cover_image_url (full URL via read_cover_image_url()).
print_templatesJSONFielddefault=list. Opaque on the model; the real shape is list[PrintTemplate] (see PrintTemplate). Accepted typed on create; returned as list[dict] on retrieve.

Integration & audit

FieldTypeSpec / Notes
created_byFK → users.Useron_delete=SET_NULL, nullable. Set to the facility administrator when the root org is provisioned. Read specs expose it as a nested UserSpec dict (cache-backed), None when unset.

Choices & enums

These choice sets live in the model module and are consumed by Facility and related code.

FacilityFeature (IntegerChoices)

ValueNameLabel
1CT_SCAN_FACILITYCT Scan Facility
2MATERNITY_CAREMaternity Care
3X_RAY_FACILITYX-Ray Facility
4NEONATAL_CARENeonatal Care
5OPERATION_THEATEROperation Theater
6BLOOD_BANKBlood Bank

HubRelationship (IntegerChoices)

ValueNameLabel
1REGULAR_HUBRegular Hub
2TELE_ICU_HUBTele ICU Hub

FACILITY_TYPES

A list of (int code, label). Codes are sparse and namespaced (8xx govt hospitals/health centres, 9xx labs/cooperatives, 10xx–16xx COVID-era centres, 3xxx/4xxx NGO/CBO). Commented-out legacy codes (8, 801, 820, 831, 850, 950, 1000) survive in source as historical mapping notes and are not valid. The API binds on the label, not the code.

CodeLabelCodeLabel
1Educational Inst870Govt Medical College Hospitals
2Private Hospital900Co-operative hospitals
3Other910Autonomous healthcare facility
4Hostel1010COVID-19 Domiciliary Care Center
5Hotel1100First Line Treatment Centre
6Lodge1200Second Line Treatment Center
7TeleMedicine1300Shifting Centre
9Govt Labs1400Covid Management Center
10Private Labs1500Request Approving Center
800Primary Health Centres1510Request Fulfilment Center
802Family Health Centres1600District War Room
803Community Health Centres3000Clinical Non Governmental Organization
830Taluk Hospitals3001Non Clinical Non Governmental Organization
840Women and Child Health Centres4000Community Based Organization
860District Hospitals

REVERSE_FACILITY_TYPES (code → label) and REVERSE_REVERSE_FACILITY_TYPES (label → code) are derived from FACILITY_TYPES via reverse_choices. Create validates against the label set and stores the code; read maps the code back to the label.

DOCTOR_TYPES

A list of (int, label) covering 64 medical specialties (1 General Medicine, 2 Pulmonology, 8 Cardiologist, 48 Nurse, 64 Critical Care Physician, etc.). Defined in this module but not referenced by the Facility model or facility specs.

MonetaryComponentType (str enum, from monetary_component.py)

Used by the discount monetary components in FacilityMonetaryCodeSpec / FacilityRetrieveSpec.

Value
base
surcharge
discount
tax
informational

DiscountApplicability (str enum, from monetary_component.py)

Value
total_asc
total_desc

Nested JSON shapes

These Pydantic models define the real structure behind the model's JSON/array fields and the discount config.

PrintTemplate JSON shape

Element type of the print_templates field.

PrintTemplate {
slug: str (required)
page: PageConfig | null
print_setup: PrintSetupConfig | null
branding: BrandingConfig | null
watermark: WatermarkConfig | null
}
PageConfig { size: "A4"|"A5"|"Letter"|"Legal" | null,
orientation: "portrait"|"landscape" | null,
margin: PageMargin | null }
PageMargin { top: float≥0, bottom: float≥0, left: float≥0, right: float≥0 } # all required
PrintSetupConfig { auto_print: bool | null }
BrandingConfig { logo: LogoConfig | null, header_image: HeaderImageConfig | null,
footer_image: FooterImageConfig | null }
LogoConfig { url: str (required), width: float | null, height: float | null,
alignment: "left"|"center"|"right" (required) }
HeaderImageConfig { url: str (required), height: float | null }
FooterImageConfig { url: str | null, height: float | null }
WatermarkConfig { enabled: bool | null, text: str | null,
opacity: float | null (0…1), rotation: float | null }

Discount config shapes (FacilityMonetaryCodeSpec / FacilityMonetoryConfig)

discount_codes: list[Coding] # see Coding below
discount_monetary_components: list[MonetaryComponentDefinition]
discount_configuration: DiscountConfiguration | null

Coding { system: str|null, version: str|null, code: str (required), display: str|null } # extra="forbid"

MonetaryComponentDefinition (extends MonetaryComponent) {
title: str (required)
monetary_component_type: MonetaryComponentType # base not allowed in a definition
code: Coding | null
factor: Decimal | null (max_digits=20, decimal_places=6)
amount: Decimal | null (max_digits=20, decimal_places=6)
tax_included_amount: Decimal | null (base component only)
global_component: bool = false
conditions: list[EvaluatorConditionSpec] = []
}
EvaluatorConditionSpec { metric: str, operation: str, value: dict|str } # metric validated against EvaluatorMetricsRegistry
DiscountConfiguration { max_applicable: int≥0 (required), applicability_order: DiscountApplicability (required) }

PeriodSpec (base.py) is the shared period shape used across EMR resources. Facility doesn't use it directly; it's here for reference: { start: datetime|null, end: datetime|null }, both must be timezone-aware, and start ≤ end.

Resource specs (API schema)

Every spec subclasses EMRResource (base.py), which supplies serialize (DB object → Pydantic), de_serialize (Pydantic → DB object), and the perform_extra_serialization / perform_extra_deserialization hooks. __exclude__ lists fields skipped during (de)serialization; __store_metadata__ (default False) controls whether unknown fields land in the model's meta.

The hierarchy is FacilityBareMinimumSpec → FacilityBaseSpec → {FacilityCreateSpec, FacilityReadSpec → FacilityRetrieveSpec, FacilityMinimalReadSpec}. There is no separate UpdateSpec: FacilityCreateSpec handles both create and update, with its validators reading is_update/object from the serializer context.

Spec classRoleKey fields / behaviour
FacilityBareMinimumSpecshared base (@cacheable, base manager)id: UUID4, name. __exclude__=["geo_organization"]. Cacheable, fetchable via model_from_cache.
FacilityBaseSpecshared baseAdds description, longitude, latitude, pincode, address, phone_number, middleware_address, facility_type, is_public.
FacilityCreateSpecwrite · create & updateAdds geo_organization: UUID4, features: list[int], print_templates: list[PrintTemplate] = []. Validates the facility_type label and case-insensitive name uniqueness. perform_extra_deserialization resolves geo_organization (must be org_type="govt") and maps the facility_type label → int.
FacilityReadSpecread · listFacilityBaseSpec + features, cover_image_url, read_cover_image_url, geo_organization: dict, created_by: dict|null. perform_extra_serialization sets id=external_id, resolves the cover image URL, maps facility_type int → label, serializes geo_organization via OrganizationReadSpec, and resolves created_by via cached UserSpec.
FacilityRetrieveSpecread · detailExtends FacilityReadSpec + FacilityPermissionsMixin. Adds flags (from get_facility_flags()), facility + instance discount/tax/informational code lists, patient identifier configs, invoice_number_expression, and print_templates: list[dict]. The permissions mixin adds permissions, root_org_permissions, child_org_permissions. Discount/invoice fields come from FacilityMonetoryConfig.get_monetory_config(); instance-level lists come from Django settings.
FacilityMinimalReadSpecread · minimalFacilityBaseSpec + features, cover_image_url, read_cover_image_url, geo_organization: dict. Same serialization as FacilityReadSpec minus created_by.
FacilityMonetaryCodeSpecwrite · billing configBound to FacilityMonetoryConfig. Validates ≤ 100 discount codes and ≤ 100 monetary components, forbids duplicate / system-redefining codes, and requires every monetary-component code to be a defined facility or system code.
FacilityInvoiceExpressionSpecwrite · invoice configinvoice_number_expression: str, validated by evaluate_invoice_dummy_expression (raises "Invalid Expression").

Validation & server-side behaviour

  • name — unique case-insensitively (Lower(Trim(name))); on update the current object (from context) is excluded from the check.
  • facility_type — write accepts the label; an unknown label is rejected with the sorted valid-label list. Stored as the integer code; read maps it back to the label.
  • geo_organization — write is a UUID4 resolved to an Organization filtered by org_type="govt", falling to None if no match. Read embeds the full OrganizationReadSpec.
  • featureslist[int]; each int should correspond to a FacilityFeature value.
  • Bound value sets / coded fields — discount/tax/informational components use Coding (extra="forbid"); MonetaryComponentDefinition enforces no base type, the single-base rules inherited from MonetaryComponent, and amount/factor mutual exclusion.
  • cover_image_url — stores an object key; read_cover_image_url is the resolved URL, present only on read specs.
  • PermissionsFacilityRetrieveSpec computes them per requesting user from facility root and sub-org roles (FacilityAccess); can_update_facility is excluded from child_org_permissions.

Facility's relationships resolve through models in adjacent modules:

geo_organization → FK emr.Organization (SET_NULL) # write: UUID4 of a govt org
default_internal_organization → FK emr.FacilityOrganization (SET_NULL) # auto-provisioned root org
created_by → FK users.User (SET_NULL)
FacilityMonetoryConfig → OneToOne facility.Facility (CASCADE) # billing/discount config

On creation, save() provisions a root FacilityOrganization named Administration (org_type="root", system_generated=True) and a FacilityOrganizationUser linking created_by to it with the FACILITY_ADMIN_ROLE. Feature flags live in FacilityFlag and surface via get_facility_flags().

Methods & save behaviour

Facility overrides save() and adds a few helpers.

MemberBehaviour
save()On create only: persists the row, then creates the root Administration FacilityOrganization, sets it as default_internal_organization (an extra write), and creates a FacilityOrganizationUser granting created_by the FACILITY_ADMIN_ROLE. On every save: calls sync_cache().
sync_cache()Rebuilds geo_organization_cache (parent chain of geo_organization + its id) and internal_organization_cache (all FacilityOrganization parent chains + ids, de-duplicated), then persists only those two fields via update_fields.
read_cover_image_url()Resolves cover_image_url to a full URL via settings.FACILITY_CDN, else the S3 external endpoint/bucket; returns None when unset.
get_facility_flags()Delegates to FacilityFlag.get_all_flags(self.id) (cache-backed).
__str__()Returns name.

Creating a facility through the ORM costs three write passes: the initial insert, the default_internal_organization update, and the sync_cache() write of the two cache columns.

Deletes are soft (inherited BaseModel.delete() sets deleted=True); the row and its child organizations are retained.

API integration notes

  • external_id (UUID) is the public identifier, exposed as id by every read spec — the integer pk stays internal.
  • facility_type crosses the wire as the label string (e.g. "Private Hospital"), validated against REVERSE_REVERSE_FACILITY_TYPES; the integer code is storage-only.
  • features are integer FacilityFeature codes; map them to labels client-side.
  • geo_organization is written as the org's UUID (must be a govt org) and read as an embedded OrganizationReadSpec object.
  • print_templates follows the typed PrintTemplate shape on write and comes back as list[dict] on retrieve.
  • geo_organization_cache / internal_organization_cache are platform-maintained — never set them from clients; they rebuild on every save.
  • Creating a facility automatically provisions its root Administration organization and an admin membership for created_by; don't create these manually.
  • cover_image_url stores an object key; render read_cover_image_url instead.
  • Discount/tax/invoice billing config is managed through FacilityMonetaryCodeSpec / FacilityInvoiceExpressionSpec and surfaced on FacilityRetrieveSpec; it's backed by FacilityMonetoryConfig.
  • FacilityRetrieveSpec also returns the requesting user's permissions and feature flags; flags come via get_facility_flags() rather than a row-by-row query.