From e8df398cbbce21c91179d157a182dd4d2658b5fd Mon Sep 17 00:00:00 2001 From: Adrien Widart Date: Fri, 24 Sep 2021 11:26:30 +0000 Subject: [PATCH] [FIX] stock_landed_costs: round using the currency rounding In some cases, it is impossible to validate some landed costs ("Cost and adjustments lines do not match.[...]") To reproduce the issue: (Enable debug mode) 1. Set the Decimal Accuracy of Product Price to 4 2. Create a Product Category PC: - Costing Method: FIFO 3. Create 4 storable products (category PC) 4. Create a service product (landed cost) 5. Create a PO with the 4 products: - 6 x $0.92 - 6 x $0.92 - 3 x $75.17 - 6 x $20.54 6. Process the delivery D 7. Create a Landed Cost: - Delivery: D - Product: the service product - Split Method: By Quantity - Cost: $1000 8. Compute the landed costs + Validate Error: a User Error is raised "Cost and adjustments lines do not match. You should maybe recompute the landed costs.". The sum of the additional landed cost equals $999.99 instead of $1000 From version 14, `additional_landed_cost` is a Monetary field and its rounding property is no more based on "Product Price": https://github.com/odoo/odoo/blob/800433bde03a36ead40a78bc6d53cbb95c24f97b/addons/stock_landed_costs/models/stock_landed_cost.py#L371-L372 Therefore, we should use the rounding of the associated currency to round its value OPW-2637701 OPW-2631718 OPW-2649348 closes odoo/odoo#77445 X-original-commit: c2750517428be83eb278894932e57e0a9a78fa6d Signed-off-by: William Henrotin --- .../models/stock_landed_cost.py | 8 +-- .../tests/test_stock_landed_costs_rounding.py | 52 ++++++++++++++++++- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/addons/stock_landed_costs/models/stock_landed_cost.py b/addons/stock_landed_costs/models/stock_landed_cost.py index 563b8cf0239..a125f85fdff 100644 --- a/addons/stock_landed_costs/models/stock_landed_cost.py +++ b/addons/stock_landed_costs/models/stock_landed_cost.py @@ -205,9 +205,9 @@ class StockLandedCost(models.Model): AdjustementLines = self.env['stock.valuation.adjustment.lines'] AdjustementLines.search([('cost_id', 'in', self.ids)]).unlink() - digits = self.env['decimal.precision'].precision_get('Product Price') towrite_dict = {} for cost in self.filtered(lambda cost: cost._get_targeted_move_ids()): + rounding = cost.currency_id.rounding total_qty = 0.0 total_cost = 0.0 total_weight = 0.0 @@ -224,7 +224,7 @@ class StockLandedCost(models.Model): former_cost = val_line_values.get('former_cost', 0.0) # round this because former_cost on the valuation lines is also rounded - total_cost += tools.float_round(former_cost, precision_digits=digits) if digits else former_cost + total_cost += cost.currency_id.round(former_cost) total_line += 1 @@ -250,8 +250,8 @@ class StockLandedCost(models.Model): else: value = (line.price_unit / total_line) - if digits: - value = tools.float_round(value, precision_digits=digits, rounding_method='UP') + if rounding: + value = tools.float_round(value, precision_rounding=rounding, rounding_method='UP') fnc = min if line.price_unit > 0 else max value = fnc(value, line.price_unit - value_split) value_split += value diff --git a/addons/stock_landed_costs/tests/test_stock_landed_costs_rounding.py b/addons/stock_landed_costs/tests/test_stock_landed_costs_rounding.py index 1ec148c2c3a..9ced6d0976c 100644 --- a/addons/stock_landed_costs/tests/test_stock_landed_costs_rounding.py +++ b/addons/stock_landed_costs/tests/test_stock_landed_costs_rounding.py @@ -2,7 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo.addons.stock_landed_costs.tests.common import TestStockLandedCostsCommon -from odoo.tests import tagged +from odoo.tests import tagged, Form @tagged('post_install', '-at_install') @@ -152,3 +152,53 @@ class TestStockLandedCostsRounding(TestStockLandedCostsCommon): # I check that the landed cost is now "Closed" and that it has an accounting entry self.assertEqual(stock_landed_cost_3.state, 'done') self.assertTrue(stock_landed_cost_3.account_move_id) + + def test_stock_landed_costs_rounding_02(self): + """ The landed costs should be correctly computed, even when the decimal accuracy + of the deciaml price is increased. """ + self.env.ref("product.decimal_price").digits = 4 + + fifo_pc = self.env['product.category'].create({ + 'name': 'Fifo Category', + 'parent_id': self.env.ref("product.product_category_all").id, + 'property_valuation': 'real_time', + 'property_cost_method': 'fifo', + }) + + products = self.Product.create([{ + 'name': 'Super Product %s' % price, + 'categ_id': fifo_pc.id, + 'type': 'product', + 'standard_price': price, + } for price in [0.91, 0.93, 75.17, 20.54]]) + + landed_product = self.Product.create({ + 'name': 'Landed Costs', + 'type': 'service', + 'landed_cost_ok': True, + 'split_method_landed_cost': 'by_quantity', + 'standard_price': 1000.0, + }) + + po = self.env['purchase.order'].create({ + 'partner_id': self.partner_a.id, + 'order_line': [(0, 0, { + 'product_id': product.id, + 'product_qty': qty, + 'price_unit': product.standard_price, + }) for product, qty in zip(products, [6, 6, 3, 6])] + }) + po.button_confirm() + + res_dict = po.picking_ids.button_validate() + validate_wizard = Form(self.env[(res_dict.get('res_model'))].with_context(res_dict.get('context'))).save() + validate_wizard.process() + + lc_form = Form(self.LandedCost) + lc_form.picking_ids.add(po.picking_ids) + with lc_form.cost_lines.new() as line: + line.product_id = landed_product + lc = lc_form.save() + lc.compute_landed_cost() + + self.assertEqual(sum(lc.valuation_adjustment_lines.mapped('additional_landed_cost')), 1000.0)