| 21 | class MXPostalCodeField(RegexField): |
| 22 | default_error_messages = { |
| 23 | 'invalid': _('Enter a postal code in the format XXXXX.'), |
| 24 | } |
| 25 | |
| 26 | def __init__(self, *args, **kwargs): |
| 27 | super(MXPostalCodeField, self).__init__(r'^\d{5}$', |
| 28 | min_length=5, max_length=5, |
| 29 | *args, **kwargs) |
| 30 | |
| 31 | class MXRFCField(RegexField): |
| 32 | """ |
| 33 | Registro Federal de Contribuyente. |
| 34 | """ |
| 35 | default_error_messages = { |
| 36 | 'invalid': _('Invalid RFC.'), |
| 37 | } |
| 38 | |
| 39 | def __init__(self, *args, **kwargs): |
| 40 | super(MXRFCField, self).__init__(r'^[A-Z&]{3,4}\d{6}[A-Z\d]{3}$', |
| 41 | min_length=12, max_length=13, |
| 42 | *args, **kwargs) |
| 43 | |
| 44 | def clean(self, value): |
| 45 | value = super(MXRFCField, self).clean(value.upper().strip()) |
| 46 | |
| 47 | if value in EMPTY_VALUES: |
| 48 | return u'' |
| 49 | |
| 50 | if value[-1] != self._checksum(value[:-1]): |
| 51 | raise ValidationError(self.error_messages['invalid']) |
| 52 | |
| 53 | return value |
| 54 | |
| 55 | def _checksum(self, rfc): |
| 56 | chars = '0123456789ABCDEFGHIJKLMN&OPQRSTUVWXYZ-' |
| 57 | |
| 58 | if len(rfc) == 11: |
| 59 | rfc = '-' + rfc |
| 60 | |
| 61 | s = sum(i * chars.index(c) for i, c in zip(reversed(xrange(14)), rfc)) |
| 62 | checksum = 11 - s % 11 |
| 63 | |
| 64 | if checksum == 10: |
| 65 | return 'A' |
| 66 | elif checksum == 11: |
| 67 | return '0' |
| 68 | else: |
| 69 | return str(checksum) |
| 70 | |
| 71 | class MXCURPField(RegexField): |
| 72 | """ |
| 73 | Clave única de registro de población. |
| 74 | """ |
| 75 | default_error_messages = { |
| 76 | 'invalid': _('Invalid CURP.'), |
| 77 | } |
| 78 | |
| 79 | def __init__(self, *args, **kwargs): |
| 80 | super(MXCURPField, self).__init__( |
| 81 | r'^[A-Z]{4}\d{6}[HM][A-Z]{2}[B-DF-HJ-NP-TV-Z]{3}[0-9A-Z]\d$', |
| 82 | min_length=18, max_length=18, *args, **kwargs) |
| 83 | |
| 84 | def clean(self, value): |
| 85 | value = super(MXCURPField, self).clean(value.upper()) |
| 86 | |
| 87 | if value in EMPTY_VALUES: |
| 88 | return u'' |
| 89 | |
| 90 | try: |
| 91 | a = int(value[4:6]) |
| 92 | m = int(value[6:8]) |
| 93 | d = int(value[8:10]) |
| 94 | a += 1900 if value[-2].isdigit() else 2000 |
| 95 | date(a, m, d) |
| 96 | except: |
| 97 | raise ValidationError(self.error_messages['invalid']) |
| 98 | |
| 99 | if value[-1] != self._checksum(value[:-1]): |
| 100 | raise ValidationError(self.error_messages['invalid']) |
| 101 | |
| 102 | return value |
| 103 | |
| 104 | def _checksum(self, curp): |
| 105 | chars = '0123456789ABCDEFGHIJKLMN&OPQRSTUVWXYZ' |
| 106 | |
| 107 | s = sum(i * chars.index(c) for i, c in zip(reversed(xrange(19)), curp)) |
| 108 | checksum = 10 - s % 10 |
| 109 | |
| 110 | if checksum == 10: |
| 111 | return '0' |
| 112 | else: |
| 113 | return str(checksum) |