| 1 | # -*- coding: utf-8 -*-
|
|---|
| 2 | """
|
|---|
| 3 | VE-specific Form helpers.
|
|---|
| 4 | """
|
|---|
| 5 |
|
|---|
| 6 | from django.forms import ValidationError
|
|---|
| 7 | from django.forms.fields import RegexField, CharField, Select, EMPTY_VALUES
|
|---|
| 8 | from django.utils.encoding import smart_unicode
|
|---|
| 9 | from django.utils.translation import ugettext_lazy as _
|
|---|
| 10 |
|
|---|
| 11 | class VEStateSelect(Select):
|
|---|
| 12 | """
|
|---|
| 13 | A Select widget that uses a list of Venezuelan States
|
|---|
| 14 | as its choices.
|
|---|
| 15 | """
|
|---|
| 16 | def __init__(self, attrs=None):
|
|---|
| 17 | from ve_states import STATE_CHOICES
|
|---|
| 18 | super(VEStateSelect, self).__init__(attrs, choices=STATE_CHOICES)
|
|---|
| 19 |
|
|---|
| 20 | class VEPostalCodeField(RegexField):
|
|---|
| 21 | """
|
|---|
| 22 | A field that accepts a 'classic' XXXX Postal Code
|
|---|
| 23 | """
|
|---|
| 24 | default_error_messages = {
|
|---|
| 25 | 'invalid': _("Enter a postal code in the format XXXX."),
|
|---|
| 26 | }
|
|---|
| 27 |
|
|---|
| 28 | def __init__(self, *args, **kwargs):
|
|---|
| 29 | super(ARPostalCodeField, self).__init__(r'^\d{4}$',
|
|---|
| 30 | min_length=4, max_length=4, *args, **kwargs)
|
|---|
| 31 |
|
|---|
| 32 | def clean(self, value):
|
|---|
| 33 | value = super(VEPostalCodeField, self).clean(value)
|
|---|
| 34 | if value in EMPTY_VALUES:
|
|---|
| 35 | return u''
|
|---|
| 36 | if len(value) not in (4, 8):
|
|---|
| 37 | raise ValidationError(self.error_messages['invalid'])
|
|---|
| 38 | if len(value) == 4:
|
|---|
| 39 | return u'%s' % (value)
|
|---|
| 40 | return value
|
|---|
| 41 |
|
|---|
| 42 | class CIField(CharField):
|
|---|
| 43 | """
|
|---|
| 44 | A field that validates 'Cédula de Identidad' (CI) numbers.
|
|---|
| 45 | """
|
|---|
| 46 | default_error_messages = {
|
|---|
| 47 | 'invalid': _("This field requires only numbers."),
|
|---|
| 48 | 'max_digits': _("This field requires 7 or 8 digits."),
|
|---|
| 49 | }
|
|---|
| 50 |
|
|---|
| 51 | def __init__(self, *args, **kwargs):
|
|---|
| 52 | super(CIField, self).__init__(max_length=10, min_length=7, *args,
|
|---|
| 53 | **kwargs)
|
|---|
| 54 |
|
|---|
| 55 | def clean(self, value):
|
|---|
| 56 | """
|
|---|
| 57 | Value can be a string either in the [X]X.XXX.XXX or [X]XXXXXXX formats.
|
|---|
| 58 | """
|
|---|
| 59 | value = super(CIField, self).clean(value)
|
|---|
| 60 | if value in EMPTY_VALUES:
|
|---|
| 61 | return u''
|
|---|
| 62 | if not value.isdigit():
|
|---|
| 63 | value = value.replace('.', '')
|
|---|
| 64 | if not value.isdigit():
|
|---|
| 65 | raise ValidationError(self.error_messages['invalid'])
|
|---|
| 66 | if len(value) not in (7, 8):
|
|---|
| 67 | raise ValidationError(self.error_messages['max_digits'])
|
|---|
| 68 |
|
|---|
| 69 | return value
|
|---|
| 70 |
|
|---|
| 71 | class RIFField(RegexField):
|
|---|
| 72 | """
|
|---|
| 73 | This field validates a RIF (Registro de Información Fiscal). A
|
|---|
| 74 | RIF is of the form A-XXXXXXXX-X. The last digit is a check digit.
|
|---|
| 75 | """
|
|---|
| 76 | rif_first_char = {
|
|---|
| 77 | 'v':'1',
|
|---|
| 78 | 'e':'2',
|
|---|
| 79 | 'j':'3',
|
|---|
| 80 | 'p':'4',
|
|---|
| 81 | 'g':'5',
|
|---|
| 82 | }
|
|---|
| 83 | default_error_messages = {
|
|---|
| 84 | 'invalid': _('Enter a valid RIF in A-XXXXXXXX-X or AXXXXXXXXX format.'),
|
|---|
| 85 | 'checksum': _("Invalid RIF."),
|
|---|
| 86 | }
|
|---|
| 87 |
|
|---|
| 88 | def __init__(self, *args, **kwargs):
|
|---|
| 89 | super(RIFField, self).__init__(r'^[jJ|vV|eE]-\d{8}-\d$',*args, **kwargs)
|
|---|
| 90 |
|
|---|
| 91 | def clean(self, value):
|
|---|
| 92 | """Value can be either a string in A-XXXXXXXX-X or AXXXXXXXXX format"""
|
|---|
| 93 | value = super(RIFField, self).clean(value)
|
|---|
| 94 | if value in EMPTY_VALUES:
|
|---|
| 95 | return u''
|
|---|
| 96 | value, cd = self._canon(value)
|
|---|
| 97 | if self._calc_cd(value) != cd:
|
|---|
| 98 | raise ValidationError(self.error_messages['checksum'])
|
|---|
| 99 | return self._format(value, cd)
|
|---|
| 100 |
|
|---|
| 101 | def _canon(self, rif):
|
|---|
| 102 | rif = rif.replace('-', '')
|
|---|
| 103 | if rif[0].lower() in self.rif_first_char:
|
|---|
| 104 | rif = rif.replace(rif[0], self.rif_first_char[rif[0]])
|
|---|
| 105 | else:
|
|---|
| 106 | raise ValidationError(self.error_messages['invalid'])
|
|---|
| 107 | return rif[:-1], rif[-1]
|
|---|
| 108 |
|
|---|
| 109 | def _calc_cd(self, rif):
|
|---|
| 110 | mults = (4, 3, 2, 7, 6, 5, 4, 3, 2)
|
|---|
| 111 | tmp = sum([m * int(rif[idx]) for idx, m in enumerate(mults)])
|
|---|
| 112 | return str(11 - tmp % 11)
|
|---|
| 113 |
|
|---|
| 114 | def _format(self, rif, check_digit=None):
|
|---|
| 115 | if check_digit == None:
|
|---|
| 116 | check_digit = rif[-1]
|
|---|
| 117 | rif = rif[:-1]
|
|---|
| 118 | return u'%s%s%s' % (rif[:2], rif[2:], check_digit)
|
|---|