/django/contrib/localflavor/br/forms.py
Python | 163 lines | 153 code | 6 blank | 4 comment | 3 complexity | 66cba909a1f5af2647ffa23972b6c70d MD5 | raw file
1# -*- coding: utf-8 -*- 2""" 3BR-specific Form helpers 4""" 5 6from django.core.validators import EMPTY_VALUES 7from django.forms import ValidationError 8from django.forms.fields import Field, RegexField, CharField, Select 9from django.utils.encoding import smart_unicode 10from django.utils.translation import ugettext_lazy as _ 11import re 12 13phone_digits_re = re.compile(r'^(\d{2})[-\.]?(\d{4})[-\.]?(\d{4})$') 14 15class BRZipCodeField(RegexField): 16 default_error_messages = { 17 'invalid': _('Enter a zip code in the format XXXXX-XXX.'), 18 } 19 20 def __init__(self, *args, **kwargs): 21 super(BRZipCodeField, self).__init__(r'^\d{5}-\d{3}$', 22 max_length=None, min_length=None, *args, **kwargs) 23 24class BRPhoneNumberField(Field): 25 default_error_messages = { 26 'invalid': _('Phone numbers must be in XX-XXXX-XXXX format.'), 27 } 28 29 def clean(self, value): 30 super(BRPhoneNumberField, self).clean(value) 31 if value in EMPTY_VALUES: 32 return u'' 33 value = re.sub('(\(|\)|\s+)', '', smart_unicode(value)) 34 m = phone_digits_re.search(value) 35 if m: 36 return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3)) 37 raise ValidationError(self.error_messages['invalid']) 38 39class BRStateSelect(Select): 40 """ 41 A Select widget that uses a list of Brazilian states/territories 42 as its choices. 43 """ 44 def __init__(self, attrs=None): 45 from br_states import STATE_CHOICES 46 super(BRStateSelect, self).__init__(attrs, choices=STATE_CHOICES) 47 48class BRStateChoiceField(Field): 49 """ 50 A choice field that uses a list of Brazilian states as its choices. 51 """ 52 widget = Select 53 default_error_messages = { 54 'invalid': _(u'Select a valid brazilian state. That state is not one of the available states.'), 55 } 56 57 def __init__(self, required=True, widget=None, label=None, 58 initial=None, help_text=None): 59 super(BRStateChoiceField, self).__init__(required, widget, label, 60 initial, help_text) 61 from br_states import STATE_CHOICES 62 self.widget.choices = STATE_CHOICES 63 64 def clean(self, value): 65 value = super(BRStateChoiceField, self).clean(value) 66 if value in EMPTY_VALUES: 67 value = u'' 68 value = smart_unicode(value) 69 if value == u'': 70 return value 71 valid_values = set([smart_unicode(k) for k, v in self.widget.choices]) 72 if value not in valid_values: 73 raise ValidationError(self.error_messages['invalid']) 74 return value 75 76def DV_maker(v): 77 if v >= 2: 78 return 11 - v 79 return 0 80 81class BRCPFField(CharField): 82 """ 83 This field validate a CPF number or a CPF string. A CPF number is 84 compounded by XXX.XXX.XXX-VD. The two last digits are check digits. 85 86 More information: 87 http://en.wikipedia.org/wiki/Cadastro_de_Pessoas_F%C3%ADsicas 88 """ 89 default_error_messages = { 90 'invalid': _("Invalid CPF number."), 91 'max_digits': _("This field requires at most 11 digits or 14 characters."), 92 'digits_only': _("This field requires only numbers."), 93 } 94 95 def __init__(self, *args, **kwargs): 96 super(BRCPFField, self).__init__(max_length=14, min_length=11, *args, **kwargs) 97 98 def clean(self, value): 99 """ 100 Value can be either a string in the format XXX.XXX.XXX-XX or an 101 11-digit number. 102 """ 103 value = super(BRCPFField, self).clean(value) 104 if value in EMPTY_VALUES: 105 return u'' 106 orig_value = value[:] 107 if not value.isdigit(): 108 value = re.sub("[-\.]", "", value) 109 try: 110 int(value) 111 except ValueError: 112 raise ValidationError(self.error_messages['digits_only']) 113 if len(value) != 11: 114 raise ValidationError(self.error_messages['max_digits']) 115 orig_dv = value[-2:] 116 117 new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(10, 1, -1))]) 118 new_1dv = DV_maker(new_1dv % 11) 119 value = value[:-2] + str(new_1dv) + value[-1] 120 new_2dv = sum([i * int(value[idx]) for idx, i in enumerate(range(11, 1, -1))]) 121 new_2dv = DV_maker(new_2dv % 11) 122 value = value[:-1] + str(new_2dv) 123 if value[-2:] != orig_dv: 124 raise ValidationError(self.error_messages['invalid']) 125 126 return orig_value 127 128class BRCNPJField(Field): 129 default_error_messages = { 130 'invalid': _("Invalid CNPJ number."), 131 'digits_only': _("This field requires only numbers."), 132 'max_digits': _("This field requires at least 14 digits"), 133 } 134 135 def clean(self, value): 136 """ 137 Value can be either a string in the format XX.XXX.XXX/XXXX-XX or a 138 group of 14 characters. 139 """ 140 value = super(BRCNPJField, self).clean(value) 141 if value in EMPTY_VALUES: 142 return u'' 143 orig_value = value[:] 144 if not value.isdigit(): 145 value = re.sub("[-/\.]", "", value) 146 try: 147 int(value) 148 except ValueError: 149 raise ValidationError(self.error_messages['digits_only']) 150 if len(value) != 14: 151 raise ValidationError(self.error_messages['max_digits']) 152 orig_dv = value[-2:] 153 154 new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(5, 1, -1) + range(9, 1, -1))]) 155 new_1dv = DV_maker(new_1dv % 11) 156 value = value[:-2] + str(new_1dv) + value[-1] 157 new_2dv = sum([i * int(value[idx]) for idx, i in enumerate(range(6, 1, -1) + range(9, 1, -1))]) 158 new_2dv = DV_maker(new_2dv % 11) 159 value = value[:-1] + str(new_2dv) 160 if value[-2:] != orig_dv: 161 raise ValidationError(self.error_messages['invalid']) 162 163 return orig_value