← Knowledge library

Billing

Account

An Account is the financial bucket that aggregates every transaction made against a patient within a facility. You touch it indirectly: charge items land on a patient's default account, and invoices and reconciliations settle against it.…

3.1 · versioned documentation · 8 min read

An Account is the financial bucket that aggregates every transaction made against a patient within a facility. You touch it indirectly: charge items land on a patient's default account, and invoices and reconciliations settle against it.

Several model fields are opaque JSONFields whose real structure lives in the Pydantic resource specs under care/emr/resources/account/. Each field below is paired with the spec-defined enums, JSON shapes, validation, and read/write schemas that govern it.

Source:

Models

Model Purpose
Account Aggregates charges and balances for one patient in one facility across encounters

Account extends EMRBaseModel, which contributes external_id, the audit fields created_date/modified_date, soft-delete via deleted, created_by/updated_by, and history/meta JSON.

Accounts are perpetual. One active, open account exists per patient per facility (see get_default_account), and charge items land on it. After discharge, the account can be balanced and closed. Invoices are raised against an account or against the items within it.

Account fields

Identity & scope

Field Type Required Notes
facility FK → facility.Facility (PROTECT) yes Server-set from the request scope; absent from every write spec
patient FK → emr.Patient (PROTECT) yes The entity that incurred the expenses. On create, pass a patient external_id (UUID); the server resolves it
name CharField(255) yes Human-readable label. The default account auto-names as "{patient.name} {YYYY-MM-DD}"
description TextField no Nullable free text for purpose or use
primary_encounter FK → emr.Encounter (SET_NULL, nullable) no Pins the account to a single encounter for reports, summaries, and insurance paperwork. Settable only through the update spec, as an encounter external_id

Status

Field Type Required Notes
status CharField(255) yes Lifecycle status from AccountStatusOptions (see values); the enum rejects out-of-set strings
billing_status CharField(255) yes Billing lifecycle status from AccountBillingStatusOptions (see values)
service_period JSONField (default {}) yes (on spec) Transaction window holding a PeriodSpec { start, end } — see service_period shape

Balances & totals

These decimal totals (max_digits=20, decimal_places=6, default 0) are platform-maintained aggregates that sync_account_items recomputes. None are client-writable, and none appear on a write spec.

Field Type Notes
total_net DecimalField Net total before adjustments. Not populated — the sync_account_items logic is commented out, so it holds the default 0
total_gross DecimalField Sum of total_price over charge items in paid or billed status
total_paid DecimalField Sum of active+complete payment reconciliations, minus credit-note reconciliations
total_balance DecimalField total_gross − total_paid
total_billable_charge_items DecimalField Sum of total_price over charge items in billable status
total_price_components JSONField (default {}) Intended price-component breakdown (taxes, discounts, etc.) as a list of MonetaryComponent (see below). Not populated — the sync logic is commented out
cached_items JSONField (default {}) Denormalized snapshot of the account's charge items, serialized as a list on retrieve. Not populated — the sync logic is commented out
calculated_at DateTimeField (nullable) When the balances were last computed; set to mansoq_now() on each sync

Tags & extensions

Field Type Notes
tags ArrayField[int] (default []) Tag IDs applied to the account; rendered through SingleFacilityTagManager on read
extensions JSONField (default {}) Open bag for deployment-specific metadata. ExtensionValidator checks it against ExtensionResource.account; surfaced only on the retrieve spec

Enum values

AccountStatusOptions values

Stored as the raw enum value string in status.

Value Meaning
active Open and in use (the default-account state)
inactive No longer actively used
entered_in_error Created in error
on_hold Temporarily paused

Values use underscores (entered_in_error, on_hold), not the FHIR-style hyphenated forms.

AccountBillingStatusOptions values

Stored as the raw enum value string in billing_status.

Value Meaning
open Billing open and accruing (the default-account state)
carecomplete_notbilled Care complete, not yet billed
billing Billing in progress
closed_baddebt Closed as bad debt
closed_voided Closed and voided
closed_completed Closed, billing completed
closed_combined Closed, combined into another account

JSON field shapes

service_period shape

service_period stores a PeriodSpec (from resources/base.py):

PeriodSpec {
  start: datetime | null   # ISO 8601, timezone-aware
  end:   datetime | null   # ISO 8601, timezone-aware
}

PeriodSpec.validate_period enforces three rules:

The default account sets only start, formatted as "%Y-%m-%dT%H:%M:%S.%fZ" from mansoq_now().

The FHIR-style Period common type ({ id?, start?, end? }, extra="forbid") is a related but distinct shape — Account binds service_period to PeriodSpec, not Period.

MonetaryComponent shape

total_price_components is meant to hold a list of MonetaryComponent entries, the same shape used across billing:

MonetaryComponent {
  monetary_component_type: base | surcharge | discount | tax | informational
  code:                    Coding | null
  factor:                  Decimal(max_digits=20, decimal_places=6) | null
  amount:                  Decimal(max_digits=20, decimal_places=6) | null
  tax_included_amount:     Decimal(max_digits=20, decimal_places=6) | null
  global_component:        bool = false
  conditions:              EvaluatorConditionSpec[] = []
}

MonetaryComponent validation: tax_included_amount is allowed only on a base component; a base component must carry an amount, no factor, and no conditions; amount and factor are mutually exclusive; exactly one of amount/factor is required unless the entry is a global_component with a code.

Resource specs (API schema)

The API layer lives in resources/account/spec.py. Every spec builds on EMRResource (serialize / de_serialize, with the perform_extra_serialization / perform_extra_deserialization hooks). AccountSpec sets __exclude__ = ["patient"] and binds ___extension_resource_type__ = ExtensionResource.account.

Spec class Role Fields exposed (beyond base)
AccountSpec shared base id, status, billing_status, name, service_period (PeriodSpec), description
AccountCreateSpec write · create base + patient (UUID, required)
AccountUpdateSpec write · update base + primary_encounter (UUID, optional)
AccountMinimalReadSpec read · minimal base + total_gross, total_paid, total_balance, total_billable_charge_items (all Decimal 20/6), calculated_at, created_date, modified_date
AccountReadSpec read · list minimal + patient (serialized PatientListSpec), tags (rendered tag dicts)
AccountRetrieveSpec read · detail read + patient (serialized PatientRetrieveSpec), primary_encounter (serialized EncounterRetrieveSpec when set), cached_items (list), total_price_components (dict), extensions (dict)

Validation & server-side behaviour

<a id="default-account"></a>

Default account

get_default_account(patient, facility) (default_account.py) returns the first account for that patient + facility with status = active and billing_status = open. If none exists, it creates one with:

This runs when the first charge item is added, so the default account materializes on demand. Clients normally never create accounts directly.

<a id="balance-sync"></a>

Balance sync

sync_account_items(account) (sync_items.py) recomputes the totals under an AccountLock:

Every sum runs through mansoq_round. total_net, cached_items, and total_price_components are not updated — their logic is commented out — so they hold their defaults. rebalance_account_task(account_id) is the Celery wrapper that runs the sync and saves.

Related models

Account is the only class in account.py. Its relationships:

facility          → FK facility.Facility (PROTECT)
patient           → FK emr.Patient (PROTECT)
primary_encounter → FK emr.Encounter (SET_NULL, nullable)

Charge items reference the account they belong to and drive its totals; invoices and payment reconciliations settle against the account or its items.

API integration notes

Related

Source evidence

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