[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:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user