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