diff --git a/stdnum/mz/__init__.py b/stdnum/mz/__init__.py new file mode 100644 index 00000000..9a98d930 --- /dev/null +++ b/stdnum/mz/__init__.py @@ -0,0 +1,24 @@ +# __init__.py - collection of Mozambique numbers +# coding: utf-8 +# +# Copyright (C) 2023 Leandro Regueiro +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +"""Collection of Mozambique numbers.""" + +# provide aliases +from stdnum.mz import nuit as vat # noqa: F401 diff --git a/stdnum/mz/nuit.py b/stdnum/mz/nuit.py new file mode 100644 index 00000000..3674d17f --- /dev/null +++ b/stdnum/mz/nuit.py @@ -0,0 +1,84 @@ +# nuit.py - functions for handling Mozambique NUIT numbers +# coding: utf-8 +# +# Copyright (C) 2023 Leandro Regueiro +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +"""NUIT (Número Único de Identificação Tributaria, Mozambique tax number). + +This number consists of 9 digits, sometimes separated in three groups of three +digits using whitespace to make it easier to read. + +The first digit indicates the type of entity. The next seven digits are a +sequential number. The last digit is the check digit, which is used to verify +the number was correctly typed. + +More information: + +* https://www.mobilize.org.mz/nuit-numero-unico-de-identificacao-tributaria/ +* http://www.at.gov.mz/por/Perguntas-Frequentes2/NUIT + +>>> validate('400339910') +'400339910' +>>> validate('400 005 834') +'400005834' +>>> validate('12345') +Traceback (most recent call last): + ... +InvalidLength: ... +>>> format('400339910') +'400 339 910' +""" + +from stdnum.exceptions import * +from stdnum.util import clean, isdigits + + +def compact(number): + """Convert the number to the minimal representation. + + This strips the number of any valid separators and removes surrounding + whitespace. + """ + return clean(number, ' -.').strip() + + +def validate(number): + """Check if the number is a valid Mozambique NUIT number. + + This checks the length and formatting. + """ + number = compact(number) + if len(number) != 9: + raise InvalidLength() + if not isdigits(number): + raise InvalidFormat() + return number + + +def is_valid(number): + """Check if the number is a valid Mozambique NUIT number.""" + try: + return bool(validate(number)) + except ValidationError: + return False + + +def format(number): + """Reformat the number to the standard presentation format.""" + number = compact(number) + return '-'.join([number[:3], number[3:-3], number[-3:]]) diff --git a/tests/test_mz_nuit.doctest b/tests/test_mz_nuit.doctest new file mode 100644 index 00000000..80586ea6 --- /dev/null +++ b/tests/test_mz_nuit.doctest @@ -0,0 +1,143 @@ +test_mz_nuit.doctest - more detailed doctests for stdnum.mz.nuit module + +Copyright (C) 2023 Leandro Regueiro + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA + + +This file contains more detailed doctests for the stdnum.mz.nuit module. It +tries to test more corner cases and detailed functionality that is not really +useful as module documentation. + +>>> from stdnum.mz import nuit + + +Tests for some corner cases. + +>>> nuit.validate('400339910') +'400339910' +>>> nuit.validate('400 005 834') +'400005834' +>>> nuit.validate('100.033.593') +'100033593' +>>> nuit.validate('12345') +Traceback (most recent call last): + ... +InvalidLength: ... +>>> nuit.validate('VV3456789') +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> nuit.format('400339910') +'400 339 910' +>>> nuit.format('100.033.593') +'100 033 593' + + +These have been found online and should all be valid numbers. + +>>> numbers = ''' +... +... 400339910 +... 400 005 834 +... 500171650 +... 700152855 +... 400 005 834 +... 400027145 +... 400001391 +... 400584291 +... 103017602 +... 101935626 +... 400872120 +... 500001615 +... 400786704 +... 500 024 240 +... 400066183 +... 500006005 +... 401 191 607 +... 400 102 961 +... 105564724 +... 500003545 +... 400787451 +... 116773767 +... 111878641 +... 154695168 +... 102889010 +... 101908372 +... 149349324 +... 400339910 +... 400509182 +... 400 006 245 +... 400778922 +... 400015546 +... 401343261 +... 401120807 +... 400 108 791 +... 400 415 870 +... 108 755 423 +... 108 755 385 +... 400007225 +... 401508317 +... 400535825 +... 400418810 +... 401129006 +... 400058172 +... 400267839 +... 500017341 +... 700074854 +... 401215298 +... 400786704 +... 400058921 +... 400238685 +... 400005516 +... 500050012 +... 400 052 786 +... 400 111 200 +... 400824037 +... 400 410 151 +... 120883275 +... 100002892 +... 118924045 +... 400157715 +... 400370028 +... 129926945 +... 400364001 +... 101002561 +... 400551847 +... 400 769 052 +... 400 120 323 +... 100.033.593 +... 105032031 +... 401430989 +... 103709776 +... 500171650 +... 400316341 +... 400509182 +... 400021260 +... 400129551 +... 400187398 +... 600000063 +... 400102961 +... 400994579 +... 400905649 +... 500003839 +... 401041877 +... 400 380 972 +... 400068038 +... +... ''' +>>> [x for x in numbers.splitlines() if x and not nuit.is_valid(x)] +[]