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 + + + + + + + +