Mutabiq checks Saudi ZATCA Phase 2 e-invoice XML before you submit it: pre-validation against published ZATCA requirements, not an approval by the authority. This page explains one rule, BR-CO-18.
Five checks a month on the Free plan. The file is checked in memory and is never stored.
The message ZATCA prints
[BR-CO-18]-An Invoice shall at least have one VAT breakdown group (BG-23).
What Mutabiq reports
One entry for each way this rule can be reported.
Reported as
What kind of value was found — empty, not_15_digits and the like — never the value itself.
Invoice carries no VAT total block
Reported astax_total_missingthe VAT total block is missing
What is wrong
The invoice has no cac:TaxTotal element of its own at document level. ZATCA requires at least one under BR-CO-18, because that block is where the invoice states its VAT. A cac:TaxTotal inside an invoice line does not count: UBL puts one on every line, and this rule looks only at the document's own direct children.
What to check in your ERP
The document-level VAT block sits beside the totals, not inside the lines. Check the export template in your e-invoicing module: an export built from a line loop alone will emit a cac:TaxTotal per line and none at document level, which looks complete and is not. A ZATCA invoice normally carries two at document level - one holding the VAT breakdown and one holding the total on its own.
VAT total block carries no VAT breakdown
Reported astax_subtotal_missingthe VAT breakdown is missing from the VAT total
What is wrong
The invoice has a document-level cac:TaxTotal but none of them carries a cac:TaxSubtotal, so the document states a VAT total without saying how it breaks down by category and rate. ZATCA requires at least one subtotal under BR-CO-18. A cac:TaxSubtotal inside an invoice line does not count; only the document's own blocks are read.
What to check in your ERP
This is the more common of the two and it is usually deliberate: an export that writes the VAT figure and stops, because that is all a printed invoice shows. ZATCA needs the breakdown as well - one cac:TaxSubtotal per VAT category and rate on the invoice, each with its taxable amount, its tax amount and its category code. Check whether your e-invoicing module builds that breakdown at all; on a single-rate invoice it is one entry, which is easy to treat as redundant and is not.
The clause that runs
Quoted from the validation-rule stylesheet ZATCA publishes, at the file and line named below. The executed guard is what the check runs; the reported test is what it prints when it fails, and the two are not always the same.
Executed guard
not(exists(cac:TaxTotal/cac:TaxSubtotal))
Reported test
exists(cac:TaxTotal/cac:TaxSubtotal)
Evaluated against
/ubl:Invoice | /cn:CreditNote
Source
vendor/zatca/schematron/CEN-EN16931-UBL.xsl:783
As shipped in ZATCA SDK 3.4.8: the copy quoted here is, byte for byte, the file that release ships. The date in the file name is the name ZATCA gives the file; the rules in it are those of SDK 3.4.8.
A worked example
A synthetic document from the measured test corpus that trips this rule, shown where it differs from one that passed. Mutabiq's report names the class of the value, never the value itself.
Where it differs from a document that passed
− This example
+ A document that passed
<cac:TaxTotal> <cbc:TaxAmount currencyID="SAR">15.00</cbc:TaxAmount>+A document that passed: <cac:TaxSubtotal>+A document that passed: <cbc:TaxableAmount currencyID="SAR">100.01</cbc:TaxableAmount>+A document that passed: <cbc:TaxAmount currencyID="SAR">15.00</cbc:TaxAmount>+A document that passed: <cac:TaxCategory>+A document that passed: <cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">S</cbc:ID>+A document that passed: <cbc:Percent>15.00</cbc:Percent>+A document that passed: <cac:TaxScheme>+A document that passed: <cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>+A document that passed: </cac:TaxScheme>+A document that passed: </cac:TaxCategory>+A document that passed: </cac:TaxSubtotal></cac:TaxTotal><cac:TaxTotal>
Document
bad-34-no-tax-subtotal.xml
Mutabiq's verdict
Not passed
Reported as
tax_subtotal_missingthe VAT breakdown is missing from the VAT total