first commit

This commit is contained in:
Suherdy Yacob 2026-03-17 10:42:33 +07:00
commit dcf02c0331
7 changed files with 123 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
*.pyc
*~
__pycache__/
.vscode/
.idea/
*.swp

34
README.md Normal file
View File

@ -0,0 +1,34 @@
# Accounting Audit Unlink Tracker
This Odoo module tracks deletion operations for critical business records and ensures they are recorded in the **Accounting Audit Trail** report.
## Features
- **Deletion Tracking**: Automatically intercepts `unlink` calls for major models.
- **Persistent Logs**: Creates audit logs with `res_id = 0` so history survives record deletion.
- **Detailed Information**: Logs capture:
- User responsible for deletion.
- Record Technical ID.
- Record Display Name.
- Model Name.
- **Audit Trail Integration**: Logs appear directly in the standard **Accounting > Reporting > Audit Trail** report.
## Supported Models
- **Accounting**: `account.move`, `account.account`, `account.tax`
- **Core/Partners**: `res.partner`, `res.company`
- **Manufacturing**: `mrp.production`, `mrp.bom`
- **Inventory/Logistics**: `stock.picking`, `stock.move`, `stock.lot`
- **Products**: `product.template`, `product.product`, `product.category`
- **Sales/Purchase**: `sale.order`, `purchase.order`
- **HR**: `hr.employee`
## Installation
1. Add the module to your custom addons path.
2. Update the addon list in Odoo.
3. Install `account_audit_unlink`.
## Usage
Go to **Accounting > Reporting > Audit Reports > Audit Trail** to view all deletion logs alongside standard audit trails.

2
__init__.py Normal file
View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import models

20
__manifest__.py Normal file
View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
{
'name': 'Accounting Audit Unlink Tracker',
'version': '1.0',
'summary': 'Track unlink operations and record them to accounting audit log',
'description': """
This module tracks the deletion of records in the accounting system
(Account Moves, Accounts, Taxes, Partners, Companies) and creates
a persistent log in the Accounting Audit Trail.
""",
'author': 'Antigravity',
'category': 'Accounting',
'depends': ['account', 'mrp', 'stock', 'purchase', 'sale', 'hr'],
'data': [
'views/mail_message_views.xml',
],
'installable': True,
'auto_install': False,
'license': 'LGPL-3',
}

2
models/__init__.py Normal file
View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import mail_thread

46
models/mail_thread.py Normal file
View File

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
from odoo import api, models, _
class MailThread(models.AbstractModel):
_inherit = 'mail.thread'
def unlink(self):
""" Override unlink to create a persistent audit log record before deletion.
The audit log will have res_id=0 to ensure it survives the standard unlink process.
"""
audit_models = (
'account.move', 'account.account', 'account.tax', 'res.partner', 'res.company',
'mrp.production', 'mrp.bom', 'stock.picking', 'stock.move', 'product.template', 'product.product',
'purchase.order', 'sale.order', 'hr.employee', 'product.category', 'stock.lot'
)
if self._name in audit_models:
for record in self:
# Get basic info while record still exists
record_id = record.id
record_name = record.display_name or _('Unknown Record')
company_id = getattr(record, 'company_id', self.env.company).id
# Create the persistent audit log
self.env['mail.message'].sudo().create({
'model': self._name,
'res_id': 0, # Persistent ID (not linked to an existing record)
'message_type': 'notification',
'subject': _('Record Unlinked: %s', record_name),
'body': _(
'<p>The following record was unlinked by <b>%s</b>:</p>'
'<ul>'
'<li><b>Model:</b> %s</li>'
'<li><b>Technical ID:</b> %s</li>'
'<li><b>Display Name:</b> %s</li>'
'</ul>',
self.env.user.name,
self._name,
record_id,
record_name
),
'author_id': self.env.user.partner_id.id,
'record_company_id': company_id,
})
return super(MailThread, self).unlink()

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="account.action_account_audit_trail_report" model="ir.actions.act_window">
<field name="domain">[
('message_type', '=', 'notification'),
('model', 'in', (
'account.move', 'account.account', 'account.tax', 'res.partner', 'res.company',
'mrp.production', 'mrp.bom', 'stock.picking', 'stock.move', 'product.template', 'product.product',
'purchase.order', 'sale.order', 'hr.employee', 'product.category', 'stock.lot'
)),
]</field>
</record>
</odoo>