What BR-CO-16 checks
BR-CO-16 closes the totals chain: Amount due for payment (BT-115) = Invoice total amount with VAT (BT-112) minus Paid amount (BT-113) plus Rounding amount (BT-114). BT-115 is the number payment runs and dunning processes act on, so the core model insists it be derivable from the declared totals rather than a free-floating figure. In UBL it is the cbc:PayableAmount of the legal monetary total, with BT-113 in cbc:PrepaidAmount and BT-114 in cbc:PayableRoundingAmount.
Layer and formats
An en16931 core layer rule for every syntax and profile. Its input BT-112 is policed by BR-CO-15, so chain failures cascade into this rule; a lone BR-CO-16 failure means the payable amount itself, the prepaid amount, or the rounding delta is off.
Why it fails in practice
- Prepayments applied to the wrong field. The deposit is subtracted from the gross total (breaking BR-CO-15) or from the line amounts instead of being declared in BT-113.
- BT-115 taken from accounts receivable. The open-item amount in the ledger includes credits or fees the invoice document knows nothing about.
- Rounding without declaring it. The payable amount is rounded for payment convenience, but the delta never lands in BT-114, so the equation misses by exactly that delta.
How to fix it
Derive BT-115 from the declared components:
<cac:LegalMonetaryTotal>
<cbc:TaxInclusiveAmount currencyID="EUR">1154.30</cbc:TaxInclusiveAmount>
<cbc:PrepaidAmount currencyID="EUR">500.00</cbc:PrepaidAmount>
<cbc:PayableAmount currencyID="EUR">654.30</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
In an InvoiceXML create request the fields are totals.duePayableAmount, totals.paidAmount and totals.roundingAmount. Supply the paid amount if there is one and leave the rest of invoice.totals out: the API computes the due amount server-side.
The raw rejection vs the InvoiceXML finding
The Schematron report:
<svrl:failed-assert id="BR-CO-16" flag="fatal"
location="/*:Invoice[namespace-uri()='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'][1]">
<svrl:text>[BR-CO-16]-Amount due for payment (BT-115) =
Invoice total amount with VAT (BT-112) -Paid amount (BT-113)
+Rounding amount (BT-114).</svrl:text>
</svrl:failed-assert>
The InvoiceXML finding for the same failure:
{
"rule": "BR-CO-16",
"layer": "en16931",
"line": null,
"message": "The amount due for payment must equal the total with VAT minus prepaid amounts plus rounding. Check the due amount.",
"btCodes": ["BT-115", "BT-112", "BT-113", "BT-114"],
"fields": ["totals.duePayableAmount", "totals.grandTotalAmount", "totals.paidAmount", "totals.roundingAmount"],
"raw": "[BR-CO-16] EN16931: Amount due for payment (BT-115) = Invoice total amount with VAT (BT-112) -Paid amount (BT-113) +Rounding amount (BT-114)."
}
Every field in the equation arrives as a dotted request path, so your UI can highlight all four inputs at once, and the message needs no translation layer. Clients localize by the stable rule key if they want their own copy.
Check or generate documents with correct totals
Validate a file in the UBL validator or the XRechnung validator. Generating with POST /v1/create/ubl or POST /v1/create/facturx removes the whole failure class: the chain from line sum to amount due is computed server-side. The model behind it is covered in EN 16931 explained.