Bläddra i källkod

commit inicial

Rodney Elpidio Enciso Arias 7 år sedan
incheckning
1083b861ea

+ 22 - 0
__init__.py

@@ -0,0 +1,22 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2010 Smile (<http://www.smile.fr>). All Rights Reserved
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import models

BIN
__init__.pyc


+ 52 - 0
__openerp__.py

@@ -0,0 +1,52 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2010 Smile (<http://www.smile.fr>). All Rights Reserved
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+{
+    "name": "Audit Trail",
+    "version": "0.1",
+    "sequence": 100,
+    "category": "Tools",
+    "author": "Smile",
+    "license": 'AGPL-3',
+    "website": 'http://www.smile.fr',
+    "description": """
+This module lets administrator track every user operation on all the objects of the system (for the moment, only create, write and unlink methods).
+
+WARNING: This module is not compatible with audit, so uninstall it before installing this one.
+
+Suggestions & Feedback to: corentin.pouhet-brunerie@smile.fr
+    """,
+    "depends": [
+        'base',
+    ],
+    "data": [
+        'security/ir.model.access.csv',
+        'views/audit_rule_view.xml',
+        'views/audit_log_view.xml',
+        'views/menu.xml',
+    ],
+    "test": [
+        'test/audit_test.yml',
+    ],
+    'installable': True,
+    'auto_install': False,
+    'application': False,
+}

+ 204 - 0
i18n/fr.po

@@ -0,0 +1,204 @@
+# Translation of OpenERP Server.
+# This file contains the translation of the following modules:
+#	* smile_audit
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: OpenERP Server 6.1-20121025-233243\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-10-17 15:35+0000\n"
+"PO-Revision-Date: 2013-10-17 15:35+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: smile_audit
+#: field:audit.rule,action_id:0
+msgid "Client Action"
+msgstr "Action client"
+
+#. module: smile_audit
+#: field:audit.rule,log_unlink:0
+msgid "Log Deletion"
+msgstr "Log de suppression"
+
+#. module: smile_audit
+#: view:audit.log:0
+#: model:ir.actions.act_window,name:smile_audit.action_audit_log_tree
+msgid "Audit Logs"
+msgstr "Logs d'audit"
+
+#. module: smile_audit
+#: view:audit.log:0
+msgid "Group By..."
+msgstr "Grouper par..."
+
+#. module: smile_audit
+#: view:audit.rule:0
+#: model:ir.model,name:smile_audit.model_audit_rule
+msgid "Audit Rule"
+msgstr "Règles d'audit"
+
+#. module: smile_audit
+#: view:audit.log:0
+msgid "Deletion"
+msgstr "Suppression"
+
+#. module: smile_audit
+#: field:audit.log,method:0
+msgid "Method"
+msgstr "Méthode"
+
+#. module: smile_audit
+#: field:audit.log,res_id:0
+msgid "Resource Id"
+msgstr "Id de la ressource"
+
+#. module: smile_audit
+#: sql_constraint:audit.rule:0
+msgid "There is already a rule defined on this object.\n"
+"You cannot define another: please edit the existing one."
+msgstr "Une règle d'audit existe déjà pour cet objet."
+
+#. module: smile_audit
+#: view:audit.log:0
+msgid "Log Lines"
+msgstr "Logs"
+
+#. module: smile_audit
+#: field:audit.rule,model:0
+msgid "unknown"
+msgstr "inconnu"
+
+#. module: smile_audit
+#: view:audit.rule:0
+msgid "create"
+msgstr "créer"
+
+#. module: smile_audit
+#: field:audit.log,model_id:0
+#: field:audit.rule,model_id:0
+msgid "Object"
+msgstr "Objet"
+
+#. module: smile_audit
+#: view:audit.log:0
+msgid "Update"
+msgstr "Mise à jour"
+
+#. module: smile_audit
+#: view:audit.rule:0
+msgid "write"
+msgstr "modifier"
+
+#. module: smile_audit
+#: help:audit.rule,model_id:0
+msgid "Select object for which you want to generate log."
+msgstr "Sélectionner les objets que vous souhaitez suivre"
+
+#. module: smile_audit
+#: view:audit.log:0
+#: field:audit.log,create_date:0
+msgid "Date"
+msgstr "Date"
+
+#. module: smile_audit
+#: field:audit.rule,log_create:0
+msgid "Log Creation"
+msgstr "Log de création"
+
+#. module: smile_audit
+#: view:audit.log:0
+#: field:audit.log,user_id:0
+msgid "User"
+msgstr "Utilisateur"
+
+#. module: smile_audit
+#: field:audit.log,name:0
+msgid "Resource Name"
+msgstr "Nom de la ressource"
+
+#. module: smile_audit
+#: field:audit.rule,name:0
+msgid "Name"
+msgstr "Nom"
+
+#. module: smile_audit
+#: field:audit.rule,active:0
+msgid "Active"
+msgstr "Active"
+
+#. module: smile_audit
+#: model:ir.model,name:smile_audit.model_audit_log_line
+msgid "Audit Log Line"
+msgstr "Ligne de log d'audit"
+
+#. module: smile_audit
+#: view:audit.log:0
+msgid "Model"
+msgstr "Modèle"
+
+#. module: smile_audit
+#: view:audit.rule:0
+msgid "unlink"
+msgstr "supprimer"
+
+#. module: smile_audit
+#: field:audit.rule,log_write:0
+msgid "Log Update"
+msgstr "Log de mise à jour"
+
+#. module: smile_audit
+#: field:audit.rule,values_id:0
+msgid "Add in the 'More' menu"
+msgstr "Ajouter au menu 'Autres options'"
+
+#. module: smile_audit
+#: view:audit.log:0
+#: model:ir.model,name:smile_audit.model_audit_log
+msgid "Audit Log"
+msgstr "Log d'audit"
+
+#. module: smile_audit
+#: view:audit.log:0
+msgid "Creation"
+msgstr "Création"
+
+#. module: smile_audit
+#: view:audit.rule:0
+#: model:ir.actions.act_window,name:smile_audit.action_audit_rule_tree
+msgid "Audit Rules"
+msgstr "Règles d'audit"
+
+#. module: smile_audit
+#: view:audit.log:0
+msgid "Log Line"
+msgstr "Ligne de log"
+
+#. module: smile_audit
+#: code:addons/smile_audit/audit_rule.py:79
+#, python-format
+msgid "View logs"
+msgstr "Pistes d'audit"
+
+#. module: smile_audit
+#: code:addons/smile_audit/audit_log.py:111
+#, python-format
+msgid "Field"
+msgstr "Champ"
+
+#. module: smile_audit
+#: code:addons/smile_audit/audit_log.py:111
+#, python-format
+msgid "Old value"
+msgstr "Ancienne valeur"
+
+#. module: smile_audit
+#: code:addons/smile_audit/audit_log.py:111
+#, python-format
+msgid "New value"
+msgstr "Nouvelle valeur"
+

+ 23 - 0
models/__init__.py

@@ -0,0 +1,23 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2010 Smile (<http://www.smile.fr>). All Rights Reserved
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import audit_log
+import audit_rule

BIN
models/__init__.pyc


+ 77 - 0
models/audit_decorator.py

@@ -0,0 +1,77 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2010 Smile (<http://www.smile.fr>). All Rights Reserved
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp import models
+
+
+def _get_args(self, method, args, kwargs):
+    # avoid hasattr(self, '_ids') because __getattr__() is overridden
+    if '_ids' in self.__dict__:
+        cr, uid, context = self.env.args
+        ids = self._ids
+        vals = method in ('create', 'write') and args[0] or {}
+    else:
+        cr, uid = args[:2]
+        ids = method in ('write', 'unlink') and args[2] or []
+        vals = (method == 'create' and args[2]) or (method == 'write' and args[3]) or {}
+        context = kwargs.get('context')
+    if isinstance(ids, (int, long)):
+        ids = [ids]
+    if self._name == 'res.users':
+        vals = self._remove_reified_groups(vals)
+    return cr, uid, ids, vals, context
+
+
+def audit_decorator():
+    def audit_wrapper(self, *args, **kwargs):
+        origin = audit_wrapper.origin
+        while hasattr(origin, 'origin'):
+            origin = origin.origin
+        method = origin.__name__
+        cr, uid, ids, vals, context = _get_args(self, method, args, kwargs)
+        rule_id = None
+        if getattr(self, 'audit_rule', None):
+            rule_obj = self.pool['audit.rule']
+            rule_id = rule_obj._check_audit_rule(cr).get(self._name, {}).get(method)
+        if rule_id:
+            old_values = None
+            if method != 'create':
+                records = self.browse(cr, uid, ids, context)
+                old_values = records.read(vals.keys(), load='_classic_write')
+                if method == 'unlink':
+                    rule_obj.log(cr, uid, rule_id, method, old_values)
+        result = audit_wrapper.origin(self, *args, **kwargs)
+        if rule_id:
+            new_values = None
+            if method != 'unlink':
+                if method == 'create':
+                    if isinstance(result, models.Model):
+                        ids = result.ids
+                    elif isinstance(result, int):
+                        ids = [result]
+                    else:
+                        ids = []
+                records = self.browse(cr, uid, ids, context)
+                records.invalidate_cache()
+                new_values = records.read(vals.keys(), load='_classic_write')
+                rule_obj.log(cr, uid, rule_id, method, old_values, new_values)
+        return result
+    return audit_wrapper

BIN
models/audit_decorator.pyc


+ 122 - 0
models/audit_log.py

@@ -0,0 +1,122 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2010 Smile (<http://www.smile.fr>). All Rights Reserved
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp import api, fields, models, _
+from openerp.exceptions import Warning
+from openerp.tools.safe_eval import safe_eval as eval
+
+
+class AuditLog(models.Model):
+    _name = 'audit.log'
+    _description = 'Audit Log'
+    _order = 'create_date desc'
+
+    name = fields.Char('Resource Name', size=256, compute='_get_name')
+    create_date = fields.Datetime('Date', readonly=True)
+    user_id = fields.Many2one('res.users', 'User', required=True, readonly=True)
+    model_id = fields.Many2one('ir.model', 'Object', required=True, readonly=True)
+    res_id = fields.Integer('Resource Id', readonly=True)
+    method = fields.Char('Method', size=64, readonly=True)
+    data = fields.Text('Data', readonly=True)
+    data_html = fields.Html('HTML Data', readonly=True, compute='_render_html')
+
+    @api.one
+    def _get_name(self):
+        if self.model_id and self.res_id:
+            record = self.env[self.model_id.model].browse(self.res_id).exists()
+            if record:
+                self.name = record.display_name
+            else:
+                data = eval(self.data or '{}')
+                rec_name = self.env[self.model_id.model]._rec_name
+                if rec_name in data['new']:
+                    self.name = data['new'][rec_name]
+                elif rec_name in data['old']:
+                    self.name = data['old'][rec_name]
+                else:
+                    self.name = 'id=%s' % self.res_id
+        else:
+            self.name = ''
+
+    @api.multi
+    def _format_value(self, field, value):
+        self.ensure_one()
+        if not value and field.type not in ('boolean', 'integer', 'float'):
+            return ''
+        if field.type == 'selection':
+            selection = field.selection
+            if callable(selection):
+                selection = getattr(self.env[self.model_id.model], selection)()
+            return dict(selection).get(value, value)
+        if field.type == 'many2one' and value:
+            return self.env[field.comodel_name].browse(value).exists().display_name or value
+        if field.type == 'reference' and value:
+            res_model, res_id = value.split(',')
+            return self.env[res_model].browse(int(res_id)).exists().display_name or value
+        if field.type in ('one2many', 'many2many') and value:
+            return ', '.join([self.env[field.comodel_name].browse(rec_id).exists().display_name or str(rec_id)
+                              for rec_id in value])
+        if field.type == 'binary' and value:
+            return '&lt;binary data&gt;'
+        return value
+
+    @api.multi
+    def _get_label(self, field):
+        label = field.string
+        lang = self.env.user.lang
+        translated_label = ''
+        if lang != 'en_US':
+            params = ('%s,%s' % (field.model_name, field.name), 'field', lang, label)
+            translated_label = self.env['ir.translation'].sudo()._get_source(*params)
+        return translated_label or label
+
+    @api.multi
+    def _get_content(self):
+        self.ensure_one()
+        content = []
+        data = eval(self.data or '{}')
+        model_obj = self.env[self.model_id.model]
+        for fname in set(data['new'].keys() + data['old'].keys()):
+            field = model_obj._fields.get(fname) or model_obj._inherit_fields.get(fname)
+            old_value = self._format_value(field, data['old'].get(fname, ''))
+            new_value = self._format_value(field, data['new'].get(fname, ''))
+            label = self._get_label(field)
+            content.append((label, old_value, new_value))
+        return content
+
+    @api.one
+    def _render_html(self):
+        thead = ''
+        for head in (_('Nombre'), _('Valor Anterior'), _('Nuevo Valor')):
+            thead += '<th>%s</th>' % head
+        thead = '<thead><tr class="oe_list_header_columns">%s</tr></thead>' % thead
+        tbody = ''
+        for line in self._get_content():
+            row = ''
+            for item in line:
+                row += '<td>%s</td>' % item
+            tbody += '<tr>%s</tr>' % row
+        tbody = '<tbody>%s</tbody>' % tbody
+        self.data_html = '<table class="oe_list_content">%s%s</table>' % (thead, tbody)
+
+    @api.multi
+    def unlink(self):
+        raise Warning(_('You cannot remove audit logs!'))

BIN
models/audit_log.pyc


+ 189 - 0
models/audit_rule.py

@@ -0,0 +1,189 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2010 Smile (<http://www.smile.fr>). All Rights Reserved
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import logging
+
+from openerp import api, fields, models, SUPERUSER_ID, tools, _
+from openerp.modules.registry import RegistryManager
+
+from audit_decorator import audit_decorator
+
+_logger = logging.getLogger(__package__)
+
+
+class AuditRule(models.Model):
+    _name = 'audit.rule'
+    _description = 'Audit Rule'
+
+    name = fields.Char(size=32, required=True)
+    active = fields.Boolean(default=True)
+    log_create = fields.Boolean('Log Creation', default=True)
+    log_write = fields.Boolean('Log Update', default=True)
+    log_unlink = fields.Boolean('Log Deletion', default=True)
+    state = fields.Selection([('draft', 'Draft'), ('done', 'Done')], 'Status', default='draft', readonly=True)
+    model_id = fields.Many2one('ir.model', 'Object', required=True,
+                               help='Select object for which you want to generate log.',
+                               domain=[('model', '!=', 'audit.log')],
+                               readonly=True, states={'draft': [('readonly', False)]})
+    action_id = fields.Many2one('ir.actions.act_window', 'Client Action', readonly=True)
+    values_id = fields.Many2one('ir.values', "Add in the 'More' menu", readonly=True)
+
+    _sql_constraints = [
+        ('model_uniq', 'unique(model_id)', 'There is already a rule defined on this object.\n'
+         'You cannot define another: please edit the existing one.'),
+    ]
+
+    @api.one
+    def _add_action(self):
+        if not self.action_id:
+            vals = {
+                'name': _('View audit logs'),
+                'res_model': 'audit.log',
+                'src_model': self.model_id.model,
+                'domain': "[('model_id','=', %s), ('res_id', '=', active_id)]" % self.model_id.id,
+            }
+            self.action_id = self.env['ir.actions.act_window'].create(vals)
+
+    @api.one
+    def _add_values(self):
+        if not self.values_id:
+            self.env['ir.model.data'].ir_set('action', 'client_action_relate', 'view_log_' + self.model_id.model,
+                                             [self.model_id.model], 'ir.actions.act_window,%s' % self.action_id.id,
+                                             replace=True, isobject=True, xml_id=False)
+            values = self.env['ir.values'].search([('model', '=', self.model_id.model),
+                                                   ('value', '=', 'ir.actions.act_window,%s' % self.action_id.id)])
+            if values:
+                self.values_id = values[0]
+
+    @api.one
+    def _activate(self):
+        if self._context and \
+                self._context.get('activation_in_progress'):
+            return
+        self = self.with_context(activation_in_progress=True)
+        self._add_action()
+        self._add_values()
+
+    @api.one
+    def _deactivate(self):
+        if self.values_id:
+            self.values_id.unlink()
+        if self.action_id:
+            self.action_id.unlink()
+
+    @api.multi
+    def update_rule(self, force_deactivation=False):
+        for rule in self:
+            if rule.active and not force_deactivation:
+                rule._activate()
+            else:
+                rule._deactivate()
+        return True
+
+    @tools.cache()
+    def _check_audit_rule(self, cr):
+        ids = self.search(cr, SUPERUSER_ID, [])
+        return {rule.model_id.model:
+                {method: rule.id
+                 for method in ('create', 'write', 'unlink')
+                 if getattr(rule, 'log_%s' % method)}
+                for rule in self.browse(cr, SUPERUSER_ID, ids)}
+
+    def _register_hook(self, cr, ids=None):
+        updated = False
+        if not ids:
+            ids = self.search(cr, SUPERUSER_ID, [])
+        for rule in self.browse(cr, SUPERUSER_ID, ids):
+            model_obj = self.pool.get(rule.model_id.model)
+            if not model_obj:
+                continue
+            if rule.active and not hasattr(model_obj, 'audit_rule'):
+                for method in ('create', 'write', 'unlink'):
+                    model_obj._patch_method(method, audit_decorator())
+                model_obj.audit_rule = True
+                updated = True
+            if not rule.active and hasattr(model_obj, 'audit_rule'):
+                for method_name in ('create', 'write', 'unlink'):
+                    method = getattr(model_obj, method_name)
+                    while hasattr(method, 'origin'):
+                        if method.__name__ == 'audit_wrapper':
+                            model_obj._revert_method(method_name)
+                            break
+                        method = method.origin
+                del model_obj.audit_rule
+                updated = True
+        if updated:
+            self.clear_caches()
+        return updated
+
+    @api.model
+    @api.returns('self', lambda value: value.id)
+    def create(self, vals):
+        vals['state'] = 'done'
+        rule = super(AuditRule, self).create(vals)
+        rule.update_rule()
+        if self._register_hook(rule.id):
+            RegistryManager.signal_registry_change(self.env.cr.dbname)
+        return rule
+
+    @api.multi
+    def write(self, vals):
+        res = super(AuditRule, self).write(vals)
+        self.update_rule()
+        if self._register_hook(self._ids):
+            RegistryManager.signal_registry_change(self.env.cr.dbname)
+        return res
+
+    @api.multi
+    def unlink(self):
+        self.update_rule(force_deactivation=True)
+        return super(AuditRule, self).unlink()
+
+    _ignored_fields = ['message_ids', 'message_last_post']
+
+    @classmethod
+    def _format_data_to_log(cls, old_values, new_values):
+        data = {}
+        for age in ('old', 'new'):
+            vals_list = old_values if age == 'old' else new_values
+            if isinstance(vals_list, dict):
+                vals_list = [vals_list]
+            for vals in vals_list or []:
+                for field in cls._ignored_fields:
+                    vals.pop(field, None)
+                res_id = vals.pop('id')
+                if vals:
+                    data.setdefault(res_id, {'old': {}, 'new': {}})[age] = vals
+        return data
+
+    @api.one
+    def log(self, method, old_values=None, new_values=None):
+        _logger.debug('Starting audit log')
+        data = self._format_data_to_log(old_values, new_values)
+        for res_id in data:
+            self.env['audit.log'].sudo().create({
+                'user_id': self._uid,
+                'model_id': self.sudo().model_id.id,
+                'method': method,
+                'res_id': res_id,
+                'data': repr(data[res_id]),
+            })
+        return True

BIN
models/audit_rule.pyc


+ 5 - 0
security/ir.model.access.csv

@@ -0,0 +1,5 @@
+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
+"ir_model_access_audit_rule_group_system","Audit Rule Manager","model_audit_rule","base.group_system",1,1,1,1
+"ir_model_access_audit_rule_group_user","Audit Rule User","model_audit_rule","base.group_user",1,0,0,0
+"ir_model_access_audit_log_group_system","Audit Log Manager","model_audit_log","base.group_system",1,0,1,0
+"ir_model_access_audit_log_group_user","Audit Log User","model_audit_log","base.group_user",1,0,0,0

BIN
static/description/icon.png


+ 55 - 0
test/audit_test.yml

@@ -0,0 +1,55 @@
+-
+  I create an audit rule on partners
+-
+  !python {model: audit.rule}: |
+    rule_id = self.search(cr, uid, [('model_id.model', '=', 'res.partner')], limit=1, context=context)
+    if not rule_id:
+      rule_id = self.create(cr, uid, {'name': 'Audit rule on partners', 'model_id': ref('base.model_res_partner')}, context)
+    else:
+      rule_id = rule_id[0]
+    self.pool.get('ir.model.data').create(cr, uid, {'name': 'rule_partners', 'module': 'smile_audit', 'model': 'res.partner', 'res_id': rule_id}, context)
+-
+  As demo user, I create a new partner
+-
+  !record {model: res.partner, id: res_partner_test, view: False}:
+    name: Test
+-
+  I check if an audit log is created after creation
+-
+  !python {model: audit.log}: |
+    domain = [
+      ('model_id', '=', ref('base.model_res_partner')),
+      ('method', '=', 'create'),
+      ('res_id', '=', ref('res_partner_test')),
+    ]
+    assert self.search(cr, uid, domain, limit=1, context=context), 'No audit log after user creation'
+-
+  As demo user, I update contact name
+-
+  !record {model: res.partner, id: res_partner_test, view: False}:
+    name: Test 2
+-
+  I check if an audit log is created after update
+-
+  !python {model: audit.log}: |
+    domain = [
+      ('model_id', '=', ref('base.model_res_partner')),
+      ('method', '=', 'write'),
+      ('res_id', '=', ref('res_partner_test')),
+    ]
+    assert self.search(cr, uid, domain, limit=1, context=context), 'No audit log after user update'
+-
+  As demo user, I delete this address
+-
+  !python {model: res.partner}: |
+    self.unlink(cr, uid, [ref('res_partner_test')], context)
+-
+  I check if an audit log is created after deletion
+-
+  !python {model: audit.log}: |
+    domain = [
+      ('model_id', '=', ref('base.model_res_partner')),
+      ('method', '=', 'unlink'),
+      ('res_id', '=', ref('res_partner_test')),
+    ]
+    assert self.search(cr, uid, domain, limit=1, context=context), 'No audit log after user deletion'

+ 76 - 0
views/audit_log_view.xml

@@ -0,0 +1,76 @@
+<?xml version="1.0"?>
+<openerp>
+    <data>
+
+        <record model="ir.ui.view" id="view_audit_log_form">
+            <field name="name">audit.log.form</field>
+            <field name="model">audit.log</field>
+            <field name="type">form</field>
+            <field name="arch" type="xml">
+                <form string="Regitros de Auditoria">
+                	<sheet>
+	                	<group col="4">
+		                	<group>
+			                    <field name="name" string="Nombre"/>
+			                    <field name="user_id" string="Usuario"/>
+			                    <field name="create_date" string="Fecha y Hora"/>
+			                </group>
+		                	<group>
+			                    <field name="model_id" string="Modelo"/>
+			                    <field name="method" string="Metodo"/>
+			                    <field name="res_id" string="ID del Recurso"/>
+			                </group>
+		                </group>
+                    <field name="data_html" colspan="4" nolabel="1"/>
+	                </sheet>
+                </form>
+            </field>
+        </record>
+
+        <record model="ir.ui.view" id="view_audit_log_tree">
+            <field name="name">audit.log.tree</field>
+            <field name="model">audit.log</field>
+            <field name="type">tree</field>
+            <field name="arch" type="xml">
+                <tree string="egitros de Auditoria">
+                    <field name="create_date" string="Fecha y Hora"/>
+                    <field name="name" string="Nombre"/>
+                    <field name="model_id" string="Modelo"/>
+                    <field name="method" string="Metodo"/>
+                    <field name="user_id" string="Usuario"/>
+                </tree>
+            </field>
+        </record>
+        
+       <record id="view_audit_log_search" model="ir.ui.view">
+            <field name="name">audit.log.search</field>
+            <field name="model">audit.log</field>
+            <field name="type">search</field>
+            <field name="arch" type="xml">
+                <search string="Audit Logs">
+                    <filter string="Creación" icon="terp-tools" domain="[('method','=','create')]"/>
+                    <filter string="Modificación" icon="terp-tools" domain="[('method','=','write')]"/>
+                    <filter string="Eliminación" icon="terp-tools" domain="[('method','=','unlink')]"/>
+                    <field name="name" string="Nombre"/>
+                    <field name="model_id" string="Modelo"/>
+                    <field name="user_id" string="Usuario"/>
+	                <newline/>
+    	            <group expand="0" string="Group By...">
+                    	<filter string="Usuario" icon="terp-personal" context="{'group_by':'user_id'}"/>
+                    	<filter string="Modelo" icon="terp-stock_align_left_24" context="{'group_by':'model_id'}"/>
+						<filter string="Fecha" icon="terp-go-month" context="{'group_by':'create_date'}"/>
+                	</group>
+                </search>
+            </field>
+        </record>
+
+        <record model="ir.actions.act_window" id="action_audit_log_tree">
+            <field name="name">Registros</field>
+            <field name="res_model">audit.log</field>
+            <field name="view_type">form</field>
+            <field name="search_view_id" ref="view_audit_log_search"/>
+            <field name="view_id" ref="view_audit_log_tree"/>
+        </record>
+
+    </data>
+</openerp>

+ 50 - 0
views/audit_rule_view.xml

@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<openerp>
+    <data>
+
+        <record model="ir.ui.view" id="view_audit_rule_tree">
+            <field name="name">audit.rule.tree</field>
+            <field name="model">audit.rule</field>
+            <field name="type">tree</field>
+            <field name="arch" type="xml">
+                <tree string="Reglas de Auditoria" editable="top">
+                    <field name="name" string="Nombre"/>
+                    <field name="model_id" string="Modelo"/>
+                    <field name="log_create" string="Creación"/>
+                    <field name="log_write" string="Modificación"/>
+                    <field name="log_unlink" string="Eliminación"/>
+                    <field name="active"/>
+                    <field name="state" invisible="1"/>
+                </tree>
+            </field>
+        </record>
+
+       <record id="view_audit_rule_search" model="ir.ui.view">
+            <field name="name">audit.rule.search</field>
+            <field name="model">audit.rule</field>
+            <field name="type">search</field>
+            <field name="arch" type="xml">
+                <search string="Audit Rules">
+                    <filter name="active" string="Reglas Activas" domain="[('active','=',True)]"/>
+                    <separator/>
+                    <filter string="Creación" domain="[('log_create','=',True)]"/>
+                    <filter string="Modificación" domain="[('log_write','=',True)]"/>
+                    <filter string="Eliminación" domain="[('log_unlink','=',True)]"/>
+                    <field name="name" string="Nombre"/>
+                    <field name="model_id" string="Modelo"/>
+                </search>
+            </field>
+        </record>
+
+        <record model="ir.actions.act_window" id="action_audit_rule_tree">
+            <field name="name">Reglas</field>
+            <field name="res_model">audit.rule</field>
+            <field name="type">ir.actions.act_window</field>
+            <field name="view_type">form</field>
+            <field name="view_mode">tree</field>
+            <field name="search_view_id" ref="view_audit_rule_search"/>
+            <field name="context">{'search_default_active': True, 'active_test': False}</field>
+        </record>
+
+    </data>
+</openerp>

+ 14 - 0
views/menu.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<openerp>
+    <data>
+
+        <menuitem id="menu_audit" name="Auditoria" sequence="100"/>
+
+        <menuitem id="submenu_audit" name="Auditoria" sequence="1" parent="menu_audit"/>
+
+        <menuitem id="menu_action_audit_rule_tree" action="action_audit_rule_tree" parent="submenu_audit"/>
+
+        <menuitem id="menu_action_audit_log_tree" action="action_audit_log_tree" parent="submenu_audit"/>
+
+    </data>
+</openerp>