odoo-addon-l10n-br-fiscal 17.0.2.1.0__py3-none-any.whl → 17.0.3.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 (26) hide show
  1. odoo/addons/l10n_br_fiscal/README.rst +1 -1
  2. odoo/addons/l10n_br_fiscal/__manifest__.py +1 -1
  3. odoo/addons/l10n_br_fiscal/demo/fiscal_document_demo.xml +2 -90
  4. odoo/addons/l10n_br_fiscal/demo/fiscal_document_nfse_demo.xml +0 -4
  5. odoo/addons/l10n_br_fiscal/demo/fiscal_operation_demo.xml +2 -2
  6. odoo/addons/l10n_br_fiscal/i18n/l10n_br_fiscal.pot +24 -24
  7. odoo/addons/l10n_br_fiscal/migrations/17.0.3.0.0/pre-migration.py +30 -0
  8. odoo/addons/l10n_br_fiscal/models/document.py +1 -6
  9. odoo/addons/l10n_br_fiscal/models/document_line.py +35 -5
  10. odoo/addons/l10n_br_fiscal/models/document_line_mixin.py +63 -12
  11. odoo/addons/l10n_br_fiscal/models/document_line_mixin_methods.py +74 -52
  12. odoo/addons/l10n_br_fiscal/models/document_mixin.py +3 -3
  13. odoo/addons/l10n_br_fiscal/models/document_mixin_methods.py +28 -142
  14. odoo/addons/l10n_br_fiscal/models/document_related.py +1 -1
  15. odoo/addons/l10n_br_fiscal/security/ir.model.access.csv +1 -1
  16. odoo/addons/l10n_br_fiscal/static/description/index.html +1 -1
  17. odoo/addons/l10n_br_fiscal/tests/test_document_edition.py +113 -0
  18. odoo/addons/l10n_br_fiscal/tests/test_fiscal_document_generic.py +0 -11
  19. odoo/addons/l10n_br_fiscal/tests/test_fiscal_document_nfse.py +0 -1
  20. odoo/addons/l10n_br_fiscal/tests/test_tax_benefit.py +0 -1
  21. odoo/addons/l10n_br_fiscal/views/document_line_view.xml +3 -3
  22. odoo/addons/l10n_br_fiscal/views/document_view.xml +6 -6
  23. {odoo_addon_l10n_br_fiscal-17.0.2.1.0.dist-info → odoo_addon_l10n_br_fiscal-17.0.3.1.0.dist-info}/METADATA +2 -2
  24. {odoo_addon_l10n_br_fiscal-17.0.2.1.0.dist-info → odoo_addon_l10n_br_fiscal-17.0.3.1.0.dist-info}/RECORD +26 -25
  25. {odoo_addon_l10n_br_fiscal-17.0.2.1.0.dist-info → odoo_addon_l10n_br_fiscal-17.0.3.1.0.dist-info}/WHEEL +0 -0
  26. {odoo_addon_l10n_br_fiscal-17.0.2.1.0.dist-info → odoo_addon_l10n_br_fiscal-17.0.3.1.0.dist-info}/top_level.txt +0 -0
@@ -7,8 +7,12 @@ from lxml import etree
7
7
 
8
8
  from odoo import Command, api, models
9
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
10
+ from ..constants.fiscal import CFOP_DESTINATION_EXPORT, FISCAL_IN, TAX_DOMAIN_ICMS
11
+ from ..constants.icms import (
12
+ ICMS_BASE_TYPE_DEFAULT,
13
+ ICMS_ORIGIN_DEFAULT,
14
+ ICMS_ST_BASE_TYPE_DEFAULT,
15
+ )
12
16
 
