Forward port of 7.0 up to rev 9b87d6f
authorMartin Trigaux <mat@openerp.com>
Fri, 19 Sep 2014 13:28:02 +0000 (15:28 +0200)
committerMartin Trigaux <mat@openerp.com>
Fri, 19 Sep 2014 13:28:02 +0000 (15:28 +0200)
1  2 
addons/event/event.py
addons/purchase/stock.py
addons/stock/stock.py

diff --combined addons/event/event.py
@@@ -41,6 -41,8 +41,6 @@@ class event_type(osv.osv)
          'default_registration_max': 0,
      }
  
 -event_type()
 -
  class event_event(osv.osv):
      """Event"""
      _name = 'event.event'
      def button_done(self, cr, uid, ids, context=None):
          return self.write(cr, uid, ids, {'state': 'done'}, context=context)
  
 -    def check_registration_limits(self, cr, uid, ids, context=None):
 -        for self.event in self.browse(cr, uid, ids, context=context):
 -            total_confirmed = self.event.register_current
 -            if total_confirmed < self.event.register_min or total_confirmed > self.event.register_max and self.event.register_max!=0:
 -                raise osv.except_osv(_('Error!'),_("The total of confirmed registration for the event '%s' does not meet the expected minimum/maximum. Please reconsider those limits before going further.") % (self.event.name))
 -
 -    def check_registration_limits_before(self, cr, uid, ids, no_of_registration, context=None):
 -        for event in self.browse(cr, uid, ids, context=context):
 -            available_seats = event.register_avail
 -            if available_seats and no_of_registration > available_seats:
 -                raise osv.except_osv(_('Warning!'),_("Only %d Seats are Available!") % (available_seats))
 -            elif available_seats == 0:
 -                raise osv.except_osv(_('Warning!'),_("No Tickets Available!"))
 -
      def confirm_event(self, cr, uid, ids, context=None):
          register_pool = self.pool.get('event.registration')
 -        if self.event.email_confirmation_id:
 -        #send reminder that will confirm the event for all the people that were already confirmed
 -            reg_ids = register_pool.search(cr, uid, [
 -                               ('event_id', '=', self.event.id),
 -                               ('state', 'not in', ['draft', 'cancel'])], context=context)
 -            register_pool.mail_user_confirm(cr, uid, reg_ids)
 +        for event in self.browse(cr, uid, ids, context=context):
 +            if event.email_confirmation_id:
 +            #send reminder that will confirm the event for all the people that were already confirmed
 +                reg_ids = register_pool.search(cr, uid, [
 +                                   ('event_id', '=', event.id),
 +                                   ('state', 'not in', ['draft', 'cancel'])], context=context)
 +                register_pool.mail_user_confirm(cr, uid, reg_ids)
          return self.write(cr, uid, ids, {'state': 'confirm'}, context=context)
  
      def button_confirm(self, cr, uid, ids, context=None):
          """ Confirm Event and send confirmation email to all register peoples
          """
 -        if isinstance(ids, (int, long)):
 -            ids = [ids]
 -        self.check_registration_limits(cr, uid, ids, context=context)
 -        return self.confirm_event(cr, uid, ids, context=context)
 -
 -    def _get_register(self, cr, uid, ids, fields, args, context=None):
 -        """Get Confirm or uncofirm register value.
 -        @param ids: List of Event registration type's id
 -        @param fields: List of function fields(register_current and register_prospect).
 -        @param context: A standard dictionary for contextual values
 -        @return: Dictionary of function fields value.
 +        return self.confirm_event(cr, uid, isinstance(ids, (int, long)) and [ids] or ids, context=context)
 +
 +    def _get_seats(self, cr, uid, ids, fields, args, context=None):
 +        """Get reserved, available, reserved but unconfirmed and used seats.
 +        @return: Dictionary of function field values.
          """
 -        res = {}
 +        res = dict([(id, {}) for id in ids])
          for event in self.browse(cr, uid, ids, context=context):
 -            res[event.id] = {}
 -            reg_open = reg_done = reg_draft =0
 -            for registration in event.registration_ids:
 -                if registration.state == 'open':
 -                    reg_open += registration.nb_register
 -                elif registration.state == 'done':
 -                    reg_done += registration.nb_register
 -                elif registration.state == 'draft':
 -                    reg_draft += registration.nb_register
 -            for field in fields:
 -                number = 0
 -                if field == 'register_current':
 -                    number = reg_open
 -                elif field == 'register_attended':
 -                    number = reg_done
 -                elif field == 'register_prospect':
 -                    number = reg_draft
 -                elif field == 'register_avail':
 -                    #the number of ticket is unlimited if the event.register_max field is not set.
 -                    #In that cas we arbitrary set it to 9999, it is used in the kanban view to special case the display of the 'subscribe' button
 -                    number = event.register_max - reg_open if event.register_max != 0 else 9999
 -                res[event.id][field] = number
 +            res[event.id]['seats_reserved'] = sum(reg.nb_register for reg in event.registration_ids if reg.state == "open")
 +            res[event.id]['seats_used'] = sum(reg.nb_register for reg in event.registration_ids if reg.state == "done")
 +            res[event.id]['seats_unconfirmed'] = sum(reg.nb_register for reg in event.registration_ids if reg.state == "draft")
 +            res[event.id]['seats_available'] = event.seats_max - \
 +                (res[event.id]['seats_reserved'] + res[event.id]['seats_used']) \
 +                if event.seats_max > 0 else None
          return res
  
      def _subscribe_fnc(self, cr, uid, ids, fields, args, context=None):
          return res
  
      _columns = {
 -        'name': fields.char('Name', size=64, required=True, translate=True, readonly=False, states={'done': [('readonly', True)]}),
 +        'name': fields.char('Event Name', size=64, required=True, translate=True, readonly=False, states={'done': [('readonly', True)]}),
          'user_id': fields.many2one('res.users', 'Responsible User', readonly=False, states={'done': [('readonly', True)]}),
          'type': fields.many2one('event.type', 'Type of Event', readonly=False, states={'done': [('readonly', True)]}),
 -        'register_max': fields.integer('Maximum Registrations', help="You can for each event define a maximum registration level. If you have too much registrations you are not able to confirm your event. (put 0 to ignore this rule )", readonly=True, states={'draft': [('readonly', False)]}),
 -        'register_min': fields.integer('Minimum Registrations', help="You can for each event define a minimum registration level. If you do not enough registrations you are not able to confirm your event. (put 0 to ignore this rule )", readonly=True, states={'draft': [('readonly', False)]}),
 -        'register_current': fields.function(_get_register, string='Confirmed Registrations', multi='register_numbers'),
 -        'register_avail': fields.function(_get_register, string='Available Registrations', multi='register_numbers',type='integer'),
 -        'register_prospect': fields.function(_get_register, string='Unconfirmed Registrations', multi='register_numbers'),
 -        'register_attended': fields.function(_get_register, string='# of Participations', multi='register_numbers'),
 +        'seats_max': fields.integer('Maximum Available Seats', oldname='register_max', help="You can for each event define a maximum registration level. If you have too much registrations you are not able to confirm your event. (put 0 to ignore this rule )", readonly=True, states={'draft': [('readonly', False)]}),
 +        'seats_min': fields.integer('Minimum Reserved Seats', oldname='register_min', help="You can for each event define a minimum registration level. If you do not enough registrations you are not able to confirm your event. (put 0 to ignore this rule )", readonly=True, states={'draft': [('readonly', False)]}),
 +        'seats_reserved': fields.function(_get_seats, oldname='register_current', string='Reserved Seats', type='integer', multi='seats_reserved'),
 +        'seats_available': fields.function(_get_seats, oldname='register_avail', string='Available Seats', type='integer', multi='seats_reserved'),
 +        'seats_unconfirmed': fields.function(_get_seats, oldname='register_prospect', string='Unconfirmed Seat Reservations', type='integer', multi='seats_reserved'),
 +        'seats_used': fields.function(_get_seats, oldname='register_attended', string='Number of Participations', type='integer', multi='seats_reserved'),
          'registration_ids': fields.one2many('event.registration', 'event_id', 'Registrations', readonly=False, states={'done': [('readonly', True)]}),
          'date_begin': fields.datetime('Start Date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
          'date_end': fields.datetime('End Date', required=True, readonly=True, states={'draft': [('readonly', False)]}),
              ('confirm', 'Confirmed'),
              ('done', 'Done')],
              'Status', readonly=True, required=True,
 -            track_visibility='onchange',
              help='If event is created, the status is \'Draft\'.If event is confirmed for the particular dates the status is set to \'Confirmed\'. If the event is over, the status is set to \'Done\'.If event is cancelled the status is set to \'Cancelled\'.'),
          'email_registration_id' : fields.many2one('email.template','Registration Confirmation Email', help='This field contains the template of the mail that will be automatically sent each time a registration for this event is confirmed.'),
          'email_confirmation_id' : fields.many2one('email.template','Event Confirmation Email', help="If you set an email template, each participant will receive this email announcing the confirmation of the event."),
          'reply_to': fields.char('Reply-To Email', size=64, readonly=False, states={'done': [('readonly', True)]}, help="The email address of the organizer is likely to be put here, with the effect to be in the 'Reply-To' of the mails sent automatically at event or registrations confirmation. You can also put the email address of your mail gateway if you use one."),
 -        'main_speaker_id': fields.many2one('res.partner','Main Speaker', readonly=False, states={'done': [('readonly', True)]}, help="Speaker who will be giving speech at the event."),
 -        'address_id': fields.many2one('res.partner','Location Address', readonly=False, states={'done': [('readonly', True)]}),
 -        'street': fields.related('address_id','street',type='char',string='Street'),
 -        'street2': fields.related('address_id','street2',type='char',string='Street2'),
 -        'state_id': fields.related('address_id','state_id',type='many2one', relation="res.country.state", string='State'),
 -        'zip': fields.related('address_id','zip',type='char',string='zip'),
 -        'city': fields.related('address_id','city',type='char',string='city'),
 -        'speaker_confirmed': fields.boolean('Speaker Confirmed', readonly=False, states={'done': [('readonly', True)]}),
 +        'address_id': fields.many2one('res.partner','Location', readonly=False, states={'done': [('readonly', True)]}),
          'country_id': fields.related('address_id', 'country_id',
 -                    type='many2one', relation='res.country', string='Country', readonly=False, states={'done': [('readonly', True)]}),
 -        'note': fields.text('Description', readonly=False, states={'done': [('readonly', True)]}),
 +                    type='many2one', relation='res.country', string='Country', readonly=False, states={'done': [('readonly', True)]}, store=True),
 +        'description': fields.html(
 +            'Description', readonly=False,
 +            states={'done': [('readonly', True)]},
 +            oldname='note'),
          'company_id': fields.many2one('res.company', 'Company', required=False, change_default=True, readonly=False, states={'done': [('readonly', True)]}),
          'is_subscribed' : fields.function(_subscribe_fnc, type="boolean", string='Subscribed'),
 +        'organizer_id': fields.many2one('res.partner', "Organizer"),
      }
      _defaults = {
          'state': 'draft',
          'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'event.event', context=c),
          'user_id': lambda obj, cr, uid, context: uid,
 +        'organizer_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, context=c).company_id.partner_id.id,
 +        'address_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, context=c).company_id.partner_id.id
      }
  
 +    def _check_seats_limit(self, cr, uid, ids, context=None):
 +        print "event _check_seats_limit"
 +        for event in self.browse(cr, uid, ids, context=context):
 +            if event.seats_max and event.seats_available < 0:
 +                return False
 +        return True
 +
 +    _constraints = [
 +        (_check_seats_limit, 'No more available seats.', ['registration_ids','seats_max']),
 +    ]
 +
      def subscribe_to_event(self, cr, uid, ids, context=None):
          register_pool = self.pool.get('event.registration')
          user_pool = self.pool.get('res.users')
          num_of_seats = int(context.get('ticket', 1))
 -        self.check_registration_limits_before(cr, uid, ids, num_of_seats, context=context)
          user = user_pool.browse(cr, uid, uid, context=context)
          curr_reg_ids = register_pool.search(cr, uid, [('user_id', '=', user.id), ('event_id', '=' , ids[0])])
          #the subscription is done with SUPERUSER_ID because in case we share the kanban view, we want anyone to be able to subscribe
                'reply_to': type_info.default_reply_to,
                'email_registration_id': type_info.default_email_registration.id,
                'email_confirmation_id': type_info.default_email_event.id,
 -              'register_min': type_info.default_registration_min,
 -              'register_max': type_info.default_registration_max,
 +              'seats_min': type_info.default_registration_min,
 +              'seats_max': type_info.default_registration_max,
              }
              values.update(dic)
          return values
  
 -    def on_change_address_id(self, cr, uid, ids, address_id, context=None):
 -        values = {}
 -        if not address_id:
 -            return values
 -        address = self.pool.get('res.partner').browse(cr, uid, address_id, context=context)
 -        values.update({
 -            'street' : address.street,
 -            'street2' : address.street2,
 -            'city' : address.city,
 -            'country_id' : address.country_id and address.country_id.id or False,
 -            'state_id' : address.state_id and address.state_id.id or False,
 -            'zip' : address.zip,
 -        })
 -        return {'value' : values}
 -
      def onchange_start_date(self, cr, uid, ids, date_begin=False, date_end=False, context=None):
          res = {'value':{}}
          if date_end:
@@@ -267,6 -311,7 +267,6 @@@ class event_registration(osv.osv)
                                      ('cancel', 'Cancelled'),
                                      ('open', 'Confirmed'),
                                      ('done', 'Attended')], 'Status',
 -                                    track_visibility='onchange',
                                      size=16, readonly=True),
          'email': fields.char('Email', size=64),
          'phone': fields.char('Phone', size=64),
      }
      _order = 'name, create_date desc'
  
 +
 +    def _check_seats_limit(self, cr, uid, ids, context=None):
 +        for registration in self.browse(cr, uid, ids, context=context):
 +            if registration.event_id.seats_max and \
 +                registration.event_id.seats_available < (registration.state == 'draft' and registration.nb_register or 0):
 +                return False
 +        return True
 +
 +    _constraints = [
 +        (_check_seats_limit, 'No more available seats.', ['event_id','nb_register','state']),
 +    ]
 +
      def do_draft(self, cr, uid, ids, context=None):
          return self.write(cr, uid, ids, {'state': 'draft'}, context=context)
  
      def registration_open(self, cr, uid, ids, context=None):
          """ Open Registration
          """
 -        event_obj = self.pool.get('event.event')
 -        for register in  self.browse(cr, uid, ids, context=context):
 -            event_id = register.event_id.id
 -            no_of_registration = register.nb_register
 -            event_obj.check_registration_limits_before(cr, uid, [event_id], no_of_registration, context=context)
          res = self.confirm_registration(cr, uid, ids, context=context)
          self.mail_user(cr, uid, ids, context=context)
          return res
              else:
                  template_id = registration.event_id.email_registration_id.id
                  if template_id:
-                     mail_message = self.pool.get('email.template').send_mail(cr,uid,template_id,registration.id)
+                     self.pool.get('email.template').send_mail(cr,uid,template_id,registration.id, context=context)
          return True
  
      def mail_user_confirm(self, cr, uid, ids, context=None):
          for registration in self.browse(cr, uid, ids, context=context):
              template_id = registration.event_id.email_confirmation_id.id
              if template_id:
-                 mail_message = self.pool.get('email.template').send_mail(cr,uid,template_id,registration.id)
+                 self.pool.get('email.template').send_mail(cr,uid,template_id,registration.id, context=context)
          return True
  
      def onchange_contact_id(self, cr, uid, ids, contact, partner, context=None):
diff --combined addons/purchase/stock.py
@@@ -30,6 -30,7 +30,6 @@@ class stock_move(osv.osv)
              readonly=True),
      }
  
 -stock_move()
  
  #
  # Inherit of picking to add the link to the PO
@@@ -71,7 -72,7 +71,7 @@@ class stock_picking(osv.osv)
  
      def get_currency_id(self, cursor, user, picking):
          if picking.purchase_id:
 -            return picking.purchase_id.pricelist_id.currency_id.id
 +            return picking.purchase_id.currency_id.id
          else:
              return super(stock_picking, self).get_currency_id(cursor, user, picking)
  
@@@ -127,14 -128,23 +127,23 @@@ class stock_partial_picking(osv.osv_mem
      # Overridden to inject the purchase price as true 'cost price' when processing
      # incoming pickings.
      def _product_cost_for_average_update(self, cr, uid, move):
-         if move.purchase_line_id and move.purchase_line_id and move.purchase_line_id.invoice_lines:
-             cost = move.price_unit
-             for inv_line in move.purchase_line_id.invoice_lines:
-                 if inv_line.invoice_id.state not in ('draft', 'cancel'):
-                     inv_currency = inv_line.invoice_id.currency_id.id
-                     company_currency = inv_line.invoice_id.company_id.currency_id.id
-                     cost = self.pool.get('res.currency').compute(cr, uid, inv_currency, company_currency, inv_line.price_unit, round=False, context={'date': inv_line.invoice_id.date_invoice})
-                     return {'cost': cost, 'currency': company_currency}
+         purchase_line = move.purchase_line_id
+         if move.picking_id.purchase_id and purchase_line:
+             if any([x.invoice_id.state not in ('draft', 'cancel') for x in purchase_line.invoice_lines]):
+                 # use price set on validated invoices
+                 cost = move.price_unit
+                 for inv_line in purchase_line.invoice_lines:
+                     if inv_line.invoice_id.state not in ('draft', 'cancel'):
+                         inv_currency = inv_line.invoice_id.currency_id.id
+                         company_currency = inv_line.invoice_id.company_id.currency_id.id
+                         cost = self.pool.get('res.currency').compute(cr, uid, inv_currency, company_currency, inv_line.price_unit, round=False, context={'date': inv_line.invoice_id.date_invoice})
+                         return {'cost': cost, 'currency': company_currency}
+             else:
+                 # use price set on the purchase order
+                 pur_currency = purchase_line.order_id.currency_id.id
+                 company_currency = purchase_line.company_id.currency_id.id
+                 cost = self.pool.get('res.currency').compute(cr, uid, pur_currency, company_currency, purchase_line.price_unit, round=False, context={'date': purchase_line.date_order})
+                 return {'cost': cost, 'currency': company_currency}
          return super(stock_partial_picking, self)._product_cost_for_average_update(cr, uid, move)
  
      def __get_help_text(self, cursor, user, picking_id, context=None):
diff --combined addons/stock/stock.py
@@@ -27,7 -27,7 +27,7 @@@ from itertools import groupb
  
  from openerp.osv import fields, osv, orm
  from openerp.tools.translate import _
 -from openerp import netsvc
 +from openerp import workflow
  from openerp import tools
  from openerp.tools import float_compare, DEFAULT_SERVER_DATETIME_FORMAT
  import openerp.addons.decimal_precision as dp
@@@ -49,6 -49,7 +49,6 @@@ class stock_incoterms(osv.osv)
          'active': True,
      }
  
 -stock_incoterms()
  
  class stock_journal(osv.osv):
      _name = "stock.journal"
@@@ -61,6 -62,7 +61,6 @@@
          'user_id': lambda s, c, u, ctx: u
      }
  
 -stock_journal()
  
  #----------------------------------------------------------
  # Stock Location
@@@ -159,10 -161,6 +159,10 @@@ class stock_location(osv.osv)
                         \n* Production: Virtual counterpart location for production operations: this location consumes the raw material and produces finished products
                        """, select = True),
           # temporarily removed, as it's unused: 'allocation_method': fields.selection([('fifo', 'FIFO'), ('lifo', 'LIFO'), ('nearest', 'Nearest')], 'Allocation Method', required=True),
 +
 +        # as discussed on bug 765559, the main purpose of this field is to allow sorting the list of locations
 +        # according to the displayed names, and reversing that sort by clicking on a column. It does not work for
 +        # translated values though - so it needs fixing.
          'complete_name': fields.function(_complete_name, type='char', size=256, string="Location Name",
                              store={'stock.location': (_get_sublocations, ['name', 'location_id'], 10)}),
  
                      continue
          return False
  
 -stock_location()
  
  
  class stock_tracking(osv.osv):
          """
          return self.pool.get('action.traceability').action_traceability(cr,uid,ids,context)
  
 -stock_tracking()
  
  #----------------------------------------------------------
  # Stock Picking
@@@ -631,7 -631,7 +631,7 @@@ class stock_picking(osv.osv)
          return res
  
      def create(self, cr, user, vals, context=None):
 -        if ('name' not in vals) or (vals.get('name')=='/'):
 +        if ('name' not in vals) or (vals.get('name')=='/') or (vals.get('name') == False):
              seq_obj_name =  self._name
              vals['name'] = self.pool.get('ir.sequence').get(cr, user, seq_obj_name)
          new_id = super(stock_picking, self).create(cr, user, vals, context)
          """ Changes state of picking to available if all moves are confirmed.
          @return: True
          """
 -        wf_service = netsvc.LocalService("workflow")
          for pick in self.browse(cr, uid, ids):
              if pick.state == 'draft':
 -                wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_confirm', cr)
 +                self.signal_button_confirm(cr, uid, [pick.id])
              move_ids = [x.id for x in pick.move_lines if x.state == 'confirmed']
              if not move_ids:
                  raise osv.except_osv(_('Warning!'),_('Not enough stock, unable to reserve the products.'))
          """ Changes state of picking to available if moves are confirmed or waiting.
          @return: True
          """
          for pick in self.browse(cr, uid, ids):
              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)
 -            wf_service.trg_write(uid, 'stock.picking', pick.id, cr)
 +            workflow.trg_write(uid, 'stock.picking', pick.id, cr)
          return True
  
      def draft_force_assign(self, cr, uid, ids, *args):
          """ Confirms picking directly from draft state.
          @return: True
          """
          for pick in self.browse(cr, uid, ids):
              if not pick.move_lines:
                  raise osv.except_osv(_('Error!'),_('You cannot process picking without stock moves.'))
 -            wf_service.trg_validate(uid, 'stock.picking', pick.id,
 -                'button_confirm', cr)
 +            self.signal_button_confirm(cr, uid, [pick.id])
          return True
  
      def draft_validate(self, cr, uid, ids, context=None):
          """ Validates picking directly from draft state.
          @return: True
          """
 -        wf_service = netsvc.LocalService("workflow")
          self.draft_force_assign(cr, uid, ids)
          for pick in self.browse(cr, uid, ids, context=context):
              move_ids = [x.id for x in pick.move_lines]
              self.pool.get('stock.move').force_assign(cr, uid, move_ids)
 -            wf_service.trg_write(uid, 'stock.picking', pick.id, cr)
 +            workflow.trg_write(uid, 'stock.picking', pick.id, cr)
          return self.action_process(
              cr, uid, ids, context=context)
      def cancel_assign(self, cr, uid, ids, *args):
          """ Cancels picking and moves.
          @return: True
          """
          for pick in self.browse(cr, uid, ids):
              move_ids = [x.id for x in pick.move_lines]
              self.pool.get('stock.move').cancel_assign(cr, uid, move_ids)
 -            wf_service.trg_write(uid, 'stock.picking', pick.id, cr)
 +            workflow.trg_write(uid, 'stock.picking', pick.id, cr)
          return True
  
      def action_assign_wkf(self, cr, uid, ids, context=None):
              context = {}
          for pick in self.browse(cr, uid, ids, context=context):
              if pick.state in ['done','cancel']:
-                 raise osv.except_osv(_('Error!'), _('You cannot remove the picking which is in %s state!')%(pick.state,))
+                 # retrieve the string value of field in user's language
+                 state = dict(self.fields_get(cr, uid, context=context)['state']['selection']).get(pick.state, pick.state)
+                 raise osv.except_osv(_('Error!'), _('You cannot remove the picking which is in %s state!')%(state,))
              else:
                  ids2 = [move.id for move in pick.move_lines]
                  ctx = context.copy()
          currency_obj = self.pool.get('res.currency')
          uom_obj = self.pool.get('product.uom')
          sequence_obj = self.pool.get('ir.sequence')
 -        wf_service = netsvc.LocalService("workflow")
          for pick in self.browse(cr, uid, ids, context=context):
              new_picking = None
              complete, too_many, too_few = [], [], []
  
              # At first we confirm the new picking (if necessary)
              if new_picking:
 -                wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_confirm', cr)
 +                self.signal_button_confirm(cr, uid, [new_picking])
                  # Then we finish the good picking
                  self.write(cr, uid, [pick.id], {'backorder_id': new_picking})
                  self.action_move(cr, uid, [new_picking], context=context)
 -                wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_done', cr)
 -                wf_service.trg_write(uid, 'stock.picking', pick.id, cr)
 +                self.signal_button_done(cr, uid, [new_picking])
 +                workflow.trg_write(uid, 'stock.picking', pick.id, cr)
                  delivered_pack_id = new_picking
                  self.message_post(cr, uid, new_picking, body=_("Back order <em>%s</em> has been <b>created</b>.") % (pick.name), context=context)
              else:
                  self.action_move(cr, uid, [pick.id], context=context)
 -                wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_done', cr)
 +                self.signal_button_done(cr, uid, [pick.id])
                  delivered_pack_id = pick.id
  
              delivered_pack = self.browse(cr, uid, delivered_pack_id, context=context)
@@@ -1499,6 -1508,7 +1501,6 @@@ class stock_production_lot(osv.osv)
          default.update(date=time.strftime('%Y-%m-%d %H:%M:%S'), move_ids=[])
          return super(stock_production_lot, self).copy(cr, uid, id, default=default, context=context)
  
 -stock_production_lot()
  
  class stock_production_lot_revision(osv.osv):
      _name = 'stock.production.lot.revision'
          'date': fields.date.context_today,
      }
  
 -stock_production_lot_revision()
  
  # ----------------------------------------------------
  # Move
@@@ -2056,6 -2067,7 +2058,6 @@@ class stock_move(osv.osv)
          res_obj = self.pool.get('res.company')
          location_obj = self.pool.get('stock.location')
          move_obj = self.pool.get('stock.move')
 -        wf_service = netsvc.LocalService("workflow")
          new_moves = []
          if context is None:
              context = {}
                      })
                      new_moves.append(self.browse(cr, uid, [new_id])[0])
                  if pickid:
 -                    wf_service.trg_validate(uid, 'stock.picking', pickid, 'button_confirm', cr)
 +                    self.pool.get('stock.picking').signal_button_confirm(cr, uid, [pickid])
          if new_moves:
              new_moves += self.create_chained_picking(cr, uid, new_moves, context)
          return new_moves
          @return: True
          """
          self.write(cr, uid, ids, {'state': 'assigned'})
 -        wf_service = netsvc.LocalService('workflow')
          for move in self.browse(cr, uid, ids, context):
              if move.picking_id:
 -                wf_service.trg_write(uid, 'stock.picking', move.picking_id.id, cr)
 +                workflow.trg_write(uid, 'stock.picking', move.picking_id.id, cr)
          return True
  
      def cancel_assign(self, cr, uid, ids, context=None):
          # fix for bug lp:707031
          # called write of related picking because changing move availability does
          # not trigger workflow of picking in order to change the state of picking
 -        wf_service = netsvc.LocalService('workflow')
          for move in self.browse(cr, uid, ids, context):
              if move.picking_id:
 -                wf_service.trg_write(uid, 'stock.picking', move.picking_id.id, cr)
 +                workflow.trg_write(uid, 'stock.picking', move.picking_id.id, cr)
          return True
  
      #
  
          if count:
              for pick_id in pickings:
 -                wf_service = netsvc.LocalService("workflow")
 -                wf_service.trg_write(uid, 'stock.picking', pick_id, cr)
 +                workflow.trg_write(uid, 'stock.picking', pick_id, cr)
          return count
  
      def setlast_tracking(self, cr, uid, ids, context=None):
              if move.move_dest_id and move.move_dest_id.state == 'waiting':
                  self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'}, context=context)
                  if context.get('call_unlink',False) and move.move_dest_id.picking_id:
 -                    wf_service = netsvc.LocalService("workflow")
 -                    wf_service.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr)
 +                    workflow.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr)
          self.write(cr, uid, ids, {'state': 'cancel', 'move_dest_id': False}, context=context)
          if not context.get('call_unlink',False):
              for pick in self.pool.get('stock.picking').browse(cr, uid, list(pickings), context=context):
                  if all(move.state == 'cancel' for move in pick.move_lines):
                      self.pool.get('stock.picking').write(cr, uid, [pick.id], {'state': 'cancel'}, context=context)
  
 -        wf_service = netsvc.LocalService("workflow")
          for id in ids:
 -            wf_service.trg_trigger(uid, 'stock.move', id, cr)
 +            workflow.trg_trigger(uid, 'stock.move', id, cr)
          return True
  
      def _get_accounting_data_for_valuation(self, cr, uid, move, context=None):
          """
          picking_ids = []
          move_ids = []
 -        wf_service = netsvc.LocalService("workflow")
          if context is None:
              context = {}
  
                      if move.move_dest_id.state in ('waiting', 'confirmed'):
                          self.force_assign(cr, uid, [move.move_dest_id.id], context=context)
                          if move.move_dest_id.picking_id:
 -                            wf_service.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr)
 +                            workflow.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr)
                          if move.move_dest_id.auto_validate:
                              self.action_done(cr, uid, [move.move_dest_id.id], context=context)
  
  
          self.write(cr, uid, move_ids, {'state': 'done', 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}, context=context)
          for id in move_ids:
 -             wf_service.trg_trigger(uid, 'stock.move', id, cr)
 +             workflow.trg_trigger(uid, 'stock.move', id, cr)
  
          for pick_id in picking_ids:
 -            wf_service.trg_write(uid, 'stock.picking', pick_id, cr)
 +            workflow.trg_write(uid, 'stock.picking', pick_id, cr)
  
          return True
  
          product_obj = self.pool.get('product.product')
          currency_obj = self.pool.get('res.currency')
          uom_obj = self.pool.get('product.uom')
 -        wf_service = netsvc.LocalService("workflow")
  
          if context is None:
              context = {}
                  res = cr.fetchall()
                  if len(res) == len(move.picking_id.move_lines):
                      picking_obj.action_move(cr, uid, [move.picking_id.id])
 -                    wf_service.trg_validate(uid, 'stock.picking', move.picking_id.id, 'button_done', cr)
 +                    picking_obj.signal_button_done(cr, uid, [move.picking_id.id])
  
          return [move.id for move in complete]
  
 -stock_move()
  
  class stock_inventory(osv.osv):
      _name = "stock.inventory"
              self.write(cr, uid, [inv.id], {'state': 'cancel'}, context=context)
          return True
  
 -stock_inventory()
  
  class stock_inventory_line(osv.osv):
      _name = "stock.inventory.line"
          result = {'product_qty': amount, 'product_uom': uom, 'prod_lot_id': False}
          return {'value': result}
  
 -stock_inventory_line()
  
  #----------------------------------------------------------
  # Stock Warehouse
@@@ -2974,6 -2996,7 +2976,6 @@@ class stock_warehouse(osv.osv)
          'lot_output_id': _default_lot_output_id,
      }
  
 -stock_warehouse()
  
  #----------------------------------------------------------
  # "Empty" Classes that are used to vary from the original stock.picking  (that are dedicated to the internal pickings)
@@@ -3002,25 -3025,15 +3004,25 @@@ class stock_picking_in(osv.osv)
          #override in order to redirect the check of acces rules on the stock.picking object
          return self.pool.get('stock.picking').check_access_rule(cr, uid, ids, operation, context=context)
  
 -    def _workflow_trigger(self, cr, uid, ids, trigger, context=None):
 -        #override in order to trigger the workflow of stock.picking at the end of create, write and unlink operation
 -        #instead of it's own workflow (which is not existing)
 -        return self.pool.get('stock.picking')._workflow_trigger(cr, uid, ids, trigger, context=context)
 +    def create_workflow(self, cr, uid, ids, context=None):
 +        # overridden in order to trigger the workflow of stock.picking at the end of create,
 +        # write and unlink operation instead of its own workflow (which is not existing)
 +        return self.pool.get('stock.picking').create_workflow(cr, uid, ids, context=context)
 +
 +    def delete_workflow(self, cr, uid, ids, context=None):
 +        # overridden in order to trigger the workflow of stock.picking at the end of create,
 +        # write and unlink operation instead of its own workflow (which is not existing)
 +        return self.pool.get('stock.picking').delete_workflow(cr, uid, ids, context=context)
  
 -    def _workflow_signal(self, cr, uid, ids, signal, context=None):
 -        #override in order to fire the workflow signal on given stock.picking workflow instance
 -        #instead of it's own workflow (which is not existing)
 -        return self.pool.get('stock.picking')._workflow_signal(cr, uid, ids, signal, context=context)
 +    def step_workflow(self, cr, uid, ids, context=None):
 +        # overridden in order to trigger the workflow of stock.picking at the end of create,
 +        # write and unlink operation instead of its own workflow (which is not existing)
 +        return self.pool.get('stock.picking').step_workflow(cr, uid, ids, context=context)
 +
 +    def signal_workflow(self, cr, uid, ids, signal, context=None):
 +        # overridden in order to fire the workflow signal on given stock.picking workflow instance
 +        # instead of its own workflow (which is not existing)
 +        return self.pool.get('stock.picking').signal_workflow(cr, uid, ids, signal, context=context)
  
      def message_post(self, *args, **kwargs):
          """Post the message on stock.picking to be able to see it in the form view when using the chatter"""
@@@ -3085,25 -3098,15 +3087,25 @@@ class stock_picking_out(osv.osv)
          #override in order to redirect the check of acces rules on the stock.picking object
          return self.pool.get('stock.picking').check_access_rule(cr, uid, ids, operation, context=context)
  
 -    def _workflow_trigger(self, cr, uid, ids, trigger, context=None):
 -        #override in order to trigger the workflow of stock.picking at the end of create, write and unlink operation
 -        #instead of it's own workflow (which is not existing)
 -        return self.pool.get('stock.picking')._workflow_trigger(cr, uid, ids, trigger, context=context)
 -
 -    def _workflow_signal(self, cr, uid, ids, signal, context=None):
 -        #override in order to fire the workflow signal on given stock.picking workflow instance
 -        #instead of it's own workflow (which is not existing)
 -        return self.pool.get('stock.picking')._workflow_signal(cr, uid, ids, signal, context=context)
 +    def create_workflow(self, cr, uid, ids, context=None):
 +        # overridden in order to trigger the workflow of stock.picking at the end of create,
 +        # write and unlink operation instead of its own workflow (which is not existing)
 +        return self.pool.get('stock.picking').create_workflow(cr, uid, ids, context=context)
 +
 +    def delete_workflow(self, cr, uid, ids, context=None):
 +        # overridden in order to trigger the workflow of stock.picking at the end of create,
 +        # write and unlink operation instead of its own workflow (which is not existing)
 +        return self.pool.get('stock.picking').delete_workflow(cr, uid, ids, context=context)
 +
 +    def step_workflow(self, cr, uid, ids, context=None):
 +        # overridden in order to trigger the workflow of stock.picking at the end of create,
 +        # write and unlink operation instead of its own workflow (which is not existing)
 +        return self.pool.get('stock.picking').step_workflow(cr, uid, ids, context=context)
 +
 +    def signal_workflow(self, cr, uid, ids, signal, context=None):
 +        # overridden in order to fire the workflow signal on given stock.picking workflow instance
 +        # instead of its own workflow (which is not existing)
 +        return self.pool.get('stock.picking').signal_workflow(cr, uid, ids, signal, context=context)
  
      def message_post(self, *args, **kwargs):
          """Post the message on stock.picking to be able to see it in the form view when using the chatter"""