diff --git a/addons/sale_stock/test/cancel_order_sale_stock.yml b/addons/sale_stock/test/cancel_order_sale_stock.yml
index d9116d843f5..77508e28a2d 100644
--- a/addons/sale_stock/test/cancel_order_sale_stock.yml
+++ b/addons/sale_stock/test/cancel_order_sale_stock.yml
@@ -20,7 +20,9 @@
'picking_id': pick.id,
'product_id': ref('product.product_product_27'),
'product_uom_id': ref('product.product_uom_unit'),
- 'product_qty': 1
+ 'product_qty': 1,
+ 'location_id': pick.location_id.id,
+ 'location_dest_id': pick.location_dest_id.id,
})
pick.do_transfer(context=context)
-
diff --git a/addons/stock/report/picking.py b/addons/stock/report/picking.py
index ce901a8b92e..a82e72766ac 100644
--- a/addons/stock/report/picking.py
+++ b/addons/stock/report/picking.py
@@ -68,24 +68,25 @@ class picking(report_sxw.rml_parse):
def get_internal_picking_dest_lines(self, picking):
res = []
- for line in picking.move_lines:
- row = {'description': self.get_product_desc(line)}
- if not line.putaway_ids:
- row['quantity'] = line.product_uom_qty
- row['uom'] = line.product_uom.name
- row['location_id'] = line.location_dest_id.name
- row['barcode'] = line.product_id.ean13
- row['lot_id'] = ''
- res.append(row)
- else:
- for rec in line.putaway_ids:
- row2 = row.copy()
- row2['quantity'] = rec.quantity
- row2['uom'] = line.product_id.uom_id.name
- row2['location_id'] = rec.location_id.name
- row2['lot_id'] = rec.lot_id and rec.lot_id.name or ''
- row2['barcode'] = rec.lot_id and rec.lot_id.name or line.product_id.ean13
- res.append(row2)
+ #TODO: rewrite according to pack ops
+# for line in picking.move_lines:
+# row = {'description': self.get_product_desc(line)}
+# if not line.putaway_ids:
+# row['quantity'] = line.product_uom_qty
+# row['uom'] = line.product_uom.name
+# row['location_id'] = line.location_dest_id.name
+# row['barcode'] = line.product_id.ean13
+# row['lot_id'] = ''
+# res.append(row)
+# else:
+# for rec in line.putaway_ids:
+# row2 = row.copy()
+# row2['quantity'] = rec.quantity
+# row2['uom'] = line.product_id.uom_id.name
+# row2['location_id'] = rec.location_id.name
+# row2['lot_id'] = rec.lot_id and rec.lot_id.name or ''
+# row2['barcode'] = rec.lot_id and rec.lot_id.name or line.product_id.ean13
+# res.append(row2)
return res
report_sxw.report_sxw('report.stock.picking.list', 'stock.picking', 'addons/stock/report/picking.rml', parser=picking)
diff --git a/addons/stock/security/ir.model.access.csv b/addons/stock/security/ir.model.access.csv
index 58a754d283c..9312527c4a4 100644
--- a/addons/stock/security/ir.model.access.csv
+++ b/addons/stock/security/ir.model.access.csv
@@ -66,7 +66,6 @@ access_procurement_rule_internal,procurement.rule.flow internal,model_procuremen
access_stock_pack_operation_manager,stock.pack.operation manager,model_stock_pack_operation,stock.group_stock_manager,1,1,1,1
access_stock_pack_operation_user,stock.pack.operation user,model_stock_pack_operation,stock.group_stock_user,1,1,1,1
access_stock_pack_operation_all,stock.pack.operation all users,model_stock_pack_operation,base.group_user,1,0,0,0
-access_stock_putaway_all,stock.putaway all users,model_stock_move_putaway,base.group_user,1,0,0,0
access_product_putaway_all,product.putaway all users,model_product_putaway,base.group_user,1,0,0,0
access_stock_removal_all,product.removal all users,model_product_removal,base.group_user,1,0,0,0
access_stock_move_operation_link_manager,stock.move.operation.link manager,model_stock_move_operation_link,stock.group_stock_manager,1,1,1,1
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index 1e83b9d4dae..00f084f4cc1 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -257,7 +257,7 @@ class stock_quant(osv.osv):
'propagated_from_id': fields.many2one('stock.quant', 'Linked Quant', help='The negative quant this is coming from'),
'negative_move_id': fields.many2one('stock.move', 'Move Negative Quant', help='If this is a negative quant, this will be the move that caused this negative quant.'),
'negative_dest_location_id': fields.related('negative_move_id', 'location_dest_id', type='many2one', relation='stock.location', string="Negative Destination Location",
- help="Technical field used to record the destination location of a move that created a negative quant"),
+ help="Technical field used to record the destination location of a move that created a negative quant"),
'inventory_value': fields.function(_calc_inventory_value, string="Inventory Value", type='float', readonly=True),
}
@@ -322,12 +322,13 @@ class stock_quant(osv.osv):
if move.reserved_availability == move.product_qty and move.state in ('confirmed', 'waiting'):
self.pool.get('stock.move').write(cr, uid, [move.id], {'state': 'assigned'}, context=context)
- def quants_move(self, cr, uid, quants, move, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False, context=None):
- """Moves all given stock.quant in the destination location of the given move.
+ def quants_move(self, cr, uid, quants, move, location_to, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False, context=None):
+ """Moves all given stock.quant in the given destination location.
:param quants: list of tuple(browse record(stock.quant) or None, quantity to move)
:param move: browse record (stock.move)
- :param lot_id: ID of the lot that mus be set on the quants to move
+ :param location_to: browse record (stock.location) depicting where the quants have to be moved
+ :param lot_id: ID of the lot that must be set on the quants to move
:param owner_id: ID of the partner that must own the quants to move
:param src_package_id: ID of the package that contains the quants to move
:param dest_package_id: ID of the package that must be set on the moved quant
@@ -335,23 +336,9 @@ class stock_quant(osv.osv):
for quant, qty in quants:
if not quant:
#If quant is None, we will create a quant to move (and potentially a negative counterpart too)
- quant = self._quant_create(cr, uid, qty, move, lot_id=lot_id, owner_id=owner_id, src_package_id=src_package_id, dest_package_id=dest_package_id, context=context)
- self.move_single_quant_tuple(cr, uid, quant, qty, move, context=context)
-
- def check_preferred_location(self, cr, uid, move, qty, context=None):
- '''Checks the preferred location on the move, if any returned by a putaway strategy, and returns a list of
- tuple(location, qty) where the quant have to be moved
-
- :param move: browse record (stock.move)
- :param qty: float
- :returns: list of tuple build as [(browe record (stock.move), float)]
- '''
- if move.putaway_ids:
- res = []
- for record in move.putaway_ids:
- res.append((record.location_id, record.quantity))
- return res
- return [(move.location_dest_id, qty)]
+ quant = self._quant_create(cr, uid, qty, move, lot_id=lot_id, owner_id=owner_id, src_package_id=src_package_id, dest_package_id=dest_package_id, force_location=location_to, context=context)
+ self.move_single_quant(cr, uid, quant, location_to, qty, move, context=context)
+ self._quant_reconcile_negative(cr, uid, quant, move, context=context)
def move_single_quant(self, cr, uid, quant, location_to, qty, move, context=None):
'''Moves the given 'quant' in 'location_to' for the given 'qty', and logs the stock.move that triggered this move in the quant history.
@@ -374,20 +361,6 @@ class stock_quant(osv.osv):
quant.refresh()
return new_quant
- def move_single_quant_tuple(self, cr, uid, quant, qty, move, context=None):
- '''Effectively process the move of a tuple (quant record, qty to move). This may result in several quants moved
- if the preferred locations on the move say so but by default it will only move the quant record given as argument
- :param quant: browse record (stock.quant)
- :param qty: float
- :param move: browse record (stock.move)
- '''
- for location_to, qty in self.check_preferred_location(cr, uid, move, qty, context=context):
- if not quant:
- break
- new_quant = self.move_single_quant(cr, uid, quant, location_to, qty, move, context=context)
- self._quant_reconcile_negative(cr, uid, quant, move, context=context)
- quant = new_quant
-
def quants_get_prefered_domain(self, cr, uid, location, product, qty, domain=None, prefered_domain=False, fallback_domain=False, restrict_lot_id=False, restrict_partner_id=False, context=None):
''' This function tries to find quants in the given location for the given domain, by trying to first limit
the choice on the quants that match the prefered_domain as well. But if the qty requested is not reached
@@ -939,104 +912,165 @@ class stock_picking(osv.osv):
self.action_assign(cr, uid, picking_ids, context=context)
self.do_prepare_partial(cr, uid, picking_ids, context=context)
+ def _picking_putaway_resolution(self, cr, uid, picking, product, putaway, context=None):
+ if putaway.method == 'fixed' and putaway.location_spec_id:
+ return putaway.location_spec_id.id
+ return False
+
+ def _get_top_level_packages(self, cr, uid, quants_suggested_locations, context=None):
+ """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.
+ """
+ # Try to find as much as possible top-level packages that can be moved
+ pack_obj = self.pool.get("stock.quant.package")
+ quant_obj = self.pool.get("stock.quant")
+ top_lvl_packages = set()
+ quants_to_compare = quants_suggested_locations.keys()
+ for pack in list(set([x.package_id for x in quants_suggested_locations.keys() if x and x.package_id])):
+ loop = True
+ test_pack = pack
+ good_pack = False
+ pack_destination = False
+ while loop:
+ pack_quants = pack_obj.get_content(cr, uid, [test_pack.id], context=context)
+ all_in = True
+ for quant in quant_obj.browse(cr, uid, pack_quants, context=context):
+ # If the quant is not in the quants to compare and not in the common location
+ if not quant in quants_to_compare:
+ 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 = quants_suggested_locations[quant]
+ elif pack_destination != quants_suggested_locations[quant]:
+ all_in = False
+ break
+ if all_in:
+ good_pack = test_pack.id
+ 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.add(good_pack)
+ return list(top_lvl_packages)
+
+ def _prepare_pack_ops(self, cr, uid, picking, quants, forced_qties, context=None):
+ """ returns a list of dict, ready to be used in create() of stock.pack.operation.
+
+ :param picking: browse record (stock.picking)
+ :param quants: browse record list (stock.quant). List of quants associated to the picking
+ :param forced_qties: dictionary showing for each product (keys) its corresponding quantity (value) that is not covered by the quants associated to the picking
+ """
+ def _picking_putaway_apply(product):
+ location = False
+ # Search putaway strategy
+ if product_putaway_strats.get(product.id):
+ putaway_strat = product_putaway_strats[product.id]
+ else:
+ putaway_strat = self.pool.get('stock.location').get_putaway_strategy(cr, uid, picking.location_dest_id, product, context=context)
+ product_putaway_strats[product.id] = putaway_strat
+ if putaway_strat:
+ location = self._picking_putaway_resolution(cr, uid, picking, product, putaway_strat, context=context)
+ return location or picking.picking_type_id.default_location_dest_id.id or picking.location_dest_id.id
+
+ pack_obj = self.pool.get("stock.quant.package")
+ quant_obj = self.pool.get("stock.quant")
+ vals = []
+ qtys_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 = _picking_putaway_apply(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(cr, uid, quants_suggested_locations, context=context)
+ # and then create pack operations for the top-level packages found
+ for pack in pack_obj.browse(cr, uid, top_lvl_packages, context=context):
+ pack_quants = pack_obj.get_content(cr, uid, [pack.id], context=context)
+ vals.append({
+ 'picking_id': picking.id,
+ 'package_id': pack.id,
+ 'product_qty': 1.0,
+ 'location_id': pack.location_id.id,
+ 'location_dest_id': quants_suggested_locations[quant_obj.browse(cr, uid, pack_quants[0], context=context)],
+ })
+ #remove the quants inside the package so that they are excluded from the rest of the computation
+ for quant in quant_obj.browse(cr, uid, pack_quants, context=context):
+ del quants_suggested_locations[quant]
+
+ # Go through all remaining reserved quants and group by product, package, lot, owner, source location and dest location
+ for quant, dest_location_id in quants_suggested_locations.items():
+ key = (quant.product_id.id, quant.package_id.id, quant.lot_id.id, quant.owner_id.id, quant.location_id.id, dest_location_id)
+ if qtys_grouped.get(key):
+ qtys_grouped[key] += quant.qty
+ else:
+ qtys_grouped[key] = 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:
+ continue
+ suggested_location_id = _picking_putaway_apply(product)
+ key = (product.id, False, False, False, picking.picking_type_id.default_location_src_id.id or picking.location_id.id, suggested_location_id)
+ if qtys_grouped.get(key):
+ qtys_grouped[key] += qty
+ else:
+ qtys_grouped[key] = qty
+
+ # Create the necessary operations for the grouped quants and remaining qtys
+ for key, qty in qtys_grouped.items():
+ vals.append({
+ 'picking_id': picking.id,
+ 'product_qty': qty,
+ 'product_id': key[0],
+ 'package_id': key[1],
+ 'lot_id': key[2],
+ 'owner_id': key[3],
+ 'location_id': key[4],
+ 'location_dest_id': key[5],
+ 'product_uom_id': self.pool.get("product.product").browse(cr, uid, key[0], context=context).uom_id.id,
+ })
+ return vals
+
def do_prepare_partial(self, cr, uid, picking_ids, context=None):
context = context or {}
pack_operation_obj = self.pool.get('stock.pack.operation')
- pack_obj = self.pool.get("stock.quant.package")
- quant_obj = self.pool.get("stock.quant")
+
#get list of existing operations and delete them
existing_package_ids = pack_operation_obj.search(cr, uid, [('picking_id', 'in', picking_ids)], context=context)
if existing_package_ids:
pack_operation_obj.unlink(cr, uid, existing_package_ids, context)
for picking in self.browse(cr, uid, picking_ids, context=context):
- packages = []
- reserved_move = {}
- qtys_remaining = {}
+ forced_qties = {} # Quantity remaining after calculating reserved quants
+ picking_quants = []
#Calculate packages, reserved quants, qtys of this picking's moves
for move in picking.move_lines:
if move.state not in ('assigned', 'confirmed'):
continue
- packages += [x.package_id for x in move.reserved_quant_ids if x.package_id]
- reserved_move[move.id] = set([x.id for x in move.reserved_quant_ids])
- if move.state == 'assigned':
- qty = move.product_qty
- else:
- qty = move.reserved_availability
-
- #Add qty to qtys remaining
- if qtys_remaining.get(move.product_id.id):
- qtys_remaining[move.product_id.id] += qty
- else:
- qtys_remaining[move.product_id.id] = qty
-
- # Try to find as much as possible top-level packages that can be moved
- top_lvl_packages = set()
- for pack in packages:
- loop = True
- good_pack = False
- test_pack = pack
- while loop:
- quants = pack_obj.get_content(cr, uid, [test_pack.id], context=context)
- move_list = [m.id for m in picking.move_lines]
- if all([(x.reservation_id and x.reservation_id.id in move_list or False) for x in quant_obj.browse(cr, uid, quants, context=context)]):
- good_pack = test_pack.id
- if test_pack.parent_id:
- test_pack = test_pack.parent_id
- else:
- #stop the loop when there's no parent package anymore
- loop = False
+ move_quants = move.reserved_quant_ids
+ picking_quants += move_quants
+ forced_qty = (move.state == 'assigned') and move.product_qty - move.reserved_availability or 0
+ #if we used force_assign() on the move, or if the move is incomming, forced_qty > 0
+ if forced_qty:
+ if forced_qties.get(move.product_id):
+ forced_qties[move.product_id] += forced_qty
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.add(good_pack)
-
- # Create pack operations for the top-level packages found
- for pack in pack_obj.browse(cr, uid, list(top_lvl_packages), context=context):
- quants = pack_obj.get_content(cr, uid, [pack.id], context=context)
- for quant in quant_obj.browse(cr, uid, quants, context=context):
- reserved_move[quant.reservation_id.id] -= set([quant.id])
- qtys_remaining[quant.product_id.id] -= quant.qty
- pack_operation_obj.create(cr, uid, {
- 'picking_id': picking.id,
- 'package_id': pack.id,
- 'product_qty': 1.0,
- }, context=context)
-
- # Go through all remaining reserved quants and group by product, package, lot, owner
- qtys_grouped = {}
- for move, quant_set in reserved_move.items():
- for quant in quant_obj.browse(cr, uid, list(quant_set), context=context):
- qtys_remaining[quant.product_id.id] -= quant.qty
- key = (quant.product_id.id, quant.package_id.id, quant.lot_id.id, quant.owner_id.id)
- if qtys_grouped.get(key):
- qtys_grouped[key] += quant.qty
- else:
- qtys_grouped[key] = quant.qty
-
- # Add remaining qtys (in cases of force_assign for example)
- for product in qtys_remaining.keys():
- if qtys_remaining[product] > 0:
- key = (product, False, False, False)
- if qtys_grouped.get(key):
- qtys_grouped[key] += qtys_remaining[product]
- else:
- qtys_grouped[key] = qtys_remaining[product]
-
- # Create the necessary operations for the grouped quants and remaining qtys
- for key, qty in qtys_grouped.items():
- pack_operation_obj.create(cr, uid, {
- 'picking_id': picking.id,
- 'product_qty': qty,
- 'product_id': key[0],
- 'package_id': key[1],
- 'lot_id': key[2],
- 'owner_id': key[3],
- 'product_uom_id': self.pool.get("product.product").browse(cr, uid, key[0], context=context).uom_id.id,
- }, context=context)
+ forced_qties[move.product_id] = forced_qty
+ for vals in self._prepare_pack_ops(cr, uid, picking, picking_quants, forced_qties, context=context):
+ pack_operation_obj.create(cr, uid, vals, context=context)
def do_unreserve(self, cr, uid, picking_ids, context=None):
"""
@@ -1126,6 +1160,8 @@ class stock_picking(osv.osv):
todo_move_ids.append(move.id)
#Assign move as it was assigned before
toassign_move_ids.append(new_move)
+ else:
+ todo_move_ids.append(move.id)
self.rereserve_quants(cr, uid, picking, move_ids=todo_move_ids, context=context)
if todo_move_ids and not context.get('do_only_split'):
self.pool.get('stock.move').action_done(cr, uid, todo_move_ids, context=context)
@@ -1452,7 +1488,6 @@ class stock_move(osv.osv):
'string_availability_info': fields.function(_get_string_qty_information, type='text', string='Availability', readonly=True, help='Show various information on stock availability for this move'),
'restrict_lot_id': fields.many2one('stock.production.lot', 'Lot', help="Technical field used to depict a restriction on the lot of quants to consider when marking this move as 'done'"),
'restrict_partner_id': fields.many2one('res.partner', 'Owner ', help="Technical field used to depict a restriction on the ownership of quants to consider when marking this move as 'done'"),
- 'putaway_ids': fields.one2many('stock.move.putaway', 'move_id', 'Put Away Suggestions'),
'route_ids': fields.many2many('stock.location.route', 'stock_location_route_move', 'move_id', 'route_id', 'Destination route', help="Preferred route to be followed by the procurement order"),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', help="Technical field depicting the warehouse to consider for the route selection on the next procurement (if any)."),
}
@@ -1523,10 +1558,7 @@ class stock_move(osv.osv):
if move.state in ('done', 'cancel'):
raise osv.except_osv(_('Operation Forbidden!'), _('Cannot unreserve a done move'))
quant_obj.quants_unreserve(cr, uid, move, context=context)
- putaway_values = []
- for putaway_rec in move.putaway_ids:
- putaway_values.append((2, putaway_rec.id))
- self.write(cr, uid, [move.id], {'state': 'confirmed', 'putaway_ids': putaway_values}, context=context)
+ self.write(cr, uid, [move.id], {'state': 'confirmed'}, context=context)
def _prepare_procurement_from_move(self, cr, uid, move, context=None):
origin = (move.group_id and (move.group_id.name + ":") or "") + (move.rule_id and move.rule_id.name or "/")
@@ -1574,38 +1606,6 @@ class stock_move(osv.osv):
push_obj._apply(cr, uid, rule, move, context=context)
return True
- # Create the stock.move.putaway records
- def _putaway_apply(self, cr, uid, move, putaway, context=None):
- # Should call different methods here in later versions
- moveputaway_obj = self.pool.get('stock.move.putaway')
- quant_obj = self.pool.get('stock.quant')
- if putaway.method == 'fixed' and putaway.location_spec_id:
- qty = move.product_qty
- for row in quant_obj.read_group(cr, uid, [('reservation_id', '=', move.id)], ['qty', 'lot_id'], ['lot_id'], context=context):
- vals = {
- 'move_id': move.id,
- 'location_id': putaway.location_spec_id.id,
- 'quantity': row['qty'],
- 'lot_id': row.get('lot_id') and row['lot_id'][0] or False,
- }
- moveputaway_obj.create(cr, SUPERUSER_ID, vals, context=context)
- qty -= row['qty']
-
- #if the quants assigned aren't fully explaining where the products have to be moved
- if qty > 0:
- vals = {
- 'move_id': move.id,
- 'location_id': putaway.location_spec_id.id,
- 'quantity': qty,
- }
- moveputaway_obj.create(cr, SUPERUSER_ID, vals, context=context)
-
- def _putaway_check(self, cr, uid, ids, context=None):
- for move in self.browse(cr, uid, ids, context=context):
- putaway = self.pool.get('stock.location').get_putaway_strategy(cr, uid, move.location_dest_id, move.product_id, context=context)
- if putaway:
- self._putaway_apply(cr, uid, move, putaway, context=context)
-
def _create_procurement(self, cr, uid, move, context=None):
""" This will create a procurement order """
return self.pool.get("procurement.order").create(cr, uid, self._prepare_procurement_from_move(cr, uid, move, context=context))
@@ -1846,7 +1846,6 @@ class stock_move(osv.osv):
@return: True
"""
#check putaway method
- self._putaway_check(cr, uid, ids, context=context)
return self.write(cr, uid, ids, {'state': 'assigned'})
def check_tracking(self, cr, uid, move, lot_id, context=None):
@@ -1868,8 +1867,6 @@ class stock_move(osv.osv):
context = context or {}
quant_obj = self.pool.get("stock.quant")
to_assign_moves = []
- prefered_domain = {}
- fallback_domain = {}
main_domain = {}
todo_moves = []
operations = set()
@@ -1915,7 +1912,7 @@ class stock_move(osv.osv):
domain = main_domain[move.id] + self.pool.get('stock.move.operation.link').get_specific_domain(cr, uid, record, context=context)
qty_already_assigned = sum([q.qty for q in record.reserved_quant_ids])
qty = record.qty - qty_already_assigned
- quants = quant_obj.quants_get_prefered_domain(cr, uid, move.location_id, move.product_id, qty, domain=domain, prefered_domain=[], fallback_domain=[], restrict_lot_id=move.restrict_lot_id.id, restrict_partner_id=move.restrict_partner_id.id, context=context)
+ quants = quant_obj.quants_get_prefered_domain(cr, uid, ops.location_id, move.product_id, qty, domain=domain, prefered_domain=[], fallback_domain=[], restrict_lot_id=move.restrict_lot_id.id, restrict_partner_id=move.restrict_partner_id.id, context=context)
quant_obj.quants_reserve(cr, uid, quants, move, record, context=context)
for move in todo_moves:
@@ -1929,8 +1926,6 @@ class stock_move(osv.osv):
#force assignation of consumable products and picking type auto_force_assign
if to_assign_moves:
self.force_assign(cr, uid, to_assign_moves, context=context)
- #check if a putaway rule is likely to be processed and store result on the move
- self._putaway_check(cr, uid, ids, context=context)
def action_cancel(self, cr, uid, ids, context=None):
""" Cancels the moves and if all moves are cancelled it cancels the picking.
@@ -1997,14 +1992,14 @@ class stock_move(osv.osv):
fallback_domain = [('reservation_id', '=', False)]
self.check_tracking(cr, uid, move, ops.package_id.id or ops.lot_id.id, context=context)
dom = main_domain + self.pool.get('stock.move.operation.link').get_specific_domain(cr, uid, record, context=context)
- quants = quant_obj.quants_get_prefered_domain(cr, uid, move.location_id, move.product_id, record.qty, domain=dom, prefered_domain=prefered_domain,
+ quants = quant_obj.quants_get_prefered_domain(cr, uid, ops.location_id, move.product_id, record.qty, domain=dom, prefered_domain=prefered_domain,
fallback_domain=fallback_domain, restrict_lot_id=move.restrict_lot_id.id, restrict_partner_id=move.restrict_partner_id.id, context=context)
package_id = False
if not record.operation_id.package_id:
#if a package and a result_package is given, we don't enter here because it will be processed by process_packaging() later
#but for operations having only result_package_id, we will create new quants in the final package directly
package_id = record.operation_id.result_package_id.id or False
- quant_obj.quants_move(cr, uid, quants, move, lot_id=ops.lot_id.id, owner_id=ops.owner_id.id, src_package_id=ops.package_id.id, dest_package_id=package_id, context=context)
+ quant_obj.quants_move(cr, uid, quants, move, ops.location_dest_id, lot_id=ops.lot_id.id, owner_id=ops.owner_id.id, src_package_id=ops.package_id.id, dest_package_id=package_id, context=context)
#packaging process
pack_op_obj.process_packaging(cr, uid, ops, [x[0].id for x in quants if x[0]], context=context)
move_qty[move.id] -= record.qty
@@ -2017,7 +2012,7 @@ class stock_move(osv.osv):
self.check_tracking(cr, uid, move, move.restrict_lot_id.id, context=context)
qty = move_qty[move.id]
quants = quant_obj.quants_get_prefered_domain(cr, uid, move.location_id, move.product_id, qty, domain=main_domain, prefered_domain=prefered_domain, fallback_domain=fallback_domain, restrict_lot_id=move.restrict_lot_id.id, restrict_partner_id=move.restrict_partner_id.id, context=context)
- quant_obj.quants_move(cr, uid, quants, move, lot_id=move.restrict_lot_id.id, owner_id=move.restrict_partner_id.id, context=context)
+ quant_obj.quants_move(cr, uid, quants, move, move.location_dest_id, lot_id=move.restrict_lot_id.id, owner_id=move.restrict_partner_id.id, context=context)
#unreserve the quants and make them available for other operations/moves
quant_obj.quants_unreserve(cr, uid, move, context=context)
@@ -3189,16 +3184,6 @@ class stock_location_path(osv.osv):
})
move_obj.action_confirm(cr, uid, [move_id], context=None)
-class stock_move_putaway(osv.osv):
- _name = 'stock.move.putaway'
- _description = 'Proposed Destination'
- _columns = {
- 'move_id': fields.many2one('stock.move', required=True),
- 'location_id': fields.many2one('stock.location', 'Location', required=True),
- 'lot_id': fields.many2one('stock.production.lot', 'Lot'),
- 'quantity': fields.float('Quantity', required=True),
- }
-
# -------------------------
@@ -3470,6 +3455,8 @@ class stock_pack_operation(osv.osv):
'currency': fields.many2one('res.currency', string="Currency", help="Currency in which Unit cost is expressed", ondelete='CASCADE'),
'linked_move_operation_ids': fields.one2many('stock.move.operation.link', 'operation_id', string='Linked Moves', readonly=True, help='Moves impacted by this operation for the computation of the remaining quantities'),
'remaining_qty': fields.function(_get_remaining_qty, type='float', string='Remaining Qty'),
+ 'location_id': fields.many2one('stock.location', 'Location From', required=True),
+ 'location_dest_id': fields.many2one('stock.location', 'Location To', required=True),
}
_defaults = {
diff --git a/addons/stock/stock_demo.yml b/addons/stock/stock_demo.yml
index a3a49ba1225..46427cb5e09 100644
--- a/addons/stock/stock_demo.yml
+++ b/addons/stock/stock_demo.yml
@@ -90,6 +90,8 @@
- product_id: product.product_product_6
product_qty: 100
product_uom_id: product.product_uom_unit
+ location_id: stock.stock_location_stock
+ location_dest_id: stock.stock_location_customers
-
!record {model: stock.picking, id: outgoing_shipment_main_warehouse2}:
picking_type_id: stock.picking_type_out
@@ -209,6 +211,8 @@
- product_id: product.product_product_48
product_qty: 100
product_uom_id: product.product_uom_unit
+ location_id: stock.stock_location_suppliers
+ location_dest_id: stock.stock_location_shop0
-
!record {model: stock.picking, id: incomming_chicago_warehouse2}:
picking_type_id: chi_picking_type_in
@@ -264,6 +268,8 @@
- product_id: product.product_product_6
product_qty: 100
product_uom_id: product.product_uom_unit
+ location_id: stock.stock_location_shop0
+ location_dest_id: stock.stock_location_customers
-
!record {model: stock.picking, id: outgoing_chicago_warehouse2}:
picking_type_id: chi_picking_type_out
diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml
index 596988da556..ede38e6058a 100644
--- a/addons/stock/stock_view.xml
+++ b/addons/stock/stock_view.xml
@@ -769,14 +769,16 @@
-
+
+
+
@@ -789,6 +791,8 @@
+
+
diff --git a/addons/stock/test/packing.yml b/addons/stock/test/packing.yml
index 3be5b9038c6..70e844ca4eb 100644
--- a/addons/stock/test/packing.yml
+++ b/addons/stock/test/packing.yml
@@ -49,8 +49,8 @@
#Create package for each line and assign it as result_package_id
#create pack operation
stock_pack.write(cr, uid, record.pack_operation_ids[0].id, {'result_package_id': package1, 'product_qty': 120})
- new_pack1 = stock_pack.create(cr, uid, {'product_id': ref('product1'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick1'), 'lot_id': lot_a, 'result_package_id': package2, 'product_qty': 120}, context=context)
- new_pack2 = stock_pack.create(cr, uid, {'product_id': ref('product1'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick1'), 'result_package_id': package3, 'product_qty': 60}, context=context)
+ new_pack1 = stock_pack.create(cr, uid, {'product_id': ref('product1'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick1'), 'lot_id': lot_a, 'result_package_id': package2, 'product_qty': 120, 'location_id': ref('stock_location_suppliers'), 'location_dest_id': ref('stock_location_stock')}, context=context)
+ new_pack2 = stock_pack.create(cr, uid, {'product_id': ref('product1'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick1'), 'result_package_id': package3, 'product_qty': 60, 'location_id': ref('stock_location_suppliers'), 'location_dest_id': ref('stock_location_stock')}, context=context)
-
Transfer the reception
-
diff --git a/addons/stock/test/packingneg.yml b/addons/stock/test/packingneg.yml
index d42cbfa52e2..5bb93d44aca 100644
--- a/addons/stock/test/packingneg.yml
+++ b/addons/stock/test/packingneg.yml
@@ -49,8 +49,8 @@
#Create package for each line and assign it as result_package_id
#create pack operation
stock_pack.write(cr, uid, record.pack_operation_ids[0].id, {'result_package_id': package1, 'product_qty': 120})
- new_pack1 = stock_pack.create(cr, uid, {'product_id': ref('product_neg'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick_neg'), 'lot_id': lot_a, 'result_package_id': package2, 'product_qty': 120}, context=context)
- new_pack2 = stock_pack.create(cr, uid, {'product_id': ref('product_neg'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick_neg'), 'result_package_id': package3, 'product_qty': 60}, context=context)
+ new_pack1 = stock_pack.create(cr, uid, {'product_id': ref('product_neg'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick_neg'), 'lot_id': lot_a, 'result_package_id': package2, 'product_qty': 120, 'location_id': ref('stock_location_suppliers'), 'location_dest_id': ref('stock_location_stock')}, context=context)
+ new_pack2 = stock_pack.create(cr, uid, {'product_id': ref('product_neg'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick_neg'), 'result_package_id': package3, 'product_qty': 60, 'location_id': ref('stock_location_suppliers'), 'location_dest_id': ref('stock_location_stock')}, context=context)
-
Transfer the reception
-
diff --git a/addons/stock/test/shipment.yml b/addons/stock/test/shipment.yml
index 5379cd8d4e0..4e640f3aa64 100644
--- a/addons/stock/test/shipment.yml
+++ b/addons/stock/test/shipment.yml
@@ -18,7 +18,9 @@
'picking_id': pick.id,
'product_id': ref('product_icecream'),
'product_uom_id': ref('product.product_uom_kgm'),
- 'product_qty': 40
+ 'product_qty': 40,
+ 'location_id': ref('stock.stock_location_suppliers'),
+ 'location_dest_id': ref('stock.stock_location_14')
})
context.update({'active_model': 'stock.picking', 'active_id': ref('incomming_shipment'), 'active_ids': [ref('incomming_shipment')]})
pick.do_transfer(context=context)
@@ -46,7 +48,9 @@
'picking_id': backorder.id,
'product_id': ref('product_icecream'),
'product_uom_id': ref('product.product_uom_kgm'),
- 'product_qty': 10
+ 'product_qty': 10,
+ 'location_id': ref('stock.stock_location_suppliers'),
+ 'location_dest_id': ref('stock.stock_location_14')
})
backorder.do_transfer(context=context)
-