Code

Ticket #3866: more_it_fields_2.diff

File more_it_fields_2.diff, 7.3 KB (added by Massimiliano Ravelli <massimiliano.ravelli@…>, 7 years ago)

Little improvement to ITFiscalCodeField

Line 
1Index: django/contrib/localflavor/it/util.py
2===================================================================
3--- django/contrib/localflavor/it/util.py       (revision 0)
4+++ django/contrib/localflavor/it/util.py       (revision 0)
5@@ -0,0 +1,43 @@
6+from django.utils.translation import gettext
7+
8+FISCAL_CODE_EVEN_CHARS = {
9+    '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9,
10+    'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7, 'I': 8, 'J': 9,
11+    'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15, 'Q': 16, 'R': 17, 'S': 18,
12+    'T': 19, 'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24, 'Z': 25
13+}
14+FISCAL_CODE_ODD_CHARS = {
15+    '0': 1, '1': 0, '2': 5, '3': 7, '4': 9, '5': 13, '6': 15, '7': 17, '8': 19, '9': 21,
16+    'A': 1, 'B': 0, 'C': 5, 'D': 7, 'E': 9, 'F': 13, 'G': 15, 'H': 17, 'I': 19, 'J': 21,
17+    'K': 2, 'L': 4, 'M': 18, 'N': 20, 'O': 11, 'P': 3, 'Q': 6, 'R': 8, 'S': 12,
18+    'T': 14, 'U': 16, 'V': 10, 'W': 22, 'X': 25, 'Y': 24, 'Z': 23
19+}
20+# Chars from 'A' to 'Z'
21+FISCAL_CODE_CHECK_DIGITS = [chr(x) for x in range(65, 91)]
22+
23+
24+def fiscal_code_check_digit(fiscal_code):
25+    """ Calculate italian fiscal code check digit. """
26+    code = fiscal_code.upper()
27+    total = 0
28+    for i in range(0,15):
29+        try:
30+            if i % 2 == 0:
31+                total += FISCAL_CODE_ODD_CHARS[code[i]]
32+            else:
33+                total += FISCAL_CODE_EVEN_CHARS[code[i]]
34+        except KeyError:
35+            msg = gettext(u" Character '%(char)s' is not allowed.") % {'char': code[i]}
36+            raise ValueError(msg)
37+    return FISCAL_CODE_CHECK_DIGITS[total % 26]
38+
39+def vat_number_check_digit(vat_number):
40+    """ Calculate italian vat number check digit. """
41+    normalized_vat_number = str(vat_number).zfill(10)
42+    total = 0
43+    for i in range(0, 10, 2):
44+        total += int(normalized_vat_number[i])
45+    for i in range(1, 11, 2):
46+        quotient , remainder = divmod(int(normalized_vat_number[i]) * 2, 10)
47+        total += quotient + remainder
48+    return str((10 - total % 10) % 10)
49Index: django/contrib/localflavor/it/forms.py
50===================================================================
51--- django/contrib/localflavor/it/forms.py      (revision 5007)
52+++ django/contrib/localflavor/it/forms.py      (working copy)
53@@ -5,8 +5,11 @@
54 from django.newforms import ValidationError
55 from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
56 from django.utils.translation import gettext
57+from django.utils.encoding import smart_unicode
58+from django.contrib.localflavor.it.util import fiscal_code_check_digit, vat_number_check_digit
59 import re
60 
61+fc_re = re.compile(r"^(?P<area>\d{3})[-\ ]?(?P<group>\d{2})[-\ ]?(?P<serial>\d{4})$")
62 class ITZipCodeField(RegexField):
63     def __init__(self, *args, **kwargs):
64         super(ITZipCodeField, self).__init__(r'^\d{5}$',
65@@ -29,3 +32,46 @@
66     def __init__(self, attrs=None):
67         from it_province import PROVINCE_CHOICES # relative import
68         super(ITProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)
69+
70+class ITFiscalCodeField(RegexField):
71+    """
72+    A form field  that validates it's input in Italian fiscal code.
73+    For reference see http://www.agenziaentrate.it/ .
74+    """
75+    err_msg = gettext(u'Enter a valid fiscal code.')
76+    def __init__(self, *args, **kwargs):
77+        super(ITFiscalCodeField, self).__init__(r'^\w{3}\s*\w{3}\s*\w{5}\s*\w{5}$',
78+        max_length=None, min_length=None, error_message=self.err_msg,
79+        *args, **kwargs)
80+
81+    def clean(self, value):
82+        value = super(ITFiscalCodeField, self).clean(value)
83+        if value == u'':
84+            return value
85+        value = re.sub('\s', u'', value).upper()
86+        try:
87+            check_digit = fiscal_code_check_digit(value)
88+        except ValueError:
89+            raise ValidationError(self.err_msg)
90+        if not value[15] == check_digit:
91+            raise ValidationError(self.err_msg)
92+        return value
93+
94+class ITVatNumberField(Field):
95+    """
96+    A form field  that validates it's input in Italian VAT number.
97+    """
98+    def clean(self, value):
99+        value = super(ITVatNumberField, self).clean(value)
100+        if value == u'':
101+            return value
102+        err_msg = gettext(u'Enter a valid VAT number.')
103+        try:
104+            vat_number = int(value)
105+        except ValueError:
106+            raise ValidationError(err_msg)
107+        vat_number = str(vat_number).zfill(11)
108+        check_digit = vat_number_check_digit(vat_number[0:10])
109+        if not vat_number[10] == check_digit:
110+            raise ValidationError(err_msg)
111+        return smart_unicode(vat_number)
112Index: tests/regressiontests/forms/localflavor.py
113===================================================================
114--- tests/regressiontests/forms/localflavor.py  (revision 5007)
115+++ tests/regressiontests/forms/localflavor.py  (working copy)
116@@ -642,6 +642,44 @@
117 >>> w.render('regions', 'PMN')
118 u'<select name="regions">\n<option value="ABR">Abruzzo</option>\n<option value="BAS">Basilicata</option>\n<option value="CAL">Calabria</option>\n<option value="CAM">Campania</option>\n<option value="EMR">Emilia-Romagna</option>\n<option value="FVG">Friuli-Venezia Giulia</option>\n<option value="LAZ">Lazio</option>\n<option value="LIG">Liguria</option>\n<option value="LOM">Lombardia</option>\n<option value="MAR">Marche</option>\n<option value="MOL">Molise</option>\n<option value="PMN" selected="selected">Piemonte</option>\n<option value="PUG">Puglia</option>\n<option value="SAR">Sardegna</option>\n<option value="SIC">Sicilia</option>\n<option value="TOS">Toscana</option>\n<option value="TAA">Trentino-Alto Adige</option>\n<option value="UMB">Umbria</option>\n<option value="VAO">Valle d\u2019Aosta</option>\n<option value="VEN">Veneto</option>\n</select>'
119 
120+# ITFiscalCodeField ##########################################################
121+
122+>>> from django.contrib.localflavor.it.forms import ITFiscalCodeField
123+>>> f = ITFiscalCodeField()
124+>>> f.clean('RVLMSM71L12E897Q')
125+u'RVLMSM71L12E897Q'
126+>>> f.clean('rvlmsm71l12e897q')
127+u'RVLMSM71L12E897Q'
128+>>> f.clean('RVL MSM 71L12 E897Q')
129+u'RVLMSM71L12E897Q'
130+>>> f.clean('RVLMSM71L12E897A')
131+Traceback (most recent call last):
132+...
133+ValidationError: [u'Enter a valid fiscal code.']
134+>>> f.clean('%VLMSM71L12E897Q')
135+Traceback (most recent call last):
136+...
137+ValidationError: [u'Enter a valid fiscal code.']
138+
139+# ITVatNumberField ###########################################################
140+
141+>>> from django.contrib.localflavor.it.forms import ITVatNumberField
142+>>> f = ITVatNumberField()
143+>>> f.clean('07973780013')
144+u'07973780013'
145+>>> f.clean('7973780013')
146+u'07973780013'
147+>>> f.clean(7973780013)
148+u'07973780013'
149+>>> f.clean('07973780014')
150+Traceback (most recent call last):
151+...
152+ValidationError: [u'Enter a valid VAT number.']
153+>>> f.clean('A7973780013')
154+Traceback (most recent call last):
155+...
156+ValidationError: [u'Enter a valid VAT number.']
157+
158 # FIZipCodeField #############################################################
159 
160 FIZipCodeField validates that the data is a valid FI zipcode.
161Index: AUTHORS
162===================================================================
163--- AUTHORS     (revision 5007)
164+++ AUTHORS     (working copy)
165@@ -174,6 +174,7 @@
166     J. Rademaker
167     Michael Radziej <mir@noris.de>
168     ramiro
169+    Massimiliano Ravelli <massimiliano.ravelli@gmail.com>
170     Brian Ray <http://brianray.chipy.org/>
171     remco@diji.biz
172     rhettg@gmail.com