fix calculation when using advance account

This commit is contained in:
Suherdy Yacob 2026-01-23 13:03:23 +07:00
parent 43b6ab10db
commit 7e2c132f1b
6 changed files with 94 additions and 58 deletions

View File

@ -24,23 +24,25 @@ def fix_payment_amounts():
fixed_count = 0 fixed_count = 0
for payment in payments: for payment in payments:
if payment.move_id: if payment.move_id:
# Find the counterpart line (payable/expense line with debit) # Robust logic to find gross amount from moves
counterpart_lines = payment.move_id.line_ids.filtered( liquidity_lines, counterpart_lines, writeoff_lines = payment._seek_for_lines()
lambda l: l.debit > 0 and l.account_id.account_type in ('liability_payable', 'expense') non_liquidity_lines = counterpart_lines + writeoff_lines
)
if counterpart_lines: if payment.payment_type == 'outbound':
correct_amount = counterpart_lines[0].debit gross_lines = non_liquidity_lines.filtered(lambda l: l.debit > 0)
else:
gross_lines = non_liquidity_lines.filtered(lambda l: l.credit > 0)
if gross_lines:
correct_amount = sum(abs(l.amount_currency) for l in gross_lines)
current_amount = payment.amount current_amount = payment.amount
# Check if amount needs fixing (allow for small rounding differences) # Check if amount needs fixing (allow for small rounding differences)
if abs(current_amount - correct_amount) > 0.01: if abs(current_amount - correct_amount) > 0.001:
print(f"Payment {payment.name} (ID: {payment.id}):") print(f"Payment {payment.name} (ID: {payment.id}):")
print(f" Current amount: {current_amount}") print(f" Current amount: {current_amount}")
print(f" Correct amount: {correct_amount}") print(f" Correct amount: {correct_amount}")
print(f" Deductions: {payment.amount_substract}") print(f" Deductions: {payment.amount_substract}")
print(f" Current final: {payment.final_payment_amount}")
print(f" Expected final: {correct_amount - payment.amount_substract}")
# Fix the amount using SQL to avoid triggering computed fields # Fix the amount using SQL to avoid triggering computed fields
env.cr.execute( env.cr.execute(

View File

@ -100,13 +100,18 @@ class AccountPayment(models.Model):
if payment.amount_substract and payment.amount_substract > 0: if payment.amount_substract and payment.amount_substract > 0:
# Get the correct amount from the journal entry # Get the correct amount from the journal entry
if payment.move_id: if payment.move_id:
# Find the counterpart line (payable/expense line) liquidity_lines, counterpart_lines, writeoff_lines = payment._seek_for_lines()
counterpart_lines = payment.move_id.line_ids.filtered( non_liquidity_lines = counterpart_lines + writeoff_lines
lambda l: l.account_id.account_type in ('liability_payable', 'expense') and l.debit > 0
) # Find gross amount lines (Debits for outbound, Credits for inbound)
if counterpart_lines: if payment.payment_type == 'outbound':
correct_amount = counterpart_lines[0].debit gross_lines = non_liquidity_lines.filtered(lambda l: l.debit > 0)
if abs(payment.amount - correct_amount) > 0.01: # Allow for rounding differences else:
gross_lines = non_liquidity_lines.filtered(lambda l: l.credit > 0)
if gross_lines:
correct_amount = sum(abs(l.amount_currency) for l in gross_lines)
if abs(payment.amount - correct_amount) > 0.001:
import logging import logging
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
_logger.info(f"Fixing amount for payment {payment.id}: {payment.amount} -> {correct_amount}") _logger.info(f"Fixing amount for payment {payment.id}: {payment.amount} -> {correct_amount}")
@ -133,14 +138,19 @@ class AccountPayment(models.Model):
fixed_count = 0 fixed_count = 0
for payment in payments: for payment in payments:
if payment.move_id: if payment.move_id:
# Find the counterpart line (payable/expense line with debit) # Robust logic to find gross amount from moves
counterpart_lines = payment.move_id.line_ids.filtered( liquidity_lines, counterpart_lines, writeoff_lines = payment._seek_for_lines()
lambda l: l.debit > 0 and l.account_id.account_type in ('liability_payable', 'expense') non_liquidity_lines = counterpart_lines + writeoff_lines
)
if counterpart_lines: # Find gross amount lines (Debits for outbound, Credits for inbound)
correct_amount = counterpart_lines[0].debit if payment.payment_type == 'outbound':
if abs(payment.amount - correct_amount) > 0.01: gross_lines = non_liquidity_lines.filtered(lambda l: l.debit > 0)
else:
gross_lines = non_liquidity_lines.filtered(lambda l: l.credit > 0)
if gross_lines:
correct_amount = sum(abs(l.amount_currency) for l in gross_lines)
if abs(payment.amount - correct_amount) > 0.001:
# Fix using SQL to avoid sync issues # Fix using SQL to avoid sync issues
payment.env.cr.execute( payment.env.cr.execute(
"UPDATE account_payment SET amount = %s WHERE id = %s", "UPDATE account_payment SET amount = %s WHERE id = %s",
@ -165,38 +175,51 @@ class AccountPayment(models.Model):
Override to handle synchronization when we have deductions. Override to handle synchronization when we have deductions.
When we have a substract amount, the bank credit line is reduced to final_payment_amount, When we have a substract amount, the bank credit line is reduced to final_payment_amount,
but we want to keep the payment amount at the original value (not sync it down). but we want to keep the payment amount at the original gross value.
""" """
# For payments with deductions, we need to handle synchronization carefully # For payments with deductions, we need to handle synchronization carefully
for payment in self: for payment in self:
if payment.amount_substract and payment.amount_substract > 0: if payment.amount_substract and payment.amount_substract > 0:
# Store the original amount before any synchronization # Store potential original values
original_amount = payment.amount original_amount = payment.amount
original_substract = payment.amount_substract
# Try to call parent sync but handle any errors # Call parent sync
try: try:
super(AccountPayment, payment)._synchronize_from_moves(changed_fields) super(AccountPayment, payment)._synchronize_from_moves(changed_fields)
except Exception as e: except Exception as e:
# If there's an error (like missing payable account when using expense_account_id),
# that's expected, so we just continue
import logging import logging
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
_logger.info(f"Sync error for payment {payment.id} (expected with deductions): {e}") _logger.debug(f"Sync info for payment {payment.id} (handling deductions): {e}")
# After sync, ensure the amount is still correct # Restore gross amount from the move's counterpart lines
# The sync might have changed it based on journal entry lines if payment.move_id:
if payment.amount != original_amount: liquidity_lines, counterpart_lines, writeoff_lines = payment._seek_for_lines()
import logging
_logger = logging.getLogger(__name__)
_logger.info(f"Restoring amount for payment {payment.id}: {payment.amount} -> {original_amount}")
# Use SQL to restore the original amount without triggering more syncs # The gross amount is the sum of counterpart/writeoff lines that balance the liquidity line
payment.env.cr.execute( # For outbound (Send Money): Gross = Sum of Debits (excluding liquidity)
"UPDATE account_payment SET amount = %s WHERE id = %s", # For inbound (Receive Money): Gross = Sum of Credits (excluding liquidity)
(original_amount, payment.id)
) non_liquidity_lines = counterpart_lines + writeoff_lines
payment.invalidate_recordset(['amount']) if payment.payment_type == 'outbound':
gross_lines = non_liquidity_lines.filtered(lambda l: l.debit > 0)
else:
gross_lines = non_liquidity_lines.filtered(lambda l: l.credit > 0)
if gross_lines:
# Use amount_currency because it represents the amount in payment currency
correct_gross_amount = sum(abs(l.amount_currency) for l in gross_lines)
if abs(payment.amount - correct_gross_amount) > 0.001:
import logging
_logger = logging.getLogger(__name__)
_logger.info(f"Restoring gross amount for payment {payment.id}: {payment.amount} -> {correct_gross_amount}")
# Use SQL to restore to avoid triggering more syncs
payment.env.cr.execute(
"UPDATE account_payment SET amount = %s WHERE id = %s",
(correct_gross_amount, payment.id)
)
payment.invalidate_recordset(['amount'])
# Ensure final_payment_amount is recalculated correctly # Ensure final_payment_amount is recalculated correctly
payment._compute_final_payment_amount() payment._compute_final_payment_amount()

View File

@ -58,7 +58,7 @@ class PaymentDeductionLine(models.Model):
'account.account', 'account.account',
string='Deduction Account', string='Deduction Account',
required=True, required=True,
domain="[('account_type', 'not in', ['asset_cash', 'asset_cash_bank', 'asset_receivable', 'liability_payable'])]", domain="[('account_type', 'not in', ['asset_cash', 'asset_cash_bank', 'asset_receivable', 'liability_payable']), ('active', '=', True)]",
help='Account where the deduction will be recorded (use tax payable or expense accounts, NOT payable/receivable accounts)', help='Account where the deduction will be recorded (use tax payable or expense accounts, NOT payable/receivable accounts)',
) )
name = fields.Char( name = fields.Char(

View File

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from odoo import fields from odoo import fields
from odoo.tests import TransactionCase from odoo.tests import TransactionCase, tagged
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from hypothesis import given, strategies as st, settings from hypothesis import given, strategies as st, settings
@tagged('post_install', '-at_install')
class TestAccountPayment(TransactionCase): class TestAccountPayment(TransactionCase):
"""Test cases for vendor payment deduction functionality""" """Test cases for vendor payment deduction functionality"""
@ -37,7 +38,7 @@ class TestAccountPayment(TransactionCase):
'name': 'Withholding Tax Account', 'name': 'Withholding Tax Account',
'code': 'WHT001', 'code': 'WHT001',
'account_type': 'expense', 'account_type': 'expense',
'company_id': self.env.company.id, 'company_ids': [self.env.company.id],
}) })
@given( @given(

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from odoo import fields from odoo import fields
from odoo.tests import TransactionCase from odoo.tests import TransactionCase, tagged
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
@tagged('post_install', '-at_install')
class TestBatchPaymentIntegration(TransactionCase): class TestBatchPaymentIntegration(TransactionCase):
"""Test cases for batch payment integration with deduction functionality""" """Test cases for batch payment integration with deduction functionality"""

View File

@ -28,12 +28,17 @@ class PaymentAmountFixWizard(models.TransientModel):
payments_to_fix = [] payments_to_fix = []
for payment in payments: for payment in payments:
if payment.move_id: if payment.move_id:
counterpart_lines = payment.move_id.line_ids.filtered( liquidity_lines, counterpart_lines, writeoff_lines = payment._seek_for_lines()
lambda l: l.debit > 0 and l.account_id.account_type in ('liability_payable', 'expense') non_liquidity_lines = counterpart_lines + writeoff_lines
)
if counterpart_lines: if payment.payment_type == 'outbound':
correct_amount = counterpart_lines[0].debit gross_lines = non_liquidity_lines.filtered(lambda l: l.debit > 0)
if abs(payment.amount - correct_amount) > 0.01: else:
gross_lines = non_liquidity_lines.filtered(lambda l: l.credit > 0)
if gross_lines:
correct_amount = sum(abs(l.amount_currency) for l in gross_lines)
if abs(payment.amount - correct_amount) > 0.001:
payments_to_fix.append(payment.id) payments_to_fix.append(payment.id)
res['payment_ids'] = [(6, 0, payments_to_fix)] res['payment_ids'] = [(6, 0, payments_to_fix)]
@ -45,13 +50,17 @@ class PaymentAmountFixWizard(models.TransientModel):
for payment in self.payment_ids: for payment in self.payment_ids:
if payment.move_id: if payment.move_id:
counterpart_lines = payment.move_id.line_ids.filtered( liquidity_lines, counterpart_lines, writeoff_lines = payment._seek_for_lines()
lambda l: l.debit > 0 and l.account_id.account_type in ('liability_payable', 'expense') non_liquidity_lines = counterpart_lines + writeoff_lines
)
if counterpart_lines: if payment.payment_type == 'outbound':
correct_amount = counterpart_lines[0].debit gross_lines = non_liquidity_lines.filtered(lambda l: l.debit > 0)
if abs(payment.amount - correct_amount) > 0.01: else:
gross_lines = non_liquidity_lines.filtered(lambda l: l.credit > 0)
if gross_lines:
correct_amount = sum(abs(l.amount_currency) for l in gross_lines)
if abs(payment.amount - correct_amount) > 0.001:
# Fix using SQL to avoid sync issues # Fix using SQL to avoid sync issues
payment.env.cr.execute( payment.env.cr.execute(
"UPDATE account_payment SET amount = %s WHERE id = %s", "UPDATE account_payment SET amount = %s WHERE id = %s",