Skip to content

Commit a1fdd5d

Browse files
unhoarthurdejong
authored andcommitted
Add support for Azerbaijan TIN
Thanks to Adam Handke for finding the weights for the check digit algorithm. Closes #200 CLoses #329
1 parent e741318 commit a1fdd5d

File tree

3 files changed

+371
-0
lines changed

3 files changed

+371
-0
lines changed

stdnum/az/__init__.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# __init__.py - collection of Azerbaijan numbers
2+
# coding: utf-8
3+
#
4+
# Copyright (C) 2022 Leandro Regueiro
5+
#
6+
# This library is free software; you can redistribute it and/or
7+
# modify it under the terms of the GNU Lesser General Public
8+
# License as published by the Free Software Foundation; either
9+
# version 2.1 of the License, or (at your option) any later version.
10+
#
11+
# This library is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
# Lesser General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU Lesser General Public
17+
# License along with this library; if not, write to the Free Software
18+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19+
# 02110-1301 USA
20+
21+
"""Collection of Azerbaijan numbers."""
22+
23+
from __future__ import annotations
24+
25+
# provide aliases
26+
from stdnum.az import voen as vat # noqa: F401

stdnum/az/voen.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# voen.py - functions for handling Azerbaijan VOEN numbers
2+
# coding: utf-8
3+
#
4+
# Copyright (C) 2022 Leandro Regueiro
5+
#
6+
# This library is free software; you can redistribute it and/or
7+
# modify it under the terms of the GNU Lesser General Public
8+
# License as published by the Free Software Foundation; either
9+
# version 2.1 of the License, or (at your option) any later version.
10+
#
11+
# This library is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
# Lesser General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU Lesser General Public
17+
# License along with this library; if not, write to the Free Software
18+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19+
# 02110-1301 USA
20+
21+
"""VÖEN (Vergi ödəyicisinin eyniləşdirmə nömrəsi, Azerbaijan tax number).
22+
23+
The Vergi ödəyicisinin eyniləşdirmə nömrəsi is issued by the Azerbaijan state
24+
tax authorities to individuals and legal entities.
25+
26+
This number consists of 10 digits. The first two digits are the code for the
27+
territorial administrative unit. The following six digits are a serial
28+
number. The ninth digit is a check digit. The tenth digit represents the
29+
legal status of a taxpayer: 1 for legal persons and 2 for natural persons.
30+
31+
More information:
32+
33+
* https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Azerbaijan-TIN.pdf
34+
* https://www.e-taxes.gov.az/ebyn/payerOrVoenChecker.jsp
35+
36+
>>> validate('140 155 5071')
37+
'1401555071'
38+
>>> validate('140 155 5081')
39+
Traceback (most recent call last):
40+
...
41+
InvalidChecksum: ...
42+
>>> validate('1400057424')
43+
Traceback (most recent call last):
44+
...
45+
InvalidComponent: ...
46+
""" # noqa: E501
47+
48+
from __future__ import annotations
49+
50+
from stdnum.exceptions import *
51+
from stdnum.util import clean, isdigits
52+
53+
54+
def compact(number: str) -> str:
55+
"""Convert the number to the minimal representation.
56+
57+
This strips the number of any valid separators and removes surrounding
58+
whitespace.
59+
"""
60+
number = clean(number, ' ')
61+
if len(number) == 9:
62+
number = '0' + number
63+
return number
64+
65+
66+
def _calc_check_digit(number: str) -> str:
67+
"""Calculate the check digit for the VÖEN."""
68+
weights = [4, 1, 8, 6, 2, 7, 5, 3]
69+
return str(sum(w * int(n) for w, n in zip(weights, number)) % 11)
70+
71+
72+
def validate(number: str) -> str:
73+
"""Check if the number is a valid Azerbaijan VÖEN number."""
74+
number = compact(number)
75+
if len(number) != 10:
76+
raise InvalidLength()
77+
if not isdigits(number):
78+
raise InvalidFormat()
79+
if number[-1] not in ('1', '2'):
80+
raise InvalidComponent()
81+
if number[-2:-1] != _calc_check_digit(number):
82+
raise InvalidChecksum()
83+
return number
84+
85+
86+
def is_valid(number: str) -> bool:
87+
"""Check if the number is a valid Azerbaijan VÖEN number."""
88+
try:
89+
return bool(validate(number))
90+
except ValidationError:
91+
return False

