BR-KSA-23VAT breakdown gives no exemption reason code (BR-KSA-23)
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-KSA-23.
Five checks a month on the Free plan. The file is checked in memory and is never stored.
The message ZATCA prints
[BR-KSA-23]-A VAT breakdown (BG-23) with VAT Category code (BT-118) 'Exempt from VAT' shall have a VAT exemption reason code (BT-121).
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.
VAT breakdown gives no exemption reason code (BR-KSA-23)
Reported asreason_code_missingthe reason code is missing
What is wrong
A cac:TaxSubtotal in the invoice's VAT breakdown is coded Z, E or O and its cac:TaxCategory has no cbc:TaxExemptionReasonCode. BR-KSA-23's message speaks of exempt breakdowns, but ZATCA's current checker applies it to zero-rated, exempt and out-of-scope breakdowns alike, and BR-KSA-24 repeats the same check, so expect both notes together. ZATCA flags this as a warning, which does not by itself reject the document.
What to check in your ERP
Add a reason code to every zero-rated, exempt and out-of-scope VAT code in your system, from the VATEX-SA codes ZATCA publishes, and write it into cac:TaxSubtotal/cac:TaxCategory/cbc:TaxExemptionReasonCode. Which code applies is a tax question; settle it with whoever owns the VAT code table.
VAT breakdown's exemption reason code is empty (BR-KSA-23)
Reported asreason_code_emptythe reason code is present but empty
What is wrong
A cac:TaxSubtotal in the invoice's VAT breakdown is coded Z, E or O and its cac:TaxCategory carries cbc:TaxExemptionReasonCode with no value, or only spaces. BR-KSA-23 requires a non-empty code on zero-rated, exempt and out-of-scope breakdowns alike, and BR-KSA-24 repeats the same check, so expect both notes together. ZATCA flags this as a warning, which does not by itself reject the document.
What to check in your ERP
Fill the reason code from the VATEX-SA codes ZATCA publishes. An empty element usually comes from a VAT code with no reason code configured, written through by a template that always writes the tag.
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(cbc:TaxExemptionReasonCode) and normalize-space(cbc:TaxExemptionReasonCode )!='' )
Reported test
not(exists(cbc:TaxExemptionReasonCode) and (contains('VATEX-SA-29 VATEX-SA-29-7 VATEX-SA-30',upper-case(cbc:TaxExemptionReasonCode))))
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.