Convert GOBL documents into the Spain's FacturaE format.
Copyright Invopop Ltd. 2023. Released publicly under the Apache License Version 2.0. For commercial licenses please contact the dev team at invopop. In order to accept contributions to this library we will require transferring copyrights to Invopop Ltd.
There are a couple of entry points to build a new FacturaE document. If you already have a GOBL Envelope available in Go, you could convert and output to a data file like this:
doc, err := facturae.NewInvoice(env)
if err != nil {
panic(err)
}
data, err := doc.Bytes()
if err != nil {
panic(err)
}
if err = os.WriteFile("./test.xml", data, 0644); err != nil {
panic(err)
}If you're loading from a file, you can use the LoadGOBL convenience method:
doc, err := facturae.LoadGOBL(file)
if err != nil {
panic(err)
}
// do something with docOutputting to a FacturaE XML is most useful when the document is signed. Use a certificate to sign the document as follows:
// import from github.com/invopop/xmldsig
cert, err := xmldsig.LoadCertificate(filename, password)
if err != nil {
panic(err)
}
doc, err := facturae.NewInvoice(env, facturae.WithCertificate(cert))
if err != nil {
panic(err)
}The command line interface is useful when working with languages other than Go.
go install github.com/invopop/gobl.facturae/cmd/gobl.facturae@latestConvert a GOBL JSON invoice to FacturaE XML:
gobl.facturae convert input.json output.xmlWith a digital certificate:
gobl.facturae convert -c cert.p12 -p password input.json output.xmlThe command also supports pipes:
cat input.json | gobl.facturae convert > output.xmlThe conversion process follows these steps:
- Load GOBL Envelope: Parse the input JSON containing a GOBL invoice
- Remove Included Taxes: If the invoice has
prices_includeset (e.g., VAT included in prices), the converter callsinvoice.RemoveIncludedTaxes()to recalculate amounts from base prices. This is required because FacturaE expects taxes to be separate from base amounts. - Invert Credit Notes: If the document is a credit note, amounts are inverted to match FacturaE expectations
- Map to FacturaE Structure: Convert GOBL structures to FacturaE XML elements
- Sign (Optional): Apply digital signature if a certificate is provided
Important: The RemoveIncludedTaxes() operation recalculates all amounts from item-level prices, which can introduce minor rounding differences (typically ±0.01) compared to the original GOBL totals. Test fixtures account for these expected differences.
For XML schema validation during testing, this package uses go-xsd-validate, which depends on the libxml2 C library. This dependency is optional and only required when running tests with the xsdvalidate build tag.
Debian/Ubuntu:
sudo apt-get install libxml2-devmacOS:
brew install libxml2Tests will run without libxml2, but schema validation will be skipped unless you explicitly enable it with the -tags xsdvalidate flag.
Run all tests (without schema validation):
go test ./...Run tests with XSD schema validation (requires libxml2):
go test -tags xsdvalidate ./...Run tests for a specific example:
go test -v -run "TestXMLGeneration/should_convert_invoice-vat.json"Test data is organized in the test/data/ directory:
*.json- GOBL invoice envelopes (input)out/*.xml- Expected FacturaE XML output (fixtures)schema/facturaev3_2_2.xsd- Official FacturaE XML schema
When you make changes that affect XML output, update the fixtures:
go test -tags xsdvalidate -run TestXMLGeneration --updateThis will:
- Convert all JSON examples to FacturaE XML
- Validate each XML against the schema (requires libxml2)
- Update the fixtures in
test/data/out/
Note: The -tags xsdvalidate flag enables schema validation to ensure generated XML is valid according to the official FacturaE schema. Without this flag, fixtures will be updated but validation will be skipped.
For automated testing, XML documents are not signed. To generate signed documents for manual testing:
mage -v convertXMLDigital certificates for testing are available in /test/certificates.
To validate generated XML documents and digital signatures, use the official Spanish government validator:
- Go structures use the same naming conventions as the FacturaE XML schema where possible. This means XML tag names are often omitted in struct definitions, making it easier to map between Go code and XML output.
- Amount handling uses
num.Amountfrom GOBL for precise decimal arithmetic, with theMinimalString()method used for XML output to avoid trailing zeros.
When working with invoices that have prices_include tax settings, you may notice small rounding differences (typically ±0.01) between:
- The original GOBL
payableamount - The FacturaE
TotalOutstandingAmount/TotalExecutableAmount
This is expected behavior because:
- GOBL invoices with included taxes store the gross (tax-included) amounts
- FacturaE requires net (tax-excluded) amounts
- The conversion calls
RemoveIncludedTaxes()which recalculates everything from item-level unit prices - This multi-step recalculation can accumulate minor rounding differences
Solution: Test fixtures should reflect the amounts after conversion, not the original GOBL amounts. Update fixtures using go test --update.
To enable XSD schema validation during testing:
- Install libxml2 development libraries (see Dependencies section above)
- Run tests with the
-tags xsdvalidateflag - Without this flag, tests will run but skip schema validation
This is particularly useful when updating fixtures to ensure generated XML conforms to the official FacturaE schema.
When upgrading the GOBL dependency:
- Amount calculation logic may change
- Run
go test --updateto regenerate all fixtures - Manually verify a few key examples to ensure conversion is still correct
- Check the official FacturaE validator for complex cases
The FacturaE format is quite complex due to the number of local requirements in Spain.
- Payment Information: FacturaE requires each payment instruction to have a Due Date. The GOBL invoice allows these details to be independent. If you require payment instructions to appear on a FacturaE document, there must be a due date.