odoo-addon-l10n-br-fiscal 18.0.2.0.0.10__py3-none-any.whl → 18.0.7.1.0__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 (78) hide show
  1. odoo/addons/l10n_br_fiscal/README.rst +1 -1
  2. odoo/addons/l10n_br_fiscal/__manifest__.py +7 -3
  3. odoo/addons/l10n_br_fiscal/constants/fiscal.py +64 -18
  4. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.cest.csv +1043 -983
  5. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.cst.csv +58 -0
  6. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.document.type.csv +1 -0
  7. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.operation.indicator.csv +27 -0
  8. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.tax.classification.csv +163 -0
  9. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.tax.csv +31 -0
  10. odoo/addons/l10n_br_fiscal/data/l10n_br_fiscal.tax.group.csv +3 -0
  11. odoo/addons/l10n_br_fiscal/data/operation_data.xml +1 -1
  12. odoo/addons/l10n_br_fiscal/demo/fiscal_document_demo.xml +3 -179
  13. odoo/addons/l10n_br_fiscal/demo/fiscal_document_nfse_demo.xml +0 -4
  14. odoo/addons/l10n_br_fiscal/demo/fiscal_operation_demo.xml +2 -2
  15. odoo/addons/l10n_br_fiscal/i18n/l10n_br_fiscal.pot +665 -79
  16. odoo/addons/l10n_br_fiscal/i18n/pt_BR.po +713 -102
  17. odoo/addons/l10n_br_fiscal/migrations/18.0.3.0.0/pre-migration.py +30 -0
  18. odoo/addons/l10n_br_fiscal/models/__init__.py +2 -2
  19. odoo/addons/l10n_br_fiscal/models/comment.py +3 -1
  20. odoo/addons/l10n_br_fiscal/models/data_abstract.py +9 -6
  21. odoo/addons/l10n_br_fiscal/models/document.py +27 -8
  22. odoo/addons/l10n_br_fiscal/models/document_line.py +51 -8
  23. odoo/addons/l10n_br_fiscal/models/document_line_mixin.py +1107 -35
  24. odoo/addons/l10n_br_fiscal/models/document_mixin.py +244 -6
  25. odoo/addons/l10n_br_fiscal/models/document_related.py +1 -1
  26. odoo/addons/l10n_br_fiscal/models/document_serie.py +33 -0
  27. odoo/addons/l10n_br_fiscal/models/icms_regulation.py +1 -1
  28. odoo/addons/l10n_br_fiscal/models/operation_dashboard.py +3 -2
  29. odoo/addons/l10n_br_fiscal/models/operation_indicator.py +58 -0
  30. odoo/addons/l10n_br_fiscal/models/operation_line.py +28 -0
  31. odoo/addons/l10n_br_fiscal/models/partner_profile.py +6 -0
  32. odoo/addons/l10n_br_fiscal/models/product_template.py +4 -0
  33. odoo/addons/l10n_br_fiscal/models/res_company.py +17 -0
  34. odoo/addons/l10n_br_fiscal/models/res_partner.py +17 -0
  35. odoo/addons/l10n_br_fiscal/models/simplified_tax_range.py +8 -0
  36. odoo/addons/l10n_br_fiscal/models/tax.py +7 -3
  37. odoo/addons/l10n_br_fiscal/models/tax_classification.py +81 -0
  38. odoo/addons/l10n_br_fiscal/security/fiscal_security.xml +6 -16
  39. odoo/addons/l10n_br_fiscal/security/ir.model.access.csv +7 -2
  40. odoo/addons/l10n_br_fiscal/static/description/index.html +1 -1
  41. odoo/addons/l10n_br_fiscal/tests/__init__.py +2 -0
  42. odoo/addons/l10n_br_fiscal/tests/test_document_edition.py +175 -10
  43. odoo/addons/l10n_br_fiscal/tests/test_fiscal_document_generic.py +13 -42
  44. odoo/addons/l10n_br_fiscal/tests/test_fiscal_document_nfse.py +0 -5
  45. odoo/addons/l10n_br_fiscal/tests/test_fiscal_document_serie.py +60 -0
  46. odoo/addons/l10n_br_fiscal/tests/test_tax_benefit.py +2 -5
  47. odoo/addons/l10n_br_fiscal/tests/test_tax_classification.py +110 -0
  48. odoo/addons/l10n_br_fiscal/views/document_line_mixin_view.xml +107 -4
  49. odoo/addons/l10n_br_fiscal/views/document_line_view.xml +7 -3
  50. odoo/addons/l10n_br_fiscal/views/document_view.xml +34 -15
  51. odoo/addons/l10n_br_fiscal/views/icms_regulation_view.xml +1 -5
  52. odoo/addons/l10n_br_fiscal/views/l10n_br_fiscal_action.xml +30 -0
  53. odoo/addons/l10n_br_fiscal/views/l10n_br_fiscal_menu.xml +16 -9
  54. odoo/addons/l10n_br_fiscal/views/nbs_view.xml +1 -5
  55. odoo/addons/l10n_br_fiscal/views/ncm_view.xml +1 -5
  56. odoo/addons/l10n_br_fiscal/views/operation_dashboard_view.xml +3 -3
  57. odoo/addons/l10n_br_fiscal/views/operation_indicator_view.xml +75 -0
  58. odoo/addons/l10n_br_fiscal/views/operation_line_view.xml +4 -5
  59. odoo/addons/l10n_br_fiscal/views/operation_view.xml +1 -5
  60. odoo/addons/l10n_br_fiscal/views/product_product_view.xml +33 -6
  61. odoo/addons/l10n_br_fiscal/views/product_template_view.xml +22 -4
  62. odoo/addons/l10n_br_fiscal/views/res_company_view.xml +6 -0
  63. odoo/addons/l10n_br_fiscal/views/res_partner_view.xml +10 -0
  64. odoo/addons/l10n_br_fiscal/views/service_type_view.xml +1 -5
  65. odoo/addons/l10n_br_fiscal/views/tax_classification.xml +108 -0
  66. odoo/addons/l10n_br_fiscal/views/tax_definition_view.xml +1 -5
  67. odoo/addons/l10n_br_fiscal/views/tax_view.xml +2 -2
  68. odoo/addons/l10n_br_fiscal/wizards/__init__.py +1 -1
  69. odoo/addons/l10n_br_fiscal/wizards/base_wizard_mixin.py +1 -1
  70. odoo/addons/l10n_br_fiscal/wizards/document_import_wizard.py +234 -0
  71. odoo/addons/l10n_br_fiscal/wizards/{document_import_wizard_mixin.xml → document_import_wizard.xml} +26 -7
  72. {odoo_addon_l10n_br_fiscal-18.0.2.0.0.10.dist-info → odoo_addon_l10n_br_fiscal-18.0.7.1.0.dist-info}/METADATA +3 -3
  73. {odoo_addon_l10n_br_fiscal-18.0.2.0.0.10.dist-info → odoo_addon_l10n_br_fiscal-18.0.7.1.0.dist-info}/RECORD +75 -68
  74. odoo/addons/l10n_br_fiscal/models/document_line_mixin_methods.py +0 -837
  75. odoo/addons/l10n_br_fiscal/models/document_mixin_methods.py +0 -349
  76. odoo/addons/l10n_br_fiscal/wizards/document_import_wizard_mixin.py +0 -129
  77. {odoo_addon_l10n_br_fiscal-18.0.2.0.0.10.dist-info → odoo_addon_l10n_br_fiscal-18.0.7.1.0.dist-info}/WHEEL +0 -0
  78. {odoo_addon_l10n_br_fiscal-18.0.2.0.0.10.dist-info → odoo_addon_l10n_br_fiscal-18.0.7.1.0.dist-info}/top_level.txt +0 -0
