diff --git a/doc/breakdown.go b/doc/breakdown.go index 5960e5a..47f5ddf 100644 --- a/doc/breakdown.go +++ b/doc/breakdown.go @@ -94,6 +94,9 @@ type taxInfo struct { } func newTipoDesglose(gobl *bill.Invoice) *TipoDesglose { + if gobl.Totals == nil || gobl.Totals.Taxes == nil { + return nil + } catTotal := gobl.Totals.Taxes.Category(tax.CategoryVAT) if catTotal == nil { return nil @@ -265,7 +268,7 @@ func (t taxInfo) isNoSujeta(r *tax.RateTotal) bool { if t.customerRates { return true } - return r.Percent == nil && r.Ext[tbai.ExtKeyExemption].Code().In(notSubjectExemptionCodes...) + return r.Percent == nil && r.Ext[tbai.ExtKeyExemption].In(notSubjectExemptionCodes...) } func (t taxInfo) causaNoSujeta(r *tax.RateTotal) string { @@ -276,5 +279,5 @@ func (t taxInfo) causaNoSujeta(r *tax.RateTotal) string { } func (taxInfo) isExenta(r *tax.RateTotal) bool { - return r.Percent == nil && !r.Ext[tbai.ExtKeyExemption].Code().In(notSubjectExemptionCodes...) + return r.Percent == nil && !r.Ext[tbai.ExtKeyExemption].In(notSubjectExemptionCodes...) } diff --git a/doc/invoice.go b/doc/invoice.go index 3a6c2b2..bf13168 100644 --- a/doc/invoice.go +++ b/doc/invoice.go @@ -3,8 +3,8 @@ package doc import ( "github.com/invopop/gobl/addons/es/tbai" "github.com/invopop/gobl/bill" - "github.com/invopop/gobl/cbc" "github.com/invopop/gobl/num" + "github.com/invopop/gobl/org" "github.com/invopop/gobl/regimes/es" "github.com/invopop/gobl/tax" ) @@ -109,22 +109,24 @@ func newDatosFactura(inv *bill.Invoice) (*DatosFactura, error) { }, nil } -func newDescription(notes []*cbc.Note) (string, error) { +func newDescription(notes []*org.Note) (string, error) { for _, note := range notes { - if note.Key == cbc.NoteKeyGeneral { + if note.Key == org.NoteKeyGeneral { return note.Text, nil } } - return "", validationErr(`notes: missing note with key '%s'`, cbc.NoteKeyGeneral) + return "", validationErr(`notes: missing note with key '%s'`, org.NoteKeyGeneral) } func newImporteTotal(inv *bill.Invoice) string { totalWithDiscounts := inv.Totals.Total totalTaxes := num.MakeAmount(0, 2) - for _, category := range inv.Totals.Taxes.Categories { - if !category.Retained { - totalTaxes = totalTaxes.Add(category.Amount) + if inv.Totals.Taxes != nil { + for _, category := range inv.Totals.Taxes.Categories { + if !category.Retained { + totalTaxes = totalTaxes.Add(category.Amount) + } } } @@ -133,9 +135,11 @@ func newImporteTotal(inv *bill.Invoice) string { func newRetencionSoportada(inv *bill.Invoice) string { totalRetention := num.MakeAmount(0, 2) - for _, category := range inv.Totals.Taxes.Categories { - if category.Retained { - totalRetention = totalRetention.Add(category.Amount) + if inv.Totals.Taxes != nil { + for _, category := range inv.Totals.Taxes.Categories { + if category.Retained { + totalRetention = totalRetention.Add(category.Amount) + } } } @@ -204,6 +208,9 @@ func newFacturasRectificadasSustituidas(inv *bill.Invoice) *FacturasRectificadas } func hasSurchargedLines(inv *bill.Invoice) bool { + if inv.Totals == nil || inv.Totals.Taxes == nil { + return false + } vat := inv.Totals.Taxes.Category(tax.CategoryVAT) if vat == nil { return false diff --git a/doc/invoice_test.go b/doc/invoice_test.go index 5eb3a91..42d072b 100644 --- a/doc/invoice_test.go +++ b/doc/invoice_test.go @@ -9,7 +9,6 @@ import ( "github.com/invopop/gobl/addons/es/tbai" "github.com/invopop/gobl/bill" "github.com/invopop/gobl/cal" - "github.com/invopop/gobl/cbc" "github.com/invopop/gobl/num" "github.com/invopop/gobl/org" "github.com/invopop/gobl/regimes/es" @@ -67,8 +66,8 @@ func TestFacturaConversion(t *testing.T) { t.Run("should fill invoice description from general note", func(t *testing.T) { goblInvoice := test.LoadInvoice("sample-invoice.json") - goblInvoice.Notes = []*cbc.Note{ - {Key: cbc.NoteKeyGeneral, Text: "Description of invoice"}, + goblInvoice.Notes = []*org.Note{ + {Key: org.NoteKeyGeneral, Text: "Description of invoice"}, } invoice, _ := doc.NewTicketBAI(goblInvoice, ts, role, doc.ZoneBI) @@ -79,7 +78,7 @@ func TestFacturaConversion(t *testing.T) { t.Run("should return error if no description (general note) found", func(t *testing.T) { goblInvoice := test.LoadInvoice("sample-invoice.json") - goblInvoice.Notes = []*cbc.Note{} + goblInvoice.Notes = []*org.Note{} _, err := doc.NewTicketBAI(goblInvoice, ts, role, doc.ZoneBI) diff --git a/doc/parties.go b/doc/parties.go index d1cf00a..a5f9293 100644 --- a/doc/parties.go +++ b/doc/parties.go @@ -75,7 +75,7 @@ func newDestinatario(party *org.Party) (*IDDestinatario, error) { } if len(party.Addresses) > 0 && party.Addresses[0].Code != "" { - d.CodigoPostal = party.Addresses[0].Code + d.CodigoPostal = party.Addresses[0].Code.String() d.Direccion = formatAddress(party.Addresses[0]) } diff --git a/document.go b/document.go index 5f1673a..26a2f07 100644 --- a/document.go +++ b/document.go @@ -27,6 +27,9 @@ func (c *Client) Convert(env *gobl.Envelope) (*doc.TicketBAI, error) { if inv.Supplier.TaxID.Country != l10n.ES.Tax() { return nil, ErrValidation.withMessage("only spanish invoices are supported") } + if inv.Totals == nil || inv.Totals.Taxes == nil { + return nil, ErrValidation.withMessage("missing taxes") + } zone := zoneFor(inv) if zone == "" { @@ -36,7 +39,7 @@ func (c *Client) Convert(env *gobl.Envelope) (*doc.TicketBAI, error) { out, err := doc.NewTicketBAI(inv, c.CurrentTime(), c.issuerRole, zone) if err != nil { if _, ok := err.(*doc.ValidationError); ok { - return nil, ErrValidation.withMessage(err.Error()) + return nil, ErrValidation.withMessage(err.Error()) //nolint:govet } return nil, err diff --git a/go.mod b/go.mod index 190bbd4..2f17d0c 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ toolchain go1.22.1 require ( github.com/go-resty/resty/v2 v2.13.1 - github.com/invopop/gobl v0.204.2-0.20241105091334-a418e60fb2b5 + github.com/invopop/gobl v0.208.0 github.com/invopop/xmldsig v0.10.0 github.com/joho/godotenv v1.5.1 github.com/lestrrat-go/libxml2 v0.0.0-20231124114421-99c71026c2f5 diff --git a/go.sum b/go.sum index 53d12dc..9d29aaf 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/gobl v0.204.2-0.20241105091334-a418e60fb2b5 h1:MLlwU4olYRx1FFa5pLur6PTTaXdaLfT7gscZ6DjkxTw= github.com/invopop/gobl v0.204.2-0.20241105091334-a418e60fb2b5/go.mod h1:DmPohPel8b3ta4nDKnXRNzWQlB89cN74e0/WwPUEZUU= +github.com/invopop/gobl v0.208.0 h1:qy5GHXELMy7qBtvgLL8m4d/aBgBaxq0bdAaae4a2luE= +github.com/invopop/gobl v0.208.0/go.mod h1:DmPohPel8b3ta4nDKnXRNzWQlB89cN74e0/WwPUEZUU= 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.7.0 h1:NBPLqvYGmLZLQuk5jh0PbaBBetJW7f2VEk/BTWJkGBU= diff --git a/test/data/credit-note-es-es-tbai.json b/test/data/credit-note-es-es-tbai.json index 96fd3f0..b449be6 100644 --- a/test/data/credit-note-es-es-tbai.json +++ b/test/data/credit-note-es-es-tbai.json @@ -4,7 +4,7 @@ "uuid": "0192237d-4943-72a7-b1d0-51dca3e8bc71", "dig": { "alg": "sha256", - "val": "f7165b79e26a6d53e163030b0312e80d15f8af189e0eb51d98864cb06c64e9fc" + "val": "c703a148a9395e3a14882d582618fe6503ab4719007715449c752eaf5cc22831" } }, "doc": { @@ -86,7 +86,10 @@ "item": { "name": "Development services", "price": "90.00", - "unit": "h" + "unit": "h", + "ext": { + "es-tbai-product": "services" + } }, "sum": "1800.00", "discounts": [ @@ -110,7 +113,10 @@ "quantity": "1", "item": { "name": "Financial service", - "price": "10.00" + "price": "10.00", + "ext": { + "es-tbai-product": "services" + } }, "sum": "10.00", "discounts": [ diff --git a/test/data/invoice-es-es-b2c.json b/test/data/invoice-es-es-b2c.json index b86f37e..ba1b91e 100644 --- a/test/data/invoice-es-es-b2c.json +++ b/test/data/invoice-es-es-b2c.json @@ -4,7 +4,7 @@ "uuid": "0192d3c7-2dc5-74cf-b263-5892feb44432", "dig": { "alg": "sha256", - "val": "58a808907be339175b8fa53ff5f326f1f074641cfbb653f7f1619763e1f4f2d7" + "val": "57cfc9eb9cc3c8b0013b7ad7ff6fc8b0320d9afbeda923eea05c463bd3cc16cd" } }, "doc": { @@ -56,7 +56,10 @@ "item": { "name": "Development services", "price": "90.00", - "unit": "h" + "unit": "h", + "ext": { + "es-tbai-product": "services" + } }, "sum": "1800.00", "discounts": [ @@ -83,7 +86,10 @@ "quantity": "1", "item": { "name": "Some merch", - "price": "90.00" + "price": "90.00", + "ext": { + "es-tbai-product": "services" + } }, "sum": "90.00", "taxes": [ @@ -103,7 +109,10 @@ "quantity": "1", "item": { "name": "Some essential needs merch", - "price": "30.00" + "price": "30.00", + "ext": { + "es-tbai-product": "services" + } }, "sum": "30.00", "taxes": [ diff --git a/test/data/invoice-es-nl-b2c.json b/test/data/invoice-es-nl-b2c.json index 4e7c1cc..881a847 100644 --- a/test/data/invoice-es-nl-b2c.json +++ b/test/data/invoice-es-nl-b2c.json @@ -4,7 +4,7 @@ "uuid": "0192237d-4965-7c4b-914e-3a62f8e074e5", "dig": { "alg": "sha256", - "val": "0ab95209a3a9da3f9e4500a39cd9685f142ff86d42e3d143de14042fe6639749" + "val": "b796c7ee85d32112c3ce7690f4ba5498eaf196ab9df57ea3a2128fe90247b612" } }, "doc": { @@ -68,7 +68,10 @@ "item": { "name": "Development services", "price": "90.00", - "unit": "h" + "unit": "h", + "ext": { + "es-tbai-product": "services" + } }, "sum": "1800.00", "discounts": [ @@ -96,7 +99,10 @@ "quantity": "1", "item": { "name": "Some merch", - "price": "90.00" + "price": "90.00", + "ext": { + "es-tbai-product": "services" + } }, "sum": "90.00", "taxes": [ @@ -117,7 +123,10 @@ "quantity": "1", "item": { "name": "Some essential needs merch", - "price": "30.00" + "price": "30.00", + "ext": { + "es-tbai-product": "services" + } }, "sum": "30.00", "taxes": [ diff --git a/test/data/invoice-es-nl-tbai-b2c-explicit.json b/test/data/invoice-es-nl-tbai-b2c-explicit.json index e96e6e2..231ce6c 100644 --- a/test/data/invoice-es-nl-tbai-b2c-explicit.json +++ b/test/data/invoice-es-nl-tbai-b2c-explicit.json @@ -4,7 +4,7 @@ "uuid": "0192237d-498e-7273-bc95-6a1daf16cb37", "dig": { "alg": "sha256", - "val": "bc1cc3c6887e628c94e42d011041522695a73005a3daf4b0f521b9a297e9e97f" + "val": "68b6594f9204664935f572e9fc537e84f680c86a4a325f61ddd646685ffcbde7" } }, "doc": { @@ -65,7 +65,10 @@ "item": { "name": "Development services", "price": "90.00", - "unit": "h" + "unit": "h", + "ext": { + "es-tbai-product": "services" + } }, "sum": "1800.00", "discounts": [ diff --git a/test/data/invoice-es-nl-tbai-exempt.json b/test/data/invoice-es-nl-tbai-exempt.json index b39f90c..364f6e2 100644 --- a/test/data/invoice-es-nl-tbai-exempt.json +++ b/test/data/invoice-es-nl-tbai-exempt.json @@ -4,7 +4,7 @@ "uuid": "0192237d-49c2-79a7-8249-4280119d8ade", "dig": { "alg": "sha256", - "val": "0294246ded2b071f8c6eb6011251f3bd254726613b5e7402abcbc2c91e96ecd1" + "val": "f9db4d04b45e555f37068e2d363c153d0a36e29bbdfd828452a78a92491babec" } }, "doc": { @@ -60,7 +60,10 @@ "item": { "name": "Development services", "price": "90.00", - "unit": "h" + "unit": "h", + "ext": { + "es-tbai-product": "services" + } }, "sum": "1800.00", "discounts": [ diff --git a/test/data/invoice-ss.json b/test/data/invoice-ss.json index 61cc1cb..e386688 100644 --- a/test/data/invoice-ss.json +++ b/test/data/invoice-ss.json @@ -4,7 +4,7 @@ "uuid": "0192fbbc-005b-7eea-8973-210ca4c0ffdc", "dig": { "alg": "sha256", - "val": "265465a226476105b9339754dd8e8856033fca217b8b6e89a984295de402b4b6" + "val": "38e8aef5c956bd0ca7ab99d559bdb13174a3f6af82feab70e1e7242bfe10abe7" } }, "doc": { @@ -74,7 +74,10 @@ "quantity": "10", "item": { "name": "Item being purchased", - "price": "100.00" + "price": "100.00", + "ext": { + "es-tbai-product": "services" + } }, "sum": "1000.00", "discounts": [ diff --git a/test/data/invoice-vi.json b/test/data/invoice-vi.json index e3a54a1..faf9409 100644 --- a/test/data/invoice-vi.json +++ b/test/data/invoice-vi.json @@ -4,7 +4,7 @@ "uuid": "0192fd51-6cd8-73f2-a182-8c4d3901d125", "dig": { "alg": "sha256", - "val": "a9fcef2ca22b63043a5c66137a39f8ad2be7db01c42ad94f4c5c80ee060c49b0" + "val": "6503a793d90d96cefdfd1652d219a43e44472eb5e8f741d43cf86cb8f984b2ec" } }, "doc": { @@ -74,7 +74,10 @@ "quantity": "10", "item": { "name": "Item being purchased", - "price": "100.00" + "price": "100.00", + "ext": { + "es-tbai-product": "services" + } }, "sum": "1000.00", "discounts": [ diff --git a/test/data/sample-invoice-export.json b/test/data/sample-invoice-export.json index c9de11c..1b90c24 100644 --- a/test/data/sample-invoice-export.json +++ b/test/data/sample-invoice-export.json @@ -4,7 +4,7 @@ "uuid": "0192fbd4-5087-708c-9b31-e8f653ef0c52", "dig": { "alg": "sha256", - "val": "27db0398b2f300511bfb0401a576876e5061248930ba31fb4d3bd140bddd5a6f" + "val": "963d8171e4ecb8b3c6fa952ce32849d2093fc9f56b9ee35a61741b043bc588d4" } }, "doc": { @@ -71,7 +71,10 @@ "quantity": "6", "item": { "name": "Item being purchased", - "price": "100.00" + "price": "100.00", + "ext": { + "es-tbai-product": "services" + } }, "sum": "600.00", "taxes": [ @@ -91,7 +94,10 @@ "quantity": "5", "item": { "name": "Another item being purchased", - "price": "20.00" + "price": "20.00", + "ext": { + "es-tbai-product": "services" + } }, "sum": "100.00", "taxes": [ @@ -111,7 +117,10 @@ "quantity": "2", "item": { "name": "Service being paid", - "price": "150.00" + "price": "150.00", + "ext": { + "es-tbai-product": "services" + } }, "sum": "300.00", "taxes": [ diff --git a/test/data/sample-invoice-supplier2.json b/test/data/sample-invoice-supplier2.json index 083e0bd..120b52f 100644 --- a/test/data/sample-invoice-supplier2.json +++ b/test/data/sample-invoice-supplier2.json @@ -4,7 +4,7 @@ "uuid": "0192fbd4-81b4-75ed-899b-eb4c18a65f1c", "dig": { "alg": "sha256", - "val": "177b1a668214b82d634010ca686f671872c4f98865f4c259b88d8025dfbc3aaf" + "val": "cff6f81c8bb31fcf3fbed5429b07a2a9fd36d5ba349592ec83d70f63399be1c5" } }, "doc": { @@ -74,7 +74,10 @@ "quantity": "15", "item": { "name": "Item being purchased", - "price": "90.00" + "price": "90.00", + "ext": { + "es-tbai-product": "services" + } }, "sum": "1350.00", "discounts": [ diff --git a/test/data/sample-invoice.json b/test/data/sample-invoice.json index 7b3de71..3d369aa 100644 --- a/test/data/sample-invoice.json +++ b/test/data/sample-invoice.json @@ -4,7 +4,7 @@ "uuid": "0192fbd4-a54b-7d7a-be6e-258ea15f17a2", "dig": { "alg": "sha256", - "val": "2900e06e5224e2b3c6bf004d036ee3356d3f2a4220fd3deaa80ae871915670b7" + "val": "3d0c71a198b2d0bcf3c07dede6e291c2c552577d7b20c13339fcbc3b77d38ab2" } }, "doc": { @@ -74,7 +74,10 @@ "quantity": "10", "item": { "name": "Item being purchased", - "price": "100.00" + "price": "100.00", + "ext": { + "es-tbai-product": "services" + } }, "sum": "1000.00", "discounts": [ diff --git a/test/data/sample-invoice2.json b/test/data/sample-invoice2.json index ca35e79..3a97c14 100644 --- a/test/data/sample-invoice2.json +++ b/test/data/sample-invoice2.json @@ -4,7 +4,7 @@ "uuid": "0192fbd5-3b54-7735-a8de-2e4f13219f68", "dig": { "alg": "sha256", - "val": "61ef29f84ae8f5fe2e0366a0a0dd3582c75dcd46884335619a9d1129343bfa56" + "val": "f598c765c4aa997dff2f37c711415164a459dabd7805d39fcd0321b87bc21cd0" } }, "doc": { @@ -74,7 +74,10 @@ "quantity": "15", "item": { "name": "Item being purchased", - "price": "90.00" + "price": "90.00", + "ext": { + "es-tbai-product": "services" + } }, "sum": "1350.00", "discounts": [