Skip to content

Commit d64d7f8

Browse files
committed
Add support for Mozambique TIN
Fixes arthurdejong#360
1 parent 6d366e3 commit d64d7f8

File tree

3 files changed

+251
-0
lines changed

3 files changed

+251
-0
lines changed

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

stdnum/mz/nuit.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# nuit.py - functions for handling Mozambique NUIT 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+
"""NUIT (Número Único de Identificação Tributaria, Mozambique tax number).
22+
23+
This number consists of 9 digits, sometimes separated in three groups of three
24+
digits using whitespace to make it easier to read.
25+
26+
The first digit indicates the type of entity. The next seven digits are a
27+
sequential number. The last digit is the check digit, which is used to verify
28+
the number was correctly typed.
29+
30+
More information:
31+
32+
* https://www.mobilize.org.mz/nuit-numero-unico-de-identificacao-tributaria/
33+
* http://www.at.gov.mz/por/Perguntas-Frequentes2/NUIT
34+
35+
>>> validate('400339910')
36+
'400339910'
37+
>>> validate('400 005 834')
38+
'400005834'
39+
>>> validate('12345')
40+
Traceback (most recent call last):
41+
...
42+
InvalidLength: ...
43+
>>> format('400339910')
44+
'400 339 910'
45+
"""
46+
47+
from stdnum.exceptions import *
48+
from stdnum.util import clean, isdigits
49+
50+
51+
def compact(number):
52+
"""Convert the number to the minimal representation.
53+
54+
This strips the number of any valid separators and removes surrounding
55+
whitespace.
56+
"""
57+
return clean(number, ' -.').strip()
58+
59+
60+
def validate(number):
61+
"""Check if the number is a valid Mozambique NUIT number.
62+
63+
This checks the length and formatting.
64+
"""
65+
number = compact(number)
66+
if len(number) != 9:
67+
raise InvalidLength()
68+
if not isdigits(number):
69+
raise InvalidFormat()
70+
return number
71+
72+
73+
def is_valid(number):
74+
"""Check if the number is a valid Mozambique NUIT number."""
75+
try:
76+
return bool(validate(number))
77+
except ValidationError:
78+
return False
79+
80+
81+
def format(number):
82+
"""Reformat the number to the standard presentation format."""
83+
number = compact(number)
84+
return ' '.join([number[:3], number[3:-3], number[-3:]])

tests/test_mz_nuit.doctest

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
test_mz_nuit.doctest - more detailed doctests for stdnum.mz.nuit 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.mz.nuit 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.mz import nuit
26+
27+
28+
Tests for some corner cases.
29+
30+
>>> nuit.validate('400339910')
31+
'400339910'
32+
>>> nuit.validate('400 005 834')
33+
'400005834'
34+
>>> nuit.validate('100.033.593')
35+
'100033593'
36+
>>> nuit.validate('12345')
37+
Traceback (most recent call last):
38+
...
39+
InvalidLength: ...
40+
>>> nuit.validate('VV3456789')
41+
Traceback (most recent call last):
42+
...
43+
InvalidFormat: ...
44+
>>> nuit.format('400339910')
45+
'400 339 910'
46+
>>> nuit.format('100.033.593')
47+
'100 033 593'
48+
49+
50+
These have been found online and should all be valid numbers.
51+
52+
>>> numbers = '''
53+
...
54+
... 400339910
55+
... 400 005 834
56+
... 500171650
57+
... 700152855
58+
... 400 005 834
59+
... 400027145
60+
... 400001391
61+
... 400584291
62+
... 103017602
63+
... 101935626
64+
... 400872120
65+
... 500001615
66+
... 400786704
67+
... 500 024 240
68+
... 400066183
69+
... 500006005
70+
... 401 191 607
71+
... 400 102 961
72+
... 105564724
73+
... 500003545
74+
... 400787451
75+
... 116773767
76+
... 111878641
77+
... 154695168
78+
... 102889010
79+
... 101908372
80+
... 149349324
81+
... 400339910
82+
... 400509182
83+
... 400 006 245
84+
... 400778922
85+
... 400015546
86+
... 401343261
87+
... 401120807
88+
... 400 108 791
89+
... 400 415 870
90+
... 108 755 423
91+
... 108 755 385
92+
... 400007225
93+
... 401508317
94+
... 400535825
95+
... 400418810
96+
... 401129006
97+
... 400058172
98+
... 400267839
99+
... 500017341
100+
... 700074854
101+
... 401215298
102+
... 400786704
103+
... 400058921
104+
... 400238685
105+
... 400005516
106+
... 500050012
107+
... 400 052 786
108+
... 400 111 200
109+
... 400824037
110+
... 400 410 151
111+
... 120883275
112+
... 100002892
113+
... 118924045
114+
... 400157715
115+
... 400370028
116+
... 129926945
117+
... 400364001
118+
... 101002561
119+
... 400551847
120+
... 400 769 052
121+
... 400 120 323
122+
... 100.033.593
123+
... 105032031
124+
... 401430989
125+
... 103709776
126+
... 500171650
127+
... 400316341
128+
... 400509182
129+
... 400021260
130+
... 400129551
131+
... 400187398
132+
... 600000063
133+
... 400102961
134+
... 400994579
135+
... 400905649
136+
... 500003839
137+
... 401041877
138+
... 400 380 972
139+
... 400068038
140+
...
141+
... '''
142+
>>> [x for x in numbers.splitlines() if x and not nuit.is_valid(x)]
143+
[]

0 commit comments

Comments
 (0)