diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index ed5f181..a244333 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -30,4 +30,4 @@ jobs:
- name: Lint
uses: golangci/golangci-lint-action@v3
with:
- version: v1.52
+ version: v1.55
diff --git a/README.md b/README.md
index 30eeb73..4db2137 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,51 @@
-# gobl.fatturapa
+# GOBL to FatturaPA Tools
-GOBL Conversion into FatturaPA in Italy
+Convert GOBL into the Italy's FatturaPA format.
-# GOBL to FatturaPA Toolkit
+Copyright [Invopop Ltd.](https://invopop.com) 2023. Released publicly under the [Apache License Version 2.0](LICENSE). For commercial licenses please contact the [dev team at invopop](mailto:dev@invopop.com). In order to accept contributions to this library we will require transferring copyrights to Invopop Ltd.
-Convert GOBL documents into the Italy's FatturaPA format.
+[![Lint](https://github.com/invopop/gobl.factturapa/actions/workflows/lint.yaml/badge.svg)](https://github.com/invopop/gobl.fatturapa/actions/workflows/lint.yaml)
+[![Test Go](https://github.com/invopop/gobl.fatturapa/actions/workflows/test.yaml/badge.svg)](https://github.com/invopop/gobl.fatturapa/actions/workflows/test.yaml)
+[![Go Report Card](https://goreportcard.com/badge/github.com/invopop/gobl.fatturapa)](https://goreportcard.com/report/github.com/invopop/gobl.fatturapa)
+[![GoDoc](https://godoc.org/github.com/invopop/gobl.fatturapa?status.svg)](https://godoc.org/github.com/invopop/gobl.fatturapa)
+![Latest Tag](https://img.shields.io/github/v/tag/invopop/gobl.fatturapa)
-TODO: copyright, license, build statuses
+## Introduction
+
+FatturaPA defines two versions of invoices:
+
+- Ordinary invoices, `FatturaElettronica` types `FPA12` and `FPR12` defined in the v1.2 schema, usable for all sales.
+- Simplified invoices, `FatturaElettronicaSemplificata` type `FSM10` defined in the v1.0 schema, with a reduced set of requirements but can only be used for sales of less then €400, as of writing. **Currently not supported!**
+
+Unlike other tax regimes, Italy requires simplified invoices to include the customer's tax ID. For "cash register" style receipts locally called "Scontrinos", another format and API is used for this from approved hardware.
+
+## Sources
+
+You can find copies of the Italian FatturaPA schema in the [schemas folder](./schema).
+
+- [Historical Documentation](https://www.fatturapa.gov.it/en/norme-e-regole/documentazione-fattura-elettronica/formato-fatturapa/)
+- [Ordinary Schema V1.2.1 Spec Table View (EN)](https://www.fatturapa.gov.it/export/documenti/fatturapa/v1.2.1/Table-view-B2B-Ordinary-invoice.pdf) - by far the most comprehensible spec doc. Since the difference between 1.2.2 and 1.2.1 is minimal, this is perfectly usable.
+- [Ordinary Schema V1.2.2 PDF (IT)](https://www.fatturapa.gov.it/export/documenti/Specifiche_tecniche_del_formato_FatturaPA_v1.3.1.pdf) - most up-to-date but difficult
+- [XSD V1.2.2](https://www.fatturapa.gov.it/export/documenti/fatturapa/v1.2.2/Schema_del_file_xml_FatturaPA_v1.2.2.xsd)
+- [XSD V1 (FSM10) - simplified invoices](https://www.agenziaentrate.gov.it/portale/documents/20143/288192/ST+Fatturazione+elettronica+-+Schema+VFSM10_Schema_VFSM10.xsd/010f1b41-6683-1b31-ba36-c8bced659c06)
+
+## Limitations
+
+The FatturaPA XML schema is quite large and complex. This library is not complete and only supports a subset of the schema. The current implementation is focused on the most common use cases.
+
+- Simplified invoices are not currently supported (please get in touch if you need this).
+- FatturaPA allows multiple invoices within the document, but this library only supports a single invoice per transmission.
+- Only a subset of payment methods (ModalitaPagamento) are supported. See `payments.go` for the list of supported codes.
+
+Some of the optional elements currently not supported include:
+
+- `Allegati` (attachments)
+- `DatiOrdineAcquisto` (data related to purchase orders)
+- `DatiContratto` (data related to contracts)
+- `DatiConvenzione` (data related to conventions)
+- `DatiRicezione` (data related to receipts)
+- `DatiFattureCollegate` (data related to linked invoices)
+- `DatiBollo` (data related to duty stamps)
## Usage
@@ -78,29 +117,28 @@ converter := fatturapa.NewConverter(
### CLI
-The command line interface can be useful for situations when you're using a language other than Golang in your application.
+The command line interface can be useful for situations when you're using a language other than Golang in your application. Install with:
```bash
-cd cmd/gobl.fatturapa
-go build
+go install github.com/invopop/gobl.fatturapa
```
Simply provide the input GOBL JSON file and output to a file or another application:
```bash
-./gobl.fatturapa convert input.json output.xml
+gobl.fatturapa convert input.json output.xml
```
If you have a digital certificate, run with:
```bash
-./gobl.fatturapa convert -c cert.p12 -p password input.json output.xml
+gobl.fatturapa convert -c cert.p12 -p password input.json output.xml
```
To include the transmitter information, add the `-T` flag and provide the _country code_ and the _tax ID_:
```bash
-./gobl.fatturapa convert -T ES12345678 input.json output.xml
+gobl.fatturapa convert -T ES12345678 input.json output.xml
```
The command also supports pipes:
@@ -111,7 +149,7 @@ cat input.json > ./gobl.fatturapa output.xml
## Notes
-- In all cases Go structures have been written using the same naming from the XML style document. This means names are not repeated in tags and generally makes it a bit easier map the XML output to the internal structures.
+- In all cases Go structures have been written using the same naming from the XML style document. This means names are not repeated in tags and generally makes it a bit easier to map the XML output to the internal structures.
## Integration Tests
@@ -122,20 +160,3 @@ mage -v TestConversion
```
Sample data sources are contained in the `/test/data` directory. JSON (for tests) documents are stored in the Git repository, but the XML must be generated using the above commands.
-
-## Current Conversion Limitations
-
-The FatturaPA XML schema is quite large and complex. This library is not complete and only supports a subset of the schema. The current implementation is focused on the most common use cases.
-
-- FatturaPA allows multiple invoices within the document, but this library only supports a single invoice per transmission.
-- Only a subset of payment methods (ModalitaPagamento) are supported. See `payments.go` for the list of supported codes.
-
-Some of the optional elements currently not supported include:
-
-- `Allegati` (attachments)
-- `DatiOrdineAcquisto` (data related to purchase orders)
-- `DatiContratto` (data related to contracts)
-- `DatiConvenzione` (data related to conventions)
-- `DatiRicezione` (data related to receipts)
-- `DatiFattureCollegate` (data related to linked invoices)
-- `DatiBollo` (data related to duty stamps)
diff --git a/body.go b/body.go
index 9d76746..f7e1ff3 100644
--- a/body.go
+++ b/body.go
@@ -1,6 +1,7 @@
package fatturapa
import (
+ "errors"
"fmt"
"github.com/invopop/gobl/bill"
@@ -92,6 +93,11 @@ func newDatiGenerali(inv *bill.Invoice) (*datiGenerali, error) {
return nil, err
}
+ switch codeTipoDocumento {
+ case "TD07", "TD08", "TD09":
+ return nil, errors.New("simplified invoices are not currently supported")
+ }
+
return &datiGenerali{
DatiGeneraliDocumento: &datiGeneraliDocumento{
TipoDocumento: codeTipoDocumento,
diff --git a/items.go b/items.go
index 277b9f4..6b19138 100644
--- a/items.go
+++ b/items.go
@@ -47,18 +47,23 @@ func generateLineDetails(inv *bill.Invoice) []*dettaglioLinee {
var dl []*dettaglioLinee
for _, line := range inv.Lines {
- vatTax := line.Taxes.Get(tax.CategoryVAT)
-
- dl = append(dl, &dettaglioLinee{
+ d := &dettaglioLinee{
NumeroLinea: strconv.Itoa(line.Index),
Descrizione: line.Item.Name,
Quantita: formatAmount(&line.Quantity),
PrezzoUnitario: formatAmount(&line.Item.Price),
PrezzoTotale: formatAmount(&line.Sum),
- AliquotaIVA: formatPercentage(vatTax.Percent),
- Natura: vatTax.Ext[it.ExtKeySDINature].String(),
ScontoMaggiorazione: extractLinePriceAdjustments(line),
- })
+ }
+ if len(line.Taxes) > 0 {
+ vatTax := line.Taxes.Get(tax.CategoryVAT)
+ if vatTax != nil {
+ d.AliquotaIVA = formatPercentage(vatTax.Percent)
+ d.Natura = vatTax.Ext[it.ExtKeySDINature].String()
+ }
+ }
+
+ dl = append(dl, d)
}
return dl
@@ -114,7 +119,6 @@ func findRiferimentoNormativo(rateTotal *tax.RateTotal) string {
def := regime.ExtensionDef(it.ExtKeySDINature)
nature := rateTotal.Ext[it.ExtKeySDINature]
-
for _, c := range def.Codes {
if c.Code == nature {
return c.Name[i18n.IT]
diff --git a/parties.go b/parties.go
index e504c37..0a51c8e 100644
--- a/parties.go
+++ b/parties.go
@@ -95,8 +95,11 @@ func newCedentePrestatore(s *org.Party) *supplier {
}
func newCessionarioCommittente(c *org.Party) *customer {
- nc := new(customer)
+ if c == nil {
+ return nil
+ }
+ nc := new(customer)
if len(c.Addresses) > 0 {
nc.Sede = newAddress(c.Addresses[0])
}
diff --git a/schema/ST Fatturazione elettronica - Schema VFSM10_Schema_VFSM10.xsd b/schema/ST Fatturazione elettronica - Schema VFSM10_Schema_VFSM10.xsd
new file mode 100644
index 0000000..1c8e3e5
--- /dev/null
+++ b/schema/ST Fatturazione elettronica - Schema VFSM10_Schema_VFSM10.xsd
@@ -0,0 +1,522 @@
+
+
+
+
+
+
+
+
+ XML schema fatture destinate a privati in forma semplificata 1.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Blocco relativo ai dati di trasmissione della Fattura Elettronica
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Fattura verso privati semplificata
+
+
+
+
+
+
+
+ Blocco relativo ai Dati Generali della Fattura Elettronica
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SI = Documento emesso secondo modalità e termini stabiliti con DM ai sensi dell'art. 73 DPR 633/72
+
+
+
+
+
+
+
+
+
+
+ Fattura semplificata
+
+
+
+
+ Nota di credito semplificata
+
+
+
+
+ Nota di debito semplificata
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Cessionario / Committente
+
+
+
+
+ Terzo
+
+
+
+
+
+
+
+ Blocco relativo ai dati del Cedente / Prestatore
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Regime ordinario
+
+
+
+
+ Regime dei contribuenti minimi (art. 1,c.96-117, L. 244/2007)
+
+
+
+
+ Agricoltura e attività connesse e pesca (artt. 34 e 34-bis, D.P.R. 633/1972)
+
+
+
+
+ Vendita sali e tabacchi (art. 74, c.1, D.P.R. 633/1972)
+
+
+
+
+ Commercio dei fiammiferi (art. 74, c.1, D.P.R. 633/1972)
+
+
+
+
+ Editoria (art. 74, c.1, D.P.R. 633/1972)
+
+
+
+
+ Gestione di servizi di telefonia pubblica (art. 74, c.1, D.P.R. 633/1972)
+
+
+
+
+ Rivendita di documenti di trasporto pubblico e di sosta (art. 74, c.1, D.P.R. 633/1972)
+
+
+
+
+ Intrattenimenti, giochi e altre attività di cui alla tariffa allegata al D.P.R. 640/72 (art. 74, c.6, D.P.R. 633/1972)
+
+
+
+
+
+ Agenzie di viaggi e turismo (art. 74-ter, D.P.R. 633/1972)
+
+
+
+
+ Agriturismo (art. 5, c.2, L. 413/1991)
+
+
+
+
+ Vendite a domicilio (art. 25-bis, c.6, D.P.R. 600/1973)
+
+
+
+
+ Rivendita di beni usati, di oggetti d’arte, d’antiquariato o da collezione (art. 36, D.L. 41/1995)
+
+
+
+
+ Agenzie di vendite all’asta di oggetti d’arte, antiquariato o da collezione (art. 40-bis, D.L. 41/1995)
+
+
+
+
+ IVA per cassa P.A. (art. 6, c.5, D.P.R. 633/1972)
+
+
+
+
+ IVA per cassa (art. 32-bis, D.L. 83/2012)
+
+
+
+
+ Regime forfettario
+
+
+
+
+ Altro
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Blocco relativo ai dati del Rappresentante Fiscale
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Blocco relativo ai dati del Cessionario / Committente
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Blocco relativo ai dati di Beni Servizi della Fattura Elettronica
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Blocco relativo ai dati di eventuali allegati
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Escluse ex. art. 15
+
+
+
+
+ Non soggette
+
+
+
+
+ Non Imponibili
+
+
+
+
+ Esenti
+
+
+
+
+ Regime del margine
+
+
+
+
+
+
+
+
+
+
+
+
+
+ socio unico
+
+
+
+
+ più soci
+
+
+
+
+
+
+
+
+ in liquidazione
+
+
+
+
+ non in liquidazione
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/schema/fatturapav1_2_2.xsd b/schema/fatturapav1_2_2.xsd
similarity index 100%
rename from test/schema/fatturapav1_2_2.xsd
rename to schema/fatturapav1_2_2.xsd
diff --git a/test/data/invoice-simplified.json.not-supported b/test/data/invoice-simplified.json.not-supported
new file mode 100644
index 0000000..e8e81dc
--- /dev/null
+++ b/test/data/invoice-simplified.json.not-supported
@@ -0,0 +1,128 @@
+{
+ "$schema": "https://gobl.org/draft-0/envelope",
+ "head": {
+ "uuid": "679a2f25-7483-11ec-9722-7ea2cb436ff6",
+ "dig": {
+ "alg": "sha256",
+ "val": "427155d9bff1a6df157e956f0850ee880a44c8aa2adc8864e3cd48a758805193"
+ }
+ },
+ "doc": {
+ "$schema": "https://gobl.org/draft-0/bill/invoice",
+ "type": "standard",
+ "series": "SAMPLE",
+ "code": "055",
+ "issue_date": "2023-12-04",
+ "currency": "EUR",
+ "tax": {
+ "tags": ["simplified"]
+ },
+ "supplier": {
+ "name": "MªF. Services",
+ "tax_id": {
+ "country": "IT",
+ "code": "12345678903"
+ },
+ "people": [
+ {
+ "name": {
+ "given": "GIANCARLO",
+ "surname": "ROSSI"
+ }
+ }
+ ],
+ "addresses": [
+ {
+ "num": "1",
+ "street": "VIALE DELLA LIBERTÀ",
+ "locality": "ROMA",
+ "region": "RM",
+ "code": "00100",
+ "country": "IT"
+ }
+ ],
+ "emails": [
+ {
+ "addr": "billing@example.com"
+ }
+ ],
+ "telephones": [
+ {
+ "num": "999999999"
+ }
+ ],
+ "registration": {
+ "capital": "50000.00",
+ "currency": "EUR",
+ "office": "RM",
+ "entry": "123456"
+ },
+ "ext": {
+ "it-sdi-fiscal-regime": "RF01"
+ }
+ },
+ "customer": {
+ "name": "MARIO LEONI",
+ "tax_id": {
+ "country": "IT",
+ "code": "09876543217"
+ }
+ },
+ "lines": [
+ {
+ "i": 1,
+ "quantity": "20",
+ "item": {
+ "name": "Random products",
+ "price": "90.00",
+ "unit": "h"
+ },
+ "sum": "1800.00",
+ "discounts": [
+ {
+ "percent": "10%",
+ "amount": "180.00",
+ "reason": "Special discount"
+ }
+ ],
+ "taxes": [
+ {
+ "cat": "VAT",
+ "rate": "standard",
+ "percent": "22.0%"
+ }
+ ],
+ "total": "1620.00"
+ }
+ ],
+ "payment": {
+ "instructions": {
+ "key": "card"
+ }
+ },
+ "totals": {
+ "sum": "1620.00",
+ "total": "1620.00",
+ "taxes": {
+ "categories": [
+ {
+ "code": "VAT",
+ "rates": [
+ {
+ "key": "standard",
+ "base": "1620.00",
+ "percent": "22.0%",
+ "amount": "356.40"
+ }
+ ],
+ "amount": "356.40"
+ }
+ ],
+ "sum": "356.40"
+ },
+ "tax": "356.40",
+ "total_with_tax": "1976.40",
+ "payable": "1976.40"
+ }
+ }
+}
diff --git a/test/examples/invoice_simplified_sample.xml b/test/examples/invoice_simplified_sample.xml
new file mode 100644
index 0000000..596cfd6
--- /dev/null
+++ b/test/examples/invoice_simplified_sample.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+ IT
+ CCCNNN80D07L407I
+
+ 36
+ FSM10
+ OG04W6V
+
+
+
+ IT
+ 01754930384
+
+ CCCNNN80D07L407I
+ ANTONINO
+ CACCAMO
+
+ VIA RAVENNA 402/B
+ 44124
+ FERRARA
+ FE
+ IT
+
+ RF01
+
+
+
+
+ IT
+ 10442360961
+
+
+
+ A-Cube s.r.l.
+
+ Viale San Martino 57
+ 20862
+ Arcore
+ MB
+ IT
+
+
+
+
+
+
+
+ TD07
+ EUR
+ 2020-06-26
+ 13
+
+
+
+ test
+ 100.00
+
+ 22.00
+
+
+
+
\ No newline at end of file
diff --git a/test/examples_test.go b/test/examples_test.go
new file mode 100644
index 0000000..3a2b52e
--- /dev/null
+++ b/test/examples_test.go
@@ -0,0 +1,13 @@
+package test_test
+
+import (
+ "testing"
+
+ "github.com/invopop/gobl.fatturapa/test"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestExamples(t *testing.T) {
+ err := test.TestConversion()
+ assert.NoError(t, err)
+}
diff --git a/transmission.go b/transmission.go
index d186c42..aeca811 100644
--- a/transmission.go
+++ b/transmission.go
@@ -52,7 +52,7 @@ func (c *Converter) newDatiTrasmissione(inv *bill.Invoice, env *gobl.Envelope) *
func formatoTransmissione(cus *org.Party) string {
if cus != nil {
taxID := cus.TaxID
- if taxID.Country == l10n.IT && taxID.Type == it.TaxIdentityTypeGovernment {
+ if taxID != nil && taxID.Country == l10n.IT && taxID.Type == it.TaxIdentityTypeGovernment {
return formatoTrasmissioneFPA12
}
}