mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[14.0][IMP] stock_move_location: packages support
This commit is contained in:
committed by
Ethan Hildick
parent
fe69537172
commit
321c93e961
@@ -10,3 +10,4 @@
|
|||||||
* João Marques
|
* João Marques
|
||||||
* Jacques-Etienne Baudoux <je@bcim.be>
|
* Jacques-Etienne Baudoux <je@bcim.be>
|
||||||
* Iryna Vyshnevska <i.vyshnevska@mobilunity.com>
|
* Iryna Vyshnevska <i.vyshnevska@mobilunity.com>
|
||||||
|
* Abraham Anes <abraham@studio73.es>
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ class TestsCommon(common.SavepointCase):
|
|||||||
cls.product_lots = product_obj.create(
|
cls.product_lots = product_obj.create(
|
||||||
{"name": "Apple", "type": "product", "tracking": "lot"}
|
{"name": "Apple", "type": "product", "tracking": "lot"}
|
||||||
)
|
)
|
||||||
|
cls.product_package = product_obj.create(
|
||||||
|
{"name": "Orange", "type": "product", "tracking": "lot"}
|
||||||
|
)
|
||||||
cls.lot1 = cls.env["stock.production.lot"].create(
|
cls.lot1 = cls.env["stock.production.lot"].create(
|
||||||
{
|
{
|
||||||
"name": "lot1",
|
"name": "lot1",
|
||||||
@@ -69,6 +72,14 @@ class TestsCommon(common.SavepointCase):
|
|||||||
"company_id": cls.company.id,
|
"company_id": cls.company.id,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
cls.lot4 = cls.env["stock.production.lot"].create(
|
||||||
|
{
|
||||||
|
"name": "lot4",
|
||||||
|
"product_id": cls.product_package.id,
|
||||||
|
"company_id": cls.company.id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
cls.package = cls.env["stock.quant.package"].create({})
|
||||||
|
|
||||||
def setup_product_amounts(self):
|
def setup_product_amounts(self):
|
||||||
self.set_product_amount(self.product_no_lots, self.internal_loc_1, 123)
|
self.set_product_amount(self.product_no_lots, self.internal_loc_1, 123)
|
||||||
@@ -81,16 +92,27 @@ class TestsCommon(common.SavepointCase):
|
|||||||
self.set_product_amount(
|
self.set_product_amount(
|
||||||
self.product_lots, self.internal_loc_1, 1.0, lot_id=self.lot3
|
self.product_lots, self.internal_loc_1, 1.0, lot_id=self.lot3
|
||||||
)
|
)
|
||||||
|
self.set_product_amount(
|
||||||
def set_product_amount(self, product, location, amount, lot_id=None):
|
self.product_package,
|
||||||
self.env["stock.quant"]._update_available_quantity(
|
self.internal_loc_1,
|
||||||
product, location, amount, lot_id=lot_id
|
1.0,
|
||||||
|
lot_id=self.lot4,
|
||||||
|
package_id=self.package,
|
||||||
)
|
)
|
||||||
|
|
||||||
def check_product_amount(self, product, location, amount, lot_id=None):
|
def set_product_amount(
|
||||||
|
self, product, location, amount, lot_id=None, package_id=None
|
||||||
|
):
|
||||||
|
self.env["stock.quant"]._update_available_quantity(
|
||||||
|
product, location, amount, lot_id=lot_id, package_id=package_id
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_product_amount(
|
||||||
|
self, product, location, amount, lot_id=None, package_id=None
|
||||||
|
):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.env["stock.quant"]._get_available_quantity(
|
self.env["stock.quant"]._get_available_quantity(
|
||||||
product, location, lot_id=lot_id
|
product, location, lot_id=lot_id, package_id=package_id
|
||||||
),
|
),
|
||||||
amount,
|
amount,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,10 +21,16 @@ class TestMoveLocation(TestsCommon):
|
|||||||
self.check_product_amount(self.product_lots, self.internal_loc_1, 0, self.lot1)
|
self.check_product_amount(self.product_lots, self.internal_loc_1, 0, self.lot1)
|
||||||
self.check_product_amount(self.product_lots, self.internal_loc_1, 0, self.lot2)
|
self.check_product_amount(self.product_lots, self.internal_loc_1, 0, self.lot2)
|
||||||
self.check_product_amount(self.product_lots, self.internal_loc_1, 0, self.lot3)
|
self.check_product_amount(self.product_lots, self.internal_loc_1, 0, self.lot3)
|
||||||
|
self.check_product_amount(
|
||||||
|
self.product_package, self.internal_loc_1, 0, self.lot4, self.package
|
||||||
|
)
|
||||||
self.check_product_amount(self.product_no_lots, self.internal_loc_2, 123)
|
self.check_product_amount(self.product_no_lots, self.internal_loc_2, 123)
|
||||||
self.check_product_amount(self.product_lots, self.internal_loc_2, 1, self.lot1)
|
self.check_product_amount(self.product_lots, self.internal_loc_2, 1, self.lot1)
|
||||||
self.check_product_amount(self.product_lots, self.internal_loc_2, 1, self.lot2)
|
self.check_product_amount(self.product_lots, self.internal_loc_2, 1, self.lot2)
|
||||||
self.check_product_amount(self.product_lots, self.internal_loc_2, 1, self.lot3)
|
self.check_product_amount(self.product_lots, self.internal_loc_2, 1, self.lot3)
|
||||||
|
self.check_product_amount(
|
||||||
|
self.product_package, self.internal_loc_2, 1, self.lot4, self.package
|
||||||
|
)
|
||||||
|
|
||||||
def test_move_location_wizard_amount(self):
|
def test_move_location_wizard_amount(self):
|
||||||
"""Can't move more than exists."""
|
"""Can't move more than exists."""
|
||||||
@@ -54,9 +60,9 @@ class TestMoveLocation(TestsCommon):
|
|||||||
"""Test lines getting cleared properly."""
|
"""Test lines getting cleared properly."""
|
||||||
wizard = self._create_wizard(self.internal_loc_1, self.internal_loc_2)
|
wizard = self._create_wizard(self.internal_loc_1, self.internal_loc_2)
|
||||||
wizard.onchange_origin_location()
|
wizard.onchange_origin_location()
|
||||||
self.assertEqual(len(wizard.stock_move_location_line_ids), 4)
|
self.assertEqual(len(wizard.stock_move_location_line_ids), 5)
|
||||||
wizard._onchange_destination_location_id()
|
wizard._onchange_destination_location_id()
|
||||||
self.assertEqual(len(wizard.stock_move_location_line_ids), 4)
|
self.assertEqual(len(wizard.stock_move_location_line_ids), 5)
|
||||||
dest_location_line = wizard.stock_move_location_line_ids.mapped(
|
dest_location_line = wizard.stock_move_location_line_ids.mapped(
|
||||||
"destination_location_id"
|
"destination_location_id"
|
||||||
)
|
)
|
||||||
@@ -72,9 +78,9 @@ class TestMoveLocation(TestsCommon):
|
|||||||
wizard.action_move_location()
|
wizard.action_move_location()
|
||||||
picking = wizard.picking_id
|
picking = wizard.picking_id
|
||||||
self.assertEqual(picking.state, "assigned")
|
self.assertEqual(picking.state, "assigned")
|
||||||
self.assertEqual(len(picking.move_line_ids), 4)
|
self.assertEqual(len(picking.move_line_ids), 5)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
sorted(picking.move_line_ids.mapped("product_uom_qty")), [1, 1, 1, 123]
|
sorted(picking.move_line_ids.mapped("product_uom_qty")), [1, 1, 1, 1, 123]
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_quant_transfer(self):
|
def test_quant_transfer(self):
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ class StockMoveLocationWizard(models.TransientModel):
|
|||||||
"reserved_quantity": quant.reserved_quantity,
|
"reserved_quantity": quant.reserved_quantity,
|
||||||
"origin_location_id": quant.location_id.id,
|
"origin_location_id": quant.location_id.id,
|
||||||
"lot_id": quant.lot_id.id,
|
"lot_id": quant.lot_id.id,
|
||||||
|
"package_id": quant.package_id.id,
|
||||||
"product_uom_id": quant.product_uom_id.id,
|
"product_uom_id": quant.product_uom_id.id,
|
||||||
"custom": False,
|
"custom": False,
|
||||||
},
|
},
|
||||||
@@ -131,6 +132,7 @@ class StockMoveLocationWizard(models.TransientModel):
|
|||||||
"reserved_quantity": quant.reserved_quantity,
|
"reserved_quantity": quant.reserved_quantity,
|
||||||
"origin_location_id": quant.location_id.id,
|
"origin_location_id": quant.location_id.id,
|
||||||
"lot_id": quant.lot_id.id,
|
"lot_id": quant.lot_id.id,
|
||||||
|
"package_id": quant.package_id.id,
|
||||||
"product_uom_id": quant.product_uom_id.id,
|
"product_uom_id": quant.product_uom_id.id,
|
||||||
"custom": False,
|
"custom": False,
|
||||||
},
|
},
|
||||||
@@ -239,6 +241,7 @@ class StockMoveLocationWizard(models.TransientModel):
|
|||||||
("product_id", "=", line.product_id.id),
|
("product_id", "=", line.product_id.id),
|
||||||
("location_id", "=", line.origin_location_id.id),
|
("location_id", "=", line.origin_location_id.id),
|
||||||
("lot_id", "=", line.lot_id.id),
|
("lot_id", "=", line.lot_id.id),
|
||||||
|
("package_id", "=", line.package_id.id),
|
||||||
("product_uom_qty", ">", 0.0),
|
("product_uom_qty", ">", 0.0),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -280,11 +283,11 @@ class StockMoveLocationWizard(models.TransientModel):
|
|||||||
# Using sql as search_group doesn't support aggregation functions
|
# Using sql as search_group doesn't support aggregation functions
|
||||||
# leading to overhead in queries to DB
|
# leading to overhead in queries to DB
|
||||||
query = """
|
query = """
|
||||||
SELECT product_id, lot_id, SUM(quantity) AS quantity,
|
SELECT product_id, lot_id, package_id, SUM(quantity) AS quantity,
|
||||||
SUM(reserved_quantity) AS reserved_quantity
|
SUM(reserved_quantity) AS reserved_quantity
|
||||||
FROM stock_quant
|
FROM stock_quant
|
||||||
WHERE location_id = %s
|
WHERE location_id = %s
|
||||||
GROUP BY product_id, lot_id
|
GROUP BY product_id, lot_id, package_id
|
||||||
"""
|
"""
|
||||||
self.env.cr.execute(query, (location_id.id,))
|
self.env.cr.execute(query, (location_id.id,))
|
||||||
return self.env.cr.dictfetchall()
|
return self.env.cr.dictfetchall()
|
||||||
@@ -310,6 +313,7 @@ class StockMoveLocationWizard(models.TransientModel):
|
|||||||
"destination_location_id": location_dest_id,
|
"destination_location_id": location_dest_id,
|
||||||
# cursor returns None instead of False
|
# cursor returns None instead of False
|
||||||
"lot_id": group.get("lot_id") or False,
|
"lot_id": group.get("lot_id") or False,
|
||||||
|
"package_id": group.get("package_id") or False,
|
||||||
"product_uom_id": product.uom_id.id,
|
"product_uom_id": product.uom_id.id,
|
||||||
"custom": False,
|
"custom": False,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,6 +77,11 @@
|
|||||||
groups="stock.group_production_lot"
|
groups="stock.group_production_lot"
|
||||||
options="{'no_create': True}"
|
options="{'no_create': True}"
|
||||||
/>
|
/>
|
||||||
|
<field
|
||||||
|
name="package_id"
|
||||||
|
domain="[('location_id', '=', origin_location_id)]"
|
||||||
|
options="{'no_create': True}"
|
||||||
|
/>
|
||||||
<field name="move_quantity" />
|
<field name="move_quantity" />
|
||||||
<field name="custom" invisible="1" />
|
<field name="custom" invisible="1" />
|
||||||
<field
|
<field
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ class StockMoveLocationWizardLine(models.TransientModel):
|
|||||||
comodel_name="stock.production.lot",
|
comodel_name="stock.production.lot",
|
||||||
domain="[('product_id','=',product_id)]",
|
domain="[('product_id','=',product_id)]",
|
||||||
)
|
)
|
||||||
|
package_id = fields.Many2one(
|
||||||
|
string="Package Number",
|
||||||
|
comodel_name="stock.quant.package",
|
||||||
|
domain="[('location_id', '=', origin_location_id)]",
|
||||||
|
)
|
||||||
move_quantity = fields.Float(
|
move_quantity = fields.Float(
|
||||||
string="Quantity to move", digits="Product Unit of Measure"
|
string="Quantity to move", digits="Product Unit of Measure"
|
||||||
)
|
)
|
||||||
@@ -73,6 +78,10 @@ class StockMoveLocationWizardLine(models.TransientModel):
|
|||||||
search_args.append(("lot_id", "=", self.lot_id.id))
|
search_args.append(("lot_id", "=", self.lot_id.id))
|
||||||
else:
|
else:
|
||||||
search_args.append(("lot_id", "=", False))
|
search_args.append(("lot_id", "=", False))
|
||||||
|
if self.package_id:
|
||||||
|
search_args.append(("package_id", "=", self.package_id.id))
|
||||||
|
else:
|
||||||
|
search_args.append(("package_id", "=", False))
|
||||||
res = self.env["stock.quant"].read_group(search_args, ["quantity"], [])
|
res = self.env["stock.quant"].read_group(search_args, ["quantity"], [])
|
||||||
max_quantity = res[0]["quantity"]
|
max_quantity = res[0]["quantity"]
|
||||||
return max_quantity
|
return max_quantity
|
||||||
@@ -96,6 +105,8 @@ class StockMoveLocationWizardLine(models.TransientModel):
|
|||||||
return {
|
return {
|
||||||
"product_id": self.product_id.id,
|
"product_id": self.product_id.id,
|
||||||
"lot_id": self.lot_id.id,
|
"lot_id": self.lot_id.id,
|
||||||
|
"package_id": self.package_id.id,
|
||||||
|
"result_package_id": self.package_id.id,
|
||||||
"location_id": self.origin_location_id.id,
|
"location_id": self.origin_location_id.id,
|
||||||
"location_dest_id": location_dest_id,
|
"location_dest_id": location_dest_id,
|
||||||
"product_uom_qty": qty_todo,
|
"product_uom_qty": qty_todo,
|
||||||
@@ -124,6 +135,10 @@ class StockMoveLocationWizardLine(models.TransientModel):
|
|||||||
search_args.append(("lot_id", "=", self.lot_id.id))
|
search_args.append(("lot_id", "=", self.lot_id.id))
|
||||||
else:
|
else:
|
||||||
search_args.append(("lot_id", "=", False))
|
search_args.append(("lot_id", "=", False))
|
||||||
|
if self.package_id:
|
||||||
|
search_args.append(("package_id", "=", self.package_id.id))
|
||||||
|
else:
|
||||||
|
search_args.append(("package_id", "=", False))
|
||||||
res = self.env["stock.quant"].read_group(search_args, ["quantity"], [])
|
res = self.env["stock.quant"].read_group(search_args, ["quantity"], [])
|
||||||
available_qty = res[0]["quantity"]
|
available_qty = res[0]["quantity"]
|
||||||
if not available_qty:
|
if not available_qty:
|
||||||
|
|||||||
Reference in New Issue
Block a user