diff --git a/addons/claim_from_delivery/claim_delivery_view.xml b/addons/claim_from_delivery/claim_delivery_view.xml index e1a1a43062e..8ce2e7bb7ce 100644 --- a/addons/claim_from_delivery/claim_delivery_view.xml +++ b/addons/claim_from_delivery/claim_delivery_view.xml @@ -14,8 +14,8 @@ stock.picking - - diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml index f2d2bf1253b..1913fe3e3ce 100644 --- a/addons/mrp/mrp_view.xml +++ b/addons/mrp/mrp_view.xml @@ -717,7 +717,7 @@
- - - - - - - - - - - - -
-
-
-

-

-
- - - - -
-
-
- -
-
-
-

- - -

-
-
-

-
- - - - -
-

-
-
-

- - -

-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - -
ProductScannedTodoFromTo
- - - - -
- -
- - -
- -
-
- - - -
- - - - : - : - - -
-
- - - -
-

Search Results

-
-
- - No picking found. - - - -
-
-
- -
-
-
-
-
-
-
- - - -
- -

Select your operation

- - - - -
- -
-
- - - - -
- picking(s) -
-
-
-
-
-
-
- - - - - -
-
-
- The reserved stock changed. You might want to the operations. -
-
- - - diff --git a/addons/stock/stock.py b/addons/stock/stock.py index beb09b8528d..4f96d0db47c 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -12,7 +12,7 @@ from openerp.osv import fields, osv from openerp.tools.float_utils import float_compare, float_round from openerp.tools.translate import _ from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT -from openerp import SUPERUSER_ID, api, models, fields as new_fields +from openerp import SUPERUSER_ID, api, models import openerp.addons.decimal_precision as dp from openerp.addons.procurement import procurement import logging @@ -365,9 +365,6 @@ class stock_quant(osv.osv): #reserve quants if toreserve: self.write(cr, SUPERUSER_ID, toreserve, {'reservation_id': move.id}, context=context) - #if move has a picking_id, write on that picking that pack_operation might have changed and need to be recomputed - if move.picking_id: - self.pool.get('stock.picking').write(cr, uid, [move.picking_id.id], {'recompute_pack_op': True}, context=context) #check if move'state needs to be set as 'assigned' rounding = move.product_id.uom_id.rounding if float_compare(reserved_availability, move.product_qty, precision_rounding=rounding) == 0 and move.state in ('confirmed', 'waiting') : @@ -597,8 +594,6 @@ class stock_quant(osv.osv): related_quants = [x.id for x in move.reserved_quant_ids] if related_quants: #if move has a picking_id, write on that picking that pack_operation might have changed and need to be recomputed - if move.picking_id: - self.pool.get('stock.picking').write(cr, uid, [move.picking_id.id], {'recompute_pack_op': True}, context=context) if move.partially_available: self.pool.get("stock.move").write(cr, uid, [move.id], {'partially_available': False}, context=context) self.write(cr, SUPERUSER_ID, related_quants, {'reservation_id': False}, context=context) @@ -643,7 +638,7 @@ class stock_quant(osv.osv): # Stock Picking #---------------------------------------------------------- -class stock_picking(osv.osv): +class stock_picking(models.Model): _name = "stock.picking" _inherit = ['mail.thread'] _description = "Transfer" @@ -693,8 +688,31 @@ class stock_picking(osv.osv): ptype_id = vals.get('picking_type_id', context.get('default_picking_type_id', False)) sequence_id = self.pool.get('stock.picking.type').browse(cr, user, ptype_id, context=context).sequence_id.id vals['name'] = self.pool.get('ir.sequence').next_by_id(cr, user, sequence_id, context=context) + # As the on_change in one2many list is WIP, we will overwrite the locations on the stock moves here + # As it is a create the format will be a list of (0, 0, dict) + if vals.get('move_lines') and vals.get('location_id') and vals.get('location_dest_id'): + for move in vals['move_lines']: + if len(move) == 3: + move[2]['location_id'] = vals['location_id'] + move[2]['location_dest_id'] = vals['location_dest_id'] return super(stock_picking, self).create(cr, user, vals, context) + def write(self, cr, uid, ids, vals, context=None): + res = super(stock_picking, self).write(cr, uid, ids, vals, context=context) + after_vals = {} + if vals.get('location_id'): + after_vals['location_id'] = vals['location_id'] + if vals.get('location_dest_id'): + after_vals['location_dest_id'] = vals['location_dest_id'] + # Change locations of moves if those of the picking change + if after_vals: + moves = [] + for pick in self.browse(cr, uid, ids, context=context): + moves += [x.id for x in pick.move_lines if not x.scrapped] + if moves: + self.pool['stock.move'].write(cr, uid, moves, after_vals, context=context) + return res + def _state_get(self, cr, uid, ids, field_name, arg, context=None): '''The state of a picking depends on the state of its related stock.move draft: the picking has no line or any one of the lines is draft @@ -703,7 +721,10 @@ class stock_picking(osv.osv): ''' res = {} for pick in self.browse(cr, uid, ids, context=context): - if (not pick.move_lines) or any([x.state == 'draft' for x in pick.move_lines]): + if not pick.move_lines: + res[pick.id] = pick.launch_pack_operations and 'assigned' or 'draft' + continue + if any([x.state == 'draft' for x in pick.move_lines]): res[pick.id] = 'draft' continue if all([x.state == 'cancel' for x in pick.move_lines]): @@ -759,19 +780,33 @@ class stock_picking(osv.osv): continue return res - def check_group_lot(self, cr, uid, context=None): - """ This function will return true if we have the setting to use lots activated. """ - return self.pool.get('res.users').has_group(cr, uid, 'stock.group_production_lot') - - def check_group_pack(self, cr, uid, context=None): - """ This function will return true if we have the setting to use package activated. """ - return self.pool.get('res.users').has_group(cr, uid, 'stock.group_tracking_lot') - def action_assign_owner(self, cr, uid, ids, context=None): for picking in self.browse(cr, uid, ids, context=context): packop_ids = [op.id for op in picking.pack_operation_ids] self.pool.get('stock.pack.operation').write(cr, uid, packop_ids, {'owner_id': picking.owner_id.id}, context=context) + def onchange_picking_type(self, cr, uid, ids, picking_type_id): + res = {} + if picking_type_id: + picking_type = self.pool['stock.picking.type'].browse(cr, uid, picking_type_id) + res['value'] = {'location_id': picking_type.default_location_src_id.id, + 'location_dest_id': picking_type.default_location_dest_id.id} + return res + + def _default_location_destination(self): + context = self._context or {} + if context.get('default_picking_type_id', False): + pick_type = self.env['stock.picking.type'].browse(context['default_picking_type_id']) + return pick_type.default_location_dest_id and pick_type.default_location_dest_id.id or False + return False + + def _default_location_source(self): + context = self._context or {} + if context.get('default_picking_type_id', False): + pick_type = self.env['stock.picking.type'].browse(context['default_picking_type_id']) + return pick_type.default_location_src_id and pick_type.default_location_src_id.id or False + return False + _columns = { 'name': fields.char('Reference', select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, copy=False), 'origin': fields.char('Source Document', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="Reference of the document", select=True), @@ -780,7 +815,7 @@ class stock_picking(osv.osv): 'move_type': fields.selection([('direct', 'Partial'), ('one', 'All at once')], 'Delivery Method', required=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="It specifies goods to be deliver partially or all at once"), 'state': fields.function(_state_get, type="selection", copy=False, store={ - 'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_type'], 20), + 'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_type', 'launch_pack_operations'], 20), 'stock.move': (_get_pickings, ['state', 'picking_id', 'partially_available'], 20)}, selection=[ ('draft', 'Draft'), @@ -800,6 +835,15 @@ class stock_picking(osv.osv): * Transferred: has been processed, can't be modified or cancelled anymore\n * Cancelled: has been cancelled, can't be confirmed anymore""" ), + 'location_id': fields.many2one('stock.location', required=True, string="Source Location Zone", + default=_default_location_source, readonly=True, states={'draft': [('readonly', False)]}), + 'location_dest_id': fields.many2one('stock.location', required=True,string="Destination Location Zone", + default=_default_location_destination, readonly=True, states={'draft': [('readonly', False)]}), + 'move_lines': fields.one2many('stock.move', 'picking_id', string="Stock Moves", copy=True), + 'move_lines_related': fields.related('move_lines', type='one2many', relation='stock.move', string="Move Lines"), + 'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, required=True), + 'picking_type_code': fields.related('picking_type_id', 'code', type='selection', selection=[('incoming', 'Suppliers'), ('outgoing', 'Customers'), ('internal', 'Internal')]), + 'picking_type_entire_packs': fields.related('picking_type_id', 'show_entire_packs', type='boolean'), 'priority': fields.function(get_min_max_date, multi="min_max_date", fnct_inv=_set_priority, type='selection', selection=procurement.PROCUREMENT_PRIORITIES, string='Priority', store={'stock.move': (_get_pickings, ['priority', 'picking_id'], 20)}, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, select=1, help="Priority for this picking. Setting manually a value here would set it as priority for all the moves", track_visibility='onchange', required=True), @@ -809,30 +853,23 @@ class stock_picking(osv.osv): store={'stock.move': (_get_pickings, ['date_expected', 'picking_id'], 20)}, type='datetime', string='Max. Expected Date', select=2, help="Scheduled time for the last part of the shipment to be processed"), 'date': fields.datetime('Creation Date', help="Creation Date, usually the time of the order", select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, track_visibility='onchange'), 'date_done': fields.datetime('Date of Transfer', help="Completion Date of Transfer", readonly=True, copy=False), - 'move_lines': fields.one2many('stock.move', 'picking_id', 'Internal Moves', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, copy=True), 'quant_reserved_exist': fields.function(_get_quant_reserved_exist, type='boolean', string='Quant already reserved ?', help='technical field used to know if there is already at least one quant reserved on moves of a given picking'), 'partner_id': fields.many2one('res.partner', 'Partner', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}), 'company_id': fields.many2one('res.company', 'Company', required=True, select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}), 'pack_operation_ids': fields.one2many('stock.pack.operation', 'picking_id', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, string='Related Packing Operations'), + 'pack_operation_product_ids': fields.one2many('stock.pack.operation', 'picking_id', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, domain=[('product_id', '!=', False)], string='Non pack'), + 'pack_operation_pack_ids': fields.one2many('stock.pack.operation', 'picking_id', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, domain=[('product_id', '=', False)], string='Pack'), 'pack_operation_exist': fields.function(_get_pack_operation_exist, type='boolean', string='Pack Operation Exists?', help='technical field for attrs in view'), - 'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, required=True), - 'picking_type_code': fields.related('picking_type_id', 'code', type='char', string='Picking Type Code', help="Technical field used to display the correct label on print button in the picking view"), - 'owner_id': fields.many2one('res.partner', 'Owner', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="Default Owner"), # Used to search on pickings 'product_id': fields.related('move_lines', 'product_id', type='many2one', relation='product.product', string='Product'), 'recompute_pack_op': fields.boolean('Recompute pack operation?', help='True if reserved quants changed, which mean we might need to recompute the package operations', copy=False), - 'location_id': fields.related('move_lines', 'location_id', type='many2one', relation='stock.location', string='Location', - readonly=True, store={'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_lines'], 10), - 'stock.move': (_get_pickings, ['location_id', 'picking_id'], 10),}), - 'location_dest_id': fields.related('move_lines', 'location_dest_id', type='many2one', relation='stock.location', string='Destination Location', - readonly=True, store={'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_lines'], 10), - 'stock.move': (_get_pickings, ['location_dest_id', 'picking_id'], 10),}), 'group_id': fields.related('move_lines', 'group_id', type='many2one', relation='procurement.group', string='Procurement Group', readonly=True, store={ 'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_lines'], 10), 'stock.move': (_get_pickings, ['group_id', 'picking_id'], 10), }), + 'launch_pack_operations': fields.boolean("Launch Pack Operations", copy=False), } _defaults = { @@ -842,7 +879,8 @@ class stock_picking(osv.osv): 'priority': '1', # normal 'date': fields.datetime.now, 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.picking', context=c), - 'recompute_pack_op': True, + 'recompute_pack_op': False, + 'launch_pack_operations': False, } _sql_constraints = [ ('name_uniq', 'unique(name, company_id)', 'Reference must be unique per company!'), @@ -858,10 +896,15 @@ class stock_picking(osv.osv): context = dict(context or {}, active_ids=ids) return self.pool.get("report").get_action(cr, uid, ids, 'stock.report_picking_operations', context=context) + def launch_packops(self, cr, uid, ids, context=None): + self.write(cr, uid, ids, {'launch_pack_operations': True}, context=context) + def action_confirm(self, cr, uid, ids, context=None): todo = [] todo_force_assign = [] for picking in self.browse(cr, uid, ids, context=context): + if not picking.move_lines: + self.launch_packops(cr, uid, [picking.id], context=context) if picking.location_id.usage in ('supplier', 'inventory', 'production'): todo_force_assign.append(picking.id) for r in picking.move_lines: @@ -894,11 +937,10 @@ class stock_picking(osv.osv): """ Changes state of picking to available if moves are confirmed or waiting. @return: True """ - for pick in self.browse(cr, uid, ids, context=context): + pickings = self.browse(cr, uid, ids, context=context) + for pick in pickings: move_ids = [x.id for x in pick.move_lines if x.state in ['confirmed', 'waiting']] self.pool.get('stock.move').force_assign(cr, uid, move_ids, context=context) - #pack_operation might have changed and need to be recomputed - self.write(cr, uid, ids, {'recompute_pack_op': True}, context=context) return True def action_cancel(self, cr, uid, ids, context=None): @@ -934,19 +976,6 @@ class stock_picking(osv.osv): move_obj.unlink(cr, uid, move_ids, context=context) return super(stock_picking, self).unlink(cr, uid, ids, context=context) - def write(self, cr, uid, ids, vals, context=None): - if vals.get('move_lines') and not vals.get('pack_operation_ids'): - # pack operations are directly dependant of move lines, it needs to be recomputed - pack_operation_obj = self.pool['stock.pack.operation'] - existing_package_ids = pack_operation_obj.search(cr, uid, [('picking_id', 'in', ids)], context=context) - if existing_package_ids: - pack_operation_obj.unlink(cr, uid, existing_package_ids, context) - res = super(stock_picking, self).write(cr, uid, ids, vals, context=context) - #if we changed the move lines or the pack operations, we need to recompute the remaining quantities of both - if 'move_lines' in vals or 'pack_operation_ids' in vals: - self.do_recompute_remaining_quantities(cr, uid, ids, context=context) - return res - def _create_backorder(self, cr, uid, picking, backorder_moves=[], context=None): """ Move all non-done lines into a new backorder picking. If the key 'do_only_split' is given in the context, then move all lines not in context.get('split', []) instead of all non-done lines. """ @@ -970,6 +999,7 @@ class stock_picking(osv.osv): self.write(cr, uid, [picking.id], {'date_done': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}, context=context) self.action_confirm(cr, uid, [backorder_id], context=context) + self.action_assign(cr, uid, [backorder_id], context=context) return backorder_id return False @@ -1130,16 +1160,6 @@ class stock_picking(osv.osv): processed_products.add(move.product_id.id) return vals - @api.cr_uid_ids_context - def open_barcode_interface(self, cr, uid, picking_ids, context=None): - final_url="/stock/barcode/#action=stock.ui&picking_id="+str(picking_ids[0]) - return {'type': 'ir.actions.act_url', 'url':final_url, 'target': 'self',} - - @api.cr_uid_ids_context - def do_partial_open_barcode(self, cr, uid, picking_ids, context=None): - self.do_prepare_partial(cr, uid, picking_ids, context=context) - return self.open_barcode_interface(cr, uid, picking_ids, context=context) - @api.cr_uid_ids_context def do_prepare_partial(self, cr, uid, picking_ids, context=None): context = context or {} @@ -1169,6 +1189,7 @@ class stock_picking(osv.osv): else: forced_qties[move.product_id] = forced_qty for vals in self._prepare_pack_ops(cr, uid, picking, picking_quants, forced_qties, context=context): + vals['fresh_record'] = False pack_operation_obj.create(cr, uid, vals, context=ctx) #recompute the remaining quantities all at once self.do_recompute_remaining_quantities(cr, uid, picking_ids, context=context) @@ -1189,7 +1210,7 @@ class stock_picking(osv.osv): self.pool.get('stock.pack.operation').unlink(cr, uid, pack_line_to_unreserve, context=context) self.pool.get('stock.move').do_unreserve(cr, uid, moves_to_unreserve, context=context) - def recompute_remaining_qty(self, cr, uid, picking, context=None): + def recompute_remaining_qty(self, cr, uid, picking, done_qtys=False, context=None): def _create_link_for_index(operation_id, index, product_id, qty_to_assign, quant_id=False): move_dict = prod2move_ids[product_id][index] qty_on_link = min(move_dict['remaining_qty'], qty_to_assign) @@ -1253,7 +1274,7 @@ class stock_picking(osv.osv): for ops in operations: #for each operation, create the links with the stock move by seeking on the matching reserved quants, #and deffer the operation if there is some ambiguity on the move to select - if ops.package_id and not ops.product_id: + if ops.package_id and not ops.product_id and (not done_qtys or ops.product_qty): #entire package quant_ids = package_obj.get_content(cr, uid, [ops.package_id.id], context=context) for quant in quant_obj.browse(cr, uid, quant_ids, context=context): @@ -1268,7 +1289,8 @@ class stock_picking(osv.osv): need_rereserve = True elif ops.product_id.id: #Check moves with same product - qty_to_assign = uom_obj._compute_qty_obj(cr, uid, ops.product_uom_id, ops.product_qty, ops.product_id.uom_id, context=context) + product_qty = done_qtys and ops.qty_done or ops.product_qty + qty_to_assign = uom_obj._compute_qty_obj(cr, uid, ops.product_uom_id, product_qty, ops.product_id.uom_id, context=context) for move_dict in prod2move_ids.get(ops.product_id.id, []): move = move_dict['move'] for quant in move.reserved_quant_ids: @@ -1301,18 +1323,18 @@ class stock_picking(osv.osv): all_op_processed = _create_link_for_product(ops.id, product_id, remaining_qty) and all_op_processed return (need_rereserve, all_op_processed) - def picking_recompute_remaining_quantities(self, cr, uid, picking, context=None): + def picking_recompute_remaining_quantities(self, cr, uid, picking, done_qtys=False, context=None): need_rereserve = False all_op_processed = True if picking.pack_operation_ids: - need_rereserve, all_op_processed = self.recompute_remaining_qty(cr, uid, picking, context=context) + need_rereserve, all_op_processed = self.recompute_remaining_qty(cr, uid, picking, done_qtys=done_qtys, context=context) return need_rereserve, all_op_processed @api.cr_uid_ids_context - def do_recompute_remaining_quantities(self, cr, uid, picking_ids, context=None): + def do_recompute_remaining_quantities(self, cr, uid, picking_ids, done_qtys=False, context=None): for picking in self.browse(cr, uid, picking_ids, context=context): if picking.pack_operation_ids: - self.recompute_remaining_qty(cr, uid, picking, context=context) + self.recompute_remaining_qty(cr, uid, picking, done_qtys=done_qtys, context=context) def _prepare_values_extra_move(self, cr, uid, op, product, remaining_qty, context=None): """ @@ -1371,33 +1393,75 @@ class stock_picking(osv.osv): self.action_assign(cr, uid, [picking.id], context=context) else: stock_move_obj.do_unreserve(cr, uid, move_ids, context=context) - stock_move_obj.action_assign(cr, uid, move_ids, context=context) + stock_move_obj.action_assign(cr, uid, move_ids, no_prepare=True, context=context) - @api.cr_uid_ids_context - def do_enter_transfer_details(self, cr, uid, picking, context=None): - if not context: - context = {} + def do_new_transfer(self, cr, uid, ids, context=None): + pack_op_obj = self.pool['stock.pack.operation'] + data_obj = self.pool['ir.model.data'] + for pick in self.browse(cr, uid, ids, context=context): + to_delete = [] + # In draft or with no pack operations edited yet, ask if we can just do everything + if pick.state == 'draft' or all([x.qty_done == 0.0 for x in pick.pack_operation_ids]): + view = data_obj.xmlid_to_res_id(cr, uid, 'stock.view_immediate_transfer') + wiz_id = self.pool['stock.immediate.transfer'].create(cr, uid, {'pick_id': pick.id}, context=context) + return { + 'name': _('Immediate Transfer?'), + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'stock.immediate.transfer', + 'views': [(view, 'form')], + 'view_id': view, + 'target': 'new', + 'res_id': wiz_id, + 'context': context, + } - context.update({ - 'active_model': self._name, - 'active_ids': picking, - 'active_id': len(picking) and picking[0] or False - }) + # Check backorder should check for other barcodes + if self.check_backorder(cr, uid, pick, context=context): + view = data_obj.xmlid_to_res_id(cr, uid, 'stock.view_backorder_confirmation') + wiz_id = self.pool['stock.backorder.confirmation'].create(cr, uid, {'pick_id': pick.id}, context=context) + return { + 'name': _('Create Backorder?'), + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'stock.backorder.confirmation', + 'views': [(view, 'form')], + 'view_id': view, + 'target': 'new', + 'res_id': wiz_id, + 'context': context, + } + for operation in pick.pack_operation_ids: + if operation.qty_done < 0: + raise UserError(_('No negative quantities allowed')) + if operation.qty_done > 0: + pack_op_obj.write(cr, uid, operation.id, {'product_qty': operation.qty_done}, context=context) + else: + to_delete.append(operation.id) + if to_delete: + pack_op_obj.unlink(cr, uid, to_delete, context=context) + self.do_transfer(cr, uid, ids, context=context) + return - created_id = self.pool['stock.transfer_details'].create(cr, uid, {'picking_id': len(picking) and picking[0] or False}, context) - return self.pool['stock.transfer_details'].wizard_view(cr, uid, created_id, context) + def check_backorder(self, cr, uid, picking, context=None): + need_rereserve, all_op_processed = self.picking_recompute_remaining_quantities(cr, uid, picking, done_qtys=True, context=context) + for move in picking.move_lines: + if float_compare(move.remaining_qty, 0, precision_rounding = move.product_id.uom_id.rounding) != 0: + return True + return False - - @api.cr_uid_ids_context - def do_transfer(self, cr, uid, picking_ids, context=None): + def do_transfer(self, cr, uid, ids, context=None): """ If no pack operation, we do simple action_done of the picking Otherwise, do the pack operations """ if not context: context = {} + stock_move_obj = self.pool.get('stock.move') - for picking in self.browse(cr, uid, picking_ids, context=context): + for picking in self.browse(cr, uid, ids, context=context): if not picking.pack_operation_ids: self.action_done(cr, uid, [picking.id], context=context) continue @@ -1435,8 +1499,6 @@ class stock_picking(osv.osv): elif context.get('do_only_split'): context = dict(context, split=todo_move_ids) self._create_backorder(cr, uid, picking, context=context) - if toassign_move_ids: - stock_move_obj.action_assign(cr, uid, toassign_move_ids, context=context) return True @api.cr_uid_ids_context @@ -1448,115 +1510,26 @@ class stock_picking(osv.osv): ctx['do_only_split'] = True return self.do_transfer(cr, uid, picking_ids, context=ctx) - def get_next_picking_for_ui(self, cr, uid, context=None): - """ returns the next pickings to process. Used in the barcode scanner UI""" - if context is None: - context = {} - domain = [('state', 'in', ('assigned', 'partially_available'))] - if context.get('default_picking_type_id'): - domain.append(('picking_type_id', '=', context['default_picking_type_id'])) - return self.search(cr, uid, domain, context=context) - - def action_done_from_ui(self, cr, uid, picking_id, context=None): - """ called when button 'done' is pushed in the barcode scanner UI """ - #write qty_done into field product_qty for every package_operation before doing the transfer - pack_op_obj = self.pool.get('stock.pack.operation') - for operation in self.browse(cr, uid, picking_id, context=context).pack_operation_ids: - pack_op_obj.write(cr, uid, operation.id, {'product_qty': operation.qty_done}, context=context) - self.do_transfer(cr, uid, [picking_id], context=context) - #return id of next picking to work on - return self.get_next_picking_for_ui(cr, uid, context=context) - - @api.cr_uid_ids_context - def action_pack(self, cr, uid, picking_ids, operation_filter_ids=None, context=None): - """ Create a package with the current pack_operation_ids of the picking that aren't yet in a pack. - Used in the barcode scanner UI and the normal interface as well. - operation_filter_ids is used by barcode scanner interface to specify a subset of operation to pack""" - if operation_filter_ids == None: - operation_filter_ids = [] - stock_operation_obj = self.pool.get('stock.pack.operation') - package_obj = self.pool.get('stock.quant.package') - stock_move_obj = self.pool.get('stock.move') - package_id = False - for picking_id in picking_ids: - operation_search_domain = [('picking_id', '=', picking_id), ('result_package_id', '=', False)] - if operation_filter_ids != []: - operation_search_domain.append(('id', 'in', operation_filter_ids)) - operation_ids = stock_operation_obj.search(cr, uid, operation_search_domain, context=context) + def put_in_pack(self, cr, uid, ids, context=None): + stock_move_obj = self.pool["stock.move"] + stock_operation_obj = self.pool["stock.pack.operation"] + package_obj = self.pool["stock.quant.package"] + for pick in self.browse(cr, uid, ids, context=context): + operations = [x for x in pick.pack_operation_ids if x.qty_done > 0 and (not x.result_package_id)] pack_operation_ids = [] - if operation_ids: - for operation in stock_operation_obj.browse(cr, uid, operation_ids, context=context): - #If we haven't done all qty in operation, we have to split into 2 operation - 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, 'lot_id': False}, context=context) - op = stock_operation_obj.browse(cr, uid, new_operation, context=context) - pack_operation_ids.append(op.id) - if op.product_id and op.location_id and op.location_dest_id: - stock_move_obj.check_tracking_product(cr, uid, op.product_id, op.lot_id.id, op.location_id, op.location_dest_id, context=context) + for operation in operations: + #If we haven't done all qty in operation, we have to split into 2 operation + 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, 'lot_id': False}, context=context) + op = stock_operation_obj.browse(cr, uid, new_operation, context=context) + pack_operation_ids.append(op.id) + if op.product_id and op.location_id and op.location_dest_id: + stock_move_obj.check_tracking_product(cr, uid, op.product_id, op.lot_id.id, op.location_id, op.location_dest_id, context=context) + if operations: package_id = package_obj.create(cr, uid, {}, context=context) stock_operation_obj.write(cr, uid, pack_operation_ids, {'result_package_id': package_id}, context=context) - return package_id - - def process_product_id_from_ui(self, cr, uid, picking_id, product_id, op_id, increment=1, context=None): - return self.pool.get('stock.pack.operation')._search_and_increment(cr, uid, picking_id, [('product_id', '=', product_id),('id', '=', op_id)], increment=increment, context=context) - - def process_barcode_from_ui(self, cr, uid, picking_id, barcode_str, visible_op_ids, context=None): - '''This function is called each time there barcode scanner reads an input''' - stock_operation_obj = self.pool.get('stock.pack.operation') - answer = {'filter_loc': False, 'operation_id': False} - - # Barcode Nomenclatures - picking_type_id = self.browse(cr, uid, [picking_id], context=context).picking_type_id.id - barcode_nom = self.pool.get('stock.picking.type').browse(cr, uid, [picking_type_id], context=context).barcode_nomenclature_id - parsed_result = barcode_nom.parse_barcode(barcode_str) - - #check if the barcode is a weighted barcode or simply a product - if parsed_result['type'] in ['weight', 'product', 'package']: - weight=1 - if parsed_result['type'] == 'weight': - domain = ['|', ('barcode', '=', parsed_result['base_code']), ('default_code', '=', parsed_result['base_code'])] - weight=parsed_result['value'] - obj = self.pool.get('product.product') - id_in_operation = 'product_id' - elif parsed_result['type'] == 'product': - domain = ['|', ('barcode', '=', parsed_result['code']), ('default_code', '=', parsed_result['code'])] - obj = self.pool.get('product.product') - id_in_operation = 'product_id' - else: - domain = [('name', '=', parsed_result['code'])] - obj = self.pool.get('stock.quant.package') - id_in_operation = 'package_id' - - matching_product_ids = obj.search(cr, uid, domain, context=context) - if matching_product_ids: - op_id = stock_operation_obj._search_and_increment(cr, uid, picking_id, [(id_in_operation, '=', matching_product_ids[0])], filter_visible=True, visible_op_ids=visible_op_ids, increment=weight, context=context) - answer['operation_id'] = op_id - return answer - - #check if the barcode correspond to a lot - elif parsed_result['type'] == 'lot': - lot_obj = self.pool.get('stock.production.lot') - matching_lot_ids = lot_obj.search(cr, uid, [('name', '=', parsed_result['code'])], context=context) - if matching_lot_ids: - lot = lot_obj.browse(cr, uid, matching_lot_ids[0], context=context) - op_id = stock_operation_obj._search_and_increment(cr, uid, picking_id, [('product_id', '=', lot.product_id.id), ('lot_id', '=', lot.id)], filter_visible=True, visible_op_ids=visible_op_ids, increment=1, context=context) - answer['operation_id'] = op_id - return answer - - #check if the barcode correspond to a location - elif parsed_result['type'] == 'location': - stock_location_obj = self.pool.get('stock.location') - matching_location_ids = stock_location_obj.search(cr, uid, [('barcode', '=', parsed_result['code'])], context=context) - if matching_location_ids: - #if we have a location, return immediatly with the location name - location = stock_location_obj.browse(cr, uid, matching_location_ids[0], context=None) - answer['filter_loc'] = stock_location_obj._name_get(cr, uid, location, context=None) - answer['filter_loc_id'] = matching_location_ids[0] - return answer - - return answer class stock_production_lot(osv.osv): @@ -1821,26 +1794,10 @@ class stock_move(osv.osv): '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)."), } - def _default_location_destination(self, cr, uid, context=None): - context = context or {} - if context.get('default_picking_type_id', False): - pick_type = self.pool.get('stock.picking.type').browse(cr, uid, context['default_picking_type_id'], context=context) - return pick_type.default_location_dest_id and pick_type.default_location_dest_id.id or False - return False - - def _default_location_source(self, cr, uid, context=None): - context = context or {} - if context.get('default_picking_type_id', False): - pick_type = self.pool.get('stock.picking.type').browse(cr, uid, context['default_picking_type_id'], context=context) - return pick_type.default_location_src_id and pick_type.default_location_src_id.id or False - return False - def _default_destination_address(self, cr, uid, context=None): return False _defaults = { - 'location_id': _default_location_source, - 'location_dest_id': _default_location_destination, 'partner_id': _default_destination_address, 'state': 'draft', 'priority': '1', @@ -2108,6 +2065,8 @@ class stock_move(osv.osv): 'move_type': move.group_id and move.group_id.move_type or 'direct', 'partner_id': move.partner_id.id or False, 'picking_type_id': move.picking_type_id and move.picking_type_id.id or False, + 'location_id': move.location_id.id, + 'location_dest_id': move.location_dest_id.id, } pick = pick_obj.create(cr, uid, values, context=context) return self.write(cr, uid, move_ids, {'picking_id': pick}, context=context) @@ -2183,7 +2142,9 @@ class stock_move(osv.osv): """ Changes the state to assigned. @return: True """ - return self.write(cr, uid, ids, {'state': 'assigned'}, context=context) + res = self.write(cr, uid, ids, {'state': 'assigned'}, context=context) + self.check_recompute_pack_op(cr, uid, ids, context=context) + return res def check_tracking_product(self, cr, uid, product, lot_id, location, location_dest, context=None): check = False @@ -2201,9 +2162,24 @@ class stock_move(osv.osv): """ Checks if serial number is assigned to stock move or not and raise an error if it had to. """ self.check_tracking_product(cr, uid, move.product_id, lot_id, move.location_id, move.location_dest_id, context=context) - - def action_assign(self, cr, uid, ids, context=None): + def check_recompute_pack_op(self, cr, uid, ids, context=None): + pickings = list(set([x.picking_id for x in self.browse(cr, uid, ids, context=context) if x.picking_id])) + pickings_partial = [] + pickings_write = [] + pick_obj = self.pool['stock.picking'] + for pick in pickings: + # Check if someone was treating the picking already + if not any([x.qty_done > 0 for x in pick.pack_operation_ids]): + pickings_partial.append(pick.id) + else: + pickings_write.append(pick.id) + if pickings_partial: + pick_obj.do_prepare_partial(cr, uid, pickings_partial, context=context) + if pickings_write: + pick_obj.write(cr, uid, pickings_write, {'recompute_pack_op': True}, context=context) + + def action_assign(self, cr, uid, ids, no_prepare=False, context=None): """ Checks the product type and accordingly writes the state. """ context = context or {} @@ -2266,8 +2242,11 @@ class stock_move(osv.osv): quant_obj.quants_reserve(cr, uid, quants, move, context=context) #force assignation of consumable products and incoming from supplier/inventory/production + # Do not take force_assign as it would create pack operations if to_assign_moves: - self.force_assign(cr, uid, to_assign_moves, context=context) + self.write(cr, uid, to_assign_moves, {'state': 'assigned'}, context=context) + if not no_prepare: + self.check_recompute_pack_op(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. @@ -3924,6 +3903,8 @@ class stock_pack_operation(osv.osv): _name = "stock.pack.operation" _description = "Packing Operation" + _order = "result_package_id desc, id" + def _get_remaining_prod_quantities(self, cr, uid, operation, context=None): '''Get the remaining quantities per product on an operation with a package. This function returns a dictionary''' #if the operation doesn't concern a package, it's not relevant to call this function @@ -3958,9 +3939,11 @@ class stock_pack_operation(osv.osv): def product_id_change(self, cr, uid, ids, product_id, product_uom_id, product_qty, context=None): res = self.on_change_tests(cr, uid, ids, product_id, product_uom_id, product_qty, context=context) - if product_id and not product_uom_id: - product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + uom_obj = self.pool['product.uom'] + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + if product_id and not product_uom_id or uom_obj.browse(cr, uid, product_uom_id, context=context).category_id.id != product.uom_id.category_id.id: res['value']['product_uom_id'] = product.uom_id.id + res['domain'] = {'product_uom': [('category_id','=',product.uom_id.category_id.id)]} return res def on_change_tests(self, cr, uid, ids, product_id, product_uom_id, product_qty, context=None): @@ -3984,12 +3967,38 @@ class stock_pack_operation(osv.osv): } return res + def _compute_location_description(self, cr, uid, ids, field_name, arg, context=None): + res = {} + for op in self.browse(cr, uid, ids, context=context): + from_name = op.location_id.name + to_name = op.location_dest_id.name + if op.package_id and op.product_id: + from_name += " : " + op.package_id.name + if op.result_package_id: + to_name += " : " + op.result_package_id.name + res[op.id] = {'from_loc': from_name, + 'to_loc': to_name} + return res + + def _get_bool(self, cr, uid, ids, field_name, arg, context=None): + res = {} + for pack in self.browse(cr, uid, ids, context=context): + res[pack.id] = (pack.qty_done > 0.0) + return res + + def _set_processed_qty(self, cr, uid, id, field_name, field_value, arg, context=None): + op = self.browse(cr, uid, id, context=context) + if field_value and op.qty_done == 0: + self.write(cr, uid, [id], {'qty_done': 1.0}, context=context) + return True + _columns = { 'picking_id': fields.many2one('stock.picking', 'Stock Picking', help='The stock operation where the packing has been made', required=True), 'product_id': fields.many2one('product.product', 'Product', ondelete="CASCADE"), # 1 - 'product_uom_id': fields.many2one('product.uom', 'Product Unit of Measure'), - 'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True), - 'qty_done': fields.float('Quantity Processed', digits_compute=dp.get_precision('Product Unit of Measure')), + 'product_uom_id': fields.many2one('product.uom', 'Unit of Measure'), + 'product_qty': fields.float('To Do', digits_compute=dp.get_precision('Product Unit of Measure'), required=True), + 'qty_done': fields.float('Processed', digits_compute=dp.get_precision('Product Unit of Measure')), + 'processed_boolean': fields.function(_get_bool, fnct_inv=_set_processed_qty, type='boolean', string='Processed'), 'package_id': fields.many2one('stock.quant.package', 'Source Package'), # 2 'lot_id': fields.many2one('stock.production.lot', 'Lot/Serial Number'), 'result_package_id': fields.many2one('stock.quant.package', 'Destination Package', help="If set, the operations are packed into this package", required=False, ondelete='cascade'), @@ -4002,16 +4011,30 @@ class stock_pack_operation(osv.osv): 'remaining_qty': fields.function(_get_remaining_qty, type='float', digits = 0, string="Remaining Qty", help="Remaining quantity in default UoM according to moves matched with this operation. "), 'location_id': fields.many2one('stock.location', 'Source Location', required=True), 'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True), - 'processed': fields.selection([('true','Yes'), ('false','No')],'Has been processed?', required=True), + 'from_loc': fields.function(_compute_location_description, type='char', string='From', multi='loc', readonly=True), + 'to_loc': fields.function(_compute_location_description, type='char', string='To', multi='loc', readonly=True), + 'fresh_record': fields.boolean('Newly created pack operation'), + 'state': fields.related('picking_id', 'state', type='selection', selection=[ + ('draft', 'Draft'), + ('cancel', 'Cancelled'), + ('waiting', 'Waiting Another Operation'), + ('confirmed', 'Waiting Availability'), + ('partially_available', 'Partially Available'), + ('assigned', 'Ready to Transfer'), + ('done', 'Transferred'), + ]), } _defaults = { 'date': fields.date.context_today, - 'qty_done': 0, - 'processed': lambda *a: 'false', + 'qty_done': 0.0, + 'product_qty': 0.0, + 'processed_boolean': lambda *a: False, + 'fresh_record': True, } def write(self, cr, uid, ids, vals, context=None): + vals['fresh_record'] = False context = context or {} res = super(stock_pack_operation, self).write(cr, uid, ids, vals, context=context) if isinstance(ids, (int, long)): @@ -4028,6 +4051,11 @@ class stock_pack_operation(osv.osv): self.pool.get("stock.picking").do_recompute_remaining_quantities(cr, uid, [vals['picking_id']], context=context) return res_id + def unlink(self, cr, uid, ids, context=None): + if any([x.state in ('done', 'cancel') for x in self.browse(cr, uid, ids, context=context)]): + raise UserError(_('You can not delete pack operations of a done picking')) + return super(stock_pack_operation, self).unlink(cr, uid, ids, context=context) + def action_drop_down(self, cr, uid, ids, context=None): ''' Used by barcode interface to say that pack_operation has been moved from src location to destination location, if qty_done is less than product_qty than we have to split the @@ -4406,6 +4434,7 @@ class stock_picking_type(osv.osv): 'default_location_dest_id': fields.many2one('stock.location', 'Default Destination Location'), 'code': fields.selection([('incoming', 'Suppliers'), ('outgoing', 'Customers'), ('internal', 'Internal')], 'Type of Operation', required=True), 'return_picking_type_id': fields.many2one('stock.picking.type', 'Picking Type for Returns'), + 'show_entire_packs': fields.boolean('Show entire packs to move'), 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', ondelete='cascade'), 'active': fields.boolean('Active'), diff --git a/addons/stock/stock_demo.yml b/addons/stock/stock_demo.yml index 671dc84c09c..a2dc7e537f4 100644 --- a/addons/stock/stock_demo.yml +++ b/addons/stock/stock_demo.yml @@ -66,6 +66,8 @@ picking_type_id: stock.picking_type_out origin: 'outgoing shipment main_warehouse' partner_id: base.res_partner_6 + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers move_lines: - product_id: product.product_product_27 product_uom: product.product_uom_unit @@ -79,6 +81,8 @@ origin: 'outgoing shipment' partner_id: base.res_partner_6 date: !eval "'%s-%s-15' %((datetime.now()-timedelta(days=60)).year, (datetime.now()-timedelta(days=60)).month)" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers move_lines: - product_id: product.product_product_6 product_uom: product.product_uom_unit @@ -98,6 +102,8 @@ origin: 'outgoing shipment your_company warehouse' partner_id: base.res_partner_6 date: !eval "'%s-%s-15' % ((datetime.now()-timedelta(days=30)).month,(datetime.now()-timedelta(days=30)).month)" + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers move_lines: - product_id: product.product_product_31 product_uom: product.product_uom_unit @@ -110,6 +116,8 @@ picking_type_id: stock.picking_type_out origin: 'your company warehouse' partner_id: base.res_partner_6 + location_id: stock.stock_location_stock + location_dest_id: stock.stock_location_customers move_lines: - product_id: product.product_product_5 product_uom: product.product_uom_unit @@ -123,9 +131,10 @@ !record {model: stock.picking, id: incomming_shipment}: picking_type_id: stock.picking_type_in origin: 'incoming_shipment for test' + location_id: stock_location_suppliers + location_dest_id: stock_location_14 - !record {model: stock.move, id: incomming_shipment_icecream}: - picking_id: stock.incomming_shipment product_id: product_icecream product_uom: product.product_uom_kgm product_uom_qty: 50.0 @@ -137,6 +146,8 @@ picking_type_id: stock.picking_type_in origin: 'incoming_shipment' partner_id: base.res_partner_6 + location_id: stock.stock_location_suppliers + location_dest_id: stock.stock_location_stock move_lines: - product_id: product.product_product_48 product_uom: product.product_uom_unit @@ -149,6 +160,8 @@ picking_type_id: stock.picking_type_in origin: 'incoming_shipment main_warehouse' partner_id: base.res_partner_6 + location_id: stock.stock_location_suppliers + location_dest_id: stock.stock_location_stock move_lines: - product_id: product.product_product_22 product_uom: product.product_uom_unit @@ -161,6 +174,8 @@ picking_type_id: stock.picking_type_in origin: 'incoming_shipment your_company warehouse' partner_id: base.res_partner_6 + location_id: stock.stock_location_suppliers + location_dest_id: stock.stock_location_stock move_lines: - product_id: product.product_product_10 product_uom: product.product_uom_unit @@ -170,16 +185,18 @@ location_dest_id: stock.stock_location_stock - !record {model: stock.picking, id: incomming_shipment4}: - picking_type_id: stock.picking_type_in - origin: 'incoming_shipment_main_warehouse' - partner_id: base.res_partner_6 - move_lines: - - product_id: product.product_product_31 - product_uom: product.product_uom_unit - product_uom_qty: 50.0 - picking_type_id: stock.picking_type_in - location_id: stock.stock_location_suppliers - location_dest_id: stock.stock_location_stock + picking_type_id: stock.picking_type_in + origin: 'incoming_shipment_main_warehouse' + partner_id: base.res_partner_6 + location_id: stock.stock_location_suppliers + location_dest_id: stock.stock_location_stock + move_lines: + - product_id: product.product_product_31 + product_uom: product.product_uom_unit + product_uom_qty: 50.0 + picking_type_id: stock.picking_type_in + location_id: stock.stock_location_suppliers + location_dest_id: stock.stock_location_stock - Create STOCK_PICKING_IN for Chicago Warehouse - @@ -187,6 +204,8 @@ picking_type_id: chi_picking_type_in origin: 'incoming_chicago_warehouse' partner_id: base.res_partner_6 + location_id: stock.stock_location_suppliers + location_dest_id: stock.stock_location_shop0 move_lines: - product_id: product.product_product_9 product_uom: product.product_uom_unit @@ -194,11 +213,14 @@ picking_type_id: chi_picking_type_in location_id: stock.stock_location_suppliers location_dest_id: stock.stock_location_shop0 + - !record {model: stock.picking, id: incomming_chicago_warehouse1}: picking_type_id: chi_picking_type_in origin: 'incoming_shipment_chicago_warehouse' partner_id: base.res_partner_6 + location_id: stock.stock_location_suppliers + location_dest_id: stock.stock_location_shop0 move_lines: - product_id: product.product_product_48 product_uom: product.product_uom_unit @@ -218,6 +240,8 @@ picking_type_id: chi_picking_type_in origin: 'incoming_shipment chicago_warehouse' partner_id: base.res_partner_6 + location_id: stock.stock_location_suppliers + location_dest_id: stock.stock_location_shop0 move_lines: - product_id: product.product_product_32 product_uom: product.product_uom_unit @@ -231,6 +255,8 @@ origin: 'chicago_warehouse' partner_id: base.res_partner_6 date: !eval "'%s-%s-2' % ((datetime.now()-timedelta(days=30)).month,(datetime.now()-timedelta(days=30)).month)" + location_id: stock.stock_location_suppliers + location_dest_id: stock.stock_location_shop0 move_lines: - product_id: product.product_product_22 product_uom: product.product_uom_unit @@ -245,6 +271,8 @@ picking_type_id: chi_picking_type_out origin: 'outgoing_chicago_warehouse' partner_id: base.res_partner_6 + location_id: stock.stock_location_shop0 + location_dest_id: stock.stock_location_customers move_lines: - product_id: product.product_product_27 product_uom: product.product_uom_unit @@ -257,6 +285,8 @@ picking_type_id: chi_picking_type_out origin: 'outgoing_shipment_chicago_warehouse' partner_id: base.res_partner_6 + location_id: stock.stock_location_shop0 + location_dest_id: stock.stock_location_customers move_lines: - product_id: product.product_product_6 product_uom: product.product_uom_unit @@ -275,6 +305,8 @@ picking_type_id: chi_picking_type_out origin: 'chicago_warehouse' partner_id: base.res_partner_6 + location_id: stock.stock_location_shop0 + location_dest_id: stock.stock_location_customers move_lines: - product_id: product.product_product_31 product_uom: product.product_uom_unit @@ -286,6 +318,8 @@ !record {model: stock.picking, id: outgoing_chicago_warehouse3}: picking_type_id: chi_picking_type_out origin: 'outgoing chicago warehouse' + location_id: stock.stock_location_shop0 + location_dest_id: stock.stock_location_customers partner_id: base.res_partner_6 move_lines: - product_id: product.product_product_5 @@ -302,4 +336,4 @@ record_chi_out = self.browse(cr, uid, ref('outgoing_chicago_warehouse1'), context=context) if record.state != 'done' and record_chi_in.state != 'done' and record_chi_out.state != 'done': self.force_assign(cr, uid, [ref('outgoing_shipment_main_warehouse'),ref('outgoing_chicago_warehouse3'),ref('outgoing_shipment_main_warehouse2')], context=context) - self.do_transfer(cr, uid, [ref('outgoing_shipment_main_warehouse1'),ref('incomming_chicago_warehouse1'),ref('outgoing_chicago_warehouse1')], context=context) + self.do_transfer(cr, uid, [ref('outgoing_shipment_main_warehouse1'),ref('incomming_chicago_warehouse1'),ref('outgoing_chicago_warehouse1')], context=context) \ No newline at end of file diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index 1319a61d90c..e0659d35b82 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -689,20 +689,21 @@
-
- - +

@@ -710,12 +711,12 @@ + + - -

@@ -1146,7 +1166,7 @@ - + @@ -1161,15 +1181,11 @@
-
- + -