diff --git a/README.md b/README.md
index da1b734..62fe568 100644
--- a/README.md
+++ b/README.md
@@ -24,10 +24,12 @@ Unlike other tax regimes, Italy requires simplified invoices to include the cust
You can find copies of the Italian FatturaPA schema in the [schemas folder](./schema).
Key websites:
-- [FatturaPA & SDI service documentation page on on Italy's tax authority's website](https://www.agenziaentrate.gov.it/portale/web/guest/fatturazione-elettronica-e-dati-fatture-transfrontaliere-new/)
+
+- [FatturaPA & SDI service documentation page on on Italy's tax authority's website](https://www.agenziaentrate.gov.it/portale/web/guest/fatturazione-elettronica-e-dati-fatture-transfrontaliere-new/)
- [FatturaPA documentation page on FatturaPA's dedicated website](https://www.fatturapa.gov.it/en/norme-e-regole/documentazione-fattura-elettronica/formato-fatturapa/)
Useful files:
+
- [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)
@@ -154,13 +156,3 @@ 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 to map the XML output to the internal structures.
-
-## Integration Tests
-
-There are some integration and XML generation tests available in the `/test` path. to generate the FatturaPA XML documents from the GOBL sources, use the digital certificates that are available in the `/test/certificates` path:
-
-```
-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.
diff --git a/body.go b/body.go
index 5db0e15..2d45530 100644
--- a/body.go
+++ b/body.go
@@ -57,9 +57,9 @@ type datiBollo struct {
// scontoMaggiorazione contains data about price adjustments like discounts and
// charges.
type scontoMaggiorazione struct {
- Tipo string
- Percentuale string
- Importo string
+ Tipo string `xml:"Tipo"`
+ Percentuale string `xml:"Percentuale,omitempty"`
+ Importo string `xml:"Importo,omitempty"`
}
func newFatturaElettronicaBody(inv *bill.Invoice) (*fatturaElettronicaBody, error) {
diff --git a/helpers.go b/helpers.go
index c1f0a62..883a415 100644
--- a/helpers.go
+++ b/helpers.go
@@ -4,12 +4,18 @@ import "github.com/invopop/gobl/num"
func formatPercentage(p *num.Percentage) string {
if p == nil {
- return num.MakePercentage(0, 4).StringWithoutSymbol()
+ return ""
}
-
return p.Rescale(4).StringWithoutSymbol()
}
+func formatPercentageWithZero(p *num.Percentage) string {
+ if p == nil {
+ p = num.NewPercentage(0, 4)
+ }
+ return formatPercentage(p)
+}
+
func formatAmount(a *num.Amount) string {
if a == nil {
return ""
diff --git a/items.go b/items.go
index 3ed739e..17812ea 100644
--- a/items.go
+++ b/items.go
@@ -17,14 +17,14 @@ type datiBeniServizi struct {
// dettaglioLinee contains line data such as description, quantity, price, etc.
type dettaglioLinee struct {
- NumeroLinea string
- Descrizione string
- Quantita string
- PrezzoUnitario string
- ScontoMaggiorazione []*scontoMaggiorazione `xml:",omitempty"`
- PrezzoTotale string
- AliquotaIVA string
- Natura string `xml:",omitempty"`
+ NumeroLinea string `xml:"NumeroLinea"`
+ Descrizione string `xml:"Descrizione"`
+ Quantita string `xml:"Quantita"`
+ PrezzoUnitario string `xml:"PrezzoUnitario"`
+ ScontoMaggiorazione []*scontoMaggiorazione `xml:"ScontoMaggiorazione,omitempty"`
+ PrezzoTotale string `xml:"PrezzoTotale"`
+ AliquotaIVA string `xml:"AliquotaIVA"`
+ Natura string `xml:"Natura,omitempty"`
}
// datiRiepilogo contains tax summary data such as tax rate, tax amount, etc.
@@ -58,7 +58,7 @@ func generateLineDetails(inv *bill.Invoice) []*dettaglioLinee {
if line.Taxes != nil && len(line.Taxes) > 0 {
vatTax := line.Taxes.Get(tax.CategoryVAT)
if vatTax != nil {
- d.AliquotaIVA = formatPercentage(vatTax.Percent)
+ d.AliquotaIVA = formatPercentageWithZero(vatTax.Percent)
d.Natura = vatTax.Ext[it.ExtKeySDINature].String()
}
}
@@ -82,7 +82,7 @@ func generateTaxSummary(inv *bill.Invoice) []*datiRiepilogo {
for _, rateTotal := range vatRateTotals {
dr = append(dr, &datiRiepilogo{
- AliquotaIVA: formatPercentage(rateTotal.Percent),
+ AliquotaIVA: formatPercentageWithZero(rateTotal.Percent),
Natura: rateTotal.Ext[it.ExtKeySDINature].String(),
ImponibileImporto: formatAmount(&rateTotal.Base),
Imposta: formatAmount(&rateTotal.Amount),
@@ -94,25 +94,30 @@ func generateTaxSummary(inv *bill.Invoice) []*datiRiepilogo {
}
func extractLinePriceAdjustments(line *bill.Line) []*scontoMaggiorazione {
- var scontiMaggiorazioni []*scontoMaggiorazione
+ list := make([]*scontoMaggiorazione, 0)
for _, discount := range line.Discounts {
- scontiMaggiorazioni = append(scontiMaggiorazioni, &scontoMaggiorazione{
+ // Unlike most formats, FatturaPA applies the discount to the unit price
+ // instead of the line sum.
+ // Quick ref: https://fex-app.com/FatturaElettronica/FatturaElettronicaBody/DatiBeniServizi/DettaglioLinee/PrezzoTotale
+ a := discount.Amount.Divide(line.Quantity)
+ list = append(list, &scontoMaggiorazione{
Tipo: scontoMaggiorazioneTypeDiscount,
Percentuale: formatPercentage(discount.Percent),
- Importo: formatAmount(&discount.Amount),
+ Importo: formatAmount(&a),
})
}
for _, charge := range line.Charges {
- scontiMaggiorazioni = append(scontiMaggiorazioni, &scontoMaggiorazione{
+ a := charge.Amount.Divide(line.Quantity)
+ list = append(list, &scontoMaggiorazione{
Tipo: scontoMaggiorazioneTypeCharge,
Percentuale: formatPercentage(charge.Percent),
- Importo: formatAmount(&charge.Amount),
+ Importo: formatAmount(&a),
})
}
- return scontiMaggiorazioni
+ return list
}
func findRiferimentoNormativo(rateTotal *tax.RateTotal) string {
diff --git a/items_test.go b/items_test.go
index edd45d2..7b99a3a 100644
--- a/items_test.go
+++ b/items_test.go
@@ -28,7 +28,7 @@ func TestDettaglioLinee(t *testing.T) {
assert.Equal(t, "SC", sm.Tipo)
assert.Equal(t, "10.00", sm.Percentuale)
- assert.Equal(t, "180.00", sm.Importo)
+ assert.Equal(t, "9.00", sm.Importo)
dl = doc.FatturaElettronicaBody[0].DatiBeniServizi.DettaglioLinee[1]
diff --git a/mage.go b/mage.go
deleted file mode 100644
index d4a8cb0..0000000
--- a/mage.go
+++ /dev/null
@@ -1,109 +0,0 @@
-//go:build mage
-// +build mage
-
-package main
-
-import (
- "fmt"
- "os"
- "strings"
- "time"
-
- "github.com/invopop/gobl.fatturapa/test"
- "github.com/magefile/mage/sh"
- "github.com/magefile/mage/target"
-)
-
-const (
- name = "gobl.fatturapa"
- mainBranch = "main"
-)
-
-// TestConversion takes previously converted GOBL JSON and produces XML files.
-func TestConversion() error {
- return test.TestConversion()
-}
-
-// Build the binary
-func Build() error {
- changed, err := target.Dir("./"+name, ".")
- if os.IsNotExist(err) || (err == nil && changed) {
- return build("build")
- }
- return nil
-}
-
-// Install the binary into your go bin path
-func Install() error {
- return build("install")
-}
-
-func build(action string) error {
- args := []string{action}
- flags, err := buildFlags()
- if err != nil {
- return err
- }
- args = append(args, flags...)
- args = append(args, "./cmd/"+name)
- return sh.RunV("go", args...)
-}
-
-func buildFlags() ([]string, error) {
- ldflags := []string{
- fmt.Sprintf("-X 'main.date=%s'", date()),
- }
- if v, err := version(); err != nil {
- return nil, err
- } else if v != "" {
- ldflags = append(ldflags, fmt.Sprintf("-X 'main.version=%s'", v))
- }
-
- out := []string{}
- if len(ldflags) > 0 {
- out = append(out, "-ldflags="+strings.Join(ldflags, " "))
- }
- return out, nil
-}
-
-func version() (string, error) {
- vt, err := versionTag()
- if err != nil {
- return "", err
- }
- v := []string{vt}
- if b, err := branch(); err != nil {
- return "", err
- } else if b != mainBranch {
- v = append(v, b)
- }
- if uncommittedChanges() {
- v = append(v, "uncommitted")
- }
- return strings.Join(v, "-"), nil
-}
-
-func versionTag() (string, error) {
- return trimOutput("git", "describe", "--tags") // no "--exact-match"
-}
-
-func branch() (string, error) {
- return trimOutput("git", "rev-parse", "--abbrev-ref", "HEAD")
-}
-
-func uncommittedChanges() bool {
- err := sh.Run("git", "diff-index", "--quiet", "HEAD", "--")
- return err != nil
-}
-
-func date() string {
- return time.Now().UTC().Format(time.RFC3339)
-}
-
-func trimOutput(cmd string, args ...string) (string, error) {
- txt, err := sh.Output(cmd, args...)
- if err != nil {
- return "", err
- }
- return strings.TrimSpace(txt), nil
-}
diff --git a/test/data/invoice-hotel.json b/test/data/invoice-hotel.json
index f20c969..040a0eb 100644
--- a/test/data/invoice-hotel.json
+++ b/test/data/invoice-hotel.json
@@ -4,7 +4,7 @@
"uuid": "679a2f25-7483-11ec-9722-7ea2cb436ff6",
"dig": {
"alg": "sha256",
- "val": "aa6690ff798fe637147f95713634f33e34f8c03b1b5bba1b12150a8665c3fb2a"
+ "val": "4b6ec1bb24bdd707520a89e632279966d887f0a3339dc737c30f69f7664c5345"
}
},
"doc": {
@@ -85,12 +85,18 @@
},
{
"i": 2,
- "quantity": "1",
+ "quantity": "2",
"item": {
"name": "Camera Matrimoniale",
"price": "125.00"
},
- "sum": "125.00",
+ "sum": "250.00",
+ "discounts": [
+ {
+ "amount": "10.00",
+ "reason": "Sconto"
+ }
+ ],
"taxes": [
{
"cat": "VAT",
@@ -98,7 +104,7 @@
"percent": "10.0%"
}
],
- "total": "125.00"
+ "total": "240.00"
}
],
"payment": {
@@ -112,9 +118,9 @@
]
},
"totals": {
- "sum": "126.00",
- "tax_included": "11.36",
- "total": "114.64",
+ "sum": "241.00",
+ "tax_included": "21.82",
+ "total": "219.18",
"taxes": {
"categories": [
{
@@ -130,21 +136,21 @@
},
{
"key": "intermediate",
- "base": "113.64",
+ "base": "218.18",
"percent": "10.0%",
- "amount": "11.36"
+ "amount": "21.82"
}
],
- "amount": "11.36"
+ "amount": "21.82"
}
],
- "sum": "11.36"
+ "sum": "21.82"
},
- "tax": "11.36",
- "total_with_tax": "126.00",
- "payable": "126.00",
+ "tax": "21.82",
+ "total_with_tax": "241.00",
+ "payable": "241.00",
"advance": "29.00",
- "due": "97.00"
+ "due": "212.00"
}
}
}
\ No newline at end of file
diff --git a/test/data/out/invoice-hotel.xml b/test/data/out/invoice-hotel.xml
index 3f61e7f..68b47e2 100644
--- a/test/data/out/invoice-hotel.xml
+++ b/test/data/out/invoice-hotel.xml
@@ -62,7 +62,7 @@
EUR
2023-05-21
SAMPLE-002
- 126.00
+ 241.00
@@ -78,9 +78,13 @@
2
Camera Matrimoniale
- 1.00
+ 2.00
113.6364
- 113.6364
+
+ SC
+ 4.5455
+
+ 218.1819
10.00
@@ -92,8 +96,8 @@
10.00
- 113.64
- 11.36
+ 218.18
+ 21.82
@@ -106,7 +110,7 @@
- jYM7kYt27X2bFcXWHZD/pG60gB8fJoJB7rk1U8aOL+9iPydC3Y4Cp3NhBnN/LBpsV9z6cyOkXwGxFJ9BkmVxTQ==
+ 4KAycPCYtM1f6vffnVi5gWc6nFXhyM2gMCRF61WzyF8/jWna05ffdB89Fz1bMn8ijwrJrJDA7iM3sFWBmMgY6g==
@@ -117,7 +121,7 @@
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
- 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
+ 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
diff --git a/test/data/out/invoice-irpef.xml b/test/data/out/invoice-irpef.xml
index 6cb7359..de2a6f2 100644
--- a/test/data/out/invoice-irpef.xml
+++ b/test/data/out/invoice-irpef.xml
@@ -86,7 +86,6 @@
MG
- 0.00
12.34
1026.74
@@ -101,7 +100,7 @@
SC
10.00
- 180.00
+ 9.00
1620.00
22.00
@@ -151,7 +150,7 @@
- aRZnVDtXgaZvA8XfEKmBxoEegr3EY60wYJ2hL5ITB8U1eT1ypLwDqdojVDDl6mqZciZ1Qa5njsZOvQ+IqcPM2A==
+ 4Y4kb/GhgESEb51F/vu9BfbrqjKd4BWtsLXVRW4T9lvrXG/d3cLEWkn1mP+TE8h51kFr2CtB2A397UxFldno5Q==
@@ -162,7 +161,7 @@
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
- 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
+ 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
diff --git a/test/data/out/invoice-simple-with-pec.xml b/test/data/out/invoice-simple-with-pec.xml
index eca3ea0..611dbf5 100644
--- a/test/data/out/invoice-simple-with-pec.xml
+++ b/test/data/out/invoice-simple-with-pec.xml
@@ -89,7 +89,7 @@
SC
10.00
- 180.00
+ 9.00
1620.00
22.00
@@ -133,7 +133,7 @@
- Ay6Y2DYI+Zp74duu8pt2VTaSijjo1EbPImnKjsmL6H2H7fxYu446bgq7qn9yciRr/dpWXpgDDjqzwEFNyf22xg==
+ MJmg642dB8xY5J5mOk/aB98XJACPOC4fl5TeAB5qtn1U26bzx68ykrhl14Q5azhWVHWkcGzBVvdA5Ad7lz0v4A==
@@ -144,7 +144,7 @@
7Tj/Vr2iDe5KuUvZfrT8ntgjkAtz6zIeztpC/liVkbigGbZLGFHcMpSsVtsRc+WIqqwsB7AwFVjqjSIKIDI3uw==
- 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
+ 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
diff --git a/test/data/out/invoice-simple.xml b/test/data/out/invoice-simple.xml
index 3bd72da..7144818 100644
--- a/test/data/out/invoice-simple.xml
+++ b/test/data/out/invoice-simple.xml
@@ -88,7 +88,7 @@
SC
10.00
- 180.00
+ 9.00
1620.00
22.00
@@ -132,7 +132,7 @@
- xke5JVK/SdpKK+kXeJN7DOdnSi3VuwJlfkEmYJ29mivbAOHunuCQsCsTfoHy5SbZn6xmu4xXsARZS2eW8ikGiw==
+ afVc/dLmBshNk73aKEWZoEqvj0ori61V31Moy9pRSph1r7o6LP0fc4HAt0oB4BlHE235RinSa511O1/s3Ymylg==
@@ -143,7 +143,7 @@
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
- ZRzhYTlNddkUHQHzvXFepllgHSASTAAzIyyFzINSc//ZEo59qAT27RARRvRTPknNZPxVMbHqWR5yzDYy+Vxp8PI+CtVpu1G8oKYP+Go2y1KR9tJUGzgbUIowI+9M3IyxFoWEU1kFBloXU80IyL1vExoYt0//u/FiUffMi7mSA+s83W5Jd2fbS7148GWhl310ACP8dx11TbSOpz9KIYB3oINWPcNstTbvTFzQOi3GbPD3iSQl+xbi6hx58DNlq0AGqVKhUsZ5boMZBauORfdnbTrpJuP809EykfWAz1QvVJ0KM3WNTlwYy/T63QrMr/A6FTiDy6oKpv/p+hiLiHRlUA==
+ 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