← Knowledge library

Billing

Invoice

An Invoice groups charge items raised against a single account into one billable document, with snapshotted line items, totals, and a lifecycle status. You touch it whenever you bill a patient, issue a refund, or reconcile payments. The data…

3.1 · versioned documentation · 8 min read

An Invoice groups charge items raised against a single account into one billable document, with snapshotted line items, totals, and a lifecycle status. You touch it whenever you bill a patient, issue a refund, or reconcile payments.

The data splits across two layers. The Django model is storage: columns plus several opaque JSONFields (charge_items_copy, total_price_components, lock_history) whose real structure lives in the Pydantic resource specs. The specs define the API request/response schemas, the InvoiceStatusOptions enum, the shape of those JSON fields, validation, and the server-side totalling and snapshot behaviour.

Source:

Models

Model Purpose
Invoice A billable grouping of charge items for one account, with snapshotted line items, totals, and status

Invoice extends EMRBaseModel (shared MANSOQ EMR base with external_id, audit fields, and soft-delete semantics).

On issue, the server computes net and gross totals and snapshots each charge item, so the invoice stays stable even when its charge items change later. The retrieve view reports payment reconciliation records — payments and credit notes — against the invoice.

Invoice fields

References

Field Type Notes
facility FK → facility.Facility (PROTECT) Facility where the invoice is created
patient FK → emr.Patient (PROTECT) Entity that incurred the charges; set server-side from account.patient on write
account FK → emr.Account (PROTECT) Account being balanced by this invoice

Status & metadata

Field Type Spec / values Notes
status CharField(100) InvoiceStatusOptions enum — required on write Drives the charge item lifecycle. See values
title CharField(1024) str | None, default None Optional human-readable label
cancelled_reason TextField str | None Set when the invoice moves to cancelled
payment_terms TextField str | None Markdown
note TextField str | None Free-text comments; markdown
number CharField(1000) str | None, default None Auto-generated for return invoices via the facility's invoice_number_expression (see Methods)
issue_date DateTimeField datetime | None (tz-aware), default None Set when the invoice is issued
is_refund BooleanField bool, default False Flags a refund/return invoice; required for negative totals (see validation)

Charge items

Field Type Spec shape Notes
charge_items ArrayField[int], default [] write: list[UUID4]; storage: list of integer PKs Live references to the grouped ChargeItem rows. The client sends external UUIDs on write; the server resolves them to PKs
charge_items_copy JSONField, default [] list[dict] — array of serialized ChargeItemReadSpec Point-in-time snapshot of the line items, written by sync_invoice_items. Served as charge_items on retrieve for any non-draft invoice

Totals (server-maintained)

sync_invoice_items and calculate_charge_items_summary compute these. Never send them from a client.

Field Type Spec shape Notes
total_price_components JSONField, default {} retrieve: list[dict] of MonetaryComponent Aggregated monetary components across all charge items (one per type+code). See MonetaryComponent shape
total_net DecimalField(20, 6), default 0 Decimal(max_digits=20, decimal_places=6) Net total = base + surcharge − discount; tax excluded. Rounded with INVOICE_FINAL_AMOUNT_PRECISION / ..._ROUNDING_METHOD
total_gross DecimalField(20, 6), default 0 Decimal(max_digits=20, decimal_places=6) Gross total = net + tax. Same rounding

When locked is True, the read specs serialize both total_net and total_gross as 0.

Locking

Field Type Spec shape Notes
locked BooleanField, default False bool (read-only on read specs) Freezes the invoice against further edits; while set, totals serialize as 0
lock_history JSONField, default [] list[dict] — each entry { user, ... } Audit trail of lock/unlock events. On retrieve, each entry's user id is hydrated into a UserSpec dict

Enums

InvoiceStatusOptions values

Defined in care/emr/resources/invoice/spec.py. The API value is entered_in_error (underscore), not the FHIR-style hyphen.

Value Meaning
draft In progress; retrieve serializes live charge items, not the snapshot
issued Issued to the patient/payer
balanced Fully settled
cancelled Cancelled
entered_in_error Recorded in error

INVOICE_CANCELLED_STATUS = ["cancelled", "entered_in_error"] — the two terminal/void states.

MonetaryComponentType values

Defined in care/emr/resources/common/monetary_component.py. Each type contributes to net and gross differently during totalling.

Value Effect on totals
base Added to net (one base component per item; must carry an amount)
surcharge Added to net
discount Subtracted from net
tax Added to gross only (excluded from net)
informational Carried for display; not summed

Resource specs (API schema)

All specs subclass EMRResource (serialize / de_serialize, with perform_extra_serialization / perform_extra_deserialization hooks). BaseInvoiceSpec.__model__ = Invoice and __exclude__ = ["account", "charge_items"]; the hooks handle those two fields rather than the generic mapper.

Spec Role Fields beyond base Server behaviour
BaseInvoiceSpec shared id, title, status (required), cancelled_reason, payment_terms, note, issue_date, number Base for all specs
InvoiceWriteSpec write · create + update account: UUID4 (required), charge_items: list[UUID4] (default []) perform_extra_deserialization: resolves account from external_id, sets patient = account.patient, stages charge_items (rewritten in perform_create)
InvoiceReadSpec read · list total_net, total_gross, locked, created_date, modified_date, account: dict, is_refund perform_extra_serialization: id = external_id; account → AccountMinimalReadSpec; if locked, zero out total_net/total_gross
InvoiceRetrieveSpec read · detail (all read fields) + charge_items: list[dict], total_price_components: list[dict], created_by, updated_by, payments: list[dict], total_payments, credit_notes: list[dict], total_credit_notes, lock_history: list[dict] See retrieve serialization below

InvoiceWriteSpec handles both create and update — there's no separate Update spec. InvoiceRetrieveSpec extends InvoiceReadSpec.

InvoiceRetrieveSpec.perform_extra_serialization

MonetaryComponent shape

Stored inside total_price_components and charge_items_copy. Source: care/emr/resources/common/monetary_component.py.

MonetaryComponent {
  monetary_component_type: MonetaryComponentType   # required (base|surcharge|discount|tax|informational)
  code:                   Coding | None            # { system?, version?, code, display? }
  factor:                 Decimal(20,6) | None
  amount:                 Decimal(20,6) | None
  tax_included_amount:    Decimal(20,6) | None      # only valid on base
  global_component:       bool = False
  conditions:             list[EvaluatorConditionSpec] = []
}

Per-component validation: base must carry an amount and no conditions; tax_included_amount is allowed only on base; amount and factor are mutually exclusive; at least one of amount/factor is required, unless the component is a global_component with a code. Collection-level rules (MonetaryComponents) forbid duplicate codes, require exactly one base, and reconcile tax_included_amount against the sum of tax components.

Related models

Methods & save behaviour

API integration notes

Related

Source evidence

References are pinned to the private MANSOQ source repository. Repository access is required to open these links.