[FIX] mrp_account, sale_mrp: wrong kit cost after so confirmation

Steps to reproduce the bug:
- Create a storable product “B1”:
    - Costing Method: average
    - With BOM:
        - BOM Type: Kit
        - Components:
            - 3 units of “C1” (cost c1 = $2)
- Go to the “B1” product form:
    - Click on “Compute Price from BOM”
    - Cost = 3 * $2 = $6

- Create a storable product A1:
    - Costing Method: average
    - BOM
        - BOM Type: Kit
        - Components:
            - 2 units of B1

- Go to the “A1” product form:
    - Click on “Compute Price from BOM”
    So: 1 unit of A1 → 2 units of B1 → 3 units of C1
    - Cost = (2 * 3 * $2) = $12

- Create a SO:
    - Add 1 unit of “A1”
    - save
    - cost(`purchase_price`) = $12
    - Confirm the SO
    - The cost is recomputed and becomes = $6

Problem:
When the SO is created and the product “A1” is added, the cost is
retrieved from the product: https://github.com/odoo/odoo/blob/14.0/addons/sale_margin/models/sale_order.py#L27

But when the SO is confirmed, a picking is created, therefore the
cost is recomputed: https://github.com/odoo/odoo/blob/14.0/addons/sale_stock_margin/models/sale_order_line.py#L18

So The `_compute_average_price` function is called, in which a loop is
made for each move related to the `sale.order.line`, but the
`bom_line.product_qty` is used for each component (in this case the qty
necessary of the product `C1 ` to make a single unit of `B1`, but this
is not multiplied by the number of units needed to be used in the
parent's bom_line

opw-2971248

closes odoo/odoo#101012

X-original-commit: 9ec7aa9b89bc929af15473d0cc29084fd831cf94
Signed-off-by: Arnold Moyaux (arm) <arm@odoo.com>
Signed-off-by: Djamel Touati (otd) <otd@odoo.com>
This commit is contained in:
Touati Djamel (otd)
2022-09-23 22:04:21 +02:00
parent 8933614b3c
commit e0c5834bfb
2 changed files with 44 additions and 1 deletions
+1 -1
View File
@@ -60,7 +60,7 @@ class ProductProduct(models.Model):
for move in moves_list:
value += move.product_qty * move.product_id._compute_average_price(qty_invoiced * move.product_qty, qty_to_invoice * move.product_qty, move, is_returned=is_returned)
continue
line_qty = bom_line.product_uom_id._compute_quantity(bom_line.product_qty, bom_line.product_id.uom_id)
line_qty = bom_line.product_uom_id._compute_quantity(bom_lines[bom_line]['qty'], bom_line.product_id.uom_id)
moves = self.env['stock.move'].concat(*moves_list)
value += line_qty * bom_line.product_id._compute_average_price(qty_invoiced * line_qty, qty_to_invoice * line_qty, moves, is_returned=is_returned)
return value
@@ -1928,3 +1928,46 @@ class TestSaleMrpFlow(ValuationReconciliationTestCommon):
{'product_id': self.component_f.id, 'location_dest_id': stock_location.id, 'quantity_done': 20, 'state': 'done'},
{'product_id': self.component_g.id, 'location_dest_id': stock_location.id, 'quantity_done': 40, 'state': 'done'},
])
def test_kit_cost_calculation(self):
""" Check that the average cost price is computed correctly after SO confirmation:
BOM 1:
- 1 unit of “super kit”:
- 2 units of “component a”
BOM 2:
- 1 unit of “component a”:
- 3 units of "component b"
1 unit of "component b" = $10
1 unit of "super kit" = 2 * 3 * $10 = *$60
"""
super_kit = self._cls_create_product('Super Kit', self.uom_unit)
(super_kit + self.component_a + self.component_b).categ_id.property_cost_method = 'average'
self.env['mrp.bom'].create({
'product_tmpl_id': self.component_a.product_tmpl_id.id,
'product_qty': 1.0,
'type': 'phantom',
'bom_line_ids': [(0, 0, {
'product_id': self.component_b.id,
'product_qty': 3.0,
})]
})
self.env['mrp.bom'].create({
'product_tmpl_id': super_kit.product_tmpl_id.id,
'product_qty': 1.0,
'type': 'phantom',
'bom_line_ids': [(0, 0, {
'product_id': self.component_a.id,
'product_qty': 2.0,
})]
})
self.component_b.standard_price = 10
self.component_a.button_bom_cost()
super_kit.button_bom_cost()
so_form = Form(self.env['sale.order'])
so_form.partner_id = self.partner_a
with so_form.order_line.new() as line:
line.product_id = super_kit
so = so_form.save()
self.assertEqual(so.order_line.purchase_price, 60)
so.action_confirm()
self.assertEqual(so.order_line.purchase_price, 60)