From ef604945d80067d7560d67bfb68a551082f78a48 Mon Sep 17 00:00:00 2001 From: "Adrien Widart (awt)" Date: Fri, 27 Oct 2023 10:10:22 +0200 Subject: [PATCH] [FIX] mrp_subcontracting: decrease done quantity To reproduce the issue: 1. Create a subcontracted product P 2. Create and confirm a planned transfer with 10 x P 3. Set the done quantity to 8 4. Set the done quantity to 7 5. Open the detailed operation (i.e., the MO) Error: The producing quantity of the MO is 1, it should be 3 When setting a smaller done quantity than the demand (step 8), we update the expected quantity of the related MO, we set its producing quantity, and we create a backorder. When decreasing the done quantity, we update/cancel the related productions. But here is the issue: in the above case, we simply decrease the expected quantity of the backorder, so we just lose the cancelled quantity, the user "can't" produce this quantity anymore. In such situation (when decreasing the done quantity), we should consider the last production (i.e., the last backorder) as the WIP one. So, we should update/cancel the other productions and increase the expected quantity of this last backorder, so the user can produce it again. OPW-3557632 closes odoo/odoo#142513 Signed-off-by: William Henrotin (whe) Signed-off-by: Quentin Wolfs (quwo) --- .../mrp_subcontracting/models/stock_move.py | 13 +++- .../tests/test_subcontracting.py | 69 +++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/addons/mrp_subcontracting/models/stock_move.py b/addons/mrp_subcontracting/models/stock_move.py index 8d9e7395f4f..5492302a221 100644 --- a/addons/mrp_subcontracting/models/stock_move.py +++ b/addons/mrp_subcontracting/models/stock_move.py @@ -77,7 +77,7 @@ class StockMove(models.Model): move._auto_record_components(delta_qty) to_set_moves -= move elif float_compare(delta_qty, 0, precision_rounding=move.product_uom.rounding) < 0: - move._reduce_subcontract_order_qty(abs(delta_qty)) + move.with_context(transfer_qty=True)._reduce_subcontract_order_qty(abs(delta_qty)) if to_set_moves: super(StockMove, to_set_moves)._set_quantity() @@ -295,8 +295,17 @@ class StockMove(models.Model): def _reduce_subcontract_order_qty(self, quantity_to_remove): self.ensure_one() productions = self.move_orig_ids.production_id.filtered(lambda p: p.state not in ('done', 'cancel'))[::-1] + wip_production = productions[0] if self._context.get('transfer_qty') and len(productions) > 1 else self.env['mrp.production'] + + # Transfer removed qty to WIP production + if wip_production: + self.env['change.production.qty'].with_context(skip_activity=True).create({ + 'mo_id': wip_production.id, + 'product_qty': wip_production.product_uom_qty + quantity_to_remove + }).change_prod_qty() + # Cancel productions until reach new_quantity - for production in productions: + for production in (productions - wip_production): if quantity_to_remove >= production.product_qty: quantity_to_remove -= production.product_qty production.with_context(skip_activity=True).action_cancel() diff --git a/addons/mrp_subcontracting/tests/test_subcontracting.py b/addons/mrp_subcontracting/tests/test_subcontracting.py index 2f36331cac5..42e2efd1d64 100644 --- a/addons/mrp_subcontracting/tests/test_subcontracting.py +++ b/addons/mrp_subcontracting/tests/test_subcontracting.py @@ -837,6 +837,75 @@ class TestSubcontractingFlows(TestMrpSubcontractingCommon): {'product_qty': 3, 'state': 'cancel'}, ]) + def test_decrease_quantity_done(self): + self.bom.consumption = 'flexible' + supplier_location = self.env.ref('stock.stock_location_suppliers') + + receipt = self.env['stock.picking'].create({ + 'partner_id': self.subcontractor_partner1.id, + 'location_id': supplier_location.id, + 'location_dest_id': self.warehouse.lot_stock_id.id, + 'picking_type_id': self.warehouse.in_type_id.id, + 'move_ids': [(0, 0, { + 'name': self.finished.name, + 'product_id': self.finished.id, + 'product_uom_qty': 10.0, + 'product_uom': self.finished.uom_id.id, + 'location_id': supplier_location.id, + 'location_dest_id': self.warehouse.lot_stock_id.id, + })], + }) + + receipt.action_confirm() + productions = self.env['mrp.production'].search([('product_id', '=', self.finished.id)], order='id') + self.assertRecordValues(productions, [ + {'qty_producing': 0.0, 'product_qty': 10.0, 'state': 'confirmed'}, + ]) + + receipt.move_ids.quantity = 6 + productions = self.env['mrp.production'].search([('product_id', '=', self.finished.id)], order='id') + self.assertEqual(receipt.move_ids.product_uom_qty, 10.0, 'Demand should not be impacted') + self.assertRecordValues(productions, [ + {'qty_producing': 6.0, 'product_qty': 6.0, 'state': 'to_close'}, + {'qty_producing': 4.0, 'product_qty': 4.0, 'state': 'to_close'}, + ]) + + receipt.move_ids.quantity = 9 + productions = self.env['mrp.production'].search([('product_id', '=', self.finished.id)], order='id') + self.assertEqual(receipt.move_ids.product_uom_qty, 10.0, 'Demand should not be impacted') + self.assertRecordValues(productions, [ + {'qty_producing': 6.0, 'product_qty': 6.0, 'state': 'to_close'}, + {'qty_producing': 3.0, 'product_qty': 3.0, 'state': 'to_close'}, + {'qty_producing': 1.0, 'product_qty': 1.0, 'state': 'to_close'}, + ]) + + receipt.move_ids.quantity = 7 + productions = self.env['mrp.production'].search([('product_id', '=', self.finished.id)], order='id') + self.assertEqual(receipt.move_ids.product_uom_qty, 10.0, 'Demand should not be impacted') + self.assertRecordValues(productions, [ + {'qty_producing': 6.0, 'product_qty': 6.0, 'state': 'to_close'}, + {'qty_producing': 1.0, 'product_qty': 1.0, 'state': 'to_close'}, + {'qty_producing': 3.0, 'product_qty': 3.0, 'state': 'to_close'}, + ]) + + receipt.move_ids.quantity = 4 + productions = self.env['mrp.production'].search([('product_id', '=', self.finished.id)], order='id') + self.assertEqual(receipt.move_ids.product_uom_qty, 10.0, 'Demand should not be impacted') + self.assertRecordValues(productions, [ + {'qty_producing': 4.0, 'product_qty': 4.0, 'state': 'to_close'}, + {'qty_producing': 1.0, 'product_qty': 1.0, 'state': 'cancel'}, + {'qty_producing': 6.0, 'product_qty': 6.0, 'state': 'to_close'}, + ]) + + receipt.move_ids.quantity = 0 + productions = self.env['mrp.production'].search([('product_id', '=', self.finished.id)], order='id') + self.assertEqual(receipt.move_ids.product_uom_qty, 10.0, 'Demand should not be impacted') + self.assertRecordValues(productions, [ + {'qty_producing': 4.0, 'product_qty': 4.0, 'state': 'cancel'}, + {'qty_producing': 1.0, 'product_qty': 1.0, 'state': 'cancel'}, + {'qty_producing': 10.0, 'product_qty': 10.0, 'state': 'to_close'}, + ]) + @tagged('post_install', '-at_install') class TestSubcontractingTracking(TransactionCase):