13
17
  FISCAL_TAX_ID_FIELDS = [
14
18
  "cofins_tax_id",
@@ -171,11 +175,11 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
171
175
  # Total value of products or services
172
176
  record.price_gross = round_curr.round(record.price_unit * record.quantity)
173
177
  record.amount_fiscal = record.price_gross - record.discount_value
174
- record.amount_tax = record.amount_tax_not_included
178
+ record.fiscal_amount_tax = record.amount_tax_not_included
175
179
 
176
180
  add_to_amount = sum(record[a] for a in record._add_fields_to_amount())
177
181
  rm_to_amount = sum(record[r] for r in record._rm_fields_to_amount())
178
- record.amount_untaxed = (
182
+ record.fiscal_amount_untaxed = (
179
183
  record.price_gross
180
184
  - record.discount_value
181
185
  + add_to_amount
@@ -183,13 +187,17 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
183
187
  )
184
188
 
185
189
  # Valor do documento (NF)
186
- record.amount_total = record.amount_untaxed + record.amount_tax
190
+ record.fiscal_amount_total = (
191
+ record.fiscal_amount_untaxed + record.fiscal_amount_tax
192
+ )
187
193
 
188
194
  # Valor Liquido (TOTAL + IMPOSTOS - RETENÇÕES)
189
- record.amount_taxed = record.amount_total - record.amount_tax_withholding
195
+ record.amount_taxed = (
196
+ record.fiscal_amount_total - record.amount_tax_withholding
197
+ )
190
198
 
191
199
  # Valor do documento (NF) - RETENÇÕES
192
- record.amount_total = record.amount_taxed
200
+ record.fiscal_amount_total = record.amount_taxed
193
201
 
194
202
  # Valor financeiro
195
203
  if (
@@ -305,11 +313,16 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
305
313
  for tax in mapping_result["taxes"].values():
306
314
  taxes |= tax
307
315
  line.fiscal_tax_ids = taxes
308
- line.comment_ids = line.fiscal_operation_line_id.comment_ids
309
-
310
316
  else:
311
317
  line.fiscal_tax_ids = [Command.clear()]
312
318
 
319
+ @api.depends("fiscal_operation_line_id")
320
+ def _compute_comment_ids(self):
321
+ for line in self:
322
+ line.comment_ids = [
323
+ Command.set(line.fiscal_operation_line_id.comment_ids.ids)
324
+ ]
325
+
313
326
  @api.model
314
327
  def _build_null_mask_dict(self) -> dict:
315
328
  """
@@ -465,48 +478,50 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
465
478
  self.ensure_one()
466
479
  return self.partner_id
467
480
 
468
- @api.onchange("product_id")
469
- def _onchange_product_id_fiscal(self):
470
- if not self.fiscal_operation_id:
481
+ @api.depends("product_id")
482
+ def _compute_product_fiscal_fields(self):
483
+ if self._context.get("skip_compute_product_fiscal_fields"):
471
484
  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
485
+ for line in self:
486
+ if not line.product_id:
487
+ # reset to default values:
488
+ line.fiscal_type = False
489
+ line.ncm_id = False
490
+ line.nbm_id = False
491
+ line.tax_icms_or_issqn = TAX_DOMAIN_ICMS
492
+ line.icms_origin = ICMS_ORIGIN_DEFAULT
493
+ line.cest_id = False
494
+ line.nbs_id = False
495
+ line.fiscal_genre_id = False
496
+ line.service_type_id = False
497
+ line.city_taxation_code_id = False
498
+ line.issqn_fg_city_id = False
499
+ continue
500
+
501
+ product = line.product_id
502
+ line.fiscal_type = product.fiscal_type
503
+ line.ncm_id = product.ncm_id
504
+ line.nbm_id = product.nbm_id
505
+ line.tax_icms_or_issqn = product.tax_icms_or_issqn
506
+ line.icms_origin = product.icms_origin
507
+ line.cest_id = product.cest_id
508
+ line.nbs_id = product.nbs_id
509
+ line.fiscal_genre_id = product.fiscal_genre_id
510
+ line.service_type_id = product.service_type_id
511
+ if product.city_taxation_code_ids and line.company_id:
512
+ city = product.city_taxation_code_ids.filtered(
513
+ lambda r, current_line=line: r.city_id
514
+ == current_line.company_id.city_id
489
515
  )
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()
516
+ if city:
517
+ line.city_taxation_code_id = city
518
+ line.issqn_fg_city_id = line.company_id.city_id
519
+ else:
520
+ line.city_taxation_code_id = False
521
+ line.issqn_fg_city_id = False
522
+ else:
523
+ line.city_taxation_code_id = False
524
+ line.issqn_fg_city_id = False
510
525
 
511
526
  def _prepare_fields_issqn(self, tax_dict):
512
527
  self.ensure_one()
@@ -762,11 +777,18 @@ class FiscalDocumentLineMixinMethods(models.AbstractModel):
762
777
  )
763
778
  line.fiscal_tax_ids = fiscal_taxes + taxes
764
779
 
765
- @api.depends("uom_id")
780
+ @api.depends("product_id", "uom_id")
766
781
  def _compute_uot_id(self):
767
782
  for line in self:
768
- if not line.uot_id:
769
- line.uot_id = line.uom_id
783
+ if line.uom_id:
784
+ line.uot_id = line.uom_id.id
785
+ elif line.product_id:
786
+ if line.product_id.uot_id:
787
+ line.uot_id = line.product_id.uot_id.id
788
+ else:
789
+ line.uot_id = line.product_id.uom_id.id
790
+ else:
791
+ line.uot_id = False
770
792
 
771
793
  @api.onchange("price_unit")
772
794
  def _onchange_price_unit_fiscal(self):
@@ -126,7 +126,7 @@ class FiscalDocumentMixin(models.AbstractModel):
126
126
  help="Amount without discount.",
127
127
  )
128
128
 
129
- amount_untaxed = fields.Monetary(
129
+ fiscal_amount_untaxed = fields.Monetary(
130
130
  compute="_compute_fiscal_amount",
131
131
  store=True,
132
132
  )
@@ -394,12 +394,12 @@ class FiscalDocumentMixin(models.AbstractModel):
394
394
  store=True,
395
395
  )
396
396
 
397
- amount_tax = fields.Monetary(
397
+ fiscal_amount_tax = fields.Monetary(
398
398
  compute="_compute_fiscal_amount",
399
399
  store=True,
400
400
  )
401
401
 
402
- amount_total = fields.Monetary(
402
+ fiscal_amount_total = fields.Monetary(
403
403
  compute="_compute_fiscal_amount",
404
404
  store=True,
405
405
  )
@@ -73,7 +73,8 @@ class FiscalDocumentMixinMethods(models.AbstractModel):
73
73
  def _get_amount_fields(self):
74
74
  """Get all fields with 'amount_' prefix"""
75
75
  fields = self.env["l10n_br_fiscal.document.mixin"]._fields.keys()
76
- amount_fields = [f for f in fields if f.startswith("amount_")]
76
+ prefixes = ("amount_", "fiscal_amount_")
77
+ amount_fields = [f for f in fields if f.startswith(prefixes)]
77
78
  return amount_fields
78
79
 
79
80
  @api.depends("document_serie_id", "issuer")
@@ -198,152 +199,37 @@ class FiscalDocumentMixinMethods(models.AbstractModel):
198
199
  elif doc.comment_ids is None:
199
200
  doc.comment_ids = []
200
201
 
201
- def _inverse_amount_freight(self):
202
- for record in self.filtered(lambda doc: doc._get_product_amount_lines()):
203
- if (
202
+ def _distribute_amount_to_lines(self, amount_field_name, line_field_name):
203
+ for record in self:
204
+ if not (
204
205
  record.delivery_costs == "total"
205
206
  or record.force_compute_delivery_costs_by_total
206
207
  ):
207
- amount_freight_value = record.amount_freight_value
208
- if all(record._get_product_amount_lines().mapped("freight_value")):
209
- amount_freight_old = sum(
210
- record._get_product_amount_lines().mapped("freight_value")
211
- )
212
- for line in record._get_product_amount_lines()[:-1]:
213
- line.freight_value = amount_freight_value * (
214
- line.freight_value / amount_freight_old
215
- )
216
- record._get_product_amount_lines()[-1].freight_value = (
217
- amount_freight_value
218
- - sum(
219
- line.freight_value
220
- for line in record._get_product_amount_lines()[:-1]
221
- )
222
- )
223
- else:
224
- amount_total = sum(
225
- record._get_product_amount_lines().mapped("price_gross")
208
+ continue
209
+ lines = record._get_product_amount_lines()
210
+ if not lines:
211
+ continue
212
+ amount_to_distribute = record[amount_field_name]
213
+ total_gross = sum(lines.mapped("price_gross"))
214
+ if total_gross > 0:
215
+ distributed_amount = 0
216
+ for line in lines[:-1]:
217
+ proportional_amount = record.currency_id.round(
218
+ amount_to_distribute * (line.price_gross / total_gross)
226
219
  )
227
- for line in record._get_product_amount_lines()[:-1]:
228
- if line.price_gross and amount_total:
229
- line.freight_value = amount_freight_value * (
230
- line.price_gross / amount_total
231
- )
232
- record._get_product_amount_lines()[-1].freight_value = (
233
- amount_freight_value
234
- - sum(
235
- line.freight_value
236
- for line in record._get_product_amount_lines()[:-1]
237
- )
238
- )
239
- for line in record._get_product_amount_lines():
240
- line._onchange_fiscal_taxes()
241
- record._fields["amount_total"].compute_value(record)
242
- record.write(
243
- {
244
- name: value
245
- for name, value in record._cache.items()
246
- if record._fields[name].compute == "_amount_all"
247
- and not record._fields[name].inverse
248
- }
249
- )
220
+ line[line_field_name] = proportional_amount
221
+ distributed_amount += proportional_amount
222
+ lines[-1][line_field_name] = amount_to_distribute - distributed_amount
223
+ else:
224
+ lines.write({line_field_name: 0.0})
225
+ if lines:
226
+ lines[0][line_field_name] = amount_to_distribute
227
+
228
+ def _inverse_amount_freight(self):
229
+ self._distribute_amount_to_lines("amount_freight_value", "freight_value")
250
230
 
251
231
  def _inverse_amount_insurance(self):
252
- for record in self.filtered(lambda doc: doc._get_product_amount_lines()):
253
- if (
254
- record.delivery_costs == "total"
255
- or record.force_compute_delivery_costs_by_total
256
- ):
257
- amount_insurance_value = record.amount_insurance_value
258
- if all(record._get_product_amount_lines().mapped("insurance_value")):
259
- amount_insurance_old = sum(
260
- record._get_product_amount_lines().mapped("insurance_value")
261
- )
262
- for line in record._get_product_amount_lines()[:-1]:
263
- line.insurance_value = amount_insurance_value * (
264
- line.insurance_value / amount_insurance_old
265
- )
266
- record._get_product_amount_lines()[-1].insurance_value = (
267
- amount_insurance_value
268
- - sum(
269
- line.insurance_value
270
- for line in record._get_product_amount_lines()[:-1]
271
- )
272
- )
273
- else:
274
- amount_total = sum(
275
- record._get_product_amount_lines().mapped("price_gross")
276
- )
277
- for line in record._get_product_amount_lines()[:-1]:
278
- if line.price_gross and amount_total:
279
- line.insurance_value = amount_insurance_value * (
280
- line.price_gross / amount_total
281
- )
282
- record._get_product_amount_lines()[-1].insurance_value = (
283
- amount_insurance_value
284
- - sum(
285
- line.insurance_value
286
- for line in record._get_product_amount_lines()[:-1]
287
- )
288
- )
289
- for line in record._get_product_amount_lines():
290
- line._onchange_fiscal_taxes()
291
- record._fields["amount_total"].compute_value(record)
292
- record.write(
293
- {
294
- name: value
295
- for name, value in record._cache.items()
296
- if record._fields[name].compute == "_amount_all"
297
- and not record._fields[name].inverse
298
- }
299
- )
232
+ self._distribute_amount_to_lines("amount_insurance_value", "insurance_value")
300
233
 
301
234
  def _inverse_amount_other(self):
302
- for record in self.filtered(lambda doc: doc._get_product_amount_lines()):
303
- if (
304
- record.delivery_costs == "total"
305
- or record.force_compute_delivery_costs_by_total
306
- ):
307
- amount_other_value = record.amount_other_value
308
- if all(record._get_product_amount_lines().mapped("other_value")):
309
- amount_other_old = sum(
310
- record._get_product_amount_lines().mapped("other_value")
311
- )
312
- for line in record._get_product_amount_lines()[:-1]:
313
- line.other_value = amount_other_value * (
314
- line.other_value / amount_other_old
315
- )
316
- record._get_product_amount_lines()[-1].other_value = (
317
- amount_other_value
318
- - sum(
319
- line.other_value
320
- for line in record._get_product_amount_lines()[:-1]
321
- )
322
- )
323
- else:
324
- amount_total = sum(
325
- record._get_product_amount_lines().mapped("price_gross")
326
- )
327
- for line in record._get_product_amount_lines()[:-1]:
328
- if line.price_gross and amount_total:
329
- line.other_value = amount_other_value * (
330
- line.price_gross / amount_total
331
- )
332
- record._get_product_amount_lines()[-1].other_value = (
333
- amount_other_value
334
- - sum(
335
- line.other_value
336
- for line in record._get_product_amount_lines()[:-1]
337
- )
338
- )
339
- for line in record._get_product_amount_lines():
340
- line._onchange_fiscal_taxes()
341
- record._fields["amount_total"].compute_value(record)
342
- record.write(
343
- {
344
- name: value
345
- for name, value in record._cache.items()
346
- if record._fields[name].compute == "_amount_all"
347
- and not record._fields[name].inverse
348
- }
349
- )
235
+ self._distribute_amount_to_lines("amount_other_value", "other_value")
@@ -104,7 +104,7 @@ class DocumentRelated(models.Model):
104
104
  return False
105
105
 
106
106
  self.document_type_id = related.document_type_id
107
- self.document_total_amount = related.amount_total
107
+ self.document_total_amount = related.fiscal_amount_total
108
108
  self.document_total_weight = related.total_weight
109
109
 
110
110
  if related.document_type_id.electronic:
@@ -67,7 +67,7 @@
67
67
  "l10n_br_fiscal_operation_line_manager","Fiscal Operation Line for Manager","model_l10n_br_fiscal_operation_line","l10n_br_fiscal.group_manager",1,1,1,1
68
68
  "l10n_br_fiscal_operation_document_type_user","Fiscal Operation Document Type for User","model_l10n_br_fiscal_operation_document_type","l10n_br_fiscal.group_user",1,0,0,0
69
69
  "l10n_br_fiscal_operation_document_type_manager","Fiscal Operation Document Type for Manager","model_l10n_br_fiscal_operation_document_type","l10n_br_fiscal.group_manager",1,1,1,1
70
- "l10n_br_fiscal_partner_profile_user","Fiscal Partner Profile for User","model_l10n_br_fiscal_partner_profile","l10n_br_fiscal.group_user",1,0,0,0
70
+ "l10n_br_fiscal_partner_profile_user","Fiscal Partner Profile for User","model_l10n_br_fiscal_partner_profile","base.group_user",1,0,0,0
71
71
  "l10n_br_fiscal_partner_profile_manager","Fiscal Partner Profile for Manager","model_l10n_br_fiscal_partner_profile","l10n_br_fiscal.group_manager",1,1,1,1
72
72
  "l10n_br_fiscal_document_user","Fiscal Document for User","model_l10n_br_fiscal_document","l10n_br_fiscal.group_user",1,1,1,0
73
73
  "l10n_br_fiscal_document_manager","Fiscal Document for Manager","model_l10n_br_fiscal_document","l10n_br_fiscal.group_manager",1,1,1,1
@@ -372,7 +372,7 @@ ul.auto-toc {
372
372
  !! This file is generated by oca-gen-addon-readme !!
373
373
  !! changes will be overwritten. !!
374
374
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
375
- !! source digest: sha256:486f72a947ff411ae1ec91ca2ada5d0f563c8421580c7d069ef01f3bbbd9b5f7
375
+ !! source digest: sha256:a94263259afc7260a25f60fd10ada9ac820d816d2f4a157bb688764b76b7fe27
376
376
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
377
377
  <p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/l10n-brazil/tree/17.0/l10n_br_fiscal"><img alt="OCA/l10n-brazil" src="https://img.shields.io/badge/github-OCA%2Fl10n--brazil-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/l10n-brazil-17-0/l10n-brazil-17-0-l10n_br_fiscal"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/l10n-brazil&amp;target_branch=17.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
378
378
  <p><img alt="image" src="https://raw.githubusercontent.com/OCA/l10n-brazil/17.0/l10n_br_fiscal/static/img/fiscal_dashboard.png" /></p>
@@ -12,6 +12,10 @@ class TestDocumentEdition(TransactionCase):
12
12
  def setUpClass(cls):
13
13
  super().setUpClass()
14
14
  cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
15
+ cls.user = cls.env.user
16
+ cls.company = cls.env.ref("l10n_br_base.empresa_lucro_presumido")
17
+ cls.user.company_ids |= cls.company
18
+ cls.user.company_id = cls.company.id
15
19
 
16
20
  def test_basic_doc_edition(self):
17
21
  doc_form = Form(
@@ -141,3 +145,112 @@ class TestDocumentEdition(TransactionCase):
141
145
  self.assertEqual(doc.fiscal_line_ids[0].fiscal_price, 112)
142
146
  self.assertEqual(doc.fiscal_line_ids[0].quantity, 10)
143
147
  self.assertEqual(doc.fiscal_line_ids[0].fiscal_quantity, 5)
148
+
149
+ def test_landed_costs_by_line_and_by_total(self):
150
+ """
151
+ Tests both landed cost scenarios: 'by line' and 'by total'.
152
+ 1. By Line: Enters costs on lines and verifies the header totals.
153
+ 2. By Total: Enters costs on the header and verifies lines distribution.
154
+ """
155
+ self.env.user.groups_id |= self.env.ref("l10n_br_fiscal.group_user")
156
+ product1 = self.env.ref("product.product_product_6")
157
+ product2 = self.env.ref("product.product_product_7")
158
+
159
+ # Part 1: Test with delivery_costs = 'line'
160
+ # ----------------------------------------------------
161
+ self.company.delivery_costs = "line"
162
+ doc_form = Form(self.env["l10n_br_fiscal.document"])
163
+ doc_form.company_id = self.company
164
+ doc_form.partner_id = self.env.ref("l10n_br_base.res_partner_cliente1_sp")
165
+ doc_form.fiscal_operation_id = self.env.ref("l10n_br_fiscal.fo_venda")
166
+
167
+ with doc_form.fiscal_line_ids.new() as line1:
168
+ line1.product_id = product1
169
+ line1.fiscal_operation_line_id = self.env.ref(
170
+ "l10n_br_fiscal.fo_venda_venda"
171
+ )
172
+ line1.price_unit = 1000.0
173
+ line1.quantity = 2.0 # Gross: 2000
174
+ line1.freight_value = 10.0
175
+ line1.insurance_value = 20.0
176
+ line1.other_value = 5.0
177
+
178
+ with doc_form.fiscal_line_ids.new() as line2:
179
+ line2.product_id = product2
180
+ line2.fiscal_operation_line_id = self.env.ref(
181
+ "l10n_br_fiscal.fo_venda_venda"
182
+ )
183
+ line2.price_unit = 500.0
184
+ line2.quantity = 1.0 # Gross: 500
185
+ line2.freight_value = 4.0
186
+ line2.insurance_value = 6.0
187
+ line2.other_value = 2.0
188
+
189
+ doc = doc_form.save()
190
+
191
+ self.assertEqual(doc.company_id.delivery_costs, "line")
192
+ # Assert header totals are the SUM of line values
193
+ self.assertAlmostEqual(doc.amount_freight_value, 14.0) # 10.0 + 4.0
194
+ self.assertAlmostEqual(doc.amount_insurance_value, 26.0) # 20.0 + 6.0
195
+ self.assertAlmostEqual(doc.amount_other_value, 7.0) # 5.0 + 2.0
196
+
197
+ # Assert final fiscal totals (bottom-up calculation)
198
+ # price_gross = (1000*2) + (500*1) = 2500
199
+ # landed_costs = 14 + 26 + 7 = 47
200
+ # fiscal_amount_untaxed (IPI Base) = 2500 + 47 = 2547
201
+ self.assertAlmostEqual(doc.fiscal_amount_untaxed, 2547.00)
202
+ # fiscal_amount_tax (IPI) = (2035 * 3.25%) + (512 * 5%) = 66.14 + 25.60 = 91.74
203
+ self.assertAlmostEqual(doc.fiscal_amount_tax, 91.74, places=2)
204
+ # fiscal_amount_total = 2547.00 + 91.74 = 2638.74
205
+ self.assertAlmostEqual(doc.fiscal_amount_total, 2638.74, places=2)
206
+
207
+ # Part 2: Test with delivery_costs = 'total'
208
+ # ----------------------------------------------------
209
+ self.company.delivery_costs = "total"
210
+ doc_form_edit = Form(doc)
211
+ # Set new header totals, which should trigger inverse methods to distribute
212
+ doc_form_edit.amount_freight_value = 30.0
213
+ doc_form_edit.amount_insurance_value = 60.0
214
+ doc_form_edit.amount_other_value = 90.0
215
+ doc_after_total_update = doc_form_edit.save()
216
+
217
+ line1 = doc_after_total_update.fiscal_line_ids[0]
218
+ line2 = doc_after_total_update.fiscal_line_ids[1]
219
+
220
+ # Assert values were distributed proportionally to price_gross
221
+ # (2000 vs 500 -> 80% vs 20%)
222
+ # Freight: 30.0 * 0.8 = 24.0 | 30.0 * 0.2 = 6.0
223
+ self.assertAlmostEqual(line1.freight_value, 24.0)
224
+ self.assertAlmostEqual(line2.freight_value, 6.0)
225
+ # Insurance: 60.0 * 0.8 = 48.0 | 60.0 * 0.2 = 12.0
226
+ self.assertAlmostEqual(line1.insurance_value, 48.0)
227
+ self.assertAlmostEqual(line2.insurance_value, 12.0)
228
+ # Other: 90.0 * 0.8 = 72.0 | 90.0 * 0.2 = 18.0
229
+ self.assertAlmostEqual(line1.other_value, 72.0)
230
+ self.assertAlmostEqual(line2.other_value, 18.0)
231
+
232
+ # Assert final fiscal totals are recomputed correctly (top-down calculation)
233
+ # price_gross = 2500
234
+ # landed_costs = 30 + 60 + 90 = 180
235
+ # fiscal_amount_untaxed (IPI Base) = 2500 + 180 = 2680
236
+ self.assertAlmostEqual(doc_after_total_update.fiscal_amount_untaxed, 2680.00)
237
+ # Line 1 IPI Base = 2000 (product) + 24 (freight) + 48 (insurance)
238
+ # + 72 (other) = 2144
239
+ # Line 1 IPI Value = 2144 * 3.25% = 69.68
240
+ self.assertAlmostEqual(line1.ipi_base, 2144.00)
241
+ self.assertAlmostEqual(line1.ipi_value, 69.68, places=2)
242
+
243
+ # Line 2 IPI Base = 500 (product) + 6 (freight) + 12 (insurance)
244
+ # + 18 (other) = 536
245
+ # Line 2 IPI Value = 536 * 5% = 26.80
246
+ self.assertAlmostEqual(line2.ipi_base, 536.00)
247
+ self.assertAlmostEqual(line2.ipi_value, 26.80, places=2)
248
+
249
+ # fiscal_amount_tax (IPI) = 69.68 + 26.80 = 96.48
250
+ self.assertAlmostEqual(
251
+ doc_after_total_update.fiscal_amount_tax, 96.48, places=2
252
+ )
253
+ # fiscal_amount_total = 2680.00 + 96.48 = 2776.48
254
+ self.assertAlmostEqual(
255
+ doc_after_total_update.fiscal_amount_total, 2776.48, places=2
256
+ )
@@ -40,8 +40,6 @@ class TestFiscalDocumentGeneric(TransactionCase):
40
40
  def test_nfe_same_state(self):
41
41
  """Test NFe same state."""
42
42
  for line in self.nfe_same_state.fiscal_line_ids:
43
- line._onchange_product_id_fiscal()
44
-
45
43
  # Restore the original price_unit value,
46
44
  # as the product change might have altered it.
47
45
  line.price_unit = 100
@@ -163,7 +161,6 @@ class TestFiscalDocumentGeneric(TransactionCase):
163
161
  def test_nfe_other_state(self):
164
162
  """Test NFe other state."""
165
163
  for line in self.nfe_other_state.fiscal_line_ids:
166
- line._onchange_product_id_fiscal()
167
164
  line._onchange_fiscal_operation_id()
168
165
  line._onchange_fiscal_taxes()
169
166
 
@@ -278,7 +275,6 @@ class TestFiscalDocumentGeneric(TransactionCase):
278
275
  def test_nfe_not_taxpayer(self):
279
276
  """Test NFe not taxpayer."""
280
277
  for line in self.nfe_not_taxpayer.fiscal_line_ids:
281
- line._onchange_product_id_fiscal()
282
278
  line._onchange_fiscal_operation_id()
283
279
  line._onchange_fiscal_taxes()
284
280
 
@@ -380,7 +376,6 @@ class TestFiscalDocumentGeneric(TransactionCase):
380
376
  def test_nfe_not_taxpayer_not_company(self):
381
377
  """Test NFe not taxpayer not Company."""
382
378
  for line in self.nfe_not_taxpayer_pf.fiscal_line_ids:
383
- line._onchange_product_id_fiscal()
384
379
  line._onchange_fiscal_operation_id()
385
380
  line._onchange_fiscal_taxes()
386
381
 
@@ -482,7 +477,6 @@ class TestFiscalDocumentGeneric(TransactionCase):
482
477
  def test_nfe_export(self):
483
478
  """Test NFe export."""
484
479
  for line in self.nfe_export.fiscal_line_ids:
485
- line._onchange_product_id_fiscal()
486
480
  line._onchange_fiscal_operation_id()
487
481
  line._onchange_fiscal_taxes()
488
482
 
@@ -576,8 +570,6 @@ class TestFiscalDocumentGeneric(TransactionCase):
576
570
  def test_nfe_sn_same_state(self):
577
571
  """Test NFe Simples Nacional same state."""
578
572
  for line in self.nfe_sn_same_state.fiscal_line_ids:
579
- line._onchange_product_id_fiscal()
580
-
581
573
  # set fake estimate tax
582
574
  line.ncm_id.tax_estimate_ids.create(
583
575
  {
@@ -689,7 +681,6 @@ class TestFiscalDocumentGeneric(TransactionCase):
689
681
  def test_nfe_sn_other_state(self):
690
682
  """Test NFe SN other state."""
691
683
  for line in self.nfe_sn_other_state.fiscal_line_ids:
692
- line._onchange_product_id_fiscal()
693
684
  line._onchange_fiscal_operation_id()
694
685
  line._onchange_fiscal_taxes()
695
686
 
@@ -787,7 +778,6 @@ class TestFiscalDocumentGeneric(TransactionCase):
787
778
  def test_nfe_sn_not_taxpayer(self):
788
779
  """Test NFe SN not taxpayer."""
789
780
  for line in self.nfe_sn_not_taxpayer.fiscal_line_ids:
790
- line._onchange_product_id_fiscal()
791
781
  line._onchange_fiscal_operation_id()
792
782
  line._onchange_fiscal_taxes()
793
783
 
@@ -872,7 +862,6 @@ class TestFiscalDocumentGeneric(TransactionCase):
872
862
  def test_nfe_sn_export(self):
873
863
  """Test NFe SN export."""
874
864
  for line in self.nfe_sn_export.fiscal_line_ids:
875
- line._onchange_product_id_fiscal()
876
865
  line._onchange_fiscal_operation_id()
877
866
  line._onchange_fiscal_taxes()
878
867
 
@@ -15,7 +15,6 @@ class TestFiscalDocumentNFSe(TransactionCase):
15
15
  """Test NFSe same state."""
16
16
 
17
17
  for line in self.nfse_same_state.fiscal_line_ids:
18
- line._onchange_product_id_fiscal()
19
18
  line._onchange_fiscal_operation_id()
20
19
  line._onchange_fiscal_taxes()
21
20
 
@@ -40,7 +40,6 @@ class TestTaxBenefit(TransactionCase):
40
40
  """Test NFe with tax benefit."""
41
41
 
42
42
  for line in self.nfe_tax_benefit.fiscal_line_ids:
43
- line._onchange_product_id_fiscal()
44
43
  line._onchange_fiscal_operation_id()
45
44
  line._onchange_fiscal_taxes()
46
45
 
@@ -102,13 +102,13 @@
102
102
  <page name="amounts" string="Amounts">
103
103
  <group>
104
104
  <group>
105
- <field name="amount_untaxed" />
105
+ <field name="fiscal_amount_untaxed" />
106
106
  <field name="amount_fiscal" />
107
- <field name="amount_tax" />
107
+ <field name="fiscal_amount_tax" />
108
108
  <field name="estimate_tax" />
109
109
  </group>
110
110
  <group>
111
- <field name="amount_total" />
111
+ <field name="fiscal_amount_total" />
112
112
  <field name="amount_tax_withholding" />
113
113
  <field name="amount_taxed" />
114
114
  </group>