Facturae in Python

The electronic invoice XML Spanish law makes mandatory, generated from Python.

Open-source libraries · Python · Code published

The problem

Royal Decree 238/2026, of 25 March, turns business-to-business electronic invoicing from an option into an obligation. The format is neither a PDF nor JSON: it is an XML with a fixed structure, amounts that must reconcile with each other, and fields rejected if their shape is wrong. Hand-writing it works until the first invoice with two VAT rates.

What it does

  • Generates Facturae XML from invoice data written as plain Python structures.
  • Computes the totals from the lines — quantity times price — instead of taking them as given.
  • Validates the fields that get rejected on submission: Spanish postcode, alpha-2 country code and tax blocks.
  • Fails when a key is misspelled, rather than emitting an XML that is silently missing that field.

What was decided while building it

Totals are computed, never taken as given
If the caller could pass the total, sooner or later they would pass one that disagrees with the lines, and the resulting XML would be valid to a parser and rejected at the far end. The total always comes from the lines. It is less flexible and that is exactly the point: there is no way to write an invoice that disagrees with itself.
A misspelled key stops the process
A dictionary accepts any key, so a typo does not raise an error: it produces an XML missing that field, which looks fine and gets rejected weeks later. The library checks the keys and fails there and then, while somebody is still looking.
Amounts use decimal, not floating point
Money in floating point produces the cent that does not add up, and on an invoice that cent is a rejection. The standard library decimal type is used instead: exact in base ten, and the reason the library needs no dependency at all for reliable arithmetic.

How far it goes

  • The XML comes out unsigned. Facturae requires an XAdES signature before submission to FACe, and this library does not provide it.
  • It does not validate against the official schema at runtime: it checks what it checks, and what that is is written down.
  • It transmits nothing. Producing the document and filing it are two different problems and only the first is here.
  • It is not tax or legal advice. What must be invoiced, and when, is out of scope.
  • 0 dependencies
  • MIT licence
  • 3.9+ Python

Built with

  • Python
  • ElementTree
  • decimal

Backs this service

Bespoke software and application development

Read the code