diff --git a/addons/br/nfse/invoices.go b/addons/br/nfse/invoices.go index aeafb36b..ec4946a6 100644 --- a/addons/br/nfse/invoices.go +++ b/addons/br/nfse/invoices.go @@ -7,6 +7,11 @@ import ( "github.com/invopop/validation" ) +const ( + // FiscalIncentiveDefault is the default value for the fiscal incentive extenstion + FiscalIncentiveDefault = "2" // No incentiva +) + func validateInvoice(inv *bill.Invoice) error { if inv == nil { return nil @@ -57,6 +62,7 @@ func validateSupplier(value interface{}) error { tax.ExtensionsRequires( ExtKeySimplesNacional, ExtKeyMunicipality, + ExtKeyFiscalIncentive, ), validation.Skip, ), @@ -77,3 +83,16 @@ func validateSupplierAddress(value interface{}) error { validation.Field(&obj.Code, validation.Required), ) } + +func normalizeSupplier(sup *org.Party) { + if sup == nil { + return + } + + if !sup.Ext.Has(ExtKeyFiscalIncentive) { + if sup.Ext == nil { + sup.Ext = make(tax.Extensions) + } + sup.Ext[ExtKeyFiscalIncentive] = FiscalIncentiveDefault + } +} diff --git a/addons/br/nfse/invoices_test.go b/addons/br/nfse/invoices_test.go index c9eb3a58..222097bd 100644 --- a/addons/br/nfse/invoices_test.go +++ b/addons/br/nfse/invoices_test.go @@ -164,4 +164,69 @@ func TestSuppliersValidation(t *testing.T) { assert.NotContains(t, err.Error(), "addresses: (0:") } }) + + t.Run("validates extensions", func(t *testing.T) { + sup := new(org.Party) + inv := &bill.Invoice{ + Supplier: sup, + } + err := addon.Validator(inv) + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "br-nfse-simples-nacional: required") + assert.Contains(t, err.Error(), "br-nfse-municipality: required") + assert.Contains(t, err.Error(), "br-nfse-fiscal-incentive: required") + } + + sup.Ext = tax.Extensions{ + nfse.ExtKeySimplesNacional: "1", + nfse.ExtKeyMunicipality: "12345678", + nfse.ExtKeyFiscalIncentive: "2", + } + err = addon.Validator(inv) + if assert.Error(t, err) { + assert.NotContains(t, err.Error(), "br-nfse-simples-nacional: required") + assert.NotContains(t, err.Error(), "br-nfse-municipality: required") + assert.NotContains(t, err.Error(), "br-nfse-fiscal-incentive: required") + } + }) +} + +func TestSuppliersNormalization(t *testing.T) { + addon := tax.AddonForKey(nfse.V1) + + tests := []struct { + name string + supplier *org.Party + out tax.ExtValue + }{ + { + name: "no supplier", + supplier: nil, + }, + { + name: "sets default fiscal incentive", + supplier: &org.Party{}, + out: "2", + }, + { + name: "does not override fiscal incentive", + supplier: &org.Party{ + Ext: tax.Extensions{ + nfse.ExtKeyFiscalIncentive: "1", + }, + }, + out: "1", + }, + } + for _, ts := range tests { + t.Run(ts.name, func(t *testing.T) { + inv := &bill.Invoice{Supplier: ts.supplier} + addon.Normalizer(inv) + if ts.supplier == nil { + assert.Nil(t, inv.Supplier) + } else { + assert.Equal(t, ts.out, inv.Supplier.Ext[nfse.ExtKeyFiscalIncentive]) + } + }) + } } diff --git a/addons/br/nfse/nfse.go b/addons/br/nfse/nfse.go index cb94c9ac..4497e0bc 100644 --- a/addons/br/nfse/nfse.go +++ b/addons/br/nfse/nfse.go @@ -28,6 +28,7 @@ func newAddon() *tax.AddonDef { Extensions: extensions, Identities: identities, Validator: validate, + Normalizer: normalize, } } @@ -42,3 +43,10 @@ func validate(doc any) error { } return nil } + +func normalize(doc any) { + switch obj := doc.(type) { + case *bill.Invoice: + normalizeSupplier(obj.Supplier) + } +} diff --git a/regimes/br/README.md b/regimes/br/README.md index 7543e630..f9d4e34b 100644 --- a/regimes/br/README.md +++ b/regimes/br/README.md @@ -132,10 +132,10 @@ For example: Report whether the party benefits from a fiscal incentive using the `br-nfse-fiscal-incentive` extension set to any of these codes: -| Code | Description | -| ---- | ----------------------- | -| `1` | Has incentive | -| `2` | Does not have incentive | +| Code | Description | +| ---- | --------------------------------- | +| `1` | Has incentive | +| `2` | Does not have incentive (Default) | For example: