From 7ef48801bbe8081bdee8ed3aa3b3cf0339a234cd Mon Sep 17 00:00:00 2001 From: William Henrotin Date: Thu, 31 Mar 2022 14:00:20 +0000 Subject: [PATCH] [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) Signed-off-by: Adrien Widart Co-authored-by: William Henrotin (whe) Co-authored-by: Adrien Widart --- .../models/mrp_production.py | 5 +- .../tests/test_subcontracting_account.py | 57 ++++++++++++++++++- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/addons/mrp_subcontracting_account/models/mrp_production.py b/addons/mrp_subcontracting_account/models/mrp_production.py index b80579184fe..6b10e4e45a3 100644 --- a/addons/mrp_subcontracting_account/models/mrp_production.py +++ b/addons/mrp_subcontracting_account/models/mrp_production.py @@ -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) diff --git a/addons/mrp_subcontracting_account/tests/test_subcontracting_account.py b/addons/mrp_subcontracting_account/tests/test_subcontracting_account.py index d973c7652a4..cf7955b474c 100644 --- a/addons/mrp_subcontracting_account/tests/test_subcontracting_account.py +++ b/addons/mrp_subcontracting_account/tests/test_subcontracting_account.py @@ -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):