Index: django/contrib/localflavor/it/util.py
===================================================================
--- django/contrib/localflavor/it/util.py (revision 0)
+++ django/contrib/localflavor/it/util.py (revision 0)
@@ -0,0 +1,43 @@
+from django.utils.translation import gettext
+
+FISCAL_CODE_EVEN_CHARS = {
+ '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9,
+ 'A': 0, 'B': 1, 'C': 2, 'D': 3, 'E': 4, 'F': 5, 'G': 6, 'H': 7, 'I': 8, 'J': 9,
+ 'K': 10, 'L': 11, 'M': 12, 'N': 13, 'O': 14, 'P': 15, 'Q': 16, 'R': 17, 'S': 18,
+ 'T': 19, 'U': 20, 'V': 21, 'W': 22, 'X': 23, 'Y': 24, 'Z': 25
+}
+FISCAL_CODE_ODD_CHARS = {
+ '0': 1, '1': 0, '2': 5, '3': 7, '4': 9, '5': 13, '6': 15, '7': 17, '8': 19, '9': 21,
+ 'A': 1, 'B': 0, 'C': 5, 'D': 7, 'E': 9, 'F': 13, 'G': 15, 'H': 17, 'I': 19, 'J': 21,
+ 'K': 2, 'L': 4, 'M': 18, 'N': 20, 'O': 11, 'P': 3, 'Q': 6, 'R': 8, 'S': 12,
+ 'T': 14, 'U': 16, 'V': 10, 'W': 22, 'X': 25, 'Y': 24, 'Z': 23
+}
+# Chars from 'A' to 'Z'
+FISCAL_CODE_CHECK_DIGITS = [chr(x) for x in range(65, 91)]
+
+
+def fiscal_code_check_digit(fiscal_code):
+ """ Calculate italian fiscal code check digit. """
+ code = fiscal_code.upper()
+ total = 0
+ for i in range(0,15):
+ try:
+ if i % 2 == 0:
+ total += FISCAL_CODE_ODD_CHARS[code[i]]
+ else:
+ total += FISCAL_CODE_EVEN_CHARS[code[i]]
+ except KeyError:
+ msg = gettext(u" Character '%(char)s' is not allowed.") % {'char': code[i]}
+ raise ValueError(msg)
+ return FISCAL_CODE_CHECK_DIGITS[total % 26]
+
+def vat_number_check_digit(vat_number):
+ """ Calculate italian vat number check digit. """
+ normalized_vat_number = str(vat_number).zfill(10)
+ total = 0
+ for i in range(0, 10, 2):
+ total += int(normalized_vat_number[i])
+ for i in range(1, 11, 2):
+ quotient , remainder = divmod(int(normalized_vat_number[i]) * 2, 10)
+ total += quotient + remainder
+ return str((10 - total % 10) % 10)
Index: django/contrib/localflavor/it/forms.py
===================================================================
--- django/contrib/localflavor/it/forms.py (revision 4992)
+++ django/contrib/localflavor/it/forms.py (working copy)
@@ -5,6 +5,8 @@
from django.newforms import ValidationError
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.utils.translation import gettext
+from django.utils.encoding import smart_unicode
+from django.contrib.localflavor.it.util import fiscal_code_check_digit, vat_number_check_digit
import re
class ITZipCodeField(RegexField):
@@ -29,3 +31,44 @@
def __init__(self, attrs=None):
from it_province import PROVINCE_CHOICES # relative import
super(ITProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)
+
+class ITFiscalCodeField(Field):
+ """
+ A form field that validates it's input in Italian fiscal code.
+ """
+ def clean(self, value):
+ super(ITFiscalCodeField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+ err_msg = gettext(u'Enter a valid fiscal code.')
+ fiscal_code = value.upper()
+ fiscal_code = fiscal_code.replace(' ', '')
+ fiscal_code = fiscal_code.replace('-', '')
+ if not len(fiscal_code) == 16:
+ raise ValidationError(err_msg)
+ try:
+ check_digit = fiscal_code_check_digit(fiscal_code)
+ except ValueError:
+ raise ValidationError(err_msg)
+ if not fiscal_code[15] == check_digit:
+ raise ValidationError(err_msg)
+ return smart_unicode(fiscal_code)
+
+class ITVatNumberField(Field):
+ """
+ A form field that validates it's input in Italian VAT number.
+ """
+ def clean(self, value):
+ super(ITVatNumberField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+ err_msg = gettext(u'Enter a valid VAT number.')
+ try:
+ vat_number = int(value)
+ except ValueError:
+ raise ValidationError(err_msg)
+ vat_number = str(vat_number).zfill(11)
+ check_digit = vat_number_check_digit(vat_number[0:10])
+ if not vat_number[10] == check_digit:
+ raise ValidationError(err_msg)
+ return smart_unicode(vat_number)
Index: tests/regressiontests/forms/localflavor.py
===================================================================
--- tests/regressiontests/forms/localflavor.py (revision 4992)
+++ tests/regressiontests/forms/localflavor.py (working copy)
@@ -642,6 +642,46 @@
>>> w.render('regions', 'PMN')
u''
+# ITFiscalCodeField ##########################################################
+
+>>> from django.contrib.localflavor.it.forms import ITFiscalCodeField
+>>> f = ITFiscalCodeField()
+>>> f.clean('RVLMSM71L12E897Q')
+u'RVLMSM71L12E897Q'
+>>> f.clean('rvlmsm71l12e897q')
+u'RVLMSM71L12E897Q'
+>>> f.clean('RVL MSM 71L12 E897Q')
+u'RVLMSM71L12E897Q'
+>>> f.clean('RVL-MSM-71L12-E897Q')
+u'RVLMSM71L12E897Q'
+>>> f.clean('RVLMSM71L12E897A')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid fiscal code.']
+>>> f.clean('%VLMSM71L12E897Q')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid fiscal code.']
+
+# ITVatNumberField ###########################################################
+
+>>> from django.contrib.localflavor.it.forms import ITVatNumberField
+>>> f = ITVatNumberField()
+>>> f.clean('07973780013')
+u'07973780013'
+>>> f.clean('7973780013')
+u'07973780013'
+>>> f.clean(7973780013)
+u'07973780013'
+>>> f.clean('07973780014')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid VAT number.']
+>>> f.clean('A7973780013')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid VAT number.']
+
# FIZipCodeField #############################################################
FIZipCodeField validates that the data is a valid FI zipcode.
Index: AUTHORS
===================================================================
--- AUTHORS (revision 4992)
+++ AUTHORS (working copy)
@@ -174,6 +174,7 @@
J. Rademaker
Michael Radziej
ramiro
+ Massimiliano Ravelli
Brian Ray
remco@diji.biz
rhettg@gmail.com