Skip to content

Commit 974c3d6

Browse files
committed
Add support for Pakistan TIN
Fixes #211.
1 parent e40c827 commit 974c3d6

File tree

3 files changed

+364
-0
lines changed

3 files changed

+364
-0
lines changed

stdnum/pk/__init__.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# __init__.py - collection of Pakistan 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 Pakistan numbers."""
22+
23+
# provide aliases
24+
from stdnum.pk import ntn as vat # noqa: F401

stdnum/pk/ntn.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# ntn.py - functions for handling Pakistan NTN 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+
"""NTN (National Tax Number, نیشنل ٹیکس نمبر, Pakistan tax number).
22+
23+
This number consists of 8 digits, the last being a check digit, usually
24+
separated by a hyphen like XXXXXXX-X.
25+
26+
Companies and associations of persons (AOP) are assigned a National Tax Number
27+
or Registration Number when they e-enroll on the FBR Iris portal.
28+
29+
More information:
30+
31+
* https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Pakistan-TIN.pdf
32+
* https://e.fbr.gov.pk/
33+
34+
>>> validate('3804142-1')
35+
'38041421'
36+
>>> validate('0822910 - 4')
37+
'08229104'
38+
>>> validate('12345')
39+
Traceback (most recent call last):
40+
...
41+
InvalidLength: ...
42+
>>> format('38041421')
43+
'3804142-1'
44+
""" # noqa: E501
45+
46+
from stdnum.exceptions import *
47+
from stdnum.util import clean, isdigits
48+
49+
50+
def compact(number):
51+
"""Convert the number to the minimal representation.
52+
53+
This strips the number of any valid separators and removes surrounding
54+
whitespace.
55+
"""
56+
return clean(number, ' -')
57+
58+
59+
def validate(number):
60+
"""Check if the number is a valid Pakistan NTN number.
61+
62+
This checks the length, formatting and check digit.
63+
"""
64+
number = compact(number)
65+
if len(number) != 8:
66+
raise InvalidLength()
67+
if not isdigits(number):
68+
raise InvalidFormat()
69+
return number
70+
71+
72+
def is_valid(number):
73+
"""Check if the number is a valid Pakistan NTN number."""
74+
try:
75+
return bool(validate(number))
76+
except ValidationError:
77+
return False
78+
79+
80+
def format(number):
81+
"""Reformat the number to the standard presentation format."""
82+
number = compact(number)
83+
return '-'.join([number[:-1], number[-1]])

