[MERGE] latest trunk
authorOlivier Dony <odo@openerp.com>
Tue, 13 Sep 2011 14:46:00 +0000 (16:46 +0200)
committerOlivier Dony <odo@openerp.com>
Tue, 13 Sep 2011 14:46:00 +0000 (16:46 +0200)
bzr revid: odo@openerp.com-20110913144600-ve4vb5po9vi3edw4

1  2 
openerp/addons/base/ir/ir_actions.py
openerp/addons/base/res/res_lang.py
openerp/addons/base/rng/view.rng

@@@ -418,10 -418,7 +418,10 @@@ class server_object_lines(osv.osv)
      _columns = {
          'server_id': fields.many2one('ir.actions.server', 'Object Mapping'),
          'col1': fields.many2one('ir.model.fields', 'Destination', required=True),
 -        'value': fields.text('Value', required=True),
 +        'value': fields.text('Value', required=True, help="Expression containing a value specification. \n"
 +                                                          "When Formula type is selected, this field may be a Python expression "
 +                                                          " that can use the same values as for the condition field on the server action.\n"
 +                                                          "If Value type is selected, the value will be used directly without evaluation."),
          'type': fields.selection([
              ('value','Value'),
              ('equation','Formula')
@@@ -472,19 -469,8 +472,19 @@@ class actions_server(osv.osv)
      _sequence = 'ir_actions_id_seq'
      _order = 'sequence,name'
      _columns = {
 -        'name': fields.char('Action Name', required=True, size=64, help="Easy to Refer action by name e.g. One Sales Order -> Many Invoices", translate=True),
 -        'condition' : fields.char('Condition', size=256, required=True, help="Condition that is to be tested before action is executed, e.g. object.list_price > object.cost_price"),
 +        'name': fields.char('Action Name', required=True, size=64, translate=True),
 +        'condition' : fields.char('Condition', size=256, required=True,
 +                                  help="Condition that is tested before the action is executed, "
 +                                       "and prevent execution if it is not verified.\n"
 +                                       "Example: object.list_price > 5000\n"
 +                                       "It is a Python expression that can use the following values:\n"
 +                                       " - self: ORM model of the record on which the action is triggered\n"
 +                                       " - object or obj: browse_record of the record on which the action is triggered\n"
 +                                       " - pool: ORM model pool (i.e. self.pool)\n"
 +                                       " - time: Python time module\n"
 +                                       " - cr: database cursor\n"
 +                                       " - uid: current user id\n"
 +                                       " - context: current context"),
          'state': fields.selection([
              ('client_action','Client Action'),
              ('dummy','Dummy'),
              ('object_write','Write Object'),
              ('other','Multi Actions'),
          ], 'Action Type', required=True, size=32, help="Type of the Action that is to be executed"),
 -        'code':fields.text('Python Code', help="Python code to be executed"),
 +        'code':fields.text('Python Code', help="Python code to be executed if condition is met.\n"
 +                                               "It is a Python block that can use the same values as for the condition field"),
          'sequence': fields.integer('Sequence', help="Important when you deal with multiple actions, the execution order will be decided based on this, low number is higher priority."),
          'model_id': fields.many2one('ir.model', 'Object', required=True, help="Select the object on which the action will work (read, write, create)."),
          'action_id': fields.many2one('ir.actions.actions', 'Client Action', help="Select the Action Window, Report, Wizard to be executed."),
          'trigger_name': fields.selection(_select_signals, string='Trigger Name', size=128, help="Select the Signal name that is to be used as the trigger."),
          'wkf_model_id': fields.many2one('ir.model', 'Workflow On', help="Workflow to be executed on this model."),
          'trigger_obj_id': fields.many2one('ir.model.fields','Trigger On', help="Select the object from the model on which the workflow will executed."),
 -        'email': fields.char('Email Address', size=512, help="Provides the fields that will be used to fetch the email address, e.g. when you select the invoice, then `object.invoice_address_id.email` is the field which gives the correct address"),
 -        'subject': fields.char('Subject', size=1024, translate=True, help="Specify the subject. You can use fields from the object, e.g. `Hello [[ object.partner_id.name ]]`"),
 -        'message': fields.text('Message', translate=True, help="Specify the message. You can use the fields from the object. e.g. `Dear [[ object.partner_id.name ]]`"),
 +        'email': fields.char('Email Address', size=512, help="Expression that returns the email address to send to. Can be based on the same values as for the condition field.\n"
 +                                                             "Example: object.invoice_address_id.email, or 'me@example.com'"),
 +        'subject': fields.char('Subject', size=1024, translate=True, help="Email subject, may contain expressions enclosed in double brackets based on the same values as those "
 +                                                                          "available in the condition field, e.g. `Hello [[ object.partner_id.name ]]`"),
 +        'message': fields.text('Message', translate=True, help="Email contents, may contain expressions enclosed in double brackets based on the same values as those "
 +                                                                          "available in the condition field, e.g. `Dear [[ object.partner_id.name ]]`"),
          'mobile': fields.char('Mobile No', size=512, help="Provides fields that be used to fetch the mobile number, e.g. you select the invoice, then `object.invoice_address_id.mobile` is the field which gives the correct mobile number"),
          'sms': fields.char('SMS', size=160, translate=True),
          'child_ids': fields.many2many('ir.actions.server', 'rel_server_actions', 'server_id', 'action_id', 'Other Actions'),
          'condition': lambda *a: 'True',
          'type': lambda *a: 'ir.actions.server',
          'sequence': lambda *a: 5,
 -        'code': lambda *a: """# You can use the following variables
 -#    - object or obj
 -#    - time
 -#    - cr
 -#    - uid
 -#    - ids
 +        'code': lambda *a: """# You can use the following variables:
 +#  - self: ORM model of the record on which the action is triggered
 +#  - object or obj: browse_record of the record on which the action is triggered
 +#  - pool: ORM model pool (i.e. self.pool)
 +#  - time: Python time module
 +#  - cr: database cursor
 +#  - uid: current user id
 +#  - context: current context
  # If you plan to return an action, assign: action = {...}
  """,
      }
      def merge_message(self, cr, uid, keystr, action, context=None):
          if context is None:
              context = {}
 +
          def merge(match):
              obj_pool = self.pool.get(action.model_id.model)
              id = context.get('active_id')
              context = {}
          user = self.pool.get('res.users').browse(cr, uid, uid)
          for action in self.browse(cr, uid, ids, context):
+             obj = None
 +            obj_pool = self.pool.get(action.model_id.model)
-             obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
+             if context.get('active_model') == action.model_id.model and context.get('active_id'):
 -                obj_pool = self.pool.get(action.model_id.model)
+                 obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
              cxt = {
 -                'context': dict(context), # copy context to prevent side-effects of eval
 +                'self': obj_pool,
                  'object': obj,
 +                'obj': obj,
 +                'pool': self.pool,
                  'time': time,
                  'cr': cr,
 -                'pool': self.pool,
 +                'context': dict(context), # copy context to prevent side-effects of eval
                  'uid': uid,
                  'user': user
              }
                      .read(cr, uid, action.action_id.id, context=context)
  
              if action.state=='code':
 -                localdict = {
 -                    'self': self.pool.get(action.model_id.model),
 -                    'pool': self.pool,
 -                    'context': dict(context), # copy context to prevent side-effects of eval
 -                    'time': time,
 -                    'ids': ids,
 -                    'cr': cr,
 -                    'uid': uid,
 -                    'object':obj,
 -                    'obj': obj,
 -                    'user': user,
 -                }
 -                eval(action.code, localdict, mode="exec", nocopy=True) # nocopy allows to return 'action'
 -                if 'action' in localdict:
 -                    return localdict['action']
 +                eval(action.code, cxt, mode="exec", nocopy=True) # nocopy allows to return 'action'
 +                if 'action' in cxt:
 +                    return cxt['action']
  
              if action.state == 'email':
                  email_from = config['email_from']
                  if not address:
                      logger.info('No partner email address specified, not sending any email.')
                      continue
 -                
 +
                  if not email_from:
                      logger.debug('--email-from command line option is not specified, using a fallback value instead.')
                      if user.user_email:
                  subject = self.merge_message(cr, uid, action.subject, action, context)
                  body = self.merge_message(cr, uid, action.message, action, context)
  
 -                if tools.email_send(email_from, [address], subject, body, debug=False, subtype='html') == True:
 +                ir_mail_server = self.pool.get('ir.mail_server')
 +                msg = ir_mail_server.build_email(email_from, [address], subject, body)
 +                res_email = ir_mail_server.send_email(cr, uid, msg)
 +                if res_email:
                      logger.info('Email successfully sent to: %s', address)
                  else:
                      logger.warning('Failed to send email to: %s', address)
              if action.state == 'trigger':
                  wf_service = netsvc.LocalService("workflow")
                  model = action.wkf_model_id.model
 -                obj_pool = self.pool.get(action.model_id.model)
 -                res_id = self.pool.get(action.model_id.model).read(cr, uid, [context.get('active_id')], [action.trigger_obj_id.name])
 +                res_id = obj_pool.read(cr, uid, [context.get('active_id')], [action.trigger_obj_id.name])
                  id = res_id [0][action.trigger_obj_id.name]
                  wf_service.trg_validate(uid, model, int(id), action.trigger_name, cr)
  
                      result = self.run(cr, uid, [act.id], context)
                      if result:
                          res.append(result)
                  return res
  
              if action.state == 'loop':
 -                obj_pool = self.pool.get(action.model_id.model)
 -                obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
 -                cxt = {
 -                    'context': dict(context), # copy context to prevent side-effects of eval
 -                    'object': obj,
 -                    'time': time,
 -                    'cr': cr,
 -                    'pool' : self.pool,
 -                    'uid' : uid
 -                }
                  expr = eval(str(action.expression), cxt)
                  context['object'] = obj
                  for i in expr:
                  for exp in action.fields_lines:
                      euq = exp.value
                      if exp.type == 'equation':
 -                        obj_pool = self.pool.get(action.model_id.model)
 -                        obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
 -                        cxt = {
 -                            'context': dict(context), # copy context to prevent side-effects of eval
 -                            'object': obj,
 -                            'time': time,
 -                        }
                          expr = eval(euq, cxt)
                      else:
                          expr = exp.value
                  for exp in action.fields_lines:
                      euq = exp.value
                      if exp.type == 'equation':
 -                        obj_pool = self.pool.get(action.model_id.model)
 -                        obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
 -                        expr = eval(euq,
 -                                    {
 -                                        'context': dict(context), # copy context to prevent side-effects of eval
 -                                        'object': obj,
 -                                        'time': time,
 -                                    })
 +                        expr = eval(euq, cxt)
                      else:
                          expr = exp.value
                      res[exp.col1.name] = expr
                  for exp in action.fields_lines:
                      euq = exp.value
                      if exp.type == 'equation':
 -                        obj_pool = self.pool.get(action.model_id.model)
 -                        obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
 -                        expr = eval(euq,
 -                                    {
 -                                        'context': dict(context), # copy context to prevent side-effects of eval
 -                                        'object': obj,
 -                                        'time': time,
 -                                    })
 +                        expr = eval(euq, cxt)
                      else:
                          expr = exp.value
                      res[exp.col1.name] = expr
  
 -                obj_pool = None
 -                res_id = False
 -
                  model = action.copy_object.split(',')[0]
                  cid = action.copy_object.split(',')[1]
                  obj_pool = self.pool.get(model)
@@@ -194,7 -194,7 +194,7 @@@ class lang(osv.osv)
              trans_obj.unlink(cr, uid, trans_ids, context=context)
          return super(lang, self).unlink(cr, uid, ids, context=context)
  
--    def format(self, cr, uid, ids, percent, value, grouping=False, monetary=False):
++    def format(self, cr, uid, ids, percent, value, grouping=False, monetary=False, context=None):
          """ Format() will return the language-specific output for float values"""
  
          if percent[0] != '%':
  
      <rng:define name="kanban">
          <rng:element name="kanban">
+             <rng:optional>
+                 <!-- group-by field to apply in the kanban view when no other
+                      grouping is selected in the search view -->
+                 <rng:attribute name="default_group_by" />
+             </rng:optional>
              <rng:zeroOrMore>
                  <rng:ref name="field"/>
              </rng:zeroOrMore>
              <rng:optional><rng:attribute name="help"/></rng:optional>
              <rng:optional><rng:attribute name="width"/></rng:optional>
              <rng:optional><rng:attribute name="wrap"/></rng:optional>
 +            <rng:optional><rng:attribute name="name"/></rng:optional>
              <rng:zeroOrMore>
                  <rng:choice>
                      <rng:ref name="notebook"/>