[ADD] account_audit_trail: separate module from l10n_in

Before this commit
==================
The `account_audit_trail` was part of the `l10n_in` module, which restricted its
usage to Indian localization.

After this commit
=================
The `account_audit_trail` module has been separated from the `l10n_in` module.
It is now a standalone module, allowing it to be used independently in various
locations if needed.

task_id: 3326258

closes odoo/odoo#123650

Signed-off-by: William André (wan) <wan@odoo.com>
This commit is contained in:
kasp-odoo
2023-12-20 18:01:44 +00:00
committed by Kashyap Patel
parent e40413da5d
commit 763cd36f65
11 changed files with 417 additions and 0 deletions
+9
View File
@@ -10,6 +10,15 @@ resource_name = account
replace_edited_strings = false
keep_translations = false
[o:odoo:p:odoo-17:r:account_audit_trail]
file_filter = addons/account_audit_trail/i18n/<lang>.po
source_file = addons/account_audit_trail/i18n/account_audit_trail.pot
type = PO
minimum_perc = 0
resource_name = account_audit_trail
replace_edited_strings = false
keep_translations = false
[o:odoo:p:odoo-17:r:account_check_printing]
file_filter = addons/account_check_printing/i18n/<lang>.po
source_file = addons/account_check_printing/i18n/account_check_printing.pot
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
@@ -0,0 +1,14 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Account Audit Trail',
'version': '1.0',
'category': 'Accounting/Accounting',
'summary': 'Account Audit Trail',
'depends': ['account'],
'data': [
'report/audit_trail_report_views.xml',
'views/res_config_settings_views.xml',
],
'license': 'LGPL-3',
}
@@ -0,0 +1,139 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_audit_trail
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 17.0+e\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-12-15 06:37+0000\n"
"PO-Revision-Date: 2023-12-15 06:37+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: account_audit_trail
#: model_terms:ir.ui.view,arch_db:account_audit_trail.res_config_settings_view_form_inherit_account_audit_trail
msgid "Activate Audit Trail"
msgstr ""
#. module: account_audit_trail
#: model:ir.actions.act_window,name:account_audit_trail.action_account_audit_trail_report
#: model:ir.model.fields,field_description:account_audit_trail.field_res_company__check_account_audit_trail
#: model:ir.model.fields,field_description:account_audit_trail.field_res_config_settings__check_account_audit_trail
#: model:ir.ui.menu,name:account_audit_trail.account_audit_trail_menu
#: model_terms:ir.ui.view,arch_db:account_audit_trail.res_config_settings_view_form_inherit_account_audit_trail
msgid "Audit Trail"
msgstr ""
#. module: account_audit_trail
#. odoo-python
#: code:addons/account_audit_trail/models/res_config_settings.py:0
#, python-format
msgid "Can't disable audit trail when there are existing records."
msgstr ""
#. module: account_audit_trail
#. odoo-python
#: code:addons/account_audit_trail/models/mail_message.py:0
#, python-format
msgid "Changed"
msgstr ""
#. module: account_audit_trail
#: model:ir.model,name:account_audit_trail.model_res_company
msgid "Companies"
msgstr ""
#. module: account_audit_trail
#: model:ir.model,name:account_audit_trail.model_res_config_settings
msgid "Config Settings"
msgstr ""
#. module: account_audit_trail
#: model_terms:ir.ui.view,arch_db:account_audit_trail.view_message_tree_audit_log_search
msgid "Date"
msgstr ""
#. module: account_audit_trail
#: model:ir.model.fields,field_description:account_audit_trail.field_mail_mail__account_audit_log_preview
#: model:ir.model.fields,field_description:account_audit_trail.field_mail_message__account_audit_log_preview
msgid "Description"
msgstr ""
#. module: account_audit_trail
#: model_terms:ir.ui.view,arch_db:account_audit_trail.res_config_settings_view_form_inherit_account_audit_trail
msgid "Go to Audit Trail"
msgstr ""
#. module: account_audit_trail
#: model_terms:ir.ui.view,arch_db:account_audit_trail.view_message_tree_audit_log_search
msgid "Group By"
msgstr ""
#. module: account_audit_trail
#: model:ir.model,name:account_audit_trail.model_account_move
#: model:ir.model.fields,field_description:account_audit_trail.field_mail_mail__account_audit_log_move_id
#: model:ir.model.fields,field_description:account_audit_trail.field_mail_message__account_audit_log_move_id
#: model_terms:ir.ui.view,arch_db:account_audit_trail.view_message_tree_audit_log_search
msgid "Journal Entry"
msgstr ""
#. module: account_audit_trail
#: model:ir.model,name:account_audit_trail.model_mail_message
msgid "Message"
msgstr ""
#. module: account_audit_trail
#: model_terms:ir.ui.view,arch_db:account_audit_trail.view_message_tree_audit_log_search
msgid "Messages Search"
msgstr ""
#. module: account_audit_trail
#. odoo-python
#: code:addons/account_audit_trail/models/mail_message.py:0
#: code:addons/account_audit_trail/models/mail_message.py:0
#, python-format
msgid "Operation not supported"
msgstr ""
#. module: account_audit_trail
#: model:ir.model.fields,field_description:account_audit_trail.field_mail_mail__show_audit_log
#: model:ir.model.fields,field_description:account_audit_trail.field_mail_message__show_audit_log
msgid "Show Audit Log"
msgstr ""
#. module: account_audit_trail
#. odoo-python
#: code:addons/account_audit_trail/models/account_move.py:0
#, python-format
msgid ""
"To keep the audit trail, you can not delete journal entries once they have been posted.\n"
"Instead, you can cancel the journal entry."
msgstr ""
#. module: account_audit_trail
#: model_terms:ir.actions.act_window,help:account_audit_trail.action_account_audit_trail_report
msgid ""
"To see the audit log you have to enable the audit trail option from settings"
msgstr ""
#. module: account_audit_trail
#: model_terms:ir.ui.view,arch_db:account_audit_trail.view_message_tree_audit_log_search
msgid "Update Only"
msgstr ""
#. module: account_audit_trail
#. odoo-python
#: code:addons/account_audit_trail/models/mail_message.py:0
#, python-format
msgid "Updated"
msgstr ""
#. module: account_audit_trail
#: model_terms:ir.ui.view,arch_db:account_audit_trail.view_message_tree_audit_log_search
msgid "date"
msgstr ""
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import mail_message
from . import account_move
from . import res_config_settings
from . import res_company
@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
import logging
from odoo import api, models, _
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
class AccountMove(models.Model):
_inherit = "account.move"
@api.ondelete(at_uninstall=False)
def _unlink_account_audit_trail_except_once_post(self):
if not self._context.get('force_delete') and any(
move.posted_before and move.company_id.check_account_audit_trail
for move in self
):
raise UserError(_("To keep the audit trail, you can not delete journal entries once they have been posted.\nInstead, you can cancel the journal entry."))
def unlink(self):
# Add logger here because in api ondelete account.move.line is deleted and we can't get total amount
logger_msg = False
if any(m.posted_before and m.company_id.check_account_audit_trail for m in self):
if self._context.get('force_delete'):
moves_details = []
for move in self:
entry_details = "{move_name} ({move_id}) amount {amount_total} {currency} and partner {partner_name}".format(
move_name=move.name,
move_id=move.id,
amount_total=move.amount_total,
currency=move.currency_id.name,
partner_name=move.partner_id.display_name,
)
account_balances_per_account = defaultdict(float)
for line in move.line_ids:
account_balances_per_account[line.account_id] += line.balance
account_details = "\n".join(
"- {account_name} ({account_id}) with balance {balance} {currency}".format(
account_name=account.name,
account_id=account.id,
balance=balance,
currency=line.currency_id.name,
)
for account, balance in account_balances_per_account.items()
)
moves_details.append("{entry_details}\n{account_details}".format(
entry_details=entry_details, account_details=account_details
))
logger_msg = "\nForce deleted Journal Entries by {user_name} ({user_id})\nEntries\n{moves_details}".format(
user_name=self.env.user.name,
user_id=self.env.user.id,
moves_details="\n".join(moves_details),
)
res = super().unlink()
if logger_msg:
_logger.info(logger_msg)
return res
@@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from markupsafe import Markup
from odoo import fields, models, _
from odoo.exceptions import UserError
class Message(models.Model):
_inherit = 'mail.message'
account_audit_log_preview = fields.Html(string="Description", compute="_compute_account_audit_log_preview")
account_audit_log_move_id = fields.Many2one(
comodel_name='account.move',
string="Journal Entry",
compute="_compute_account_audit_log_move_id",
search="_search_account_audit_log_move_id",
)
show_audit_log = fields.Boolean(compute="_compute_account_audit_log_move_id", search="_search_show_audit_log")
def _compute_account_audit_log_preview(self):
for message in self:
title = message.subject or message.preview
tracking_value_ids = message.sudo().tracking_value_ids.filtered(lambda tracking: not tracking.field_groups or self.env.is_superuser() or self.user_has_groups(tracking.field_groups))
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 fmt_vals in tracking_value_ids._tracking_value_format():
field_desc = fmt_vals['changedField']
old_value = fmt_vals['oldValue']['value']
new_value = fmt_vals['newValue']['value']
audit_log_preview += Markup(
"<li>%(old_value)s <i class='o_TrackingValue_separator fa fa-long-arrow-right mx-1 text-600' title='%(title)s' role='img' aria-label='%(title)s'></i>%(new_value)s (%(field)s)</li>"
) % {
'old_value': old_value,
'new_value': new_value,
'title': _("Changed"),
'field': field_desc,
}
message.account_audit_log_preview = audit_log_preview
def _compute_account_audit_log_move_id(self):
messages_of_account_move = self.filtered(lambda m: m.model == 'account.move' and m.res_id)
recordset_difference = (self - messages_of_account_move)
recordset_difference.update({
'account_audit_log_move_id': False,
'show_audit_log': False,
})
moves = self.env['account.move'].sudo().search([
('id', 'in', messages_of_account_move.mapped('res_id')),
('company_id.check_account_audit_trail', '=', True),
])
moves_by_id = {m.id: m for m in moves}
for message in messages_of_account_move:
message.account_audit_log_move_id = moves_by_id.get(message.res_id, False)
message.show_audit_log = bool(moves_by_id.get(message.res_id))
def _search_account_audit_log_move_id(self, operator, value):
if operator in ['=', 'like', 'ilike', '!=', 'not ilike', 'not like'] and isinstance(value, str):
res_id_domain = [('res_id', 'in', self.env['account.move']._name_search(value, operator=operator))]
elif operator in ['=', 'in', '!=', 'not in']:
res_id_domain = [('res_id', operator, value)]
else:
raise UserError(_('Operation not supported'))
return [('model', '=', 'account.move')] + res_id_domain
def _search_show_audit_log(self, operator, value):
if operator not in ['=', '!='] or not isinstance(value, bool):
raise UserError(_('Operation not supported'))
move_query = self.env['account.move']._search([('company_id.check_account_audit_trail', operator, value)])
return [('model', '=', 'account.move'), ('res_id', 'in', move_query)]
@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class Company(models.Model):
_inherit = "res.company"
check_account_audit_trail = fields.Boolean(string='Audit Trail')
@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
check_account_audit_trail = fields.Boolean(string='Audit Trail', related='company_id.check_account_audit_trail', readonly=False)
@api.constrains('check_account_audit_trail')
def _check_audit_trail_records(self):
if not self.check_account_audit_trail:
move_count = self.env['account.move'].search_count([('company_id', '=', self.company_id.id)], limit=1)
if move_count > 0:
raise UserError(_("Can't disable audit trail when there are existing records."))
@@ -0,0 +1,59 @@
<?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="mode">primary</field>
<field name="arch" type="xml">
<tree edit="0" delete="0" create="0" action="action_open_document" type="object">
<field name="res_id" column_invisible="True"/>
<field name="date"/>
<field name="author_id" widget="many2one_avatar"/>
<field name="account_audit_log_move_id"/>
<field name="account_audit_log_preview"/>
</tree>
</field>
</record>
<record id="view_message_tree_audit_log_search" model="ir.ui.view">
<field name="name">mail.message.search</field>
<field name="model">mail.message</field>
<field name="priority">99</field>
<field name="mode">primary</field>
<field name="arch" type="xml">
<search string="Messages Search">
<field name="account_audit_log_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'}"/>
<filter string="Journal Entry" name="group_by_log_move_id" domain="[]" context="{'group_by': 'res_id'}"/>
</group>
</search>
</field>
</record>
<record id="action_account_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="help" type="html">
<p class="o_view_nocontent_smiling_face">
To see the audit log you have to enable the audit trail option from settings
</p>
</field>
<field name="domain">[
('model', '=', 'account.move'),
('message_type', '=', 'notification'),
('account_audit_log_move_id', '!=', False),
('show_audit_log', '=', True),
]</field>
<field name="search_view_id" ref="view_message_tree_audit_log_search"/>
</record>
<menuitem id="account_audit_trail_menu" name="Audit Trail" action="action_account_audit_trail_report" parent="account.account_reports_management_menu"/>
</odoo>
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="res_config_settings_view_form_inherit_account_audit_trail" model="ir.ui.view">
<field name="name">res.config.settings.form.inherit.account.audit.trail</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="account.res_config_settings_view_form"/>
<field name="arch" type="xml">
<app name="account" position="inside">
<block title="Audit Trail" name="audit_trail_setting_container">
<setting string="Audit Trail" company_dependent="1" help="Activate Audit Trail">
<field name="check_account_audit_trail"/>
<button name="%(account_audit_trail.action_account_audit_trail_report)d"
type="action"
string="Go to Audit Trail"
class="oe_highlight"
invisible="check_account_audit_trail == False"/>
</setting>
</block>
</app>
</field>
</record>
</odoo>