[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 <Whenrow@users.noreply.github.com>
This commit is contained in:
Adrien Widart
2021-09-30 09:14:49 +00:00
parent 029ecd3363
commit e8df398cbb
2 changed files with 55 additions and 5 deletions
@@ -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
@@ -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)