mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[IMP] stock_inventory_lockdown: black, isort
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -7,22 +7,23 @@ from odoo import api, models
|
||||
|
||||
|
||||
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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
# 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
|
||||
|
||||
|
||||
@@ -10,97 +11,132 @@ 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()
|
||||
@@ -113,16 +149,13 @@ class StockInventoryLocationTest(TestStockCommon):
|
||||
* 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()
|
||||
|
||||
Reference in New Issue
Block a user