[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 <jco@odoo.com>
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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("<div>%s</div>") % (title)
|
||||
for value in tracking_value_ids:
|
||||
audit_log_preview += Markup("<li>%s <i class='o_TrackingValue_separator fa fa-long-arrow-rightmx-1 text-600' title='%s' role='img' aria-label='%s'></i>%s (%s)</li>") % (
|
||||
_("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)]
|
||||
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_message_tree_audit_log" model="ir.ui.view">
|
||||
<field name="name">mail.message.tree.inherit.audit.log</field>
|
||||
<field name="model">mail.message</field>
|
||||
<field name="priority">99</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree edit="0" delete="0" create="0">
|
||||
<field name="res_id" invisible="1"/>
|
||||
<field name="date"/>
|
||||
<field name="author_id" widget="many2one_avatar"/>
|
||||
<field name="l10n_in_audit_log_account_move_id"/>
|
||||
<field name="l10n_in_audit_log_preview"/>
|
||||
<button name="action_open_document" type="object" icon="fa-arrow-right" string="View Related Document" attrs="{'invisible': [('res_id', 'in', (0, False, ''))]}" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_message_tree_audit_log_search">
|
||||
<field name="name">mail.message.search</field>
|
||||
<field name="model">mail.message</field>
|
||||
<field name="priority">99</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Messages Search">
|
||||
<field name="l10n_in_audit_log_account_move_id"/>
|
||||
<field name="author_id"/>
|
||||
<field name="date" string="Date"/>
|
||||
<filter string="Update Only" name="update_only" domain="[('tracking_value_ids', '!=', False)]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="date" name="group_by_date" domain="[]" context="{'group_by': 'date'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_l10n_in_audit_trail_report" model="ir.actions.act_window">
|
||||
<field name="name">Audit trail</field>
|
||||
<field name="res_model">mail.message</field>
|
||||
<field name="view_id" ref="view_message_tree_audit_log"/>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="domain">[
|
||||
('model', '=', 'account.move'),
|
||||
('message_type', '=', 'notification'),
|
||||
('l10n_in_audit_log_account_move_id', '!=', False),
|
||||
]</field>
|
||||
<field name="search_view_id" ref="view_message_tree_audit_log_search"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="l10n_in_audit_trail_report_menu" name="Audit trail" action="action_l10n_in_audit_trail_report" parent="l10n_in.account_reports_in_statements_menu" sequence="2"
|
||||
groups="account.group_account_readonly"/>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user