| | 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) |