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