vendor_payment_diff_amount/models/account_payment.py
2025-11-19 17:05:58 +07:00

159 lines
7.4 KiB
Python

# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
class AccountPayment(models.Model):
_inherit = 'account.payment'
# Flag to prevent infinite recursion during synchronization
_skip_amount_sync = False
amount_substract = fields.Monetary(
string='Amount Substract',
currency_field='currency_id',
help='Amount to be deducted from the payment (e.g., withholding tax, fees)',
readonly=False,
)
substract_account_id = fields.Many2one(
'account.account',
string='Substract Account',
domain="[('account_type', 'not in', ['asset_cash', 'asset_cash_bank']), ('deprecated', '=', False), ('company_id', '=', company_id)]",
help='Account where the deduction will be recorded',
readonly=False,
)
final_payment_amount = fields.Monetary(
string='Final Payment Amount',
currency_field='currency_id',
compute='_compute_final_payment_amount',
store=True,
help='Actual amount to be paid after deductions',
)
@api.depends('amount', 'amount_substract', 'currency_id')
def _compute_final_payment_amount(self):
for payment in self:
amount_substract = payment.amount_substract or 0.0
currency = payment.currency_id or payment.company_id.currency_id
payment.final_payment_amount = currency.round(payment.amount - amount_substract)
@api.constrains('amount', 'amount_substract')
def _check_amount_substract(self):
for payment in self:
if payment.amount_substract and payment.amount_substract < 0:
raise ValidationError(_("Amount Substract cannot be negative."))
if payment.amount_substract and payment.amount_substract > payment.amount:
raise ValidationError(_("Amount Substract cannot be greater than the payment amount."))
@api.constrains('amount_substract', 'substract_account_id')
def _check_substract_account(self):
for payment in self:
if payment.amount_substract > 0 and not payment.substract_account_id:
raise ValidationError(_("Please select a Substract Account when Amount Substract is specified."))
def _synchronize_from_moves(self, changed_fields):
"""
Override to prevent amount synchronization when we have a substract 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).
"""
# If we have a substract amount, we need to handle the sync differently
if self.amount_substract and self.amount_substract > 0:
# Store the original amount before sync
original_amount = self.amount
original_substract = self.amount_substract
# Call parent sync
result = super()._synchronize_from_moves(changed_fields)
# Restore the original amount if it was changed by sync
if self.amount != original_amount:
# Use write to update without triggering another sync
super(AccountPayment, self).write({
'amount': original_amount,
'amount_substract': original_substract,
})
# Force recomputation of final_payment_amount
self._compute_final_payment_amount()
return result
else:
return super()._synchronize_from_moves(changed_fields)
def _prepare_move_line_default_vals(self, write_off_line_vals=None, force_balance=None):
"""
Override to add substract account line when amount_substract > 0.
This method modifies the journal entry to:
1. Keep the payable debit line at the original amount
2. Add a new credit line for the substract account (reduction)
3. Reduce the bank credit line to final_payment_amount
The resulting entry for outbound payment (amount=1000, substract=100):
- Payable: debit 1000 (original amount)
- Substract: credit 100 (amount_substract - reduction)
- Bank: credit 900 (final_payment_amount)
Total: debit 1000 = credit 1000 (balanced)
Requirements: 4.1, 4.2, 4.3, 4.4, 4.5
"""
# Get standard line values from parent
line_vals_list = super()._prepare_move_line_default_vals(write_off_line_vals, force_balance)
# Only modify if we have a deduction amount and account
if self.amount_substract and self.amount_substract > 0 and self.substract_account_id:
# For outbound payments, we need to:
# - Keep the payable debit (counterpart line) at the original amount
# - Add a credit line for the substract account (reduction)
# - Reduce the bank credit (liquidity line) to final_payment_amount
if self.payment_type == 'outbound':
# Check if substract line already exists (to prevent duplicates)
has_substract_line = any(
line.get('account_id') == self.substract_account_id.id
for line in line_vals_list
)
if not has_substract_line:
# The liquidity line is the first line (index 0) - this is the bank account
# The counterpart line is the second line (index 1) - this is the payable account
liquidity_line = line_vals_list[0]
# Convert amount_substract to company currency for the journal entry
substract_balance = self.currency_id._convert(
self.amount_substract,
self.company_id.currency_id,
self.company_id,
self.date,
)
# Adjust the liquidity (bank) line - reduce the credit by amount_substract
# For outbound payment:
# - Original: amount_currency = -amount, credit = amount
# - Modified: amount_currency = -final_payment_amount, credit = final_payment_amount
liquidity_line['amount_currency'] += self.amount_substract # Reduce the negative amount (make it less negative)
liquidity_line['credit'] -= substract_balance # Reduce the credit
# Create the substract account line (credit - reduction)
substract_line_name = _('Payment Deduction: %s') % self.substract_account_id.name
substract_line = {
'name': substract_line_name,
'date_maturity': self.date,
'amount_currency': -self.amount_substract, # Negative because it's a credit
'currency_id': self.currency_id.id,
'debit': 0.0,
'credit': substract_balance,
'partner_id': self.partner_id.id,
'account_id': self.substract_account_id.id,
}
# Add the substract line to the list
line_vals_list.append(substract_line)
return line_vals_list