[FIX] mrp_subcontracting_account: read price unit at validation

In a subcontracting flow, the extra_cost on the subcontracted production
is read on the purchase line (via _get_price_unit() ) at the production
creation. In case the price unit on this purchase line change before the
validation, the stock valuation layer linked to the production will not
take this new value into account.

This commit ensures the price unit is the right one at validation

Note: the first version of the current fix broke a use case. The test
`test_tracked_compo_and_backorder` has been added to protect that use
case.

closes odoo/odoo#87817

X-original-commit: 1ff1b4a9a2b9ab27b29fdcac93731fa053c77cc7
Signed-off-by: William Henrotin (whe) <whe@odoo.com>
Signed-off-by: Adrien Widart <awt@odoo.com>
Co-authored-by: William Henrotin (whe) <whe@odoo.com>
Co-authored-by: Adrien Widart <awt@odoo.com>
This commit is contained in:
William Henrotin
2022-04-08 10:37:56 +02:00
committed by Adrien Widart
co-authored by Adrien Widart
parent 1cfac2f2cd
commit 7ef48801bb
2 changed files with 59 additions and 3 deletions
@@ -10,6 +10,7 @@ class MrpProduction(models.Model):
def _cal_price(self, consumed_moves):
finished_move = self.move_finished_ids.filtered(lambda x: x.product_id == self.product_id and x.state not in ('done', 'cancel') and x.quantity_done > 0)
# Take the price unit of the reception move
if finished_move.move_dest_ids.is_subcontract:
self.extra_cost = finished_move.move_dest_ids._get_price_unit()
last_done_receipt = finished_move.move_dest_ids.filtered(lambda m: m.state == 'done')[-1:]
if last_done_receipt.is_subcontract:
self.extra_cost = last_done_receipt._get_price_unit()
return super()._cal_price(consumed_moves=consumed_moves)
@@ -51,9 +51,11 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
move.product_id = self.finished
move.product_uom_qty = 1
picking_receipt = picking_form.save()
picking_receipt.move_ids.price_unit = 30.0
picking_receipt.move_ids.price_unit = 15.0
picking_receipt.action_confirm()
# Suppose the additional cost changes:
picking_receipt.move_ids.price_unit = 30.0
picking_receipt.move_ids.quantity_done = 1.0
picking_receipt._action_done()
@@ -157,6 +159,59 @@ class TestAccountSubcontractingFlows(TestMrpSubcontractingCommon):
for layer in f_layers:
self.assertEqual(layer.value, 100 + 50)
def test_tracked_compo_and_backorder(self):
"""
Suppose a subcontracted product P with two tracked components, P is FIFO
Create a receipt for 10 x P, receive 5, then 3 and then 2
"""
self.env.ref('product.product_category_all').property_cost_method = 'fifo'
self.comp1.tracking = 'lot'
self.comp1.standard_price = 10
self.comp2.tracking = 'lot'
self.comp2.standard_price = 20
lot01, lot02 = self.env['stock.lot'].create([{
'name': "Lot of %s" % product.name,
'product_id': product.id,
'company_id': self.env.company.id,
} for product in (self.comp1, self.comp2)])
receipt_form = Form(self.env['stock.picking'])
receipt_form.picking_type_id = self.env.ref('stock.picking_type_in')
receipt_form.partner_id = self.subcontractor_partner1
with receipt_form.move_ids_without_package.new() as move:
move.product_id = self.finished
move.product_uom_qty = 10
receipt = receipt_form.save()
# add an extra cost
receipt.move_ids.price_unit = 50
receipt.action_confirm()
for qty_producing in (5, 3, 2):
action = receipt.action_record_components()
mo = self.env['mrp.production'].browse(action['res_id'])
mo_form = Form(mo.with_context(**action['context']), view=action['view_id'])
mo_form.qty_producing = qty_producing
with mo_form.move_line_raw_ids.edit(0) as ml:
ml.lot_id = lot01
with mo_form.move_line_raw_ids.edit(1) as ml:
ml.lot_id = lot02
mo = mo_form.save()
mo.subcontracting_record_component()
action = receipt.button_validate()
if isinstance(action, dict):
wizard = Form(self.env[action['res_model']].with_context(action['context'])).save()
wizard.process()
receipt = receipt.backorder_ids
self.assertRecordValues(self.finished.stock_valuation_layer_ids, [
{'quantity': 5, 'value': 5 * (10 + 20 + 50)},
{'quantity': 3, 'value': 3 * (10 + 20 + 50)},
{'quantity': 2, 'value': 2 * (10 + 20 + 50)},
])
class TestBomPriceSubcontracting(TestBomPrice):
def test_01_compute_price_subcontracting_cost(self):