Skip to main content
Version: 3.0

Facility Config

Holds one facility's billing configuration: a discount-code catalog, discount component definitions, a discount-stacking rule, and the invoice-number template. The Django model FacilityMonetoryConfig stores all of it, but three fields are plain JSONFields whose shape, enums, and validation live in the Pydantic specs that drive the API. Both layers are documented here.

Sources:

Models

ModelPurpose
FacilityMonetoryConfigPer-facility billing config: facility-scoped discount codes, discount monetary component definitions, a discount-stacking rule, and the invoice-number expression

FacilityMonetoryConfig extends EMRBaseModel, so it inherits external_id, created_date/modified_date, soft-delete via deleted, created_by/updated_by, and the history/meta JSON fields.

FacilityMonetoryConfig fields

FieldTypeRequiredDefaultNotes
facilityOneToOneField → Facilityyeson_delete=CASCADE, unique=True. Exactly one config per facility
discount_codesJSONFieldlist[Coding]no[]Catalog of facility-defined discount codes, each a Coding. Capped at 100. Codes are free-form but must not duplicate each other or redefine instance settings.DISCOUNT_CODES
discount_monetary_componentsJSONFieldlist[MonetaryComponentDefinition]no[]Selectable discount line items, each a MonetaryComponentDefinition. Capped at 100. Every code referenced here must resolve to an instance discount code or a facility discount_codes entry
discount_configurationJSONFieldDiscountConfiguration | nullno{}Discount-stacking rule; see DiscountConfiguration. null/empty reads back as {}
invoice_number_expressionCharField(1000)noNoneNullable/blank. Template for generated invoice numbers; validated by dry-running it against a dummy context (see Invoice expression)

discount_codes, discount_monetary_components, and discount_configuration are declared as plain JSONFields. The database accepts any JSON — their structure is enforced only at the API layer by the Pydantic specs below.

JSON field shapes

Coding shape

Each discount_codes[] entry is a Coding object (model_config = extra="forbid", so unknown keys are rejected).

FieldTypeRequiredNotes
systemstr | nullnoCode system URI
versionstr | nullnoCode system version
codestryesThe code value
displaystr | nullnoHuman-readable label

MonetaryComponentDefinition shape

Each discount_monetary_components[] entry subclasses MonetaryComponent and adds title. Definition mode overrides or disables the base duplicate-code and amount-or-factor checks, but rejects a base-typed component outright.

FieldTypeRequiredDefaultNotes
titlestryesDisplay title, added by the definition subclass
monetary_component_typeMonetaryComponentType enumyesSee enum values. base is not allowed in a definition
codeCoding | nullnonullMust resolve against allowed codes (instance + facility) per FacilityMonetaryCodeSpec validation
factorDecimal | nullnonullmax_digits=20, decimal_places=6. Mutually exclusive with amount
amountDecimal | nullnonullmax_digits=20, decimal_places=6. Mutually exclusive with factor
tax_included_amountDecimal | nullnonullmax_digits=20, decimal_places=6. On the base MonetaryComponent this is allowed only when type is base; moot for definitions, which reject base
global_componentboolnofalseApplies the component globally rather than per-charge
conditionslist[EvaluatorConditionSpec]no[]Applicability conditions; each metric, operation, value is validated against the evaluator-metrics registry

EvaluatorConditionSpec (each conditions[] entry):

FieldTypeRequiredNotes
metricstryesMust be a registered evaluator metric, else "Invalid metric"
operationstryesValidated by the metric's validate_rule
valuedict | stryesValidated by the metric's validate_rule

DiscountConfiguration shape

The discount_configuration object.

FieldTypeRequiredNotes
max_applicableint (ge=0)yesMaximum number of discounts that may stack on a charge
applicability_orderDiscountApplicability enumyesOrder discounts are applied in; see enum values

Enum values

MonetaryComponentType values

From care/emr/resources/common/monetary_component.py. String enum.

ValueMeaning
baseBase price (not allowed in a MonetaryComponentDefinition)
surchargeAdditional charge
discountDiscount line item
taxTax line item
informationalInformational-only component

DiscountApplicability values

From care/emr/resources/common/monetary_component.py. String enum.

ValueMeaning
total_ascApply discounts in ascending order of total
total_descApply discounts in descending order of total

Resource specs (API schema)

These Pydantic specs build on EMRResource (serialize/de_serialize, the perform_extra_serialization/perform_extra_deserialization hooks, __model__, __exclude__) and define how monetary config is written and read. There is no standard CRUD endpoint for it — writes go through dedicated facility detail actions, and reads come back inside the facility retrieve payload.

SpecRoleBound toNotes
FacilityMonetaryCodeSpecwrite · set monetary configFacilityMonetoryConfigValidates and persists discount_codes, discount_monetary_components, discount_configuration in one payload. de_serialized onto the get-or-created config row
FacilityInvoiceExpressionSpecwrite · set invoice expression(plain BaseModel)Single field invoice_number_expression; validated by dry-run before save
FacilityRetrieveSpecread · facility detailFacilitySurfaces the facility's monetary config plus instance-level catalogs in perform_extra_serialization
MonetaryComponentDefinitionnested (write)Shape of each discount_monetary_components[] entry
DiscountConfigurationnested (write)Shape of discount_configuration
Codingnested (write)Shape of each discount_codes[] entry

FacilityMonetaryCodeSpec

__model__ = FacilityMonetoryConfig, __exclude__ = []. Write schema for the set_monetary_config action.

