odoo-addon-l10n-br-fiscal 16.0.1.18.0.3__py3-none-any.whl → 16.0.1.18.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of odoo-addon-l10n-br-fiscal might be problematic. Click here for more details.

Files changed (42) hide show
  1. odoo/addons/l10n_br_fiscal/README.rst +225 -105
  2. odoo/addons/l10n_br_fiscal/__manifest__.py +1 -1
  3. odoo/addons/l10n_br_fiscal/i18n/l10n_br_fiscal.pot +93 -5
  4. odoo/addons/l10n_br_fiscal/models/comment.py +4 -4
  5. odoo/addons/l10n_br_fiscal/models/data_abstract.py +3 -3
  6. odoo/addons/l10n_br_fiscal/models/data_ncm_nbs_abstract.py +7 -9
  7. odoo/addons/l10n_br_fiscal/models/document_eletronic.py +3 -4
  8. odoo/addons/l10n_br_fiscal/models/document_event.py +1 -1
  9. odoo/addons/l10n_br_fiscal/models/document_fiscal_line_mixin_methods.py +4 -3
  10. odoo/addons/l10n_br_fiscal/models/document_fiscal_mixin_fields.py +2 -2
  11. odoo/addons/l10n_br_fiscal/models/document_fiscal_mixin_methods.py +37 -31
  12. odoo/addons/l10n_br_fiscal/models/document_move_mixin.py +3 -1
  13. odoo/addons/l10n_br_fiscal/models/document_serie.py +1 -1
  14. odoo/addons/l10n_br_fiscal/models/icms_regulation.py +13 -6
  15. odoo/addons/l10n_br_fiscal/models/product_template.py +2 -2
  16. odoo/addons/l10n_br_fiscal/models/subsequent_document.py +0 -1
  17. odoo/addons/l10n_br_fiscal/models/subsequent_operation.py +0 -2
  18. odoo/addons/l10n_br_fiscal/models/tax_definition.py +1 -5
  19. odoo/addons/l10n_br_fiscal/readme/CONFIGURE.md +7 -0
  20. odoo/addons/l10n_br_fiscal/readme/CONTRIBUTORS.md +12 -0
  21. odoo/addons/l10n_br_fiscal/readme/DESCRIPTION.md +196 -0
  22. odoo/addons/l10n_br_fiscal/readme/HISTORY.md +1 -0
  23. odoo/addons/l10n_br_fiscal/readme/INSTALL.md +5 -0
  24. odoo/addons/l10n_br_fiscal/readme/ROADMAP.md +1 -0
  25. odoo/addons/l10n_br_fiscal/readme/USAGE.md +4 -0
  26. odoo/addons/l10n_br_fiscal/static/description/index.html +155 -43
  27. odoo/addons/l10n_br_fiscal/tests/test_fiscal_document_generic.py +1 -1
  28. odoo/addons/l10n_br_fiscal/tests/test_ibpt.py +1 -2
  29. odoo/addons/l10n_br_fiscal/tools.py +2 -2
  30. odoo_addon_l10n_br_fiscal-16.0.1.18.2.dist-info/METADATA +364 -0
  31. {odoo_addon_l10n_br_fiscal-16.0.1.18.0.3.dist-info → odoo_addon_l10n_br_fiscal-16.0.1.18.2.dist-info}/RECORD +33 -33
  32. {odoo_addon_l10n_br_fiscal-16.0.1.18.0.3.dist-info → odoo_addon_l10n_br_fiscal-16.0.1.18.2.dist-info}/WHEEL +1 -1
  33. odoo_addon_l10n_br_fiscal-16.0.1.18.2.dist-info/top_level.txt +1 -0
  34. odoo/addons/l10n_br_fiscal/readme/CONFIGURE.rst +0 -5
  35. odoo/addons/l10n_br_fiscal/readme/CONTRIBUTORS.rst +0 -19
  36. odoo/addons/l10n_br_fiscal/readme/DESCRIPTION.rst +0 -103
  37. odoo/addons/l10n_br_fiscal/readme/HISTORY.rst +0 -0
  38. odoo/addons/l10n_br_fiscal/readme/INSTALL.rst +0 -4
  39. odoo/addons/l10n_br_fiscal/readme/ROADMAP.rst +0 -0
  40. odoo/addons/l10n_br_fiscal/readme/USAGE.rst +0 -1
  41. odoo_addon_l10n_br_fiscal-16.0.1.18.0.3.dist-info/METADATA +0 -244
  42. odoo_addon_l10n_br_fiscal-16.0.1.18.0.3.dist-info/top_level.txt +0 -1