tests/test_pk_ntn.doctest

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
test_pk_ntn.doctest - more detailed doctests for stdnum.pk.ntn 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.pk.ntn 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.pk import ntn
26+
27+
28+
Tests for some corner cases.
29+
30+
>>> ntn.validate('3804142-1')
31+
'38041421'
32+
>>> ntn.validate('0822910 - 4')
33+
'08229104'
34+
>>> ntn.validate('08229104')
35+
'08229104'
36+
>>> ntn.format('38041421')
37+
'3804142-1'
38+
>>> ntn.format('3804142-1')
39+
'3804142-1'
40+
>>> ntn.format('0822910 - 4')
41+
'0822910-4'
42+
>>> ntn.validate('12345')
43+
Traceback (most recent call last):
44+
...
45+
InvalidLength: ...
46+
>>> ntn.validate('3804142-X')
47+
Traceback (most recent call last):
48+
...
49+
InvalidFormat: ...
50+
51+
52+
These have been found online and should all be valid numbers.
53+
54+
>>> numbers = '''
55+
...
56+
... 3804142-1
57+
... 0801599-6
58+
... 0816469-0
59+
... 0698343-0
60+
... 0711795-7
61+
... 0801380-2
62+
... 2217220-3
63+
... 2168117-1
64+
... 0700271-8
65+
... 0711545-8
66+
... 2138500-9
67+
... 3027108-8
68+
... 0785983-0
69+
... 4368251-7
70+
... 1347561-4
71+
... 1543137-1
72+
... 0710060-4
73+
... 0658560-4
74+
... 0712242-0
75+
... 0698049-0
76+
... 2261899-6
77+
... 0710672-6
78+
... 1268238-1
79+
... 1496632-8
80+
... 0698202-6
81+
... 3004934-2
82+
... 0822910 - 4
83+
... 0660564-8
84+
... 2544314-3
85+
... 0657297-9
86+
... 0698187-9
87+
... 1158490-4
88+
... 0658678-3
89+
... 0912725-9
90+
... 1143539-9
91+
... 0711554-7
92+
... 0712418-0
93+
... 0999468-8
94+
... 2180652-7
95+
... 0711167-3
96+
... 0712140-7
97+
... 0709930-4
98+
... 0801137-7
99+
... 1868111-5
100+
... 0820781-0
101+
... 0709782-4
102+
... 0698190-9
103+
... 0711982-8
104+
... 0712821-5
105+
... 0676546-7
106+
... 0711020-7
107+
... 7443848-2
108+
... 0698469-0
109+
... 1688091-9
110+
... 3123405-4
111+
... 0815616-6
112+
... 1319140-3
113+
... 7757795-2
114+
... 3072746-4
115+
... 0133480-8
116+
... 2663705-7
117+
... 0657139-5
118+
... 0709694-1
119+
... 3093299-8
120+
... 6110823-3
121+
... 6110854-7
122+
... 6110874-0
123+
... 6111000-0
124+
... 6111008-8
125+
... 6111233-8
126+
... 6111402-6
127+
... 6111534-3
128+
... 6111639-0
129+
... 6117056-8
130+
... 6123199-4
131+
... 6123937-4
132+
... 6143138-8
133+
... 6208694-8
134+
... 6213642-6
135+
... 6238197-0
136+
... 6258534-6
137+
... 6258594-3
138+
... 6258628-1
139+
... 6258698-8
140+
... 6258699-0
141+
... 6258712-4
142+
... 6258719-2
143+
... 6258739-4
144+
... 6258851-8
145+
... 6258861-0
146+
... 6258868-7
147+
... 6258987-0
148+
... 6258988-1
149+
... 6259020-6
150+
... 6259077-0
151+
... 6259177-1
152+
... 6259188-3
153+
... 6259345-7
154+
... 6259393-1
155+
... 6259485-3
156+
... 6259580-8
157+
... 6259606-7
158+
... 6259614-6
159+
... 6259631-5
160+
... 6259636-1
161+
... 6259654-1
162+
... 6259673-2
163+
... 6259684-4
164+
... 6259702-4
165+
... 6259723-7
166+
... 6259761-0
167+
... 6259848-6
168+
... 6259872-3
169+
... 6259949-8
170+
... 6260025-3
171+
... 6260034-3
172+
... 6260067-0
173+
... 6260127-6
174+
... 6260162-5
175+
... 6260189-5
176+
... 6260285-2
177+
... 6260292-0
178+
... 6260294-2
179+
... 6260301-0
180+
... 6260306-5
181+
... 6260307-6
182+
... 6260308-7
183+
... 6260313-3
184+
... 6260314-4
185+
... 6260315-5
186+
... 6260324-5
187+
... 6260334-6
188+
... 6260421-3
189+
... 6260442-6
190+
... 6260623-7
191+
... 6260640-6
192+
... 6260664-3
193+
... 6260687-8
194+
... 6260720-5
195+
... 6260838-6
196+
... 6260840-8
197+
... 6260841-0
198+
... 6260882-5
199+
... 6261128-8
200+
... 6261138-0
201+
... 6261140-2
202+
... 6261155-8
203+
... 6261158-2
204+
... 6261165-0
205+
... 6262127-8
206+
... 6262231-4
207+
... 6262235-8
208+
... 6262277-5
209+
... 6262304-5
210+
... 6262338-3
211+
... 6262384-4
212+
... 6262397-8
213+
... 6262398-0
214+
... 6262426-1
215+
... 6262476-6
216+
... 6262509-3
217+
... 6262559-8
218+
... 6262697-2
219+
... 6262748-8
220+
... 6262838-8
221+
... 6262842-3
222+
... 6262845-6
223+
... 6262849-1
224+
... 6262995-3
225+
... 6263002-1
226+
... 6263010-0
227+
... 6263057-2
228+
... 6263064-0
229+
... 6263094-3
230+
... 6263157-3
231+
... 6263217-0
232+
... 6263229-3
233+
... 6263265-3
234+
... 6263268-6
235+
... 6263276-5
236+
... 6263300-2
237+
... 6263402-5
238+
... 6263404-7
239+
... 6263444-2
240+
... 6263619-6
241+
... 6263734-4
242+
... 6263752-4
243+
... 6263824-4
244+
... 6263826-6
245+
... 6263927-8
246+
... 6263930-2
247+
... 6263980-7
248+
... 6264015-6
249+
... 6264034-7
250+
... 6264038-2
251+
... 6264083-2
252+
... 6264107-8
253+
... 6264112-4
254+
...
255+
... '''
256+
>>> [x for x in numbers.splitlines() if x and not ntn.is_valid(x)]
257+
[]

0 commit comments

Comments
 (0)