[MERGE] Forward-port of latest 7.0 bugfixes, up to rev. 10056 revid:dle@openerp.com...
authorDenis Ledoux <dle@openerp.com>
Mon, 19 May 2014 13:36:48 +0000 (15:36 +0200)
committerDenis Ledoux <dle@openerp.com>
Mon, 19 May 2014 13:36:48 +0000 (15:36 +0200)
1  2 
addons/audittrail/audittrail.py
addons/point_of_sale/point_of_sale.py

@@@ -349,190 -413,131 +349,198 @@@ def get_data(cr, uid, pool, res_ids, mo
                      x2m_model_id = x2m_model_ids and x2m_model_ids[0] or False
                      assert x2m_model_id, _("'%s' Model does not exist..." %(field_obj._obj))
                      x2m_model = pool.get('ir.model').browse(cr, SUPERUSER_ID, x2m_model_id)
 -                    # the resource_ids that need to be checked are the sum of both old and previous values (because we
 -                    # need to log also creation or deletion in those lists).
 -                    x2m_old_values_ids = old_values.get(key, {'value': {}})['value'].get(field_name, [])
 -                    x2m_new_values_ids = new_values.get(key, {'value': {}})['value'].get(field_name, [])
 -                    # We use list(set(...)) to remove duplicates.
 -                    res_ids = list(set(x2m_old_values_ids + x2m_new_values_ids))
 +                    field_resource_ids = list(set(resource[field]))
                      if model.model == x2m_model.model:
                          # we need to remove current resource_id from the many2many to prevent an infinit loop
 -                        if resource_id in res_ids:
 -                            res_ids.remove(resource_id)
 -                    for res_id in res_ids:
 -                        lines.update(self.prepare_audittrail_log_line(cr, SUPERUSER_ID, pool, x2m_model, res_id, method, old_values, new_values, field_list))
 -            # if the value value is different than the old value: record the change
 -            if key not in old_values or key not in new_values or old_values[key]['value'][field_name] != new_values[key]['value'][field_name]:
 -                data = {
 -                      'name': field_name,
 -                      'new_value': key in new_values and new_values[key]['value'].get(field_name),
 -                      'old_value': key in old_values and old_values[key]['value'].get(field_name),
 -                      'new_value_text': key in new_values and new_values[key]['text'].get(field_name),
 -                      'old_value_text': key in old_values and old_values[key]['text'].get(field_name)
 -                }
 -                lines[key].append(data)
 +                        if resource_id in field_resource_ids:
 +                            field_resource_ids.remove(resource_id)
 +                    data.update(get_data(cr, SUPERUSER_ID, pool, field_resource_ids, x2m_model, method))
 +
 +        data[(model.id, resource_id)] = {'text':values_text, 'value': values}
 +    return data
 +
 +def prepare_audittrail_log_line(cr, uid, pool, model, resource_id, method, old_values, new_values, field_list=None):
 +    """
 +    This function compares the old data (i.e before the method was executed) and the new data
 +    (after the method was executed) and returns a structure with all the needed information to
 +    log those differences.
 +
 +    :param cr: the current row, from the database cursor,
 +    :param uid: the current user’s ID. This parameter is currently not used as every
 +        operation to get data is made as super admin. Though, it could be usefull later.
 +    :param pool: current db's pooler object.
 +    :param model: model object which values are being changed
 +    :param resource_id: ID of record to which values are being changed
 +    :param method: method to log: create, read, unlink, write, actions, workflow actions
 +    :param old_values: dict of values read before execution of the method
 +    :param new_values: dict of values read after execution of the method
 +    :param field_list: optional argument containing the list of fields to log. Currently only
 +        used when performing a read, it could be usefull later on if we want to log the write
 +        on specific fields only.
 +
 +    :return: dictionary with
 +        * keys: tuples build as ID of model object to log and ID of resource to log
 +        * values: list of all the changes in field values for this couple (model, resource)
 +          return {
 +            (model.id, resource_id): []
 +          }
 +
 +    The reason why the structure returned is build as above is because when modifying an existing
 +    record, we may have to log a change done in a x2many field of that object
 +    """
 +    if field_list is None:
 +        field_list = []
 +    key = (model.id, resource_id)
 +    lines = {
 +        key: []
 +    }
 +    # loop on all the fields
 +    for field_name, field_definition in pool[model.model]._all_columns.items():
 +        if field_name in ('__last_update', 'id'):
 +            continue
 +        #if the field_list param is given, skip all the fields not in that list
 +        if field_list and field_name not in field_list:
 +            continue
 +        field_obj = field_definition.column
 +        if field_obj._type in ('one2many','many2many'):
 +            # checking if an audittrail rule apply in super admin mode
 +            if check_rules(cr, SUPERUSER_ID, field_obj._obj, method):
 +                # checking if the model associated to a *2m field exists, in super admin mode
 +                x2m_model_ids = pool.get('ir.model').search(cr, SUPERUSER_ID, [('model', '=', field_obj._obj)])
 +                x2m_model_id = x2m_model_ids and x2m_model_ids[0] or False
 +                assert x2m_model_id, _("'%s' Model does not exist..." %(field_obj._obj))
 +                x2m_model = pool.get('ir.model').browse(cr, SUPERUSER_ID, x2m_model_id)
 +                # the resource_ids that need to be checked are the sum of both old and previous values (because we
 +                # need to log also creation or deletion in those lists).
 +                x2m_old_values_ids = old_values.get(key, {'value': {}})['value'].get(field_name, [])
 +                x2m_new_values_ids = new_values.get(key, {'value': {}})['value'].get(field_name, [])
 +                # We use list(set(...)) to remove duplicates.
 +                res_ids = list(set(x2m_old_values_ids + x2m_new_values_ids))
 +                if model.model == x2m_model.model:
 +                    # we need to remove current resource_id from the many2many to prevent an infinit loop
 +                    if resource_id in res_ids:
 +                        res_ids.remove(resource_id)
 +                for res_id in res_ids:
 +                    lines.update(prepare_audittrail_log_line(cr, SUPERUSER_ID, pool, x2m_model, res_id, method, old_values, new_values, field_list))
 +        # if the value value is different than the old value: record the change
 +        if key not in old_values or key not in new_values or old_values[key]['value'][field_name] != new_values[key]['value'][field_name]:
 +            data = {
 +                  'name': field_name,
 +                  'new_value': key in new_values and new_values[key]['value'].get(field_name),
 +                  'old_value': key in old_values and old_values[key]['value'].get(field_name),
 +                  'new_value_text': key in new_values and new_values[key]['text'].get(field_name),
 +                  'old_value_text': key in old_values and old_values[key]['text'].get(field_name)
 +            }
 +            lines[key].append(data)
+             # On read log add current values for fields.
+             if method == 'read':
+                 data={
+                     'name': field_name,
+                     'old_value': key in old_values and old_values[key]['value'].get(field_name),
+                     'old_value_text': key in old_values and old_values[key]['text'].get(field_name)
+                 }
+                 lines[key].append(data)
 -        return lines
 +    return lines
  
 -    def process_data(self, cr, uid, pool, res_ids, model, method, old_values=None, new_values=None, field_list=None):
 -        """
 -        This function processes and iterates recursively to log the difference between the old
 -        data (i.e before the method was executed) and the new data and creates audittrail log
 -        accordingly.
 +def process_data(cr, uid, pool, res_ids, model, method, old_values=None, new_values=None, field_list=None):
 +    """
 +    This function processes and iterates recursively to log the difference between the old
 +    data (i.e before the method was executed) and the new data and creates audittrail log
 +    accordingly.
 +
 +    :param cr: the current row, from the database cursor,
 +    :param uid: the current user’s ID,
 +    :param pool: current db's pooler object.
 +    :param res_ids: Id's of resource to be logged/compared.
 +    :param model: model object which values are being changed
 +    :param method: method to log: create, read, unlink, write, actions, workflow actions
 +    :param old_values: dict of values read before execution of the method
 +    :param new_values: dict of values read after execution of the method
 +    :param field_list: optional argument containing the list of fields to log. Currently only
 +        used when performing a read, it could be usefull later on if we want to log the write
 +        on specific fields only.
 +    :return: True
 +    """
 +    if field_list is None:
 +        field_list = []
 +    # loop on all the given ids
 +    for res_id in res_ids:
 +        # compare old and new values and get audittrail log lines accordingly
 +        lines = prepare_audittrail_log_line(cr, uid, pool, model, res_id, method, old_values, new_values, field_list)
  
 -        :param cr: the current row, from the database cursor,
 -        :param uid: the current user’s ID,
 -        :param pool: current db's pooler object.
 -        :param res_ids: Id's of resource to be logged/compared.
 -        :param model: model object which values are being changed
 -        :param method: method to log: create, read, unlink, write, actions, workflow actions
 -        :param old_values: dict of values read before execution of the method
 -        :param new_values: dict of values read after execution of the method
 -        :param field_list: optional argument containing the list of fields to log. Currently only
 -            used when performing a read, it could be usefull later on if we want to log the write
 -            on specific fields only.
 -        :return: True
 -        """
 -        if field_list is None:
 -            field_list = []
 -        # loop on all the given ids
 -        for res_id in res_ids:
 -            # compare old and new values and get audittrail log lines accordingly
 -            lines = self.prepare_audittrail_log_line(cr, uid, pool, model, res_id, method, old_values, new_values, field_list)
 -
 -            # if at least one modification has been found
 -            for model_id, resource_id in lines:
 -                line_model = pool.get('ir.model').browse(cr, SUPERUSER_ID, model_id).model
 -
 -                vals = {
 -                    'method': method,
 -                    'object_id': model_id,
 -                    'user_id': uid,
 -                    'res_id': resource_id,
 -                }
 -                if (model_id, resource_id) not in old_values and method not in ('copy', 'read'):
 -                    # the resource was not existing so we are forcing the method to 'create'
 -                    # (because it could also come with the value 'write' if we are creating
 -                    #  new record through a one2many field)
 -                    vals.update({'method': 'create'})
 -                if (model_id, resource_id) not in new_values and method not in ('copy', 'read'):
 -                    # the resource is not existing anymore so we are forcing the method to 'unlink'
 -                    # (because it could also come with the value 'write' if we are deleting the
 -                    #  record through a one2many field)
 -                    name = old_values[(model_id, resource_id)]['value'].get('name',False)
 -                    vals.update({'method': 'unlink'})
 -                else :
 -                    name = pool[line_model].name_get(cr, uid, [resource_id])[0][1]
 -                vals.update({'name': name})
 -                # create the audittrail log in super admin mode, only if a change has been detected
 -                if lines[(model_id, resource_id)]:
 -                    log_id = pool.get('audittrail.log').create(cr, SUPERUSER_ID, vals)
 -                    model = pool.get('ir.model').browse(cr, uid, model_id)
 -                    self.create_log_line(cr, SUPERUSER_ID, log_id, model, lines[(model_id, resource_id)])
 -        return True
 +        # if at least one modification has been found
 +        for model_id, resource_id in lines:
 +            line_model = pool.get('ir.model').browse(cr, SUPERUSER_ID, model_id).model
  
 -    def check_rules(self, cr, uid, model, method):
 -        """
 -        Checks if auditrails is installed for that db and then if one rule match
 -        @param cr: the current row, from the database cursor,
 -        @param uid: the current user’s ID,
 -        @param model: value of _name of the object which values are being changed
 -        @param method: method to log: create, read, unlink,write,actions,workflow actions
 -        @return: True or False
 -        """
 -        pool = pooler.get_pool(cr.dbname)
 -        if 'audittrail.rule' in pool.models:
 -            model_ids = pool.get('ir.model').search(cr, SUPERUSER_ID, [('model', '=', model)])
 -            model_id = model_ids and model_ids[0] or False
 -            if model_id:
 -                rule_ids = pool.get('audittrail.rule').search(cr, SUPERUSER_ID, [('object_id', '=', model_id), ('state', '=', 'subscribed')])
 -                for rule in pool.get('audittrail.rule').read(cr, SUPERUSER_ID, rule_ids, ['user_id','log_read','log_write','log_create','log_unlink','log_action','log_workflow']):
 -                    if len(rule['user_id']) == 0 or uid in rule['user_id']:
 -                        if rule.get('log_'+method,0):
 +            vals = {
 +                'method': method,
 +                'object_id': model_id,
 +                'user_id': uid,
 +                'res_id': resource_id,
 +            }
 +            if (model_id, resource_id) not in old_values and method not in ('copy', 'read'):
 +                # the resource was not existing so we are forcing the method to 'create'
 +                # (because it could also come with the value 'write' if we are creating
 +                #  new record through a one2many field)
 +                vals.update({'method': 'create'})
 +            if (model_id, resource_id) not in new_values and method not in ('copy', 'read'):
 +                # the resource is not existing anymore so we are forcing the method to 'unlink'
 +                # (because it could also come with the value 'write' if we are deleting the
 +                #  record through a one2many field)
 +                name = old_values[(model_id, resource_id)]['value'].get('name',False)
 +                vals.update({'method': 'unlink'})
 +            else :
 +                name = pool[line_model].name_get(cr, uid, [resource_id])[0][1]
 +            vals.update({'name': name})
 +            # create the audittrail log in super admin mode, only if a change has been detected
 +            if lines[(model_id, resource_id)]:
 +                log_id = pool.get('audittrail.log').create(cr, SUPERUSER_ID, vals)
 +                model = pool.get('ir.model').browse(cr, uid, model_id)
 +                create_log_line(cr, SUPERUSER_ID, log_id, model, lines[(model_id, resource_id)])
 +    return True
 +
 +def check_rules(cr, uid, model, method):
 +    """
 +    Checks if auditrails is installed for that db and then if one rule match
 +    @param cr: the current row, from the database cursor,
 +    @param uid: the current user’s ID,
 +    @param model: value of _name of the object which values are being changed
 +    @param method: method to log: create, read, unlink,write,actions,workflow actions
 +    @return: True or False
 +    """
 +    pool = openerp.registry(cr.dbname)
 +    if 'audittrail.rule' in pool.models:
 +        model_ids = pool.get('ir.model').search(cr, SUPERUSER_ID, [('model', '=', model)])
 +        model_id = model_ids and model_ids[0] or False
 +        if model_id:
 +            rule_ids = pool.get('audittrail.rule').search(cr, SUPERUSER_ID, [('object_id', '=', model_id), ('state', '=', 'subscribed')])
 +            for rule in pool.get('audittrail.rule').read(cr, SUPERUSER_ID, rule_ids, ['user_id','log_read','log_write','log_create','log_unlink','log_action','log_workflow']):
 +                if len(rule['user_id']) == 0 or uid in rule['user_id']:
 +                    if rule.get('log_'+method,0):
 +                        return True
 +                    elif method not in ('default_get','read','fields_view_get','fields_get','search','search_count','name_search','name_get','get','request_get', 'get_sc', 'unlink', 'write', 'create', 'read_group', 'import_data'):
 +                        if rule['log_action']:
                              return True
 -                        elif method not in ('default_get','read','fields_view_get','fields_get','search','search_count','name_search','name_get','get','request_get', 'get_sc', 'unlink', 'write', 'create', 'read_group', 'import_data'):
 -                            if rule['log_action']:
 -                                return True
 -
 -    def execute_cr(self, cr, uid, model, method, *args, **kw):
 -        fct_src = super(audittrail_objects_proxy, self).execute_cr
 -        if self.check_rules(cr,uid,model,method):
 -            return self.log_fct(cr, uid, model, method, fct_src, *args, **kw)
 -        return fct_src(cr, uid, model, method, *args, **kw)
 -
 -    def exec_workflow_cr(self, cr, uid, model, method, *args, **kw):
 -        fct_src = super(audittrail_objects_proxy, self).exec_workflow_cr
 -        if self.check_rules(cr,uid,model,'workflow'):
 -            return self.log_fct(cr, uid, model, method, fct_src, *args, **kw)
 -        return fct_src(cr, uid, model, method, *args, **kw)
 -
 -audittrail_objects_proxy()
 +
 +# Replace the openerp.service.model functions.
 +
 +original_execute_cr = openerp.service.model.execute_cr
 +original_exec_workflow_cr = openerp.service.model.exec_workflow_cr
 +
 +def execute_cr(cr, uid, model, method, *args, **kw):
 +    fct_src = original_execute_cr
 +    if check_rules(cr,uid,model,method):
 +        return log_fct(cr, uid, model, method, fct_src, *args, **kw)
 +    return fct_src(cr, uid, model, method, *args, **kw)
 +
 +def exec_workflow_cr(cr, uid, model, method, *args, **kw):
 +    fct_src = original_exec_workflow_cr
 +    if check_rules(cr,uid,model,'workflow'):
 +        return log_fct(cr, uid, model, method, fct_src, *args, **kw)
 +    return fct_src(cr, uid, model, method, *args, **kw)
 +
 +openerp.service.model.execute_cr = execute_cr
 +openerp.service.model.exec_workflow_cr = exec_workflow_cr
  
  # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
  
@@@ -561,17 -533,11 +561,17 @@@ class pos_order(osv.osv)
                      'journal': cash_journal.id,
                  }, context=context)
              order_ids.append(order_id)
 -            wf_service = netsvc.LocalService("workflow")
 +
              try:
 -                wf_service.trg_validate(uid, 'pos.order', order_id, 'paid', cr)
 -            except Exception:
 -                _logger.error('ERROR: Could not fully process the POS Order', exc_info=True)
 +                self.signal_paid(cr, uid, [order_id])
 +            except Exception as e:
-                 _logger.error('Could not mark POS Order as Paid: %s', tools.ustr(e))
++                _logger.error('Could not fully process the POS Order: %s', tools.ustr(e))
 +
 +            if to_invoice:
 +                self.action_invoice(cr, uid, [order_id], context)
 +                order_obj = self.browse(cr, uid, order_id, context)
 +                self.pool['account.invoice'].signal_invoice_open(cr, uid, [order_obj.invoice_id.id])
 +
          return order_ids
  
      def write(self, cr, uid, ids, vals, context=None):