From 2d16599860fc28bb832e313c0b84927656b928b0 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Tue, 28 Jan 2014 11:58:09 +0100 Subject: [PATCH 01/10] [WIP] Should take into account chained moves when doing return picking bzr revid: jco@openerp.com-20140128105809-eyqsxr9q4laks86o --- addons/stock/stock.py | 17 ++-------- addons/stock/stock_view.xml | 1 + addons/stock/wizard/stock_return_picking.py | 37 +++++++++++++++------ 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index bc945d7ee06..281a0504410 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -819,14 +819,6 @@ class stock_picking(osv.osv): self.pool.get('stock.move').force_assign(cr, uid, move_ids, context=context) return True - def cancel_assign(self, cr, uid, ids, context=None): - """ Cancels picking for the moves that are in the assigned state - @return: True - """ - for pick in self.browse(cr, uid, ids, context=context): - move_ids = [x.id for x in pick.move_lines if x not in ['done', 'cancel']] - self.pool.get('stock.move').cancel_assign(cr, uid, move_ids, context=context) - return True def action_cancel(self, cr, uid, ids, context=None): for pick in self.browse(cr, uid, ids, context=context): @@ -1307,6 +1299,7 @@ class stock_move(osv.osv): 'price_unit': fields.float('Unit Price', help="Technical field used to record the product cost set by the user during a picking confirmation (when costing method used is 'average price' or 'real'). Value given in company currency and in product uom."), # as it's a technical field, we intentionally don't provide the digits attribute 'company_id': fields.many2one('res.company', 'Company', required=True, select=True), + 'split_from': fields.many2one('stock.move', string="Move Split From", select=True), 'backorder_id': fields.related('picking_id', 'backorder_id', type='many2one', relation="stock.picking", string="Back Order of", select=True), 'origin': fields.char("Source"), 'procure_method': fields.selection([('make_to_stock', 'Make to Stock'), ('make_to_order', 'Make to Order')], 'Procurement Method', required=True, help="Make to Stock: When needed, the product is taken from the stock or we wait for replenishment. \nMake to Order: When needed, the product is purchased or produced."), @@ -1697,11 +1690,6 @@ class stock_move(osv.osv): """ return self.write(cr, uid, ids, {'state': 'assigned'}) - def cancel_assign(self, cr, uid, ids, context=None): - """ Changes the state to confirmed. - @return: True - """ - return self.write(cr, uid, ids, {'state': 'confirmed'}) def check_tracking(self, cr, uid, move, lot_id, context=None): """ Checks if serial number is assigned to stock move or not and raise an error if it had to. @@ -1961,7 +1949,8 @@ class stock_move(osv.osv): 'move_dest_id': False, 'reserved_quant_ids': [], 'restrict_lot_id': restrict_lot_id, - 'restrict_partner_id': restrict_partner_id + 'restrict_partner_id': restrict_partner_id, + 'split_from': move.id, } if context.get('source_location_id'): defaults['location_id'] = context['source_location_id'] diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index 524773eff2e..6b0a91014ce 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -852,6 +852,7 @@ + diff --git a/addons/stock/wizard/stock_return_picking.py b/addons/stock/wizard/stock_return_picking.py index c16c0d51230..e58a635c10a 100644 --- a/addons/stock/wizard/stock_return_picking.py +++ b/addons/stock/wizard/stock_return_picking.py @@ -62,18 +62,30 @@ class stock_return_picking(osv.osv_memory): record_id = context and context.get('active_id', False) or False pick_obj = self.pool.get('stock.picking') pick = pick_obj.browse(cr, uid, record_id, context=context) + quant_obj = self.pool.get("stock.quant") + move_obj = self.pool.get("stock.move") if pick: if pick.state != 'done': raise osv.except_osv(_('Warning!'), _("You may only return pickings that are Done!")) - for line in pick.move_lines: - qty = line.product_uom_qty - if line.returned_move_ids: - for returned_move in line.returned_move_ids: - if returned_move.product_id.id == line.product_id.id: - qty -= returned_move.product_uom_qty - - if qty > 0: - result1.append({'product_id': line.product_id.id, 'quantity': qty, 'move_id': line.id}) + #Check if chained moves are not done already. In the mean time, sum the quants in that location + all_moves = [] + quants = [] + for move in pick.move_lines: + if move.move_dest_id: + #Backorder moves + moves = move_obj.search(cr, uid, [('split_from', '=', move.move_dest_id.id)], context=context) + moves = [move.move_dest_id] + move_obj.browse(cr, uid, moves, context=context) + if all([x.state in ['done'] for x in moves]): + raise osv.except_osv(_('Warning!'), _('You can not reverse when the chained moves are done!')) + #Search quants + quant_search = quant_obj.search(cr, uid, ['&', '&', ('history_ids', 'in', move.id), ('qty', '>', 0.0), + ('location_id', '=', move.location_dest_id.id), ], context=context) + #('reservation_id.origin_returned_move_id', '!=', move.id) + quants += [(move.id, x) for x in quant_obj.browse(cr, uid, quant_search, context=context)] + + for quant in quants: + result1.append({'product_id': quant[1].product_id.id, 'quantity': quant[1].qty, 'move_id': quant[0]}) + if len(result1) == 0: raise osv.except_osv(_('Warning!'), _("No products to return (only lines in Done state and not fully returned yet can be returned)!")) if 'product_return_moves' in fields: @@ -95,13 +107,18 @@ class stock_return_picking(osv.osv_memory): #Create new picking for returned products pick_type_id = pick.picking_type_id.return_picking_type_id and pick.picking_type_id.return_picking_type_id.id or pick.picking_type_id.id + # Cancel assignment of existing chained assigned moves + moves_to_unreserve = [x.move_dest_id.id for x in pick.move_lines if x.move_dest_id and x.move_dest_id.state == "assigned"] + if moves_to_unreserve: + move_obj.do_unreserve(cr, uid, moves_to_unreserve, context=context) + new_picking = pick_obj.copy(cr, uid, pick.id, { 'move_lines': [], 'picking_type_id': pick_type_id, 'state': 'draft', 'origin': pick.name, },context=context) - + for data_get in data_obj.browse(cr, uid, data['product_return_moves'], context=context): move = data_get.move_id if not move: From 4370ed289ffb84060962913e3c1fe5256f10f98a Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Tue, 28 Jan 2014 12:19:59 +0100 Subject: [PATCH 02/10] [IMP] commit bzr revid: jco@openerp.com-20140128111959-2sku5ymx71xpco87 --- addons/stock/wizard/stock_return_picking.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/stock/wizard/stock_return_picking.py b/addons/stock/wizard/stock_return_picking.py index e58a635c10a..c32333ff0dd 100644 --- a/addons/stock/wizard/stock_return_picking.py +++ b/addons/stock/wizard/stock_return_picking.py @@ -67,6 +67,7 @@ class stock_return_picking(osv.osv_memory): if pick: if pick.state != 'done': raise osv.except_osv(_('Warning!'), _("You may only return pickings that are Done!")) + #Check if chained moves are not done already. In the mean time, sum the quants in that location all_moves = [] quants = [] @@ -80,12 +81,12 @@ class stock_return_picking(osv.osv_memory): #Search quants quant_search = quant_obj.search(cr, uid, ['&', '&', ('history_ids', 'in', move.id), ('qty', '>', 0.0), ('location_id', '=', move.location_dest_id.id), ], context=context) - #('reservation_id.origin_returned_move_id', '!=', move.id) + # ('reservation_id.origin_returned_move_id', '!=', move.id) quants += [(move.id, x) for x in quant_obj.browse(cr, uid, quant_search, context=context)] for quant in quants: result1.append({'product_id': quant[1].product_id.id, 'quantity': quant[1].qty, 'move_id': quant[0]}) - + if len(result1) == 0: raise osv.except_osv(_('Warning!'), _("No products to return (only lines in Done state and not fully returned yet can be returned)!")) if 'product_return_moves' in fields: From e89b9b8539db56501e7abb18407b606d8615b81c Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Wed, 29 Jan 2014 16:56:38 +0100 Subject: [PATCH 03/10] [WIP] Take into account already returned moves bzr revid: jco@openerp.com-20140129155638-q9nfmm728y2l03ae --- addons/stock/stock.py | 3 ++- addons/stock/wizard/stock_return_picking.py | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 281a0504410..33c76f71959 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1384,7 +1384,8 @@ class stock_move(osv.osv): default['reserved_quant_ids'] = [] default['returned_move_ids'] = [] default['linked_move_operation_ids'] = [] - default['origin_returned_move_id'] = False + if not default.get('origin_returned_move_id'): + default['origin_returned_move_id'] = False default['state'] = 'draft' return super(stock_move, self).copy_data(cr, uid, id, default, context) diff --git a/addons/stock/wizard/stock_return_picking.py b/addons/stock/wizard/stock_return_picking.py index c32333ff0dd..3bd28ba4d38 100644 --- a/addons/stock/wizard/stock_return_picking.py +++ b/addons/stock/wizard/stock_return_picking.py @@ -80,9 +80,11 @@ class stock_return_picking(osv.osv_memory): raise osv.except_osv(_('Warning!'), _('You can not reverse when the chained moves are done!')) #Search quants quant_search = quant_obj.search(cr, uid, ['&', '&', ('history_ids', 'in', move.id), ('qty', '>', 0.0), - ('location_id', '=', move.location_dest_id.id), ], context=context) - # ('reservation_id.origin_returned_move_id', '!=', move.id) - quants += [(move.id, x) for x in quant_obj.browse(cr, uid, quant_search, context=context)] + ('location_id', 'child_of', move.location_dest_id.id), ], context=context) + quants += [(move.id, x) for x in quant_obj.browse(cr, uid, quant_search, context=context) if not x.reservation_id or x.reservation_id.origin_returned_move_id.id != move.id] + + # Still need to group by move/product? (move has only one product => ...) + for quant in quants: result1.append({'product_id': quant[1].product_id.id, 'quantity': quant[1].qty, 'move_id': quant[0]}) From b62f8a6469662f5d79bdd348bdb64c1cfe4cf077 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Thu, 30 Jan 2014 10:53:28 +0100 Subject: [PATCH 04/10] [FIX] When move is a return, assigned quants should have been processed by the original move bzr revid: jco@openerp.com-20140130095328-v63lo6dg1d951z2f --- addons/stock/stock.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 33c76f71959..df8afdb5f14 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1727,6 +1727,8 @@ class stock_move(osv.osv): fallback_domain = prev_quant_ids and [('id', 'not in', prev_quant_ids)] or [] #we always keep the quants already assigned and try to find the remaining quantity on quants not assigned only main_domain = [('reservation_id', '=', False), ('qty', '>', 0)] + if move.origin_returned_move_id: + main_domain += [('history_ids', 'in', move.origin_returned_move_id.id)] #first try to find quants based on specific domains given by linked operations for record in move.linked_move_operation_ids: domain = main_domain + self.pool.get('stock.move.operation.link').get_specific_domain(cr, uid, record, context=context) @@ -1792,6 +1794,8 @@ class stock_move(osv.osv): pickings.add(move.picking_id.id) qty = move.product_qty main_domain = [('qty', '>', 0)] + if move.origin_returned_move_id: + main_domain += [('history_ids', 'in', move.origin_returned_move_id.id)] prefered_domain = [('reservation_id', '=', move.id)] fallback_domain = [('reservation_id', '=', False)] #first, process the move per linked operation first because it may imply some specific domains to consider From d93a12588fafa2a6711a0b3568a5fc1efbdbd8b8 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Thu, 30 Jan 2014 12:01:06 +0100 Subject: [PATCH 05/10] [IMP] Add possibility to cancel chained moves bzr revid: jco@openerp.com-20140130110106-1jjpiiku546dux67 --- addons/stock/wizard/stock_return_picking.py | 12 +++++++++++- addons/stock/wizard/stock_return_picking_view.xml | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/addons/stock/wizard/stock_return_picking.py b/addons/stock/wizard/stock_return_picking.py index 3bd28ba4d38..37252f3740b 100644 --- a/addons/stock/wizard/stock_return_picking.py +++ b/addons/stock/wizard/stock_return_picking.py @@ -43,6 +43,8 @@ class stock_return_picking(osv.osv_memory): _description = 'Return Picking' _columns = { 'product_return_moves': fields.one2many('stock.return.picking.line', 'wizard_id', 'Moves'), + 'move_dest_exists': fields.boolean('Chained Move Exists', readonly = True), + 'move_dest_cancel': fields.boolean('Cancel Chained Moves'), } def default_get(self, cr, uid, fields, context=None): @@ -64,6 +66,7 @@ class stock_return_picking(osv.osv_memory): pick = pick_obj.browse(cr, uid, record_id, context=context) quant_obj = self.pool.get("stock.quant") move_obj = self.pool.get("stock.move") + move_dest = False if pick: if pick.state != 'done': raise osv.except_osv(_('Warning!'), _("You may only return pickings that are Done!")) @@ -73,6 +76,7 @@ class stock_return_picking(osv.osv_memory): quants = [] for move in pick.move_lines: if move.move_dest_id: + move_dest = True #Backorder moves moves = move_obj.search(cr, uid, [('split_from', '=', move.move_dest_id.id)], context=context) moves = [move.move_dest_id] + move_obj.browse(cr, uid, moves, context=context) @@ -80,7 +84,7 @@ class stock_return_picking(osv.osv_memory): raise osv.except_osv(_('Warning!'), _('You can not reverse when the chained moves are done!')) #Search quants quant_search = quant_obj.search(cr, uid, ['&', '&', ('history_ids', 'in', move.id), ('qty', '>', 0.0), - ('location_id', 'child_of', move.location_dest_id.id), ], context=context) + ('location_id', 'child_of', move.location_dest_id.id)], context=context) quants += [(move.id, x) for x in quant_obj.browse(cr, uid, quant_search, context=context) if not x.reservation_id or x.reservation_id.origin_returned_move_id.id != move.id] # Still need to group by move/product? (move has only one product => ...) @@ -93,6 +97,8 @@ class stock_return_picking(osv.osv_memory): raise osv.except_osv(_('Warning!'), _("No products to return (only lines in Done state and not fully returned yet can be returned)!")) if 'product_return_moves' in fields: res.update({'product_return_moves': result1}) + if 'move_dest_exists' in fields: + res.update({'move_dest_exists': move_dest}) return res def _create_returns(self, cr, uid, ids, context=None): @@ -143,6 +149,10 @@ class stock_return_picking(osv.osv_memory): 'reserved_quant_ids': quant_ids, 'origin_returned_move_id': move.id, }) + if data['move_dest_cancel']: + if move.move_dest_id: + res = move_obj.split(cr, uid, move.move_dest_id, new_qty, context=context) + move_obj.action_cancel(cr, uid, [res], context=context) if not returned_lines: raise osv.except_osv(_('Warning!'), _("Please specify at least one non-zero quantity.")) diff --git a/addons/stock/wizard/stock_return_picking_view.xml b/addons/stock/wizard/stock_return_picking_view.xml index 49b0c9a0e5c..09353a404f8 100644 --- a/addons/stock/wizard/stock_return_picking_view.xml +++ b/addons/stock/wizard/stock_return_picking_view.xml @@ -42,6 +42,8 @@