refactor: simplify expense lock bypass using context flag and direct base method calls

This commit is contained in:
Suherdy Yacob 2026-04-06 12:14:56 +07:00
parent 481b03e73f
commit 4261b6c53c

View File

@ -1,5 +1,8 @@
from odoo import fields, models, api, _ from odoo import fields, models, api, _
from odoo.exceptions import UserError from odoo.exceptions import UserError
import logging
_logger = logging.getLogger(__name__)
class AccountPayment(models.Model): class AccountPayment(models.Model):
_inherit = 'account.payment' _inherit = 'account.payment'
@ -8,46 +11,44 @@ class AccountPayment(models.Model):
def action_post(self): def action_post(self):
""" """
Overriding to bypass hr_expense restriction and ensure deductions are synced. Confirmation bypass. Calls standard post with skip flag.
We temporarily clear the link in the DB to avoid the 'Invalid Operation' error.
""" """
for payment in self: return super(AccountPayment, self.with_context(skip_expense_lock=True)).action_post()
if payment.expense_sheet_id and payment.state == 'draft':
sheet_id = payment.expense_sheet_id.id
# 1. Clear the link in DB and Cache (Skip sync to avoid recursion)
payment.with_context(skip_account_move_synchronization=True).write({'expense_sheet_id': False})
try:
# 2. Force Sync Deductions while 'unlinked'
payment._synchronize_to_moves({'amount', 'deduction_line_ids', 'amount_substract'})
# 3. Call standard post
res = super(AccountPayment, payment).action_post()
finally:
# 4. Restore the link
payment.with_context(skip_account_move_synchronization=True).write({'expense_sheet_id': sheet_id})
else:
super(AccountPayment, payment).action_post()
return True
def _synchronize_to_moves(self, changed_fields): def _synchronize_to_moves(self, changed_fields):
# Trigger deduction sync if needed # Always allow sync if bypass flag is set
if 'deduction_line_ids' in changed_fields or 'amount_substract' in changed_fields: if self._context.get('skip_expense_lock'):
if 'amount' not in changed_fields: # Force the refresh by ensuring 'amount' is in fields
changed_fields = set(changed_fields) | {'amount'} if 'deduction_line_ids' in changed_fields or 'amount_substract' in changed_fields:
if 'amount' not in changed_fields:
# Bypass for manual edits changed_fields = set(changed_fields) | {'amount'}
for payment in self:
if payment.expense_sheet_id and payment.state == 'draft': # SURGICAL BYPASS:
sheet_id = payment.expense_sheet_id.id # We call the base 'account.payment' method directly, skipping the 'hr_expense' override
payment.with_context(skip_account_move_synchronization=True).write({'expense_sheet_id': False}) # that raises the UserError.
try: from odoo.addons.account.models.account_payment import AccountPayment as AccountPaymentBase
return super(AccountPayment, payment)._synchronize_to_moves(changed_fields) return AccountPaymentBase._synchronize_to_moves(self, changed_fields)
finally:
payment.with_context(skip_account_move_synchronization=True).write({'expense_sheet_id': sheet_id})
# Trigger deduction sync for manual edits (write) if not locked
if 'deduction_line_ids' in changed_fields or 'amount_substract' in changed_fields:
if not self.expense_sheet_id:
if 'amount' not in changed_fields:
changed_fields = set(changed_fields) | {'amount'}
return super()._synchronize_to_moves(changed_fields) return super()._synchronize_to_moves(changed_fields)
def _synchronize_from_moves(self, changed_fields):
if self._context.get('skip_expense_lock'):
from odoo.addons.account.models.account_payment import AccountPayment as AccountPaymentBase
return AccountPaymentBase._synchronize_from_moves(self, changed_fields)
return super()._synchronize_from_moves(changed_fields)
def write(self, vals):
if 'deduction_line_ids' in vals or 'amount_substract' in vals:
# Force trigger sync with bypass flag
return super(AccountPayment, self.with_context(skip_expense_lock=True)).write(vals)
return super().write(vals)
def action_cancel(self): def action_cancel(self):
res = super().action_cancel() res = super().action_cancel()
for payment in self: for payment in self: