fields: add Luhn algorithm to string field validation (#35013)
This commit is contained in:
parent
e5990b82fb
commit
b7cbff134c
|
@ -630,6 +630,66 @@ def test_wcsextrastringwidget_builtin_validation():
|
|||
mock_form_submission(req, widget, {'test': '1234'})
|
||||
assert widget.has_error()
|
||||
|
||||
def test_wcsextrastringwidget_siren_validation():
|
||||
class FakeField: pass
|
||||
fakefield = FakeField()
|
||||
fakefield.validation = {'type': 'siren-fr'}
|
||||
|
||||
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
||||
widget.field = fakefield
|
||||
mock_form_submission(req, widget, {'test': '443170139'})
|
||||
assert not widget.has_error()
|
||||
|
||||
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
||||
widget.field = fakefield
|
||||
mock_form_submission(req, widget, {'test': '443170130'})
|
||||
assert widget.has_error()
|
||||
|
||||
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
||||
widget.field = fakefield
|
||||
mock_form_submission(req, widget, {'test': '44317013900036'})
|
||||
assert widget.has_error()
|
||||
|
||||
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
||||
widget.field = fakefield
|
||||
mock_form_submission(req, widget, {'test': 'XXX170130'})
|
||||
assert widget.has_error()
|
||||
|
||||
|
||||
def test_wcsextrastringwidget_siret_validation():
|
||||
class FakeField: pass
|
||||
fakefield = FakeField()
|
||||
fakefield.validation = {'type': 'siret-fr'}
|
||||
|
||||
# regular case
|
||||
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
||||
widget.field = fakefield
|
||||
mock_form_submission(req, widget, {'test': '44317013900036'})
|
||||
assert not widget.has_error()
|
||||
|
||||
# special case la poste
|
||||
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
||||
widget.field = fakefield
|
||||
mock_form_submission(req, widget, {'test': '35600000000048'})
|
||||
assert not widget.has_error()
|
||||
|
||||
# failing cases
|
||||
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
||||
widget.field = fakefield
|
||||
mock_form_submission(req, widget, {'test': '44317013900037'})
|
||||
assert widget.has_error()
|
||||
|
||||
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
||||
widget.field = fakefield
|
||||
mock_form_submission(req, widget, {'test': 'ABC17013900037'})
|
||||
assert widget.has_error()
|
||||
|
||||
widget = WcsExtraStringWidget('test', value='foo', required=False)
|
||||
widget.field = fakefield
|
||||
mock_form_submission(req, widget, {'test': '443170139'})
|
||||
assert widget.has_error()
|
||||
|
||||
|
||||
def test_wcsextrastringwidget_django_validation():
|
||||
class FakeField: pass
|
||||
fakefield = FakeField()
|
||||
|
|
|
@ -906,6 +906,8 @@ class ValidationWidget(CompositeWidget):
|
|||
('digits', {'title': N_('Digits'), 'regex': '\d+'}),
|
||||
('phone-fr', {'title': N_('Phone Number (France)'), 'regex': '0[\d\.\s]{9}'}),
|
||||
('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'}),
|
||||
('regex', {'title': N_('Regular Expression')}),
|
||||
('django', {'title': N_('Django Condition')}),
|
||||
])
|
||||
|
@ -965,6 +967,9 @@ class ValidationWidget(CompositeWidget):
|
|||
condition = ValidationCondition(validation['value'], value=value)
|
||||
return condition.evaluate()
|
||||
return django_validation
|
||||
validation_method = cls.validation_methods.get(validation['type'])
|
||||
if 'function' in validation_method:
|
||||
return getattr(misc, validation_method['function'])
|
||||
|
||||
@classmethod
|
||||
def get_validation_pattern(cls, validation):
|
||||
|
|
|
@ -639,3 +639,40 @@ def html2text(text):
|
|||
if isinstance(text, (htmltext, str)):
|
||||
text = unicode(str(text), get_publisher().site_charset)
|
||||
return site_encode(HTMLParser().unescape(strip_tags(text)))
|
||||
|
||||
|
||||
def validate_luhn(string_value, length=None):
|
||||
'''Verify Luhn checksum on a string representing a number'''
|
||||
if not string_value:
|
||||
return False
|
||||
if length is not None and len(string_value) != length:
|
||||
return False
|
||||
if not string_value.isdigit():
|
||||
return False
|
||||
|
||||
# take all digits counting from the right, double value for digits pair
|
||||
# index (counting from 1), if double has 2 digits take their sum
|
||||
checksum = 0
|
||||
for i, x in enumerate(reversed(string_value)):
|
||||
if i % 2 == 0:
|
||||
checksum += int(x)
|
||||
else:
|
||||
checksum += sum(int(y) for y in str(2 * int(x)))
|
||||
if checksum % 10 != 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def validate_siren(string_value):
|
||||
return validate_luhn(string_value, length=9)
|
||||
|
||||
|
||||
def validate_siret(string_value):
|
||||
# special case : La Poste
|
||||
if not string_value.isdigit():
|
||||
return False
|
||||
if (string_value.startswith('356000000')
|
||||
and len(string_value) == 14
|
||||
and sum(int(x) for x in string_value) % 5 == 0):
|
||||
return True
|
||||
return validate_luhn(string_value, length=14)
|
||||
|
|
Loading…
Reference in New Issue