Skip to content

Commit 09b613c

Browse files
committed
Add support for Burkina Faso TIN
Fixes arthurdejong#375
1 parent 6d366e3 commit 09b613c

File tree

3 files changed

+360
-0
lines changed

3 files changed

+360
-0
lines changed

stdnum/bf/__init__.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# __init__.py - collection of Burkina Faso numbers
2+
# coding: utf-8
3+
#
4+
# Copyright (C) 2023 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 Burkina Faso numbers."""
22+
23+
# provide aliases
24+
from stdnum.bf import ifu as vat # noqa: F401

stdnum/bf/ifu.py

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# ifu.py - functions for handling Burkina Faso IFU numbers
2+
# coding: utf-8
3+
#
4+
# Copyright (C) 2023 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+
"""IFU (Identifiant Financier Unique, Burkina Faso tax number).
22+
23+
This number consists of 9 characters, including a check character.
24+
25+
More information:
26+
27+
* https://www.impots.gov.bf/fileadmin/user_upload/storage/fichiers/Loi-058-portant-CODE-GENERAL-DES-IMPOTS-final.pdf
28+
* http://dgi.impots.gov.bf/services-en-ligne/ifu/page4_7.php
29+
30+
>>> validate('00014729V')
31+
'00014729V'
32+
>>> validate('0000 59 83 T')
33+
'00005983T'
34+
>>> validate('12345')
35+
Traceback (most recent call last):
36+
...
37+
InvalidLength: ...
38+
>>> format('0000 59 83 T')
39+
'00005983T'
40+
""" # noqa: E501
41+
42+
from stdnum.exceptions import *
43+
from stdnum.util import clean, isdigits
44+
45+
46+
def compact(number):
47+
"""Convert the number to the minimal representation.
48+
49+
This strips the number of any valid separators and removes surrounding
50+
whitespace.
51+
"""
52+
return clean(number, ' -').upper().strip()
53+
54+
55+
def validate(number):
56+
"""Check if the number is a valid Burkina Faso IFU number.
57+
58+
This checks the length and formatting.
59+
"""
60+
number = compact(number)
61+
if len(number) != 9:
62+
raise InvalidLength()
63+
if not isdigits(number[:-1]):
64+
raise InvalidFormat()
65+
if not number[-1].isalpha():
66+
raise InvalidFormat()
67+
return number
68+
69+
70+
def is_valid(number):
71+
"""Check if the number is a valid Burkina Faso IFU number."""
72+
try:
73+
return bool(validate(number))
74+
except ValidationError:
75+
return False
76+
77+
78+
def format(number):
79+
"""Reformat the number to the standard presentation format."""
80+
return compact(number)

tests/test_bf_ifu.doctest

