diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 3d58f572294..0a7829d2660 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -154,6 +154,16 @@ class purchase_order(osv.osv): obj_data = self.pool.get('ir.model.data') return obj_data.get_object_reference(cr, uid, 'stock','picking_type_in') and obj_data.get_object_reference(cr, uid, 'stock','picking_type_in')[1] or False + def _get_picking_ids(self, cr, uid, ids, name, args, context=None): + res = {} + for purchase_id in ids: + picking_ids = set() + move_ids = self.pool.get('stock.move').search(cr, uid, [('purchase_line_id.order_id','=', purchase_id)] , context=context) + for move in self.pool.get('stock.move').browse(cr, uid, move_ids, context=context): + picking_ids.add(move.picking_id.id) + res[purchase_id] = list(picking_ids) + return res + STATE_SELECTION = [ ('draft', 'Draft PO'), ('sent', 'RFQ Sent'), @@ -196,7 +206,7 @@ class purchase_order(osv.osv): 'validator' : fields.many2one('res.users', 'Validated by', readonly=True), 'notes': fields.text('Terms and Conditions'), 'invoice_ids': fields.many2many('account.invoice', 'purchase_invoice_rel', 'purchase_id', 'invoice_id', 'Invoices', help="Invoices generated for a purchase order"), - 'picking_ids': fields.one2many('stock.picking', 'purchase_id', 'Picking List', readonly=True, help="This is the list of incoming shipments that have been generated for this purchase order."), + 'picking_ids': fields.function(_get_picking_ids, method=True, type='one2many', relation='stock.picking', string='Picking List', help="This is the list of operations that have been generated for this purchase order."), 'shipped':fields.boolean('Received', readonly=True, select=True, help="It indicates that a picking has been done"), 'shipped_rate': fields.function(_shipped_rate, string='Received Ratio', type='float'), 'invoiced': fields.function(_invoiced, string='Invoice Received', type='boolean', help="It indicates that an invoice has been paid"), @@ -235,7 +245,7 @@ class purchase_order(osv.osv): 'bid_validity': fields.date('Bid Valid Until', help="Date on which the bid expired"), 'picking_type_id': fields.many2one('stock.picking.type', 'Deliver To', help="This will determine picking type of incoming shipment", required=True, states={'confirmed': [('readonly', True)], 'approved': [('readonly', True)], 'done': [('readonly', True)]}), - 'related_location_id': fields.related('picking_type_id', 'default_location_dest_id', type='many2one', relation='stock.location', string="Related location", store=True), + 'related_location_id': fields.related('picking_type_id', 'default_location_dest_id', type='many2one', relation='stock.location', string="Related location", store=True), } _defaults = { 'date_order': fields.date.context_today, @@ -394,32 +404,22 @@ class purchase_order(osv.osv): if context is None: context = {} mod_obj = self.pool.get('ir.model.data') + dummy, action_id = tuple(mod_obj.get_object_reference(cr, uid, 'stock', 'action_picking_tree')) + action = self.pool.get('ir.actions.act_window').read(cr, uid, action_id, context=context) + pick_ids = [] for po in self.browse(cr, uid, ids, context=context): pick_ids += [picking.id for picking in po.picking_ids] - action_model, action_id = tuple(mod_obj.get_object_reference(cr, uid, 'stock', 'action_picking_tree')) - action = self.pool[action_model].read(cr, uid, action_id, context=context) - active_id = context.get('active_id',ids[0]) - picking_type_id = self.browse(cr, uid, active_id, context=context)['picking_type_id'].id - - ctx = eval(action['context'],{'active_id': picking_type_id}, nocopy=True) - ctx.update({ - 'search_default_purchase_id': ids[0] - }) - if pick_ids and len(pick_ids) == 1: - form_view_ids = [view_id for view_id, view in action['views'] if view == 'form'] - view_id = form_view_ids and form_view_ids[0] or False - action.update({ - 'views': [], - 'view_mode': 'form', - 'view_id': view_id, - 'res_id': pick_ids[0] - }) - - action.update({ - 'context': ctx, - }) + #override the context to get rid of the default filtering on picking type + action['context'] = {} + #choose the view_mode accordingly + if len(pick_ids) > 1: + action['domain'] = "[('id','in',[" + ','.join(map(str, pick_ids)) + "])]" + else: + res = mod_obj.get_object_reference(cr, uid, 'stock', 'view_picking_form') + action['views'] = [(res and res[1] or False, 'form')] + action['res_id'] = pick_ids and pick_ids[0] or False return action def wkf_approve_order(self, cr, uid, ids, context=None): @@ -429,15 +429,6 @@ class purchase_order(osv.osv): def wkf_bid_received(self, cr, uid, ids, context=None): return self.write(cr, uid, ids, {'state':'bid', 'bid_date': fields.date.context_today(self,cr,uid,context=context)}) - def print_confirm(self,cr,uid,ids,context=None): - print "Confirmed" - - def print_double(self,cr,uid,ids,context=None): - print "double Approval" - - def print_router(self,cr,uid,ids,context=None): - print "Routed" - def wkf_send_rfq(self, cr, uid, ids, context=None): ''' This function opens a window to compose an email, with the edi purchase template message loaded by default @@ -497,8 +488,7 @@ class purchase_order(osv.osv): raise osv.except_osv(_('Error!'),_('You cannot confirm a purchase order without any purchase order line.')) for line in po.order_line: if line.state=='draft': - todo.append(line.id) - + todo.append(line.id) self.pool.get('purchase.order.line').action_confirm(cr, uid, todo, context) for id in ids: self.write(cr, uid, [id], {'state' : 'confirmed', 'validator' : uid}) @@ -666,20 +656,6 @@ class purchase_order(osv.osv): return user_datetime.strftime(DEFAULT_SERVER_DATETIME_FORMAT) return user_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT) - def _prepare_order_picking(self, cr, uid, order, context=None): - return { - 'name': self.pool.get('ir.sequence').get_id(cr, uid, order.picking_type_id.sequence_id.id, 'id'), - 'origin': order.name + ((order.origin and (':' + order.origin)) or ''), - 'date': self.date_to_datetime(cr, uid, order.date_order, context), - 'partner_id': order.dest_address_id.id or order.partner_id.id, - 'invoice_state': '2binvoiced' if order.invoice_method == 'picking' else 'none', - 'partner_id': order.dest_address_id.id or order.partner_id.id, - 'purchase_id': order.id, - 'company_id': order.company_id.id, - 'move_lines' : [], - 'picking_type_id': order.picking_type_id.id, - } - def _prepare_order_line_move(self, cr, uid, order, order_line, picking_id, group_id, context=None): ''' prepare the stock move data from the PO line ''' price_unit = order_line.price_unit @@ -687,7 +663,7 @@ class purchase_order(osv.osv): price_unit *= order_line.product_uom.factor if order.currency_id.id != order.company_id.currency_id.id: price_unit = self.pool.get('res.currency').compute(cr, uid, order.currency_id.id, order.company_id.currency_id.id, price_unit, context=context) - + return { 'name': order_line.name or '', 'product_id': order_line.product_id.id, @@ -708,15 +684,16 @@ class purchase_order(osv.osv): 'price_unit': price_unit, 'picking_type_id': order.picking_type_id.id, 'group_id': group_id, + 'route_ids': order.picking_type_id.warehouse_id and [(6, 0, [x.id for x in order.picking_type_id.warehouse_id.route_ids])] or [], } - def _create_pickings(self, cr, uid, order, order_lines, picking_id=False, context=None): - """Creates pickings and appropriate stock moves for given order lines, then - confirms the moves, makes them available, and confirms the picking. + def _create_stock_moves(self, cr, uid, order, order_lines, picking_id=False, context=None): + """Creates appropriate stock moves for given order lines, whose can optionally create a + picking if none is given or no suitable is found, then confirms the moves, makes them + available, and confirms the pickings. - If ``picking_id`` is provided, the stock moves will be added to it, otherwise - a standard outgoing picking will be created to wrap the stock moves, as returned - by :meth:`~._prepare_order_picking`. + If ``picking_id`` is provided, the stock moves will be added to it, otherwise a standard + incoming picking will be created to wrap the stock moves (default behavior of the stock.move) Modules that wish to customize the procurements or partition the stock moves over multiple stock pickings may override this method and call ``super()`` with @@ -727,19 +704,16 @@ class purchase_order(osv.osv): and moves should be created. :param int picking_id: optional ID of a stock picking to which the created stock moves will be added. A new picking will be created if omitted. - :return: list of IDs of pickings used/created for the given order lines (usually just one) + :return: None """ stock_picking = self.pool.get('stock.picking') - if not picking_id: - picking_id = stock_picking.create(cr, uid, self._prepare_order_picking(cr, uid, order, context=context)) todo_moves = [] stock_move = self.pool.get('stock.move') new_group = False if any([(not x.group_id) for x in order_lines]): new_group = self.pool.get("procurement.group").create(cr, uid, {'name':order.name, 'partner_id': order.partner_id.id}, context=context) - - + for order_line in order_lines: if not order_line.product_id: continue @@ -750,9 +724,9 @@ class purchase_order(osv.osv): if order_line.move_dest_id: order_line.move_dest_id.write({'location_id': order.location_id.id}) todo_moves.append(move) + stock_move.action_confirm(cr, uid, todo_moves) stock_move.force_assign(cr, uid, todo_moves) - return [picking_id] def test_moves_done(self, cr, uid, ids, context=None): '''PO is done at the delivery side if all the incoming shipments are done''' @@ -784,15 +758,8 @@ class purchase_order(osv.osv): return res def action_picking_create(self, cr, uid, ids, context=None): - picking_ids = [] for order in self.browse(cr, uid, ids): - picking_ids.extend(self._create_pickings(cr, uid, order, order.order_line, None, context=context)) - - # Must return one unique picking ID: the one to connect in the subflow of the purchase order. - # In case of multiple (split) pickings, we should return the ID of the critical one, i.e. the - # one that should trigger the advancement of the purchase workflow. - # By default we will consider the first one as most important, but this behavior can be overridden. - return picking_ids[0] if picking_ids else False + self._create_stock_moves(cr, uid, order, order.order_line, None, context=context) def picking_done(self, cr, uid, ids, context=None): self.write(cr, uid, ids, {'shipped':1,'state':'approved'}, context=context) diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index b36cc481a2b..512a3bc2261 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -136,6 +136,7 @@ class sale_order(osv.osv): of given sales order ids. It can either be a in a list or in a form view, if there is only one delivery order to show. ''' + mod_obj = self.pool.get('ir.model.data') act_obj = self.pool.get('ir.actions.act_window') @@ -147,6 +148,7 @@ class sale_order(osv.osv): pick_ids = [] for so in self.browse(cr, uid, ids, context=context): pick_ids += [picking.id for picking in so.picking_ids] + #choose the view_mode accordingly if len(pick_ids) > 1: result['domain'] = "[('id','in',[" + ','.join(map(str, pick_ids)) + "])]" diff --git a/addons/stock/product.py b/addons/stock/product.py index 4883497e77c..7fc77bb8163 100644 --- a/addons/stock/product.py +++ b/addons/stock/product.py @@ -25,7 +25,7 @@ import openerp.addons.decimal_precision as dp class product_product(osv.osv): _inherit = "product.product" - + def _stock_move_count(self, cr, uid, ids, field_name, arg, context=None): res = dict([(id, {'reception_count': 0, 'delivery_count': 0}) for id in ids]) move_pool=self.pool.get('stock.move') diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 44301a5aace..0ccc19799ca 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -562,6 +562,7 @@ 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').get_id(cr, user, sequence_id, 'id', context=context) + return super(stock_picking, self).create(cr, user, vals, context) # The state of a picking depends on the state of its related stock.move @@ -615,16 +616,10 @@ class stock_picking(osv.osv): continue return res - def _get_group_id(self, cr, uid, ids, field_name, args, context=None): - res = {} - for pick in self.browse(cr, uid, ids, context=context): - if pick.move_lines and pick.move_lines[0].group_id: - res[pick.id] = pick.move_lines[0].group_id.id - else: - res[pick.id] = False - return res - - + 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) _columns = { 'name': fields.char('Reference', size=64, select=True, states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}), @@ -664,13 +659,14 @@ class stock_picking(osv.osv): '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', required=True), + 'owner_id': fields.many2one('res.partner', 'Owner', help="Default Owner"), # Used to search on pickings 'product_id': fields.related('move_lines', 'product_id', type='many2one', relation='product.product', string='Product'),#? 'location_id': fields.related('move_lines', 'location_id', type='many2one', relation='stock.location', string='Location', readonly=True), 'location_dest_id': fields.related('move_lines', 'location_dest_id', type='many2one', relation='stock.location', string='Destination Location', readonly=True), 'group_id': fields.related('move_lines', 'group_id', type='many2one', relation='procurement.group', string='Procurement Group', readonly=True), -# 'group_id': fields.function(_get_group_id, type='many2one', store={'stock.move': (_get_pickings, ['picking_id', 'group_id'], 10)}), } + _defaults = { 'name': lambda self, cr, uid, context: '/', 'state': 'draft', @@ -1431,10 +1427,12 @@ class stock_move(osv.osv): for move in moves: if not move.move_dest_id: routes = [x.id for x in move.product_id.route_ids + move.product_id.categ_id.total_route_ids] - rules = push_obj.search(cr, uid, [('route_id', 'in', routes), ('location_from_id', '=', move.location_dest_id.id)], context=context) - if rules: - rule = push_obj.browse(cr, uid, rules[0], context=context) - push_obj._apply(cr, uid, rule, move, context=context) + routes = routes or [x.id for x in move.route_ids] + if routes: + rules = push_obj.search(cr, uid, [('route_id', 'in', routes), ('location_from_id', '=', move.location_dest_id.id)], context=context) + if rules: + rule = push_obj.browse(cr, uid, rules[0], context=context) + push_obj._apply(cr, uid, rule, move, context=context) return True # Create the stock.move.putaway records @@ -2756,6 +2754,33 @@ class stock_warehouse(osv.osv): #TODO try to delete location and route and if not possible, put them in inactive return super(stock_warehouse, self).unlink(cr, uid, ids, context=context) + def get_all_routes_for_wh(self, cr, uid, warehouse, context=None): + all_routes = [] + all_routes += [warehouse.crossdock_route_id.id] + all_routes += [warehouse.reception_route_id.id] + all_routes += [warehouse.delivery_route_id.id] + all_routes += [warehouse.mto_pull_id.route_id.id] + all_routes += [route.id for route in warehouse.resupply_route_ids] + all_routes += [route.id for route in warehouse.route_ids] + return all_routes + + def view_all_routes_for_wh(self, cr, uid, ids, context=None): + all_routes = [] + for wh in self.browse(cr, uid, ids, context=context): + all_routes += self.get_all_routes_for_wh(cr, uid, wh, context=context) + + res_id = ids and ids[0] or False + domain = [('id', 'in', all_routes)] + return { + 'name': _('Warehouse\'s Routes'), + 'domain': domain, + 'res_model': 'stock.location.route', + 'type': 'ir.actions.act_window', + 'view_id': False, + 'view_mode': 'tree,form', + 'view_type': 'form', + 'limit': 20 + } class stock_location_path(osv.osv): _name = "stock.location.path" @@ -2814,7 +2839,7 @@ class stock_location_path(osv.osv): } def _apply(self, cr, uid, rule, move, context=None): move_obj = self.pool.get('stock.move') - newdate = (datetime.strptime(move.date, '%Y-%m-%d %H:%M:%S') + relativedelta(days=rule.delay or 0)).strftime('%Y-%m-%d') + newdate = (datetime.strptime(move.date, '%Y-%m-%d %H:%M:%S') + relativedelta.relativedelta(days=rule.delay or 0)).strftime('%Y-%m-%d') if rule.auto=='transparent': move_obj.write(cr, uid, [move.id], { 'date': newdate, @@ -3015,7 +3040,7 @@ class stock_package(osv.osv): class stock_pack_operation(osv.osv): _name = "stock.pack.operation" _description = "Packing Operation" - + def _get_remaining_qty(self, cr, uid, ids, context=None): res = {} for ops in self.browse(cr, uid, ids, context=context): @@ -3024,8 +3049,7 @@ class stock_pack_operation(osv.osv): qty -= quant.qty res[ops.id] = qty return res - - + _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 @@ -3045,7 +3069,7 @@ class stock_pack_operation(osv.osv): } _defaults = { - 'date': fields.date.context_today, + 'date': fields.date.context_today, } diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index fcc3e14306f..9bfe83c2092 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -648,6 +648,9 @@
+
+