PageRenderTime 13ms CodeModel.GetById 1ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/django/contrib/localflavor/br/forms.py

https://code.google.com/p/mango-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