fix error sync when account types Advance and Expense is used

This commit is contained in:
Suherdy Yacob 2026-01-23 11:12:18 +07:00
parent 82d45bf902
commit 69f874b38c
27 changed files with 67 additions and 42 deletions

0
.gitignore vendored Normal file → Executable file
View File

0
AMOUNT_FIX_GUIDE.md Normal file → Executable file
View File

0
CHANGELOG.md Normal file → Executable file
View File

0
README.md Normal file → Executable file
View File

0
__init__.py Normal file → Executable file
View File

0
__manifest__.py Normal file → Executable file
View File

0
__pycache__/__init__.cpython-310.pyc Normal file → Executable file
View File

20
fix_amount_issue.py Normal file → Executable file
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(

0
models/__init__.py Normal file → Executable file
View File

0
models/__pycache__/__init__.cpython-310.pyc Normal file → Executable file
View File

View File

0
models/__pycache__/account_payment.cpython-310.pyc Normal file → Executable file
View File

0
models/account_batch_payment.py Normal file → Executable file
View File

89
models/account_payment.py Normal file → Executable file
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()

0
models/payment_deduction_line.py Normal file → Executable file
View File

0
security/ir.model.access.csv Normal file → Executable file
View File

0
tests/__init__.py Normal file → Executable file
View File

0
tests/__pycache__/__init__.cpython-310.pyc Normal file → Executable file
View File

0
tests/__pycache__/test_account_payment.cpython-310.pyc Normal file → Executable file
View File

View File

0
tests/test_account_payment.py Normal file → Executable file
View File

0
tests/test_batch_payment_integration.py Normal file → Executable file
View File

0
views/account_batch_payment_views.xml Normal file → Executable file
View File

0
views/account_payment_views.xml Normal file → Executable file
View File

0
wizard/__init__.py Normal file → Executable file
View File

0
wizard/payment_amount_fix_wizard.py Normal file → Executable file
View File

0
wizard/payment_amount_fix_wizard_views.xml Normal file → Executable file
View File