Skip to main content
Version: 3.1

Payment Reconciliation

A PaymentReconciliation is a single payment recorded against an Account, and optionally allocated to one Invoice. It captures how much was paid, by what method, when, and by whom — mirroring the FHIR PaymentReconciliation resource. Reach for it whenever you post a payment, refund, credit note, or adjustment to a patient or insurer account.

Source:

The model is thin storage: enum-constrained fields are plain CharFields and extensions is an opaque JSONField. The behaviour you integrate against — enum constraints, amount computation, validation, and the read/write schema split — lives in the Pydantic resource specs (care/emr/resources/payment_reconciliation/spec.py), built on EMRResource.

Models

ModelPurpose
PaymentReconciliationA single payment (or credit note / adjustment) recorded against an account or invoice

The model extends EMRBaseModel, the shared Care EMR base supplying external_id, audit fields, and soft-delete semantics — see Base model.

PaymentReconciliation fields

Relationships

FieldTypeNotes
facilityFK → Facility (PROTECT)Facility where the payment is recorded
target_invoiceFK → Invoice (PROTECT)The invoice this payment is allocated to. Nullable (default=None); one allocation per record. On write, pass the invoice external_id (UUID)
accountFK → Account (PROTECT)The account being paid. Required even when target_invoice is set. On write, pass the account external_id (UUID)
locationFK → FacilityLocation (PROTECT)Physical location (e.g. the counter) where payment was taken. Nullable. On write, pass the location external_id (UUID)

Classification

Six columns map to fixed enums defined in the resource spec. Each is stored as a free-form CharField(100); the enum is enforced at the schema/API layer, not by the database. Full value lists are under enum values.

FieldTypeSpec enumNotes
reconciliation_typeCharField(100)PaymentReconciliationTypeOptionsRequired. payment | adjustment | advance
statusCharField(100)PaymentReconciliationStatusOptionsRequired. active | cancelled | draft | entered_in_error
kindCharField(100)PaymentReconciliationKindOptionsRequired. deposit | periodic_payment | online | kiosk
issuer_typeCharField(100)PaymentReconciliationIssuerTypeOptionsRequired. Who paid — patient | insurer
outcomeCharField(100)PaymentReconciliationOutcomeOptionsRequired. queued | complete | error | partial
methodCharField(100)PaymentReconciliationPaymentMethodOptionsRequired. HL7 v2-0570 payment-method codes — cash | ccca | cchk | cdac | chck | ddpo | debc

Payment details

FieldTypeNotes
payment_datetimeDateTimeFielddatetime | NoneWhen the payment was issued. Optional (default None)
reference_numberCharField(1024)str | NoneCheque number or payment reference. Optional
authorizationCharField(1024)str | NoneAuthorization number. Optional
dispositionTextFieldstr | NoneMessage describing the outcome. Optional
noteTextFieldstr | NoneFree-text note. Optional

Amounts

Every amount is a DecimalField(max_digits=20, decimal_places=6). Six decimal places allow sub-currency precision.

FieldTypeNotes
tendered_amountDecimal(20, 6)Required on write. Amount offered by the issuer
returned_amountDecimal(20, 6)Required on write. Amount returned to the issuer (e.g. change). Must be strictly less than tendered_amount, or the write is rejected
amountDecimal(20, 6)Net payment, computed server-side as tendered_amount - returned_amount. A client-supplied value is always overwritten

Flags & extensions

FieldTypeNotes
is_credit_noteBooleanFieldboolDefault False. Marks the record as a credit note rather than an inbound payment
extensionsJSONFielddictDefault {}. Open extension bag. On write, each key is validated against the registered handler for resource type payment_reconciliation — unknown keys are currently dropped. On retrieve, rendered through those handlers

Enum values

All enums are str, Enum classes in spec.py.

PaymentReconciliationTypeOptions (reconciliation_type)

Value
payment
adjustment
advance

PaymentReconciliationStatusOptions (status)

Value
active
cancelled
draft
entered_in_error

PaymentReconciliationKindOptions (kind)

Value
deposit
periodic_payment
online
kiosk

PaymentReconciliationIssuerTypeOptions (issuer_type)

Value
patient
insurer

PaymentReconciliationOutcomeOptions (outcome)

Value
queued
complete
error
partial

PaymentReconciliationPaymentMethodOptions (method)

HL7 v2-0570 payment-method codes.

