[MERGE] Merge from loc on op

bzr revid: jco@openerp.com-20140304161534-20ug4ioic1ik766d
This commit is contained in:
Josse Colpaert
2014-03-04 17:15:34 +01:00
4 changed files with 142 additions and 122 deletions
+19 -18
View File
@@ -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)
@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
66 access_stock_pack_operation_manager stock.pack.operation manager model_stock_pack_operation stock.group_stock_manager 1 1 1 1
67 access_stock_pack_operation_user stock.pack.operation user model_stock_pack_operation stock.group_stock_user 1 1 1 1
68 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
69 access_product_putaway_all product.putaway all users model_product_putaway base.group_user 1 0 0 0
70 access_stock_removal_all product.removal all users model_product_removal base.group_user 1 0 0 0
71 access_stock_move_operation_link_manager stock.move.operation.link manager model_stock_move_operation_link stock.group_stock_manager 1 1 1 1
+121 -103
View File
@@ -322,12 +322,12 @@ 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):
def quants_move(self, cr, uid, quants, move, lot_id=False, owner_id=False, src_package_id=False, dest_package_id=False, location_dest_id = False, context=None):
"""Moves all given stock.quant in the destination location of the given move.
: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 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 +335,11 @@ 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)
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_dest_id, context=context)
if not location_dest_id:
location_dest_id = move.location_dest_id
self.move_single_quant_tuple(cr, uid, quant, qty, move, location_dest_id, 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)]
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,19 +362,18 @@ class stock_quant(osv.osv):
quant.refresh()
return new_quant
def move_single_quant_tuple(self, cr, uid, quant, qty, move, context=None):
def move_single_quant_tuple(self, cr, uid, quant, qty, move, location_dest_id, 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
if not quant:
return True
new_quant = self.move_single_quant(cr, uid, quant, location_dest_id, 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
@@ -933,37 +920,84 @@ 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 _quant_putaway_apply(self, cr, uid, picking, quant, putaway, context=None):
if putaway.method == 'fixed' and putaway.location_spec_id:
return putaway.location_spec_id
return False
def _putaway_apply(self, cr, uid, picking, quants, qtys_remaining, context=None):
"""
Apply putaway strategy to quants and remaining quantities
"""
product_putaway = {}
quant_dict = {}
for quant in quants:
location = False
# Search putaway strategy
if product_putaway.get(quant.product_id.id):
putaway = product_putaway[quant.product_id.id]
else:
putaway = self.pool.get('stock.location').get_putaway_strategy(cr, uid, picking.location_dest_id, quant.product_id, context=context)
product_putaway[quant.product_id.id] = putaway
if putaway:
location = self._quant_putaway_apply(cr, uid, picking, quant, putaway, context=context)
if location:
quant_dict[quant] = [(quant.qty, location,)]
if not location:
quant_dict[quant] = [(quant.qty, picking.location_dest_id,)]
remaining_dict = {}
for product in qtys_remaining.keys():
location = False
if product_putaway.get(product):
putaway = product_putaway[product]
else:
putaway = self.pool.get('stock.location').get_putaway_strategy(cr, uid, picking.location_dest_id, product, context=context)
product_putaway[product] = putaway
if putaway:
location = self._quant_putaway_apply(cr, uid, picking, False, putaway, context=context)
if location:
remaining_dict[product] = [(qtys_remaining[product], location,)]
if not location:
remaining_dict[product] = [(qtys_remaining[product], picking.location_dest_id,)]
return (quant_dict, remaining_dict,)
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 = {}
qtys_remaining = {} #Quantity remaining after calculating reserved quants
quants = []
#Calculate packages, reserved quants, qtys of this picking's moves
for move in picking.move_lines:
quants += move.reserved_quant_ids
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
qty = move.product_qty - move.reserved_availability
else:
qty = move.reserved_availability
qty = 0
#Add qty to qtys remaining
if qtys_remaining.get(move.product_id.id):
qtys_remaining[move.product_id.id] += qty
if qtys_remaining.get(move.product_id):
qtys_remaining[move.product_id] += qty
else:
qtys_remaining[move.product_id.id] = qty
qtys_remaining[move.product_id] = qty
(putaway_quants_dict, putaway_remaining_dict) = self._putaway_apply(cr, uid, picking, quants, qtys_remaining, context=context)
packages = list(set([x.package_id for x in putaway_quants_dict.keys() if x and x.package_id]))
# Try to find as much as possible top-level packages that can be moved
top_lvl_packages = set()
@@ -973,8 +1007,28 @@ class stock_picking(osv.osv):
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)]):
quants_to_compare = putaway_quants_dict.keys()
# move_list = [m.id for m in picking.move_lines]
common_location = False
all_in = True
for quant in quant_obj.browse(cr, uid, 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:
for quants_tup in putaway_quants_dict[quant]:
if not common_location:
common_location = quants_tup[1]
elif common_location != quants_tup[1]:
all_in = False
break
# if not (quant in quants_to_compare and (not common_location or putaway_quants_dict[quant][1] == common_location)):
# all_in = False
# break
# elif not common_location and quant in quants_to_compare:
# common_location = putaway_quants_dict[quant][1]
if all_in:
good_pack = test_pack.id
if test_pack.parent_id:
test_pack = test_pack.parent_id
@@ -991,34 +1045,40 @@ class stock_picking(osv.osv):
# 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
quant = quant_obj.browse(cr, uid, quants[0], context=context)
pack_operation_obj.create(cr, uid, {
'picking_id': picking.id,
'package_id': pack.id,
'product_qty': 1.0,
'location_id': pack.location_id.id,
'location_dest_id': putaway_quants_dict[quant][0][1].id,
}, context=context)
for quant in quant_obj.browse(cr, uid, quants, context=context):
reserved_move[quant.reservation_id.id] -= set([quant.id])
del putaway_quants_dict[quant]
qtys_remaining[quant.product_id] -= quant.qty
# 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
# Go through all remaining reserved quants and group by product, package, lot, owner, source location and dest location
for quant in putaway_quants_dict.keys():
for quant_tup in putaway_quants_dict[quant]:
if quant_tup[0] > 0:
key = (quant.product_id.id, quant.package_id.id, quant.lot_id.id, quant.owner_id.id, quant.location_id.id, quant_tup[1].id)
if qtys_grouped.get(key):
qtys_grouped[key] += quant_tup[0]
else:
qtys_grouped[key] = quant_tup[0]
# 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]
for product in putaway_remaining_dict.keys():
for product_tup in putaway_remaining_dict[product]:
if product_tup[0] > 0:
key = (product.id, False, False, False, picking.location_id.id, product_tup[1].id)
if qtys_grouped.get(key):
qtys_grouped[key] += product_tup[0]
else:
qtys_grouped[key] = product_tup[0]
# Create the necessary operations for the grouped quants and remaining qtys
for key, qty in qtys_grouped.items():
@@ -1029,6 +1089,8 @@ class stock_picking(osv.osv):
'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,
}, context=context)
@@ -1446,7 +1508,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)."),
}
@@ -1517,10 +1578,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 "/")
@@ -1568,37 +1626,7 @@ 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 """
@@ -1840,7 +1868,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):
@@ -1923,8 +1950,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.
@@ -1998,7 +2023,7 @@ class stock_move(osv.osv):
#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, lot_id=ops.lot_id.id, owner_id=ops.owner_id.id, src_package_id=ops.package_id.id, dest_package_id=package_id, location_dest_id = ops.location_dest_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
@@ -3184,15 +3209,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),
}
@@ -3465,6 +3481,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'),
'location_dest_id': fields.many2one('stock.location', 'Location To'),
}
_defaults = {
+2
View File
@@ -771,12 +771,14 @@
</group>
<field name="pack_operation_ids" attrs="{'invisible': [('pack_operation_exist', '=', False)]}" context="{'default_owner_id': owner_id}">
<tree editable="top">
<field name="location_id"/>
<field name="product_id" on_change='product_id_change(product_id, product_uom_id, product_qty, context)'/>
<field name="product_uom_id" groups="product.group_uom" on_change='on_change_tests(product_id, product_uom_id, product_qty, context)'/>
<field name="lot_id" domain="[('product_id','=?', product_id)]" context="{'product_id': product_id}" groups="stock.group_production_lot"/>
<field name="package_id" groups="stock.group_tracking_lot"/>
<field name="owner_id" groups="stock.group_tracking_owner"/>
<field name="product_qty" attrs="{'required': [('product_id', '!=', False)]}" on_change='on_change_tests(product_id, product_uom_id, product_qty, context)'/>
<field name="location_dest_id"/>
<field name="result_package_id" groups="stock.group_tracking_lot"/>
</tree>
</field>