odoo-addon-l10n-br-fiscal 16.0.1.18.1__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.
- odoo/addons/l10n_br_fiscal/README.rst +225 -105
- odoo/addons/l10n_br_fiscal/__manifest__.py +1 -1
- odoo/addons/l10n_br_fiscal/i18n/l10n_br_fiscal.pot +93 -5
- odoo/addons/l10n_br_fiscal/models/comment.py +4 -4
- odoo/addons/l10n_br_fiscal/models/data_abstract.py +3 -3
- odoo/addons/l10n_br_fiscal/models/data_ncm_nbs_abstract.py +7 -9
- odoo/addons/l10n_br_fiscal/models/document_eletronic.py +3 -4
- odoo/addons/l10n_br_fiscal/models/document_event.py +1 -1
- odoo/addons/l10n_br_fiscal/models/document_fiscal_line_mixin_methods.py +4 -3
- odoo/addons/l10n_br_fiscal/models/document_fiscal_mixin_fields.py +2 -2
- odoo/addons/l10n_br_fiscal/models/document_fiscal_mixin_methods.py +37 -31
- odoo/addons/l10n_br_fiscal/models/document_move_mixin.py +3 -1
- odoo/addons/l10n_br_fiscal/models/document_serie.py +1 -1
- odoo/addons/l10n_br_fiscal/models/product_template.py +2 -2
- odoo/addons/l10n_br_fiscal/models/subsequent_document.py +0 -1
- odoo/addons/l10n_br_fiscal/models/subsequent_operation.py +0 -2
- odoo/addons/l10n_br_fiscal/models/tax_definition.py +1 -5
- odoo/addons/l10n_br_fiscal/readme/CONFIGURE.md +7 -0
- odoo/addons/l10n_br_fiscal/readme/CONTRIBUTORS.md +12 -0
- odoo/addons/l10n_br_fiscal/readme/DESCRIPTION.md +196 -0
- odoo/addons/l10n_br_fiscal/readme/HISTORY.md +1 -0
- odoo/addons/l10n_br_fiscal/readme/INSTALL.md +5 -0
- odoo/addons/l10n_br_fiscal/readme/ROADMAP.md +1 -0
- odoo/addons/l10n_br_fiscal/readme/USAGE.md +4 -0
- odoo/addons/l10n_br_fiscal/static/description/index.html +155 -43
- odoo/addons/l10n_br_fiscal/tests/test_fiscal_document_generic.py +1 -1
- odoo/addons/l10n_br_fiscal/tests/test_ibpt.py +1 -2
- odoo/addons/l10n_br_fiscal/tools.py +2 -2
- odoo_addon_l10n_br_fiscal-16.0.1.18.2.dist-info/METADATA +364 -0
- {odoo_addon_l10n_br_fiscal-16.0.1.18.1.dist-info → odoo_addon_l10n_br_fiscal-16.0.1.18.2.dist-info}/RECORD +32 -32
- {odoo_addon_l10n_br_fiscal-16.0.1.18.1.dist-info → odoo_addon_l10n_br_fiscal-16.0.1.18.2.dist-info}/WHEEL +1 -1
- odoo_addon_l10n_br_fiscal-16.0.1.18.2.dist-info/top_level.txt +1 -0
- odoo/addons/l10n_br_fiscal/readme/CONFIGURE.rst +0 -5
- odoo/addons/l10n_br_fiscal/readme/CONTRIBUTORS.rst +0 -19
- odoo/addons/l10n_br_fiscal/readme/DESCRIPTION.rst +0 -103
- odoo/addons/l10n_br_fiscal/readme/HISTORY.rst +0 -0
- odoo/addons/l10n_br_fiscal/readme/INSTALL.rst +0 -4
- odoo/addons/l10n_br_fiscal/readme/ROADMAP.rst +0 -0
- odoo/addons/l10n_br_fiscal/readme/USAGE.rst +0 -1
- odoo_addon_l10n_br_fiscal-16.0.1.18.1.dist-info/METADATA +0 -244
- odoo_addon_l10n_br_fiscal-16.0.1.18.1.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 = "{
|
|
92
|
+
name = f"{name[:60]}..."
|
|
93
93
|
return name
|
|
94
94
|
|
|
95
|
-
return [(r.id, "{
|
|
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"
|
|
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}{
|
|
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 = "{
|
|
97
|
+
name = f"{name[:60]}..."
|
|
98
98
|
return name
|
|
99
99
|
|
|
100
100
|
if self._context.get("show_code_only"):
|
|
101
|
-
return [(r.id, "{
|
|
101
|
+
return [(r.id, f"{r.code}") for r in self]
|
|
102
102
|
|
|
103
|
-
return [(r.id, "{} - {
|
|
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 {
|
|
150
|
+
query = f"""
|
|
151
|
+
WITH {object_name.lower()}_max_date AS (
|
|
152
152
|
SELECT
|
|
153
|
-
{
|
|
153
|
+
{object_name.lower()}_id,
|
|
154
154
|
max(create_date)
|
|
155
155
|
FROM
|
|
156
156
|
l10n_br_fiscal_tax_estimate
|
|
157
|
-
GROUP BY {
|
|
158
|
-
SELECT {
|
|
159
|
-
FROM {
|
|
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
|
-
"""
|
|
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}/{
|
|
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
|
|
230
|
+
"The field 'Issuer' is required for brazilian electronic "
|
|
231
|
+
"documents!"
|
|
233
232
|
)
|
|
234
233
|
)
|
|
@@ -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
|
|
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_
|
|
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', '=',
|
|
32
|
+
f"('company_id', '=', {self.env.company.id}),"
|
|
33
33
|
"('company_id', '=', False),"
|
|
34
|
-
)
|
|
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_
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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":
|
|
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, "{
|
|
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()
|
|
@@ -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
|
|
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")],
|
|
@@ -20,11 +20,7 @@ class TaxDefinition(models.Model):
|
|
|
20
20
|
_description = "Tax Definition"
|
|
21
21
|
|
|
22
22
|
def _get_complete_name(self):
|
|
23
|
-
return "{
|
|
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
|
+

|
|
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
|
+

|
|
146
|
+
|
|
147
|
+

|
|
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
|
+
> 
|
|
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 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|