References/Charge Item: Difference between revisions
More languages
More actions
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
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: [https://github.com/ohcnetwork/care/blob/develop/care/emr/models/charge_item.py
care/emr/models/charge_item.py] - Resource specs: [https://github.com/ohcnetwork/care/tree/develop/care/emr/resources/charge_item
care/emr/resources/charge_item/] ([https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/charge_item/spec.pyspec.py], [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/charge_item/sync_charge_item_costs.pysync_charge_item_costs.py], [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/charge_item/apply_charge_item_definition.pyapply_charge_item_definition.py], [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/charge_item/handle_charge_item_cancel.pyhandle_charge_item_cancel.py]) - Viewset: [https://github.com/ohcnetwork/care/blob/develop/care/emr/api/viewsets/charge_item.py
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
{{Field|model=ChargeItem|section=Description & status|name=status|type=CharField(255)|notes=Required; lifecycle status, constrained to |
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 |
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 |
|---|---|---|
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 References/Charge Item Definition |
InvoiceReadSpec
|
paid_invoice (read)
|
full nested invoice — see References/Invoice |
UserSpec
|
performer_actor, audit users (read)
|
see [[References/User & Skills |
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
Pages that link here:
- Reference: References/Base models & conventions
Wiki: ohcnwiki.tellmey.fyi