8000 Add support for Ghana TIN · Dj0ulo/python-stdnum@7be2291 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7be2291

Browse files
unhoarthurdejong
authored andcommitted
Add support for Ghana TIN
Closes arthurdejong#326 Closes arthurdejong#262
1 parent acb6934 commit 7be2291

File tree

3 files changed

+221
-0
lines changed

3 files changed

+221
-0
lines changed

stdnum/gh/__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 Ghana 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 Ghana numbers."""
22+
23+
# provide aliases
24+
from stdnum.gh import tin as vat # noqa: F401

stdnum/gh/tin.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# tin.py - functions for handling Ghana TIN numbers
2+
# coding: utf-8
3+
#
4+
# Copyright (C) 2022 Leandro Regueiro
5+
# Copyright (C) 2022 Arthur de Jong
6+
#
7+
# This library is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU Lesser General Public
9+
# License as published by the Free Software Foundation; either
10+
# version 2.1 of the License, or (at your option) any later version.
11+
#
12+
# This library is distributed in the hope that it will be useful,
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
# Lesser General Public License for more details.
16+
#
17+
# You should have received a copy of the GNU Lesser General Public
18+
# License along with this library; if not, write to the Free Software
19+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20+
# 02110-1301 USA
21+
22+
"""TIN (Taxpayer Identification Number, Ghana tax number).
23+
24+
This number is issued by the Ghana Revenue Authority (GRA) to individuals who
25+
are not eligible for the Ghanacard PIN and other entities.
26+
27+
This number consists of 11 alpha-numeric characters. It begins with one of the
28+
following prefixes:
29+
30+
P00 For Individuals.
31+
C00 For Companies limited by guarantee, shares, Unlimited (i.e organisation
32+
required to register with the RGD).
33+
G00 Government Agencies, MDAs.
34+
Q00 Foreign Missions, Employees of foreign missions.
35+
V00 Public Institutions, Trusts, Co-operatives, Foreign Shareholder
36+
(Offshore), (Entities not registered by RGD).
37+
38+
More information:
39+
40+
* https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Ghana-TIN.pdf
41+
* https://gra.gov.gh/tin/
42+
* https://gra.gov.gh/tin/tin-faq/
43+
44+
>>> validate('C0000803561')
45+
'C0000803561'
46+
>>> validate('C0000803562')
47+
Traceback (most recent call last):
48+
...
49+
InvalidChecksum: ...
50+
""" # noqa: E501
51+
52+
import re
53+
54+
from stdnum.exceptions import *
55+
from stdnum.util import clean
56+
57+
58+
_gh_tin_re = re.compile(r'^[PCGQV]{1}00[A-Z0-9]{8}$')
59+
60+
61+
def compact(number):
62+
"""Convert the number to the minimal representation.
63+
64+
This strips the number of any valid separators and removes surrounding
65+
whitespace.
66+
"""
67+
return clean(number, ' ').upper()
68+
69+
70+
def calc_check_digit(number):
71+
"""Calculate the check digit for the TIN."""
72+
check = sum((i + 1) * int(n) for i, n in enumerate(number[1:10])) % 11
73+
return 'X' if check == 10 else str(check)
74+
75+
76+
def validate(number):
77+
"""Check if the number is a valid Ghana TIN."""
78+
number = compact(number)
79+
if len(number) != 11:
80+
raise InvalidLength()
81+
if not _gh_tin_re.match(number):
82+
raise InvalidFormat()
83+
if number[-1] != calc_check_digit(number):
84+
raise InvalidChecksum()
85+
return number
86+
87+
88+
def is_valid(number):
89+
"""Check if the number is a valid Ghana TIN."""
90+
try:
91+
return bool(validate(number))
92+
except ValidationError:
93+
return False

tests/test_gh_tin.doctest

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
test_gh_tin.doctest - more detailed doctests for stdnum.gh.tin 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.gh.tin 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.gh import tin
26+
27+
28+
Tests for some corner cases.
29+
30+
>>> tin.validate('C0000803561')
31+
'C0000803561'
32+
>>> tin.validate('P0008816751')
33+
'P0008816751'
34+
>>> tin.validate('V0022862404')
35+
'V0022862404'
36+
>>> tin.validate('G0005513405')
37+
'G0005513405'
38+
>>> tin.validate('12345')
39+
Traceback (most recent call last):
40+
...
41+
InvalidLength: ...
42+
>>> tin.validate('X0000803561')
43+
Traceback (most recent call last):
44+
...
45+
InvalidFormat: ...
46+
47+
48+
These have been found online and should all be valid numbers.
49+
50+
>>> numbers = '''
51+
...
52+
... C0000803561
53+
... C0002147866
54+
... C0002442477
55+
... C0002551888
56+
... C000261992X
57+
... C0002862646
58+
... C0003136973
59+
... C0003137007
60+
... C0003165493
61+
... C0003168417
62+
... C0003257630
63+
... C0003257673
64+
... C0003268071
65+
... C0003278263
66+
... C0003278271
67+
... C000327828X
68+
... C0003278484
69+
... C0003417425
70+
... C0003442500
71+
... C000366497X
72+
... C0003664996
73+
... C0003831426
74+
... C0004056450
75+
... C0004524764
76+
... C0004656830
77+
... C0004743520
78+
... C0004894162
79+
... C0005015774
80+
... C0005203333
81+
... C0006570275
82+
... C0009705228
83+
... C0010952330
84+
... C001095242X
85+
... C0013225812
86+
... C0021485674
87+
... C0029454158
88+
... G0005513405
89+
... G0061140708
90+
... P0001525662
91+
... P0004697499
92+
... P0006187994
93+
... P0008816751
94+
... P0009329188
95+
... P0009329250
96+
... P0009487379
97+
... P0012631833
98+
... P0039937100
99+
... V0003107108
100+
... V0022862404
101+
...
102+
... '''
103+
>>> [x for x in numbers.splitlines() if x and not tin.is_valid(x)]
104+
[]

0 commit comments

Comments
 (0)
0