123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- # -*- coding: utf-8 -*-
- """
- BR-specific Form helpers
- """
- from __future__ import absolute_import, unicode_literals
- import re
- from django.contrib.localflavor.br.br_states import STATE_CHOICES
- from django.core.validators import EMPTY_VALUES
- from django.forms import ValidationError
- from django.forms.fields import Field, RegexField, CharField, Select
- from django.utils.encoding import smart_unicode
- from django.utils.translation import ugettext_lazy as _
- phone_digits_re = re.compile(r'^(\d{2})[-\.]?(\d{4})[-\.]?(\d{4})$')
- class BRZipCodeField(RegexField):
- default_error_messages = {
- 'invalid': _('Enter a zip code in the format XXXXX-XXX.'),
- }
- def __init__(self, max_length=None, min_length=None, *args, **kwargs):
- super(BRZipCodeField, self).__init__(r'^\d{5}-\d{3}$',
- max_length, min_length, *args, **kwargs)
- class BRPhoneNumberField(Field):
- default_error_messages = {
- 'invalid': _('Phone numbers must be in XX-XXXX-XXXX format.'),
- }
- def clean(self, value):
- super(BRPhoneNumberField, self).clean(value)
- if value in EMPTY_VALUES:
- return ''
- value = re.sub('(\(|\)|\s+)', '', smart_unicode(value))
- m = phone_digits_re.search(value)
- if m:
- return '%s-%s-%s' % (m.group(1), m.group(2), m.group(3))
- raise ValidationError(self.error_messages['invalid'])
- class BRStateSelect(Select):
- """
- A Select widget that uses a list of Brazilian states/territories
- as its choices.
- """
- def __init__(self, attrs=None):
- super(BRStateSelect, self).__init__(attrs, choices=STATE_CHOICES)
- class BRStateChoiceField(Field):
- """
- A choice field that uses a list of Brazilian states as its choices.
- """
- widget = Select
- default_error_messages = {
- 'invalid': _('Select a valid brazilian state. That state is not one of the available states.'),
- }
- def __init__(self, required=True, widget=None, label=None,
- initial=None, help_text=None):
- super(BRStateChoiceField, self).__init__(required, widget, label,
- initial, help_text)
- self.widget.choices = STATE_CHOICES
- def clean(self, value):
- value = super(BRStateChoiceField, self).clean(value)
- if value in EMPTY_VALUES:
- value = ''
- value = smart_unicode(value)
- if value == '':
- return value
- valid_values = set([smart_unicode(k) for k, v in self.widget.choices])
- if value not in valid_values:
- raise ValidationError(self.error_messages['invalid'])
- return value
- def DV_maker(v):
- if v >= 2:
- return 11 - v
- return 0
- class BRCPFField(CharField):
- """
- This field validate a CPF number or a CPF string. A CPF number is
- compounded by XXX.XXX.XXX-VD. The two last digits are check digits.
- More information:
- http://en.wikipedia.org/wiki/Cadastro_de_Pessoas_F%C3%ADsicas
- """
- default_error_messages = {
- 'invalid': _("Invalid CPF number."),
- 'max_digits': _("This field requires at most 11 digits or 14 characters."),
- 'digits_only': _("This field requires only numbers."),
- }
- def __init__(self, max_length=14, min_length=11, *args, **kwargs):
- super(BRCPFField, self).__init__(max_length, min_length, *args, **kwargs)
- def clean(self, value):
- """
- Value can be either a string in the format XXX.XXX.XXX-XX or an
- 11-digit number.
- """
- value = super(BRCPFField, self).clean(value)
- if value in EMPTY_VALUES:
- return ''
- orig_value = value[:]
- if not value.isdigit():
- value = re.sub("[-\.]", "", value)
- try:
- int(value)
- except ValueError:
- raise ValidationError(self.error_messages['digits_only'])
- if len(value) != 11:
- raise ValidationError(self.error_messages['max_digits'])
- orig_dv = value[-2:]
- new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(10, 1, -1))])
- new_1dv = DV_maker(new_1dv % 11)
- value = value[:-2] + str(new_1dv) + value[-1]
- new_2dv = sum([i * int(value[idx]) for idx, i in enumerate(range(11, 1, -1))])
- new_2dv = DV_maker(new_2dv % 11)
- value = value[:-1] + str(new_2dv)
- if value[-2:] != orig_dv:
- raise ValidationError(self.error_messages['invalid'])
- return orig_value
- class BRCNPJField(Field):
- default_error_messages = {
- 'invalid': _("Invalid CNPJ number."),
- 'digits_only': _("This field requires only numbers."),
- 'max_digits': _("This field requires at least 14 digits"),
- }
- def clean(self, value):
- """
- Value can be either a string in the format XX.XXX.XXX/XXXX-XX or a
- group of 14 characters.
- """
- value = super(BRCNPJField, self).clean(value)
- if value in EMPTY_VALUES:
- return ''
- orig_value = value[:]
- if not value.isdigit():
- value = re.sub("[-/\.]", "", value)
- try:
- int(value)
- except ValueError:
- raise ValidationError(self.error_messages['digits_only'])
- if len(value) != 14:
- raise ValidationError(self.error_messages['max_digits'])
- orig_dv = value[-2:]
- new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(5, 1, -1) + range(9, 1, -1))])
- new_1dv = DV_maker(new_1dv % 11)
- value = value[:-2] + str(new_1dv) + value[-1]
- new_2dv = sum([i * int(value[idx]) for idx, i in enumerate(range(6, 1, -1) + range(9, 1, -1))])
- new_2dv = DV_maker(new_2dv % 11)
- value = value[:-1] + str(new_2dv)
- if value[-2:] != orig_dv:
- raise ValidationError(self.error_messages['invalid'])
- return orig_value
|