+256
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
test_bf_ifu.doctest - more detailed doctests for stdnum.bf.ifu module
2+
3+
Copyright (C) 2023 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.bf.ifu 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.bf import ifu
26+
27+
28+
Tests for some corner cases.
29+
30+
>>> ifu.validate('00014729V')
31+
'00014729V'
32+
>>> ifu.validate('0000 59 83 T')
33+
'00005983T'
34+
>>> ifu.validate('12345')
35+
Traceback (most recent call last):
36+
...
37+
InvalidLength: ...
38+
>>> ifu.validate('VV345678A')
39+
Traceback (most recent call last):
40+
...
41+
InvalidFormat: ...
42+
>>> ifu.validate('123456789')
43+
Traceback (most recent call last):
44+
...
45+
InvalidFormat: ...
46+
>>> ifu.format('0000 59 83 T')
47+
'00005983T'
48+
49+
50+
These have been found online and should all be valid numbers.
51+
52+
>>> numbers = '''
53+
...
54+
... 00014729V
55+
... 0000 59 83 T
56+
... 00002597C
57+
... 00114782D
58+
... 00113681 W
59+
... 00005112P
60+
... 00024940N
61+
... 00106113L
62+
... 00118640P
63+
... 00109342A
64+
... 00095734N
65+
... 00041085Y
66+
... 00049455J
67+
... 00112375B
68+
... 00108291Y
69+
... 00105602V
70+
... 00028638T
71+
... 00004184C
72+
... 00002475L
73+
... 00068712S
74+
... 00030559W
75+
... 00100809A
76+
... 00046475W
77+
... 00004788U
78+
... 00045255L
79+
... 00027070Z
80+
... 00004196G
81+
... 00061051V
82+
... 00044493S
83+
... 00092211U
84+
... 00007151C
85+
... 00013647N
86+
... 00085469E
87+
... 00114793A
88+
... 00088424D
89+
... 00059379R
90+
... 00040060X
91+
... 00015274H
92+
... 00041850M
93+
... 00060062V
94+
... 00015965T
95+
... 00053217K
96+
... 00065890K
97+
... 00061772X
98+
... 00024934H
99+
... 00000346E
100+
... 00043589U
101+
... 00005643N
102+
... 00018945R
103+
... 00045073R
104+
... 00048430W
105+
... 00018284Z
106+
... 00043593N
107+
... 00059665T
108+
... 00042911R
109+
... 00005983T
110+
... 00000612L
111+
... 00016079H
112+
... 00030276N
113+
... 00009763S
114+
... 00007047V
115+
... 00011610K
116+
... 00037904A
117+
... 00060700T
118+
... 00010790T
119+
... 00006204X
120+
... 00064526S
121+
... 00055782Y
122+
... 00010855Z
123+
... 00063250A
124+
... 00034469W
125+
... 00029551F
126+
... 00041096K
127+
... 00002929N
128+
... 00007572J
129+
... 00023755F
130+
... 00062653E
131+
... 00007345N
132+
... 00002927P
133+
... 00059052S
134+
... 00002772D
135+
... 00028333B
136+
... 00014729V
137+
... 00034932K
138+
... 00027503K
139+
... 00008488E
140+
... 00000261N
141+
... 00011684A
142+
... 00051386U
143+
... 00034653W
144+
... 00065225B
145+
... 00004805C
146+
... 00033666X
147+
... 00023967E
148+
... 00022991L
149+
... 00055301V
150+
... 00006970E
151+
... 00033818N
152+
... 00009384A
153+
... 00017763Y
154+
... 00003744K
155+
... 00027465T
156+
... 00008443H
157+
... 00001199T
158+
... 00055532G
159+
... 00019340B
160+
... 00011789M
161+
... 00033863Y
162+
... 00032218K
163+
... 00035115X
164+
... 00003343N
165+
... 00034918U
166+
... 00037476V
167+
... 00029263Y
168+
... 00011425X
169+
... 00004268M
170+
... 00072439D
171+
... 00051703Z
172+
... 00030796Z
173+
... 00065712E
174+
... 00026127Z
175+
... 00050212T
176+
... 00034742D
177+
... 00003453T
178+
... 00017764Y
179+
... 00037703P
180+
... 00030202T
181+
... 00010206X
182+
... 00058000X
183+
... 00046438E
184+
... 00035561T
185+
... 00003126C
186+
... 00001379Z
187+
... 00036616A
188+
... 00019411Z
189+
... 00041324R
190+
... 72004531S
191+
... 00032293E
192+
... 00009786S
193+
... 00016696C
194+
... 00019411Z
195+
... 00041324R
196+
... 72004531S
197+
... 00032293E
198+
... 00009786S
199+
... 00007484V
200+
... 00021863M
201+
... 00016973Z
202+
... 00024407U
203+
... 00032177A
204+
... 00021050J
205+
... 00015052G
206+
... 00013445M
207+
... 00019148D
208+
... 00021050J
209+
... 00015052G
210+
... 00013445M
211+
... 00005847J
212+
... 00005906K
213+
... 00044686B
214+
... 00004443U
215+
... 00005637H
216+
... 00035653T
217+
... 00014309T
218+
... 00010064M
219+
... 00005918Y
220+
... 00010289N
221+
... 00021463U
222+
... 00005975K
223+
... 00034097S
224+
... 79506499X
225+
... 00038521F
226+
... 00000323E
227+
... 00000496M
228+
... 00007420L
229+
... 00004052 U
230+
... 00037929H
231+
... 00070665V
232+
... 00027622 Z
233+
... 00114204V
234+
... 00002888M
235+
... 00000 624 T
236+
... 00004430 L
237+
... 00069260Y
238+
... 00065152R
239+
... 00040484U
240+
... 00042208T
241+
... 00002855T
242+
... 00021943E
243+
... 00005141D
244+
... 00034456H
245+
... 00006669Z
246+
... 00042538C
247+
... 00124727C
248+
... 00097224 T
249+
... 00066145B
250+
... 00051680Z
251+
... 00095420 R
252+
... 00026749A
253+
...
254+
... '''
255+
>>> [x for x in numbers.splitlines() if x and not ifu.is_valid(x)]
256+
[]

0 commit comments

Comments
 (0)