Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

References/Facility Config: Difference between revisions

From OHC Network Wiki
More languages
Content deleted Content added
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 &amp; 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>&gt;= 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>&quot;Invalid Expression&quot;</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: &lt;config&gt;}</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>&quot;&quot;</code>, producing no number.

== Methods &amp; 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 + &quot;/&quot; + 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}}