mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
322 lines
11 KiB
Python
322 lines
11 KiB
Python
# Copyright 2023 Raumschmiede (http://www.raumschmiede.de)
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
|
|
|
|
|
from collections import namedtuple
|
|
|
|
from odoo.exceptions import ValidationError
|
|
from odoo.tests.common import SavepointCase
|
|
|
|
from odoo.addons.stock_location_package_restriction.models.stock_location import (
|
|
MULTIPACKAGE,
|
|
NORESTRICTION,
|
|
SINGLEPACKAGE,
|
|
)
|
|
|
|
ShortMoveInfo = namedtuple(
|
|
"ShortMoveInfo", ["product", "location_dest", "qty", "package_id"]
|
|
)
|
|
|
|
|
|
class TestStockMove(SavepointCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
"""
|
|
Data:
|
|
2 products: product_1, product_2
|
|
2 packages: pack_1, pack_2
|
|
1 new warehouse: warehouse1
|
|
2 new locations: location1 and location2 are children of
|
|
warehouse1's stock location and without
|
|
restriction
|
|
stock:
|
|
* 50 product_1 in location_1
|
|
* 0 product_2 in stock
|
|
"""
|
|
super().setUpClass()
|
|
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
|
|
cls.uom_unit = cls.env.ref("uom.product_uom_unit")
|
|
Product = cls.env["product.product"]
|
|
cls.product_1 = Product.create(
|
|
{"name": "Wood", "type": "product", "uom_id": cls.uom_unit.id}
|
|
)
|
|
cls.product_2 = Product.create(
|
|
{"name": "Stone", "type": "product", "uom_id": cls.uom_unit.id}
|
|
)
|
|
|
|
# Warehouses
|
|
cls.warehouse_1 = cls.env["stock.warehouse"].create(
|
|
{
|
|
"name": "Base Warehouse",
|
|
"reception_steps": "one_step",
|
|
"delivery_steps": "ship_only",
|
|
"code": "BWH",
|
|
}
|
|
)
|
|
|
|
# Locations
|
|
cls.location_1 = cls.env["stock.location"].create(
|
|
{
|
|
"name": "TestLocation1",
|
|
"posx": 3,
|
|
"location_id": cls.warehouse_1.lot_stock_id.id,
|
|
}
|
|
)
|
|
|
|
cls.location_2 = cls.env["stock.location"].create(
|
|
{
|
|
"name": "TestLocation2",
|
|
"posx": 4,
|
|
"location_id": cls.warehouse_1.lot_stock_id.id,
|
|
}
|
|
)
|
|
|
|
# Packages:
|
|
Package = cls.env["stock.quant.package"]
|
|
cls.pack_1 = Package.create({"name": "Package 1"})
|
|
cls.pack_2 = Package.create({"name": "Package 2"})
|
|
|
|
cls.supplier_location = cls.env.ref("stock.stock_location_suppliers")
|
|
|
|
# partner
|
|
cls.partner_1 = cls.env["res.partner"].create(
|
|
{"name": "Raumschmiede.de", "email": "info@raumschmiede.de"}
|
|
)
|
|
|
|
# picking type
|
|
cls.picking_type_in = cls.env.ref("stock.picking_type_in")
|
|
|
|
cls.StockMove = cls.env["stock.move"]
|
|
cls.StockPicking = cls.env["stock.picking"]
|
|
|
|
@classmethod
|
|
def _change_product_qty(cls, product, location, package, qty):
|
|
cls.env["stock.quant"].with_context(inventory_mode=True).create(
|
|
{
|
|
"product_id": product.id,
|
|
"package_id": package.id,
|
|
"inventory_quantity": qty,
|
|
"location_id": location.id,
|
|
}
|
|
)
|
|
|
|
def _get_package_in_location(self, location):
|
|
return (
|
|
self.env["stock.quant"]
|
|
.search([("location_id", "=", location.id)])
|
|
.mapped("package_id")
|
|
)
|
|
|
|
def _create_and_assign_picking(self, short_move_infos, location_dest=None):
|
|
location_dest = location_dest or self.location_1
|
|
picking_in = self.StockPicking.create(
|
|
{
|
|
"partner_id": self.partner_1.id,
|
|
"picking_type_id": self.picking_type_in.id,
|
|
"location_id": self.supplier_location.id,
|
|
"location_dest_id": location_dest.id,
|
|
}
|
|
)
|
|
for move_info in short_move_infos:
|
|
self.StockMove.create(
|
|
{
|
|
"name": move_info.product.name,
|
|
"product_id": move_info.product.id,
|
|
"product_uom_qty": move_info.qty,
|
|
"product_uom": move_info.product.uom_id.id,
|
|
"picking_id": picking_in.id,
|
|
"location_id": self.supplier_location.id,
|
|
"location_dest_id": move_info.location_dest.id,
|
|
}
|
|
)
|
|
|
|
picking_in.action_confirm()
|
|
for move_info in short_move_infos:
|
|
line = picking_in.move_line_ids.filtered(
|
|
lambda x: x.product_id.id == move_info.product.id
|
|
)
|
|
if line:
|
|
line.result_package_id = move_info.package_id
|
|
return picking_in
|
|
|
|
def _process_picking(self, picking):
|
|
picking.action_assign()
|
|
for line in picking.move_line_ids:
|
|
line.qty_done = line.product_qty
|
|
picking.button_validate()
|
|
|
|
def test_00(self):
|
|
"""
|
|
Data:
|
|
location_1 without package_restriction
|
|
location_1 with 50 product_1 and pack_1
|
|
Test case:
|
|
Add qty of product_2 into location_1 with pack_2
|
|
Expected result:
|
|
The location contains the 2 products in 2 different packages
|
|
"""
|
|
# Inventory Add product_1 to location_1
|
|
self._change_product_qty(self.product_1, self.location_1, self.pack_1, 50)
|
|
self.assertEqual(self.pack_1, self._get_package_in_location(self.location_1))
|
|
|
|
self._change_product_qty(self.product_2, self.location_1, self.pack_2, 10)
|
|
self.assertEqual(
|
|
self.pack_1 | self.pack_2,
|
|
self._get_package_in_location(self.location_1),
|
|
)
|
|
|
|
def test_01(self):
|
|
"""
|
|
Data:
|
|
location_1 with single package restriction
|
|
location_1 with 50 product_1 and pack_1
|
|
Test case:
|
|
Add qty of product_2 into location_1 with pack_2
|
|
Expected result:
|
|
ValidationError
|
|
"""
|
|
# Inventory Add product_1 to location_1
|
|
self._change_product_qty(self.product_1, self.location_1, self.pack_1, 50)
|
|
self.assertEqual(self.pack_1, self._get_package_in_location(self.location_1))
|
|
self.location_1.package_restriction = SINGLEPACKAGE
|
|
with self.assertRaises(ValidationError):
|
|
self._change_product_qty(self.product_2, self.location_1, self.pack_2, 10)
|
|
|
|
def test_02(self):
|
|
"""
|
|
Data:
|
|
location_2 without product nor product restriction
|
|
a picking with two move with destination location_2
|
|
Test case:
|
|
Process the picking
|
|
Expected result:
|
|
The two packages are on location 2
|
|
"""
|
|
self.location_2.package_restriction = MULTIPACKAGE
|
|
picking = self._create_and_assign_picking(
|
|
[
|
|
ShortMoveInfo(
|
|
product=self.product_1,
|
|
location_dest=self.location_2,
|
|
qty=2,
|
|
package_id=self.pack_1,
|
|
),
|
|
ShortMoveInfo(
|
|
product=self.product_2,
|
|
location_dest=self.location_2,
|
|
qty=4,
|
|
package_id=self.pack_2,
|
|
),
|
|
],
|
|
location_dest=self.location_2,
|
|
)
|
|
self._process_picking(picking)
|
|
self.assertEqual(
|
|
self.pack_1 | self.pack_2,
|
|
self._get_package_in_location(self.location_2),
|
|
)
|
|
|
|
def test_03(self):
|
|
"""
|
|
Data:
|
|
location_2 without package but with package restriction = 'single package'
|
|
a picking with two move with destination location_2
|
|
Test case:
|
|
Process the picking
|
|
Expected result:
|
|
ValidationError
|
|
"""
|
|
self.location_2.package_restriction = SINGLEPACKAGE
|
|
picking = self._create_and_assign_picking(
|
|
[
|
|
ShortMoveInfo(
|
|
product=self.product_1,
|
|
location_dest=self.location_2,
|
|
qty=2,
|
|
package_id=self.pack_1,
|
|
),
|
|
ShortMoveInfo(
|
|
product=self.product_2,
|
|
location_dest=self.location_2,
|
|
qty=2,
|
|
package_id=self.pack_2,
|
|
),
|
|
],
|
|
location_dest=self.location_2,
|
|
)
|
|
with self.assertRaises(ValidationError):
|
|
self._process_picking(picking)
|
|
|
|
def test_04(self):
|
|
"""
|
|
Data:
|
|
location_1 with product_1 and without product restriction = 'single package'
|
|
a picking with two moves:
|
|
* product_1 -> location_1,
|
|
* product_2 -> location_1
|
|
Test case:
|
|
Process the picking
|
|
Expected result:
|
|
We now have two products into the same location
|
|
"""
|
|
# Inventory Add product_1 to location_1
|
|
self._change_product_qty(self.product_1, self.location_1, self.pack_1, 50)
|
|
self.assertEqual(
|
|
self.pack_1,
|
|
self._get_package_in_location(self.location_1),
|
|
)
|
|
self.location_1.package_restriction = NORESTRICTION
|
|
picking = self._create_and_assign_picking(
|
|
[
|
|
ShortMoveInfo(
|
|
product=self.product_1,
|
|
location_dest=self.location_1,
|
|
qty=2,
|
|
package_id=self.pack_1,
|
|
),
|
|
ShortMoveInfo(
|
|
product=self.product_2,
|
|
location_dest=self.location_1,
|
|
qty=2,
|
|
package_id=self.pack_2,
|
|
),
|
|
],
|
|
location_dest=self.location_1,
|
|
)
|
|
self._process_picking(picking)
|
|
self.assertEqual(
|
|
self.pack_1 | self.pack_2,
|
|
self._get_package_in_location(self.location_1),
|
|
)
|
|
|
|
def test_05(self):
|
|
"""
|
|
Data:
|
|
location_1 with product_1 but with product restriction = 'single package'
|
|
a picking with one move: product_2 -> location_1
|
|
Test case:
|
|
Process the picking
|
|
Expected result:
|
|
ValidationError
|
|
"""
|
|
# Inventory Add product_1 to location_1
|
|
self._change_product_qty(self.product_1, self.location_1, self.pack_1, 50)
|
|
self.assertEqual(
|
|
self.pack_1,
|
|
self._get_package_in_location(self.location_1),
|
|
)
|
|
self.location_1.package_restriction = SINGLEPACKAGE
|
|
picking = self._create_and_assign_picking(
|
|
[
|
|
ShortMoveInfo(
|
|
product=self.product_2,
|
|
location_dest=self.location_1,
|
|
qty=2,
|
|
package_id=self.pack_2,
|
|
),
|
|
],
|
|
location_dest=self.location_1,
|
|
)
|
|
with self.assertRaises(ValidationError):
|
|
self._process_picking(picking)
|