From baa88e7a263d67143df079f9e5a26dadcc261088 Mon Sep 17 00:00:00 2001 From: Florian Damhaut Date: Wed, 8 Dec 2021 10:18:57 +0000 Subject: [PATCH] [FIX] stock: assign in priority to earlier move line Steps to reproduce: -Create a quote with 2 lines (same product - different lead time). -When a partial delivery is made (e.g., 5 of 10), Odoo places the operation related to the remaining undelivered quantity (5), on the last line in the Operations list. -When the next delivery is made, Odoo deducts the quantity from the first operation in the list instead of from the line whose deadline is closest. Current Behaviour : Odoo assign button assign quantity to the first move line in the list. Behaviour After the PR: Odoo assign button assign quantity to a move line based on the priority, the closest deadline and the id. opw-2657048 closes odoo/odoo#82062 X-original-commit: 430f4f2ca1cebaea62415ee72bf4369684abbfaa Signed-off-by: Arnold Moyaux Signed-off-by: Damhaut Florian (flda) --- addons/stock/models/stock_picking.py | 4 +- addons/stock/tests/test_move2.py | 60 ++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index c5e4bc7ddce..5535b88eb9f 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -808,7 +808,9 @@ class Picking(models.Model): @return: True """ self.filtered(lambda picking: picking.state == 'draft').action_confirm() - moves = self.move_ids.filtered(lambda move: move.state not in ('draft', 'cancel', 'done')) + moves = self.move_ids.filtered(lambda move: move.state not in ('draft', 'cancel', 'done')).sorted( + key=lambda move: (-int(move.priority), not bool(move.date_deadline), move.date_deadline, move.id) + ) if not moves: raise UserError(_('Nothing to check the availability for.')) # If a package level is done when confirmed its location can be different than where it will be reserved. diff --git a/addons/stock/tests/test_move2.py b/addons/stock/tests/test_move2.py index 97925e867b9..20345a99ece 100644 --- a/addons/stock/tests/test_move2.py +++ b/addons/stock/tests/test_move2.py @@ -9,6 +9,8 @@ from odoo.exceptions import UserError from odoo.tests import Form from odoo.tools import float_is_zero, float_compare +from datetime import datetime +from dateutil.relativedelta import relativedelta class TestPickShip(TestStockCommon): def create_pick_ship(self): @@ -980,6 +982,64 @@ class TestSinglePicking(TestStockCommon): self.assertEqual(delivery_order.state, 'done') self.assertEqual(delivery_order.move_ids[1].state, 'cancel') + def test_assign_deadline(self): + """ Check if similar items with shorter deadline are prioritized. + """ + delivery_order = self.PickingObj.create({ + 'location_id': self.pack_location, + 'location_dest_id': self.customer_location, + 'picking_type_id': self.picking_type_out, + }) + self.MoveObj.create({ + 'name': "move1", + 'product_id': self.productA.id, + 'product_uom_qty': 4, + 'product_uom': self.productA.uom_id.id, + 'picking_id': delivery_order.id, + 'location_id': self.pack_location, + 'location_dest_id': self.customer_location, + 'date_deadline': datetime.now() + relativedelta(days=1) + }) + self.MoveObj.create({ + 'name': "move2", + 'product_id': self.productA.id, + 'product_uom_qty': 4, + 'product_uom': self.productA.uom_id.id, + 'picking_id': delivery_order.id, + 'location_id': self.pack_location, + 'location_dest_id': self.customer_location, + 'date_deadline': datetime.now() + relativedelta(days=2) + }) + + # make some stock + pack_location = self.env['stock.location'].browse(self.pack_location) + self.StockQuantObj._update_available_quantity(self.productA, pack_location, 2) + + # assign to partially available + delivery_order.action_confirm() + delivery_order.action_assign() + reservedMove1 = sum([x.reserved_availability for x in delivery_order.move_ids if x.name == "move1"]) + reservedMove2 = sum([x.reserved_availability for x in delivery_order.move_ids if x.name == "move2"]) + + self.assertEqual(reservedMove1, 2, "Earlier deadline should have reserved quantity") + self.assertEqual(reservedMove2, 0, "Later deadline should not have reserved quantity") + + delivery_order.move_ids[0].move_line_ids[0].qty_done = 2 + delivery_order._action_done() + + # add new stock + self.StockQuantObj._update_available_quantity(self.productA, pack_location, 2) + + # assign new stock to backorder + backorder = delivery_order.backorder_ids + backorder.action_assign() + + reservedMove1 = sum([x.reserved_availability for x in backorder.move_ids if x.name == "move1"]) + reservedMove2 = sum([x.reserved_availability for x in backorder.move_ids if x.name == "move2"]) + + self.assertEqual(reservedMove1, 2, "Earlier deadline should have reserved quantity") + self.assertEqual(reservedMove2, 0, "Later deadline should not have reserved quantity") + def test_extra_move_1(self): """ Check the good behavior of creating an extra move in a delivery order. This usecase simulates the delivery of 2 item while the initial stock move had to move 1 and there's