ValueHL7 meaning
cashCash
cccaCredit card
cchkCashier's check
cdacCredit/debit account
chckCheck
ddpoDirect deposit
debcDebit card

Resource specs (API schema)

Defined in care/emr/resources/payment_reconciliation/spec.py. All extend EMRResource (serialize / de_serialize, with perform_extra_serialization / perform_extra_deserialization hooks). See Base model.

Spec classRoleNotes
BasePaymentReconciliationSpecshared baseHolds id, the six enum fields, disposition, payment_datetime, method, reference_number, authorization, note. __exclude__ = ["target_invoice", "account"] (FKs handled in hooks). ___extension_resource_type__ = payment_reconciliation
PaymentReconciliationWriteSpecwrite · create & updateAdds target_invoice (UUID, optional), account (UUID, required), tendered_amount, returned_amount, amount (optional), is_credit_note (default False), location (UUID, optional). Mixes in ExtensionValidator
PaymentReconciliationMinimalReadSpecread · listAdds amount, tendered_amount, returned_amount, is_credit_note, created_date, modified_date
PaymentReconciliationReadSpecread · detail (nested)Extends minimal read; adds account: dict (serialized via AccountReadSpec) and target_invoice: dict | None (serialized via InvoiceReadSpec)
PaymentReconciliationRetrieveSpecread · full retrieveExtends read; adds location: dict | None (via FacilityLocationListSpec), extensions: dict, created_by / updated_by. Also injects account.patient via PatientRetrieveSpec

Write validation & side effects

PaymentReconciliationWriteSpec runs three steps:

  • The amount validator (model_validator(mode="after"), check_amount_or_factor) raises "Returned amount cannot be greater than tendered amount" when returned_amount >= tendered_amount, then sets amount = tendered_amount - returned_amount. The spec's amount field is advisory and always recomputed.
  • perform_extra_deserialization(is_update, obj) resolves the FK UUIDs to model instances:
    • target_invoice (if provided) → Invoice.objects.get(external_id=...)
    • account (required) → Account.objects.get(external_id=...)
    • location (if provided) → FacilityLocation.objects.get(external_id=...)
  • ExtensionValidator.validate_extensions checks extensions against the payment_reconciliation extension registry before save.

Read serialization side effects

  • PaymentReconciliationMinimalReadSpec.perform_extra_serialization maps idobj.external_id.
  • PaymentReconciliationReadSpec serializes the related account (AccountReadSpec) and, when present, target_invoice (InvoiceReadSpec) into nested objects.
  • PaymentReconciliationRetrieveSpec additionally nests the account's patient (PatientRetrieveSpec, scoped to the account's facility), the location (FacilityLocationListSpec), the raw extensions, and audit users (created_by / updated_by via serialize_audit_users).

PaymentReconciliation is self-contained; its foreign keys reach into the billing and facility domains:

facility → FK Facility (PROTECT)
target_invoice → FK Invoice (PROTECT, nullable)
account → FK Account (PROTECT)
location → FK FacilityLocation (PROTECT, nullable)

Each foreign key uses on_delete=PROTECT, so a payment record blocks deletion of the facility, invoice, account, or location it references.

Methods & save behaviour

PaymentReconciliation defines no custom save(); it inherits EMRBaseModel behaviour (audit fields, external_id, soft delete). Everything below happens in the write spec, not the model:

  • amount is derived, not stored as sent. On every create and update the write spec computes amount = tendered_amount - returned_amount and rejects returned_amount >= tendered_amount.
  • FK resolution happens in perform_extra_deserialization: external_id UUIDs for account, target_invoice, and location resolve to instances at de-serialization time.
  • extensions are validated against the payment_reconciliation registry on write and rendered through registered handlers on retrieve.

API integration notes

  • Create and update use PaymentReconciliationWriteSpec; list responses use PaymentReconciliationMinimalReadSpec; detail and retrieve responses use PaymentReconciliationReadSpec / PaymentReconciliationRetrieveSpec.
  • account is always required; target_invoice is optional and allocates the payment to one invoice. Pass account, target_invoice, and location as external_id UUIDs.
  • The Pydantic schema constrains the six classification fields (reconciliation_type, status, kind, issuer_type, outcome, method) to the enums above and rejects invalid values, even though each column is a plain CharField.
  • Send tendered_amount and returned_amount (both required); the server derives amount, so any value you send for it is ignored.
  • extensions holds custom key-value data without a schema migration, validated against registered handlers.