Skip to content

Commit

Permalink
[IMP] stock_inventory_lockdown: black, isort
Browse files Browse the repository at this point in the history
  • Loading branch information
AdriaGForgeFlow committed Dec 20, 2019
1 parent e861f81 commit d922fc7
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 101 deletions.
2 changes: 1 addition & 1 deletion .isort.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ line_length=88
known_odoo=odoo
known_odoo_addons=odoo.addons
sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER
known_third_party=
known_third_party=setuptools
5 changes: 1 addition & 4 deletions stock_inventory_lockdown/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@
"author": "Numérigraphe, Eficent, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/stock-logistics-warehouse",
"category": "Warehouse Management",
"images": [
"images/move_error.png",
"images/location_locked.png",
],
"images": ["images/move_error.png", "images/location_locked.png"],
"license": "AGPL-3",
"installable": True,
}
15 changes: 8 additions & 7 deletions stock_inventory_lockdown/models/stock_inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,23 @@


class StockInventory(models.Model):
_inherit = 'stock.inventory'
_inherit = "stock.inventory"

@api.model
def _get_locations_open_inventories(self, locations_ids=None):
"""IDs of locations in open exhaustive inventories, with children"""
inventory_domain = [('state', '=', 'confirm')]
inventory_domain = [("state", "=", "confirm")]
if locations_ids:
inventory_domain.append(('location_id', 'child_of', locations_ids))
inventory_domain.append(("location_id", "child_of", locations_ids))
inventories = self.search(inventory_domain)
if not inventories:
# Early exit if no match found
return []
location_ids = inventories.mapped('location_id')
location_ids = inventories.mapped("location_id")

# Extend to the children Locations
location_domain = [
('location_id', 'child_of', location_ids.ids),
('usage', 'in', ['internal', 'transit'])]
return self.env['stock.location'].search(location_domain)
("location_id", "child_of", location_ids.ids),
("usage", "in", ["internal", "transit"]),
]
return self.env["stock.location"].search(location_domain)
27 changes: 14 additions & 13 deletions stock_inventory_lockdown/models/stock_location.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
# © 2016 Numérigraphe SARL
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import api, models, _
from odoo import _, api, models
from odoo.exceptions import ValidationError


class StockLocation(models.Model):
"""Refuse changes during exhaustive Inventories"""
_inherit = 'stock.location'
_order = 'name'

_inherit = "stock.location"
_order = "name"

@api.multi
@api.constrains('location_id')
@api.constrains("location_id")
def _check_inventory_location_id(self):
"""Error if an inventory is being conducted here"""
vals = set(self.ids) | set(self.mapped('location_id').ids)
location_inventory_open_ids = self.env['stock.inventory'].sudo().\
_get_locations_open_inventories(vals)
vals = set(self.ids) | set(self.mapped("location_id").ids)
location_inventory_open_ids = (
self.env["stock.inventory"].sudo()._get_locations_open_inventories(vals)
)
if location_inventory_open_ids:
raise ValidationError(
_('An inventory is being conducted at this location'))
raise ValidationError(_("An inventory is being conducted at this location"))

@api.multi
def unlink(self):
"""Refuse unlink if an inventory is being conducted"""
location_inventory_open_ids = self.env['stock.inventory'].sudo().\
_get_locations_open_inventories(self.ids)
location_inventory_open_ids = (
self.env["stock.inventory"].sudo()._get_locations_open_inventories(self.ids)
)
if location_inventory_open_ids:
raise ValidationError(
_('An inventory is being conducted at this location'))
raise ValidationError(_("An inventory is being conducted at this location"))
return super(StockLocation, self).unlink()
48 changes: 31 additions & 17 deletions stock_inventory_lockdown/models/stock_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,52 @@
# (http://www.eficent.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import api, models, _
from odoo import _, api, models
from odoo.exceptions import ValidationError


class StockMove(models.Model):
_inherit = 'stock.move'
_inherit = "stock.move"

@api.multi
def _get_reserved_locations(self):
self.ensure_one()
return self.move_line_ids.mapped('location_id')
return self.move_line_ids.mapped("location_id")

@api.multi
def _get_dest_locations(self):
self.ensure_one()
return self.move_line_ids.mapped('location_dest_id')
return self.move_line_ids.mapped("location_dest_id")