@@ -89,16 +89,16 @@ class Comment(models.Model):
89
89
  def name_get(self):
90
90
  def truncate_name(name):
91
91
  if len(name) > 60:
92
- name = "{}...".format(name[:60])
92
+ name = f"{name[:60]}..."
93
93
  return name
94
94
 
95
- return [(r.id, "{}".format(truncate_name(r.name))) for r in self]
95
+ return [(r.id, f"{truncate_name(r.name)}") for r in self]
96
96
 
97
97
  # format_amount function for fiscal observation
98
98
  # This way we can format numbers in currency template on fiscal observation
99
99
  # msg We'll call this function when setting the variables env below
100
100
  def format_amount(self, env, amount, currency):
101
- fmt = "%.{}f".format(currency.decimal_places)
101
+ fmt = f"%.{currency.decimal_places}f"
102
102
  lang = env.ref("base.lang_pt_BR")
103
103
 
104
104
  formatted_amount = (
@@ -113,7 +113,7 @@ class Comment(models.Model):
113
113
  else:
114
114
  post = "\N{NO-BREAK SPACE}" + "{}".format(currency.symbol or "")
115
115
 
116
- return "{pre}{0}{post}".format(formatted_amount, pre=pre, post=post)
116
+ return f"{pre}{formatted_amount}{post}"
117
117
 
118
118
  def compute_message(self, vals, manual_comment=None):
119
119
  if not self.ids and not manual_comment:
@@ -94,10 +94,10 @@ class DataAbstract(models.AbstractModel):
94
94
  def name_get(self):
95
95
  def truncate_name(name):
96
96
  if len(name) > 60:
97
- name = "{}...".format(name[:60])
97
+ name = f"{name[:60]}..."
98
98
  return name
99
99
 
100
100
  if self._context.get("show_code_only"):
101
- return [(r.id, "{}".format(r.code)) for r in self]
101
+ return [(r.id, f"{r.code}") for r in self]
102
102
 
103
- return [(r.id, "{} - {}".format(r.code, truncate_name(r.name))) for r in self]
103
+ return [(r.id, f"{r.code} - {truncate_name(r.name)}") for r in self]
@@ -147,20 +147,18 @@ class DataNcmNbsAbstract(models.AbstractModel):
147
147
  lambda r: r.product_tmpl_qty > 0 and not r.tax_estimate_ids
148
148
  )
149
149
 
150
- query = """
151
- WITH {0}_max_date AS (
150
+ query = f"""
151
+ WITH {object_name.lower()}_max_date AS (
152
152
  SELECT
153
- {0}_id,
153
+ {object_name.lower()}_id,
154
154
  max(create_date)
155
155
  FROM
156
156
  l10n_br_fiscal_tax_estimate
157
- GROUP BY {0}_id)
158
- SELECT {0}_id
159
- FROM {0}_max_date
157
+ GROUP BY {object_name.lower()}_id)
158
+ SELECT {object_name.lower()}_id
159
+ FROM {object_name.lower()}_max_date
160
160
  WHERE max < %(create_date)s
161
- """.format(
162
- object_name.lower()
163
- )
161
+ """
164
162
 
165
163
  query_params = {"create_date": data_max.strftime("%Y-%m-%d")}
166
164
 
@@ -191,9 +191,7 @@ class DocumentEletronic(models.AbstractModel):
191
191
  if attachment_id:
192
192
  return {
193
193
  "type": "ir.actions.act_url",
194
- "url": "/web/content/{id}/{nome}".format(
195
- id=attachment_id.id, nome=attachment_id.name
196
- ),
194
+ "url": f"/web/content/{attachment_id.id}/{attachment_id.name}",
197
195
  "target": "new",
198
196
  }
199
197
 
@@ -229,6 +227,7 @@ class DocumentEletronic(models.AbstractModel):
229
227
  if not record.issuer:
230
228
  raise ValidationError(
231
229
  _(
232
- "The field 'Issuer' is required for brazilian electronic documents!"
230
+ "The field 'Issuer' is required for brazilian electronic "
231
+ "documents!"
233
232
  )
234
233
  )
@@ -242,7 +242,7 @@ class Event(models.Model):
242
242
  if not os.path.exists(save_dir):
243
243
  os.makedirs(save_dir)
244
244
  f = open(file_path, "w")
