11"""Colombian-specific form helpers."""
22
33from django .forms .fields import Select
4+ from django .forms import ValidationError
5+ from django .forms .fields import RegexField
6+ from django .core .validators import EMPTY_VALUES
7+ from django .utils .translation import ugettext_lazy as _
48
59from .co_departments import DEPARTMENT_CHOICES
610
@@ -10,3 +14,62 @@ class CODepartmentSelect(Select):
1014
1115 def __init__ (self , attrs = None ):
1216 super (CODepartmentSelect , self ).__init__ (attrs , choices = DEPARTMENT_CHOICES )
17+
18+
19+ class RUTField (RegexField ):
20+ """
21+ This field validates a NIT (NUmero de IdentificaciOn Tributaria). A
22+ NIT is of the form XXXXXXXXXX-V. The last digit is a check digit. Applies
23+ to people and companies.
24+
25+ More info:
26+ http://es.wikipedia.org/wiki/N%C3%BAmero_de_Identificaci%C3%B3n_Tributaria
27+ """
28+ default_error_messages = {
29+ 'invalid' : _ ('Enter a valid RUT in XXXXXXXXXXX-Y or XXXXXXXXXXXY format.' ),
30+ 'checksum' : _ ('Invalid RUT.' ),
31+ }
32+
33+ PRIME_PLACES = [3 , 7 , 13 , 17 , 19 , 23 , 29 , 37 , 41 , 43 , 47 , 53 , 59 , 67 , 71 ]
34+
35+ def __init__ (self , max_length = None , min_length = None , * args , ** kwargs ):
36+ super (RUTField , self ).__init__ (
37+ r'^\d{5,12}-?\d$' ,
38+ max_length ,
39+ min_length ,
40+ * args ,
41+ ** kwargs
42+ )
43+
44+ def clean (self , value ):
45+ """
46+ Value can be either a string in the format XXXXXXXXXX-Y or
47+ XXXXXXXXXXY.
48+ """
49+ value = super (RUTField , self ).clean (value )
50+ if value in EMPTY_VALUES :
51+ return ''
52+ value , cd = self ._canon (value )
53+ if self ._calc_cd (value ) != cd :
54+ raise ValidationError (self .error_messages ['checksum' ])
55+ return self ._format (value , cd )
56+
57+ def _canon (self , nit ):
58+ nit = nit .replace ('-' , '' )
59+ return nit [:- 1 ], nit [- 1 ]
60+
61+ def _calc_cd (self , nit ):
62+ # Calculation code based on:
63+ # http://es.wikipedia.org/wiki/N%C3%BAmero_de_Identificaci%C3%B3n_Tributaria
64+ tmp = sum ([
65+ self .PRIME_PLACES [idx ] * int (value )
66+ for idx , value in enumerate (reversed (nit ))
67+ ]) % 11
68+ if tmp > 1 :
69+ dv = 11 - tmp
70+ else :
71+ dv = 0
72+ return str (dv )
73+
74+ def _format (self , nit , check_digit ):
75+ return '{0}-{1}' .format (nit , check_digit )
0 commit comments