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

References/Charge Item: Difference between revisions

From OHC Network Wiki
Content deleted Content added
OHC identity seed
 
Automated edit (via update-page on MediaWiki MCP Server)
 
Line 343: Line 343:
* Create and update enforce that <code>performer_actor</code> (and, when applying definitions, the actor) belongs to the facility's organization.
* Create and update enforce that <code>performer_actor</code> (and, when applying definitions, the actor) belongs to the facility's organization.
* <code>discount_configuration</code> and <code>tags</code> are structured but deployment-tunable: <code>discount_configuration</code> follows <code>DiscountConfiguration</code>, and tags resolve through the single-facility tag manager.
* <code>discount_configuration</code> and <code>tags</code> are structured but deployment-tunable: <code>discount_configuration</code> follows <code>DiscountConfiguration</code>, and tags resolve through the single-facility tag manager.

{{Navbox billing}}


{{Related}}
{{Related}}
Wiki: ohcnwiki.tellmey.fyi

Latest revision as of 09:41, 6 July 2026

referencebillingCARE 3.0+

A ChargeItem is a single billable line for a service or product supplied to a patient — what was charged, the quantity, the unit and total price components, and any discounts or override reasons. You create one against a patient (and optionally an encounter); it always settles onto an Account.

Source:

The Django model is only the storage layer. Several fields are opaque JSONFields whose structure lives in the Pydantic resource specs under care/emr/resources/charge_item/, which also define the enums, validation, read/write API schemas, server-side cost calculation, and status side effects. The field tables below tell you what's stored; the spec and behaviour sections tell you what happens on write.

Models

Model Purpose
ChargeItem A single billable line item for a service or product rendered to a patient

ChargeItem extends EMRBaseModel (shared Care EMR base with external_id, audit fields — created_by/updated_by/created_date/modified_date — and soft-delete semantics — see Base model).

ChargeItem fields

Context & relationships

Field Type Notes
facility FK → facility.Facility (PROTECT) Set server-side from the URL; a body value is ignored
patient FK → emr.Patient (CASCADE) When you supply an encounter, the patient is taken from it rather than the request
encounter FK → emr.Encounter (CASCADE) Nullable; the encounter the charge relates to
charge_item_definition FK → emr.ChargeItemDefinition (CASCADE) Nullable; the template the charge was applied from. Not writable via create/update — set only by apply_charge_item_defs
account FK → emr.Account (CASCADE) The account this charge lands on. Falls back to the patient's default account when omitted

Description & status

Field Type Notes
title CharField(255) Required; display title for the charge
description TextField Nullable; longer description

