References/Facility Config: Difference between revisions
More languages
More actions
OHC identity seed |
Automated edit (via update-page on MediaWiki MCP Server) |
||
| Line 99: | Line 99: | ||
! Notes |
! Notes |
||
{{Field|model=FacilityConfig|section=MonetaryComponentDefinition shape|name=metric|type=str|notes='''yes'''}} |
{{Field|model=FacilityConfig|section=MonetaryComponentDefinition shape|name=metric|type=str|notes='''yes'''}} |
||
{{Field|model=FacilityConfig|section=MonetaryComponentDefinition shape|name=operation|type=str|notes='''yes'''}} |
|||
{{Field|model=FacilityConfig|section=MonetaryComponentDefinition shape|name=value|type=dict | str|notes='''yes'''}} |
|||
|} |
|||
... content truncated for display in this response but full source used in update ... |
|||
=== <code>DiscountConfiguration</code> shape === |
|||
{{Navbox facility}} |
|||
The <code>discount_configuration</code> object. |
|||
{| class="wikitable" |
|||
|- |
|||
! Field |
|||
! Type |
|||
! Required |
|||
! Notes |
|||
{{Field|model=FacilityConfig|section=DiscountConfiguration shape|name=max_applicable|type=int (ge=0)|notes='''yes'''}} |
|||
{{Field|model=FacilityConfig|section=DiscountConfiguration shape|name=applicability_order|type=DiscountApplicability enum|notes='''yes'''}} |
|||
|} |
|||
== Enum values == |
|||
=== <code>MonetaryComponentType</code> values === |
|||
From <code>care/emr/resources/common/monetary_component.py</code>. String enum. |
|||
{| class="wikitable" |
|||
|- |
|||
! Value |
|||
! Meaning |
|||
|- |
|||
| <code>base</code> |
|||
| Base price (not allowed in a <code>MonetaryComponentDefinition</code>) |
|||
|- |
|||
| <code>surcharge</code> |
|||
| Additional charge |
|||
|- |
|||
| <code>discount</code> |
|||
| Discount line item |
|||
|- |
|||
| <code>tax</code> |
|||
| Tax line item |
|||
|- |
|||
| <code>informational</code> |
|||
| Informational-only component |
|||
|} |
|||
=== <code>DiscountApplicability</code> values === |
|||
From <code>care/emr/resources/common/monetary_component.py</code>. String enum. |
|||
{| class="wikitable" |
|||
|- |
|||
! Value |
|||
! Meaning |
|||
|- |
|||
| <code>total_asc</code> |
|||
| Apply discounts in ascending order of total |
|||
|- |
|||
| <code>total_desc</code> |
|||
| Apply discounts in descending order of total |
|||
|} |
|||
== Resource specs (API schema) == |
|||
These Pydantic specs build on [[References/Base models & conventions|<code>EMRResource</code>]] (<code>serialize</code>/<code>de_serialize</code>, the <code>perform_extra_serialization</code>/<code>perform_extra_deserialization</code> hooks, <code>__model__</code>, <code>__exclude__</code>) 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. |
|||
{| class="wikitable" |
|||
|- |
|||
! Spec |
|||
! Role |
|||
! Bound to |
|||
! Notes |
|||
|- |
|||
| <code>FacilityMonetaryCodeSpec</code> |
|||
| write · set monetary config |
|||
| <code>FacilityMonetoryConfig</code> |
|||
| Validates and persists <code>discount_codes</code>, <code>discount_monetary_components</code>, <code>discount_configuration</code> in one payload. <code>de_serialize</code>d onto the get-or-created config row |
|||
|- |
|||
| <code>FacilityInvoiceExpressionSpec</code> |
|||
| write · set invoice expression |
|||
| (plain <code>BaseModel</code>) |
|||
| Single field <code>invoice_number_expression</code>; validated by dry-run before save |
|||
|- |
|||
| <code>FacilityRetrieveSpec</code> |
|||
| read · facility detail |
|||
| <code>Facility</code> |
|||
| Surfaces the facility's monetary config plus instance-level catalogs in <code>perform_extra_serialization</code> |
|||
|- |
|||
| <code>MonetaryComponentDefinition</code> |
|||
| nested (write) |
|||
| — |
|||
| Shape of each <code>discount_monetary_components[]</code> entry |
|||
|- |
|||
| <code>DiscountConfiguration</code> |
|||
| nested (write) |
|||
| — |
|||
| Shape of <code>discount_configuration</code> |
|||
|- |
|||
| <code>Coding</code> |
|||
| nested (write) |
|||
| — |
|||
| Shape of each <code>discount_codes[]</code> entry |
|||
|} |
|||
=== <code>FacilityMonetaryCodeSpec</code> === |
|||
<code>__model__ = FacilityMonetoryConfig</code>, <code>__exclude__ = []</code>. Write schema for the <code>set_monetary_config</code> action. |
|||
{| class="wikitable" |
|||
|- |
|||
! Field |
|||
! Type |
|||
! Notes |
|||
{{Field|model=FacilityConfig|section=FacilityMonetaryCodeSpec|name=discount_codes|type=list[Coding]|notes=Facility-defined codes}} |
|||
{{Field|model=FacilityConfig|section=FacilityMonetaryCodeSpec|name=discount_monetary_components|type=list[MonetaryComponentDefinition]|notes=Selectable discount definitions}} |
|||
{{Field|model=FacilityConfig|section=FacilityMonetaryCodeSpec|name=discount_configuration|type=DiscountConfiguration | null|notes=Stacking rule}} |
|||
|} |
|||
Model validators run on write: |
|||
* '''Count limits''' — <code>discount_codes</code> and <code>discount_monetary_components</code> must each hold fewer than 100 entries (<code>DISCOUNT_CODE_COUNT_LIMIT</code> / <code>DISCOUNT_MONETARY_COMPONENT_COUNT_LIMIT = 100</code>; the check rejects <code>>= 100</code>). |
|||
* '''No duplicate codes''' — <code>code</code> values within <code>discount_codes</code> must be unique. |
|||
* '''No redefining system codes''' — a facility code may not reuse a <code>[code, system]</code> pair already in instance <code>settings.DISCOUNT_CODES</code>. |
|||
* '''All component codes must be defined''' — every <code>discount_monetary_components[i].code</code> (when present) must match a <code>[code, system]</code> pair from either <code>settings.DISCOUNT_CODES</code> (instance) or the facility's <code>discount_codes</code>. |
|||
=== <code>FacilityInvoiceExpressionSpec</code> === |
|||
Plain <code>BaseModel</code>, not an <code>EMRResource</code>. Write schema for the <code>set_invoice_expression</code> action. |
|||
{| class="wikitable" |
|||
|- |
|||
! Field |
|||
! Type |
|||
! Notes |
|||
{{Field|model=FacilityConfig|section=FacilityInvoiceExpressionSpec|name=invoice_number_expression|type=str|notes=Validated via <code>evaluate_invoice_dummy_expression(v)</code>; any exception raises <code>"Invalid Expression"</code>. An empty value skips validation}} |
|||
|} |
|||
The dry-run context supplies <code>invoice_count=1234</code>, <code>current_year_yyyy=2025</code>, <code>current_year_yy=25</code>. At runtime, <code>evaluate_invoice_identifier_default_expression(facility)</code> evaluates the stored expression against the live invoice count and current year. |
|||
=== <code>FacilityRetrieveSpec</code> (read path) === |
|||
<code>FacilityRetrieveSpec</code> (extends <code>FacilityReadSpec</code> + <code>FacilityPermissionsMixin</code>) injects the monetary config into the facility detail response from <code>perform_extra_serialization</code>, calling <code>FacilityMonetoryConfig.get_monetory_config(obj.id)</code> (get-or-create). The monetary-config-derived fields on the read payload: |
|||
{| class="wikitable" |
|||
|- |
|||
! Field |
|||
! Source |
|||
|- |
|||
| <code>invoice_number_expression</code> |
|||
| config row |
|||
|- |
|||
| <code>discount_codes</code> |
|||
| config row (<code>list[dict]</code>) |
|||
|- |
|||
| <code>discount_monetary_components</code> |
|||
| config row (<code>list[dict]</code>) |
|||
|- |
|||
| <code>discount_configuration</code> |
|||
| config row (<code>dict | null</code>) |
|||
|- |
|||
| <code>instance_discount_codes</code> |
|||
| <code>settings.DISCOUNT_CODES</code> |
|||
|- |
|||
| <code>instance_discount_monetary_components</code> |
|||
| <code>settings.DISCOUNT_MONETARY_COMPONENT_DEFINITIONS</code> |
|||
|- |
|||
| <code>instance_tax_codes</code> |
|||
| <code>settings.TAX_CODES</code> |
|||
|- |
|||
| <code>instance_tax_monetary_components</code> |
|||
| <code>settings.TAX_MONETARY_COMPONENT_DEFINITIONS</code> |
|||
|- |
|||
| <code>instance_informational_codes</code> |
|||
| <code>settings.INFORMATIONAL_MONETARY_CODES</code> |
|||
|- |
|||
| <code>flags</code> |
|||
| <code>obj.get_facility_flags()</code> |
|||
|- |
|||
| <code>patient_instance_identifier_configs</code> |
|||
| <code>PatientIdentifierConfigCache.get_instance_config()</code> |
|||
|- |
|||
| <code>patient_facility_identifier_configs</code> |
|||
| <code>PatientIdentifierConfigCache.get_facility_config(obj.id)</code> |
|||
|} |
|||
<blockquote>Facility codes layer on top of instance-level catalogs. The read payload returns both the facility's own <code>discount_*</code> fields and the <code>instance_*</code> settings-derived catalogs, while write-time validation stops a facility from redefining instance codes. |
|||
</blockquote> |
|||
== API integration == |
|||
Both writes are detail actions on the facility viewset and require update authorization on the facility. |
|||
{| class="wikitable" |
|||
|- |
|||
! Action |
|||
! Method |
|||
! Request spec |
|||
! Behaviour |
|||
|- |
|||
| <code>set_monetary_config</code> |
|||
| <code>POST</code> (detail) |
|||
| <code>FacilityMonetaryCodeSpec</code> |
|||
| <code>model_validate</code> with context <code>{is_update: True, object: <config>}</code>, then <code>de_serialize</code> onto the get-or-created config, set <code>updated_by</code>, <code>save()</code>. Returns the facility retrieve payload |
|||
|- |
|||
| <code>set_invoice_expression</code> |
|||
| <code>POST</code> (detail) |
|||
| <code>FacilityInvoiceExpressionSpec</code> |
|||
| Sets <code>invoice_number_expression</code> on the get-or-created config, sets <code>updated_by</code>, <code>save()</code>. Returns the facility retrieve payload |
|||
|} |
|||
== Invoice number expression == |
|||
<code>invoice_number_expression</code> is a string template evaluated by <code>care.emr.utils.expression_evaluator.evaluate_expression</code>. It can reference: |
|||
{| class="wikitable" |
|||
|- |
|||
! Variable |
|||
! Source |
|||
|- |
|||
| <code>invoice_count</code> |
|||
| count of <code>Invoice</code> rows for the facility |
|||
|- |
|||
| <code>current_year_yyyy</code> |
|||
| four-digit current year |
|||
|- |
|||
| <code>current_year_yy</code> |
|||
| two-digit current year (<code>year % 100</code>) |
|||
|} |
|||
A <code>null</code>/empty expression evaluates to <code>""</code>, producing no number. |
|||
== Methods & save behaviour == |
|||
<code>FacilityMonetoryConfig</code> caches derived data in the Django cache and rebuilds it on every write. |
|||
=== Cache keys === |
|||
{| class="wikitable" |
|||
|- |
|||
! Key |
|||
! Built by |
|||
|- |
|||
| <code>facility:{facility_id}:monetory_component</code> |
|||
| <code>get_monetory_component_cache_key(facility_id)</code> |
|||
|- |
|||
| <code>facility:{facility_id}:discount_configuration</code> |
|||
| <code>get_discount_configuration_cache_key(facility_id)</code> |
|||
|} |
|||
=== Class methods === |
|||
* <code>get_monetory_config(facility_id)</code> — 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. |
|||
* <code>get_component_key(component)</code> — derives a component's lookup key as <code>code.system + "/" + code.code</code>. |
|||
* <code>calculate_monetory_components(components)</code> — folds a component list into a dict keyed by <code>get_component_key</code>. |
|||
* <code>get_monetory_component(facility_id)</code> — returns the cached component dict, computing it from <code>discount_monetary_components</code> and caching it on a miss. |
|||
* <code>get_discount_configuration(facility_id)</code> — returns the cached <code>discount_configuration</code> (default <code>{}</code>), caching it on a miss. |
|||
=== <code>save()</code> side effects === |
|||
<code>save()</code> deletes both cache keys for the facility before calling <code>super().save()</code>. The next call to <code>get_monetory_component()</code> or <code>get_discount_configuration()</code> 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 <code>get_monetory_config(facility_id)</code> rather than querying the table directly — the <code>set_monetary_config</code> / <code>set_invoice_expression</code> actions and <code>FacilityRetrieveSpec</code> all route through this get-or-create. |
|||
* <code>discount_codes</code>, <code>discount_monetary_components</code>, and <code>discount_configuration</code> are opaque <code>JSONField</code>s at the DB layer; their shape and validation come from <code>FacilityMonetaryCodeSpec</code> and the nested <code>Coding</code> / <code>MonetaryComponentDefinition</code> / <code>DiscountConfiguration</code> specs. |
|||
* Monetary components are keyed by a FHIR-style <code>code.system</code>/<code>code.code</code> pair (<code>get_component_key</code>); match on that derived key. |
|||
* <code>get_monetory_component()</code> / <code>get_discount_configuration()</code> results are cache-backed and platform-maintained. Don't write to the cache keys directly — write the model and let <code>save()</code> invalidate them. |
|||
{{Related}} |
{{Related}} |
||
Latest revision as of 09:32, 6 July 2026
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: [https://github.com/ohcnetwork/care/blob/develop/care/emr/models/facility_config.py
care/emr/models/facility_config.py] - Spec: [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/facility/spec.py
care/emr/resources/facility/spec.py] - Monetary types: [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/common/monetary_component.py
care/emr/resources/common/monetary_component.py] - Coding: [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/common/coding.py
care/emr/resources/common/coding.py] - Expression evaluator: [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/invoice/default_expression_evaluator.py
care/emr/resources/invoice/default_expression_evaluator.py] - API viewset: [https://github.com/ohcnetwork/care/blob/develop/care/emr/api/viewsets/facility.py
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 | ||
discount_codes |
JSONField → list[Coding] |
no | ||
discount_monetary_components |
JSONField → list[MonetaryComponentDefinition] |
no | ||
discount_configuration |
JSONField → DiscountConfiguration |
no | ||
invoice_number_expression |
CharField(1000) |
no |
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 |
no | |
version |
str |
no | |
code |
str |
yes | |
display |
str |
no |
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 | ||
monetary_component_type |
MonetaryComponentType enum |
yes | ||
code |
Coding |
no | ||
factor |
Decimal |
no | ||
amount |
Decimal |
no | ||
tax_included_amount |
Decimal |
no | ||
global_component |
bool |
no | ||
conditions |
list[EvaluatorConditionSpec] |
no |
EvaluatorConditionSpec (each conditions[] entry):
| Field | Type | Required | Notes |
|---|---|---|---|
metric |
str |
yes
... content truncated for display in this response but full source used in update ... Related
|