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:
- Model:
care/emr/models/facility_config.py - Spec:
care/emr/resources/facility/spec.py - Monetary types:
care/emr/resources/common/monetary_component.py - Coding:
care/emr/resources/common/coding.py - Expression evaluator:
care/emr/resources/invoice/default_expression_evaluator.py - API viewset:
care/emr/api/viewsets/facility.py
Models
| Model | Purpose |
|---|---|
FacilityMonetoryConfig | Per-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
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
facility | OneToOneField → Facility | yes | — | on_delete=CASCADE, unique=True. Exactly one config per facility |
discount_codes | JSONField → list[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_components | JSONField → list[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_configuration | JSONField → DiscountConfiguration | null | no | {} | Discount-stacking rule; see DiscountConfiguration. null/empty reads back as {} |
invoice_number_expression | CharField(1000) | no | None | Nullable/blank. Template for generated invoice numbers; validated by dry-running it against a dummy context (see Invoice expression) |
discount_codes,discount_monetary_components, anddiscount_configurationare declared as plainJSONFields. 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).
| Field | Type | Required | Notes |
|---|---|---|---|
system | str | null | no | Code system URI |
version | str | null | no | Code system version |
code | str | yes | The code value |
display | str | null | no | Human-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.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
title | str | yes | — | Display title, added by the definition subclass |
monetary_component_type | MonetaryComponentType enum | yes | — | See enum values. base is not allowed in a definition |
code | Coding | null | no | null | Must resolve against allowed codes (instance + facility) per FacilityMonetaryCodeSpec validation |
factor | Decimal | null | no | null | max_digits=20, decimal_places=6. Mutually exclusive with amount |
amount | Decimal | null | no | null | max_digits=20, decimal_places=6. Mutually exclusive with factor |
tax_included_amount | Decimal | null | no | null | max_digits=20, decimal_places=6. On the base MonetaryComponent this is allowed only when type is base; moot for definitions, which reject base |
global_component | bool | no | false | Applies the component globally rather than per-charge |
conditions | list[EvaluatorConditionSpec] | no | [] | Applicability conditions; each metric, operation, value is validated against the evaluator-metrics registry |
EvaluatorConditionSpec (each conditions[] entry):
| Field | Type | Required | Notes |
|---|---|---|---|
metric | str | yes | Must be a registered evaluator metric, else "Invalid metric" |
operation | str | yes | Validated by the metric's validate_rule |
value | dict | str | yes | Validated by the metric's validate_rule |
DiscountConfiguration shape
The discount_configuration object.
| Field | Type | Required | Notes |
|---|---|---|---|
max_applicable | int (ge=0) | yes | Maximum number of discounts that may stack on a charge |
applicability_order | DiscountApplicability enum | yes | Order discounts are applied in; see enum values |
Enum values
MonetaryComponentType values
From care/emr/resources/common/monetary_component.py. String enum.
| Value | Meaning |
|---|---|
base | Base price (not allowed in a MonetaryComponentDefinition) |
surcharge | Additional charge |
discount | Discount line item |
tax | Tax line item |
informational | Informational-only component |
DiscountApplicability values
From care/emr/resources/common/monetary_component.py. String enum.
| Value | Meaning |
|---|---|
total_asc | Apply discounts in ascending order of total |
total_desc | Apply 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.
| Spec | Role | Bound to | Notes |
|---|---|---|---|
FacilityMonetaryCodeSpec | write · set monetary config | FacilityMonetoryConfig | Validates and persists discount_codes, discount_monetary_components, discount_configuration in one payload. de_serialized onto the get-or-created config row |
FacilityInvoiceExpressionSpec | write · set invoice expression | (plain BaseModel) | Single field invoice_number_expression; validated by dry-run before save |
FacilityRetrieveSpec | read · facility detail | Facility | Surfaces the facility's monetary config plus instance-level catalogs in perform_extra_serialization |
MonetaryComponentDefinition | nested (write) | — | Shape of each discount_monetary_components[] entry |
DiscountConfiguration | nested (write) | — | Shape of discount_configuration |
Coding | nested (write) | — | Shape of each discount_codes[] entry |
FacilityMonetaryCodeSpec
__model__ = FacilityMonetoryConfig, __exclude__ = []. Write schema for the set_monetary_config action.
| Field | Type | Notes |
|---|---|---|
discount_codes | list[Coding] | Facility-defined codes |
discount_monetary_components | list[MonetaryComponentDefinition] | Selectable discount definitions |
discount_configuration | DiscountConfiguration | null | Stacking rule |
Model validators run on write:
- Count limits —
discount_codesanddiscount_monetary_componentsmust each hold fewer than 100 entries (DISCOUNT_CODE_COUNT_LIMIT/DISCOUNT_MONETARY_COMPONENT_COUNT_LIMIT = 100; the check rejects>= 100). - No duplicate codes —
codevalues withindiscount_codesmust be unique. - No redefining system codes — a facility code may not reuse a
[code, system]pair already in instancesettings.DISCOUNT_CODES. - All component codes must be defined — every
discount_monetary_components[i].code(when present) must match a[code, system]pair from eithersettings.DISCOUNT_CODES(instance) or the facility'sdiscount_codes.
FacilityInvoiceExpressionSpec
Plain BaseModel, not an EMRResource. Write schema for the set_invoice_expression action.
| Field | Type | Notes |
|---|---|---|
invoice_number_expression | str | Validated 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:
| Field | Source |
|---|---|
invoice_number_expression | config row |
discount_codes | config row (list[dict]) |
discount_monetary_components | config row (list[dict]) |
discount_configuration | config row (dict | null) |
instance_discount_codes | settings.DISCOUNT_CODES |
instance_discount_monetary_components | settings.DISCOUNT_MONETARY_COMPONENT_DEFINITIONS |
instance_tax_codes | settings.TAX_CODES |
instance_tax_monetary_components | settings.TAX_MONETARY_COMPONENT_DEFINITIONS |
instance_informational_codes | settings.INFORMATIONAL_MONETARY_CODES |
flags | obj.get_facility_flags() |
patient_instance_identifier_configs | PatientIdentifierConfigCache.get_instance_config() |
patient_facility_identifier_configs | PatientIdentifierConfigCache.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 theinstance_*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.
| Action | Method | Request spec | Behaviour |
|---|---|---|---|
set_monetary_config | POST (detail) | FacilityMonetaryCodeSpec | model_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_expression | POST (detail) | FacilityInvoiceExpressionSpec | Sets 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:
| Variable | Source |
|---|---|
invoice_count | count of Invoice rows for the facility |
current_year_yyyy | four-digit current year |
current_year_yy | two-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
| Key | Built by |
|---|---|
facility:{facility_id}:monetory_component | get_monetory_component_cache_key(facility_id) |
facility:{facility_id}:discount_configuration | get_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 ascode.system + "/" + code.code.calculate_monetory_components(components)— folds a component list into a dict keyed byget_component_key.get_monetory_component(facility_id)— returns the cached component dict, computing it fromdiscount_monetary_componentsand caching it on a miss.get_discount_configuration(facility_id)— returns the cacheddiscount_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 — theset_monetary_config/set_invoice_expressionactions andFacilityRetrieveSpecall route through this get-or-create. discount_codes,discount_monetary_components, anddiscount_configurationare opaqueJSONFields at the DB layer; their shape and validation come fromFacilityMonetaryCodeSpecand the nestedCoding/MonetaryComponentDefinition/DiscountConfigurationspecs.- Monetary components are keyed by a FHIR-style
code.system/code.codepair (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 letsave()invalidate them.
Related
- Reference: Facility
- Reference: Invoice
- Reference: Charge Item
- Reference: Charge Item Definition
- Reference: Payment Reconciliation
- Reference: Base model
- Source: facility_config.py on GitHub
- Source: facility/spec.py on GitHub
- Source: common/monetary_component.py on GitHub