From 7a823e0e1530ecae8002b53cb9d2b85e88320a8d Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Tue, 1 Jul 2014 17:11:46 +0200 Subject: [PATCH] [FIX] stock: backport fixes from master --- addons/stock/security/ir.model.access.csv | 2 +- addons/stock/security/stock_security.xml | 2 +- addons/stock/stock.py | 259 ++++++++++++---------- 3 files changed, 143 insertions(+), 120 deletions(-) diff --git a/addons/stock/security/ir.model.access.csv b/addons/stock/security/ir.model.access.csv index 9ce558781be..8a94f027408 100644 --- a/addons/stock/security/ir.model.access.csv +++ b/addons/stock/security/ir.model.access.csv @@ -60,7 +60,7 @@ access_stock_location_path_internal_user,stock location path internal user,model access_stock_location_path_sale_manager,stock.location.path partner salemanager,model_stock_location_path,base.group_sale_manager,1,1,1,1 access_stock_location_path_stock_user,stock.location.path stock user,model_stock_location_path,stock.group_stock_user,1,1,1,1 access_stock_location_path,stock.location.path,model_stock_location_path,base.group_sale_salesman,1,0,0,0 -access_stock_location_route,stock.location.route,model_stock_location_route,stock.group_stock_manager,1,1,1,1 +access_stock_location_route_stock_manager,stock.location.route,model_stock_location_route,stock.group_stock_manager,1,1,1,1 access_stock_location_route,stock.location.route,model_stock_location_route,base.group_user,1,0,0,0 access_procurement_rule,procurement.rule.flow,model_procurement_rule,base.group_sale_salesman,1,0,0,0 access_procurement_rule_internal,procurement.rule.flow internal,model_procurement_rule,base.group_user,1,0,0,0 diff --git a/addons/stock/security/stock_security.xml b/addons/stock/security/stock_security.xml index f44d354f121..044ff1333cd 100644 --- a/addons/stock/security/stock_security.xml +++ b/addons/stock/security/stock_security.xml @@ -50,7 +50,7 @@ ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] - + Stock Picking Type multi-company diff --git a/addons/stock/stock.py b/addons/stock/stock.py index d6d28c919e6..acb228b6232 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1248,6 +1248,7 @@ class stock_picking(osv.osv): ''' move_obj = self.pool.get('stock.move') operation_obj = self.pool.get('stock.pack.operation') + moves = [] for op in picking.pack_operation_ids: for product_id, remaining_qty in operation_obj._get_remaining_prod_quantities(cr, uid, op, context=context).items(): if remaining_qty > 0: @@ -1260,9 +1261,12 @@ class stock_picking(osv.osv): 'product_uom': product.uom_id.id, 'product_uom_qty': remaining_qty, 'name': _('Extra Move: ') + product.name, - 'state': 'confirmed', + 'state': 'draft', } - move_obj.create(cr, uid, vals, context=context) + moves.append(move_obj.create(cr, uid, vals, context=context)) + if moves: + move_obj.action_confirm(cr, uid, moves, context=context) + return moves def rereserve_quants(self, cr, uid, picking, move_ids=[], context=None): """ Unreserve quants then try to reassign quants.""" @@ -1289,11 +1293,13 @@ class stock_picking(osv.osv): else: need_rereserve, all_op_processed = self.picking_recompute_remaining_quantities(cr, uid, picking, context=context) #create extra moves in the picking (unexpected product moves coming from pack operations) + todo_move_ids = [] if not all_op_processed: - self._create_extra_moves(cr, uid, picking, context=context) + todo_move_ids += self._create_extra_moves(cr, uid, picking, context=context) + picking.refresh() #split move lines eventually - todo_move_ids = [] + toassign_move_ids = [] for move in picking.move_lines: remaining_qty = move.remaining_qty @@ -1371,7 +1377,7 @@ class stock_picking(osv.osv): op = operation if (operation.qty_done < operation.product_qty): new_operation = stock_operation_obj.copy(cr, uid, operation.id, {'product_qty': operation.qty_done,'qty_done': operation.qty_done}, context=context) - stock_operation_obj.write(cr, uid, operation.id, {'product_qty': operation.product_qty - operation.qty_done,'qty_done': 0}, context=context) + stock_operation_obj.write(cr, uid, operation.id, {'product_qty': operation.product_qty - operation.qty_done,'qty_done': 0, 'lot_id': False}, context=context) op = stock_operation_obj.browse(cr, uid, new_operation, context=context) pack_operation_ids.append(op.id) for record in op.linked_move_operation_ids: @@ -1783,7 +1789,7 @@ class stock_move(osv.osv): 'move_dest_id': move.id, 'group_id': group_id, 'route_ids': [(4, x.id) for x in move.route_ids], - 'warehouse_id': move.warehouse_id and move.warehouse_id.id or False, + 'warehouse_id': move.warehouse_id.id or (move.picking_type_id and move.picking_type_id.warehouse_id.id or False), 'priority': move.priority, } @@ -3108,6 +3114,109 @@ class stock_warehouse(osv.osv): pull_obj.write(cr, uid, warehouse.mto_pull_id.id, mto_pull_vals, context=context) return True + def create_sequences_and_picking_types(self, cr, uid, warehouse, context=None): + seq_obj = self.pool.get('ir.sequence') + picking_type_obj = self.pool.get('stock.picking.type') + #create new sequences + in_seq_id = seq_obj.create(cr, SUPERUSER_ID, values={'name': warehouse.name + _(' Sequence in'), 'prefix': warehouse.code + '/IN/', 'padding': 5}, context=context) + out_seq_id = seq_obj.create(cr, SUPERUSER_ID, values={'name': warehouse.name + _(' Sequence out'), 'prefix': warehouse.code + '/OUT/', 'padding': 5}, context=context) + pack_seq_id = seq_obj.create(cr, SUPERUSER_ID, values={'name': warehouse.name + _(' Sequence packing'), 'prefix': warehouse.code + '/PACK/', 'padding': 5}, context=context) + pick_seq_id = seq_obj.create(cr, SUPERUSER_ID, values={'name': warehouse.name + _(' Sequence picking'), 'prefix': warehouse.code + '/PICK/', 'padding': 5}, context=context) + int_seq_id = seq_obj.create(cr, SUPERUSER_ID, values={'name': warehouse.name + _(' Sequence internal'), 'prefix': warehouse.code + '/INT/', 'padding': 5}, context=context) + + wh_stock_loc = warehouse.lot_stock_id + wh_input_stock_loc = warehouse.wh_input_stock_loc_id + wh_output_stock_loc = warehouse.wh_output_stock_loc_id + wh_pack_stock_loc = warehouse.wh_pack_stock_loc_id + + #fetch customer and supplier locations, for references + customer_loc, supplier_loc = self._get_partner_locations(cr, uid, warehouse.id, context=context) + + #create in, out, internal picking types for warehouse + input_loc = wh_input_stock_loc + if warehouse.reception_steps == 'one_step': + input_loc = wh_stock_loc + output_loc = wh_output_stock_loc + if warehouse.delivery_steps == 'ship_only': + output_loc = wh_stock_loc + + #choose the next available color for the picking types of this warehouse + color = 0 + available_colors = [c%9 for c in range(3, 12)] # put flashy colors first + all_used_colors = self.pool.get('stock.picking.type').search_read(cr, uid, [('warehouse_id', '!=', False), ('color', '!=', False)], ['color'], order='color') + #don't use sets to preserve the list order + for x in all_used_colors: + if x['color'] in available_colors: + available_colors.remove(x['color']) + if available_colors: + color = available_colors[0] + + #order the picking types with a sequence allowing to have the following suit for each warehouse: reception, internal, pick, pack, ship. + max_sequence = self.pool.get('stock.picking.type').search_read(cr, uid, [], ['sequence'], order='sequence desc') + max_sequence = max_sequence and max_sequence[0]['sequence'] or 0 + + in_type_id = picking_type_obj.create(cr, uid, vals={ + 'name': _('Receptions'), + 'warehouse_id': warehouse.id, + 'code': 'incoming', + 'sequence_id': in_seq_id, + 'default_location_src_id': supplier_loc.id, + 'default_location_dest_id': input_loc.id, + 'sequence': max_sequence + 1, + 'color': color}, context=context) + out_type_id = picking_type_obj.create(cr, uid, vals={ + 'name': _('Delivery Orders'), + 'warehouse_id': warehouse.id, + 'code': 'outgoing', + 'sequence_id': out_seq_id, + 'return_picking_type_id': in_type_id, + 'default_location_src_id': output_loc.id, + 'default_location_dest_id': customer_loc.id, + 'sequence': max_sequence + 4, + 'color': color}, context=context) + picking_type_obj.write(cr, uid, [in_type_id], {'return_picking_type_id': out_type_id}, context=context) + int_type_id = picking_type_obj.create(cr, uid, vals={ + 'name': _('Internal Transfers'), + 'warehouse_id': warehouse.id, + 'code': 'internal', + 'sequence_id': int_seq_id, + 'default_location_src_id': wh_stock_loc.id, + 'default_location_dest_id': wh_stock_loc.id, + 'active': True, + 'sequence': max_sequence + 2, + 'color': color}, context=context) + pack_type_id = picking_type_obj.create(cr, uid, vals={ + 'name': _('Pack'), + 'warehouse_id': warehouse.id, + 'code': 'internal', + 'sequence_id': pack_seq_id, + 'default_location_src_id': wh_pack_stock_loc.id, + 'default_location_dest_id': output_loc.id, + 'active': warehouse.delivery_steps == 'pick_pack_ship', + 'sequence': max_sequence + 3, + 'color': color}, context=context) + pick_type_id = picking_type_obj.create(cr, uid, vals={ + 'name': _('Pick'), + 'warehouse_id': warehouse.id, + 'code': 'internal', + 'sequence_id': pick_seq_id, + 'default_location_src_id': wh_stock_loc.id, + 'default_location_dest_id': wh_pack_stock_loc.id, + 'active': warehouse.delivery_steps != 'ship_only', + 'sequence': max_sequence + 2, + 'color': color}, context=context) + + #write picking types on WH + vals = { + 'in_type_id': in_type_id, + 'out_type_id': out_type_id, + 'pack_type_id': pack_type_id, + 'pick_type_id': pick_type_id, + 'int_type_id': int_type_id, + } + super(stock_warehouse, self).write(cr, uid, warehouse.id, vals=vals, context=context) + + def create(self, cr, uid, vals, context=None): if context is None: context = {} @@ -3147,108 +3256,10 @@ class stock_warehouse(osv.osv): }, context=context_with_inactive) vals[values['field']] = location_id - #create new sequences - in_seq_id = seq_obj.create(cr, SUPERUSER_ID, values={'name': vals.get('name', '') + _(' Sequence in'), 'prefix': vals.get('code', '') + '/IN/', 'padding': 5}, context=context) - out_seq_id = seq_obj.create(cr, SUPERUSER_ID, values={'name': vals.get('name', '') + _(' Sequence out'), 'prefix': vals.get('code', '') + '/OUT/', 'padding': 5}, context=context) - pack_seq_id = seq_obj.create(cr, SUPERUSER_ID, values={'name': vals.get('name', '') + _(' Sequence packing'), 'prefix': vals.get('code', '') + '/PACK/', 'padding': 5}, context=context) - pick_seq_id = seq_obj.create(cr, SUPERUSER_ID, values={'name': vals.get('name', '') + _(' Sequence picking'), 'prefix': vals.get('code', '') + '/PICK/', 'padding': 5}, context=context) - int_seq_id = seq_obj.create(cr, SUPERUSER_ID, values={'name': vals.get('name', '') + _(' Sequence internal'), 'prefix': vals.get('code', '') + '/INT/', 'padding': 5}, context=context) - #create WH new_id = super(stock_warehouse, self).create(cr, uid, vals=vals, context=context) - warehouse = self.browse(cr, uid, new_id, context=context) - wh_stock_loc = warehouse.lot_stock_id - wh_input_stock_loc = warehouse.wh_input_stock_loc_id - wh_output_stock_loc = warehouse.wh_output_stock_loc_id - wh_pack_stock_loc = warehouse.wh_pack_stock_loc_id - - #fetch customer and supplier locations, for references - customer_loc, supplier_loc = self._get_partner_locations(cr, uid, new_id, context=context) - - #create in, out, internal picking types for warehouse - input_loc = wh_input_stock_loc - if warehouse.reception_steps == 'one_step': - input_loc = wh_stock_loc - output_loc = wh_output_stock_loc - if warehouse.delivery_steps == 'ship_only': - output_loc = wh_stock_loc - - #choose the next available color for the picking types of this warehouse - color = 0 - available_colors = [c%9 for c in range(3, 12)] # put flashy colors first - all_used_colors = self.pool.get('stock.picking.type').search_read(cr, uid, [('warehouse_id', '!=', False), ('color', '!=', False)], ['color'], order='color') - #don't use sets to preserve the list order - for x in all_used_colors: - if x['color'] in available_colors: - available_colors.remove(x['color']) - if available_colors: - color = available_colors[0] - - #order the picking types with a sequence allowing to have the following suit for each warehouse: reception, internal, pick, pack, ship. - max_sequence = self.pool.get('stock.picking.type').search_read(cr, uid, [], ['sequence'], order='sequence desc') - max_sequence = max_sequence and max_sequence[0]['sequence'] or 0 - - in_type_id = picking_type_obj.create(cr, uid, vals={ - 'name': _('Receptions'), - 'warehouse_id': new_id, - 'code': 'incoming', - 'sequence_id': in_seq_id, - 'default_location_src_id': supplier_loc.id, - 'default_location_dest_id': input_loc.id, - 'sequence': max_sequence + 1, - 'color': color}, context=context) - out_type_id = picking_type_obj.create(cr, uid, vals={ - 'name': _('Delivery Orders'), - 'warehouse_id': new_id, - 'code': 'outgoing', - 'sequence_id': out_seq_id, - 'return_picking_type_id': in_type_id, - 'default_location_src_id': output_loc.id, - 'default_location_dest_id': customer_loc.id, - 'sequence': max_sequence + 4, - 'color': color}, context=context) - picking_type_obj.write(cr, uid, [in_type_id], {'return_picking_type_id': out_type_id}, context=context) - int_type_id = picking_type_obj.create(cr, uid, vals={ - 'name': _('Internal Transfers'), - 'warehouse_id': new_id, - 'code': 'internal', - 'sequence_id': int_seq_id, - 'default_location_src_id': wh_stock_loc.id, - 'default_location_dest_id': wh_stock_loc.id, - 'active': True, - 'sequence': max_sequence + 2, - 'color': color}, context=context) - pack_type_id = picking_type_obj.create(cr, uid, vals={ - 'name': _('Pack'), - 'warehouse_id': new_id, - 'code': 'internal', - 'sequence_id': pack_seq_id, - 'default_location_src_id': wh_pack_stock_loc.id, - 'default_location_dest_id': output_loc.id, - 'active': delivery_steps == 'pick_pack_ship', - 'sequence': max_sequence + 3, - 'color': color}, context=context) - pick_type_id = picking_type_obj.create(cr, uid, vals={ - 'name': _('Pick'), - 'warehouse_id': new_id, - 'code': 'internal', - 'sequence_id': pick_seq_id, - 'default_location_src_id': wh_stock_loc.id, - 'default_location_dest_id': wh_pack_stock_loc.id, - 'active': delivery_steps != 'ship_only', - 'sequence': max_sequence + 2, - 'color': color}, context=context) - - #write picking types on WH - vals = { - 'in_type_id': in_type_id, - 'out_type_id': out_type_id, - 'pack_type_id': pack_type_id, - 'pick_type_id': pick_type_id, - 'int_type_id': int_type_id, - } - super(stock_warehouse, self).write(cr, uid, new_id, vals=vals, context=context) + self.create_sequences_and_picking_types(cr, uid, warehouse, context=context) warehouse.refresh() #create routes and push/pull rules @@ -3292,7 +3303,8 @@ class stock_warehouse(osv.osv): for push in route.push_ids: push_obj.write(cr, uid, push.id, {'name': pull.name.replace(warehouse.name, name, 1)}, context=context) #change the mto pull rule name - pull_obj.write(cr, uid, warehouse.mto_pull_id.id, {'name': warehouse.mto_pull_id.name.replace(warehouse.name, name, 1)}, context=context) + if warehouse.mto_pull_id.id: + pull_obj.write(cr, uid, warehouse.mto_pull_id.id, {'name': warehouse.mto_pull_id.name.replace(warehouse.name, name, 1)}, context=context) def _check_delivery_resupply(self, cr, uid, warehouse, new_location, change_to_multiple, context=None): """ Will check if the resupply routes from this warehouse follow the changes of number of delivery steps """ @@ -3374,11 +3386,12 @@ class stock_warehouse(osv.osv): if vals.get('name'): name = vals.get('name', warehouse.name) self._handle_renaming(cr, uid, warehouse, name, vals.get('code', warehouse.code), context=context_with_inactive) - seq_obj.write(cr, uid, warehouse.in_type_id.sequence_id.id, {'name': name + _(' Sequence in'), 'prefix': vals.get('code', warehouse.code) + '\IN\\'}, context=context) - seq_obj.write(cr, uid, warehouse.out_type_id.sequence_id.id, {'name': name + _(' Sequence out'), 'prefix': vals.get('code', warehouse.code) + '\OUT\\'}, context=context) - seq_obj.write(cr, uid, warehouse.pack_type_id.sequence_id.id, {'name': name + _(' Sequence packing'), 'prefix': vals.get('code', warehouse.code) + '\PACK\\'}, context=context) - seq_obj.write(cr, uid, warehouse.pick_type_id.sequence_id.id, {'name': name + _(' Sequence picking'), 'prefix': vals.get('code', warehouse.code) + '\PICK\\'}, context=context) - seq_obj.write(cr, uid, warehouse.int_type_id.sequence_id.id, {'name': name + _(' Sequence internal'), 'prefix': vals.get('code', warehouse.code) + '\INT\\'}, context=context) + if warehouse.in_type_id: + seq_obj.write(cr, uid, warehouse.in_type_id.sequence_id.id, {'name': name + _(' Sequence in'), 'prefix': vals.get('code', warehouse.code) + '\IN\\'}, context=context) + seq_obj.write(cr, uid, warehouse.out_type_id.sequence_id.id, {'name': name + _(' Sequence out'), 'prefix': vals.get('code', warehouse.code) + '\OUT\\'}, context=context) + seq_obj.write(cr, uid, warehouse.pack_type_id.sequence_id.id, {'name': name + _(' Sequence packing'), 'prefix': vals.get('code', warehouse.code) + '\PACK\\'}, context=context) + seq_obj.write(cr, uid, warehouse.pick_type_id.sequence_id.id, {'name': name + _(' Sequence picking'), 'prefix': vals.get('code', warehouse.code) + '\PICK\\'}, context=context) + seq_obj.write(cr, uid, warehouse.int_type_id.sequence_id.id, {'name': name + _(' Sequence internal'), 'prefix': vals.get('code', warehouse.code) + '\INT\\'}, context=context) if vals.get('resupply_wh_ids') and not vals.get('resupply_route_ids'): for cmd in vals.get('resupply_wh_ids'): if cmd[0] == 6: @@ -3818,8 +3831,8 @@ class stock_pack_operation(osv.osv): if pack_op.qty_done < pack_op.product_qty: # we split the operation in two op = self.copy(cr, uid, pack_op.id, {'product_qty': pack_op.qty_done, 'qty_done': pack_op.qty_done}, context=context) - self.write(cr, uid, ids, {'product_qty': pack_op.product_qty - pack_op.qty_done, 'qty_done': 0}, context=context) - processed_ids.append(op) + self.write(cr, uid, [pack_op.id], {'product_qty': pack_op.product_qty - pack_op.qty_done, 'qty_done': 0, 'lot_id': False}, context=context) + processed_ids.append(op) self.write(cr, uid, processed_ids, {'processed': 'true'}, context=context) def create_and_assign_lot(self, cr, uid, id, name, context=None): @@ -3828,10 +3841,16 @@ class stock_pack_operation(osv.osv): obj = self.browse(cr,uid,id,context) product_id = obj.product_id.id val = {'product_id': product_id} + new_lot_id = False if name: + lots = self.pool.get('stock.production.lot').search(cr, uid, ['&', ('name', '=', name), ('product_id', '=', product_id)], context=context) + if lots: + new_lot_id = lots[0] val.update({'name': name}) + if not obj.lot_id: - new_lot_id = self.pool.get('stock.production.lot').create(cr, uid, val, context=context) + if not new_lot_id: + new_lot_id = self.pool.get('stock.production.lot').create(cr, uid, val, context=context) self.write(cr, uid, id, {'lot_id': new_lot_id}, context=context) def _search_and_increment(self, cr, uid, picking_id, domain, filter_visible=False, visible_op_ids=False, increment=True, context=None): @@ -3869,11 +3888,15 @@ class stock_pack_operation(osv.osv): self.write(cr, uid, [operation_id], {'qty_done': qty}, context=context) else: #no existing operation found for the given domain and picking => create a new one + picking_obj = self.pool.get("stock.picking") + picking = picking_obj.browse(cr, uid, picking_id, context=context) values = { 'picking_id': picking_id, 'product_qty': 0, + 'location_id': picking.location_id.id, + 'location_dest_id': picking.location_dest_id.id, 'qty_done': 1, - } + } for key in domain: var_name, dummy, value = key uom_id = False @@ -4072,11 +4095,11 @@ class stock_picking_type(osv.osv): tristates = [] for picking in picking_obj.browse(cr, uid, picking_ids, context=context): if picking.date_done > picking.date: - tristates.insert(0, {'tooltip': picking.name + _(': Late'), 'value': -1}) + tristates.insert(0, {'tooltip': picking.name or '' + _(': Late'), 'value': -1}) elif picking.backorder_id: - tristates.insert(0, {'tooltip': picking.name + _(': Backorder exists'), 'value': 0}) + tristates.insert(0, {'tooltip': picking.name or '' + _(': Backorder exists'), 'value': 0}) else: - tristates.insert(0, {'tooltip': picking.name + _(': OK'), 'value': 1}) + tristates.insert(0, {'tooltip': picking.name or '' + _(': OK'), 'value': 1}) res[picking_type_id] = tristates return res