Warn against creating an overpayment

Closes #14457
This commit is contained in:
Cédric Krier
2025-12-31 15:57:40 +01:00
parent 9473804208
commit 067386378a
4 changed files with 153 additions and 17 deletions
+1
View File
@@ -1,3 +1,4 @@
* Warn against creating an overpayment
Version 7.8.0 - 2025-12-15
--------------------------
+48 -17
View File
@@ -24,7 +24,7 @@ from trytond.transaction import Transaction, check_access, without_check_access
from trytond.wizard import (
Button, StateAction, StateTransition, StateView, Wizard)
from .exceptions import BlockedWarning, GroupWarning
from .exceptions import BlockedWarning, GroupWarning, OverpayWarning
from .payment import KINDS
@@ -412,9 +412,12 @@ class PayLine(Wizard):
def default_start(self, fields):
pool = Pool()
Currency = pool.get('currency.currency')
Lang = pool.get('ir.lang')
Line = pool.get('account.move.line')
Warning = pool.get('res.user.warning')
lang = Lang.get()
reverse = {'receivable': 'payable', 'payable': 'receivable'}
companies = {}
lines = self.records
@@ -442,22 +445,50 @@ class PayLine(Wizard):
('move_state', '=', 'posted'),
])
for party in parties:
party_lines = [l for l in others if l.party == party]
if not party_lines:
continue
lines = [l for l in types[kind]['lines']
if l.party == party]
warning_name = Warning.format(
'%s:%s' % (reverse[kind], party), lines)
if Warning.check(warning_name):
names = ', '.join(l.rec_name for l in lines[:5])
if len(lines) > 5:
names += '...'
raise GroupWarning(warning_name,
gettext('account_payment.msg_pay_line_group',
names=names,
party=party.rec_name,
line=party_lines[0].rec_name))
lines = [
l for l in types[kind]['lines'] if l.party == party]
if party_lines := [l for l in others if l.party == party]:
warning_name = Warning.format(
'%s:%s' % (reverse[kind], party), lines)
if Warning.check(warning_name):
names = ', '.join(l.rec_name for l in lines[:5])
if len(lines) > 5:
names += '...'
raise GroupWarning(warning_name,
gettext('account_payment.msg_pay_line_group',
names=names,
party=party.rec_name,
line=party_lines[0].rec_name))
payment_amount = 0
for line in lines:
payment_amount += Currency.compute(
line.payment_currency, line.payment_amount,
company.currency).copy_sign(
line.debit - line.credit)
payment_amount_sign = payment_amount.as_tuple().sign
amount = getattr(party, kind)
amount_sign = amount.as_tuple().sign
if (abs(payment_amount) > abs(amount)
and (payment_amount_sign == amount_sign
or payment_amount.is_zero()
or amount.is_zero())):
warning_name = Warning.format(
'%s:%s' % (kind, party), lines)
if Warning.check(warning_name):
names = ', '.join(
l.rec_name for l in lines[:5])
if len(lines) > 5:
names += '...'
raise OverpayWarning(warning_name,
gettext('account_payment.msg_pay_line_overpay',
names=names,
party=party.rec_name,
payment_amount=lang.currency(
payment_amount, company.currency),
amount=lang.currency(
amount, company.currency)))
return {}
def _get_journals(self):
+3
View File
@@ -24,6 +24,9 @@ this repository contains the full copyright notices and license terms. -->
<record model="ir.message" id="msg_pay_line_group">
<field name="text">The lines "%(names)s" for %(party)s could be grouped with the line "%(line)s".</field>
</record>
<record model="ir.message" id="msg_pay_line_overpay">
<field name="text">The payment of %(payment_amount)s for lines "%(names)s" exceeds the amount for %(party)s, which is %(amount)s.</field>
</record>
<record model="ir.message" id="msg_move_cancel_payments">
<field name="text">The moves "%(moves)s" contain lines with payments, you may want to cancel them before cancelling.</field>
</record>
@@ -0,0 +1,101 @@
================================
Account Payment Overpay Scenario
================================
Imports::
>>> import datetime as dt
>>> from decimal import Decimal
>>> from proteus import Model, Wizard
>>> from trytond.modules.account.tests.tools import (
... create_chart, create_fiscalyear, get_accounts)
>>> from trytond.modules.company.tests.tools import create_company
>>> from trytond.tests.tools import activate_modules
>>> today = dt.date.today()
Activate modules::
>>> config = activate_modules('account_payment', create_company, create_chart)
>>> Journal = Model.get('account.journal')
>>> Move = Model.get('account.move')
>>> Party = Model.get('party.party')
>>> Payment = Model.get('account.payment')
>>> PaymentJournal = Model.get('account.payment.journal')
Create fiscal year::
>>> fiscalyear = create_fiscalyear()
>>> fiscalyear.click('create_period')
Get accounts::
>>> accounts = get_accounts()
>>> expense_journal, = Journal.find([('code', '=', 'EXP')])
>>> cash_journal, = Journal.find([('code', '=', 'CASH')])
Create parties::
>>> supplier = Party(name="Supplier")
>>> supplier.save()
Create payable line::
>>> move = Move()
>>> move.journal = expense_journal
>>> line = move.lines.new(
... account=accounts['payable'], party=supplier, maturity_date=today,
... credit=Decimal('100.00'))
>>> line = move.lines.new(
... account=accounts['expense'],
... debit=Decimal('100.00'))
>>> move.click('post')
>>> move.state
'posted'
Make partial payment::
>>> payment_move = Move()
>>> payment_move.journal = cash_journal
>>> _ = payment_move.lines.new(
... account=accounts['payable'], party=supplier,
... debit=Decimal('50.00'))
>>> _ = payment_move.lines.new(
... account=accounts['cash'],
... credit=Decimal('50.00'))
>>> payment_move.click('post')
>>> payment_move.state
'posted'
Try to overpay the line::
>>> line, = [l for l in move.lines if l.account == accounts['payable']]
>>> pay_line = Wizard('account.move.line.pay', [line])
Traceback (most recent call last):
...
OverpayWarning: ...
Make full payment::
>>> payment_move2 = Move()
>>> payment_move2.journal = cash_journal
>>> _ = payment_move2.lines.new(
... account=accounts['payable'], party=supplier,
... debit=Decimal('50.00'))
>>> _ = payment_move2.lines.new(
... account=accounts['cash'],
... credit=Decimal('50.00'))
>>> payment_move2.click('post')
>>> payment_move2.state
'posted'
Try to overpay the line::
>>> line, = [l for l in move.lines if l.account == accounts['payable']]
>>> pay_line = Wizard('account.move.line.pay', [line])
Traceback (most recent call last):
...
OverpayWarning: ...