From 6eb8213d0ea1867ba13bd2afc928283bc74f03ab Mon Sep 17 00:00:00 2001 From: Jigar Vaghela Date: Fri, 31 Mar 2023 12:28:09 +0000 Subject: [PATCH] [IMP] l10n_in: Audit Trail In Indian government required audit trail report for private limited companies So from this commit user can't delete journal entries after posting once. Create an audit trail report from mail.message to show a full log of journal entries task - 3239932 closes odoo/odoo#118945 X-original-commit: f4365ee1c6e979e352ece66b03f3c00e830f2117 Signed-off-by: Josse Colpaert --- addons/l10n_in/__manifest__.py | 1 + addons/l10n_in/models/__init__.py | 1 + addons/l10n_in/models/account_invoice.py | 30 ++++++++++- addons/l10n_in/models/mail_message.py | 50 ++++++++++++++++++ .../report/audit_trail_report_views.xml | 52 +++++++++++++++++++ 5 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 addons/l10n_in/models/mail_message.py create mode 100644 addons/l10n_in/report/audit_trail_report_views.xml diff --git a/addons/l10n_in/__manifest__.py b/addons/l10n_in/__manifest__.py index c8334eba143..069f99d0958 100644 --- a/addons/l10n_in/__manifest__.py +++ b/addons/l10n_in/__manifest__.py @@ -40,6 +40,7 @@ Sheet, now only Vertical format has been permitted Which is Supported By Odoo. 'views/res_partner_views.xml', 'views/account_tax_views.xml', 'views/uom_uom_views.xml', + 'report/audit_trail_report_views.xml', ], 'demo': [ 'demo/demo_company.xml', diff --git a/addons/l10n_in/models/__init__.py b/addons/l10n_in/models/__init__.py index fd1aa5c2811..bdb3566e50f 100644 --- a/addons/l10n_in/models/__init__.py +++ b/addons/l10n_in/models/__init__.py @@ -9,3 +9,4 @@ from . import res_config_settings from . import res_country_state from . import res_partner from . import uom_uom +from . import mail_message diff --git a/addons/l10n_in/models/account_invoice.py b/addons/l10n_in/models/account_invoice.py index 8852d1d6ca2..b1289a5bddf 100644 --- a/addons/l10n_in/models/account_invoice.py +++ b/addons/l10n_in/models/account_invoice.py @@ -1,8 +1,12 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +import logging + from odoo import api, fields, models, _ -from odoo.exceptions import ValidationError, RedirectWarning +from odoo.exceptions import ValidationError, RedirectWarning, UserError + +_logger = logging.getLogger(__name__) class AccountMove(models.Model): @@ -94,3 +98,27 @@ class AccountMove(models.Model): # TO OVERRIDE self.ensure_one() return False + + @api.ondelete(at_uninstall=False) + def _unlink_l10n_in_except_once_post(self): + # Prevent deleting entries once it's posted for Indian Company only + if any(m.country_code == 'IN' and m.posted_before for m in self) and not self._context.get('force_delete'): + raise UserError(_("To keep the audit trail, you can not delete journal entries once they have been posted")) + + def unlink(self): + # Add logger here becouse in api ondelete account.move.line is deleted and we can't get total amount + logger_msg = False + if any(m.country_code == 'IN' and m.posted_before for m in self): + if self._context.get('force_delete'): + moves_details = ", ".join("{entry_number} ({move_id}) amount {amount_total} {currency} and partner {partner_name}".format( + entry_number=m.name, + move_id=m.id, + amount_total=m.amount_total, + currency=m.currency_id.name, + partner_name=m.partner_id.display_name) + for m in self) + logger_msg = 'Force deleted Journal Entries %s by %s (%s)' % (moves_details, self.env.user.name, self.env.user.id) + res = super().unlink() + if logger_msg: + _logger.info(logger_msg) + return res diff --git a/addons/l10n_in/models/mail_message.py b/addons/l10n_in/models/mail_message.py new file mode 100644 index 00000000000..908f039da0f --- /dev/null +++ b/addons/l10n_in/models/mail_message.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from markupsafe import Markup + +from odoo import fields, api, models, _ +from odoo.exceptions import UserError + + +class Message(models.Model): + _inherit = 'mail.message' + + l10n_in_audit_log_preview = fields.Html(string="Description", compute="_compute_l10n_in_audit_log_preview") + l10n_in_audit_log_account_move_id = fields.Many2one('account.move', string="Journal Entry", compute="_compute_l10n_in_audit_log_document_name", search="_search_l10n_in_audit_log_document_name") + + @api.depends('body', 'subject', 'tracking_value_ids', 'subtype_id') + def _compute_l10n_in_audit_log_preview(self): + for message in self: + title = message.subject or message.preview + tracking_value_ids = message.sudo().tracking_value_ids + if not title and tracking_value_ids: + title = _("Updated") + elif not title and message.subtype_id and not message.subtype_id.internal: + title = message.subtype_id.display_name + audit_log_preview = Markup("
%s
") % (title) + for value in tracking_value_ids: + audit_log_preview += Markup("
  • %s %s (%s)
  • ") % ( + _("Changed"), _("Changed"), value._get_old_display_value()[0], value._get_new_display_value()[0], value.field.field_description) + message.l10n_in_audit_log_preview = audit_log_preview + + @api.depends('model', 'res_id') + def _compute_l10n_in_audit_log_document_name(self): + messages_of_account_move = self.filtered(lambda m: m.model == 'account.move' and m.res_id) + (self - messages_of_account_move).l10n_in_audit_log_account_move_id = False + moves = self.env['account.move'].search([('id', 'in', messages_of_account_move.mapped('res_id'))]) + moves_by_id = {m.id: m for m in moves} + for message in messages_of_account_move: + message.l10n_in_audit_log_account_move_id = moves_by_id.get(message.res_id, False) + + def _search_l10n_in_audit_log_document_name(self, operator, value): + is_set = False + if operator == '!=' and isinstance(value, bool): + is_set = True + elif operator not in ['=', 'ilike'] or not isinstance(value, str): + raise UserError(_('Operation not supported')) + move_domain = [('company_id.account_fiscal_country_id.code', '=', 'IN')] + if not is_set: + move_domain += [('name', operator, value)] + move_query = self.env['account.move']._search(move_domain) + return [('model', '=', 'account.move'), ('res_id', 'in', move_query)] diff --git a/addons/l10n_in/report/audit_trail_report_views.xml b/addons/l10n_in/report/audit_trail_report_views.xml new file mode 100644 index 00000000000..4e0a3ba8991 --- /dev/null +++ b/addons/l10n_in/report/audit_trail_report_views.xml @@ -0,0 +1,52 @@ + + + + + mail.message.tree.inherit.audit.log + mail.message + 99 + + + + + + + +