From 139c62d72f6240ab72fdd9820b2db0040c38cbeb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thomas=20NO=C3=8BL?= <tnoel@entrouvert.com>
Date: Fri, 5 Aug 2022 15:11:49 +0200
Subject: [PATCH] forms: add normalization for siren, siren and nir-fr
 validation (#67988)

---
 tests/test_widgets.py | 52 +++++++++++++++++++++++++++++++++++++++++++
 wcs/qommon/form.py    | 13 ++++++++---
 2 files changed, 62 insertions(+), 3 deletions(-)

diff --git a/tests/test_widgets.py b/tests/test_widgets.py
index 8612b979b..f5e08d030 100644
--- a/tests/test_widgets.py
+++ b/tests/test_widgets.py
@@ -923,27 +923,44 @@ def test_wcsextrastringwidget_siren_validation():
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': '443170139'})
     assert not widget.has_error()
+    assert widget.value == '443170139'
+
+    widget = WcsExtraStringWidget('test', value='foo', required=False)
+    widget.field = fakefield
+    mock_form_submission(req, widget, {'test': ' 443 170  139'})
+    assert not widget.has_error()
+    assert widget.value == '443170139'
 
     widget = WcsExtraStringWidget('test', value='foo', required=False)
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': '443170130'})
     assert widget.has_error()
     assert widget.error == 'Invalid SIREN code'
+    assert widget.value == '443170130'
 
     widget = WcsExtraStringWidget('test', value='foo', required=False)
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': '44317013900036'})
     assert widget.has_error()
+    assert widget.value == '44317013900036'
 
     widget = WcsExtraStringWidget('test', value='foo', required=False)
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': 'XXX170130'})
     assert widget.has_error()
+    assert widget.value == 'XXX170130'
+
+    widget = WcsExtraStringWidget('test', value='foo', required=False)
+    widget.field = fakefield
+    mock_form_submission(req, widget, {'test': 'XXX 170 130'})
+    assert widget.has_error()
+    assert widget.value == 'XXX 170 130'  # do not normalize invalid value
 
     widget = WcsExtraStringWidget('test', value='foo', required=False)
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': '443170¹39'})
     assert widget.has_error()
+    assert widget.value == '443170¹39'
 
 
 def test_wcsextrastringwidget_siret_validation():
@@ -958,12 +975,21 @@ def test_wcsextrastringwidget_siret_validation():
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': '44317013900036'})
     assert not widget.has_error()
+    assert widget.value == '44317013900036'
 
+    widget = WcsExtraStringWidget('test', value='foo', required=False)
+    widget.field = fakefield
+    mock_form_submission(req, widget, {'test': '443 170 139  00036'})
+    assert not widget.has_error()
+    assert widget.value == '44317013900036'  # normalized
+
+    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()
+    assert widget.value == '35600000000048'
 
     # failing cases
     widget = WcsExtraStringWidget('test', value='foo', required=False)
@@ -971,16 +997,25 @@ def test_wcsextrastringwidget_siret_validation():
     mock_form_submission(req, widget, {'test': '44317013900037'})
     assert widget.has_error()
     assert widget.error == 'Invalid SIRET code'
+    assert widget.value == '44317013900037'
 
     widget = WcsExtraStringWidget('test', value='foo', required=False)
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': 'ABC17013900037'})
     assert widget.has_error()
+    assert widget.value == 'ABC17013900037'
 
     widget = WcsExtraStringWidget('test', value='foo', required=False)
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': '443170139'})
     assert widget.has_error()
+    assert widget.value == '443170139'
+
+    widget = WcsExtraStringWidget('test', value='foo', required=False)
+    widget.field = fakefield
+    mock_form_submission(req, widget, {'test': '443 170 139'})
+    assert widget.has_error()
+    assert widget.value == '443 170 139'  # do not normalize invalid value
 
 
 def test_wcsextrastringwidget_nir_validation():
@@ -996,16 +1031,26 @@ def test_wcsextrastringwidget_nir_validation():
     # 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()
+    assert widget.value == '269054958815780'
 
     # corsica
     widget = WcsExtraStringWidget('test', value='foo', required=False)
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': '269052A58815717'})
     assert not widget.has_error()
+    assert widget.value == '269052A58815717'
     widget = WcsExtraStringWidget('test', value='foo', required=False)
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': '269052B58815744'})
     assert not widget.has_error()
+    assert widget.value == '269052B58815744'
+
+    # accept spaces, but remove them
+    widget = WcsExtraStringWidget('test', value='foo', required=False)
+    widget.field = fakefield
+    mock_form_submission(req, widget, {'test': '2 69 05 2B588 157  44'})
+    assert not widget.has_error()
+    assert widget.value == '269052B58815744'
 
     # failing cases
     widget = WcsExtraStringWidget('test', value='foo', required=False)
@@ -1013,36 +1058,43 @@ def test_wcsextrastringwidget_nir_validation():
     mock_form_submission(req, widget, {'test': '42'})
     assert widget.has_error()
     assert widget.error == 'Invalid NIR'
+    assert widget.value == '42'
 
     widget = WcsExtraStringWidget('test', value='foo', required=False)
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': '269054958815700'})
     assert widget.has_error()
+    assert widget.value == '269054958815700'
 
     widget = WcsExtraStringWidget('test', value='foo', required=False)
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': 'hello 789012345'})
     assert widget.has_error()
+    assert widget.value == 'hello 789012345'
 
     widget = WcsExtraStringWidget('test', value='foo', required=False)
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': '069054958815780'})
     assert widget.has_error()
+    assert widget.value == '069054958815780'
 
     widget = WcsExtraStringWidget('test', value='foo', required=False)
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': '269004958815780'})
     assert widget.has_error()
+    assert widget.value == '269004958815780'
 
     widget = WcsExtraStringWidget('test', value='foo', required=False)
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': '269054900015780'})
     assert widget.has_error()
+    assert widget.value == '269054900015780'
 
     widget = WcsExtraStringWidget('test', value='foo', required=False)
     widget.field = fakefield
     mock_form_submission(req, widget, {'test': '269054958800080'})
     assert widget.has_error()
+    assert widget.value == '269054958800080'
 
 
 def test_wcsextrastringwidget_belgian_nrn_validation():
diff --git a/wcs/qommon/form.py b/wcs/qommon/form.py
index 5eb6e5878..f34a75c7c 100644
--- a/wcs/qommon/form.py
+++ b/wcs/qommon/form.py
@@ -1167,6 +1167,7 @@ class ValidationWidget(CompositeWidget):
                     'function': 'validate_siren',
                     'error_message': _('Invalid SIREN code'),
                     'html_inputmode': 'numeric',
+                    'normalize_function': lambda v: v.upper().strip().replace(' ', ''),
                 },
             ),
             (
@@ -1176,6 +1177,7 @@ class ValidationWidget(CompositeWidget):
                     'function': 'validate_siret',
                     'error_message': _('Invalid SIRET code'),
                     'html_inputmode': 'numeric',
+                    'normalize_function': lambda v: v.upper().strip().replace(' ', ''),
                 },
             ),
             (
@@ -1184,6 +1186,7 @@ class ValidationWidget(CompositeWidget):
                     'title': _('NIR (France)'),
                     'error_message': _('Invalid NIR'),
                     'function': 'validate_nir',
+                    'normalize_function': lambda v: v.upper().strip().replace(' ', ''),
                 },
             ),
             (
@@ -1376,12 +1379,16 @@ class WcsExtraStringWidget(StringWidget):
                 self.field.validation
             )
 
-        if self.value and self.validation_function and not self.validation_function(self.value):
+        normalized_value = self.value
+        if self.field and self.value and self.field.validation:
+            normalize = ValidationWidget.get_normalize_function(self.field.validation)
+            normalized_value = normalize(self.value)
+
+        if self.value and self.validation_function and not self.validation_function(normalized_value):
             self.error = self.validation_function_error_message or _('invalid value')
 
         if self.field and self.value and not self.error and self.field.validation:
-            normalize = ValidationWidget.get_normalize_function(self.field.validation)
-            self.value = normalize(self.value)
+            self.value = normalized_value
 
 
 class DateWidget(StringWidget):
-- 
2.35.1

