fields: add nir and iban algorithms to string field validation (#35619)

This commit is contained in:
Nicolas Roche 2019-08-28 17:23:30 +02:00
parent aec1c32c9c
commit d0842a5729
3 changed files with 145 additions and 0 deletions

View File

@ -690,6 +690,100 @@ def test_wcsextrastringwidget_siret_validation():
assert widget.has_error()
def test_wcsextrastringwidget_nir_validation():
class FakeField: pass
fakefield = FakeField()
fakefield.validation = {'type': 'nir-fr'}
# regular cases
widget = WcsExtraStringWidget('test', value='foo', required=False)
widget.field = fakefield
# https://fr.wikipedia.org/wiki/Num%C3%A9ro_de_s%C3%A9curit%C3%A9_sociale_en_France#/media/Fichier:CarteVitale2.jpg
mock_form_submission(req, widget, {'test': '269054958815780'})
assert not widget.has_error()
# corsica
widget = WcsExtraStringWidget('test', value='foo', required=False)
widget.field = fakefield
mock_form_submission(req, widget, {'test': '269052A58815717'})
assert not widget.has_error()
widget = WcsExtraStringWidget('test', value='foo', required=False)
widget.field = fakefield
mock_form_submission(req, widget, {'test': '269052B58815744'})
assert not widget.has_error()
# failing cases
widget = WcsExtraStringWidget('test', value='foo', required=False)
widget.field = fakefield
mock_form_submission(req, widget, {'test': '42'})
assert widget.has_error()
widget = WcsExtraStringWidget('test', value='foo', required=False)
widget.field = fakefield
mock_form_submission(req, widget, {'test': '269054958815700'})
assert widget.has_error()
widget = WcsExtraStringWidget('test', value='foo', required=False)
widget.field = fakefield
mock_form_submission(req, widget, {'test': 'hello 789012345'})
assert widget.has_error()
widget = WcsExtraStringWidget('test', value='foo', required=False)
widget.field = fakefield
mock_form_submission(req, widget, {'test': '069054958815780'})
assert widget.has_error()
widget = WcsExtraStringWidget('test', value='foo', required=False)
widget.field = fakefield
mock_form_submission(req, widget, {'test': '269004958815780'})
assert widget.has_error()
widget = WcsExtraStringWidget('test', value='foo', required=False)
widget.field = fakefield
mock_form_submission(req, widget, {'test': '269054900015780'})
assert widget.has_error()
widget = WcsExtraStringWidget('test', value='foo', required=False)
widget.field = fakefield
mock_form_submission(req, widget, {'test': '269054958800080'})
assert widget.has_error()
def test_wcsextrastringwidget_iban_validation():
class FakeField: pass
fakefield = FakeField()
fakefield.validation = {'type': 'iban'}
# regular cases
for iban in ['BE71 0961 2345 6769', # Belgium
'FR76 3000 6000 0112 3456 7890 189', # France
'FR27 2004 1000 0101 2345 6Z02 068', # France (having letter)
'DE91 1000 0000 0123 4567 89', # Germany
'GR96 0810 0010 0000 0123 4567 890', # Greece
'RO09 BCYP 0000 0012 3456 7890', # Romania
'SA44 2000 0001 2345 6789 1234', # Saudi Arabia
'ES79 2100 0813 6101 2345 6789', # Spain
'CH56 0483 5012 3456 7800 9', # Switzerland
'GB98 MIDL 0700 9312 3456 78', # United Kingdom
]:
widget = WcsExtraStringWidget('test', value='foo', required=False)
widget.field = fakefield
mock_form_submission(req, widget, {'test': iban.replace(' ', '')})
assert not widget.has_error()
# failing cases
for iban in ['42',
'FR76 2004 1000 0101 2345 6Z02 068',
'FR76 2004 1000 0101 2345 6%02 068',
'FR76 hello 234 6789 1234 6789 123',
'FRxx 2004 1000 0101 2345 6Z02 068',
]:
widget = WcsExtraStringWidget('test', value='foo', required=False)
widget.field = fakefield
mock_form_submission(req, widget, {'test': iban.replace(' ', '')})
assert widget.has_error()
def test_wcsextrastringwidget_django_validation():
class FakeField: pass
fakefield = FakeField()

View File

@ -908,6 +908,8 @@ class ValidationWidget(CompositeWidget):
('zipcode-fr', {'title': N_('Zip Code (France)'), 'regex': '\d{5}'}),
('siren-fr', {'title': N_('SIREN Code (France)'), 'function': 'validate_siren'}),
('siret-fr', {'title': N_('SIRET Code (France)'), 'function': 'validate_siret'}),
('nir-fr', {'title': N_('NIR (France)'), 'function': 'validate_nir'}),
('iban', {'title': N_('IBAN'), 'function': 'validate_iban'}),
('regex', {'title': N_('Regular Expression')}),
('django', {'title': N_('Django Condition')}),
])

View File

@ -654,3 +654,52 @@ def validate_siret(string_value):
and sum(int(x) for x in string_value) % 5 == 0):
return True
return validate_luhn(string_value, length=14)
def validate_nir(string_value):
'''https://fr.wikipedia.org/wiki/Num%C3%A9ro_de_s%C3%A9curit%C3%A9_sociale_en_France'''
if not string_value:
return False
if len(string_value) != 15:
return False
if string_value[0] == '0': # sex
return False
if string_value[7:10] == '000': # municipality
return False
if string_value[10:13] == '000': # order
return False
dept = string_value[5:7]
if dept == '2A':
string_value = string_value.replace('2A', '19', 1)
elif dept == '2B':
string_value = string_value.replace('2B', '18', 1)
if not string_value.isdigit():
return False
month = int(string_value[3:5])
if month < 50 and month not in range(1, 13) + [20] + range(30, 43):
return False
nir_key = string_value[13:]
return int(nir_key) == 97 - int(string_value[:13]) % 97
def validate_iban(string_value):
'''https://fr.wikipedia.org/wiki/International_Bank_Account_Number'''
if not string_value:
return False
country_code = string_value[:2]
iban_key = string_value[2:4]
bban = string_value[4:]
if not (country_code.isalpha() and country_code.isupper()):
return False
if not iban_key.isdigit():
return False
dummy_iban = bban + country_code + '00'
dummy_iban_converted = ''
for car in dummy_iban:
if car >= 'A' and car <= 'Z':
dummy_iban_converted += str(ord(car) - ord('A') + 10)
else:
dummy_iban_converted += car
if not dummy_iban_converted.isdigit():
return False
return int(iban_key) == 98 - int(dummy_iban_converted) % 97