FieldTypeNotes
discount_codeslist[Coding]Facility-defined codes
discount_monetary_componentslist[MonetaryComponentDefinition]Selectable discount definitions
discount_configurationDiscountConfiguration | nullStacking rule

Model validators run on write:

  • Count limitsdiscount_codes and discount_monetary_components must each hold fewer than 100 entries (DISCOUNT_CODE_COUNT_LIMIT / DISCOUNT_MONETARY_COMPONENT_COUNT_LIMIT = 100; the check rejects >= 100).
  • No duplicate codescode values within discount_codes must be unique.
  • No redefining system codes — a facility code may not reuse a [code, system] pair already in instance settings.DISCOUNT_CODES.
  • All component codes must be defined — every discount_monetary_components[i].code (when present) must match a [code, system] pair from either settings.DISCOUNT_CODES (instance) or the facility's discount_codes.

FacilityInvoiceExpressionSpec

Plain BaseModel, not an EMRResource. Write schema for the set_invoice_expression action.

FieldTypeNotes
invoice_number_expressionstrValidated via evaluate_invoice_dummy_expression(v); any exception raises "Invalid Expression". An empty value skips validation

The dry-run context supplies invoice_count=1234, current_year_yyyy=2025, current_year_yy=25. At runtime, evaluate_invoice_identifier_default_expression(facility) evaluates the stored expression against the live invoice count and current year.

FacilityRetrieveSpec (read path)

FacilityRetrieveSpec (extends FacilityReadSpec + FacilityPermissionsMixin) injects the monetary config into the facility detail response from perform_extra_serialization, calling FacilityMonetoryConfig.get_monetory_config(obj.id) (get-or-create). The monetary-config-derived fields on the read payload:

FieldSource
invoice_number_expressionconfig row
discount_codesconfig row (list[dict])
discount_monetary_componentsconfig row (list[dict])
discount_configurationconfig row (dict | null)
instance_discount_codessettings.DISCOUNT_CODES
instance_discount_monetary_componentssettings.DISCOUNT_MONETARY_COMPONENT_DEFINITIONS
instance_tax_codessettings.TAX_CODES
instance_tax_monetary_componentssettings.TAX_MONETARY_COMPONENT_DEFINITIONS
instance_informational_codessettings.INFORMATIONAL_MONETARY_CODES
flagsobj.get_facility_flags()
patient_instance_identifier_configsPatientIdentifierConfigCache.get_instance_config()
patient_facility_identifier_configsPatientIdentifierConfigCache.get_facility_config(obj.id)

Facility codes layer on top of instance-level catalogs. The read payload returns both the facility's own discount_* fields and the instance_* settings-derived catalogs, while write-time validation stops a facility from redefining instance codes.

API integration

Both writes are detail actions on the facility viewset and require update authorization on the facility.

ActionMethodRequest specBehaviour
set_monetary_configPOST (detail)FacilityMonetaryCodeSpecmodel_validate with context {is_update: True, object: <config>}, then de_serialize onto the get-or-created config, set updated_by, save(). Returns the facility retrieve payload
set_invoice_expressionPOST (detail)FacilityInvoiceExpressionSpecSets invoice_number_expression on the get-or-created config, sets updated_by, save(). Returns the facility retrieve payload

Invoice number expression

invoice_number_expression is a string template evaluated by care.emr.utils.expression_evaluator.evaluate_expression. It can reference:

VariableSource
invoice_countcount of Invoice rows for the facility
current_year_yyyyfour-digit current year
current_year_yytwo-digit current year (year % 100)

A null/empty expression evaluates to "", producing no number.

Methods & save behaviour

FacilityMonetoryConfig caches derived data in the Django cache and rebuilds it on every write.

Cache keys

KeyBuilt by
facility:{facility_id}:monetory_componentget_monetory_component_cache_key(facility_id)
facility:{facility_id}:discount_configurationget_discount_configuration_cache_key(facility_id)

Class methods

  • get_monetory_config(facility_id) — returns the facility's config, creating an empty one if none exists. This get-or-create entry point backs both write actions and the retrieve serializer.
  • get_component_key(component) — derives a component's lookup key as code.system + "/" + code.code.
  • calculate_monetory_components(components) — folds a component list into a dict keyed by get_component_key.
  • get_monetory_component(facility_id) — returns the cached component dict, computing it from discount_monetary_components and caching it on a miss.
  • get_discount_configuration(facility_id) — returns the cached discount_configuration (default {}), caching it on a miss.

save() side effects

save() deletes both cache keys for the facility before calling super().save(). The next call to get_monetory_component() or get_discount_configuration() recomputes from the persisted row, so the cache invalidates on write and rebuilds lazily on the next read.

API integration notes

  • One config exists per facility. Read or auto-create it through get_monetory_config(facility_id) rather than querying the table directly — the set_monetary_config / set_invoice_expression actions and FacilityRetrieveSpec all route through this get-or-create.
  • discount_codes, discount_monetary_components, and discount_configuration are opaque JSONFields at the DB layer; their shape and validation come from FacilityMonetaryCodeSpec and the nested Coding / MonetaryComponentDefinition / DiscountConfiguration specs.
  • Monetary components are keyed by a FHIR-style code.system/code.code pair (get_component_key); match on that derived key.
  • get_monetory_component() / get_discount_configuration() results are cache-backed and platform-maintained. Don't write to the cache keys directly — write the model and let save() invalidate them.