tests/test_az_voen.doctest

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
test_az_voen.doctest - more detailed doctests for stdnum.az.voen module
2+
3+
Copyright (C) 2022 Leandro Regueiro
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18+
02110-1301 USA
19+
20+
21+
This file contains more detailed doctests for the stdnum.az.voen module. It
22+
tries to test more corner cases and detailed functionality that is not really
23+
useful as module documentation.
24+
25+
>>> from stdnum.az import voen
26+
27+
28+
Tests for some corner cases.
29+
30+
>>> voen.validate('1400057421')
31+
'1400057421'
32+
>>> voen.validate('140 155 5071')
33+
'1401555071'
34+
>>> voen.validate('12345')
35+
Traceback (most recent call last):
36+
...
37+
InvalidLength: ...
38+
>>> voen.validate('ZZ00057421')
39+
Traceback (most recent call last):
40+
...
41+
InvalidFormat: ...
42+
>>> voen.validate('1400057424')
43+
Traceback (most recent call last):
44+
...
45+
InvalidComponent: ...
46+
47+
48+
Nine digit numbers get turned into 10 digits because a leading 0 is sometimes
49+
left out.
50+
51+
>>> voen.compact('300725012')
52+
'0300725012'
53+
>>> voen.validate('300725012')
54+
'0300725012'
55+
56+
57+
These have been found online and should all be valid numbers.
58+
59+
>>> numbers = '''
60+
...
61+
... 0200432771
62+
... 0400198812
63+
... 0600192862
64+
... 0800000512
65+
... 0900062472
66+
... 1000276532
67+
... 1002031802
68+
... 1002193082
69+
... 1002632171
70+
... 1002900962
71+
... 1003160261
72+
... 1003337762
73+
... 1005196511
74+
... 1005643421
75+
... 1005837482
76+
... 1005840682
77+
... 1101062081
78+
... 1200941152
79+
... 1202174962
80+
... 1300182372
81+
... 1301478392
82+
... 1302363462
83+
... 1303029632
84+
... 1303392331
85+
... 1303776231
86+
... 1303959432
87+
... 1304429532
88+
... 1305161112
89+
... 1305777442
90+
... 1306144182
91+
... 1306379412
92+
... 1400138202
93+
... 1401146511
94+
... 1401598061
95+
... 1403147441
96+
... 1403149281
97+
... 1403424841
98+
... 1403575391
99+
... 1404034501
100+
... 1404363351
101+
... 1404678141
102+
... 1501238982
103+
... 1501398032
104+
... 1502964842
105+
... 1503660862
106+
... 1503706462
107+
... 1503883601
108+
... 1503929742
109+
... 1504014182
110+
... 1504695681
111+
... 1601293792
112+
... 1602158822
113+
... 1602379421
114+
... 1603117061
115+
... 1603390091
116+
... 1700548702
117+
... 1701338762
118+
... 1701423382
119+
... 1701825192
120+
... 1702640641
121+
... 1801735962
122+
... 1803381652
123+
... 1803742052
124+
... 1803964261
125+
... 1804578291
126+
... 1900156291
127+
... 1900666092
128+
... 1901679992
129+
... 1902783672
130+
... 1903742192
131+
... 2001344972
132+
... 2001938462
133+
... 2002612202
134+
... 2003266571
135+
... 2003583512
136+
... 2004402192
137+
... 2005090691
138+
... 2005913681
139+
... 200722502
140+
... 2100223801
141+
... 2100710642
142+
... 2201511592
143+
... 2300195562
144+
... 2300216922
145+
... 2300295452
146+
... 2304643692
147+
... 2600559872
148+
... 2600774242
149+
... 2601337332
150+
... 2602941392
151+
... 2700093202
152+
... 2700300252
153+
... 2700947102
154+
... 2800038132
155+
... 2900398871
156+
... 2903227542
157+
... 2903472571
158+
... 3000740252
159+
... 300293922
160+
... 3100775152
161+
... 3100922132
162+
... 3101355922
163+
... 3101945282
164+
... 3102821612
165+
... 3200741062
166+
... 3200777152
167+
... 3300511732
168+
... 3400435452
169+
... 3500865702
170+
... 3600093232
171+
... 3700292512
172+
... 3700362132
173+
... 3800115252
174+
... 3800140902
175+
... 3900596722
176+
... 4001130832
177+
... 400198382
178+
... 4100217642
179+
... 4101419902
180+
... 4200287721
181+
... 4200811431
182+
... 4300343432
183+
... 4300573181
184+
... 4401208732
185+
... 4500078262
186+
... 4501381622
187+
... 4600238532
188+
... 4700899992
189+
... 4800213191
190+
... 4900279562
191+
... 5000136892
192+
... 500027792
193+
... 5100109431
194+
... 5100186382
195+
... 5200103342
196+
... 5300164942
197+
... 5400520582
198+
... 5400932482
199+
... 5500638121
200+
... 5600095582
201+
... 5700281172
202+
... 5700680892
203+
... 5800143892
204+
... 5900080492
205+
... 6000440592
206+
... 6000474432
207+
... 600495162
208+
... 6100192552
209+
... 6100585341
210+
... 6101051612
211+
... 6200094532
212+
... 6300004442
213+
... 6300030322
214+
... 6400005112
215+
... 6402002901
216+
... 6500021732
217+
... 6500262492
218+
... 6600304962
219+
... 6701087912
220+
... 6800062912
221+
... 6800100962
222+
... 6900587432
223+
... 7000419952
224+
... 700516122
225+
... 7100015622
226+
... 7200649742
227+
... 7300403792
228+
... 7400146452
229+
... 7400216762
230+
... 7500537672
231+
... 7600071671
232+
... 7600205102
233+
... 7700749702
234+
... 7701086342
235+
... 7701233582
236+
... 7800009861
237+
... 7900062142
238+
... 8000051472
239+
... 800044702
240+
... 800047872
241+
... 8100182712
242+
... 8200088092
243+
... 8300356482
244+
... 8300737062
245+
... 8400248092
246+
... 8500094382
247+
... 8600050502
248+
... 8700029382
249+
... 900215552
250+
... 9900051231
251+
...
252+
... '''
253+
>>> [x for x in numbers.splitlines() if x and not voen.is_valid(x)]
254+
[]

0 commit comments

Comments
 (0)