| 1 | """
|
|---|
| 2 | Chile specific form helpers.
|
|---|
| 3 | """
|
|---|
| 4 |
|
|---|
| 5 | from django.forms import ValidationError
|
|---|
| 6 | from django.forms.fields import RegexField, Select, EMPTY_VALUES
|
|---|
| 7 | from django.utils.translation import ugettext_lazy as _
|
|---|
| 8 | from django.utils.encoding import smart_unicode
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 | class CLRegionSelect(Select):
|
|---|
| 12 |
|
|---|
| 13 | def __init__(self, attrs=None):
|
|---|
| 14 | from cl_reg_prov_com import REGION_CHOICES
|
|---|
| 15 | super(CLRegionSelect, self).__init__(attrs, choices=REGION_CHOICES)
|
|---|
| 16 |
|
|---|
| 17 | class CLProvinciaSelect(Select):
|
|---|
| 18 |
|
|---|
| 19 | def __init__(self, attrs=None):
|
|---|
| 20 | from cl_reg_prov_com import PROVINCIA_CHOICES
|
|---|
| 21 | super(CLProvinciaSelect, self).__init__(attrs, choices=PROVINCIA_CHOICES)
|
|---|
| 22 |
|
|---|
| 23 | class CLComunaSelect(Select):
|
|---|
| 24 |
|
|---|
| 25 | def __init__(self, attrs=None):
|
|---|
| 26 | from cl_reg_prov import COMUNA_CHOICES
|
|---|
| 27 | super(CLComunaSelect, self).__init__(attrs, choices=COMUNA_CHOICES)
|
|---|
| 28 |
|
|---|
| 29 | class CLRutField(RegexField):
|
|---|
| 30 | """
|
|---|
| 31 | Chilean "Rol Unico Tributario" (RUT) field. This is the Chilean national
|
|---|
| 32 | identification number.
|
|---|
| 33 |
|
|---|
| 34 | Samples for testing are available from
|
|---|
| 35 | https://palena.sii.cl/cvc/dte/ee_empresas_emisoras.html
|
|---|
| 36 | """
|
|---|
| 37 | default_error_messages = {
|
|---|
| 38 | 'invalid': _('Enter a valid Chilean RUT.'),
|
|---|
| 39 | 'strict': _('Enter a valid Chilean RUT. The format is XX.XXX.XXX-X.'),
|
|---|
| 40 | 'checksum': _('The Chilean RUT is not valid.'),
|
|---|
| 41 | }
|
|---|
| 42 |
|
|---|
| 43 | def __init__(self, *args, **kwargs):
|
|---|
| 44 | if 'strict' in kwargs:
|
|---|
| 45 | del kwargs['strict']
|
|---|
| 46 | super(CLRutField, self).__init__(r'^(\d{1,2}\.)?\d{3}\.\d{3}-[\dkK]$',
|
|---|
| 47 | error_message=self.default_error_messages['strict'], *args, **kwargs)
|
|---|
| 48 | else:
|
|---|
| 49 | # In non-strict mode, accept RUTs that validate but do not exist in
|
|---|
| 50 | # the real world.
|
|---|
| 51 | super(CLRutField, self).__init__(r'^[\d\.]{1,11}-?[\dkK]$', *args, **kwargs)
|
|---|
| 52 |
|
|---|
| 53 | def clean(self, value):
|
|---|
| 54 | """
|
|---|
| 55 | Check and clean the Chilean RUT.
|
|---|
| 56 | """
|
|---|
| 57 | super(CLRutField, self).clean(value)
|
|---|
| 58 | if value in EMPTY_VALUES:
|
|---|
| 59 | return u''
|
|---|
| 60 | rut, verificador = self._canonify(value)
|
|---|
| 61 | if self._algorithm(rut) == verificador:
|
|---|
| 62 | return self._format(rut, verificador)
|
|---|
| 63 | else:
|
|---|
| 64 | raise ValidationError(self.error_messages['checksum'])
|
|---|
| 65 |
|
|---|
| 66 | def _algorithm(self, rut):
|
|---|
| 67 | """
|
|---|
| 68 | Takes RUT in pure canonical form, calculates the verifier digit.
|
|---|
| 69 | """
|
|---|
| 70 | suma = 0
|
|---|
| 71 | multi = 2
|
|---|
| 72 | for r in rut[::-1]:
|
|---|
| 73 | suma += int(r) * multi
|
|---|
| 74 | multi += 1
|
|---|
| 75 | if multi == 8:
|
|---|
| 76 | multi = 2
|
|---|
| 77 | return u'0123456789K0'[11 - suma % 11]
|
|---|
| 78 |
|
|---|
| 79 | def _canonify(self, rut):
|
|---|
| 80 | """
|
|---|
| 81 | Turns the RUT into one normalized format. Returns a (rut, verifier)
|
|---|
| 82 | tuple.
|
|---|
| 83 | """
|
|---|
| 84 | rut = smart_unicode(rut).replace(' ', '').replace('.', '').replace('-', '')
|
|---|
| 85 | return rut[:-1], rut[-1]
|
|---|
| 86 |
|
|---|
| 87 | def _format(self, code, verifier=None):
|
|---|
| 88 | """
|
|---|
| 89 | Formats the RUT from canonical form to the common string representation.
|
|---|
| 90 | If verifier=None, then the last digit in 'code' is the verifier.
|
|---|
| 91 | """
|
|---|
| 92 | if verifier is None:
|
|---|
| 93 | verifier = code[-1]
|
|---|
| 94 | code = code[:-1]
|
|---|
| 95 | while len(code) > 3 and '.' not in code[:3]:
|
|---|
| 96 | pos = code.find('.')
|
|---|
| 97 | if pos == -1:
|
|---|
| 98 | new_dot = -3
|
|---|
| 99 | else:
|
|---|
| 100 | new_dot = pos - 3
|
|---|
| 101 | code = code[:new_dot] + '.' + code[new_dot:]
|
|---|
| 102 | return u'%s-%s' % (code, verifier)
|
|---|