245
- except IOError as e:
245
+ except OSError as e:
246
246
  raise UserError(
247
247
  _("Erro!"),
248
248
  _(
@@ -207,7 +207,8 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
207
207
  for line in self:
208
208
  # Determine if 'CSLL' and 'IRPJ' taxes may apply:
209
209
  # 1. When providing services (tax_icms_or_issqn == "issqn")
210
- # 2. When supplying products to public entities (partner_is_public_entity is True)
210
+ # 2. When supplying products to public entities (partner_is_public_entity
211
+ # is True)
211
212
  if line.tax_icms_or_issqn == "issqn" or line.partner_is_public_entity:
212
213
  line.allow_csll_irpj = True # Tax charges may apply
213
214
  else:
@@ -224,7 +225,7 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
224
225
  vals.pop("id", None)
225
226
 
226
227
  if default: # in case you want to use new rather than write later
227
- return {"default_%s" % (k,): vals[k] for k in vals.keys()}
228
+ return {f"default_{k}": vals[k] for k in vals.keys()}
228
229
  return vals
229
230
 
230
231
  def _get_all_tax_id_fields(self):
@@ -275,7 +276,7 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
275
276
  for line in self:
276
277
  taxes_groups = line.fiscal_tax_ids.mapped("tax_domain")
277
278
  fiscal_taxes = line.fiscal_tax_ids.filtered(
278
- lambda ft: ft.tax_domain not in taxes_groups
279
+ lambda ft, taxes_groups=taxes_groups: ft.tax_domain not in taxes_groups
279
280
  )
280
281
  line.fiscal_tax_ids = fiscal_taxes + taxes
281
282
 
@@ -29,9 +29,9 @@ class FiscalDocumentMixinFields(models.AbstractModel):
29
29
  domain = (
30
30
  "[('state', '=', 'approved'),"
31
31
  "'|',"
32
- "('company_id', '=', %s),"
32
+ f"('company_id', '=', {self.env.company.id}),"
33
33
  "('company_id', '=', False),"
34
- ) % (self.env.company.id,)
34
+ )
35
35
  return domain
36
36
 
37
37
  fiscal_operation_id = fields.Many2one(
@@ -21,7 +21,7 @@ class FiscalDocumentMixinMethods(models.AbstractModel):
21
21
  vals.pop("id", None)
22
22
 
23
23
  if default: # in case you want to use new rather than write later
24
- return {"default_%s" % (k,): vals[k] for k in vals.keys()}
24
+ return {f"default_{k}": vals[k] for k in vals.keys()}
25
25
  return vals
26
26
 
27
27
  def _get_amount_lines(self):
@@ -121,11 +121,12 @@ class FiscalDocumentMixinMethods(models.AbstractModel):
121
121
  line.freight_value = amount_freight_value * (
122
122
  line.freight_value / amount_freight_old
123
123
  )
124
- record._get_product_amount_lines()[
125
- -1
126
- ].freight_value = amount_freight_value - sum(
127
- line.freight_value
128
- for line in record._get_product_amount_lines()[:-1]
124
+ record._get_product_amount_lines()[-1].freight_value = (
125
+ amount_freight_value
126
+ - sum(
127
+ line.freight_value
128
+ for line in record._get_product_amount_lines()[:-1]
129
+ )
129
130
  )
130
131
  else:
131
132
  amount_total = sum(
@@ -136,11 +137,12 @@ class FiscalDocumentMixinMethods(models.AbstractModel):
136
137
  line.freight_value = amount_freight_value * (
137
138
  line.price_gross / amount_total
138
139
  )
139
- record._get_product_amount_lines()[
140
- -1
141
- ].freight_value = amount_freight_value - sum(
142
- line.freight_value
143
- for line in record._get_product_amount_lines()[:-1]
140
+ record._get_product_amount_lines()[-1].freight_value = (
141
+ amount_freight_value
142
+ - sum(
143
+ line.freight_value
144
+ for line in record._get_product_amount_lines()[:-1]
145
+ )
144
146
  )
145
147
  for line in record._get_product_amount_lines():
146
148
  line._onchange_fiscal_taxes()
@@ -169,11 +171,12 @@ class FiscalDocumentMixinMethods(models.AbstractModel):
169
171
  line.insurance_value = amount_insurance_value * (
170
172
  line.insurance_value / amount_insurance_old
171
173
  )
172
- record._get_product_amount_lines()[
173
- -1
174
- ].insurance_value = amount_insurance_value - sum(
175
- line.insurance_value
176
- for line in record._get_product_amount_lines()[:-1]
174
+ record._get_product_amount_lines()[-1].insurance_value = (
175
+ amount_insurance_value
176
+ - sum(
177
+ line.insurance_value
178
+ for line in record._get_product_amount_lines()[:-1]
179
+ )
177
180
  )
178
181
  else:
179
182
  amount_total = sum(
@@ -184,11 +187,12 @@ class FiscalDocumentMixinMethods(models.AbstractModel):
184
187
  line.insurance_value = amount_insurance_value * (
185
188
  line.price_gross / amount_total
186
189
  )
187
- record._get_product_amount_lines()[
188
- -1
189
- ].insurance_value = amount_insurance_value - sum(
190
- line.insurance_value
191
- for line in record._get_product_amount_lines()[:-1]
190
+ record._get_product_amount_lines()[-1].insurance_value = (
191
+ amount_insurance_value
192
+ - sum(
193
+ line.insurance_value
194
+ for line in record._get_product_amount_lines()[:-1]
195
+ )
192
196
  )
193
197
  for line in record._get_product_amount_lines():
194
198
  line._onchange_fiscal_taxes()
@@ -217,11 +221,12 @@ class FiscalDocumentMixinMethods(models.AbstractModel):
217
221
  line.other_value = amount_other_value * (
218
222
  line.other_value / amount_other_old
219
223
  )
220
- record._get_product_amount_lines()[
221
- -1
222
- ].other_value = amount_other_value - sum(
223
- line.other_value
224
- for line in record._get_product_amount_lines()[:-1]
224
+ record._get_product_amount_lines()[-1].other_value = (
225
+ amount_other_value
226
+ - sum(
227
+ line.other_value
228
+ for line in record._get_product_amount_lines()[:-1]
229
+ )
225
230
  )
226
231
  else:
227
232
  amount_total = sum(
@@ -232,11 +237,12 @@ class FiscalDocumentMixinMethods(models.AbstractModel):
232
237
  line.other_value = amount_other_value * (
233
238
  line.price_gross / amount_total
234
239
  )
235
- record._get_product_amount_lines()[
236
- -1
237
- ].other_value = amount_other_value - sum(
238
- line.other_value
239
- for line in record._get_product_amount_lines()[:-1]
240
+ record._get_product_amount_lines()[-1].other_value = (
241
+ amount_other_value
242
+ - sum(
243
+ line.other_value
244
+ for line in record._get_product_amount_lines()[:-1]
245
+ )
240
246
  )
241
247
  for line in record._get_product_amount_lines():
242
248
  line._onchange_fiscal_taxes()
@@ -247,7 +247,9 @@ class DocumentMoveMixin(models.AbstractModel):
247
247
  {
248
248
  "source_document_id": self.id,
249
249
  "subsequent_operation_id": subsequent_id.id,
250
- "fiscal_operation_id": subsequent_id.subsequent_operation_id.id,
250
+ "fiscal_operation_id": (
251
+ subsequent_id.subsequent_operation_id.id
252
+ ),
251
253
  },
252
254
  )
253
255
  )
@@ -79,7 +79,7 @@ class DocumentSerie(models.Model):
79
79
  return super().create(vals_list)
80
80
 
81
81
  def name_get(self):
82
- return [(r.id, "{}".format(r.name)) for r in self]
82
+ return [(r.id, f"{r.name}") for r in self]
83
83
 
84
84
  def _is_invalid_number(self, document_number):
85
85
  self.ensure_one()
@@ -2067,6 +2067,7 @@ class ICMSRegulation(models.Model):
2067
2067
  nbm=None,
2068
2068
  cest=None,
2069
2069
  operation_line=None,
2070
+ ind_final=None,
2070
2071
  ):
2071
2072
  self.ensure_one()
2072
2073
  tax_group_icmsst = self.env.ref("l10n_br_fiscal.tax_group_icmsst")
@@ -2076,7 +2077,9 @@ class ICMSRegulation(models.Model):
2076
2077
  company, partner, tax_group_icmsst, ncm, nbm, cest
2077
2078
  )
2078
2079
 
2079
- tax_definitions = self._tax_definition_search(domain, ncm, nbm, cest, product)
2080
+ tax_definitions = self._tax_definition_search(
2081
+ domain, ncm, nbm, cest, product, ind_final
2082
+ )
2080
2083
  return tax_definitions
2081
2084
 
2082
2085
  def map_tax_def_icms_difal(
@@ -2119,6 +2122,7 @@ class ICMSRegulation(models.Model):
2119
2122
  nbm=None,
2120
2123
  cest=None,
2121
2124
  operation_line=None,
2125
+ ind_final=None,
2122
2126
  ):
2123
2127
  self.ensure_one()
2124
2128
  tax_definitions = self.env["l10n_br_fiscal.tax.definition"]
@@ -2137,7 +2141,7 @@ class ICMSRegulation(models.Model):
2137
2141
  )
2138
2142
 
2139
2143
  tax_definitions = self._tax_definition_search(
2140
- domain, ncm, nbm, cest, product
2144
+ domain, ncm, nbm, cest, product, ind_final
2141
2145
  )
2142
2146
 
2143
2147
  return tax_definitions
@@ -2151,6 +2155,7 @@ class ICMSRegulation(models.Model):
2151
2155
  nbm=None,
2152
2156
  cest=None,
2153
2157
  operation_line=None,
2158
+ ind_final=None,
2154
2159
  ):
2155
2160
  self.ensure_one()
2156
2161
  tax_definitions = self.env["l10n_br_fiscal.tax.definition"]
@@ -2161,7 +2166,9 @@ class ICMSRegulation(models.Model):
2161
2166
  company, partner, tax_group_icmsfcpst, ncm, nbm, cest
2162
2167
  )
2163
2168
 
2164
- tax_definitions = self._tax_definition_search(domain, ncm, nbm, cest, product)
2169
+ tax_definitions = self._tax_definition_search(
2170
+ domain, ncm, nbm, cest, product, ind_final
2171
+ )
2165
2172
 
2166
2173
  return tax_definitions
2167
2174
 
@@ -2192,15 +2199,15 @@ class ICMSRegulation(models.Model):
2192
2199
  )
2193
2200
 
2194
2201
  icms_def_taxes |= self._map_tax_def_icmsst(
2195
- company, partner, product, ncm, nbm, cest, operation_line
2202
+ company, partner, product, ncm, nbm, cest, operation_line, ind_final
2196
2203
  )
2197
2204
 
2198
2205
  icms_def_taxes |= self._map_tax_def_icmsfcp(
2199
- company, partner, product, ncm, nbm, cest, operation_line
2206
+ company, partner, product, ncm, nbm, cest, operation_line, ind_final
2200
2207
  )
2201
2208
 
2202
2209
  icms_def_taxes |= self._map_tax_def_icmsfcpst(
2203
- company, partner, product, ncm, nbm, cest, operation_line
2210
+ company, partner, product, ncm, nbm, cest, operation_line, ind_final
2204
2211
  )
2205
2212
 
2206
2213
  icms_taxes |= icms_def_taxes.mapped("tax_id")
@@ -26,8 +26,8 @@ class ProductTemplate(models.Model):
26
26
  # demo products of type 'product' (this type is added to product.template
27
27
  # in the stock module).
28
28
  # For some reason when running the tests, some inverse method fields then fail when
29
- # reading 'product' value for the product type. It seems it is because l10n_br_fiscal
30
- # doesn't depend on stock. But we don't want such a dependency.
29
+ # reading 'product' value for the product type. It seems it is because
30
+ # l10n_br_fiscal doesn't depend on stock. But we don't want such a dependency.
31
31
  # So a workaround to avoid the bug we add the 'product' value to the selection.
32
32
  type = fields.Selection(
33
33
  selection_add=[("product", "Storable Product")],
@@ -3,7 +3,6 @@
3
3
  # License AGPL-3 or later (http://www.gnu.org/licenses/agpl)
4
4
  #
5
5
 
6
- from __future__ import division, print_function, unicode_literals
7
6
 
8
7
  from odoo import api, fields, models
9
8
 
@@ -2,8 +2,6 @@
2
2
  # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
3
3
 
4
4
 
5
- from __future__ import division, print_function, unicode_literals
6
-
7
5
  from odoo import fields, models
8
6
 
9
7
  from ..constants.fiscal import SITUACAO_EDOC
@@ -20,11 +20,7 @@ class TaxDefinition(models.Model):
20
20
  _description = "Tax Definition"
21
21
 
22
22
  def _get_complete_name(self):
23
- return "{tax_group}-{tax}-{cst_code}".format(
24
- tax_group=self.tax_group_id.name,
25
- tax=self.tax_id.name,
26
- cst_code=self.cst_code,
27
- )
23
+ return f"{self.tax_group_id.name}-{self.tax_id.name}-{self.cst_code}"
28
24
 
29
25
  @api.depends("tax_group_id", "tax_id", "cst_code")
30
26
  def _compute_display_name(self):
@@ -0,0 +1,7 @@
1
+ Para uma boa configuração fiscal, você tem que revisar bem:
2
+
3
+ - em Configurações: as operaçoes fiscais que você vai usar, as linhas de
4
+ operação fiscal e as definições das taxas nessas linhas.
5
+ - a configuração fiscal da sua empresa (aba fiscal)
6
+ - a configuração fiscal dos clientes e fornecedores (aba fiscal) e dos
7
+ produtos (aba fiscal).
@@ -0,0 +1,12 @@
1
+ - [Akretion](https://www.akretion.com/pt-BR):
2
+ - Renato Lima \<<renato.lima@akretion.com.br>\>
3
+ - Raphaël Valyi \<<raphael.valyi@akretion.com.br>\>
4
+ - Magno Costa \<<magno.costa@akretion.com.br>\>
5
+ - [KMEE](https://www.kmee.com.br):
6
+ - Luis Felipe Mileo \<<mileo@kmee.com.br>\>
7
+ - Luis Otavio Malta Conceição \<<luis.malta@kmee.com.br>\>
8
+ - [Escodoo](https://www.escodoo.com.br):
9
+ - Marcel Savegnago \<<marcel.savegnago@escodoo.com.br>\>
10
+ - [Engenere](https://engenere.one):
11
+ - Antônio S. Pereira Neto \<<neto@engenere.one>\>
12
+ - Felipe Motter Pereira \<<felipe@engenere.one>\>
@@ -0,0 +1,196 @@
1
+ ![image](../static/img/fiscal_dashboard.png)
2
+
3
+ ## Classificações fiscais
4
+
5
+ Primeramente, este módulo traz uma variedade de cadastros fiscais para
6
+ produtos, parceiros ou empresas. Na hora de emitir documentos fiscais
7
+ como NF-e, NFS-e etc... até empresas do regime simplificado ou MEI
8
+ precisam de vários desses cadastros. E empresas do regime normal
9
+ precisam deles para calcular os impostos ou emitir documentos fiscais.
10
+
11
+ Produtos:
12
+ - tipo fiscal
13
+ - NCM (com ligações com os impostos)
14
+ - genêro fiscal
15
+ - CEST
16
+ - NBM
17
+ - NBS
18
+ - tipo de serviço
19
+ - unidades fiscais
20
+
21
+ Parceiros:
22
+ - CNAE
23
+ - perfil fiscal
24
+
25
+ ## Conceito de documento fiscal
26
+
27
+ O Odoo nativo não tem o conceito de documento fiscal. O conceito mais
28
+ parecido seria o `account.move` e até a versão 10.0 a localização
29
+ estendia o invoice para suportar as NF-e e NFS-e apenas. Naquela época
30
+ não era razoável você cogitar fazer o SPED no Odoo, o próprio core do
31
+ Odoo não tinha maturidade para isso então era válido a abordagem
32
+ empírica de ir suportando mais casos de NFe dentro do invoice Odoo
33
+ apenas.
34
+
35
+ Porém, na v12, amadurecemos o framework XML/SOAP de forma que se torna
36
+ razoável suportar vários documentos fiscais (NF-e, NFS-e, MDF-e, CT-e,
37
+ EFD-Reinf, e-Social, GNRE, BP-e...) com a qualidade OCA dentro do Odoo.
38
+ Também, apesar de complexo, o core do Odoo finalmente tem suporte
39
+ suficiente para as operações de uma empresa que faria o SPED.
40
+
41
+ Nisso se torna interessante ter o conceito de documento fiscal
42
+ `l10n_br_fiscal.document` independente do invoice Odoo para suportar
43
+ todos os documentos fiscais mesmo, de forma modular. Um outro motivo
44
+ para ter o conceito de documento fiscal fora do módulo account é que
45
+ quando você analisa o código deste módulo `l10n_br_fiscal`, quase nada
46
+ dele poderia ser feito pelo módulo account do Odoo. Então ter esse
47
+ módulo l10n_br_fiscal que não depende do módulo account também é uma
48
+ forma de modularizar a localização para facilitar a manutenção dela,
49
+ especialmente quando se trata de migrar e que o módulo pode ter mudado
50
+ bastante como foi o caso entre a v8.0 e a v9.0 ou a v12.0 e v13.0 por
51
+ exemplo. Facilita também a governança do projeto possibilitando que
52
+ pessoas sejam responsáveis por diferentes partes. O módulo
53
+ l10n_br_fiscal foi principalmente extraído do módulo
54
+ l10n_br_l10n_br_account_product das v7.0 as v.10.0.
55
+
56
+ Esse módulo `l10n_br_fiscal` é agnóstico de qualquer tecnologia XML ou
57
+ SOAP. Ele contém apenas o que há de comum entre os documentos fiscais
58
+ mas esses últimos são implementados em outros módulos. Para um
59
+ determinado documento fiscal como a Nf-e, você tem então por exemplo:
60
+
61
+ - `nfelib`: um pacote de data bindings puro Python (que não depende do
62
+ Odoo). Em geral usamos um gerador de código para gerar esses bindings
63
+ a partir dos esquemas XSD da fazenda.
64
+ - `l10n_br_nfe_spec`: um modulo de mixins Odoo geridos também a partir
65
+ dos XSD. Esses mixins são apenas as estruturas de dados das
66
+ especificações antes de ser injectados em objetos Odoo existantes
67
+ (como res.partner ou l10n_br_fiscal.document) ou até tornados
68
+ concretos caso não exite objetos na Odoo ou na OCA para eles já.
69
+ - `l10n_br_nfe`: um módulo Odoo que trata de injectar esses mappings
70
+ fiscais nos objetos Odoo e que implementa a lógica como os wizards
71
+ para a transmissão.
72
+
73
+ A transmissão é realizada usando uma lib de transmissão como
74
+ `erpbrasil.doc` (baseada em Python Zeep). Importante: no caso da
75
+ `NFS-e`, a ausência de padrão nacional hoje e a simplicidade do modelo
76
+ (comparado com a NFe) faz que foi decidido de não usar um módulo de
77
+ mixins fiscais Odoo geridos, o mapping é com a lib de binding é feito
78
+ manualmente, família de NFS-e por família.
79
+
80
+ Alem disso a maioria do codigo do `l10n_br_fiscal.document` e das linhas
81
+ dele `l10n_br_fiscal.document.line` é na verdade feito dentro de mixins
82
+ `10n_br_fiscal.document.mixin` e `10n_br_fiscal.document.line.mixin`
83
+ respectivamente. Esses mixins podem assim ser injectados em outros
84
+ objetos Odoo que precedem os documentos fiscais e podem armazenar então
85
+ o mesmo tipo de informação: `sale.order`, `purchase.order`,
86
+ `stock.picking`... Isso é bem visível nos módulos que estendem esse
87
+ módulo:
88
+
89
+ ``` text
90
+ |-- l10n_br_fiscal
91
+ |-- l10n_br_sale
92
+ |-- l10n_br_purchase
93
+ |-- l10n_br_account
94
+ |-- ...
95
+ ```
96
+
97
+ Porem o caso do invoice Odoo no modulo `l10n_br_account` é diferente
98
+ ainda. Pois já se tem a tabela independente do documento fiscal cuja
99
+ grande maioria das dezenas e até centenas de campos fiscais (no caso de
100
+ muitos tipos de documentos fiscais) não são redundante com os do invoice
101
+ Odoo. Se a gente injetasse esses mixins dentro do invoice, aí sim essas
102
+ centenas de campos seriam duplicados entre o invoice e o documento
103
+ fiscal. Por isso, o sistema que foi adotado no modulo `l10n_br_account`
104
+ é que um invoice Odoo passa a ter um
105
+ `_inherits = "l10n_br_fiscal.document"` de forma que se pilota o
106
+ documento fiscal através do invoice, oferecendo o mesmo tipo de
107
+ integração como antes. O mesmo tipo de mecanismo acontece com a linha do
108
+ documento fiscal.
109
+
110
+ Sendo assim, já pelos \_inherits, o invoice Odoo e as linhas dele já vão
111
+ puxar todos campos fiscais como se eles fossem das suas respectivas
112
+ tabelas sem duplicar eles no banco. Se alem disso a gente injetasse os
113
+ mixins `10n_br_fiscal.document.mixin` e
114
+ `10n_br_fiscal.document.line.mixin` no invoice e invoice.line, esses
115
+ campos fiscais apareceriam também nas tabelas `account_move` e
116
+ `account_move_line` de forma redundantes com os campos puxados pelos
117
+ \_inherits. Para não ter esse problema, os métodos fiscais comuns (sem
118
+ os campos) foram ainda extraidos nos mixins:
119
+ `10n_br_fiscal.document.mixin.methods` e
120
+ `10n_br_fiscal.document.line.mixin.methods` que são injectados nos
121
+ objetos `account_move` e `account_move_line` respectivamente dentro do
122
+ modulo `l10n_br_account`.
123
+
124
+ ## Impostos brasileiros
125
+
126
+ O módulo l10n_br_fiscal lida com os principais impostos brasileiros
127
+ como:
128
+
129
+ - ICMS do Simples Nacional
130
+ - ICMS do Regime normal
131
+ - IPI
132
+ - PIS
133
+ - COFINS
134
+ - ISSQN
135
+ - IRPJ
136
+ - II
137
+ - CSLL
138
+ - INSS
139
+
140
+ O módulo l10n_br_fiscal também lida com:
141
+
142
+ - ST
143
+ - retenções
144
+
145
+ ![image](../static/img/fiscal_line.png)
146
+
147
+ ![image](../static/img/fiscal_total.png)
148
+
149
+ É notório que o cálculo dos impostos no Brasil é muito especial e muito
150
+ trabalhoso. Geralmente é o motivo pelo qual os ERPs internacionais não
151
+ tem grande fatia de mercado brasileiro.
152
+
153
+ Até a versão 10.0, tentamos usar e estender o objeto Odoo `account.tax`.
154
+ A Akretion até criou o projeto `OCA/account-fiscal-rule` para determinar
155
+ as alíquotas de cada imposto de accordo com os parâmetros da operação
156
+ fiscal. Porém, a gente acabava usando quase nada do
157
+ `account.fiscal.position` nativo na parte fiscal e pelo contrário, isso
158
+ nos obrigava a ter um registro `account.tax` para cada aliquota e nos
159
+ obrigava a manter centenas de taxas e dezenas de milhares de regras para
160
+ selecionar a "posição fiscal" Odoo que aplicaria as taxas corretas. E
161
+ você ainda tinha que gerir essas dezenas de milhares de regras para uma
162
+ determinada empresa do regime normal. Conclusão: era inviável nos
163
+ projetos menores de tentar se encaixa na lógica do Odoo para calcular os
164
+ impostos brasileiros.
165
+
166
+ Nisso criamos neste módulo os modelos de taxas que representam
167
+ exatamente o funcionamentos dos impostos brasileiros. Além dos cálculos,
168
+ esses modelos também nos servem a carregar as tabelas dos impostos. E
169
+ mais adiante, no módulo `l10n_br_account`, ligamos os objetos nativos
170
+ `account.tax` as alíquotas dos impostos brasileiros.
171
+
172
+ Claro esses modelos dos impostos atendem as empresas do regime normal,
173
+ mas é bom lembrar que até empresas do regime simplificado precisam
174
+ desses modelos para realizar as operações com ST (Substituição
175
+ Tributária)...
176
+
177
+ ## Operações fiscais
178
+
179
+ > ![image](../static/img/fiscal_operation.png)
180
+
181
+ No Odoo nativo, o conceito mais parecido com a operação fiscal e o
182
+ `account.fiscal.position`. E ate a versão 10.0, era o que a gente usava.
183
+ Porém, a posição fiscal do Odoo não resolve muito os nossos problemas
184
+ pois:
185
+
186
+ - no Brasil se tem uma operação fiscal por linha de documento fiscal
187
+ - a posição fiscal do Odoo desconhece a lógica da parametrização fiscal
188
+ brasileira
189
+ - já que puxamos o cadastro dos impostos no módulo l10n_br_fiscal fora
190
+ do módulo account (sem depender dele), não temos ainda o objeto
191
+ `account.fiscal.position` neste módulo.
192
+
193
+ Com tudo, optamos por criar um objeto `l10n_br_fiscal.operation` que faz
194
+ exactamente o que precisamos para o Brasil. Mais adiante, no módulo
195
+ `l10n_br_account` é realizado a integração entre a posição fiscal do
196
+ Odoo e essa operação fiscal.
@@ -0,0 +1,5 @@
1
+ Para instalar o módulo l10n_br_fiscal, você precisa de instalar primeiro
2
+ os pacotes Python
3
+
4
+ - erpbrasil.base
5
+ - erpbrasil.assinatura
@@ -0,0 +1,4 @@
1
+ Você pode criar documentos fiscais direitamente pelo menu fiscal, mas a
2
+ princípio você vai pilotar a criação de documentos fiscais a partir dos
3
+ invoices Odoo, usando módulos adicionais como l10n_br_account,
4
+ l10n_br_sale, l10n_br_purchase...