Skip to content

Commit

Permalink
Discounts applied per line item price instead of sum
Browse files Browse the repository at this point in the history
  • Loading branch information
samlown committed Aug 13, 2024
1 parent 888cbe2 commit 50826ae
Show file tree
Hide file tree
Showing 10 changed files with 76 additions and 173 deletions.
14 changes: 3 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
6 changes: 3 additions & 3 deletions body.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
10 changes: 8 additions & 2 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 ""
Expand Down
37 changes: 21 additions & 16 deletions items.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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()
}
}
Expand All @@ -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),
Expand All @@ -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 {
Expand Down
109 changes: 0 additions & 109 deletions mage.go

This file was deleted.

36 changes: 21 additions & 15 deletions test/data/invoice-hotel.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"uuid": "679a2f25-7483-11ec-9722-7ea2cb436ff6",
"dig": {
"alg": "sha256",
"val": "aa6690ff798fe637147f95713634f33e34f8c03b1b5bba1b12150a8665c3fb2a"
"val": "4b6ec1bb24bdd707520a89e632279966d887f0a3339dc737c30f69f7664c5345"
}
},
"doc": {
Expand Down Expand Up @@ -85,20 +85,26 @@
},
{
"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",
"rate": "intermediate",
"percent": "10.0%"
}
],
"total": "125.00"
"total": "240.00"
}
],
"payment": {
Expand All @@ -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": [
{
Expand All @@ -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"
}
}
}
18 changes: 11 additions & 7 deletions test/data/out/invoice-hotel.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
<Divisa>EUR</Divisa>
<Data>2023-05-21</Data>
<Numero>SAMPLE-002</Numero>
<ImportoTotaleDocumento>126.00</ImportoTotaleDocumento>
<ImportoTotaleDocumento>241.00</ImportoTotaleDocumento>
</DatiGeneraliDocumento>
</DatiGenerali>
<DatiBeniServizi>
Expand All @@ -78,9 +78,13 @@
<DettaglioLinee>
<NumeroLinea>2</NumeroLinea>
<Descrizione>Camera Matrimoniale</Descrizione>
<Quantita>1.00</Quantita>
<Quantita>2.00</Quantita>
<PrezzoUnitario>113.6364</PrezzoUnitario>
<PrezzoTotale>113.6364</PrezzoTotale>
<ScontoMaggiorazione>
<Tipo>SC</Tipo>
<Importo>4.5455</Importo>
</ScontoMaggiorazione>
<PrezzoTotale>218.1819</PrezzoTotale>
<AliquotaIVA>10.00</AliquotaIVA>
</DettaglioLinee>
<DatiRiepilogo>
Expand All @@ -92,8 +96,8 @@
</DatiRiepilogo>
<DatiRiepilogo>
<AliquotaIVA>10.00</AliquotaIVA>
<ImponibileImporto>113.64</ImponibileImporto>
<Imposta>11.36</Imposta>
<ImponibileImporto>218.18</ImponibileImporto>
<Imposta>21.82</Imposta>
</DatiRiepilogo>
</DatiBeniServizi>
</FatturaElettronicaBody>
Expand All @@ -106,7 +110,7 @@
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></ds:DigestMethod>
<ds:DigestValue>jYM7kYt27X2bFcXWHZD/pG60gB8fJoJB7rk1U8aOL+9iPydC3Y4Cp3NhBnN/LBpsV9z6cyOkXwGxFJ9BkmVxTQ==</ds:DigestValue>
<ds:DigestValue>4KAycPCYtM1f6vffnVi5gWc6nFXhyM2gMCRF61WzyF8/jWna05ffdB89Fz1bMn8ijwrJrJDA7iM3sFWBmMgY6g==</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#Certificate-679a2f25-7483-11ec-9722-7ea2cb436ff6">
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></ds:DigestMethod>
Expand All @@ -117,7 +121,7 @@
<ds:DigestValue>7Tj/Vr2iDe5KuUvZfrT8ntgjkAtz6zIeztpC/liVkbigGbZLGFHcMpSsVtsRc+WIqqwsB7AwFVjqjSIKIDI3uw==</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue Id="Signature-679a2f25-7483-11ec-9722-7ea2cb436ff6-SignatureValue">SEsDrtqY+j1efLLDlmmOdpVRA1LqFp2a1uigdfpUmeHB+YpGEQrmrImXqoxcw4sLyKYFDg81PptRg1lkAmA0+JaRw0RdX2FofnuUe72yzZiHvUcMTg9WjiEUrkrMJ2i6DDKpFC3ewCb6UqA6NQmhBUSKwq8MQ9AhQdgfC5M/yA9diAgT8Z9ntMckryFKH5U27LNXD/06kVvulyOxsA4cRRPevk4jxu55v/Oeu5C6MMy5ALgKLuJqIto6G67U1tYgco4Mpg4QIZ/5siajMrMrv0ohu8sLnD2FWQTuI8ngaveULpmPg9TSQeBShuMB+i84hSWZbLwt9H9svISRQNnY0w==</ds:SignatureValue>
<ds:SignatureValue Id="Signature-679a2f25-7483-11ec-9722-7ea2cb436ff6-SignatureValue">RewHnpuyoODyss60TEGSUPnYl9F4hpP7zPJkSenU8Sf2m1I53DD7shn+ioYVssu4pDtKvGE/EPaggktb9F14ov7kc+oPVAoX9vtho+yQelUtHFxFVRq1qgHcath9q1H2Iekm14HiK6Xm7ZUqQlxJn4LDtVU35eVfIeRGGu2j1vxdKdXNxQMZSd4wO2/7Xk5FuKNoHPYchkQUI+udWpW2SasxxXHvT1QFfLsBjHbIH735GhDw2f+rR/0GHolxFM/SSrCJzuCcBeGUAY9bDMCCQUWFMFf/UZnaWNPJnpfrQWbo2ja90JLfClofRY5nYkwB2HPhpKppu/b/gg3GYIMxFw==</ds:SignatureValue>
<ds:KeyInfo Id="Certificate-679a2f25-7483-11ec-9722-7ea2cb436ff6">
<ds:X509Data>
<ds:X509Certificate>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</ds:X509Certificate>
Expand Down
Loading

0 comments on commit 50826ae

Please sign in to comment.