BR-CL-04Document currency code is not one ZATCA accepts
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-CL-04.
Five checks a month on the Free plan. The file is checked in memory and is never stored.
The message ZATCA prints
[BR-CL-04]-Invoice currency code MUST be coded using ISO code list 4217 alpha-3
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.
Document currency code is not one ZATCA accepts
Reported ascurrency_not_iso4217not an ISO 4217 currency code
What is wrong
The document currency code in cbc:DocumentCurrencyCode is not on the ISO 4217 list ZATCA checks against, and the document is refused under BR-CL-04. The code must be one of the three-letter currency codes on that list - SAR for the Saudi riyal. A value with a space inside it fails here for the same reason rather than for a different one: no code on the list contains a space, so a space in the middle of the value means the lookup can never match. Leading and trailing spaces are not the problem - those are stripped before the code is looked up - so where a space is at fault it is one in the middle of the value. One thing about the list itself is worth knowing: ZATCA's copy of ISO 4217 has not been kept current. It still carries MRO, STD and VEF, which ISO has withdrawn, and it does not carry their replacements MRU, STN and VES. So an invoice in Mauritanian ouguiya, Sao Tome dobra or Venezuelan bolivar written with the code ISO publishes today is rejected, and the withdrawn code is the one ZATCA accepts.
What to check in your ERP
For a domestic invoice the answer is SAR: the three letters alone, with nothing around them. If the document is genuinely in another currency, check the code your system holds against what ZATCA accepts rather than against a current ISO list - for the three currencies above the two disagree, and ZATCA's copy is what decides. Otherwise the usual causes are a currency's name or symbol written where the three-letter code belongs, a two-letter country code in place of the currency code, a blank currency field on a customer or company record that the export writes through unchanged, and - where a space is what broke it - the currency's name written next to its code, or two codes concatenated by a mapping that expected one. Correct it in the currency configuration or the export mapping inside your e-invoicing module rather than in the exported file.
Invoice type code is longer than three characters
Reported aslonger_than_3longer than three characters
What is wrong
The invoice type code (BT-3), the text content of cbc:InvoiceTypeCode, is more than three characters long. Only rule packs before 2.0.0 report this: SDK 3.0.8's EN 16931 layer measured the code exactly as written, without normalising whitespace, and refused one longer than three characters under BR-CL-04 and again under BR-CL-400, so the correct code 388 with a space on each side was refused. In SDK 3.4.8, which rule pack 2.0.0 reads, BR-CL-04 checks the document currency code only (EN:1881) and BR-CL-400 is gone, while BR-KSA-05 compares the type code after normalising whitespace (KSA:863), so a padded 388 passes and a report produced under 2.0.0 never carries this note.
What to check in your ERP
A padded code is no longer refused, but the bare code is still the one to send: 388, 383 or 381, with no leading or trailing spaces, no tab, no line break between the opening and closing tags, and no padding. The usual sources are a field padded to a fixed width on the way out, whitespace stored in the document type configuration table and carried through the mapping untouched, and an XML writer set to pretty-print, which puts the value on its own indented line and makes the element content several characters longer than the code. Whitespace is invisible on screen, so check the length of the stored value rather than looking at it, and correct it in the mapping or the export template inside your e-invoicing module rather than in the exported file.
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(((not(contains(normalize-space(.), ' ')) and contains(' AED AFN ALL AMD ANG AOA ARS AUD AWG AZN BAM BBD BDT BGN BHD BIF BMD BND BOB BOV BRL BSD BTN BWP BYN BZD CAD CDF CHE CHF CHW CLF CLP CNY COP COU CRC CUC CUP CVE CZK DJF DKK DOP DZD EGP ERN ETB EUR FJD FKP GBP GEL GHS GIP GMD GNF GTQ GYD HKD HNL HRK HTG HUF IDR ILS INR IQD IRR ISK JMD JOD JPY KES KGS KHR KMF KPW KRW KWD KYD KZT LAK LBP LKR LRD LSL LYD MAD MDL MGA MKD MMK MNT MOP MRO MUR MVR MWK MXN MXV MYR MZN NAD NGN NIO NOK NPR NZD OMR PAB PEN PGK PHP PKR PLN PYG QAR RON RSD RUB RWF SAR SBD SCR SDG SEK SGD SHP SLL SOS SRD SSP STD SVC SYP SZL THB TJS TMT TND TOP TRY TTD TWD TZS UAH UGX USD USN UYI UYU UZS VEF VND VUV WST XAF XAG XAU XBA XBB XBC XBD XCD XDR XOF XPD XPF XPT XSU XTS XUA XXX YER ZAR ZMW ZWL ', concat(' ', normalize-space(.), ' ')))))
Reported test
((not(contains(normalize-space(.), ' ')) and contains(' AED AFN ALL AMD ANG AOA ARS AUD AWG AZN BAM BBD BDT BGN BHD BIF BMD BND BOB BOV BRL BSD BTN BWP BYN BZD CAD CDF CHE CHF CHW CLF CLP CNY COP COU CRC CUC CUP CVE CZK DJF DKK DOP DZD EGP ERN ETB EUR FJD FKP GBP GEL GHS GIP GMD GNF GTQ GYD HKD HNL HRK HTG HUF IDR ILS INR IQD IRR ISK JMD JOD JPY KES KGS KHR KMF KPW KRW KWD KYD KZT LAK LBP LKR LRD LSL LYD MAD MDL MGA MKD MMK MNT MOP MRO MUR MVR MWK MXN MXV MYR MZN NAD NGN NIO NOK NPR NZD OMR PAB PEN PGK PHP PKR PLN PYG QAR RON RSD RUB RWF SAR SBD SCR SDG SEK SGD SHP SLL SOS SRD SSP STD SVC SYP SZL THB TJS TMT TND TOP TRY TTD TWD TZS UAH UGX USD USN UYI UYU UZS VEF VND VUV WST XAF XAG XAU XBA XBB XBC XBD XCD XDR XOF XPD XPF XPT XSU XTS XUA XXX YER ZAR ZMW ZWL ', concat(' ', normalize-space(.), ' '))))
Evaluated against
cbc:DocumentCurrencyCode
Source
vendor/zatca/schematron/CEN-EN16931-UBL.xsl:1882
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
<cbc:IssueTime>10:00:00</cbc:IssueTime><cbc:InvoiceTypeCode name="0100000">388</cbc:InvoiceTypeCode>−This example: <cbc:DocumentCurrencyCode>ZZZ</cbc:DocumentCurrencyCode>+A document that passed: <cbc:DocumentCurrencyCode>SAR</cbc:DocumentCurrencyCode><cbc:TaxCurrencyCode>SAR</cbc:TaxCurrencyCode><cac:AdditionalDocumentReference>