{{Field|model=ChargeItem|section=Description & status|name=status|type=CharField(255)|notes=Required; lifecycle status, constrained to ChargeItemStatusOptions — see [[#status-values}} {{Field|model=ChargeItem|section=Description & status|name=code|type=JSONField|notes=Nullable; a Coding that identifies the charge (e.g. a billing code), shape Coding { system?, version?, code, display? } — see [[#coding-shape}}

note TextField Nullable; free-text (markdown) comments

Pricing

Field Type Notes
quantity DecimalField(20, 6) Required on write; the quantity serviced. Multiplies the base/per-unit components during cost sync

{{Field|model=ChargeItem|section=Pricing|name=unit_price_components|type=JSONField|notes=Required on write; list of MonetaryComponent — see [[#monetarycomponent-shape}}

total_price_components JSONField Nullable; server-computed list of MonetaryComponent (as dicts) — the resolved breakdown after base × quantity, surcharges, discounts, and taxes. Not client-writable
total_price DecimalField(20, 6) Nullable; server-computed total. Must be ≥ 0 unless the charge is a reversal
override_reason JSONField Nullable; ChargeItemOverrideReason { text: str, code?: Coding } explaining a list-price/factor override. No bound value set
discount_configuration JSONField Nullable (default None); DiscountConfiguration { max_applicable: int (≥0), applicability_order: total_asc

Service source

These fields trace the charge back to the resource that produced it.

Field Type Notes

{{Field|model=ChargeItem|section=Service source|name=service_resource|type=CharField(255)|notes=Nullable (default None); resource type, constrained to ChargeItemResourceOptions — see [[#service-resource-types}}

service_resource_id CharField(255) Nullable (default None); external id of the originating resource. Required once service_resource is set
performer_actor FK → users.User (CASCADE) Nullable (default None); the user who performed the service. Must belong to the facility's organization

Invoicing & tags

Field Type Notes
paid_invoice FK → emr.Invoice (CASCADE) Nullable (default None); denormalized link to the settling invoice. Platform-maintained; cleared on cancel
paid_on DateTimeField Nullable (default None); when the charge was paid. Platform-maintained; cleared on cancel
tags ArrayField[int] Tag IDs, default []. Managed through the tag mixin (SingleFacilityTagManager), serialized as objects on read

Enums

Status values

status is a plain CharField in storage, but every write passes through ChargeItemStatusOptions (care/emr/resources/charge_item/spec.py):

Value Meaning
billable Ready to be invoiced
not_billable Will not be billed
aborted Charge was cancelled before billing
billed Included on an invoice
paid Settled (links to paid_invoice / paid_on)
entered_in_error Recorded in error

(planned exists in the source as a commented-out option and is not currently selectable.)

not_billable, aborted, and entered_in_error form the cancelled set (CHARGE_ITEM_CANCELLED_STATUS). Moving into this set on update triggers the cancel side effect (see Methods & save behaviour). billed and paid cannot be set by hand — the viewset rejects manual transitions into them.

Service resource types

service_resource is constrained by ChargeItemResourceOptions:

Value
service_request
medication_dispense
appointment
bed_association

On create, the viewset validates the referenced resource: service_request must exist (and not be completed) for the patient/encounter, and bed_association must reference a FacilityLocationEncounter on a non-completed encounter in the facility.

MonetaryComponentType values

Used by every entry in unit_price_components / total_price_components (care/emr/resources/common/monetary_component.py):

Value Role in cost sync
base Per-unit base price. Exactly one allowed; must carry amount (not factor); no conditions. base.amount × quantity seeds the running total
surcharge Added on top of base; amount or factor (% of base)
discount Subtracted from the net price; amount or factor (% of net). Filtered/limited by discount_configuration
tax Added on the post-discount taxable price; amount or factor (% of taxable)
informational Carried through without affecting the computed total

DiscountApplicability values

discount_configuration.applicability_order (care/emr/resources/common/monetary_component.py):

Value Effect
total_asc Sort candidate discounts by amount ascending before applying max_applicable
total_desc Sort by amount descending before applying max_applicable

max_applicable = 0 drops all discounts.

JSON field shapes

Coding shape

code, plus the code field inside components and override_reason (care/emr/resources/common/coding.py, extra="forbid"):

Coding {
  system?:  str
  version?: str
  code:     str        # required
  display?: str
}

MonetaryComponent shape

Each entry of unit_price_components and total_price_components (care/emr/resources/common/monetary_component.py):

MonetaryComponent {
  monetary_component_type: base | surcharge | discount | tax | informational   # required
  code?:                Coding
  factor?:              Decimal(20,6)      # percentage; mutually exclusive with amount
  amount?:              Decimal(20,6)      # absolute; mutually exclusive with factor
  tax_included_amount?: Decimal(20,6)      # base component only
  global_component:     bool = False       # resolved against facility monetary config
  conditions:           [EvaluatorConditionSpec] = []   # not allowed on base
}

Validation: exactly one of amount/factor (unless global_component with a code); base must have amount, no factor, no conditions; tax_included_amount only on base; duplicate component codes are rejected.

EvaluatorConditionSpec { metric: str, operation: str, value: dict | str } — conditions are evaluated against patient/facility/encounter context when a definition is applied; a component whose conditions fail is dropped.

ChargeItemOverrideReason shape

override_reason (care/emr/resources/charge_item/spec.py):

ChargeItemOverrideReason {
  text: str            # required
  code?: Coding        # no bound value set
}

DiscountConfiguration shape

discount_configuration (care/emr/resources/common/monetary_component.py):

DiscountConfiguration {
  max_applicable:      int (>= 0)               # 0 drops all discounts
  applicability_order: total_asc | total_desc
}

Resource specs (API schema)

Charge items are exposed through ChargeItemViewSet (create / retrieve / update / upsert / list / tag actions, plus the apply_charge_item_defs and change_account custom actions). The Pydantic specs in care/emr/resources/charge_item/spec.py build on EMRResource (serialize / de_serialize, with perform_extra_serialization / perform_extra_deserialization hooks).

Spec Role Notes
ChargeItemSpec shared base __model__ = ChargeItem, __exclude__ = ["encounter", "account"]. Fields: id, title, description, status, code, quantity, unit_price_components, note, override_reason. Validators: no duplicate component codes, ≤1 base component
ChargeItemWriteSpec write · create Extends base with encounter, patient, account, service_resource, service_resource_id, performer_actor (all UUID4/enum, optional). Requires encounter or patient; requires service_resource_id when service_resource set
ChargeItemUpdateSpec write · update Base fields + performer_actor. Does not re-bind patient/encounter/account
ChargeItemReadSpec read · list & detail Base fields + server-side: total_price_components, total_price, charge_item_definition (nested), paid_invoice (nested), tags (objects), service_resource(_id), created_date, modified_date, paid_on, performer_actor, created_by, updated_by, discount_configuration

Notable validation and binding:

  • code, override_reason.code, and component codes are FHIR-aligned Codings with no bound value set.
  • ChargeItemWriteSpec.perform_extra_deserialization resolves patient/encounter/account/performer_actor from external ids; supplying encounter overrides patient with the encounter's patient, and account is looked up scoped to that patient.
  • ChargeItemReadSpec.perform_extra_serialization nests the full ChargeItemDefinitionReadSpec and InvoiceReadSpec, renders tags, and resolves performer_actor / created_by / updated_by from the user cache.
Spec Used by Shape
Coding code, component & override codes see [[#coding-shape
MonetaryComponent unit_price_components, total_price_components see [[#monetarycomponent-shape
ChargeItemOverrideReason override_reason { text, code? }
DiscountConfiguration discount_configuration { max_applicable, applicability_order }
ChargeItemDefinitionReadSpec charge_item_definition (read) full nested definition — see References/Charge Item Definition
InvoiceReadSpec paid_invoice (read) full nested invoice — see References/Invoice
UserSpec performer_actor, audit users (read) see [[References/User & Skills

ChargeItem is a leaf model — it holds foreign keys rather than owning child rows:

facility               → FK facility.Facility (PROTECT)
patient                → FK emr.Patient (CASCADE)
encounter              → FK emr.Encounter (CASCADE, nullable)
charge_item_definition → FK emr.ChargeItemDefinition (CASCADE, nullable)
account                → FK emr.Account (CASCADE)
paid_invoice           → FK emr.Invoice (CASCADE, nullable)
performer_actor        → FK users.User (CASCADE, nullable)

Methods & save behaviour

Cost resolution and status side effects live in the spec helpers and the viewset, not in the model's save().

  • Cost sync (sync_charge_item_costs) — runs on every create and on update (unless suppressed). It walks unit_price_components: multiplies the base amount by quantity to seed total_price/base, adds surcharges, applies discounts (filtered by discount_configuration via apply_discount_configuration), then adds taxes on the taxable price. It writes the resolved breakdown to total_price_components and the sum to total_price, and raises ValidationError if total_price < 0 on a non-reversal charge.
  • Apply from definition (apply_charge_item_definition / POST apply_charge_item_defs) — builds a ChargeItem from a ChargeItemDefinition: copies title/description, merges category + global components, evaluates each component's conditions against patient/facility/encounter context (dropping unmet ones), sets status = billable, resolves discount_configuration, defaults the account to the patient's default account, and runs cost sync. reverse negates component amounts to produce a credit.
  • Cancel (handle_charge_item_cancel) — fires on update when status moves into CHARGE_ITEM_CANCELLED_STATUS (not_billable / aborted / entered_in_error). If the charge sits on a draft invoice, it is removed from the invoice, the invoice is rebalanced and saved, and paid_invoice / paid_on are cleared. Cancelling a charge on a non-draft invoice raises ValidationError. Cost sync is skipped for cancellations.
  • Update guards (validate_data / perform_update) — a charge in a cancelled status can't be updated; updates are blocked when the linked invoice is balanced/issued; status can't be moved by hand into billed/paid; and if the source definition has can_edit_charge_item = False, the pricing fields (unit_price_components, total_price_components, total_price, quantity) are reset from the previous object and cost sync is skipped. After update, a draft paid_invoice is re-synced.
  • Cancel authorization (authorize_cancel) — cancellation is free within CHARGE_ITEM_FREE_CANCEL_PERIOD_MINUTES of creation; after that it needs the can_cancel_charge_item_in_facility permission.
  • Change account (POST change_account) — bulk-moves up to 100 billable charge items to a target account, then queues rebalance_account_task for every affected account.

API integration notes

  • The viewset sets facility from the URL and ignores any body value; patient is derived from encounter when one is supplied.
  • A charge item must resolve to an account — with none supplied, the patient's default account is used. encounter and charge_item_definition are optional.
  • total_price, total_price_components, paid_invoice, and paid_on are server-maintained; don't set them from clients.
  • Create and update enforce that performer_actor (and, when applying definitions, the actor) belongs to the facility's organization.
  • discount_configuration and tags are structured but deployment-tunable: discount_configuration follows DiscountConfiguration, and tags resolve through the single-facility tag manager.

Pages that link here:

Wiki: ohcnwiki.tellmey.fyi