[FIX] stock: partial reserve in MTO
- Create a product with -4.0 Units in stock - Set the Buy and MTO routes, assign a vendor - Create a SO for 25 Units - A PO of 25 Units is created => validate and receive products - Check availability on the picking linked to the SO Nothing is reserved, while 21 Units could be reserved. opw-1850688 Co-authored-by: Nicolas Martinelli <nim@odoo.com>
This commit is contained in:
committed by
Nicolas Martinelli
co-authored by
Nicolas Martinelli
parent
caadeeee62
commit
d42856f29d
@@ -865,11 +865,6 @@ class StockMove(models.Model):
|
||||
package_id=package_id, owner_id=owner_id, strict=strict
|
||||
)
|
||||
except UserError:
|
||||
# If it raises here, it means that the `available_quantity` brought by a done move line
|
||||
# is not available on the quants itself. This could be the result of an inventory
|
||||
# adjustment that removed totally of partially `available_quantity`. When this happens, we
|
||||
# chose to do nothing. This situation could not happen on MTS move, because in this case
|
||||
# `available_quantity` is directly the quantity on the quants themselves.
|
||||
taken_quantity = 0
|
||||
|
||||
# Find a candidate move line to update or create a new one.
|
||||
@@ -985,7 +980,17 @@ class StockMove(models.Model):
|
||||
available_move_lines[(move_line.location_id, move_line.lot_id, move_line.result_package_id, move_line.owner_id)] -= move_line.product_qty
|
||||
for (location_id, lot_id, package_id, owner_id), quantity in available_move_lines.items():
|
||||
need = move.product_qty - sum(move.move_line_ids.mapped('product_qty'))
|
||||
taken_quantity = move._update_reserved_quantity(need, quantity, location_id, lot_id, package_id, owner_id)
|
||||
# `quantity` is what is brought by chained done move lines. We double check
|
||||
# here this quantity is available on the quants themselves. If not, this
|
||||
# could be the result of an inventory adjustment that removed totally of
|
||||
# partially `quantity`. When this happens, we chose to reserve the maximum
|
||||
# still available. This situation could not happen on MTS move, because in
|
||||
# this case `quantity` is directly the quantity on the quants themselves.
|
||||
available_quantity = self.env['stock.quant']._get_available_quantity(
|
||||
move.product_id, location_id, lot_id=lot_id, package_id=package_id, owner_id=owner_id, strict=True)
|
||||
if float_is_zero(available_quantity, precision_rounding=move.product_id.uom_id.rounding):
|
||||
continue
|
||||
taken_quantity = move._update_reserved_quantity(need, min(quantity, available_quantity), location_id, lot_id, package_id, owner_id)
|
||||
if float_is_zero(taken_quantity, precision_rounding=move.product_id.uom_id.rounding):
|
||||
continue
|
||||
if need - taken_quantity == 0.0:
|
||||
|
||||
@@ -1751,7 +1751,7 @@ class StockMove(TransactionCase):
|
||||
ml.qty_done = 1
|
||||
res_dict_for_back_order = picking_pack_cust.button_validate()
|
||||
backorder_wizard = self.env[(res_dict_for_back_order.get('res_model'))].browse(res_dict_for_back_order.get('res_id'))
|
||||
backorder_wizard.with_context(debug=True).process()
|
||||
backorder_wizard.process()
|
||||
backorder = self.env['stock.picking'].search([('backorder_id', '=', picking_pack_cust.id)])
|
||||
backordered_move = backorder.move_lines
|
||||
|
||||
@@ -1782,6 +1782,57 @@ class StockMove(TransactionCase):
|
||||
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product2, self.customer_location), 3)
|
||||
self.assertEqual(self.env['stock.quant']._get_available_quantity(self.product2, self.pack_location), 0)
|
||||
|
||||
def test_link_assign_10(self):
|
||||
""" Test the assignment mechanism with partial availability.
|
||||
"""
|
||||
# make some stock:
|
||||
# stock location: 2.0
|
||||
# pack location: -1.0
|
||||
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2.0)
|
||||
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.stock_location)), 1.0)
|
||||
|
||||
move_out = self.env['stock.move'].create({
|
||||
'name': 'test_link_assign_out',
|
||||
'location_id': self.pack_location.id,
|
||||
'location_dest_id': self.customer_location.id,
|
||||
'product_id': self.product1.id,
|
||||
'product_uom': self.uom_unit.id,
|
||||
'product_uom_qty': 1.0,
|
||||
})
|
||||
move_out._action_confirm()
|
||||
move_out._action_assign()
|
||||
move_out.quantity_done = 1.0
|
||||
move_out._action_done()
|
||||
self.assertEqual(len(self.env['stock.quant']._gather(self.product1, self.pack_location)), 1.0)
|
||||
|
||||
move_stock_pack = self.env['stock.move'].create({
|
||||
'name': 'test_link_assign_1_1',
|
||||
'location_id': self.stock_location.id,
|
||||
'location_dest_id': self.pack_location.id,
|
||||
'product_id': self.product1.id,
|
||||
'product_uom': self.uom_unit.id,
|
||||
'product_uom_qty': 2.0,
|
||||
})
|
||||
move_pack_cust = self.env['stock.move'].create({
|
||||
'name': 'test_link_assign_1_2',
|
||||
'location_id': self.pack_location.id,
|
||||
'location_dest_id': self.customer_location.id,
|
||||
'product_id': self.product1.id,
|
||||
'product_uom': self.uom_unit.id,
|
||||
'product_uom_qty': 2.0,
|
||||
})
|
||||
move_stock_pack.write({'move_dest_ids': [(4, move_pack_cust.id, 0)]})
|
||||
move_pack_cust.write({'move_orig_ids': [(4, move_stock_pack.id, 0)]})
|
||||
|
||||
(move_stock_pack + move_pack_cust)._action_confirm()
|
||||
move_stock_pack._action_assign()
|
||||
move_stock_pack.quantity_done = 2.0
|
||||
move_stock_pack._action_done()
|
||||
self.assertEqual(len(move_pack_cust.move_line_ids), 1)
|
||||
|
||||
self.assertAlmostEqual(move_pack_cust.reserved_availability, 1.0)
|
||||
self.assertEqual(move_pack_cust.state, 'partially_available')
|
||||
|
||||
def test_use_unreserved_move_line_1(self):
|
||||
""" Test that validating a stock move linked to an untracked product reserved by another one
|
||||
correctly unreserves the other one.
|
||||
|
||||
Reference in New Issue
Block a user