Validate the format of payment references

This commit is contained in:
Cédric Krier
2025-09-29 15:33:46 +02:00
parent 7c90b22b75
commit 4c03672232
5 changed files with 60 additions and 3 deletions
+1
View File
@@ -1,3 +1,4 @@
* Validate the format of payment references
Version 7.6.0 - 2025-04-28
--------------------------
+3
View File
@@ -12,6 +12,9 @@ this repository contains the full copyright notices and license terms. -->
<record model="ir.message" id="msg_payment_reconciled">
<field name="text">The line "%(line)s" of payment "%(payment)s" is already reconciled.</field>
</record>
<record model="ir.message" id="msg_payment_reference_invalid">
<field name="text">The %(type)s "%(reference)s" on payment "%(payment)s" is not valid.</field>
</record>
<record model="ir.message" id="msg_erase_party_pending_payment">
<field name="text">You cannot erase party "%(party)s" while they have pending payments with company "%(company)s".</field>
</record>
+49 -2
View File
@@ -4,9 +4,11 @@ from collections import defaultdict
from decimal import Decimal
from itertools import groupby
import stdnum.exceptions
from sql.aggregate import Count, Sum
from sql.functions import CharLength
from sql.operators import Abs
from stdnum import iso11649
from trytond import backend
from trytond.i18n import gettext
@@ -16,7 +18,7 @@ from trytond.model.exceptions import AccessError
from trytond.modules.company.model import (
employee_field, reset_employee, set_employee)
from trytond.modules.currency.fields import Monetary
from trytond.pool import Pool
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval, If
from trytond.rpc import RPC
from trytond.tools import (
@@ -25,7 +27,8 @@ from trytond.tools import (
from trytond.transaction import Transaction
from trytond.wizard import StateAction, Wizard
from .exceptions import OverpayWarning, ReconciledWarning
from .exceptions import (
OverpayWarning, PaymentValidationError, ReconciledWarning)
KINDS = [
('payable', 'Payable'),
@@ -276,6 +279,11 @@ class Payment(Workflow, ModelSQL, ModelView):
_rec_name = 'number'
number = fields.Char("Number", required=True, readonly=True)
reference = fields.Char("Reference", states=_STATES)
reference_type = fields.Selection([
(None, ""),
('creditor_reference', "Creditor Reference"),
], "Reference Type")
reference_type_string = reference_type.translated('reference_type')
company = fields.Many2One(
'company.company', "Company", required=True, states=_STATES)
journal = fields.Many2One('account.payment.journal', 'Journal',
@@ -489,6 +497,19 @@ class Payment(Workflow, ModelSQL, ModelView):
table, _ = tables[None]
return [CharLength(table.number), table.number]
@fields.depends('reference_type', 'reference')
def on_change_with_reference(self):
if (reference := self.reference) and (type := self.reference_type):
reference = getattr(self, f'_format_reference_{type}')(reference)
return reference
@classmethod
def _format_reference_creditor_reference(cls, reference):
try:
return iso11649.format(reference)
except stdnum.exceptions.ValidationError:
return reference
@staticmethod
def default_company():
return Transaction().context.get('company')
@@ -603,6 +624,7 @@ class Payment(Workflow, ModelSQL, ModelView):
default = default.copy()
default.setdefault('number', None)
default.setdefault('reference')
default.setdefault('reference_type')
default.setdefault('group', None)
default.setdefault('approved_by')
default.setdefault('succeeded_by')
@@ -663,6 +685,31 @@ class Payment(Workflow, ModelSQL, ModelView):
callback.append(lambda: Line.set_payment_amount(lines))
return callback
@classmethod
def validate_fields(cls, payments, field_names):
super().validate_fields(payments, field_names)
cls.check_reference(payments, field_names)
@classmethod
def check_reference(cls, payments, field_names):
if field_names and not (field_names & {'reference', 'reference_type'}):
return
for payment in payments:
if type := payment.reference_type:
method = getattr(cls, f'_check_reference_{type}')
if not method(payment):
reference = payment.reference
type = payment.reference_type_string
raise PaymentValidationError(gettext(
'account_payment.msg_payment_reference_invalid',
type=type,
reference=reference,
payment=payment.rec_name))
def _check_reference_creditor_reference(self):
return iso11649.is_valid(self.reference)
@classmethod
@ModelView.button
@Workflow.transition('draft')
@@ -90,6 +90,10 @@ Partially pay line::
>>> payment.amount
Decimal('50.00')
>>> payment.amount = Decimal('20.00')
>>> payment.reference_type = 'creditor_reference'
>>> payment.reference = 'RF18539007547034'
>>> payment.reference
'RF18 5390 0754 7034'
>>> payment.click('submit')
>>> assertEqual(payment.submitted_by, employee)
>>> payment.click('approve')
@@ -22,7 +22,9 @@ this repository contains the full copyright notices and license terms. -->
<notebook colspan="4">
<page string="Payment" id="payment">
<label name="reference"/>
<field name="reference" colspan="3"/>
<field name="reference"/>
<label name="reference_type"/>
<field name="reference_type"/>
</page>
<page string="Other Info" id="info">
<label name="group"/>