diff --git a/django/contrib/localflavor/pl/forms.py b/django/contrib/localflavor/pl/forms.py
index d1e9773a80..ef4a38b7de 100644
--- a/django/contrib/localflavor/pl/forms.py
+++ b/django/contrib/localflavor/pl/forms.py
@@ -44,7 +44,7 @@ class PLPESELField(RegexField):
super(PLPESELField, self).__init__(r'^\d{11}$',
max_length=None, min_length=None, *args, **kwargs)
- def clean(self,value):
+ def clean(self, value):
super(PLPESELField, self).clean(value)
if value in EMPTY_VALUES:
return u''
@@ -62,6 +62,60 @@ class PLPESELField(RegexField):
result += int(number[i]) * multiple_table[i]
return result % 10 == 0
+class PLNationalIDCardNumberField(RegexField):
+ """
+ A form field that validates as Polish National ID Card Number.
+
+ Checks the following rules:
+ * the length consist of 3 letter and 6 digits
+ * has a valid checksum
+
+ The algorithm is documented at http://en.wikipedia.org/wiki/Polish_identity_card.
+ """
+ default_error_messages = {
+ 'invalid': _(u'National ID Card Number consists of 3 letters and 6 digits.'),
+ 'checksum': _(u'Wrong checksum for the National ID Card Number.'),
+ }
+
+ def __init__(self, *args, **kwargs):
+ super(PLNationalIDCardNumberField, self).__init__(r'^[A-Za-z]{3}\d{6}$',
+ max_length=None, min_length=None, *args, **kwargs)
+
+ def clean(self,value):
+ super(PLNationalIDCardNumberField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+
+ value = value.upper()
+
+ if not self.has_valid_checksum(value):
+ raise ValidationError(self.error_messages['checksum'])
+ return u'%s' % value
+
+ def has_valid_checksum(self, number):
+ """
+ Calculates a checksum with the provided algorithm.
+ """
+ letter_dict = {'A': 10, 'B': 11, 'C': 12, 'D': 13,
+ 'E': 14, 'F': 15, 'G': 16, 'H': 17,
+ 'I': 18, 'J': 19, 'K': 20, 'L': 21,
+ 'M': 22, 'N': 23, 'O': 24, 'P': 25,
+ 'Q': 26, 'R': 27, 'S': 28, 'T': 29,
+ 'U': 30, 'V': 31, 'W': 32, 'X': 33,
+ 'Y': 34, 'Z': 35}
+
+ # convert letters to integer values
+ int_table = [(not c.isdigit()) and letter_dict[c] or int(c)
+ for c in number]
+
+ multiple_table = (7, 3, 1, -1, 7, 3, 1, 7, 3)
+ result = 0
+ for i in range(len(int_table)):
+ result += int_table[i] * multiple_table[i]
+
+ return result % 10 == 0
+
+
class PLNIPField(RegexField):
"""
A form field that validates as Polish Tax Number (NIP).
@@ -158,3 +212,4 @@ class PLPostalCodeField(RegexField):
def __init__(self, *args, **kwargs):
super(PLPostalCodeField, self).__init__(r'^\d{2}-\d{3}$',
max_length=None, min_length=None, *args, **kwargs)
+
diff --git a/docs/ref/contrib/localflavor.txt b/docs/ref/contrib/localflavor.txt
index 59ce7bf3c5..4935382093 100644
--- a/docs/ref/contrib/localflavor.txt
+++ b/docs/ref/contrib/localflavor.txt
@@ -759,6 +759,17 @@ Poland (``pl``)
.. _PESEL: http://en.wikipedia.org/wiki/PESEL
+.. versionadded:: 1.4
+
+.. class:: pl.forms.PLNationalIDCardNumberField
+
+ A form field that validates input as a Polish National ID Card number. The
+ valid format is AAAXXXXXX, where A is letter (A-Z), X is digit and left-most
+ digit is checksum digit. More information about checksum calculation algorithm
+ see `Polish identity card`_.
+
+.. _`Polish identity card`: http://en.wikipedia.org/wiki/Polish_identity_card
+
.. class:: pl.forms.PLREGONField
A form field that validates input as a Polish National Official Business
diff --git a/tests/regressiontests/forms/localflavor/pl.py b/tests/regressiontests/forms/localflavor/pl.py
index 51721f83f8..7225abe87d 100644
--- a/tests/regressiontests/forms/localflavor/pl.py
+++ b/tests/regressiontests/forms/localflavor/pl.py
@@ -1,5 +1,5 @@
from django.contrib.localflavor.pl.forms import (PLProvinceSelect,
- PLCountySelect, PLPostalCodeField, PLNIPField, PLPESELField, PLREGONField)
+ PLCountySelect, PLPostalCodeField, PLNIPField, PLPESELField, PLNationalIDCardNumberField, PLREGONField)
from utils import LocalFlavorTestCase
@@ -26,7 +26,7 @@ class PLLocalFlavorTests(LocalFlavorTestCase):
'''
self.assertEqual(f.render('voivodeships', 'pomerania'), out)
-
+
def test_PLCountrySelect(self):
f = PLCountySelect()
out = u''''''
self.assertEqual(f.render('administrativeunit', 'katowice'), out)
-
+
def test_PLPostalCodeField(self):
error_format = [u'Enter a postal code in the format XX-XXX.']
valid = {
@@ -418,7 +418,7 @@ class PLLocalFlavorTests(LocalFlavorTestCase):
'43--434': error_format,
}
self.assertFieldOutput(PLPostalCodeField, valid, invalid)
-
+
def test_PLNIPField(self):
error_format = [u'Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX.']
error_checksum = [u'Wrong checksum for the Tax Number (NIP).']
@@ -431,7 +431,7 @@ class PLLocalFlavorTests(LocalFlavorTestCase):
'646-241-41-23': error_checksum,
}
self.assertFieldOutput(PLNIPField, valid, invalid)
-
+
def test_PLPESELField(self):
error_checksum = [u'Wrong checksum for the National Identification Number.']
error_format = [u'National Identification Number consists of 11 digits.']
@@ -444,7 +444,22 @@ class PLLocalFlavorTests(LocalFlavorTestCase):
'800716106AA': error_format,
}
self.assertFieldOutput(PLPESELField, valid, invalid)
-
+
+ def test_PLNationalIDCardNumberField(self):
+ error_checksum = [u'Wrong checksum for the National ID Card Number.']
+ error_format = [u'National ID Card Number consists of 3 letters and 6 digits.']
+ valid = {
+ 'ABC123458': 'ABC123458',
+ 'abc123458': 'ABC123458',
+ }
+ invalid = {
+ 'ABC123457': error_checksum,
+ 'abc123457': error_checksum,
+ 'a12Aaaaaa': error_format,
+ 'AA1234443': error_format,
+ }
+ self.assertFieldOutput(PLNationalIDCardNumberField, valid, invalid)
+
def test_PLREGONField(self):
error_checksum = [u'Wrong checksum for the National Business Register Number (REGON).']
error_format = [u'National Business Register Number (REGON) consists of 9 or 14 digits.']
@@ -459,4 +474,3 @@ class PLLocalFlavorTests(LocalFlavorTestCase):
'590096': error_format,
}
self.assertFieldOutput(PLREGONField, valid, invalid)
-