odoo-addon-l10n-br-fiscal 18.0.2.0.0.10__py3-none-any.whl → 18.0.5.0.0.1__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 +1 -1
- odoo/addons/l10n_br_fiscal/__manifest__.py +2 -2
- odoo/addons/l10n_br_fiscal/constants/fiscal.py +46 -18
- odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.document.type.csv +1 -0
- odoo/addons/l10n_br_fiscal/data/operation_data.xml +1 -1
- odoo/addons/l10n_br_fiscal/demo/fiscal_document_demo.xml +3 -179
- odoo/addons/l10n_br_fiscal/demo/fiscal_document_nfse_demo.xml +0 -4
- odoo/addons/l10n_br_fiscal/demo/fiscal_operation_demo.xml +2 -2
- odoo/addons/l10n_br_fiscal/i18n/l10n_br_fiscal.pot +142 -36
- odoo/addons/l10n_br_fiscal/i18n/pt_BR.po +190 -58
- odoo/addons/l10n_br_fiscal/migrations/18.0.3.0.0/pre-migration.py +30 -0
- odoo/addons/l10n_br_fiscal/models/comment.py +3 -1
- odoo/addons/l10n_br_fiscal/models/document.py +27 -8
- odoo/addons/l10n_br_fiscal/models/document_line.py +51 -8
- odoo/addons/l10n_br_fiscal/models/document_line_mixin.py +118 -31
- odoo/addons/l10n_br_fiscal/models/document_line_mixin_methods.py +263 -282
- odoo/addons/l10n_br_fiscal/models/document_mixin.py +8 -5
- odoo/addons/l10n_br_fiscal/models/document_mixin_methods.py +49 -151
- odoo/addons/l10n_br_fiscal/models/document_related.py +1 -1
- odoo/addons/l10n_br_fiscal/models/document_serie.py +33 -0
- odoo/addons/l10n_br_fiscal/models/icms_regulation.py +1 -1
- odoo/addons/l10n_br_fiscal/models/operation_dashboard.py +3 -2
- odoo/addons/l10n_br_fiscal/models/partner_profile.py +6 -0
- odoo/addons/l10n_br_fiscal/models/res_partner.py +7 -0
- odoo/addons/l10n_br_fiscal/models/tax.py +7 -3
- odoo/addons/l10n_br_fiscal/security/fiscal_security.xml +6 -16
- odoo/addons/l10n_br_fiscal/security/ir.model.access.csv +0 -1
- odoo/addons/l10n_br_fiscal/static/description/index.html +1 -1
- odoo/addons/l10n_br_fiscal/tests/__init__.py +1 -0
- odoo/addons/l10n_br_fiscal/tests/test_document_edition.py +175 -10
- odoo/addons/l10n_br_fiscal/tests/test_fiscal_document_generic.py +13 -42
- odoo/addons/l10n_br_fiscal/tests/test_fiscal_document_nfse.py +0 -5
- odoo/addons/l10n_br_fiscal/tests/test_fiscal_document_serie.py +60 -0
- odoo/addons/l10n_br_fiscal/tests/test_tax_benefit.py +2 -5
- odoo/addons/l10n_br_fiscal/views/document_line_mixin_view.xml +1 -0
- odoo/addons/l10n_br_fiscal/views/document_line_view.xml +3 -3
- odoo/addons/l10n_br_fiscal/views/document_view.xml +20 -15
- odoo/addons/l10n_br_fiscal/views/l10n_br_fiscal_menu.xml +0 -9
- odoo/addons/l10n_br_fiscal/views/operation_dashboard_view.xml +3 -3
- odoo/addons/l10n_br_fiscal/views/product_product_view.xml +33 -6
- odoo/addons/l10n_br_fiscal/views/product_template_view.xml +17 -4
- odoo/addons/l10n_br_fiscal/views/res_partner_view.xml +6 -0
- odoo/addons/l10n_br_fiscal/wizards/base_wizard_mixin.py +1 -1
- {odoo_addon_l10n_br_fiscal-18.0.2.0.0.10.dist-info → odoo_addon_l10n_br_fiscal-18.0.5.0.0.1.dist-info}/METADATA +3 -3
- {odoo_addon_l10n_br_fiscal-18.0.2.0.0.10.dist-info → odoo_addon_l10n_br_fiscal-18.0.5.0.0.1.dist-info}/RECORD +47 -45
- {odoo_addon_l10n_br_fiscal-18.0.2.0.0.10.dist-info → odoo_addon_l10n_br_fiscal-18.0.5.0.0.1.dist-info}/WHEEL +0 -0
- {odoo_addon_l10n_br_fiscal-18.0.2.0.0.10.dist-info → odoo_addon_l10n_br_fiscal-18.0.5.0.0.1.dist-info}/top_level.txt +0 -0
|
@@ -4,44 +4,22 @@
|
|
|
4
4
|
from copy import deepcopy
|
|
5
5
|
|
|
6
6
|
from lxml import etree
|
|
7
|
+
from lxml.builder import E
|
|
7
8
|
|
|
8
9
|
from odoo import Command, api, models
|
|
9
10
|
|
|
10
|
-
from ..constants.fiscal import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
FISCAL_TAX_ID_FIELDS
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"icmsst_tax_id",
|
|
23
|
-
"icmsfcpst_tax_id",
|
|
24
|
-
"ii_tax_id",
|
|
25
|
-
"inss_tax_id",
|
|
26
|
-
"inss_wh_tax_id",
|
|
27
|
-
"ipi_tax_id",
|
|
28
|
-
"irpj_tax_id",
|
|
29
|
-
"irpj_wh_tax_id",
|
|
30
|
-
"issqn_tax_id",
|
|
31
|
-
"issqn_wh_tax_id",
|
|
32
|
-
"pis_tax_id",
|
|
33
|
-
"pis_wh_tax_id",
|
|
34
|
-
"pisst_tax_id",
|
|
35
|
-
]
|
|
36
|
-
|
|
37
|
-
FISCAL_CST_ID_FIELDS = [
|
|
38
|
-
"icms_cst_id",
|
|
39
|
-
"ipi_cst_id",
|
|
40
|
-
"pis_cst_id",
|
|
41
|
-
"pisst_cst_id",
|
|
42
|
-
"cofins_cst_id",
|
|
43
|
-
"cofinsst_cst_id",
|
|
44
|
-
]
|
|
11
|
+
from ..constants.fiscal import (
|
|
12
|
+
CFOP_DESTINATION_EXPORT,
|
|
13
|
+
FISCAL_IN,
|
|
14
|
+
FISCAL_TAX_ID_FIELDS,
|
|
15
|
+
TAX_BASE_TYPE_PERCENT,
|
|
16
|
+
TAX_DOMAIN_ICMS,
|
|
17
|
+
)
|
|
18
|
+
from ..constants.icms import (
|
|
19
|
+
ICMS_BASE_TYPE_DEFAULT,
|
|
20
|
+
ICMS_ORIGIN_DEFAULT,
|
|
21
|
+
ICMS_ST_BASE_TYPE_DEFAULT,
|
|
22
|
+
)
|
|
45
23
|
|
|
46
24
|
|
|
47
25
|
class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
@@ -81,6 +59,23 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
81
59
|
Inject common fiscal fields into view placeholder elements.
|
|
82
60
|
Used for invoice line, sale order line, purchase order line...
|
|
83
61
|
"""
|
|
62
|
+
|
|
63
|
+
# the list of computed fields we will add to the view when missing
|
|
64
|
+
missing_line_fields = set(
|
|
65
|
+
[
|
|
66
|
+
fname
|
|
67
|
+
for fname, _field in filter(
|
|
68
|
+
lambda item: item[1].compute
|
|
69
|
+
in (
|
|
70
|
+
"_compute_tax_fields",
|
|
71
|
+
"_compute_fiscal_tax_ids",
|
|
72
|
+
"_compute_product_fiscal_fields",
|
|
73
|
+
),
|
|
74
|
+
self.env["l10n_br_fiscal.document.line.mixin"]._fields.items(),
|
|
75
|
+
)
|
|
76
|
+
]
|
|
77
|
+
)
|
|
78
|
+
|
|
84
79
|
fiscal_view = self.env.ref(
|
|
85
80
|
"l10n_br_fiscal.document_fiscal_line_mixin_form"
|
|
86
81
|
).sudo()
|
|
@@ -129,13 +124,19 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
129
124
|
e.attrib["name"] for e in target_node if e.tag == "field"
|
|
130
125
|
]
|
|
131
126
|
for fiscal_node in fiscal_nodes:
|
|
127
|
+
if fiscal_node.attrib["name"] in missing_line_fields:
|
|
128
|
+
missing_line_fields.remove(fiscal_node.attrib["name"])
|
|
132
129
|
if fiscal_node.attrib["name"] in existing_fields:
|
|
133
130
|
continue
|
|
134
131
|
field = deepcopy(fiscal_node)
|
|
135
132
|
if not field.attrib.get("optional"):
|
|
136
|
-
field.attrib["invisible"] = "0"
|
|
137
133
|
field.attrib["optional"] = "hide"
|
|
138
134
|
target_node.append(field)
|
|
135
|
+
for fname in missing_line_fields:
|
|
136
|
+
if fname not in existing_fields:
|
|
137
|
+
target_node.append(
|
|
138
|
+
E.field(name=fname, string=fname, optional="hide")
|
|
139
|
+
)
|
|
139
140
|
return doc
|
|
140
141
|
|
|
141
142
|
@api.model
|
|
@@ -146,23 +147,22 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
146
147
|
return arch, view
|
|
147
148
|
|
|
148
149
|
@api.depends(
|
|
149
|
-
"fiscal_price",
|
|
150
150
|
"discount_value",
|
|
151
|
-
"insurance_value",
|
|
152
|
-
"other_value",
|
|
153
|
-
"freight_value",
|
|
154
|
-
"fiscal_quantity",
|
|
155
151
|
"amount_tax_not_included",
|
|
156
|
-
"amount_tax_included",
|
|
157
152
|
"amount_tax_withholding",
|
|
158
|
-
"uot_id",
|
|
159
|
-
"product_id",
|
|
160
|
-
"partner_id",
|
|
161
|
-
"company_id",
|
|
162
153
|
"price_unit",
|
|
163
154
|
"quantity",
|
|
164
|
-
"icms_relief_id",
|
|
165
155
|
"fiscal_operation_line_id",
|
|
156
|
+
"cfop_id",
|
|
157
|
+
"icms_relief_value",
|
|
158
|
+
"insurance_value",
|
|
159
|
+
"other_value",
|
|
160
|
+
"freight_value",
|
|
161
|
+
"pis_value",
|
|
162
|
+
"cofins_value",
|
|
163
|
+
"icms_value",
|
|
164
|
+
"ii_value",
|
|
165
|
+
"ii_customhouse_charges",
|
|
166
166
|
)
|
|
167
167
|
def _compute_fiscal_amounts(self):
|
|
168
168
|
for record in self:
|
|
@@ -171,11 +171,11 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
171
171
|
# Total value of products or services
|
|
172
172
|
record.price_gross = round_curr.round(record.price_unit * record.quantity)
|
|
173
173
|
record.amount_fiscal = record.price_gross - record.discount_value
|
|
174
|
-
record.
|
|
174
|
+
record.fiscal_amount_tax = record.amount_tax_not_included
|
|
175
175
|
|
|
176
176
|
add_to_amount = sum(record[a] for a in record._add_fields_to_amount())
|
|
177
177
|
rm_to_amount = sum(record[r] for r in record._rm_fields_to_amount())
|
|
178
|
-
record.
|
|
178
|
+
record.fiscal_amount_untaxed = (
|
|
179
179
|
record.price_gross
|
|
180
180
|
- record.discount_value
|
|
181
181
|
+ add_to_amount
|
|
@@ -183,13 +183,17 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
183
183
|
)
|
|
184
184
|
|
|
185
185
|
# Valor do documento (NF)
|
|
186
|
-
record.
|
|
186
|
+
record.fiscal_amount_total = (
|
|
187
|
+
record.fiscal_amount_untaxed + record.fiscal_amount_tax
|
|
188
|
+
)
|
|
187
189
|
|
|
188
190
|
# Valor Liquido (TOTAL + IMPOSTOS - RETENÇÕES)
|
|
189
|
-
record.amount_taxed =
|
|
191
|
+
record.amount_taxed = (
|
|
192
|
+
record.fiscal_amount_total - record.amount_tax_withholding
|
|
193
|
+
)
|
|
190
194
|
|
|
191
195
|
# Valor do documento (NF) - RETENÇÕES
|
|
192
|
-
record.
|
|
196
|
+
record.fiscal_amount_total = record.amount_taxed
|
|
193
197
|
|
|
194
198
|
# Valor financeiro
|
|
195
199
|
if (
|
|
@@ -206,7 +210,7 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
206
210
|
record.financial_total_gross = record.financial_total = 0.0
|
|
207
211
|
record.financial_discount_value = 0.0
|
|
208
212
|
|
|
209
|
-
@api.depends("tax_icms_or_issqn", "
|
|
213
|
+
@api.depends("tax_icms_or_issqn", "partner_id")
|
|
210
214
|
def _compute_allow_csll_irpj(self):
|
|
211
215
|
"""Calculates the possibility of 'CSLL' and 'IRPJ' tax charges."""
|
|
212
216
|
for line in self:
|
|
@@ -233,82 +237,63 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
233
237
|
return {f"default_{k}": vals[k] for k in vals.keys()}
|
|
234
238
|
return vals
|
|
235
239
|
|
|
236
|
-
@api.
|
|
237
|
-
def
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
240
|
+
@api.depends("fiscal_operation_id", "partner_id", "product_id")
|
|
241
|
+
def _compute_fiscal_operation_line_id(self):
|
|
242
|
+
for line in self:
|
|
243
|
+
if line.fiscal_operation_id:
|
|
244
|
+
line.fiscal_operation_line_id = (
|
|
245
|
+
line.fiscal_operation_id.line_definition(
|
|
246
|
+
company=line.company_id,
|
|
247
|
+
partner=line.partner_id,
|
|
248
|
+
product=line.product_id,
|
|
249
|
+
)
|
|
250
|
+
)
|
|
244
251
|
|
|
245
|
-
|
|
246
|
-
""
|
|
247
|
-
|
|
248
|
-
""
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
"cest_id",
|
|
258
|
-
"city_taxation_code_id",
|
|
259
|
-
"service_type_id",
|
|
260
|
-
"ind_final",
|
|
261
|
-
]
|
|
262
|
-
|
|
263
|
-
@api.depends(lambda self: self._get_fiscal_tax_ids_dependencies())
|
|
252
|
+
@api.depends(
|
|
253
|
+
"partner_id",
|
|
254
|
+
"fiscal_operation_line_id",
|
|
255
|
+
"product_id",
|
|
256
|
+
"ncm_id",
|
|
257
|
+
"nbs_id",
|
|
258
|
+
"nbm_id",
|
|
259
|
+
"cest_id",
|
|
260
|
+
"city_taxation_code_id",
|
|
261
|
+
"service_type_id",
|
|
262
|
+
"ind_final",
|
|
263
|
+
)
|
|
264
264
|
def _compute_fiscal_tax_ids(self):
|
|
265
|
-
"""
|
|
266
|
-
Use fiscal_operation_line_id to map and compute the applicable Brazilian taxes.
|
|
267
|
-
|
|
268
|
-
Among the dependencies, company_id, partner_id and ind_final are related
|
|
269
|
-
to the fiscal document/line container. When called from account.move.line
|
|
270
|
-
via _inherits on newID records, we read these values from the related aml
|
|
271
|
-
to work around and _inherits/precompute limitation.
|
|
272
|
-
"""
|
|
273
|
-
if self._context.get("skip_compute_fiscal_tax_ids"):
|
|
274
|
-
return
|
|
275
265
|
for line in self:
|
|
276
|
-
if
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
partner=wrapped_line._get_fiscal_partner(),
|
|
289
|
-
product=wrapped_line.product_id,
|
|
290
|
-
ncm=wrapped_line.ncm_id,
|
|
291
|
-
nbm=wrapped_line.nbm_id,
|
|
292
|
-
nbs=wrapped_line.nbs_id,
|
|
293
|
-
cest=wrapped_line.cest_id,
|
|
294
|
-
city_taxation_code=wrapped_line.city_taxation_code_id,
|
|
295
|
-
service_type=wrapped_line.service_type_id,
|
|
296
|
-
ind_final=wrapped_line.ind_final,
|
|
266
|
+
if line.fiscal_operation_line_id:
|
|
267
|
+
mapping_result = line.fiscal_operation_line_id.map_fiscal_taxes(
|
|
268
|
+
company=line.company_id,
|
|
269
|
+
partner=line._get_fiscal_partner(),
|
|
270
|
+
product=line.product_id,
|
|
271
|
+
ncm=line.ncm_id,
|
|
272
|
+
nbm=line.nbm_id,
|
|
273
|
+
nbs=line.nbs_id,
|
|
274
|
+
cest=line.cest_id,
|
|
275
|
+
city_taxation_code=line.city_taxation_code_id,
|
|
276
|
+
service_type=line.service_type_id,
|
|
277
|
+
ind_final=line.ind_final,
|
|
297
278
|
)
|
|
298
279
|
line.cfop_id = mapping_result["cfop"]
|
|
299
280
|
line.ipi_guideline_id = mapping_result["ipi_guideline"]
|
|
300
281
|
line.icms_tax_benefit_id = mapping_result["icms_tax_benefit_id"]
|
|
301
|
-
|
|
302
|
-
|
|
282
|
+
|
|
283
|
+
if line._is_imported():
|
|
284
|
+
continue
|
|
303
285
|
|
|
304
286
|
taxes = line.env["l10n_br_fiscal.tax"]
|
|
305
287
|
for tax in mapping_result["taxes"].values():
|
|
306
288
|
taxes |= tax
|
|
307
289
|
line.fiscal_tax_ids = taxes
|
|
308
|
-
line.comment_ids = line.fiscal_operation_line_id.comment_ids
|
|
309
290
|
|
|
310
|
-
|
|
311
|
-
|
|
291
|
+
@api.depends("fiscal_operation_line_id")
|
|
292
|
+
def _compute_comment_ids(self):
|
|
293
|
+
for line in self:
|
|
294
|
+
line.comment_ids = [
|
|
295
|
+
Command.set(line.fiscal_operation_line_id.comment_ids.ids)
|
|
296
|
+
]
|
|
312
297
|
|
|
313
298
|
@api.model
|
|
314
299
|
def _build_null_mask_dict(self) -> dict:
|
|
@@ -326,85 +311,115 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
326
311
|
mask_dict[fiscal_tax_field] = False
|
|
327
312
|
return mask_dict
|
|
328
313
|
|
|
329
|
-
def
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
"
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
314
|
+
def write(self, vals):
|
|
315
|
+
res = super().write(vals)
|
|
316
|
+
|
|
317
|
+
# Verifica se algum campo de imposto relevante foi alterado no 'write'
|
|
318
|
+
tax_fields_in_vals = [fld for fld in vals if fld in FISCAL_TAX_ID_FIELDS]
|
|
319
|
+
|
|
320
|
+
if tax_fields_in_vals:
|
|
321
|
+
# Por segurança, sempre recalcula se um campo relevante mudou.
|
|
322
|
+
self._update_fiscal_tax_ids()
|
|
323
|
+
|
|
324
|
+
return res
|
|
325
|
+
|
|
326
|
+
def _update_fiscal_tax_ids(self):
|
|
327
|
+
taxes = self.env["l10n_br_fiscal.tax"]
|
|
328
|
+
for fiscal_tax_field in FISCAL_TAX_ID_FIELDS:
|
|
329
|
+
taxes |= self[fiscal_tax_field]
|
|
330
|
+
|
|
331
|
+
for line in self:
|
|
332
|
+
taxes_groups = line.fiscal_tax_ids.mapped("tax_domain")
|
|
333
|
+
fiscal_taxes = line.fiscal_tax_ids.filtered(
|
|
334
|
+
lambda ft, taxes_groups=taxes_groups: ft.tax_domain not in taxes_groups
|
|
335
|
+
)
|
|
336
|
+
line.fiscal_tax_ids = fiscal_taxes + taxes
|
|
337
|
+
|
|
338
|
+
@api.onchange(*FISCAL_TAX_ID_FIELDS)
|
|
339
|
+
def _onchange_fiscal_taxes(self):
|
|
340
|
+
self._update_fiscal_tax_ids()
|
|
341
|
+
|
|
342
|
+
@api.depends(
|
|
343
|
+
"partner_id",
|
|
344
|
+
"fiscal_tax_ids",
|
|
345
|
+
"product_id",
|
|
346
|
+
"price_unit",
|
|
347
|
+
"quantity",
|
|
348
|
+
"uom_id",
|
|
349
|
+
"fiscal_price",
|
|
350
|
+
"fiscal_quantity",
|
|
351
|
+
"uot_id",
|
|
352
|
+
"discount_value",
|
|
353
|
+
"insurance_value",
|
|
354
|
+
"ii_customhouse_charges",
|
|
355
|
+
"ii_iof_value",
|
|
356
|
+
"other_value",
|
|
357
|
+
"freight_value",
|
|
358
|
+
"ncm_id",
|
|
359
|
+
"nbs_id",
|
|
360
|
+
"nbm_id",
|
|
361
|
+
"cest_id",
|
|
362
|
+
"fiscal_operation_line_id",
|
|
363
|
+
"cfop_id",
|
|
364
|
+
"icmssn_range_id",
|
|
365
|
+
"icms_origin",
|
|
366
|
+
"icms_cst_id",
|
|
367
|
+
"ind_final",
|
|
368
|
+
"icms_relief_id",
|
|
369
|
+
)
|
|
357
370
|
def _compute_tax_fields(self):
|
|
358
371
|
"""
|
|
359
372
|
Compute base, percent, value... tax fields for ICMS, IPI, PIS, COFINS... taxes.
|
|
360
373
|
"""
|
|
361
|
-
if self._context.get("skip_compute_tax_fields"):
|
|
362
|
-
return
|
|
363
|
-
|
|
364
374
|
null_mask = None
|
|
365
375
|
for line in self.filtered(lambda line: not line._is_imported()):
|
|
366
|
-
if hasattr(line, "account_line_ids") and line.account_line_ids:
|
|
367
|
-
# it seems Odoo 16 ORM has a limitation when line is an
|
|
368
|
-
# l10n_br_fiscal.document.line that is edited via an account.move.line
|
|
369
|
-
# form and when both are a newID, then line relational field might be
|
|
370
|
-
# empty here. But in this case, we detect it and we wrap it back in the
|
|
371
|
-
wrapped_line = line.account_line_ids[0]
|
|
372
|
-
else:
|
|
373
|
-
wrapped_line = line
|
|
374
|
-
|
|
375
376
|
if null_mask is None:
|
|
376
377
|
null_mask = self._build_null_mask_dict()
|
|
377
378
|
to_update = null_mask.copy()
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
379
|
+
# prepare with default values
|
|
380
|
+
to_update.update(
|
|
381
|
+
{
|
|
382
|
+
"icms_base_type": ICMS_BASE_TYPE_DEFAULT,
|
|
383
|
+
"icmsst_base_type": ICMS_ST_BASE_TYPE_DEFAULT,
|
|
384
|
+
"ipi_base_type": TAX_BASE_TYPE_PERCENT,
|
|
385
|
+
"cofins_base_type": TAX_BASE_TYPE_PERCENT,
|
|
386
|
+
"cofinsst_base_type": TAX_BASE_TYPE_PERCENT,
|
|
387
|
+
"cofins_wh_base_type": TAX_BASE_TYPE_PERCENT,
|
|
388
|
+
"pis_base_type": TAX_BASE_TYPE_PERCENT,
|
|
389
|
+
"pisst_base_type": TAX_BASE_TYPE_PERCENT,
|
|
390
|
+
"pis_wh_base_type": TAX_BASE_TYPE_PERCENT,
|
|
391
|
+
}
|
|
392
|
+
)
|
|
393
|
+
if line.fiscal_operation_line_id:
|
|
394
|
+
compute_result = line.fiscal_tax_ids.compute_taxes(
|
|
395
|
+
company=line.company_id,
|
|
396
|
+
partner=line._get_fiscal_partner(),
|
|
397
|
+
product=line.product_id,
|
|
398
|
+
price_unit=line.price_unit,
|
|
399
|
+
quantity=line.quantity,
|
|
400
|
+
uom_id=line.uom_id,
|
|
401
|
+
fiscal_price=line.fiscal_price,
|
|
402
|
+
fiscal_quantity=line.fiscal_quantity,
|
|
403
|
+
uot_id=line.uot_id,
|
|
404
|
+
discount_value=line.discount_value,
|
|
405
|
+
insurance_value=line.insurance_value,
|
|
406
|
+
ii_customhouse_charges=line.ii_customhouse_charges,
|
|
407
|
+
ii_iof_value=line.ii_iof_value,
|
|
408
|
+
other_value=line.other_value,
|
|
409
|
+
freight_value=line.freight_value,
|
|
410
|
+
ncm=line.ncm_id,
|
|
411
|
+
nbs=line.nbs_id,
|
|
412
|
+
nbm=line.nbm_id,
|
|
413
|
+
cest=line.cest_id,
|
|
414
|
+
operation_line=line.fiscal_operation_line_id,
|
|
415
|
+
cfop=line.cfop_id,
|
|
416
|
+
icmssn_range=line.icmssn_range_id,
|
|
417
|
+
icms_origin=line.icms_origin,
|
|
418
|
+
icms_cst_id=line.icms_cst_id,
|
|
419
|
+
ind_final=line.ind_final,
|
|
420
|
+
icms_relief_id=line.icms_relief_id,
|
|
406
421
|
)
|
|
407
|
-
to_update.update(
|
|
422
|
+
to_update.update(line._prepare_tax_fields(compute_result))
|
|
408
423
|
else:
|
|
409
424
|
compute_result = {}
|
|
410
425
|
to_update.update(
|
|
@@ -419,11 +434,11 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
419
434
|
"estimate_tax": compute_result.get("estimate_tax", 0.0),
|
|
420
435
|
}
|
|
421
436
|
)
|
|
422
|
-
in_draft_mode =
|
|
437
|
+
in_draft_mode = line != line._origin
|
|
423
438
|
if in_draft_mode:
|
|
424
|
-
|
|
439
|
+
line.update(to_update)
|
|
425
440
|
else:
|
|
426
|
-
|
|
441
|
+
line.write(to_update)
|
|
427
442
|
|
|
428
443
|
def _prepare_tax_fields(self, compute_result):
|
|
429
444
|
self.ensure_one()
|
|
@@ -454,6 +469,10 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
454
469
|
"cost_price": line.product_id.standard_price,
|
|
455
470
|
}.get(line.fiscal_operation_id.default_price_unit, 0)
|
|
456
471
|
|
|
472
|
+
def _get_document(self):
|
|
473
|
+
self.ensure_one()
|
|
474
|
+
return self.document_id
|
|
475
|
+
|
|
457
476
|
def _get_fiscal_partner(self):
|
|
458
477
|
"""
|
|
459
478
|
Meant to be overriden when the l10n_br_fiscal.document partner_id should not
|
|
@@ -465,48 +484,47 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
465
484
|
self.ensure_one()
|
|
466
485
|
return self.partner_id
|
|
467
486
|
|
|
468
|
-
@api.
|
|
469
|
-
def
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
self._onchange_fiscal_operation_id()
|
|
487
|
+
@api.depends("product_id")
|
|
488
|
+
def _compute_product_fiscal_fields(self):
|
|
489
|
+
for line in self:
|
|
490
|
+
if not line.product_id:
|
|
491
|
+
# reset to default values:
|
|
492
|
+
line.fiscal_type = False
|
|
493
|
+
line.ncm_id = False
|
|
494
|
+
line.nbm_id = False
|
|
495
|
+
line.tax_icms_or_issqn = TAX_DOMAIN_ICMS
|
|
496
|
+
line.icms_origin = ICMS_ORIGIN_DEFAULT
|
|
497
|
+
line.cest_id = False
|
|
498
|
+
line.nbs_id = False
|
|
499
|
+
line.fiscal_genre_id = False
|
|
500
|
+
line.service_type_id = False
|
|
501
|
+
continue
|
|
502
|
+
p = line.product_id
|
|
503
|
+
line.fiscal_type = p.fiscal_type
|
|
504
|
+
line.ncm_id = p.ncm_id
|
|
505
|
+
line.nbm_id = p.nbm_id
|
|
506
|
+
line.tax_icms_or_issqn = p.tax_icms_or_issqn
|
|
507
|
+
line.icms_origin = p.icms_origin
|
|
508
|
+
line.cest_id = p.cest_id
|
|
509
|
+
line.nbs_id = p.nbs_id
|
|
510
|
+
line.fiscal_genre_id = p.fiscal_genre_id
|
|
511
|
+
line.service_type_id = p.service_type_id
|
|
512
|
+
|
|
513
|
+
@api.depends("product_id")
|
|
514
|
+
def _compute_city_taxation_code_id(self):
|
|
515
|
+
for line in self:
|
|
516
|
+
if not line.product_id:
|
|
517
|
+
line.city_taxation_code_id = False
|
|
518
|
+
continue
|
|
519
|
+
company_city = line.company_id.city_id
|
|
520
|
+
city_tax_codes = line.product_id.city_taxation_code_ids
|
|
521
|
+
city_tax_code = city_tax_codes.filtered(
|
|
522
|
+
lambda r, _city_id=company_city: r.city_id == _city_id
|
|
523
|
+
)
|
|
524
|
+
if city_tax_code:
|
|
525
|
+
line.city_taxation_code_id = city_tax_code
|
|
526
|
+
else:
|
|
527
|
+
line.city_taxation_code_id = False
|
|
510
528
|
|
|
511
529
|
def _prepare_fields_issqn(self, tax_dict):
|
|
512
530
|
self.ensure_one()
|
|
@@ -749,68 +767,31 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
|
|
|
749
767
|
"cofinsst_value": tax_dict.get("tax_value", 0.00),
|
|
750
768
|
}
|
|
751
769
|
|
|
752
|
-
@api.
|
|
753
|
-
def _onchange_fiscal_taxes(self):
|
|
754
|
-
taxes = self.env["l10n_br_fiscal.tax"]
|
|
755
|
-
for fiscal_tax_field in FISCAL_TAX_ID_FIELDS:
|
|
756
|
-
taxes |= self[fiscal_tax_field]
|
|
757
|
-
|
|
758
|
-
for line in self:
|
|
759
|
-
taxes_groups = line.fiscal_tax_ids.mapped("tax_domain")
|
|
760
|
-
fiscal_taxes = line.fiscal_tax_ids.filtered(
|
|
761
|
-
lambda ft, taxes_groups=taxes_groups: ft.tax_domain not in taxes_groups
|
|
762
|
-
)
|
|
763
|
-
line.fiscal_tax_ids = fiscal_taxes + taxes
|
|
764
|
-
|
|
765
|
-
@api.depends("uom_id")
|
|
770
|
+
@api.depends("product_id", "uom_id")
|
|
766
771
|
def _compute_uot_id(self):
|
|
767
772
|
for line in self:
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
@api.onchange("price_unit")
|
|
772
|
-
def _onchange_price_unit_fiscal(self):
|
|
773
|
-
self.fiscal_price = 0
|
|
774
|
-
self._compute_fiscal_price()
|
|
773
|
+
p = line.product_id
|
|
774
|
+
line.uot_id = (p.uot_id if p else False) or line.uom_id
|
|
775
775
|
|
|
776
776
|
@api.depends("price_unit")
|
|
777
777
|
def _compute_fiscal_price(self):
|
|
778
778
|
for line in self:
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
)
|
|
786
|
-
else:
|
|
787
|
-
line.fiscal_price = line.price_unit
|
|
788
|
-
|
|
789
|
-
@api.onchange("quantity")
|
|
790
|
-
def _onchange_quantity_fiscal(self):
|
|
791
|
-
self.fiscal_quantity = 0
|
|
792
|
-
self._compute_fiscal_quantity()
|
|
779
|
+
if line.product_id and line.price_unit:
|
|
780
|
+
line.fiscal_price = line.price_unit / (
|
|
781
|
+
line.product_id.uot_factor or 1.0
|
|
782
|
+
)
|
|
783
|
+
else:
|
|
784
|
+
line.fiscal_price = line.price_unit
|
|
793
785
|
|
|
794
786
|
@api.depends("quantity")
|
|
795
787
|
def _compute_fiscal_quantity(self):
|
|
796
788
|
for line in self:
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
)
|
|
804
|
-
else:
|
|
805
|
-
line.fiscal_quantity = line.quantity
|
|
806
|
-
|
|
807
|
-
@api.onchange("city_taxation_code_id")
|
|
808
|
-
def _onchange_city_taxation_code_id(self):
|
|
809
|
-
if self.city_taxation_code_id:
|
|
810
|
-
self.cnae_id = self.city_taxation_code_id.cnae_id
|
|
811
|
-
self._onchange_fiscal_operation_id()
|
|
812
|
-
if self.city_taxation_code_id.city_id:
|
|
813
|
-
self.update({"issqn_fg_city_id": self.city_taxation_code_id.city_id})
|
|
789
|
+
if line.product_id and line.quantity:
|
|
790
|
+
line.fiscal_quantity = line.quantity * (
|
|
791
|
+
line.product_id.uot_factor or 1.0
|
|
792
|
+
)
|
|
793
|
+
else:
|
|
794
|
+
line.fiscal_quantity = line.quantity
|
|
814
795
|
|
|
815
796
|
@api.model
|
|
816
797
|
def _add_fields_to_amount(self):
|