[IMP] stock_picking: code cleaning

Method _get_top_level_packages is moved to the quant model. Some code cleaning
is also performed in picking in methods computing pack operations.
This commit is contained in:
Thibault Delavallée
2016-04-29 12:18:49 +02:00
parent 20223fe920
commit 0d0f96ddcd
2 changed files with 54 additions and 99 deletions
+31 -99
View File
@@ -487,55 +487,13 @@ class Picking(models.Model):
self.action_assign()
self.do_prepare_partial()
@api.model
def _get_top_level_packages(self, quants, quant_to_locations):
"""This method searches for the higher level packages that can be moved as a single operation, given a list of quants
to move and their suggested destination, and returns the list of matching packages.
"""
# TDE FIXME: this method uses shitty arguments
# TDE CLEAME while loop could be simpliied with pack.parent_id + all_in check
# Try to find as much as possible top-level packages that can be moved
top_lvl_packages = self.env['stock.quant.package']
for pack in set(quant.package_id for quant in quants if quant.package_id):
loop = True
test_pack = pack
good_pack = False
pack_destination = False
while loop:
all_in = True
for quant in test_pack.get_content():
# If the quant is not in the quants to compare and not in the common location
if not quant in quants:
all_in = False
break
else:
#if putaway strat apply, the destination location of each quant may be different (and thus the package should not be taken as a single operation)
if not pack_destination:
pack_destination = quant_to_locations[quant]
elif pack_destination != quant_to_locations[quant]:
all_in = False
break
if all_in:
good_pack = test_pack
if test_pack.parent_id:
test_pack = test_pack.parent_id
else:
#stop the loop when there's no parent package anymore
loop = False
else:
#stop the loop when the package test_pack is not totally reserved for moves of this picking
#(some quants may be reserved for other picking or not reserved at all)
loop = False
if good_pack:
top_lvl_packages |= good_pack
return top_lvl_packages
def _prepare_pack_ops(self, quants, forced_qties):
""" Prepare pack_operations, returns a list of dict to give at create """
# TDE CLEANME: oh dear ...
valid_quants = quants.filtered(lambda quant: quant.qty > 0)
_Mapping = namedtuple('Mapping', ('product', 'package', 'owner', 'location', 'location_dst_id'))
all_products = quants.mapped('product_id') | self.env['product.product'].browse(p.id for p in forced_qties.keys()) | self.move_lines.mapped('product_id')
all_products = valid_quants.mapped('product_id') | self.env['product.product'].browse(p.id for p in forced_qties.keys()) | self.move_lines.mapped('product_id')
computed_putaway_locations = dict(
(product, self.location_dest_id.get_putaway_strategy(product) or self.location_dest_id.id) for product in all_products)
@@ -550,90 +508,64 @@ class Picking(models.Model):
if len(picking_moves.mapped('location_dest_id')) > 1:
raise UserError(_('The destination location must be the same for all the moves of the picking.'))
vals = []
qtys_grouped = {}
lots_grouped = {}
# for each quant of the picking, find the suggested location
quants_suggested_locations = {}
# product_putaway_strats = {}
for quant in quants:
if quant.qty <= 0:
continue
suggested_location_id = computed_putaway_locations[quant.product_id]
quants_suggested_locations[quant] = suggested_location_id
# find the packages we can movei as a whole
top_lvl_packages = self._get_top_level_packages(quants_suggested_locations.keys(), quants_suggested_locations)
# and then create pack operations for the top-level packages found
pack_operation_values = []
# find the packages we can move as a whole, create pack operations and mark related quants as done
top_lvl_packages = valid_quants._get_top_level_packages(computed_putaway_locations)
for pack in top_lvl_packages:
pack_quants = pack.get_content()
vals.append({
pack_operation_values.append({
'picking_id': self.id,
'package_id': pack.id,
'product_qty': 1.0,
'location_id': pack.location_id.id,
'location_dest_id': quants_suggested_locations[pack_quants[0]],
'location_dest_id': computed_putaway_locations[pack_quants[0].product_id],
'owner_id': pack.owner_id.id,
})
# remove the quants inside the package so that they are excluded from the rest of the computation
for quant in pack_quants:
del quants_suggested_locations[quant]
valid_quants -= pack_quants
# Go through all remaining reserved quants and group by product, package, owner, source location and dest location
# Lots will go into pack operation lot object
for quant, dest_location_id in quants_suggested_locations.items():
key = _Mapping(quant.product_id, quant.package_id, quant.owner_id, quant.location_id, dest_location_id)
if qtys_grouped.get(key):
qtys_grouped[key] += quant.qty
else:
qtys_grouped[key] = quant.qty
qtys_grouped = {}
lots_grouped = {}
for quant in valid_quants:
key = _Mapping(quant.product_id, quant.package_id, quant.owner_id, quant.location_id, computed_putaway_locations[quant.product_id])
qtys_grouped.setdefault(key, 0.0)
qtys_grouped[key] += quant.qty
if quant.product_id.tracking != 'none' and quant.lot_id:
lots_grouped.setdefault(key, {}).setdefault(quant.lot_id.id, 0.0)
lots_grouped.setdefault(key, dict()).setdefault(quant.lot_id.id, 0.0)
lots_grouped[key][quant.lot_id.id] += quant.qty
# Do the same for the forced quantities (in cases of force_assign or incomming shipment for example)
for product, qty in forced_qties.items():
if qty <= 0:
if qty <= 0.0:
continue
suggested_location_id = computed_putaway_locations[product]
key = _Mapping(product, self.env['stock.quant.package'], self.owner_id, self.location_id, suggested_location_id)
if qtys_grouped.get(key):
qtys_grouped[key] += qty
else:
qtys_grouped[key] = qty
key = _Mapping(product, self.env['stock.quant.package'], self.owner_id, self.location_id, computed_putaway_locations[product])
qtys_grouped.setdefault(key, 0.0)
qtys_grouped[key] += qty
# Create the necessary operations for the grouped quants and remaining qtys
Uom = self.env['product.uom']
prevals = {}
product_id_to_vals = {} # use it to create operations using the same order as the picking stock moves
for mapping, qty in qtys_grouped.items():
# product = self.env["product.product"].browse(key[0])
uom = product_to_uom[mapping.product.id]
qty_uom = Uom._compute_qty_obj(mapping.product.uom_id, qty, uom)
pack_lot_ids = []
if lots_grouped.get(mapping):
for lot in lots_grouped[mapping].keys():
pack_lot_ids += [(0, 0, {'lot_id': lot, 'qty': 0.0, 'qty_todo': lots_grouped[mapping][lot]})]
val_dict = {
'picking_id': self.id,
'product_qty': qty_uom,
'product_qty': Uom._compute_qty_obj(mapping.product.uom_id, qty, uom),
'product_id': mapping.product.id,
'package_id': mapping.package.id,
'owner_id': mapping.owner.id,
'location_id': mapping.location.id,
'location_dest_id': mapping.location_dst_id,
'product_uom_id': uom.id,
'pack_lot_ids': pack_lot_ids,
'pack_lot_ids': [
(0, 0, {'lot_id': lot, 'qty': 0.0, 'qty_todo': lots_grouped[mapping][lot]})
for lot in lots_grouped.get(mapping, {}).keys()],
}
if mapping.product.id in prevals:
prevals[mapping.product.id].append(val_dict)
else:
prevals[mapping.product.id] = [val_dict]
# prevals var holds the operations in order to create them in the same order than the picking stock moves if possible
processed_products = set()
for move in [x for x in self.move_lines if x.state not in ('done', 'cancel')]:
if move.product_id.id not in processed_products:
vals += prevals.get(move.product_id.id, [])
processed_products.add(move.product_id.id)
return vals
product_id_to_vals.setdefault(mapping.product.id, list()).append(val_dict)
for move in self.move_lines.filtered(lambda move: move.state not in ('done', 'cancel')):
values = product_id_to_vals.pop(move.product_id.id, [])
pack_operation_values += values
return pack_operation_values
@api.multi
def do_prepare_partial(self):
+23
View File
@@ -490,6 +490,29 @@ class Quant(models.Model):
# Misc tools
# ----------------------------------------------------------------------
def _get_top_level_packages(self, product_to_location):
""" This method searches for as much possible higher level packages that
can be moved as a single operation, given a list of quants to move and
their suggested destination, and returns the list of matching packages. """
top_lvl_packages = self.env['stock.quant.package']
for package in self.mapped('package_id'):
all_in = True
top_package = self.env['stock.quant.package']
while package:
if any(quant not in self for quant in package.get_content()):
all_in = False
if all_in:
destinations = [product_to_location[product] for product in package.get_content().mapped('product_id')]
if len(destinations) > 1:
all_in = False
if all_in:
top_package = package
package = package.parent_id
else:
package = False
top_lvl_packages |= top_package
return top_lvl_packages
@api.multi
def _get_latest_move(self):
latest_move = self.history_ids[0]