[FIX] purchase_mrp: calculate valuation unit_cost correctly

commit that introduced the issue : https://github.com/odoo/odoo/commit/8e516dccac4ced7e48adfabe756a899784bac9ca

Issue:
======
valuation unit cost is wrong when we do backorder with real time unit
price computation

Steps to reproduce the issue:
=============================
- Create a kit with 2 components with product quntity to produce =  3
- Put qty = 2 for the first component and qty = 1 for the second component
- Assign product category to the kit product and the components as fifo
  one with automatec price computation
- Create a purchase order with 30 quantity of the kit and price unit =
  90
- confirm order and go to receipt
- Confirm 4 qty for the first component and 2 qty for the second.
- Create backorder
- Go to the confirmed receipt and go to valuation
- You will see that the total sum corresponds to the price of all the
  products and not only the confirmed ones.

Solution:
=========
We need to use the bom quantities and not the order line to get the
unit_cost of each component.

opw-3790132

closes odoo/odoo#158788

X-original-commit: a72232694f68a810986e5c1c24fd0394d058af41
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
Signed-off-by: Mahdi Cheikh Rouhou (macr) <macr@odoo.com>
This commit is contained in:
Mahdi Cheikh Rouhou (macr)
2024-03-22 14:48:02 +00:00
parent f3c534cc90
commit 85ac6cb2fe
2 changed files with 64 additions and 1 deletions
+13 -1
View File
@@ -19,12 +19,24 @@ class StockMove(models.Model):
if self.product_id == self.purchase_line_id.product_id or not self.bom_line_id or self._should_ignore_pol_price():
return super()._get_price_unit()
line = self.purchase_line_id
# price_unit here with uom of product
kit_price_unit = line._get_gross_price_unit()
bom_line = self.bom_line_id
bom = bom_line.bom_id
if line.currency_id != self.company_id.currency_id:
kit_price_unit = line.currency_id._convert(kit_price_unit, self.company_id.currency_id, self.company_id, fields.Date.context_today(self), round=False)
cost_share = self.bom_line_id._get_cost_share()
price_unit_prec = self.env['decimal.precision'].precision_get('Product Price')
return float_round(kit_price_unit * cost_share * line.product_qty / self.product_qty, precision_digits=price_unit_prec)
uom_factor = 1.0
kit_product = bom.product_id or bom.product_tmpl_id
# Convert uom from product_uom to bom_uom for kit product
uom_factor = bom.product_uom_id._compute_quantity(uom_factor, kit_product.uom_id)
# Convert uom from bom_line_uom to product_uom for bom_line
uom_factor = bom_line.product_id.uom_id._compute_quantity(uom_factor, bom_line.product_uom_id)
return float_round(kit_price_unit * cost_share * uom_factor * bom.product_qty / bom_line.product_qty, precision_digits=price_unit_prec)
def _get_valuation_price_and_qty(self, related_aml, to_curr):
valuation_price_unit_total, valuation_total_qty = super()._get_valuation_price_and_qty(related_aml, to_curr)
@@ -931,3 +931,54 @@ class TestPurchaseMrpFlow(AccountTestInvoicingCommon):
report_values = self.env['report.mrp.report_bom_structure']._get_report_data(bom_id=bom.id)
line_values = report_values['lines']['components'][0]
self.assertEqual(line_values['availability_state'], 'expected', 'The first component should be expected as there is an incoming PO.')
def test_valuation_with_backorder(self):
fifo_category = self.env['product.category'].create({
'name': 'FIFO',
'property_cost_method': 'fifo',
'property_valuation': 'real_time'
})
kit, cmp1, cmp2 = self.env['product.product'].create([{
'name': name,
'standard_price': 0,
'type': 'product',
'categ_id': fifo_category.id,
} for name in ['Kit', 'Cmp1', 'Cmp2']])
kit.uom_id = self.uom_gm.id
cmp1.uom_id = self.uom_gm.id
cmp2.uom_id = self.uom_kg.id
self.env['mrp.bom'].create({
'product_uom_id': self.uom_kg.id,
'product_qty': 3,
'product_tmpl_id': kit.product_tmpl_id.id,
'type': 'phantom',
'bom_line_ids': [
(0, 0, {'product_id': cmp1.id, 'product_qty': 2, 'product_uom_id': self.uom_kg.id}),
(0, 0, {'product_id': cmp2.id, 'product_qty': 1, 'product_uom_id': self.uom_gm.id})]
})
po_form = Form(self.env['purchase.order'])
partner = self.env['res.partner'].create({'name': 'My Test Partner'})
po_form.partner_id = partner
with po_form.order_line.new() as pol_form:
pol_form.product_id = kit
pol_form.product_qty = 30
pol_form.product_uom = self.uom_kg
pol_form.price_unit = 90000
pol_form.taxes_id.clear()
po = po_form.save()
po.button_confirm()
receipt = po.picking_ids
receipt.move_line_ids[0].qty_done = 4
receipt.move_line_ids[1].qty_done = 2
action = receipt.button_validate()
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
# Price Unit for 1 gm of the kit = 90000/1000 = 90
# unit_cost for cmp1 = 90 *1000* 3 / 2 / 2 / 1000 = 67.5
# unit_cost for cmp2 = 90 *1000* 3 / 2 / 1 * 1000 = 135000000
svl = po.picking_ids[0].move_ids.stock_valuation_layer_ids
self.assertEqual(svl[0].unit_cost, 67.5)
self.assertEqual(svl[1].unit_cost, 135000000)