/django/contrib/localflavor/ar/forms.py

https://code.google.com/p/mango-py/ · Python · 115 lines · 103 code · 3 blank · 9 comment · 1 complexity · 00cea5c9a0db277a9c7d3ab36a0d4172 MD5 · raw file

  1. # -*- coding: utf-8 -*-
  2. """
  3. AR-specific Form helpers.
  4. """
  5. from django.forms import ValidationError
  6. from django.core.validators import EMPTY_VALUES
  7. from django.forms.fields import RegexField, CharField, Select
  8. from django.utils.encoding import smart_unicode
  9. from django.utils.translation import ugettext_lazy as _
  10. class ARProvinceSelect(Select):
  11. """
  12. A Select widget that uses a list of Argentinean provinces/autonomous cities
  13. as its choices.
  14. """
  15. def __init__(self, attrs=None):
  16. from ar_provinces import PROVINCE_CHOICES
  17. super(ARProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)
  18. class ARPostalCodeField(RegexField):
  19. """
  20. A field that accepts a 'classic' NNNN Postal Code or a CPA.
  21. See http://www.correoargentino.com.ar/consulta_cpa/home.php
  22. """
  23. default_error_messages = {
  24. 'invalid': _("Enter a postal code in the format NNNN or ANNNNAAA."),
  25. }
  26. def __init__(self, *args, **kwargs):
  27. super(ARPostalCodeField, self).__init__(r'^\d{4}$|^[A-HJ-NP-Za-hj-np-z]\d{4}\D{3}$',
  28. min_length=4, max_length=8, *args, **kwargs)
  29. def clean(self, value):
  30. value = super(ARPostalCodeField, self).clean(value)
  31. if value in EMPTY_VALUES:
  32. return u''
  33. if len(value) not in (4, 8):
  34. raise ValidationError(self.error_messages['invalid'])
  35. if len(value) == 8:
  36. return u'%s%s%s' % (value[0].upper(), value[1:5], value[5:].upper())
  37. return value
  38. class ARDNIField(CharField):
  39. """
  40. A field that validates 'Documento Nacional de Identidad' (DNI) numbers.
  41. """
  42. default_error_messages = {
  43. 'invalid': _("This field requires only numbers."),
  44. 'max_digits': _("This field requires 7 or 8 digits."),
  45. }
  46. def __init__(self, *args, **kwargs):
  47. super(ARDNIField, self).__init__(max_length=10, min_length=7, *args,
  48. **kwargs)
  49. def clean(self, value):
  50. """
  51. Value can be a string either in the [X]X.XXX.XXX or [X]XXXXXXX formats.
  52. """
  53. value = super(ARDNIField, self).clean(value)
  54. if value in EMPTY_VALUES:
  55. return u''
  56. if not value.isdigit():
  57. value = value.replace('.', '')
  58. if not value.isdigit():
  59. raise ValidationError(self.error_messages['invalid'])
  60. if len(value) not in (7, 8):
  61. raise ValidationError(self.error_messages['max_digits'])
  62. return value
  63. class ARCUITField(RegexField):
  64. """
  65. This field validates a CUIT (Código Único de Identificación Tributaria). A
  66. CUIT is of the form XX-XXXXXXXX-V. The last digit is a check digit.
  67. """
  68. default_error_messages = {
  69. 'invalid': _('Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'),
  70. 'checksum': _("Invalid CUIT."),
  71. }
  72. def __init__(self, *args, **kwargs):
  73. super(ARCUITField, self).__init__(r'^\d{2}-?\d{8}-?\d$',
  74. *args, **kwargs)
  75. def clean(self, value):
  76. """
  77. Value can be either a string in the format XX-XXXXXXXX-X or an
  78. 11-digit number.
  79. """
  80. value = super(ARCUITField, self).clean(value)
  81. if value in EMPTY_VALUES:
  82. return u''
  83. value, cd = self._canon(value)
  84. if self._calc_cd(value) != cd:
  85. raise ValidationError(self.error_messages['checksum'])
  86. return self._format(value, cd)
  87. def _canon(self, cuit):
  88. cuit = cuit.replace('-', '')
  89. return cuit[:-1], cuit[-1]
  90. def _calc_cd(self, cuit):
  91. mults = (5, 4, 3, 2, 7, 6, 5, 4, 3, 2)
  92. tmp = sum([m * int(cuit[idx]) for idx, m in enumerate(mults)])
  93. return str(11 - tmp % 11)
  94. def _format(self, cuit, check_digit=None):
  95. if check_digit == None:
  96. check_digit = cuit[-1]
  97. cuit = cuit[:-1]
  98. return u'%s-%s-%s' % (cuit[:2], cuit[2:], check_digit)