Invoice calculations and validations
Core document fields
document_currency_code: valid ISO 4217;tax_currency_exchange_raterequired (>0, ≤6 decimals) when currency ≠ AED, must be 1/omitted when AED.issue_date: not future, not >30 days past, evaluated in Asia/Dubai time.due_date: required when the payable amount > 0, unlessDEEMED_SUPPLY.tax_point_date: must be beforeissue_date.trader_invoice_number: unique per branch (checked in-app + DB unique constraint, race-safe).note: required wheninvoice_period.billing_frequency_code = OTH.beneficiary_trn/principle_trn: valid TRN; required byFREE_TRADE_ZONE/AGENT_BILLINGrespectively; principle TRN must differ from the branch's own TRN.customer_party_id/payee_party_id/tax_representative_party_id/supplier_id: must reference existing entities in the branch. (tax_representative_party_idcheck is correctly implemented but has no dedicated test.)payments: at least one entry required, exceptDEEMED_SUPPLY.
Invoice type codes
380Commercial Invoice: must have at least one item that isn't E/O.480Out-of-Scope Invoice: items + doc-level A/Cs restricted to E/O/Z; buyer must havecompany_id; forbidden withSUMMARY_INVOICE/DEEMED_SUPPLY/PROFIT_MARGIN_SCHEME.389— see Self-Billed Invoices below.
Transaction types (combinable) — FREE_TRADE_ZONE, AGENT_BILLING, SUMMARY_INVOICE, CONTINUOUS_SUPPLY, EXPORTS (forbidden with PROFIT_MARGIN_SCHEME), SUPPLY_THROUGH_ECOMMERCE, PROFIT_MARGIN_SCHEME (forbidden with EXPORTS/type 480), DEEMED_SUPPLY (forbidden with type 480) - each with its own required fields (see prior message for the full per-type table).
Document-level allowances/charges
tax_category_coderequired;Nforbidden at doc level.tax_ratebetween 0–1, must satisfy per-category rules.amount = base_amount × multiplier_ratewhen both given.reason_codevalidated against charge vs. allowance reason lists; exemption reason code required for categoryE.
Document-level calculations
- Tax breakdown buckets grouped by
(taxCategoryCode, taxRate);TaxableAmount = Σ line.LineExtensionAmount ± doc-level A/Cper bucket; only S buckets getRound(TaxableAmount × rate). Rounding happens once here, not per line. LineExtensionAmount = Σ line.LineExtensionAmountTaxAmount = Σ bucket.TaxAmountTaxExclusiveAmount = LineExtensionAmount − docAllowances + docChargesTaxInclusiveAmount = TaxExclusiveAmount + TaxAmountPayableAmount = TaxInclusiveAmount − PrepaidAmount + PayableRoundingAmount(PrepaidAmountis its own request field, not summed frompayments— payments carry method metadata only).TaxAmountInTaxCurrency = Round(TaxAmount × taxCurrencyExchangeRate);TaxCurrencyCodehardcoded to AED.
Updated about 1 month ago
Did this page help you?
