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
for payment in payments:
if payment.move_id:
# Find the counterpart line (payable/expense line with debit)
counterpart_lines = payment.move_id.line_ids.filtered(
lambda l: l.debit > 0 and l.account_id.account_type in ('liability_payable', 'expense')
)
# Robust logic to find gross amount from moves
liquidity_lines, counterpart_lines, writeoff_lines = payment._seek_for_lines()
non_liquidity_lines = counterpart_lines + writeoff_lines
if counterpart_lines:
correct_amount = counterpart_lines[0].debit
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:
correct_amount = sum(abs(l.amount_currency) for l in gross_lines)
current_amount = payment.amount
# 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" Current amount: {current_amount}")
print(f" Correct amount: {correct_amount}")
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
env.cr.execute(

View File

@ -100,13 +100,18 @@ class AccountPayment(models.Model):
if payment.amount_substract and payment.amount_substract > 0:
# Get the correct amount from the journal entry
if payment.move_id:
# Find the counterpart line (payable/expense line)
counterpart_lines = payment.move_id.line_ids.filtered(
lambda l: l.account_id.account_type in ('liability_payable', 'expense') and l.debit > 0
)
if counterpart_lines:
correct_amount = counterpart_lines[0].debit
if abs(payment.amount - correct_amount) > 0.01: # Allow for rounding differences
liquidity_lines, counterpart_lines, writeoff_lines = payment._seek_for_lines()
non_liquidity_lines = counterpart_lines + writeoff_lines
# Find gross amount lines (Debits for outbound, Credits for inbound)
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:
correct_amount = sum(abs(l.amount_currency) for l in gross_lines)
if abs(payment.amount - correct_amount) > 0.001:
import logging
_logger = logging.getLogger(__name__)
_logger.info(f"Fixing amount for payment {payment.id}: {payment.amount} -> {correct_amount}")
@ -133,14 +138,19 @@ class AccountPayment(models.Model):
fixed_count = 0
for payment in payments:
if payment.move_id:
# Find the counterpart line (payable/expense line with debit)
counterpart_lines = payment.move_id.line_ids.filtered(
lambda l: l.debit > 0 and l.account_id.account_type in ('liability_payable', 'expense')
)
# Robust logic to find gross amount from moves
liquidity_lines, counterpart_lines, writeoff_lines = payment._seek_for_lines()
non_liquidity_lines = counterpart_lines + writeoff_lines
if counterpart_lines:
correct_amount = counterpart_lines[0].debit
if abs(payment.amount - correct_amount) > 0.01:
# Find gross amount lines (Debits for outbound, Credits for inbound)
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:
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
payment.env.cr.execute(
"UPDATE account_payment SET amount = %s WHERE id = %s",
@ -165,36 +175,49 @@ class AccountPayment(models.Model):
Override to handle synchronization when we have deductions.
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 payment in self:
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_substract = payment.amount_substract
# Try to call parent sync but handle any errors
# Call parent sync
try:
super(AccountPayment, payment)._synchronize_from_moves(changed_fields)
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
_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
# The sync might have changed it based on journal entry lines
if payment.amount != original_amount:
# Restore gross amount from the move's counterpart lines
if payment.move_id:
liquidity_lines, counterpart_lines, writeoff_lines = payment._seek_for_lines()
# The gross amount is the sum of counterpart/writeoff lines that balance the liquidity line
# For outbound (Send Money): Gross = Sum of Debits (excluding liquidity)
# For inbound (Receive Money): Gross = Sum of Credits (excluding liquidity)
non_liquidity_lines = counterpart_lines + writeoff_lines
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 amount for payment {payment.id}: {payment.amount} -> {original_amount}")
_logger.info(f"Restoring gross amount for payment {payment.id}: {payment.amount} -> {correct_gross_amount}")
# Use SQL to restore the original amount without triggering more syncs
# Use SQL to restore to avoid triggering more syncs
payment.env.cr.execute(
"UPDATE account_payment SET amount = %s WHERE id = %s",
(original_amount, payment.id)
(correct_gross_amount, payment.id)
)
payment.invalidate_recordset(['amount'])

View File

@ -58,7 +58,7 @@ class PaymentDeductionLine(models.Model):
'account.account',
string='Deduction Account',
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)',
)
name = fields.Char(

View File

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

View File

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

View File

@ -28,12 +28,17 @@ class PaymentAmountFixWizard(models.TransientModel):
payments_to_fix = []
for payment in payments:
if payment.move_id:
counterpart_lines = payment.move_id.line_ids.filtered(
lambda l: l.debit > 0 and l.account_id.account_type in ('liability_payable', 'expense')
)
if counterpart_lines:
correct_amount = counterpart_lines[0].debit
if abs(payment.amount - correct_amount) > 0.01:
liquidity_lines, counterpart_lines, writeoff_lines = payment._seek_for_lines()
non_liquidity_lines = counterpart_lines + writeoff_lines
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:
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)
res['payment_ids'] = [(6, 0, payments_to_fix)]
@ -45,13 +50,17 @@ class PaymentAmountFixWizard(models.TransientModel):
for payment in self.payment_ids:
if payment.move_id:
counterpart_lines = payment.move_id.line_ids.filtered(
lambda l: l.debit > 0 and l.account_id.account_type in ('liability_payable', 'expense')
)
liquidity_lines, counterpart_lines, writeoff_lines = payment._seek_for_lines()
non_liquidity_lines = counterpart_lines + writeoff_lines
if counterpart_lines:
correct_amount = counterpart_lines[0].debit
if abs(payment.amount - correct_amount) > 0.01:
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:
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
payment.env.cr.execute(
"UPDATE account_payment SET amount = %s WHERE id = %s",