[FIX] stock, return picking wizard: several bugs fixed (see details bellow) and removal of feature to recreate or cancel chained operations (because it was totally bugged, not easily achievable and it's a new feature).
bugs fixed: 1) do_unreserve() on stock.move was setting back to 'confirmed' state also moves in 'done' or 'cancel' state 2) in return picking wizard, unreservation of quants was made only on the chained operation regardless if it had been split before or not 3) lot management in return picking wizard 4) new implementation or return picking wizard in order to only allow a manual fixing 5) uom support in return picking wizard bzr revid: qdp-launchpad@openerp.com-20140206123852-g22nkgcxud0c9tzr
This commit is contained in:
@@ -993,11 +993,11 @@ class stock_picking(osv.osv):
|
||||
moves_to_unreserve = []
|
||||
pack_line_to_unreserve = []
|
||||
for picking in self.browse(cr, uid, picking_ids, context=context):
|
||||
moves_to_unreserve += [m.id for m in picking.move_lines]
|
||||
moves_to_unreserve += [m.id for m in picking.move_lines if m.state not in ('done', 'cancel')]
|
||||
pack_line_to_unreserve += [p.id for p in picking.pack_operation_ids]
|
||||
if pack_line_to_unreserve:
|
||||
self.pool.get('stock.pack.operation').unlink(cr, uid, pack_line_to_unreserve, context=context)
|
||||
if moves_to_unreserve:
|
||||
if pack_line_to_unreserve:
|
||||
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 do_recompute_remaining_quantities(self, cr, uid, picking_ids, context=None):
|
||||
@@ -1487,6 +1487,8 @@ class stock_move(osv.osv):
|
||||
def do_unreserve(self, cr, uid, move_ids, context=None):
|
||||
quant_obj = self.pool.get("stock.quant")
|
||||
for move in self.browse(cr, uid, move_ids, context=context):
|
||||
if move.state in ('done', 'cancel'):
|
||||
raise osv.except_osv(_('Operation Forbidden!'), _('Cannot unreserve a done move'))
|
||||
quant_obj.quants_unreserve(cr, uid, move, context=context)
|
||||
putaway_values = []
|
||||
for putaway_rec in move.putaway_ids:
|
||||
@@ -1521,7 +1523,10 @@ class stock_move(osv.osv):
|
||||
def _push_apply(self, cr, uid, moves, context=None):
|
||||
push_obj = self.pool.get("stock.location.path")
|
||||
for move in moves:
|
||||
if not move.move_dest_id:
|
||||
#1) if the move is already chained, there is no need to check push rules
|
||||
#2) if the move is a returned move, we don't want to check push rules, as returning a returned move is the only decent way
|
||||
# to receive goods without triggering the push rules again (which would duplicate chained operations)
|
||||
if not move.move_dest_id and not move.origin_returned_move_id:
|
||||
domain = [('location_from_id', '=', move.location_dest_id.id)]
|
||||
if move.warehouse_id:
|
||||
domain += ['|', ('warehouse_id', '=', move.warehouse_id.id), ('warehouse_id', '=', False)]
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import time
|
||||
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.tools.translate import _
|
||||
import openerp.addons.decimal_precision as dp
|
||||
@@ -34,7 +32,7 @@ class stock_return_picking_line(osv.osv_memory):
|
||||
'quantity': fields.float("Quantity", digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
|
||||
'wizard_id': fields.many2one('stock.return.picking', string="Wizard"),
|
||||
'move_id': fields.many2one('stock.move', "Move"),
|
||||
'lot_id': fields.related('move_id', 'quant_ids', 'lot_id', type="many2one", relation='stock.production.lot', string='Serial Number', readonly=True),
|
||||
'lot_id': fields.many2one('stock.production.lot', 'Serial Number', help="Used to choose the lot/serial number of the product returned"),
|
||||
}
|
||||
|
||||
|
||||
@@ -43,12 +41,7 @@ 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, help="Technical field used to hide 'move_dest_method' and help tooltip if not needed"),
|
||||
'move_dest_method': fields.selection([('cancel', 'Cancel'), ('create', 'Recreate'), ('nothing', 'Fix Manually')], string="Chained Picking Resolution"),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'move_dest_method': 'nothing',
|
||||
'move_dest_exists': fields.boolean('Chained Move Exists', readonly=True, help="Technical field used to hide help tooltip if not needed"),
|
||||
}
|
||||
|
||||
def default_get(self, cr, uid, fields, context=None):
|
||||
@@ -66,10 +59,10 @@ class stock_return_picking(osv.osv_memory):
|
||||
context = {}
|
||||
res = super(stock_return_picking, self).default_get(cr, uid, fields, context=context)
|
||||
record_id = context and context.get('active_id', False) or False
|
||||
uom_obj = self.pool.get('product.uom')
|
||||
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")
|
||||
chained_move_exist = False
|
||||
if pick:
|
||||
if pick.state != 'done':
|
||||
@@ -78,13 +71,13 @@ class stock_return_picking(osv.osv_memory):
|
||||
for move in pick.move_lines:
|
||||
if move.move_dest_id:
|
||||
chained_move_exist = True
|
||||
#Sum the quants in that location that can be returned (they must belong to the moves that were included in the returned picking)
|
||||
#Sum the quants in that location that can be returned (they should have been moved by the moves that were included in the returned picking)
|
||||
qty = 0
|
||||
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)
|
||||
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)
|
||||
for quant in quant_obj.browse(cr, uid, quant_search, context=context):
|
||||
if not quant.reservation_id or quant.reservation_id.origin_returned_move_id.id != move.id:
|
||||
qty += quant.qty
|
||||
qty = uom_obj._compute_qty(cr, uid, move.product_id.uom_id.id, qty, move.product_uom.id)
|
||||
result1.append({'product_id': move.product_id.id, 'quantity': qty, 'move_id': move.id})
|
||||
|
||||
if len(result1) == 0:
|
||||
@@ -106,11 +99,23 @@ class stock_return_picking(osv.osv_memory):
|
||||
pick = pick_obj.browse(cr, uid, record_id, context=context)
|
||||
data = self.read(cr, uid, ids[0], context=context)
|
||||
returned_lines = 0
|
||||
|
||||
|
||||
# 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.quant_ids]
|
||||
moves_to_unreserve = []
|
||||
for move in pick.move_lines:
|
||||
to_check_moves = [move.move_dest_id]
|
||||
while to_check_moves:
|
||||
current_move = to_check_moves.pop()
|
||||
if current_move.state not in ('done', 'cancel') and current_move.reserved_quant_ids:
|
||||
moves_to_unreserve.append(current_move.id)
|
||||
split_move_ids = move_obj.search(cr, uid, [('split_from', '=', current_move.id)], context=context)
|
||||
if split_move_ids:
|
||||
to_check_moves += move_obj.browse(cr, uid, split_move_ids, context=context)
|
||||
|
||||
if moves_to_unreserve:
|
||||
move_obj.do_unreserve(cr, uid, moves_to_unreserve, context=context)
|
||||
#break the link between moves in order to be able to fix them later if needed
|
||||
move_obj.write(cr, uid, moves_to_unreserve, {'move_orig_ids': False}, context=context)
|
||||
|
||||
#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
|
||||
@@ -119,8 +124,8 @@ class stock_return_picking(osv.osv_memory):
|
||||
'picking_type_id': pick_type_id,
|
||||
'state': 'draft',
|
||||
'origin': pick.name,
|
||||
},context=context)
|
||||
|
||||
}, 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:
|
||||
@@ -138,25 +143,9 @@ class stock_return_picking(osv.osv_memory):
|
||||
'location_dest_id': move.location_id.id,
|
||||
'origin_returned_move_id': move.id,
|
||||
'procure_method': 'make_to_stock',
|
||||
'restrict_lot_id': data_get.lot_id.id,
|
||||
})
|
||||
|
||||
if data['move_dest_method'] == 'cancel':
|
||||
if move.move_dest_id and move.move_dest_id.state not in ['done', 'cancel']:
|
||||
res = move_obj.split(cr, uid, move.move_dest_id, new_qty, context=context)
|
||||
move_obj.action_cancel(cr, uid, [res], context=context)
|
||||
if data['move_dest_method'] == 'create':
|
||||
if move.move_dest_id and move.move_dest_id.state not in ['done', 'cancel']:
|
||||
res = move_obj.split(cr, uid, move.move_dest_id, new_qty, context=context)
|
||||
new_move = move_obj.copy(cr, uid, move.id, {
|
||||
'location_id': move.location_id.id,
|
||||
'location_dest_id': move.location_dest_id.id,
|
||||
'product_uom_qty': new_qty,
|
||||
'product_uos_qty': uom_obj._compute_qty(cr, uid, move.product_uom.id, new_qty, move.product_uos.id),
|
||||
'move_dest_id': res,
|
||||
'picking_id': False,
|
||||
})
|
||||
move_obj.action_confirm(cr, uid, [new_move], context=context)
|
||||
|
||||
if not returned_lines:
|
||||
raise osv.except_osv(_('Warning!'), _("Please specify at least one non-zero quantity."))
|
||||
|
||||
@@ -177,13 +166,13 @@ class stock_return_picking(osv.osv_memory):
|
||||
new_picking_id, pick_type_id = self._create_returns(cr, uid, ids, context=context)
|
||||
# Override the context to disable all the potential filters that could have been set previously
|
||||
ctx = {
|
||||
'search_default_picking_type_id': pick_type_id,
|
||||
'search_default_draft': False,
|
||||
'search_default_assigned': False,
|
||||
'search_default_confirmed': False,
|
||||
'search_default_ready': False,
|
||||
'search_default_late': False,
|
||||
'search_default_available': False,
|
||||
'search_default_picking_type_id': pick_type_id,
|
||||
'search_default_draft': False,
|
||||
'search_default_assigned': False,
|
||||
'search_default_confirmed': False,
|
||||
'search_default_ready': False,
|
||||
'search_default_late': False,
|
||||
'search_default_available': False,
|
||||
}
|
||||
return {
|
||||
'domain': "[('id', 'in', [" + str(new_picking_id) + "])]",
|
||||
|
||||
@@ -43,18 +43,16 @@
|
||||
<form string="Return lines" version="7.0">
|
||||
<group string="Provide the quantities of the returned products.">
|
||||
<field name="move_dest_exists" invisible="1"/>
|
||||
<div colspan="4" class="oe_grey oe_inline" attrs="{'invisible': [('move_dest_exists','=',False)]}">
|
||||
<p>This picking is chained with at least another operation. Choose what you want to do with it:</p>
|
||||
<ul>
|
||||
<li>Cancel: use this option if you're not going to receive the goods you're returning. The next picking will be split and the returned part will be cancelled.</li>
|
||||
<li>Recreate: use this option if you plan to receive the returned goods later on. The next picking will be split and linked to another incoming picking for the same quantity</li>
|
||||
<li>Fix manually: use this option if none of the 2 other suits you or if you want to process the next picking in a single time.</li>
|
||||
</ul>
|
||||
<div attrs="{'invisible': [('move_dest_exists', '=', False)]}" class="oe_grey" colspan="4">
|
||||
<p>This picking appears to be chained with another operation. Later, if you receive the goods you are returning now, make sure to <b>reverse</b> the returned picking in order to avoid logistic rules to be applied again (which would create duplicated operations)</p>
|
||||
</div>
|
||||
</group>
|
||||
<group>
|
||||
<field name="move_dest_method" attrs="{'invisible': [('move_dest_exists','=',False)]}"/>
|
||||
<field name="product_return_moves"/>
|
||||
<field name="product_return_moves" colspan="4">
|
||||
<tree editable="top">
|
||||
<field name="product_id"/>
|
||||
<field name="lot_id" domain="[('product_id', '=', product_id)]" context="{'default_product_id': product_id}"/>
|
||||
<field name="quantity"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="create_returns" string="Return" type="object" class="oe_highlight"/>
|
||||
|
||||
Reference in New Issue
Block a user