@@ -1,837 +0,0 @@
1
- # Copyright (C) 2019 Renato Lima - Akretion <renato.lima@akretion.com.br>
2
- # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
3
-
4
- from copy import deepcopy
5
-
6
- from lxml import etree
7
-
8
- from odoo import Command, api, models
9
-
10
- from ..constants.fiscal import CFOP_DESTINATION_EXPORT, FISCAL_IN
11
- from ..constants.icms import ICMS_BASE_TYPE_DEFAULT, ICMS_ST_BASE_TYPE_DEFAULT
12
-
13
- FISCAL_TAX_ID_FIELDS = [
14
- "cofins_tax_id",
15
- "cofins_wh_tax_id",
16
- "cofinsst_tax_id",
17
- "csll_tax_id",
18
- "csll_wh_tax_id",
19
- "icms_tax_id",
20
- "icmsfcp_tax_id",
21
- "icmssn_tax_id",
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
- ]
45
-
46
-
47
- class FiscalDocumentLineMixinMethods(models.AbstractModel):
48
- """
49
- Provides the method implementations for l10n_br_fiscal.document.line.mixin.
50
-
51
- These methods are extracted into this separate mixin due to the way
52
- l10n_br_fiscal.document.line is incorporated into account.move.line
53
- by the l10n_br_account module (decorator pattern).
54
-
55
- Specifically:
56
- - In l10n_br_account, fields from l10n_br_fiscal.document.line
57
- are added to account.move.line using Odoo's `_inherits` (composition)
58
- mechanism.
59
- - The methods in *this* mixin, however, are intended to be inherited
60
- using the standard `_inherit` mechanism.
61
-
62
- This separation is crucial because `_inherits` handles field composition
63
- but does not inherit methods. Thus, `_inherit` is used to bring in
64
- these methods. If these methods were defined in the same class as the
65
- fields of l10n_br_fiscal.document.line.mixin (which are subject to
66
- `_inherits`), and account.move.line also used `_inherit` on that
67
- single class, the fields would be duplicated.
68
- """
69
-
70
- _name = "l10n_br_fiscal.document.line.mixin.methods"
71
- _description = "Fiscal Document Mixin Methods"
72
-
73
- @api.model
74
- def inject_fiscal_fields(
75
- self,
76
- doc,
77
- view_ref="l10n_br_fiscal.document_fiscal_line_mixin_form",
78
- xpath_mappings=None,
79
- ):
80
- """
81
- Inject common fiscal fields into view placeholder elements.
82
- Used for invoice line, sale order line, purchase order line...
83
- """
84
- fiscal_view = self.env.ref(
85
- "l10n_br_fiscal.document_fiscal_line_mixin_form"
86
- ).sudo()
87
- fsc_doc = etree.fromstring(
88
- fiscal_view.with_context(inherit_branding=True).get_combined_arch()
89
- )
90
-
91
- if xpath_mappings is None:
92
- xpath_mappings = (
93
- # (placeholder_xpath, fiscal_xpath)
94
- (".//group[@name='fiscal_fields']", "//group[@name='fiscal_fields']"),
95
- (".//page[@name='fiscal_taxes']", "//page[@name='fiscal_taxes']"),
96
- (
97
- ".//page[@name='fiscal_line_extra_info']",
98
- "//page[@name='fiscal_line_extra_info']",
99
- ),
100
- # these will only collect (invisible) fields for onchanges:
101
- (
102
- ".//control[@name='fiscal_fields']...",
103
- "//group[@name='fiscal_fields']//field",
104
- ),
105
- (
106
- ".//control[@name='fiscal_taxes_fields']...",
107
- "//page[@name='fiscal_taxes']//field",
108
- ),
109
- (
110
- ".//control[@name='fiscal_line_extra_info_fields']...",
111
- "//page[@name='fiscal_line_extra_info']//field",
112
- ),
113
- )
114
- for placeholder_xpath, fiscal_xpath in xpath_mappings:
115
- placeholder_nodes = doc.findall(placeholder_xpath)
116
- if not placeholder_nodes:
117
- continue
118
- fiscal_nodes = fsc_doc.xpath(fiscal_xpath)
119
- for target_node in placeholder_nodes:
120
- if len(fiscal_nodes) == 1:
121
- # replace unique placeholder
122
- # (deepcopy is required to inject fiscal nodes in possible
123
- # next places)
124
- replace_node = deepcopy(fiscal_nodes[0])
125
- target_node.getparent().replace(target_node, replace_node)
126
- else:
127
- # append multiple fields to placeholder container
128
- existing_fields = [
129
- e.attrib["name"] for e in target_node if e.tag == "field"
130
- ]
131
- for fiscal_node in fiscal_nodes:
132
- if fiscal_node.attrib["name"] in existing_fields:
133
- continue
134
- field = deepcopy(fiscal_node)
135
- if not field.attrib.get("optional"):
136
- field.attrib["invisible"] = "0"
137
- field.attrib["optional"] = "hide"
138
- target_node.append(field)
139
- return doc
140
-
141
- @api.model
142
- def _get_view(self, view_id=None, view_type="form", **options):
143
- arch, view = super()._get_view(view_id, view_type, **options)
144
- if view_type == "form":
145
- arch = self.inject_fiscal_fields(arch)
146
- return arch, view
147
-
148
- @api.depends(
149
- "fiscal_price",
150
- "discount_value",
151
- "insurance_value",
152
- "other_value",
153
- "freight_value",
154
- "fiscal_quantity",
155
- "amount_tax_not_included",
156
- "amount_tax_included",
157
- "amount_tax_withholding",
158
- "uot_id",
159
- "product_id",
160
- "partner_id",
161
- "company_id",
162
- "price_unit",
163
- "quantity",
164
- "icms_relief_id",
165
- "fiscal_operation_line_id",
166
- )
167
- def _compute_fiscal_amounts(self):
168
- for record in self:
169
- round_curr = record.currency_id or self.env.ref("base.BRL")
170
-
171
- # Total value of products or services
172
- record.price_gross = round_curr.round(record.price_unit * record.quantity)
173
- record.amount_fiscal = record.price_gross - record.discount_value
174
- record.amount_tax = record.amount_tax_not_included
175
-
176
- add_to_amount = sum(record[a] for a in record._add_fields_to_amount())
177
- rm_to_amount = sum(record[r] for r in record._rm_fields_to_amount())
178
- record.amount_untaxed = (
179
- record.price_gross
180
- - record.discount_value
181
- + add_to_amount
182
- - rm_to_amount
183
- )
184
-
185
- # Valor do documento (NF)
186
- record.amount_total = record.amount_untaxed + record.amount_tax
187
-
188
- # Valor Liquido (TOTAL + IMPOSTOS - RETENÇÕES)
189
- record.amount_taxed = record.amount_total - record.amount_tax_withholding
190
-
191
- # Valor do documento (NF) - RETENÇÕES
192
- record.amount_total = record.amount_taxed
193
-
194
- # Valor financeiro
195
- if (
196
- record.fiscal_operation_line_id
197
- and record.fiscal_operation_line_id.add_to_amount
198
- and (not record.cfop_id or record.cfop_id.finance_move)
199
- ):
200
- record.financial_total = record.amount_taxed
201
- record.financial_total_gross = (
202
- record.financial_total + record.discount_value
203
- )
204
- record.financial_discount_value = record.discount_value
205
- else:
206
- record.financial_total_gross = record.financial_total = 0.0
207
- record.financial_discount_value = 0.0
208
-
209
- @api.depends("tax_icms_or_issqn", "partner_is_public_entity")
210
- def _compute_allow_csll_irpj(self):
211
- """Calculates the possibility of 'CSLL' and 'IRPJ' tax charges."""
212
- for line in self:
213
- # Determine if 'CSLL' and 'IRPJ' taxes may apply:
214
- # 1. When providing services (tax_icms_or_issqn == "issqn")
215
- # 2. When supplying products to public entities (partner_is_public_entity
216
- # is True)
217
- if line.tax_icms_or_issqn == "issqn" or line.partner_is_public_entity:
218
- line.allow_csll_irpj = True # Tax charges may apply
219
- else:
220
- line.allow_csll_irpj = False # No tax charges expected
221
-
222
- def _prepare_br_fiscal_dict(self, default=False):
223
- self.ensure_one()
224
- fields = self.env["l10n_br_fiscal.document.line.mixin"]._fields.keys()
225
-
226
- # we now read the record fiscal fields except the m2m tax:
227
- vals = self._convert_to_write(self.read(fields)[0])
228
-
229
- # remove id field to avoid conflicts
230
- vals.pop("id", None)
231
-
232
- if default: # in case you want to use new rather than write later
233
- return {f"default_{k}": vals[k] for k in vals.keys()}
234
- return vals
235
-
236
- @api.onchange("fiscal_operation_id", "company_id", "partner_id", "product_id")
237
- def _onchange_fiscal_operation_id(self):
238
- if self.fiscal_operation_id:
239
- self.fiscal_operation_line_id = self.fiscal_operation_id.line_definition(
240
- company=self.company_id,
241
- partner=self._get_fiscal_partner(),
242
- product=self.product_id,
243
- )
244
-
245
- def _get_fiscal_tax_ids_dependencies(self):
246
- """
247
- Dynamically get the list of fields dependencies, overriden in l10n_br_purchase.
248
- """
249
- return [
250
- "company_id",
251
- "partner_id",
252
- "fiscal_operation_line_id",
253
- "product_id",
254
- "ncm_id",
255
- "nbs_id",
256
- "nbm_id",
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())
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
- for line in self:
276
- if hasattr(line, "account_line_ids") and line.account_line_ids:
277
- # it seems Odoo 16 ORM has a limitation when line is an
278
- # l10n_br_fiscal.document.line that is edited via an account.move.line
279
- # form and when both are a newID, then line relational field might be
280
- # empty here. But in this case, we detect it and we wrap it back in the
281
- wrapped_line = line.account_line_ids[0]
282
- else:
283
- wrapped_line = line
284
-
285
- if wrapped_line.fiscal_operation_line_id:
286
- mapping_result = wrapped_line.fiscal_operation_line_id.map_fiscal_taxes(
287
- company=wrapped_line.company_id,
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,
297
- )
298
- line.cfop_id = mapping_result["cfop"]
299
- line.ipi_guideline_id = mapping_result["ipi_guideline"]
300
- line.icms_tax_benefit_id = mapping_result["icms_tax_benefit_id"]
301
- if wrapped_line._is_imported():
302
- return
303
-
304
- taxes = line.env["l10n_br_fiscal.tax"]
305
- for tax in mapping_result["taxes"].values():
306
- taxes |= tax
307
- line.fiscal_tax_ids = taxes
308
- line.comment_ids = line.fiscal_operation_line_id.comment_ids
309
-
310
- else:
311
- line.fiscal_tax_ids = [Command.clear()]
312
-
313
- @api.model
314
- def _build_null_mask_dict(self) -> dict:
315
- """
316
- Build a null values mask dict to reset all fiscal fields.
317
- """
318
- mask_dict = {
319
- f[0]: False
320
- for f in filter(
321
- lambda f: f[1].compute == "_compute_tax_fields",
322
- self.env["l10n_br_fiscal.document.line.mixin"]._fields.items(),
323
- )
324
- }
325
- for fiscal_tax_field in FISCAL_TAX_ID_FIELDS:
326
- mask_dict[fiscal_tax_field] = False
327
- return mask_dict
328
-
329
- def _get_tax_fields_dependencies(self):
330
- """
331
- Dynamically get the list of fields dependencies, overriden in l10n_br_purchase.
332
- """
333
- # IMPORTANT NOTE: as _compute_fiscal_tax_ids triggers _compute_tax_fields,
334
- # we don't put fields that trigger _compute_fiscal_tax_ids as dependencies here.
335
- return [
336
- "price_unit",
337
- "quantity",
338
- "uom_id",
339
- "fiscal_price",
340
- "fiscal_quantity",
341
- "uot_id",
342
- "discount_value",
343
- "insurance_value",
344
- "ii_customhouse_charges",
345
- "ii_iof_value",
346
- "other_value",
347
- "freight_value",
348
- "cfop_id",
349
- "icmssn_range_id",
350
- "icms_origin",
351
- "icms_cst_id",
352
- "icms_relief_id",
353
- "fiscal_tax_ids",
354
- ]
355
-
356
- @api.depends(lambda self: self._get_tax_fields_dependencies())
357
- def _compute_tax_fields(self):
358
- """
359
- Compute base, percent, value... tax fields for ICMS, IPI, PIS, COFINS... taxes.
360
- """
361
- if self._context.get("skip_compute_tax_fields"):
362
- return
363
-
364
- null_mask = None
365
- 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
- if null_mask is None:
376
- null_mask = self._build_null_mask_dict()
377
- to_update = null_mask.copy()
378
- if wrapped_line.fiscal_operation_line_id:
379
- compute_result = wrapped_line.fiscal_tax_ids.compute_taxes(
380
- company=wrapped_line.company_id,
381
- partner=wrapped_line._get_fiscal_partner(),
382
- product=wrapped_line.product_id,
383
- price_unit=wrapped_line.price_unit,
384
- quantity=wrapped_line.quantity,
385
- uom_id=wrapped_line.uom_id,
386
- fiscal_price=wrapped_line.fiscal_price,
387
- fiscal_quantity=wrapped_line.fiscal_quantity,
388
- uot_id=wrapped_line.uot_id,
389
- discount_value=wrapped_line.discount_value,
390
- insurance_value=wrapped_line.insurance_value,
391
- ii_customhouse_charges=wrapped_line.ii_customhouse_charges,
392
- ii_iof_value=wrapped_line.ii_iof_value,
393
- other_value=wrapped_line.other_value,
394
- freight_value=wrapped_line.freight_value,
395
- ncm=wrapped_line.ncm_id,
396
- nbs=wrapped_line.nbs_id,
397
- nbm=wrapped_line.nbm_id,
398
- cest=wrapped_line.cest_id,
399
- operation_line=wrapped_line.fiscal_operation_line_id,
400
- cfop=wrapped_line.cfop_id,
401
- icmssn_range=wrapped_line.icmssn_range_id,
402
- icms_origin=wrapped_line.icms_origin,
403
- icms_cst_id=wrapped_line.icms_cst_id,
404
- ind_final=wrapped_line.ind_final,
405
- icms_relief_id=wrapped_line.icms_relief_id,
406
- )
407
- to_update.update(wrapped_line._prepare_tax_fields(compute_result))
408
- else:
409
- compute_result = {}
410
- to_update.update(
411
- {
412
- "amount_tax_included": compute_result.get("amount_included", 0.0),
413
- "amount_tax_not_included": compute_result.get(
414
- "amount_not_included", 0.0
415
- ),
416
- "amount_tax_withholding": compute_result.get(
417
- "amount_withholding", 0.0
418
- ),
419
- "estimate_tax": compute_result.get("estimate_tax", 0.0),
420
- }
421
- )
422
- in_draft_mode = wrapped_line != wrapped_line._origin
423
- if in_draft_mode:
424
- wrapped_line.update(to_update)
425
- else:
426
- wrapped_line.write(to_update)
427
-
428
- def _prepare_tax_fields(self, compute_result):
429
- self.ensure_one()
430
- tax_values = {}
431
- if self._is_imported():
432
- return tax_values
433
- computed_taxes = compute_result.get("taxes", {})
434
- for tax in self.fiscal_tax_ids:
435
- computed_tax = computed_taxes.get(tax.tax_domain, {})
436
- tax_field_name = f"{tax.tax_domain}_tax_id"
437
- if hasattr(self, tax_field_name):
438
- tax_values[tax_field_name] = tax.ids[0]
439
- method = getattr(self, f"_prepare_fields_{tax.tax_domain}", None)
440
- if method and computed_tax:
441
- prepared_fields = method(computed_tax)
442
- if prepared_fields:
443
- tax_values.update(prepared_fields)
444
- return tax_values
445
-
446
- @api.depends(
447
- "product_id",
448
- "fiscal_operation_id",
449
- )
450
- def _compute_price_unit_fiscal(self): # OK when edited from aml?? c-> check
451
- for line in self:
452
- line.price_unit = {
453
- "sale_price": line.product_id.list_price,
454
- "cost_price": line.product_id.standard_price,
455
- }.get(line.fiscal_operation_id.default_price_unit, 0)
456
-
457
- def _get_fiscal_partner(self):
458
- """
459
- Meant to be overriden when the l10n_br_fiscal.document partner_id should not
460
- be the same as the sale.order, purchase.order, account.move (...) partner_id.
461
-
462
- (In the case of invoicing, the invoicing partner set by the user should
463
- get priority over any invoicing contact returned by address_get.)
464
- """
465
- self.ensure_one()
466
- return self.partner_id
467
-
468
- @api.onchange("product_id")
469
- def _onchange_product_id_fiscal(self):
470
- if not self.fiscal_operation_id:
471
- return
472
- if self.product_id:
473
- self.name = self.product_id.display_name
474
- self.fiscal_type = self.product_id.fiscal_type
475
- self.uom_id = self.product_id.uom_id
476
- self.ncm_id = self.product_id.ncm_id
477
- self.nbm_id = self.product_id.nbm_id
478
- self.tax_icms_or_issqn = self.product_id.tax_icms_or_issqn
479
- self.icms_origin = self.product_id.icms_origin
480
- self.cest_id = self.product_id.cest_id
481
- self.nbs_id = self.product_id.nbs_id
482
- self.fiscal_genre_id = self.product_id.fiscal_genre_id
483
- self.service_type_id = self.product_id.service_type_id
484
- self.uot_id = self.product_id.uot_id or self.product_id.uom_id
485
- if self.product_id.city_taxation_code_ids:
486
- company_city_id = self.company_id.city_id
487
- city_id = self.product_id.city_taxation_code_ids.filtered(
488
- lambda r: r.city_id == company_city_id
489
- )
490
- if city_id:
491
- self.city_taxation_code_id = city_id
492
- self.issqn_fg_city_id = company_city_id
493
- else:
494
- self.name = False
495
- self.fiscal_type = False
496
- self.uom_id = False
497
- self.ncm_id = False
498
- self.nbm_id = False
499
- self.tax_icms_or_issqn = False
500
- self.icms_origin = False
501
- self.cest_id = False
502
- self.nbs_id = False
503
- self.fiscal_genre_id = False
504
- self.service_type_id = False
505
- self.city_taxation_code_id = False
506
- self.uot_id = False
507
-
508
- self._compute_price_unit_fiscal()
509
- self._onchange_fiscal_operation_id()
510
-
511
- def _prepare_fields_issqn(self, tax_dict):
512
- self.ensure_one()
513
- return {
514
- "issqn_base": tax_dict.get("base"),
515
- "issqn_percent": tax_dict.get("percent_amount"),
516
- "issqn_reduction": tax_dict.get("percent_reduction"),
517
- "issqn_value": tax_dict.get("tax_value"),
518
- }
519
-
520
- def _prepare_fields_issqn_wh(self, tax_dict):
521
- self.ensure_one()
522
- return {
523
- "issqn_wh_base": tax_dict.get("base"),
524
- "issqn_wh_percent": tax_dict.get("percent_amount"),
525
- "issqn_wh_reduction": tax_dict.get("percent_reduction"),
526
- "issqn_wh_value": tax_dict.get("tax_value"),
527
- }
528
-
529
- def _prepare_fields_csll(self, tax_dict):
530
- self.ensure_one()
531
- return {
532
- "csll_base": tax_dict.get("base"),
533
- "csll_percent": tax_dict.get("percent_amount"),
534
- "csll_reduction": tax_dict.get("percent_reduction"),
535
- "csll_value": tax_dict.get("tax_value"),
536
- }
537
-
538
- def _prepare_fields_csll_wh(self, tax_dict):
539
- self.ensure_one()
540
- return {
541
- "csll_wh_base": tax_dict.get("base"),
542
- "csll_wh_percent": tax_dict.get("percent_amount"),
543
- "csll_wh_reduction": tax_dict.get("percent_reduction"),
544
- "csll_wh_value": tax_dict.get("tax_value"),
545
- }
546
-
547
- def _prepare_fields_irpj(self, tax_dict):
548
- self.ensure_one()
549
- return {
550
- "irpj_base": tax_dict.get("base"),
551
- "irpj_percent": tax_dict.get("percent_amount"),
552
- "irpj_reduction": tax_dict.get("percent_reduction"),
553
- "irpj_value": tax_dict.get("tax_value"),
554
- }
555
-
556
- def _prepare_fields_irpj_wh(self, tax_dict):
557
- self.ensure_one()
558
- return {
559
- "irpj_wh_base": tax_dict.get("base"),
560
- "irpj_wh_percent": tax_dict.get("percent_amount"),
561
- "irpj_wh_reduction": tax_dict.get("percent_reduction"),
562
- "irpj_wh_value": tax_dict.get("tax_value"),
563
- }
564
-
565
- def _prepare_fields_inss(self, tax_dict):
566
- self.ensure_one()
567
- return {
568
- "inss_base": tax_dict.get("base"),
569
- "inss_percent": tax_dict.get("percent_amount"),
570
- "inss_reduction": tax_dict.get("percent_reduction"),
571
- "inss_value": tax_dict.get("tax_value"),
572
- }
573
-
574
- def _prepare_fields_inss_wh(self, tax_dict):
575
- self.ensure_one()
576
- return {
577
- "inss_wh_base": tax_dict.get("base"),
578
- "inss_wh_percent": tax_dict.get("percent_amount"),
579
- "inss_wh_reduction": tax_dict.get("percent_reduction"),
580
- "inss_wh_value": tax_dict.get("tax_value"),
581
- }
582
-
583
- def _prepare_fields_icms(self, tax_dict):
584
- self.ensure_one()
585
- cst_id = tax_dict.get("cst_id").id if tax_dict.get("cst_id") else False
586
- return {
587
- "icms_cst_id": cst_id,
588
- "icms_base_type": tax_dict.get("icms_base_type", ICMS_BASE_TYPE_DEFAULT),
589
- "icms_base": tax_dict.get("base", 0.0),
590
- "icms_percent": tax_dict.get("percent_amount", 0.0),
591
- "icms_reduction": tax_dict.get("percent_reduction", 0.0),
592
- "icms_value": tax_dict.get("tax_value", 0.0),
593
- "icms_origin_percent": tax_dict.get("icms_origin_perc", 0.0),
594
- "icms_destination_percent": tax_dict.get("icms_dest_perc", 0.0),
595
- "icms_sharing_percent": tax_dict.get("icms_sharing_percent", 0.0),
596
- "icms_destination_base": tax_dict.get("icms_dest_base", 0.0),
597
- "icms_origin_value": tax_dict.get("icms_origin_value", 0.0),
598
- "icms_destination_value": tax_dict.get("icms_dest_value", 0.0),
599
- "icms_relief_value": tax_dict.get("icms_relief", 0.0),
600
- }
601
-
602
- @api.onchange(
603
- "icms_base",
604
- "icms_percent",
605
- "icms_reduction",
606
- "icms_value",
607
- "icms_destination_base",
608
- "icms_origin_percent",
609
- "icms_destination_percent",
610
- "icms_sharing_percent",
611
- "icms_origin_value",
612
- "icms_tax_benefit_id",
613
- )
614
- def _onchange_icms_fields(self):
615
- if self.icms_tax_benefit_id:
616
- self.icms_tax_id = self.icms_tax_benefit_id.tax_id
617
-
618
- def _prepare_fields_icmssn(self, tax_dict):
619
- self.ensure_one()
620
- cst_id = tax_dict.get("cst_id").id if tax_dict.get("cst_id") else False
621
- icmssn_base = tax_dict.get("base", 0.0)
622
- icmssn_credit_value = tax_dict.get("tax_value", 0.0)
623
- simple_value = icmssn_base * self.icmssn_range_id.total_tax_percent
624
- simple_without_icms_value = simple_value - icmssn_credit_value
625
- return {
626
- "icms_cst_id": cst_id,
627
- "icmssn_base": icmssn_base,
628
- "icmssn_percent": tax_dict.get("percent_amount"),
629
- "icmssn_reduction": tax_dict.get("percent_reduction"),
630
- "icmssn_credit_value": icmssn_credit_value,
631
- "simple_value": simple_value,
632
- "simple_without_icms_value": simple_without_icms_value,
633
- }
634
-
635
- def _prepare_fields_icmsst(self, tax_dict):
636
- self.ensure_one()
637
- return {
638
- "icmsst_base_type": tax_dict.get(
639
- "icmsst_base_type", ICMS_ST_BASE_TYPE_DEFAULT
640
- ),
641
- "icmsst_mva_percent": tax_dict.get("icmsst_mva_percent"),
642
- "icmsst_percent": tax_dict.get("percent_amount"),
643
- "icmsst_reduction": tax_dict.get("percent_reduction"),
644
- "icmsst_base": tax_dict.get("base"),
645
- "icmsst_value": tax_dict.get("tax_value"),
646
- }
647
-
648
- def _prepare_fields_icmsfcp(self, tax_dict):
649
- self.ensure_one()
650
- return {
651
- "icmsfcp_base": tax_dict.get("base", 0.0),
652
- "icmsfcp_percent": tax_dict.get("percent_amount", 0.0),
653
- "icmsfcp_value": tax_dict.get("tax_value", 0.0),
654
- }
655
-
656
- def _prepare_fields_icmsfcpst(self, tax_dict):
657
- self.ensure_one()
658
- return {
659
- "icmsfcpst_base": self.icmsst_base,
660
- "icmsfcpst_percent": tax_dict.get("percent_amount", 0.0),
661
- "icmsfcpst_value": tax_dict.get("tax_value", 0.0),
662
- }
663
-
664
- def _prepare_fields_ipi(self, tax_dict):
665
- self.ensure_one()
666
- cst_id = tax_dict.get("cst_id").id if tax_dict.get("cst_id") else False
667
- return {
668
- "ipi_cst_id": cst_id,
669
- "ipi_base_type": tax_dict.get("base_type", False),
670
- "ipi_base": tax_dict.get("base", 0.00),
671
- "ipi_percent": tax_dict.get("percent_amount", 0.00),
672
- "ipi_reduction": tax_dict.get("percent_reduction", 0.00),
673
- "ipi_value": tax_dict.get("tax_value", 0.00),
674
- }
675
-
676
- def _prepare_fields_ii(self, tax_dict):
677
- self.ensure_one()
678
- return {
679
- "ii_base": tax_dict.get("base", 0.00),
680
- "ii_percent": tax_dict.get("percent_amount", 0.00),
681
- "ii_value": tax_dict.get("tax_value", 0.00),
682
- }
683
-
684
- def _prepare_fields_pis(self, tax_dict):
685
- self.ensure_one()
686
- cst_id = tax_dict.get("cst_id").id if tax_dict.get("cst_id") else False
687
- return {
688
- "pis_cst_id": cst_id,
689
- "pis_base_type": tax_dict.get("base_type"),
690
- "pis_base": tax_dict.get("base", 0.00),
691
- "pis_percent": tax_dict.get("percent_amount", 0.00),
692
- "pis_reduction": tax_dict.get("percent_reduction", 0.00),
693
- "pis_value": tax_dict.get("tax_value", 0.00),
694
- }
695
-
696
- def _prepare_fields_pis_wh(self, tax_dict):
697
- self.ensure_one()
698
- return {
699
- "pis_wh_base_type": tax_dict.get("base_type"),
700
- "pis_wh_base": tax_dict.get("base", 0.00),
701
- "pis_wh_percent": tax_dict.get("percent_amount", 0.00),
702
- "pis_wh_reduction": tax_dict.get("percent_reduction", 0.00),
703
- "pis_wh_value": tax_dict.get("tax_value", 0.00),
704
- }
705
-
706
- def _prepare_fields_pisst(self, tax_dict):
707
- self.ensure_one()
708
- cst_id = tax_dict.get("cst_id").id if tax_dict.get("cst_id") else False
709
- return {
710
- "pisst_cst_id": cst_id,
711
- "pisst_base_type": tax_dict.get("base_type"),
712
- "pisst_base": tax_dict.get("base", 0.00),
713
- "pisst_percent": tax_dict.get("percent_amount", 0.00),
714
- "pisst_reduction": tax_dict.get("percent_reduction", 0.00),
715
- "pisst_value": tax_dict.get("tax_value", 0.00),
716
- }
717
-
718
- def _prepare_fields_cofins(self, tax_dict):
719
- self.ensure_one()
720
- cst_id = tax_dict.get("cst_id").id if tax_dict.get("cst_id") else False
721
- return {
722
- "cofins_cst_id": cst_id,
723
- "cofins_base_type": tax_dict.get("base_type"),
724
- "cofins_base": tax_dict.get("base", 0.00),
725
- "cofins_percent": tax_dict.get("percent_amount", 0.00),
726
- "cofins_reduction": tax_dict.get("percent_reduction", 0.00),
727
- "cofins_value": tax_dict.get("tax_value", 0.00),
728
- }
729
-
730
- def _prepare_fields_cofins_wh(self, tax_dict):
731
- self.ensure_one()
732
- return {
733
- "cofins_wh_base_type": tax_dict.get("base_type"),
734
- "cofins_wh_base": tax_dict.get("base", 0.00),
735
- "cofins_wh_percent": tax_dict.get("percent_amount", 0.00),
736
- "cofins_wh_reduction": tax_dict.get("percent_reduction", 0.00),
737
- "cofins_wh_value": tax_dict.get("tax_value", 0.00),
738
- }
739
-
740
- def _prepare_fields_cofinsst(self, tax_dict):
741
- self.ensure_one()
742
- cst_id = tax_dict.get("cst_id").id if tax_dict.get("cst_id") else False
743
- return {
744
- "cofinsst_cst_id": cst_id,
745
- "cofinsst_base_type": tax_dict.get("base_type"),
746
- "cofinsst_base": tax_dict.get("base", 0.00),
747
- "cofinsst_percent": tax_dict.get("percent_amount", 0.00),
748
- "cofinsst_reduction": tax_dict.get("percent_reduction", 0.00),
749
- "cofinsst_value": tax_dict.get("tax_value", 0.00),
750
- }
751
-
752
- @api.onchange(*FISCAL_TAX_ID_FIELDS)
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")
766
- def _compute_uot_id(self):
767
- for line in self:
768
- if not line.uot_id:
769
- line.uot_id = line.uom_id
770
-
771
- @api.onchange("price_unit")
772
- def _onchange_price_unit_fiscal(self):
773
- self.fiscal_price = 0
774
- self._compute_fiscal_price()
775
-
776
- @api.depends("price_unit")
777
- def _compute_fiscal_price(self):
778
- for line in self:
779
- # this test and the onchange are required to avoid
780
- # resetting manual changes in fiscal_price
781
- if not line.fiscal_price:
782
- if line.product_id and line.price_unit:
783
- line.fiscal_price = line.price_unit / (
784
- line.product_id.uot_factor or 1.0
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()
793
-
794
- @api.depends("quantity")
795
- def _compute_fiscal_quantity(self):
796
- for line in self:
797
- # this test and the onchange are required to avoid
798
- # resetting manual changes in fiscal_quantity
799
- if not line.fiscal_quantity:
800
- if line.product_id and line.quantity:
801
- line.fiscal_quantity = line.quantity * (
802
- line.product_id.uot_factor or 1.0
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})
814
-
815
- @api.model
816
- def _add_fields_to_amount(self):
817
- fields_to_amount = ["insurance_value", "other_value", "freight_value"]
818
- if (
819
- self.cfop_id.destination == CFOP_DESTINATION_EXPORT
820
- and self.fiscal_operation_id.fiscal_operation_type == FISCAL_IN
821
- ):
822
- fields_to_amount.append("pis_value")
823
- fields_to_amount.append("cofins_value")
824
- fields_to_amount.append("icms_value")
825
- fields_to_amount.append("ii_value")
826
- fields_to_amount.append("ii_customhouse_charges")
827
- return fields_to_amount
828
-
829
- @api.model
830
- def _rm_fields_to_amount(self):
831
- return ["icms_relief_value"]
832
-
833
- def _is_imported(self):
834
- # When the mixin is used for instance
835
- # in a PO line or SO line, there is no document_id
836
- # and we consider the document is not imported
837
- return hasattr(self, "document_id") and self.document_id.imported_document