[MERGE] branch fixing the return picking wizard
lp bug: https://launchpad.net/bugs/1269421 fixed bzr revid: qdp-launchpad@openerp.com-20140206131638-tlwdnhzdtkfkb727
This commit is contained in:
@@ -74,7 +74,7 @@
|
||||
return_picking_id, dummy = self._create_returns(cr, uid, [return_id], context={'active_id': pick_ids[0].id})
|
||||
self.pool.get('stock.picking').do_transfer(cr, uid, [return_picking_id])
|
||||
-
|
||||
Check the standard price of the product changed to 70.0 (as returns to supplier are processed as normal outgoing shipment we have sent 10kg at 50€ and 20kg at 80€, because of FIFO, so the average to store on the product is 70€)
|
||||
Check the standard price of the product changed to 80.0 as we returned the quants of purchase order 2
|
||||
-
|
||||
!python {model: product.product}: |
|
||||
assert self.browse(cr, uid, ref("product_fiforet_icecream")).standard_price == 70.0, 'Standard price should have changed to 70.0! %s found instead' % (self.browse(cr, uid, ref("product_fiforet_icecream")).standard_price,)
|
||||
assert self.browse(cr, uid, ref("product_fiforet_icecream")).standard_price == 80.0, 'Standard price should have changed to 80.0! %s found instead' % (self.browse(cr, uid, ref("product_fiforet_icecream")).standard_price,)
|
||||
|
||||
+23
-30
@@ -26,7 +26,6 @@ import time
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
from openerp import tools
|
||||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
|
||||
from openerp import SUPERUSER_ID
|
||||
import openerp.addons.decimal_precision as dp
|
||||
@@ -787,8 +786,8 @@ class stock_picking(osv.osv):
|
||||
'''This function prints the delivery order'''
|
||||
assert len(ids) == 1, 'This option should only be used for a single id at a time'
|
||||
datas = {
|
||||
'model': 'stock.picking',
|
||||
'ids': ids,
|
||||
'model': 'stock.picking',
|
||||
'ids': ids,
|
||||
}
|
||||
return {'type': 'ir.actions.report.xml', 'report_name': 'stock.picking.list', 'datas': datas, 'nodestroy': True}
|
||||
|
||||
@@ -796,8 +795,8 @@ class stock_picking(osv.osv):
|
||||
'''This function prints the picking list'''
|
||||
assert len(ids) == 1, 'This option should only be used for a single id at a time'
|
||||
datas = {
|
||||
'model': 'stock.picking',
|
||||
'ids': ids,
|
||||
'model': 'stock.picking',
|
||||
'ids': ids,
|
||||
}
|
||||
return {'type': 'ir.actions.report.xml', 'report_name': 'stock.picking.list.internal', 'datas': datas, 'nodestroy': True}
|
||||
|
||||
@@ -844,15 +843,6 @@ class stock_picking(osv.osv):
|
||||
self.do_prepare_partial(cr, uid, [pick.id], context=None)
|
||||
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):
|
||||
ids2 = [move.id for move in pick.move_lines]
|
||||
@@ -1006,11 +996,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):
|
||||
@@ -1215,14 +1205,13 @@ class stock_production_lot(osv.osv):
|
||||
@return: A dictionary of values
|
||||
"""
|
||||
quant_obj = self.pool.get("stock.quant")
|
||||
move_obj = self.pool.get("stock.move")
|
||||
quants = quant_obj.search(cr, uid, [('lot_id', 'in', ids)], context=context)
|
||||
moves = set()
|
||||
for quant in quant_obj.browse(cr, uid, quants, context=context):
|
||||
moves |= {move.id for move in quant.history_ids}
|
||||
if moves:
|
||||
return {
|
||||
'domain': "[('id','in',["+','.join(map(str, list(moves)))+"])]",
|
||||
return {
|
||||
'domain': "[('id','in',[" + ','.join(map(str, list(moves))) + "])]",
|
||||
'name': _('Traceability'),
|
||||
'view_mode': 'tree,form',
|
||||
'view_type': 'form',
|
||||
@@ -1231,7 +1220,6 @@ class stock_production_lot(osv.osv):
|
||||
'type': 'ir.actions.act_window',
|
||||
}
|
||||
return False
|
||||
|
||||
|
||||
|
||||
# ----------------------------------------------------
|
||||
@@ -1407,6 +1395,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", help="Technical field used to track the origin of a split move, which can be useful in case of debug"),
|
||||
'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."),
|
||||
@@ -1493,13 +1482,16 @@ 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)
|
||||
|
||||
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:
|
||||
@@ -1534,7 +1526,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)]
|
||||
@@ -1812,12 +1807,6 @@ class stock_move(osv.osv):
|
||||
self._putaway_check(cr, uid, ids, context=context)
|
||||
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.
|
||||
"""
|
||||
@@ -1853,6 +1842,9 @@ 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 the move is returned from another, restrict the choice of quants to the ones that follow the returned move
|
||||
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)
|
||||
@@ -2043,7 +2035,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']
|
||||
|
||||
@@ -859,6 +859,7 @@
|
||||
<field name="partner_id" filter_domain="[('partner_id','child_of',self)]"/>
|
||||
<field name="product_id"/>
|
||||
<field name="picking_type_id"/>
|
||||
<field name="group_id"/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
|
||||
<filter string="Order Date" icon="terp-go-month" domain="[]" context="{'group_by':'date'}"/>
|
||||
|
||||
@@ -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,6 +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 help tooltip if not needed"),
|
||||
}
|
||||
|
||||
def default_get(self, cr, uid, fields, context=None):
|
||||
@@ -60,24 +59,33 @@ 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")
|
||||
chained_move_exist = False
|
||||
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})
|
||||
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 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)
|
||||
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:
|
||||
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': chained_move_exist})
|
||||
return res
|
||||
|
||||
def _create_returns(self, cr, uid, ids, context=None):
|
||||
@@ -92,16 +100,32 @@ class stock_return_picking(osv.osv_memory):
|
||||
data = self.read(cr, uid, ids[0], context=context)
|
||||
returned_lines = 0
|
||||
|
||||
# Cancel assignment of existing chained assigned moves
|
||||
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
|
||||
|
||||
new_picking = pick_obj.copy(cr, uid, pick.id, {
|
||||
'move_lines': [],
|
||||
'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:
|
||||
@@ -109,9 +133,6 @@ class stock_return_picking(osv.osv_memory):
|
||||
new_qty = data_get.quantity
|
||||
if new_qty:
|
||||
returned_lines += 1
|
||||
quant_ids = []
|
||||
for quant in move.quant_ids:
|
||||
quant_ids.append((4, quant.id))
|
||||
move_obj.copy(cr, uid, move.id, {
|
||||
'product_id': data_get.product_id.id,
|
||||
'product_uom_qty': new_qty,
|
||||
@@ -120,9 +141,11 @@ class stock_return_picking(osv.osv_memory):
|
||||
'state': 'draft',
|
||||
'location_id': move.location_dest_id.id,
|
||||
'location_dest_id': move.location_id.id,
|
||||
'reserved_quant_ids': quant_ids,
|
||||
'origin_returned_move_id': move.id,
|
||||
'procure_method': 'make_to_stock',
|
||||
'restrict_lot_id': data_get.lot_id.id,
|
||||
})
|
||||
|
||||
if not returned_lines:
|
||||
raise osv.except_osv(_('Warning!'), _("Please specify at least one non-zero quantity."))
|
||||
|
||||
@@ -143,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) + "])]",
|
||||
|
||||
@@ -41,9 +41,18 @@
|
||||
<field name="model">stock.return.picking</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Return lines" version="7.0">
|
||||
<label string="Provide the quantities of the returned products."/>
|
||||
<group>
|
||||
<field name="product_return_moves"/>
|
||||
<group string="Provide the quantities of the returned products.">
|
||||
<field name="move_dest_exists" invisible="1"/>
|
||||
<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>
|
||||
<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