[10.0][FIX] stock_inventory_lockdown: locking unneded locations.

This commit is contained in:
lreficent
2017-11-16 11:16:19 +01:00
committed by Ben Stannard
parent be46a01321
commit 1479334b6b
4 changed files with 74 additions and 18 deletions

View File

@@ -5,13 +5,13 @@
{ {
"name": "Inventory Lock Down", "name": "Inventory Lock Down",
"summary": "Lock down stock locations during inventories.", "summary": "Lock down stock locations during inventories.",
"version": "10.0.1.0.0", "version": "10.0.1.0.1",
"depends": ["stock"], "depends": ["stock"],
"author": "Numérigraphe,Eficent,Odoo Community Association (OCA)", "author": "Numérigraphe, Eficent, Odoo Community Association (OCA)",
"category": "Warehouse Management", "category": "Warehouse Management",
"images": [ "images": [
"images/move_error.png", "images/move_error.png",
"images/location_locked.png"], "images/location_locked.png"],
'license': 'AGPL-3', "license": "AGPL-3",
"installable": True, "installable": True,
} }

View File

@@ -26,6 +26,4 @@ class StockInventory(models.Model):
location_domain = [ location_domain = [
('location_id', 'child_of', location_ids.ids), ('location_id', 'child_of', location_ids.ids),
('usage', 'in', ['internal', 'transit'])] ('usage', 'in', ['internal', 'transit'])]
if locations_ids:
location_domain.append(('location_id', 'child_of', locations_ids))
return self.env['stock.location'].search(location_domain) return self.env['stock.location'].search(location_domain)

View File

@@ -11,15 +11,32 @@ from odoo.exceptions import ValidationError
class StockMove(models.Model): class StockMove(models.Model):
_inherit = 'stock.move' _inherit = 'stock.move'
@api.multi
def _get_reserved_locations(self):
self.ensure_one()
return self.reserved_quant_ids.mapped('location_id') + \
self.split_from.reserved_quant_ids.mapped('location_id')
@api.multi
def _get_dest_locations(self):
self.ensure_one()
return self.linked_move_operation_ids.mapped(
'operation_id.location_dest_id')
@api.constrains('location_dest_id', 'location_id', 'state') @api.constrains('location_dest_id', 'location_id', 'state')
def _check_locked_location(self): def _check_locked_location(self):
for move in self.filtered(lambda m: m.state != 'draft'): for move in self.filtered(lambda m: m.state != 'draft'):
locked_location_ids = self.env[ locked_location_ids = self.env[
'stock.inventory']._get_locations_open_inventories( 'stock.inventory']._get_locations_open_inventories(
[move.location_dest_id.id, move.location_id.id]) [move.location_dest_id.id, move.location_id.id])
reserved_locs = move._get_reserved_locations()
dest_locs = move._get_dest_locations()
if (locked_location_ids and if (locked_location_ids and
move.product_id.property_stock_inventory not in [ move.product_id.property_stock_inventory not in [
move.location_dest_id, move.location_id]): move.location_dest_id, move.location_id] and
(move.location_dest_id in locked_location_ids or
any([l in locked_location_ids for l in dest_locs]) or
any([l in locked_location_ids for l in reserved_locs]))):
location_names = locked_location_ids.mapped('complete_name') location_names = locked_location_ids.mapped('complete_name')
raise ValidationError( raise ValidationError(
_('An inventory is being conducted at the following ' _('An inventory is being conducted at the following '

View File

@@ -10,10 +10,14 @@ from odoo.addons.stock.tests.common import TestStockCommon
class StockInventoryLocationTest(TestStockCommon): class StockInventoryLocationTest(TestStockCommon):
def setUp(self): def setUp(self):
super(StockInventoryLocationTest, self).setUp() super(StockInventoryLocationTest, self).setUp()
# Make a new location # Make a new location with a parent and a child.
self.new_parent_location = self.env['stock.location'].create(
{'name': 'Test parent location',
'usage': 'internal'})
self.new_location = self.env['stock.location'].create( self.new_location = self.env['stock.location'].create(
{'name': 'Test location', {'name': 'Test location',
'usage': 'internal'}) 'usage': 'internal',
'location_id': self.new_parent_location.id})
self.new_sublocation = self.env['stock.location'].create( self.new_sublocation = self.env['stock.location'].create(
{'name': 'Test sublocation', {'name': 'Test sublocation',
'usage': 'internal', 'usage': 'internal',
@@ -23,6 +27,10 @@ class StockInventoryLocationTest(TestStockCommon):
{'location_id': self.new_location.id, {'location_id': self.new_location.id,
'product_id': self.productA.id, 'product_id': self.productA.id,
'qty': 10.0}) 'qty': 10.0})
self.env['stock.quant'].create(
{'location_id': self.new_parent_location.id,
'product_id': self.productB.id,
'qty': 5.0})
# Prepare an inventory # Prepare an inventory
self.inventory = self.env['stock.inventory'].create( self.inventory = self.env['stock.inventory'].create(
{'name': 'Lock down location', {'name': 'Lock down location',
@@ -31,6 +39,16 @@ class StockInventoryLocationTest(TestStockCommon):
self.inventory.prepare_inventory() self.inventory.prepare_inventory()
self.assertTrue(self.inventory.line_ids, 'The inventory is empty.') self.assertTrue(self.inventory.line_ids, 'The inventory is empty.')
def create_stock_move(self, product, origin_id=False, dest_id=False):
return self.env['stock.move'].create({
'name': 'Test move lock down',
'product_id': product.id,
'product_uom_qty': 10.0,
'product_uom': product.uom_id.id,
'location_id': origin_id or self.supplier_location,
'location_dest_id': dest_id or self.customer_location,
})
def test_update_parent_location(self): def test_update_parent_location(self):
"""Updating the parent of a location is OK if no inv. in progress.""" """Updating the parent of a location is OK if no inv. in progress."""
self.inventory.action_cancel_draft() self.inventory.action_cancel_draft()
@@ -73,14 +91,37 @@ class StockInventoryLocationTest(TestStockCommon):
location=line.location_id.id).qty_available, 22.0) location=line.location_id.id).qty_available, 22.0)
def test_move(self): def test_move(self):
"""Stock move must be forbidden during inventory""" """Stock moves must be forbidden during inventory from/to inventoried
move = self.env['stock.move'].create({ location."""
'name': 'Test move lock down', move1 = self.create_stock_move(
'product_id': self.productA.id, self.productA, origin_id=self.inventory.location_id.id)
'product_uom_qty': 10.0, move1.action_confirm()
'product_uom': self.productA.uom_id.id,
'location_id': self.inventory.location_id.id,
'location_dest_id': self.customer_location
})
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
move.action_done() move1.action_assign()
move1.action_done()
move2 = self.create_stock_move(
self.productA, dest_id=self.inventory.location_id.id)
with self.assertRaises(ValidationError):
move2.action_confirm()
move2.action_assign()
move2.action_done()
def test_move_reserved_quants(self):
"""Shipping stock should be allowed or not depending on reserved
quants' locations.
* move1: quants are fetched from the parent location.
* move2: quants are fetched from 'new location' which is being
inventoried."""
move1 = self.create_stock_move(
self.productB, self.new_parent_location.id)
move1.action_confirm()
move1.action_assign()
move1.action_done()
self.assertEqual(
move1.state, 'done', 'Move has not been completed')
move2 = self.create_stock_move(
self.productA, self.new_parent_location.id)
move2.action_confirm()
with self.assertRaises(ValidationError):
move2.action_assign()
move2.action_done()