Charge Item
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:
- Model:
care/emr/models/charge_item.py - Resource specs:
care/emr/resources/charge_item/(spec.py,sync_charge_item_costs.py,apply_charge_item_definition.py,handle_charge_item_cancel.py) - Viewset:
care/emr/api/viewsets/charge_item.py
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 |
status | CharField(255) | Required; lifecycle status, constrained to ChargeItemStatusOptions — see Status values |
code | JSONField | Nullable; a Coding that identifies the charge (e.g. a billing code), shape Coding { system?, version?, code, display? } — see Coding shape. No bound value set |
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 |
unit_price_components | JSONField | Required on write; list of MonetaryComponent — see MonetaryComponent shape. At most one base component; duplicate component codes are rejected |
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 | total_desc }. Populated from the definition/facility config when applied from a definition |
Service source
These fields trace the charge back to the resource that produced it.
| Field | Type | Notes |
|---|---|---|
service_resource | CharField(255) | 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 componentcodes are FHIR-alignedCodings with no bound value set.ChargeItemWriteSpec.perform_extra_deserializationresolvespatient/encounter/account/performer_actorfrom external ids; supplyingencounteroverridespatientwith the encounter's patient, andaccountis looked up scoped to that patient.ChargeItemReadSpec.perform_extra_serializationnests the fullChargeItemDefinitionReadSpecandInvoiceReadSpec, renders tags, and resolvesperformer_actor/created_by/updated_byfrom the user cache.
Nested / related specs
| 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 Charge Item Definition |
InvoiceReadSpec | paid_invoice (read) | full nested invoice — see Invoice |
UserSpec | performer_actor, audit users (read) | see User |
Related models
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 walksunit_price_components: multiplies thebaseamount byquantityto seedtotal_price/base, addssurcharges, appliesdiscounts (filtered bydiscount_configurationviaapply_discount_configuration), then addstaxes on the taxable price. It writes the resolved breakdown tototal_price_componentsand the sum tototal_price, and raisesValidationErroriftotal_price < 0on a non-reversal charge. - Apply from definition (
apply_charge_item_definition/POST apply_charge_item_defs) — builds aChargeItemfrom aChargeItemDefinition: copiestitle/description, merges category + global components, evaluates each component'sconditionsagainst patient/facility/encounter context (dropping unmet ones), setsstatus = billable, resolvesdiscount_configuration, defaults the account to the patient's default account, and runs cost sync.reversenegates component amounts to produce a credit. - Cancel (
handle_charge_item_cancel) — fires on update whenstatusmoves intoCHARGE_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, andpaid_invoice/paid_onare cleared. Cancelling a charge on a non-draft invoice raisesValidationError. 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 isbalanced/issued;statuscan't be moved by hand intobilled/paid; and if the source definition hascan_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 draftpaid_invoiceis re-synced. - Cancel authorization (
authorize_cancel) — cancellation is free withinCHARGE_ITEM_FREE_CANCEL_PERIOD_MINUTESof creation; after that it needs thecan_cancel_charge_item_in_facilitypermission. - Change account (
POST change_account) — bulk-moves up to 100billablecharge items to a target account, then queuesrebalance_account_taskfor every affected account.
API integration notes
- The viewset sets
facilityfrom the URL and ignores any body value;patientis derived fromencounterwhen one is supplied. - A charge item must resolve to an
account— with none supplied, the patient's default account is used.encounterandcharge_item_definitionare optional. total_price,total_price_components,paid_invoice, andpaid_onare 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_configurationandtagsare structured but deployment-tunable:discount_configurationfollowsDiscountConfiguration, and tags resolve through the single-facility tag manager.
Related
- Reference: Account
- Reference: Charge Item Definition
- Reference: Invoice
- Reference: Payment Reconciliation
- Reference: Patient
- Reference: Encounter
- Reference: User
- Reference: Base model
- Source: charge_item.py on GitHub
- Source: charge_item resource specs on GitHub