From ec5de536e1d5ae828f3e38dc1b9143ee4e49a85e Mon Sep 17 00:00:00 2001 From: Xavier Codinas Date: Mon, 31 Oct 2016 13:26:20 +0100 Subject: [PATCH] Add validation to sepa creditor identifier issue5927 review28861002 --- INSTALL | 1 + party.py | 26 +++++++++++++++++++++- setup.py | 2 +- tests/test_account_payment_sepa.py | 35 +++++++++++++++++++++++++++--- 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/INSTALL b/INSTALL index 23fb8aa0c5..f0ae445107 100644 --- a/INSTALL +++ b/INSTALL @@ -12,6 +12,7 @@ Prerequisites * trytond_account_payment (http://www.tryton.org/) * trytond_bank (http://www.tryton.org/) * trytond_company (http://www.tryton.org/) + * python-stdnum (http://arthurdejong.org/python-stdnum/) Installation ------------ diff --git a/party.py b/party.py index 62ae08b55b..cf81d0b2c2 100644 --- a/party.py +++ b/party.py @@ -3,6 +3,9 @@ from sql import Null, Literal from sql.functions import CurrentTimestamp +import stdnum.eu.at_02 as sepa +import stdnum.exceptions + from trytond.pool import PoolMeta, Pool from trytond.model import fields from trytond.transaction import Transaction @@ -59,6 +62,8 @@ class PartyIdentifier: cls._error_messages.update({ 'unique_sepa': ('Party "%(party)s" has more than one ' 'SEPA Creditor Identifier.'), + 'sepa_invalid': ('The SEPA identifier "%(code)s" on party ' + '"%(party)s" is not valid.'), }) @classmethod @@ -69,7 +74,6 @@ class PartyIdentifier: def check_code(self): super(PartyIdentifier, self).check_code() - # TODO validate identifier with python-stdnum if self.type == 'sepa': for identifier in self.party.identifiers: if identifier.type == 'sepa' and self != identifier: @@ -77,3 +81,23 @@ class PartyIdentifier: 'invalid_sepa': self.code, 'party': self.party.rec_name, }) + if not sepa.is_valid(self.code): + # Called from pre_validate so party may not be saved yet + if self.party and self.party.id > 0: + party = self.party.rec_name + else: + party = '' + self.raise_user_error('sepa_invalid', { + 'code': self.code, + 'party': party, + }) + + @fields.depends('type', 'code') + def on_change_with_code(self): + code = super(PartyIdentifier, self).on_change_with_code() + if self.type == 'sepa': + try: + return sepa.compact(self.code) + except stdnum.exceptions.ValidationError: + pass + return code diff --git a/setup.py b/setup.py index d296647f66..ca043396e4 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ if minor_version % 2: 'hg+http://hg.tryton.org/modules/%s#egg=%s-%s' % ( name[8:], name, version)) -requires = ['Genshi', 'lxml', 'python-dateutil'] +requires = ['Genshi', 'lxml', 'python-dateutil', 'python-stdnum >= 1.0'] for dep in info.get('depends', []): if not re.match(r'(ir|res)(\W|$)', dep): requires.append(get_require_version('trytond_%s' % dep)) diff --git a/tests/test_account_payment_sepa.py b/tests/test_account_payment_sepa.py index f2c07f612a..5760ebedcb 100644 --- a/tests/test_account_payment_sepa.py +++ b/tests/test_account_payment_sepa.py @@ -32,7 +32,7 @@ def setup_environment(): currency = create_currency('EUR') company = create_company(currency=currency) - sepa = Identifier(party=company.party, code='BE68539007547034', + sepa = Identifier(party=company.party, code='ES23ZZZ47690558N', type='sepa') sepa.save() bank_party = Party(name='European Bank') @@ -257,15 +257,44 @@ class AccountPaymentSepaTestCase(ModuleTestCase): party = Party(name='test') - sepa = Identifier(party=party, code='BE68539007547034', + sepa = Identifier(party=party, code='ES23ZZZ47690558N', type='sepa') sepa.save() - sepa2 = Identifier(party=party, code='BE68539007547034', + sepa2 = Identifier(party=party, code='ES23ZZZ47690558N', type='sepa') with self.assertRaises(UserError): sepa2.save() + @with_transaction() + def test_sepa_identifier(self): + 'Test sepa indentifier validation' + pool = Pool() + Party = pool.get('party.party') + Identifier = pool.get('party.identifier') + + party = Party(name='test') + sepa = Identifier(party=party, code='BE68539007547034', + type='sepa') + with self.assertRaises(UserError): + sepa.save() + + party2 = Party(name='test2') + sepa = Identifier(party=party2, code='007547034', + type='sepa') + with self.assertRaises(UserError): + sepa.save() + + party3 = Party(name='test3') + sepa = Identifier(party=party3, code='ES23ZZZ47690558N', + type='sepa') + sepa.save() + + party4 = Party(name='test4') + sepa = Identifier(party=party4, code='ES 23ZZZ 4769 055 8N', + type='sepa') + sepa.save() + @with_transaction() def test_payment_sepa_bank_account_number(self): 'Test Payment.sepa_bank_account_number'