diff --git a/README.md b/README.md index c54fb8e..64329cf 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,17 @@ This module allows restricting users to specific journals in Odoo's Accounting a - **Access Control:** - If "Allowed Journals" is **empty**, the user has access to **ALL** journals (standard behavior). - If "Allowed Journals" is **populated**, the user can **ONLY** access the selected journals. -- Implements a global Record Rule to enforce this restriction across the system (Views, Search, Create, Write). +- Implements an ORM-level `_search`, `write`, and `unlink` override on `account.journal` to safely and recursively filter journals without crashing on standard views. +- **Context-Aware Bypass:** Automatically detects and bypasses restrictions during critical Point of Sale (POS) operations and administrative automated/clearing background contexts. ## Configuration 1. Go to **Settings** > **Users & Companies** > **Users**. 2. Open the user you want to restrict. -3. Go to the **Allowed Journals** tab. -4. Add the journals the user is allowed to access. -5. Save. +3. In the **Allowed Journals** field, select the journals the user is allowed to access. +4. Save. + +## Author +Suherdy Yacob ## License LGPL-3 diff --git a/__manifest__.py b/__manifest__.py index f55e07a..171f67d 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -9,10 +9,9 @@ - If "Allowed Journals" is set, the user can only access those journals. - If "Allowed Journals" is empty, the user can access all journals. """, - 'author': 'Antigravity', + 'author': 'Suherdy Yacob', 'depends': ['base', 'account'], 'data': [ - 'security/account_security.xml', 'views/res_users_views.xml', ], 'installable': True, diff --git a/models/__init__.py b/models/__init__.py index 8835165..65265a7 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1 +1,2 @@ from . import res_users +from . import account_journal diff --git a/models/account_journal.py b/models/account_journal.py new file mode 100644 index 0000000..b29e275 --- /dev/null +++ b/models/account_journal.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +from odoo import api, fields, models, _ +from odoo.exceptions import UserError + +class AccountJournal(models.Model): + _inherit = 'account.journal' + + @api.model + def _search(self, domain, offset=0, limit=None, order=None, **kwargs): + """ + Override _search to filter journals based on the allowed_journal_ids configuration on res.users. + This provides a secure, recursive-safe, and context-aware filtering mechanism. + """ + user = self.env.user + + # Determine if we should bypass the journal visibility restrictions: + # 1. Superuser / system context (env.su) is always bypassed. + # 2. Skip if user has no allowed journals configured (empty means access to all). + # 3. Explicit bypass requested in the context (bypass_allowed_journal). + # 4. Point of Sale contexts: + # - pos_session_id or pos_config_id is present. + # - pos_last_server_date is present (POS frontend loading data). + # 5. During standard module installations/upgrades (install_mode). + if not self.env.su and user.sudo().allowed_journal_ids: + bypass = ( + self.env.context.get('bypass_allowed_journal') or + self.env.context.get('pos_session_id') or + self.env.context.get('pos_config_id') or + 'pos_last_server_date' in self.env.context or + self.env.context.get('install_mode') + ) + if not bypass: + allowed_ids = user.sudo().allowed_journal_ids.ids + domain = [('id', 'in', allowed_ids)] + list(domain) + + return super(AccountJournal, self)._search(domain, offset=offset, limit=limit, order=order, **kwargs) + + def write(self, vals): + """ + Restrict write access to allowed journals only. + """ + if not self.env.su: + user = self.env.user + if user.sudo().allowed_journal_ids: + bypass = ( + self.env.context.get('bypass_allowed_journal') or + self.env.context.get('pos_session_id') or + self.env.context.get('pos_config_id') + ) + if not bypass: + allowed_ids = user.sudo().allowed_journal_ids.ids + unallowed = self.filtered(lambda j: j.id not in allowed_ids) + if unallowed: + raise UserError(_("You are not allowed to modify the following journal(s): %s") % ", ".join(unallowed.mapped('name'))) + return super(AccountJournal, self).write(vals) + + def unlink(self): + """ + Restrict delete access to allowed journals only. + """ + if not self.env.su: + user = self.env.user + if user.sudo().allowed_journal_ids: + bypass = ( + self.env.context.get('bypass_allowed_journal') or + self.env.context.get('pos_session_id') or + self.env.context.get('pos_config_id') + ) + if not bypass: + allowed_ids = user.sudo().allowed_journal_ids.ids + unallowed = self.filtered(lambda j: j.id not in allowed_ids) + if unallowed: + raise UserError(_("You are not allowed to delete the following journal(s): %s") % ", ".join(unallowed.mapped('name'))) + return super(AccountJournal, self).unlink() diff --git a/security/account_security.xml b/security/account_security.xml deleted file mode 100644 index fb85d14..0000000 --- a/security/account_security.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - Journal Multi-Company with Allowed Journals - - [(1, '=', 1)] if not user.allowed_journal_ids else [('id', 'in', user.allowed_journal_ids.ids)] - - -