SAFTCheck

Validation rule reference

Every rule SAFTCheck applies to your SAF-T (PT) XML, in plain language. Each page covers what AT expects, the most common ways the rule fails, and how to fix it.

XML encoding declaration (Windows-1252) AT requires Windows-1252 encoding. UTF-8 declarations are silently rejected. encoding.declaration Byte Order Mark (BOM) at file start AT rejects SAF-T files that start with a UTF-8 or UTF-16 BOM. encoding.bom Validate a Portuguese NIF: free checker and Mod-11 rules Check any Portuguese NIF instantly: 9 digits, valid prefix and Mod-11 check digit. Plus how the algorithm works and the most common errors. nif.mod11 ATCUD format: free checker and the rules since 2023 Mandatory unique-document code: a validation code of at least 8 characters, a dash, and the document number. Includes a free ATCUD checker. atcud.format Header dates: StartDate, EndDate, FiscalYear, DateCreated Time-window invariants AT enforces on the SAF-T Header block. header.dates Inline editor: fix NIF, ATCUD and header dates in place Edit the offending value next to the finding, re-validate immediately, no ERP round-trip needed. feature.inline-editor VIES cross-check: NIF registered in EU VAT exchange Header NIF is verified against the EU VIES service and the registered name is compared with the SAF-T CompanyName. nif.vies Document date inside the Header reporting window Every Invoice / Payment / WorkDocument / StockMovement date must fall inside Header.StartDate – Header.EndDate. invoice.date.out_of_range Block totals reconcile against child documents NumberOfEntries must count every document (cancelled included); TotalDebit and TotalCredit must equal the line amounts without VAT, cancelled documents excluded, within 0.01. totals.reconciliation Foreign-currency documents need a complete <Currency> block When a document declares a non-EUR currency, CurrencyCode + CurrencyAmount + ExchangeRate must all be present. invoice.currency.exchange Invoice status code and date consistency InvoiceStatus must be one of N/S/A/R/F and InvoiceStatusDate must be >= InvoiceDate. invoice.status

Looking for narrative deep dives instead? See the blog for end-to-end fix walkthroughs.