diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index a244333..14aa2ae 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -15,19 +15,13 @@ jobs: - name: Check out code uses: actions/checkout@v3 - - name: Prepare .netrc - uses: extractions/netrc@v1 - with: - machine: github.com - username: ${{ secrets.GO_MOD_USER }} - password: ${{ secrets.GO_MOD_PASS }} - - name: Set up Go - uses: actions/setup-go@v1 + uses: actions/setup-go@v4 with: - go-version: "1.20.4" + go-version-file: "go.mod" + id: go - name: Lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v2 with: - version: v1.55 + version: v1.58 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 47c63c4..144552a 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -6,30 +6,18 @@ jobs: runs-on: ubuntu-latest steps: - - name: Set up Go - uses: actions/setup-go@v1 - with: - go-version: "1.21.5" - id: go - - name: Check out code uses: actions/checkout@v3 - #- name: Configure git for private modules - # run: | - # git config --global url."https://${{ secrets.GO_MOD_USER }}:${{ secrets.GO_MOD_PASS }}@github.com".insteadOf "https://github.com" - - - name: Prepare .netrc - uses: extractions/netrc@v1 + - name: Set up Go + uses: actions/setup-go@v4 with: - machine: github.com - username: ${{ secrets.GO_MOD_USER }} - password: ${{ secrets.GO_MOD_PASS }} + go-version-file: "go.mod" + id: go - name: Install Dependencies env: GOPROXY: https://proxy.golang.org,direct - GOPRIVATE: github.com/invopop run: go mod download - name: Test diff --git a/.golangci.toml b/.golangci.toml deleted file mode 100644 index 1178f29..0000000 --- a/.golangci.toml +++ /dev/null @@ -1,15 +0,0 @@ -[run] -timeout = "120s" - -[output] -format = "colored-line-number" - -[linters] -enable = [ - "gocyclo", "unconvert", "goimports", "unused", - "vetshadow", "misspell", "nakedret", "errcheck", "revive", "ineffassign", - "goconst", "vet", "unparam", "gofmt" -] - -[issues] -exclude-use-default = false diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..1925251 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,30 @@ +run: + timeout: "120s" + +output: + formats: + - format: "colored-line-number" + +linters: + enable: + - "gocyclo" + - "unconvert" + - "goimports" + - "govet" + #- "misspell" # doesn't handle multilanguage well + - "nakedret" + - "revive" + - "goconst" + - "unparam" + - "gofmt" + - "errname" + - "zerologlint" + +linters-settings: + staticcheck: + # SAxxxx checks in https://staticcheck.io/docs/configuration/options/#checks + # Default: ["*"] + checks: ["all"] + +issues: + exclude-use-default: false diff --git a/address.go b/address.go index d090cb2..0517043 100644 --- a/address.go +++ b/address.go @@ -15,28 +15,28 @@ var ( provinceRegexp = regexp.MustCompile(`^[A-Z]{2}$`) ) -// address from IndirizzoType -type address struct { - Indirizzo string // Street - NumeroCivico string `xml:",omitempty"` // Number - CAP string // Post Code - Comune string // Locality - Provincia string `xml:",omitempty"` // Region - Nazione string // Country Code +// Address from IndirizzoType +type Address struct { + Street string `xml:"Indirizzo"` // Street + Number string `xml:"NumeroCivico,omitempty"` // Number + Code string `xml:"CAP"` // Post Code + Locality string `xml:"Comune"` // Locality + Region string `xml:"Provincia,omitempty"` // Region + Country string `xml:"Nazione"` // Country Code } -func newAddress(addr *org.Address) *address { - ad := &address{ - Indirizzo: addressStreet(addr), - NumeroCivico: addr.Number, - Comune: addr.Locality, - Provincia: addressRegion(addr), - Nazione: addr.Country.String(), +func newAddress(addr *org.Address) *Address { + ad := &Address{ + Street: addressStreet(addr), + Number: addr.Number, + Locality: addr.Locality, + Region: addressRegion(addr), + Country: addr.Country.String(), } if addr.Country == l10n.IT { - ad.CAP = addr.Code + ad.Code = addr.Code } else { - ad.CAP = foreignCAP + ad.Code = foreignCAP } return ad } diff --git a/address_test.go b/address_test.go index b584f38..9c187d3 100644 --- a/address_test.go +++ b/address_test.go @@ -20,7 +20,7 @@ func TestAddressRegion(t *testing.T) { } out := newAddress(addr) - assert.Equal(t, "RM", out.Provincia) + assert.Equal(t, "RM", out.Region) }) t.Run("should ignore text name", func(t *testing.T) { @@ -34,7 +34,7 @@ func TestAddressRegion(t *testing.T) { } out := newAddress(addr) - assert.Empty(t, out.Provincia) + assert.Empty(t, out.Region) }) t.Run("should ignore foreign addresses", func(t *testing.T) { @@ -48,6 +48,6 @@ func TestAddressRegion(t *testing.T) { } out := newAddress(addr) - assert.Empty(t, out.Provincia) + assert.Empty(t, out.Region) }) } diff --git a/fatturapa.go b/fatturapa.go index 494a0a2..25f755f 100644 --- a/fatturapa.go +++ b/fatturapa.go @@ -71,7 +71,7 @@ func (c *Converter) ConvertFromGOBL(env *gobl.Envelope) (*Document, error) { FPANamespace: namespaceFatturaPA, DSigNamespace: namespaceDSig, XSINamespace: namespaceXSI, - Versione: formatoTransmissione(invoice.Customer), + Versione: formatoTransmissione(invoice), SchemaLocation: schemaLocation, FatturaElettronicaHeader: header, FatturaElettronicaBody: []*fatturaElettronicaBody{body}, diff --git a/go.mod b/go.mod index 30fe1bd..be62d46 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/invopop/gobl.fatturapa go 1.20 require ( - github.com/invopop/gobl v0.76.0 + github.com/invopop/gobl v0.80.2-0.20240717130326-6f551acf496e github.com/invopop/xmldsig v0.8.0 github.com/magefile/mage v1.14.0 github.com/spf13/cobra v1.7.0 diff --git a/go.sum b/go.sum index c4c3997..0afb37b 100644 --- a/go.sum +++ b/go.sum @@ -21,8 +21,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/invopop/gobl v0.76.0 h1:WHGJGe+sqljGkcifQMjxaiiYe8kne2bm3Yv4HUlSqOQ= -github.com/invopop/gobl v0.76.0/go.mod h1:3ixShxX1jlOKo5Rw22HVQh3jXnK9AZa7Twcw7L92qn0= +github.com/invopop/gobl v0.80.2-0.20240717130326-6f551acf496e h1:uz7iSPjO714qsaRW3hszLblRhaHas7E/i3HZ2EnDyx0= +github.com/invopop/gobl v0.80.2-0.20240717130326-6f551acf496e/go.mod h1:3ixShxX1jlOKo5Rw22HVQh3jXnK9AZa7Twcw7L92qn0= github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI= github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/invopop/validation v0.3.0 h1:o260kbjXzoBO/ypXDSSrCLL7SxEFUXBsX09YTE9AxZw= diff --git a/header.go b/header.go index a8f44a3..69872d4 100644 --- a/header.go +++ b/header.go @@ -7,18 +7,15 @@ import ( // fatturaElettronicaHeader contains all data related to the parties involved // in the document. type fatturaElettronicaHeader struct { - DatiTrasmissione *datiTrasmissione `xml:",omitempty"` - CedentePrestatore *supplier `xml:",omitempty"` - CessionarioCommittente *customer `xml:",omitempty"` + DatiTrasmissione *datiTrasmissione `xml:"DatiTrasmissione,omitempty"` + Supplier *Supplier `xml:"CedentePrestatore,omitempty"` + Customer *Customer `xml:"CessionarioCommittente,omitempty"` } func newFatturaElettronicaHeader(inv *bill.Invoice, datiTrasmissione *datiTrasmissione) *fatturaElettronicaHeader { - supplier := newCedentePrestatore(inv.Supplier) - customer := newCessionarioCommittente(inv.Customer) - return &fatturaElettronicaHeader{ - DatiTrasmissione: datiTrasmissione, - CedentePrestatore: supplier, - CessionarioCommittente: customer, + DatiTrasmissione: datiTrasmissione, + Supplier: newSupplier(inv.Supplier), + Customer: newCustomer(inv.Customer), } } diff --git a/parties.go b/parties.go index 24d7043..44076ad 100644 --- a/parties.go +++ b/parties.go @@ -1,6 +1,7 @@ package fatturapa import ( + "github.com/invopop/gobl/cbc" "github.com/invopop/gobl/l10n" "github.com/invopop/gobl/org" "github.com/invopop/gobl/regimes/it" @@ -13,164 +14,187 @@ const ( nonEUBusinessTaxCodeDefault = "OO99999999999" ) -type supplier struct { - DatiAnagrafici *datiAnagrafici - Sede *address - IscrizioneREA *iscrizioneREA `xml:",omitempty"` - Contatti *contatti `xml:",omitempty"` +// Supplier describes the seller/provider of the invoice. +type Supplier struct { + Identity *Identity `xml:"DatiAnagrafici"` + Address *Address `xml:"Sede"` + PermanentEstablishment *PermanentEstablishment `xml:"StabileOrganizzazione,omitempty"` + Registration *Registration `xml:"IscrizioneREA,omitempty"` + Contact *Contact `xml:"Contatti,omitempty"` } -type customer struct { - DatiAnagrafici *datiAnagrafici - Sede *address +// Customer contains the details about who the invoice is addressed to. +type Customer struct { + Identity *Identity `xml:"DatiAnagrafici"` + Address *Address `xml:"Sede"` } -// datiAnagrafici contains information related to an individual or company -type datiAnagrafici struct { - IdFiscaleIVA *taxID `xml:",omitempty"` // nolint:revive - // CodiceFiscale is the Italian fiscal code, distinct from TaxID - CodiceFiscale string `xml:",omitempty"` - Anagrafica *anagrafica - // RegimeFiscale identifies the tax system to be applied +// Identity (DatiAnagrafici) contains information related to an individual or company +type Identity struct { + TaxID *TaxID `xml:"IdFiscaleIVA,omitempty"` // nolint:revive + FiscalCode string `xml:"CodiceFiscale,omitempty"` + Profile *Profile `xml:"Anagrafica"` + // FiscaleRegime identifies the tax system to be applied // Has the form RFXX where XX is numeric; required only for the supplier - RegimeFiscale string `xml:",omitempty"` + FiscalRegime string `xml:"RegimeFiscale,omitempty"` } -// anagrafica contains further party information -type anagrafica struct { +// TaxID is the VAT identification number consisting of a country code and the +// actual VAT number. +type TaxID struct { + Country string `xml:"IdPaese"` // ISO 3166-1 alpha-2 country code + Code string `xml:"IdCodice"` +} + +// PermanentEstablishment (StabileOrganizzazione) to be filled in if the seller/provider +// is not resident, but has a permanent establishment in Italy +type PermanentEstablishment struct { + Street string `xml:"Indirizzo"` + Number string `xml:"NumeroCivico,omitempty"` + PostCode string `xml:"CAP"` + Locality string `xml:"Comune"` + Region string `xml:"Provincia,omitempty"` // Province initials (2 characters) for IT country + Country string `xml:"Nazione"` // Country code ISO alpha-2 +} + +// Profile contains identity data of the seller/provider +type Profile struct { // Name of the organization - Denominazione string `xml:",omitempty"` - // Name of the person - Nome string `xml:",omitempty"` + Name string `xml:"Denominazione,omitempty"` + // Natural person's first or given name if no "Denominazione" is provided + Given string `xml:"Name,omitempty"` // Surname of the person - Cognome string `xml:",omitempty"` + Surname string `xml:"Cognome,omitempty"` // Title of the person - Titolo string `xml:",omitempty"` + Title string `xml:"Titolo,omitempty"` // EORI (Economic Operator Registration and Identification) code - CodEORI string `xml:",omitempty"` + EORI string `xml:"CodEORI,omitempty"` } -// iscrizioneREA contains information related to the company registration details (REA) -type iscrizioneREA struct { +// Registration contains information related to the company registration details (REA) +type Registration struct { // Initials of the province where the company's Registry Office is located - Ufficio string + Office string `xml:"Ufficio,omitempty"` // Company's REA registration number - NumeroREA string + Entry string `xml:"NumeroREA,omitempty"` // Company's share capital - CapitaleSociale string `xml:",omitempty"` + Capital string `xml:"CapitaleSociale,omitempty"` // Indication of whether the Company is in liquidation or not. // Possible values: LS (in liquidation), LN (not in liquidation) - StatoLiquidazione string + LiquidationState string `xml:"StatoLiquidazione,omitempty"` } -type contatti struct { - Telefono string `xml:",omitempty"` - Email string `xml:",omitempty"` +// Contact describes how the party can be contacted +type Contact struct { + Telephone string `xml:"Telefono,omitempty"` + Email string `xml:"Email,omitempty"` } -func newCedentePrestatore(s *org.Party) *supplier { - ns := &supplier{ - DatiAnagrafici: &datiAnagrafici{ - IdFiscaleIVA: &taxID{ - IdPaese: s.TaxID.Country.String(), - IdCodice: s.TaxID.Code.String(), +func newSupplier(s *org.Party) *Supplier { + ns := &Supplier{ + Identity: &Identity{ + TaxID: &TaxID{ + Country: s.TaxID.Country.String(), + Code: s.TaxID.Code.String(), }, - Anagrafica: newAnagrafica(s), + Profile: newProfile(s), }, - IscrizioneREA: newIscrizioneREA(s), - Contatti: newContatti(s), + Registration: newRegistration(s), + Contact: newContact(s), } if v, ok := s.Ext[it.ExtKeySDIFiscalRegime]; ok { - ns.DatiAnagrafici.RegimeFiscale = v.String() + ns.Identity.FiscalRegime = v.String() } else { - ns.DatiAnagrafici.RegimeFiscale = "RF01" + ns.Identity.FiscalRegime = "RF01" } if len(s.Addresses) > 0 { - ns.Sede = newAddress(s.Addresses[0]) + ns.Address = newAddress(s.Addresses[0]) } return ns } -func newCessionarioCommittente(c *org.Party) *customer { +func newCustomer(c *org.Party) *Customer { if c == nil { return nil } - nc := new(customer) + nc := new(Customer) if len(c.Addresses) > 0 { - nc.Sede = newAddress(c.Addresses[0]) + nc.Address = newAddress(c.Addresses[0]) } - da := &datiAnagrafici{ - Anagrafica: newAnagrafica(c), + da := &Identity{ + Profile: newProfile(c), } if c.TaxID != nil { - if isCodiceFiscale(c.TaxID) { - da.CodiceFiscale = c.TaxID.Code.String() - } else { - da.IdFiscaleIVA = customerFiscaleIVA(c.TaxID) - } + da.TaxID = customerTaxID(c.TaxID) + } + if id := org.IdentityForKey(c.Identities, it.IdentityKeyFiscalCode); id != nil { + da.FiscalCode = id.Code.String() } - nc.DatiAnagrafici = da + nc.Identity = da return nc } -func newAnagrafica(party *org.Party) *anagrafica { - if len(party.People) > 0 && party.TaxID.Type == it.TaxIdentityTypeIndividual { - name := party.People[0].Name - - return &anagrafica{ - Nome: name.Given, - Cognome: name.Surname, - Titolo: name.Prefix, +func newProfile(party *org.Party) *Profile { + if party.TaxID == nil || party.TaxID.Code == cbc.CodeEmpty { + // not a company + if len(party.People) > 0 { + name := party.People[0].Name + + return &Profile{ + Given: name.Given, + Surname: name.Surname, + Title: name.Prefix, + } } } - return &anagrafica{ - Denominazione: party.Name, + return &Profile{ + Name: party.Name, } } -func newContatti(party *org.Party) *contatti { - c := &contatti{} - +func newContact(party *org.Party) *Contact { + c := new(Contact) if len(party.Emails) > 0 { c.Email = party.Emails[0].Address } - if len(party.Telephones) > 0 { - c.Telefono = party.Telephones[0].Number + c.Telephone = party.Telephones[0].Number } - return c } -func customerFiscaleIVA(id *tax.Identity) *taxID { - idCodice := id.Code.String() +func customerTaxID(id *tax.Identity) *TaxID { + code := id.Code.String() - if idCodice == "" { + if code == "" { + if id.Country == l10n.IT { + return nil + } // Assume private individual - idCodice = nonITCitizenTaxCodeDefault + code = nonITCitizenTaxCodeDefault } else { // Must be a company with a local tax ID if !isEUCountry(id.Country) { - idCodice = nonEUBusinessTaxCodeDefault + code = nonEUBusinessTaxCodeDefault } } - return &taxID{ - IdPaese: id.Country.String(), - IdCodice: idCodice, + return &TaxID{ + Country: id.Country.String(), + Code: code, } } -func newIscrizioneREA(supplier *org.Party) *iscrizioneREA { +func newRegistration(supplier *org.Party) *Registration { if supplier.Registration == nil { return nil } @@ -184,18 +208,10 @@ func newIscrizioneREA(supplier *org.Party) *iscrizioneREA { capitalFormatted = capital.Rescale(2).String() } - return &iscrizioneREA{ - Ufficio: supplier.Registration.Office, - NumeroREA: supplier.Registration.Entry, - CapitaleSociale: capitalFormatted, - StatoLiquidazione: statoLiquidazioneDefault, + return &Registration{ + Office: supplier.Registration.Office, + Entry: supplier.Registration.Entry, + Capital: capitalFormatted, + LiquidationState: statoLiquidazioneDefault, } } - -func isCodiceFiscale(taxID *tax.Identity) bool { - if taxID.Country != l10n.IT { - return false - } - - return len(taxID.Code.String()) == 16 -} diff --git a/parties_test.go b/parties_test.go index 9cdc4f4..dd80143 100644 --- a/parties_test.go +++ b/parties_test.go @@ -6,6 +6,8 @@ import ( "github.com/invopop/gobl.fatturapa/test" "github.com/invopop/gobl/bill" "github.com/invopop/gobl/l10n" + "github.com/invopop/gobl/org" + "github.com/invopop/gobl/regimes/it" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -16,34 +18,34 @@ func TestPartiesSupplier(t *testing.T) { doc, err := test.ConvertFromGOBL(env) require.NoError(t, err) - s := doc.FatturaElettronicaHeader.CedentePrestatore - - assert.Equal(t, "IT", s.DatiAnagrafici.IdFiscaleIVA.IdPaese) - assert.Equal(t, "12345678903", s.DatiAnagrafici.IdFiscaleIVA.IdCodice) - assert.Equal(t, "MªF. Services", s.DatiAnagrafici.Anagrafica.Denominazione) - assert.Equal(t, "RF01", s.DatiAnagrafici.RegimeFiscale) - assert.Equal(t, "VIALE DELLA LIBERTÀ", s.Sede.Indirizzo) - assert.Equal(t, "1", s.Sede.NumeroCivico) - assert.Equal(t, "00100", s.Sede.CAP) - assert.Equal(t, "ROMA", s.Sede.Comune) - assert.Equal(t, "RM", s.Sede.Provincia) - assert.Equal(t, "IT", s.Sede.Nazione) - assert.Equal(t, "RM", s.IscrizioneREA.Ufficio) - assert.Equal(t, "123456", s.IscrizioneREA.NumeroREA) - assert.Equal(t, "50000.00", s.IscrizioneREA.CapitaleSociale) - assert.Equal(t, "LN", s.IscrizioneREA.StatoLiquidazione) + s := doc.FatturaElettronicaHeader.Supplier + + assert.Equal(t, "IT", s.Identity.TaxID.Country) + assert.Equal(t, "12345678903", s.Identity.TaxID.Code) + assert.Equal(t, "MªF. Services", s.Identity.Profile.Name) + assert.Equal(t, "RF02", s.Identity.FiscalRegime) + assert.Equal(t, "VIALE DELLA LIBERTÀ", s.Address.Street) + assert.Equal(t, "1", s.Address.Number) + assert.Equal(t, "00100", s.Address.Code) + assert.Equal(t, "ROMA", s.Address.Locality) + assert.Equal(t, "RM", s.Address.Region) + assert.Equal(t, "IT", s.Address.Country) + assert.Equal(t, "RM", s.Registration.Office) + assert.Equal(t, "123456", s.Registration.Entry) + assert.Equal(t, "50000.00", s.Registration.Capital) + assert.Equal(t, "LN", s.Registration.LiquidationState) }) - t.Run("should set the supplier default regime fiscale", func(t *testing.T) { + t.Run("should set the supplier fiscal regime", func(t *testing.T) { env := test.LoadTestFile("invoice-simple.json") inv := env.Extract().(*bill.Invoice) inv.Supplier.Ext = nil doc, err := test.ConvertFromGOBL(env) require.NoError(t, err) - s := doc.FatturaElettronicaHeader.CedentePrestatore + s := doc.FatturaElettronicaHeader.Supplier - assert.Equal(t, "RF01", s.DatiAnagrafici.RegimeFiscale) + assert.Equal(t, "RF01", s.Identity.FiscalRegime) }) } @@ -53,35 +55,41 @@ func TestPartiesCustomer(t *testing.T) { doc, err := test.ConvertFromGOBL(env) require.NoError(t, err) - c := doc.FatturaElettronicaHeader.CessionarioCommittente - - assert.Nil(t, c.DatiAnagrafici.IdFiscaleIVA) - assert.Equal(t, "MRALNE80E05H501C", c.DatiAnagrafici.CodiceFiscale) - assert.Equal(t, "", c.DatiAnagrafici.Anagrafica.Denominazione) - assert.Equal(t, "MARIO", c.DatiAnagrafici.Anagrafica.Nome) - assert.Equal(t, "LEONI", c.DatiAnagrafici.Anagrafica.Cognome) - assert.Equal(t, "Dott.", c.DatiAnagrafici.Anagrafica.Titolo) - assert.Equal(t, "VIALE DELI LAVORATORI", c.Sede.Indirizzo) - assert.Equal(t, "32", c.Sede.NumeroCivico) - assert.Equal(t, "50100", c.Sede.CAP) - assert.Equal(t, "FIRENZE", c.Sede.Comune) - assert.Equal(t, "FI", c.Sede.Provincia) - assert.Equal(t, "IT", c.Sede.Nazione) + c := doc.FatturaElettronicaHeader.Customer + + assert.Nil(t, c.Identity.TaxID) + assert.Equal(t, "MRALNE80E05H501C", c.Identity.FiscalCode) + assert.Equal(t, "", c.Identity.Profile.Name) + assert.Equal(t, "MARIO", c.Identity.Profile.Given) + assert.Equal(t, "LEONI", c.Identity.Profile.Surname) + assert.Equal(t, "Dott.", c.Identity.Profile.Title) + assert.Equal(t, "VIALE DELI LAVORATORI", c.Address.Street) + assert.Equal(t, "32", c.Address.Number) + assert.Equal(t, "50100", c.Address.Code) + assert.Equal(t, "FIRENZE", c.Address.Locality) + assert.Equal(t, "FI", c.Address.Region) + assert.Equal(t, "IT", c.Address.Country) }) t.Run("should contain customer info with codice fiscale", func(t *testing.T) { env := test.LoadTestFile("invoice-simple.json") test.ModifyInvoice(env, func(inv *bill.Invoice) { - inv.Customer.TaxID.Code = "RSSGNC73A02F205X" + inv.Customer.TaxID.Code = "" + inv.Customer.Identities = org.AddIdentity(inv.Customer.Identities, + &org.Identity{ + Key: it.IdentityKeyFiscalCode, + Code: "RSSGNC73A02F205X", + }, + ) }) doc, err := test.ConvertFromGOBL(env) require.NoError(t, err) - c := doc.FatturaElettronicaHeader.CessionarioCommittente + c := doc.FatturaElettronicaHeader.Customer - assert.Nil(t, c.DatiAnagrafici.IdFiscaleIVA) - assert.Equal(t, "RSSGNC73A02F205X", c.DatiAnagrafici.CodiceFiscale) + assert.Nil(t, c.Identity.TaxID) + assert.Equal(t, "RSSGNC73A02F205X", c.Identity.FiscalCode) }) t.Run("should contain customer info for EU citizen with Tax ID given", func(t *testing.T) { @@ -94,10 +102,10 @@ func TestPartiesCustomer(t *testing.T) { doc, err := test.ConvertFromGOBL(env) require.NoError(t, err) - c := doc.FatturaElettronicaHeader.CessionarioCommittente + c := doc.FatturaElettronicaHeader.Customer - assert.Equal(t, "AT", c.DatiAnagrafici.IdFiscaleIVA.IdPaese) - assert.Equal(t, "81237984062783472", c.DatiAnagrafici.IdFiscaleIVA.IdCodice) + assert.Equal(t, "AT", c.Identity.TaxID.Country) + assert.Equal(t, "81237984062783472", c.Identity.TaxID.Code) }) t.Run("should contain customer info for EU citizen with no Tax ID given", func(t *testing.T) { @@ -110,10 +118,10 @@ func TestPartiesCustomer(t *testing.T) { doc, err := test.ConvertFromGOBL(env) require.NoError(t, err) - c := doc.FatturaElettronicaHeader.CessionarioCommittente + c := doc.FatturaElettronicaHeader.Customer - assert.Equal(t, "SE", c.DatiAnagrafici.IdFiscaleIVA.IdPaese) - assert.Equal(t, "0000000", c.DatiAnagrafici.IdFiscaleIVA.IdCodice) + assert.Equal(t, "SE", c.Identity.TaxID.Country) + assert.Equal(t, "0000000", c.Identity.TaxID.Code) }) t.Run("should replace customer ID info for non-EU citizen with Tax ID given", func(t *testing.T) { @@ -126,10 +134,10 @@ func TestPartiesCustomer(t *testing.T) { doc, err := test.ConvertFromGOBL(env) require.NoError(t, err) - c := doc.FatturaElettronicaHeader.CessionarioCommittente + c := doc.FatturaElettronicaHeader.Customer - assert.Equal(t, "GB", c.DatiAnagrafici.IdFiscaleIVA.IdPaese) - assert.Equal(t, "OO99999999999", c.DatiAnagrafici.IdFiscaleIVA.IdCodice) + assert.Equal(t, "GB", c.Identity.TaxID.Country) + assert.Equal(t, "OO99999999999", c.Identity.TaxID.Code) }) t.Run("should contain customer info for non-EU citizen with no Tax ID given", func(t *testing.T) { @@ -142,10 +150,10 @@ func TestPartiesCustomer(t *testing.T) { doc, err := test.ConvertFromGOBL(env) require.NoError(t, err) - c := doc.FatturaElettronicaHeader.CessionarioCommittente + c := doc.FatturaElettronicaHeader.Customer - assert.Equal(t, "JP", c.DatiAnagrafici.IdFiscaleIVA.IdPaese) - assert.Equal(t, "0000000", c.DatiAnagrafici.IdFiscaleIVA.IdCodice) + assert.Equal(t, "JP", c.Identity.TaxID.Country) + assert.Equal(t, "0000000", c.Identity.TaxID.Code) }) t.Run("should not fail if missing key data", func(t *testing.T) { diff --git a/tax_id.go b/tax_id.go deleted file mode 100644 index c960f06..0000000 --- a/tax_id.go +++ /dev/null @@ -1,10 +0,0 @@ -package fatturapa - -// taxID is the VAT identification number consisting of a country code and the -// actual VAT number. -type taxID struct { - // ISO 3166-1 alpha-2 country code - IdPaese string // nolint:revive - // Actual VAT number - IdCodice string // nolint:revive -} diff --git a/test/data/invoice-irpef.json b/test/data/invoice-irpef.json index 0067b25..8ba8c53 100644 --- a/test/data/invoice-irpef.json +++ b/test/data/invoice-irpef.json @@ -1,257 +1,259 @@ { - "$schema": "https://gobl.org/draft-0/envelope", - "head": { - "uuid": "679a2f25-7483-11ec-9722-7ea2cb436ff6", - "dig": { - "alg": "sha256", - "val": "943bb8169e975f7b5f03e9e28bc9d476b0a715321295d65d9c29c749de171897" - } - }, - "doc": { - "$schema": "https://gobl.org/draft-0/bill/invoice", - "type": "standard", - "series": "SAMPLE", - "code": "001", - "issue_date": "2023-03-02", - "currency": "EUR", - "tax": { - "tags": [ - "freelance" - ] - }, - "supplier": { - "name": "Rossi Digital 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" - } - ] - }, - "customer": { - "name": "MARIO LEONI", - "tax_id": { - "country": "IT", - "type": "individual", - "code": "MRALNE80E05H501C" - }, - "people": [ - { - "name": { - "prefix": "Dott.", - "given": "MARIO", - "surname": "LEONI" - } - } - ], - "inboxes": [ - { - "key": "codice-destinatario", - "code": "ABCDEF1" - } - ], - "addresses": [ - { - "num": "32", - "street": "VIALE DELI LAVORATORI", - "locality": "FIRENZE", - "region": "FI", - "code": "50100", - "country": "IT" - } - ], - "emails": [ - { - "addr": "leoni@mario.com" - } - ] - }, - "lines": [ - { - "i": 1, - "quantity": "20", - "item": { - "name": "Development services", - "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%" - }, - { - "cat": "IRPEF", - "percent": "20.0%", - "ext": { - "it-sdi-retained-tax": "A" - } - } - ], - "total": "1620.00" - }, - { - "i": 2, - "quantity": "1", - "item": { - "name": "Truffle Gathering", - "price": "100.00", - "unit": "h" - }, - "sum": "100.00", - "taxes": [ - { - "cat": "VAT", - "rate": "exempt", - "ext": { - "it-sdi-nature": "N2.2" - } - }, - { - "cat": "IRPEF", - "percent": "50.0%", - "ext": { - "it-sdi-retained-tax": "J" - } - } - ], - "total": "100.00" - } - ], - "discounts": [ - { - "i": 1, - "base": "1720.00", - "percent": "50%", - "amount": "860.00", - "reason": "10th year anniversary discount" - } - ], - "charges": [ - { - "i": 1, - "base": "1720.00", - "percent": "10%", - "amount": "172.00", - "reason": "10th year anniversary charge" - }, - { - "key": "stamp-duty", - "i": 2, - "amount": "12.34" - } - ], - "payment": { - "terms": { - "key": "due-date", - "due_dates": [ - { - "date": "2023-03-02", - "amount": "500.00" - }, - { - "date": "2023-04-02", - "amount": "544.40" - } - ] - }, - "instructions": { - "key": "credit-transfer", - "credit_transfer": [ - { - "iban": "IT60X0542811101000000123456", - "bic": "BCITITMM", - "name": "BANCA POPOLARE DI MILANO" - } - ] - } - }, - "totals": { - "sum": "1720.00", - "discount": "860.00", - "charge": "184.34", - "total": "1044.34", - "taxes": { - "categories": [ - { - "code": "VAT", - "rates": [ - { - "key": "standard", - "base": "1620.00", - "percent": "22.0%", - "amount": "356.40" - }, - { - "key": "exempt", - "ext": { - "it-sdi-nature": "N2.2" - }, - "base": "100.00", - "amount": "0.00" - } - ], - "amount": "356.40" - }, - { - "code": "IRPEF", - "retained": true, - "rates": [ - { - "ext": { - "it-sdi-retained-tax": "A" - }, - "base": "1620.00", - "percent": "20.0%", - "amount": "324.00" - }, - { - "ext": { - "it-sdi-retained-tax": "J" - }, - "base": "100.00", - "percent": "50.0%", - "amount": "50.00" - } - ], - "amount": "374.00" - } - ], - "sum": "-17.60" - }, - "tax": "-17.60", - "total_with_tax": "1026.74", - "payable": "1026.74" - } - } + "$schema": "https://gobl.org/draft-0/envelope", + "head": { + "uuid": "679a2f25-7483-11ec-9722-7ea2cb436ff6", + "dig": { + "alg": "sha256", + "val": "943bb8169e975f7b5f03e9e28bc9d476b0a715321295d65d9c29c749de171897" + } + }, + "doc": { + "$schema": "https://gobl.org/draft-0/bill/invoice", + "type": "standard", + "series": "SAMPLE", + "code": "001", + "issue_date": "2023-03-02", + "currency": "EUR", + "tax": { + "tags": ["freelance"] + }, + "supplier": { + "name": "Rossi Digital 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" + } + ] + }, + "customer": { + "name": "MARIO LEONI", + "tax_id": { + "country": "IT" + }, + "identities": [ + { + "key": "it-fiscal-code", + "code": "MRALNE80E05H501C" + } + ], + "people": [ + { + "name": { + "prefix": "Dott.", + "given": "MARIO", + "surname": "LEONI" + } + } + ], + "inboxes": [ + { + "key": "codice-destinatario", + "code": "ABCDEF1" + } + ], + "addresses": [ + { + "num": "32", + "street": "VIALE DELI LAVORATORI", + "locality": "FIRENZE", + "region": "FI", + "code": "50100", + "country": "IT" + } + ], + "emails": [ + { + "addr": "leoni@mario.com" + } + ] + }, + "lines": [ + { + "i": 1, + "quantity": "20", + "item": { + "name": "Development services", + "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%" + }, + { + "cat": "IRPEF", + "percent": "20.0%", + "ext": { + "it-sdi-retained-tax": "A" + } + } + ], + "total": "1620.00" + }, + { + "i": 2, + "quantity": "1", + "item": { + "name": "Truffle Gathering", + "price": "100.00", + "unit": "h" + }, + "sum": "100.00", + "taxes": [ + { + "cat": "VAT", + "rate": "exempt", + "ext": { + "it-sdi-nature": "N2.2" + } + }, + { + "cat": "IRPEF", + "percent": "50.0%", + "ext": { + "it-sdi-retained-tax": "J" + } + } + ], + "total": "100.00" + } + ], + "discounts": [ + { + "i": 1, + "base": "1720.00", + "percent": "50%", + "amount": "860.00", + "reason": "10th year anniversary discount" + } + ], + "charges": [ + { + "i": 1, + "base": "1720.00", + "percent": "10%", + "amount": "172.00", + "reason": "10th year anniversary charge" + }, + { + "key": "stamp-duty", + "i": 2, + "amount": "12.34" + } + ], + "payment": { + "terms": { + "key": "due-date", + "due_dates": [ + { + "date": "2023-03-02", + "amount": "500.00" + }, + { + "date": "2023-04-02", + "amount": "544.40" + } + ] + }, + "instructions": { + "key": "credit-transfer", + "credit_transfer": [ + { + "iban": "IT60X0542811101000000123456", + "bic": "BCITITMM", + "name": "BANCA POPOLARE DI MILANO" + } + ] + } + }, + "totals": { + "sum": "1720.00", + "discount": "860.00", + "charge": "184.34", + "total": "1044.34", + "taxes": { + "categories": [ + { + "code": "VAT", + "rates": [ + { + "key": "standard", + "base": "1620.00", + "percent": "22.0%", + "amount": "356.40" + }, + { + "key": "exempt", + "ext": { + "it-sdi-nature": "N2.2" + }, + "base": "100.00", + "amount": "0.00" + } + ], + "amount": "356.40" + }, + { + "code": "IRPEF", + "retained": true, + "rates": [ + { + "ext": { + "it-sdi-retained-tax": "A" + }, + "base": "1620.00", + "percent": "20.0%", + "amount": "324.00" + }, + { + "ext": { + "it-sdi-retained-tax": "J" + }, + "base": "100.00", + "percent": "50.0%", + "amount": "50.00" + } + ], + "amount": "374.00" + } + ], + "sum": "-17.60" + }, + "tax": "-17.60", + "total_with_tax": "1026.74", + "payable": "1026.74" + } + } } diff --git a/test/data/invoice-simple.json b/test/data/invoice-simple.json index 53e46d9..44247bb 100644 --- a/test/data/invoice-simple.json +++ b/test/data/invoice-simple.json @@ -1,206 +1,209 @@ { - "$schema": "https://gobl.org/draft-0/envelope", - "head": { - "uuid": "679a2f25-7483-11ec-9722-7ea2cb436ff6", - "dig": { - "alg": "sha256", - "val": "a822e8e5808906fec7f7c01238604745da5303a975215421913e7ab1b26adfc3" - } - }, - "doc": { - "$schema": "https://gobl.org/draft-0/bill/invoice", - "type": "standard", - "series": "SAMPLE", - "code": "001", - "issue_date": "2023-03-02", - "currency": "EUR", - "tax": { - "tags": ["freelance"] - }, - "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" - }, - "people": [ - { - "name": { - "prefix": "Dott.", - "given": "MARIO", - "surname": "LEONI" - } - } - ], - "inboxes": [ - { - "key": "it-sdi-code", - "code": "ABCDEF1" - } - ], - "addresses": [ - { - "num": "32", - "street": "VIALE DELI LAVORATORI", - "locality": "FIRENZE", - "region": "FI", - "code": "50100", - "country": "IT" - } - ], - "emails": [ - { - "addr": "leoni@mario.com" - } - ] - }, - "lines": [ - { - "i": 1, - "quantity": "20", - "item": { - "name": "Development services", - "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" - }, - { - "i": 2, - "quantity": "1", - "item": { - "name": "Special Untaxed Work", - "price": "100.00", - "unit": "h" - }, - "sum": "100.00", - "taxes": [ - { - "cat": "VAT", - "rate": "exempt", - "ext": { - "it-sdi-nature": "N2.2" - } - } - ], - "total": "100.00" - } - ], - "discounts": [ - { - "i": 1, - "base": "1720.00", - "percent": "50%", - "amount": "860.00", - "reason": "10th year anniversary discount" - } - ], - "charges": [ - { - "i": 1, - "base": "1720.00", - "percent": "10%", - "amount": "172.00", - "reason": "10th year anniversary charge" - } - ], - "payment": { - "instructions": { - "key": "card" - } - }, - "totals": { - "sum": "1720.00", - "discount": "860.00", - "charge": "172.00", - "total": "1032.00", - "taxes": { - "categories": [ - { - "code": "VAT", - "rates": [ - { - "key": "standard", - "base": "1620.00", - "percent": "22.0%", - "amount": "356.40" - }, - { - "key": "exempt", - "ext": { - "it-sdi-nature": "N2.2" - }, - "base": "100.00", - "amount": "0.00" - } - ], - "amount": "356.40" - } - ], - "sum": "356.40" - }, - "tax": "356.40", - "total_with_tax": "1388.40", - "payable": "1388.40" - } - } + "$schema": "https://gobl.org/draft-0/envelope", + "head": { + "uuid": "679a2f25-7483-11ec-9722-7ea2cb436ff6", + "dig": { + "alg": "sha256", + "val": "c039dc11fc9e45449c998d603c43f8b3dc3bc87c9cf80d269d7730e7c939a7c3" + } + }, + "doc": { + "$schema": "https://gobl.org/draft-0/bill/invoice", + "uuid": "0190c0ec-8109-756b-a4f0-88c4b542ab6e", + "type": "standard", + "series": "SAMPLE", + "code": "001", + "issue_date": "2023-03-02", + "currency": "EUR", + "tax": { + "tags": [ + "freelance" + ] + }, + "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": "RF02" + } + }, + "customer": { + "name": "MARIO LEONI", + "tax_id": { + "country": "IT", + "code": "09876543217" + }, + "people": [ + { + "name": { + "prefix": "Dott.", + "given": "MARIO", + "surname": "LEONI" + } + } + ], + "inboxes": [ + { + "key": "it-sdi-code", + "code": "ABCDEF1" + } + ], + "addresses": [ + { + "num": "32", + "street": "VIALE DELI LAVORATORI", + "locality": "FIRENZE", + "region": "FI", + "code": "50100", + "country": "IT" + } + ], + "emails": [ + { + "addr": "leoni@mario.com" + } + ] + }, + "lines": [ + { + "i": 1, + "quantity": "20", + "item": { + "name": "Development services", + "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" + }, + { + "i": 2, + "quantity": "1", + "item": { + "name": "Special Untaxed Work", + "price": "100.00", + "unit": "h" + }, + "sum": "100.00", + "taxes": [ + { + "cat": "VAT", + "rate": "exempt", + "ext": { + "it-sdi-nature": "N2.2" + } + } + ], + "total": "100.00" + } + ], + "discounts": [ + { + "i": 1, + "base": "1720.00", + "percent": "50%", + "amount": "860.00", + "reason": "10th year anniversary discount" + } + ], + "charges": [ + { + "i": 1, + "base": "1720.00", + "percent": "10%", + "amount": "172.00", + "reason": "10th year anniversary charge" + } + ], + "payment": { + "instructions": { + "key": "card" + } + }, + "totals": { + "sum": "1720.00", + "discount": "860.00", + "charge": "172.00", + "total": "1032.00", + "taxes": { + "categories": [ + { + "code": "VAT", + "rates": [ + { + "key": "standard", + "base": "1620.00", + "percent": "22.0%", + "amount": "356.40" + }, + { + "key": "exempt", + "ext": { + "it-sdi-nature": "N2.2" + }, + "base": "100.00", + "amount": "0.00" + } + ], + "amount": "356.40" + } + ], + "sum": "356.40" + }, + "tax": "356.40", + "total_with_tax": "1388.40", + "payable": "1388.40" + } + } } diff --git a/test/test.go b/test/test.go index 06189fa..437102b 100644 --- a/test/test.go +++ b/test/test.go @@ -65,7 +65,7 @@ func ConvertFromGOBL(env *gobl.Envelope, converter ...*fatturapa.Converter) (*fa // into XML fatturapa documents. func TestConversion() error { // nolint:revive var files []string - err := filepath.Walk(GetDataPath(), func(path string, info os.FileInfo, err error) error { + err := filepath.Walk(GetDataPath(), func(path string, _ os.FileInfo, _ error) error { if filepath.Ext(path) == ".json" { files = append(files, filepath.Base(path)) } diff --git a/transmission.go b/transmission.go index aeca811..ddf7742 100644 --- a/transmission.go +++ b/transmission.go @@ -9,8 +9,8 @@ import ( ) const ( - formatoTrasmissioneFPA12 = "FPA12" - formatoTrasmissioneFPR12 = "FPR12" + formatoTrasmissioneFPA12 = "FPA12" // B2G + formatoTrasmissioneFPR12 = "FPR12" // B2B or B2C ) // Invoices sent to Italian individuals or businesses can use 0000000 as the @@ -23,11 +23,11 @@ const ( // Data related to the transmission of the invoice type datiTrasmissione struct { - IdTrasmittente *taxID `xml:",omitempty"` // nolint:revive - ProgressivoInvio string `xml:",omitempty"` - FormatoTrasmissione string `xml:",omitempty"` - CodiceDestinatario string - PECDestinatario string `xml:",omitempty"` + IdTrasmittente *TaxID `xml:"IdTrasmittente,omitempty"` // nolint:revive + ProgressivoInvio string `xml:"ProgressivoInvio,omitempty"` + FormatoTrasmissione string `xml:"FormatoTrasmissione,omitempty"` + CodiceDestinatario string `xml:"CodiceDestinatario"` + PECDestinatario string `xml:"PECDestinatario,omitempty"` } func (c *Converter) newDatiTrasmissione(inv *bill.Invoice, env *gobl.Envelope) *datiTrasmissione { @@ -38,25 +38,23 @@ func (c *Converter) newDatiTrasmissione(inv *bill.Invoice, env *gobl.Envelope) * // Do we need to add the transmitter info? if c.Config.Transmitter != nil { - dt.IdTrasmittente = &taxID{ - IdPaese: c.Config.Transmitter.CountryCode, - IdCodice: c.Config.Transmitter.TaxID, + dt.IdTrasmittente = &TaxID{ + Country: c.Config.Transmitter.CountryCode, + Code: c.Config.Transmitter.TaxID, } dt.ProgressivoInvio = env.Head.UUID.String()[:8] - dt.FormatoTrasmissione = formatoTransmissione(inv.Customer) + dt.FormatoTrasmissione = formatoTransmissione(inv) } return dt } -func formatoTransmissione(cus *org.Party) string { - if cus != nil { - taxID := cus.TaxID - if taxID != nil && taxID.Country == l10n.IT && taxID.Type == it.TaxIdentityTypeGovernment { - return formatoTrasmissioneFPA12 - } - } +func formatoTransmissione(inv *bill.Invoice) string { + if inv.Tax != nil && inv.Tax.Ext.Has(it.ExtKeySDIFormat) { + return inv.Tax.Ext[it.ExtKeySDIFormat].String() + } + // Default is always FPR12 for regular non-government invoices return formatoTrasmissioneFPR12 } diff --git a/transmission_test.go b/transmission_test.go index 22ed8b2..08a8953 100644 --- a/transmission_test.go +++ b/transmission_test.go @@ -17,8 +17,8 @@ func TestTransmissionData(t *testing.T) { dt := doc.FatturaElettronicaHeader.DatiTrasmissione - assert.Equal(t, converter.Config.Transmitter.CountryCode, dt.IdTrasmittente.IdPaese) - assert.Equal(t, converter.Config.Transmitter.TaxID, dt.IdTrasmittente.IdCodice) + assert.Equal(t, converter.Config.Transmitter.CountryCode, dt.IdTrasmittente.Country) + assert.Equal(t, converter.Config.Transmitter.TaxID, dt.IdTrasmittente.Code) assert.Equal(t, "679a2f25", dt.ProgressivoInvio) assert.Equal(t, "FPR12", dt.FormatoTrasmissione) assert.Equal(t, "ABCDEF1", dt.CodiceDestinatario)