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

https://code.google.com/p/mango-py/ · Python · 163 lines · 117 code · 20 blank · 26 comment · 23 complexity · 66cba909a1f5af2647ffa23972b6c70d MD5 · raw file

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