@api.constrains('location_dest_id', 'location_id', 'state')
@api.constrains("location_dest_id", "location_id", "state")
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[
'stock.inventory']._get_locations_open_inventories(
[move.location_dest_id.id, move.location_id.id])
"stock.inventory"
]._get_locations_open_inventories(
[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
not any([move.location_dest_id.usage == 'inventory',
move.location_id.usage == 'inventory']) 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')
if (
locked_location_ids
and not any(
[
move.location_dest_id.usage == "inventory",
move.location_id.usage == "inventory",
]
)
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")
raise ValidationError(
_('An inventory is being conducted at the following '
'location(s):\n%s') % "\n - ".join(location_names))
_(
"An inventory is being conducted at the following "
"location(s):\n%s"
)
% "\n - ".join(location_names)
)
151 changes: 92 additions & 59 deletions stock_inventory_lockdown/tests/test_stock_inventory_lockdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,104 +3,140 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo.exceptions import ValidationError

from odoo.addons.stock.tests.common import TestStockCommon


class StockInventoryLocationTest(TestStockCommon):
def setUp(self):
super(StockInventoryLocationTest, self).setUp()
# 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(
{'name': 'Test location',
'usage': 'internal',
'location_id': self.new_parent_location.id})
self.new_sublocation = self.env['stock.location'].create(
{'name': 'Test sublocation',
'usage': 'internal',
'location_id': self.new_location.id})
self.new_parent_location = self.env["stock.location"].create(
{"name": "Test parent location", "usage": "internal"}
)
self.new_location = self.env["stock.location"].create(
{
"name": "Test location",
"usage": "internal",
"location_id": self.new_parent_location.id,
}
)
self.new_sublocation = self.env["stock.location"].create(
{
"name": "Test sublocation",
"usage": "internal",
"location_id": self.new_location.id,
}
)
# Input goods
self.env['stock.quant'].create(
{'location_id': self.new_location.id,
'product_id': self.productA.id,
'quantity': 10.0})
self.env['stock.quant'].create(
{'location_id': self.new_parent_location.id,
'product_id': self.productB.id,
'quantity': 5.0})
self.env["stock.quant"].create(
{
"location_id": self.new_location.id,
"product_id": self.productA.id,
"quantity": 10.0,
}
)
self.env["stock.quant"].create(
{
"location_id": self.new_parent_location.id,
"product_id": self.productB.id,
"quantity": 5.0,
}
)
# Prepare an inventory
self.inventory = self.env['stock.inventory'].create(
{'name': 'Lock down location',
'filter': 'none',
'location_id': self.new_location.id})
self.inventory = self.env["stock.inventory"].create(
{
"name": "Lock down location",
"filter": "none",
"location_id": self.new_location.id,
}
)
self.inventory.action_start()
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,
})
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):
"""Updating the parent of a location is OK if no inv. in progress."""
self.inventory.action_cancel_draft()
self.inventory.location_id.location_id = self.env.ref(
'stock.stock_location_4')
self.inventory.location_id.location_id = self.env.ref("stock.stock_location_4")

def test_update_parent_location_locked_down(self):
"""Updating the parent of a location must fail"""
with self.assertRaises(ValidationError):
self.inventory.location_id.location_id = self.env.ref(
'stock.stock_location_4')
"stock.stock_location_4"
)

def test_inventory(self):
"""We must still be able to finish the inventory"""
self.assertTrue(self.inventory.line_ids)
self.inventory.line_ids.write({'product_qty': 42.0})
self.inventory.line_ids.write({"product_qty": 42.0})
for line in self.inventory.line_ids:
self.assertNotEqual(line.product_id.with_context(
location=line.location_id.id).qty_available, 42.0)
self.assertNotEqual(
line.product_id.with_context(
location=line.location_id.id
).qty_available,
42.0,
)
self.inventory.action_validate()
for line in self.inventory.line_ids:
self.assertEqual(line.product_id.with_context(
location=line.location_id.id).qty_available, 42.0)
self.assertEqual(
line.product_id.with_context(
location=line.location_id.id
).qty_available,
42.0,
)

def test_inventory_sublocation(self):
"""We must be able to make an inventory in a sublocation"""
inventory_subloc = self.env['stock.inventory'].create(
{'name': 'Lock down location',
'filter': 'partial',
'location_id': self.new_sublocation.id})
inventory_subloc = self.env["stock.inventory"].create(
{
"name": "Lock down location",
"filter": "partial",
"location_id": self.new_sublocation.id,
}
)
inventory_subloc.action_start()
line = self.env['stock.inventory.line'].create(
{'product_id': self.productA.id,
'product_qty': 22.0,
'location_id': self.new_sublocation.id,
'inventory_id': inventory_subloc.id})
line = self.env["stock.inventory.line"].create(
{
"product_id": self.productA.id,
"product_qty": 22.0,
"location_id": self.new_sublocation.id,
"inventory_id": inventory_subloc.id,
}
)
self.assertTrue(inventory_subloc.line_ids)
inventory_subloc.action_validate()
self.assertEqual(line.product_id.with_context(
location=line.location_id.id).qty_available, 22.0)
self.assertEqual(
line.product_id.with_context(location=line.location_id.id).qty_available,
22.0,
)

def test_move(self):
"""Stock moves must be forbidden during inventory from/to inventoried
location."""
move1 = self.create_stock_move(
self.productA, origin_id=self.inventory.location_id.id)
self.productA, origin_id=self.inventory.location_id.id
)
move1._action_confirm()
with self.assertRaises(ValidationError):
move1._action_assign()
move1.move_line_ids[0].qty_done = 10.0
move1._action_done()
move2 = self.create_stock_move(
self.productA, dest_id=self.inventory.location_id.id)
self.productA, dest_id=self.inventory.location_id.id
)
with self.assertRaises(ValidationError):
move2._action_confirm()
move2._action_assign()
Expand All @@ -113,16 +149,13 @@ def test_move_reserved_quants(self):
* 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 = self.create_stock_move(self.productB, self.new_parent_location.id)
move1._action_confirm()
move1._action_assign()
move1.move_line_ids[0].qty_done = 10.0
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)
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()
Expand Down

0 comments on commit d922fc7

Please sign in to comment.