From 07125cdc43a46d368ca447e4c2e2f502e33efac7 Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Fri, 29 May 2026 11:34:46 +0700 Subject: [PATCH] fix: resolve AccessError in POS session message posting by using sudo for partner access and message notification --- models/pos_session.py | 82 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 10 deletions(-) diff --git a/models/pos_session.py b/models/pos_session.py index 24287c8..1dbc7b5 100644 --- a/models/pos_session.py +++ b/models/pos_session.py @@ -1,22 +1,84 @@ -from odoo import models +from odoo import models, _ +from odoo.tools import plaintext2html class PosSession(models.Model): _inherit = 'pos.session' def _get_message_author(self): - """Override to read employee partner with sudo() to avoid res.partner - access errors when the cashier's allowed companies don't include the - company_id set on work_contact_id (caused by hr_multi_company_employee - assigning employees to branch companies). + """Read employee partner with sudo() to avoid res.partner access errors + for cashier users whose allowed companies don't include the partner's company_id. """ if not self.employee_id: return None - - # Use sudo() to bypass multi-company partner rule when reading - # the employee's work_contact_id or user partner for message posting. employee = self.employee_id.sudo() - if related_partners := employee._get_related_partners(): + related_partners = employee._get_related_partners() + if related_partners: return related_partners[0] - return self.sudo().user_id.partner_id + + def _set_opening_control_data(self, cashbox_value: int, notes: str): + """Override pos_hr's _set_opening_control_data to use sudo for message_post. + + pos_hr does: + super()._set_opening_control_data(...) + if author_id := self._get_message_author(): + self.message_post(body=..., author_id=author_id.id) + + The problem: message_post internally reads the author res.partner in the + cashier's restricted context, causing an AccessError. + + Fix: we call the SAME logic but with self.sudo().message_post(). + """ + # Step 1: call the pos_hr super chain but stop before message_post. + # We do this by walking MRO to find the base pos.session method + # (skipping pos_hr's override) and calling it directly. + base_method = None + found_pos_hr = False + for cls in type(self).__mro__: + if found_pos_hr: + if '_set_opening_control_data' in cls.__dict__: + base_method = cls._set_opening_control_data + break + elif (cls.__name__ == 'PosSession' + and getattr(cls, '__module__', '').endswith('pos_hr.models.pos_session')): + found_pos_hr = True + + if base_method: + base_method(self, cashbox_value, notes) + else: + # pos_hr not in MRO or already at base — call normal super + super()._set_opening_control_data(cashbox_value, notes) + + # Step 2: re-implement pos_hr's message posting with sudo + author = self._get_message_author() + if author: + self.sudo().message_post( + body=plaintext2html(_('Opened register')), + author_id=author.id, + ) + + def post_close_register_message(self): + """Override pos_hr's post_close_register_message to use sudo for message_post.""" + author = self._get_message_author() + if author: + self.sudo().message_post( + body=plaintext2html(_('Closed Register')), + author_id=author.id, + ) + else: + # No employee — call the base pos.session fallback + base_method = None + found_pos_hr = False + for cls in type(self).__mro__: + if found_pos_hr: + if 'post_close_register_message' in cls.__dict__: + base_method = cls.post_close_register_message + break + elif (cls.__name__ == 'PosSession' + and getattr(cls, '__module__', '').endswith('pos_hr.models.pos_session')): + found_pos_hr = True + if base_method: + base_method(self) + else: + super().post_close_register_message()