Errors:
XML-INVOICE-ERROR: XML Submitted using reporting API is not a simplified document
ZATCA XML Rounding Issue – Required Fix
i FIXD IT AND TEST IT BY UPDATE THIS FILE TOU CAN FIND IT HERE
ZATCA XML Rounding Issue – Required Fix · Issue #213 · ERPGulf/zatca_erpgulf
Problem Description
We are facing a ZATCA rejection (BR-CO-15) due to a rounding mismatch in the generated XML.
Example:
-
TaxExclusiveAmount: 2337348.90
-
TaxAmount: 350602.34
-
Expected TaxInclusiveAmount: 2687951.24
-
Generated TaxInclusiveAmount: 2687951.23

Difference: 0.01
Important Note (Critical)
This issue mainly occurs when using foreign currencies (e.g., USD) instead of the base currency (SAR).
When the invoice currency is USD (or any foreign currency):
-
Floating-point precision issues become more likely
-
Conversion + tax calculation increases rounding errors
-
ZATCA validation becomes stricter on totals
Therefore, this issue must be handled carefully for multi-currency invoices
Root Cause (Code-Level)
The issue originates from the function:
tax_data(…)
Inside:
zatca_erpgulf/zatca_erpgulf/xml_tax_data.py
Current implementation uses:
round(total + tax, 2)
This uses float rounding, which causes precision errors (especially in USD cases)
Important Observation
There is already a correct implementation in:
tax_data_with_template(…)
Which uses:
Decimal(…).quantize(Decimal(“0.01”), rounding=ROUND_HALF_UP)
This is compliant with ZATCA requirements
Prevents rounding mismatch
Works correctly even with foreign currencies
Critical Issue
In “sign_invoice.py”, the routing logic is:
if not any_item_has_tax_template:
invoice = tax_data(…)
else:
invoice = tax_data_with_template(…)
This means:
-
Most invoices (without Item Tax Template) use incorrect logic
-
This is especially problematic for USD invoices
Additional Issue
In “create_xml_final_part.py”, line-level rounding exists:
cbc:RoundingAmount = lineextensionamount + taxamount
This field:
-
Is not required by ZATCA
-
May introduce inconsistencies in totals
Required Fix
- Fix “tax_data(…)”
Replace ALL float rounding logic with:
from decimal import Decimal, ROUND_HALF_UP
total_amount = Decimal(str(sales_invoice_doc.total))
discount_amount = Decimal(str(sales_invoice_doc.get(“discount_amount”, 0.0)))
tax_amount = Decimal(str(tax_amount_without_retention)).quantize(
Decimal(“0.01”), rounding=ROUND_HALF_UP
)
tax_inclusive_amount = (
abs(total_amount - discount_amount) + tax_amount
).quantize(Decimal(“0.01”), rounding=ROUND_HALF_UP)
cbc_taxinclusiveamount.text = str(tax_inclusive_amount)
cbc_payableamount.text = str(tax_inclusive_amount)
- Ensure Consistency Rule (ZATCA Requirement)
Always enforce:
TaxInclusiveAmount = TaxExclusiveAmount + TaxAmount
PayableAmount = TaxInclusiveAmount
- (Recommended) Remove RoundingAmount
cbc:RoundingAmount…</cbc:RoundingAmount>
- (Optional but Strongly Recommended)
Unify logic:
Avoid splitting logic
tax_data(…) ![]()
tax_data_with_template(…) ![]()
Use a single Decimal-based implementation
Expected Result
After fix:
-
No rounding mismatch (0.01 issues gone)
-
Correct handling for USD and multi-currency invoices
-
Full compliance with ZATCA rules
Summary
Issue| Status
Float rounding|
Incorrect
Decimal rounding|
Correct
USD handling|
Currently problematic
Required action|
Fix tax_data
Please implement the fix in “tax_data(…)” to ensure correct behavior for foreign currencies (USD) and full ZATCA compliance.
Please change the calculation logic so that “TaxInclusiveAmount” and “PayableAmount” are not derived from any precomputed original total.
Instead, calculate them strictly as:
TaxInclusiveAmount = TaxExclusiveAmount + TaxAmount
PayableAmount = TaxInclusiveAmount
Where:
-
“TaxExclusiveAmount” = amount before VAT
-
“TaxAmount” = the already calculated and rounded VAT amount
This avoids mismatches in foreign currency invoices such as USD and ensures that the final total is always built from the actual XML tax values.
Suggested logic:
tax_exclusive_amount = Decimal(str(abs(taxable_amount_1))).quantize(
Decimal(“0.01”), rounding=ROUND_HALF_UP
)
tax_amount_decimal = Decimal(str(abs(tax_amount_without_retention))).quantize(
Decimal(“0.01”), rounding=ROUND_HALF_UP
)
tax_inclusive_amount = (tax_exclusive_amount + tax_amount_decimal).quantize(
Decimal(“0.01”), rounding=ROUND_HALF_UP
)
cbc_taxinclusiveamount.text = str(tax_inclusive_amount)
cbc_payableamount.text = str(tax_inclusive_amount)
This way, the final amount is always mathematically consistent with the XML tax values and will avoid BR-CO-15 rounding mismatches.
New Features Added to the ZATCA Phase-2 ERPNext App
Hello Frappe Community,
We have added several new features to the Saudi Arabian E-Invoicing (ZATCA Phase-2) app to support more business scenarios and simplify migration from legacy systems.
1. Credit Notes for Legacy Invoices
Many businesses migrate to ERPNext while having historical invoices in another system. To support this use case, users can now create and submit credit notes to ZATCA even when the original invoice does not exist in ERPNext.
How to Enable
-
Open Company → ZATCA Settings.
-
Enable Allow Credit-note without Original Invoice in the System.
-
Enter the original invoice number in Return Against for ZATCA on the Credit Note.
2. Skip ERPNext Credit Note Validations
A new checkbox named Skip Validation for Credit Note has been added to Sales Invoice.
When enabled, ERPNext validations related to return invoices can be bypassed, allowing organizations to process exceptional business cases while still generating the required ZATCA documents.
3. Profit Margin Method (PMM)
We have added a ZATCA PMM (Profit Margin Method) checkbox to the Sales Invoice in ERPNext.
When enabled:
-
The invoice is submitted normally within ERPNext.
-
The invoice is not transmitted to ZATCA.
-
No clearance or reporting request is generated.
This allows businesses to handle PMM invoices separately, which is essential for complying with ZATCA’s special VAT rules for Profit Margin Method (PMM) transactions.
This feature is particularly useful for businesses that buy and resell used or second-hand goods, including Used-car brokers and dealerships , Electronics and mobile phone resellers etc.These businesses can now manage their PMM transactions independently while maintaining compliance with ZATCA requirements.
Important: Please consult your auditor or ZATCA account manager before activating any of the newly added features to ensure that your business is legally compliant and eligible to use them.
We welcome feedback, suggestions, and contributions from the community.
Thank you for your continued support!
Can you explain how Used cars margin tax working in this app ?
There is no XML format provided by ZATCA for submitting tax on used-item margin. We are following up this case, and update here one we get it . Will update the code accordingly
thanks for the update. will wait.

