1"""Spain."""
2
3# standard
4from typing import Dict
5
6# local
7from validators.utils import validator
8
9
10def _nif_nie_validation(value: str, number_by_letter: Dict[str, str]):
11 """Validate if the doi is a NIF or a NIE."""
12 if len(value) != 9:
13 return False
14 value = value.upper()
15 table = "TRWAGMYFPDXBNJZSQVHLCKE"
16 # If it is not a DNI, convert the first
17 # letter to the corresponding digit
18 numbers = number_by_letter.get(value[0], value[0]) + value[1:8]
19 # doi[8] is control
20 return numbers.isdigit() and value[8] == table[int(numbers) % 23]
21
22
23@validator
24def es_cif(value: str, /):
25 """Validate a Spanish CIF.
26
27 Each company in Spain prior to 2008 had a distinct CIF and has been
28 discontinued. For more information see [wikipedia.org/cif][1].
29
30 The new replacement is to use NIF for absolutely everything. The issue is
31 that there are "types" of NIFs now: company, person [citizen or resident]
32 all distinguished by the first character of the DOI. For this reason we
33 will continue to call CIFs NIFs, that are used for companies.
34
35 This validator is based on [generadordni.es][2].
36
37 [1]: https://es.wikipedia.org/wiki/C%C3%B3digo_de_identificaci%C3%B3n_fiscal
38 [2]: https://generadordni.es/
39
40 Examples:
41 >>> es_cif('B25162520')
42 True
43 >>> es_cif('B25162529')
44 ValidationError(func=es_cif, args={'value': 'B25162529'})
45
46 Args:
47 value:
48 DOI string which is to be validated.
49
50 Returns:
51 (Literal[True]): If `value` is a valid DOI string.
52 (ValidationError): If `value` is an invalid DOI string.
53 """
54 if not value or len(value) != 9:
55 return False
56 value = value.upper()
57 table = "JABCDEFGHI"
58 first_chr = value[0]
59 doi_body = value[1:8]
60 control = value[8]
61 if not doi_body.isdigit():
62 return False
63 res = (
64 10
65 - sum(
66 # Multiply each positionally even doi
67 # digit by 2 and sum it all together
68 sum(map(int, str(int(char) * 2))) if index % 2 == 0 else int(char)
69 for index, char in enumerate(doi_body)
70 )
71 % 10
72 ) % 10
73 if first_chr in "ABEH": # Number type
74 return str(res) == control
75 if first_chr in "PSQW": # Letter type
76 return table[res] == control
77 return control in {str(res), table[res]} if first_chr in "CDFGJNRUV" else False
78
79
80@validator
81def es_nif(value: str, /):
82 """Validate a Spanish NIF.
83
84 Each entity, be it person or company in Spain has a distinct NIF. Since
85 we've designated CIF to be a company NIF, this NIF is only for person.
86 For more information see [wikipedia.org/nif][1]. This validator
87 is based on [generadordni.es][2].
88
89 [1]: https://es.wikipedia.org/wiki/N%C3%BAmero_de_identificaci%C3%B3n_fiscal
90 [2]: https://generadordni.es/
91
92 Examples:
93 >>> es_nif('26643189N')
94 True
95 >>> es_nif('26643189X')
96 ValidationError(func=es_nif, args={'value': '26643189X'})
97
98 Args:
99 value:
100 DOI string which is to be validated.
101
102 Returns:
103 (Literal[True]): If `value` is a valid DOI string.
104 (ValidationError): If `value` is an invalid DOI string.
105 """
106 number_by_letter = {"L": "0", "M": "0", "K": "0"}
107 return _nif_nie_validation(value, number_by_letter)
108
109
110@validator
111def es_nie(value: str, /):
112 """Validate a Spanish NIE.
113
114 The NIE is a tax identification number in Spain, known in Spanish
115 as the NIE, or more formally the Número de identidad de extranjero.
116 For more information see [wikipedia.org/nie][1]. This validator
117 is based on [generadordni.es][2].
118
119 [1]: https://es.wikipedia.org/wiki/N%C3%BAmero_de_identidad_de_extranjero
120 [2]: https://generadordni.es/
121
122 Examples:
123 >>> es_nie('X0095892M')
124 True
125 >>> es_nie('X0095892X')
126 ValidationError(func=es_nie, args={'value': 'X0095892X'})
127
128 Args:
129 value:
130 DOI string which is to be validated.
131
132 Returns:
133 (Literal[True]): If `value` is a valid DOI string.
134 (ValidationError): If `value` is an invalid DOI string.
135 """
136 number_by_letter = {"X": "0", "Y": "1", "Z": "2"}
137 # NIE must must start with X Y or Z
138 if value and value[0] in number_by_letter:
139 return _nif_nie_validation(value, number_by_letter)
140 return False
141
142
143@validator
144def es_doi(value: str, /):
145 """Validate a Spanish DOI.
146
147 A DOI in spain is all NIF / CIF / NIE / DNI -- a digital ID.
148 For more information see [wikipedia.org/doi][1]. This validator
149 is based on [generadordni.es][2].
150
151 [1]: https://es.wikipedia.org/wiki/Identificador_de_objeto_digital
152 [2]: https://generadordni.es/
153
154 Examples:
155 >>> es_doi('X0095892M')
156 True
157 >>> es_doi('X0095892X')
158 ValidationError(func=es_doi, args={'value': 'X0095892X'})
159
160 Args:
161 value:
162 DOI string which is to be validated.
163
164 Returns:
165 (Literal[True]): If `value` is a valid DOI string.
166 (ValidationError): If `value` is an invalid DOI string.
167 """
168 return es_nie(value) or es_nif(value) or es_cif(value)