From 2cb3a90c1c3a67c9ed878ffc84a24f92f2546d16 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Wed, 8 Apr 2015 11:57:10 +0200 Subject: [PATCH] [IMP] Barcode interface and transfer wizard in picking form instead Pack operations will not appear in a transfer wizard or bar code interface anymore, but in the picking form itself. Some wizards are still needed however to ask whether a backorder needs to be created and whether we need to do an immediate transfer The biggest change in the model is in the picking where the locations of the picking are not a related anymore, but need to be set. That is why the tests need to be changed. This allows also to have pickings without moves initially and just move one pack e.g. Locations won't be visible on stock moves in a picking either. (as they should be those of the picking) In order to avoid confusion, the pack operations are created immediately when stock is reserved and we are not waiting until the wizard or the bar code interface is launched anymore. The recompute button previously in the barcode interface will only appear when the reservation changed and the user indicated in the pack operations that something was moved already. Otherwise it will recompute the pack operations. That way the reservation on the stock moves, is reflected by the pack operations. Return pickings are not confirmed automatically when confirming the return wizard. That should make it possible to choose another location for the return. (Don't return in output e.g.) Barcode scanning is not in this commit. --- .../claim_delivery_view.xml | 4 +- addons/mrp/mrp_view.xml | 2 +- addons/point_of_sale/point_of_sale.py | 20 +- addons/purchase/purchase.py | 4 +- addons/purchase/test/fifo_returns.yml | 6 +- addons/stock/__openerp__.py | 7 +- addons/stock/procurement.py | 20 +- addons/stock/security/ir.model.access.csv | 4 +- addons/stock/static/src/css/barcode.css | 134 --- addons/stock/static/src/css/stock.css | 21 - addons/stock/static/src/img/scan.png | Bin 5405 -> 0 bytes addons/stock/static/src/js/widgets.js | 1070 ----------------- .../static/src/less/stock_dashboard.less | 6 - addons/stock/static/src/xml/picking.xml | 274 ----- addons/stock/stock.py | 455 +++---- addons/stock/stock_demo.yml | 58 +- addons/stock/stock_view.xml | 162 +-- addons/stock/test/packingneg.yml | 5 - addons/stock/test/procrule.yml | 4 +- addons/stock/test/wiseoperator.yml | 12 +- addons/stock/tests/test_owner_available.py | 14 +- addons/stock/tests/test_stock_flow.py | 32 +- addons/stock/views/stock.xml | 68 -- addons/stock/wizard/__init__.py | 4 +- .../wizard/stock_backorder_confirmation.py | 41 + .../wizard/stock_backorder_confirmation.xml | 20 + .../stock/wizard/stock_immediate_transfer.py | 33 + .../stock/wizard/stock_immediate_transfer.xml | 21 + addons/stock/wizard/stock_pack_details.py | 72 ++ addons/stock/wizard/stock_pack_details.xml | 57 + addons/stock/wizard/stock_return_picking.py | 4 +- addons/stock/wizard/stock_transfer_details.py | 164 --- .../stock/wizard/stock_transfer_details.xml | 61 - addons/stock_account/stock_account_view.xml | 2 +- addons/stock_invoice_directly/__init__.py | 1 + .../stock_invoice_directly.py | 11 +- addons/stock_invoice_directly/wizards.py | 56 + .../stock_picking_wave_demo.xml | 12 +- 38 files changed, 783 insertions(+), 2158 deletions(-) delete mode 100644 addons/stock/static/src/css/barcode.css delete mode 100644 addons/stock/static/src/css/stock.css delete mode 100644 addons/stock/static/src/img/scan.png delete mode 100644 addons/stock/static/src/js/widgets.js delete mode 100644 addons/stock/static/src/less/stock_dashboard.less delete mode 100644 addons/stock/static/src/xml/picking.xml delete mode 100644 addons/stock/views/stock.xml create mode 100644 addons/stock/wizard/stock_backorder_confirmation.py create mode 100644 addons/stock/wizard/stock_backorder_confirmation.xml create mode 100644 addons/stock/wizard/stock_immediate_transfer.py create mode 100644 addons/stock/wizard/stock_immediate_transfer.xml create mode 100644 addons/stock/wizard/stock_pack_details.py create mode 100644 addons/stock/wizard/stock_pack_details.xml delete mode 100644 addons/stock/wizard/stock_transfer_details.py delete mode 100644 addons/stock/wizard/stock_transfer_details.xml create mode 100644 addons/stock_invoice_directly/wizards.py 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 @@
-
- + -