From 4fb10dacc02e908f10e96b33fd7de5c644889217 Mon Sep 17 00:00:00 2001 From: Leandro Regueiro Date: Sun, 5 Feb 2023 16:54:23 +0100 Subject: [PATCH] Add support for Cape Verde TIN Fixes #387 --- stdnum/cv/__init__.py | 24 ++++ stdnum/cv/nif.py | 107 +++++++++++++++ tests/test_cv_nif.doctest | 278 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 409 insertions(+) create mode 100644 stdnum/cv/__init__.py create mode 100644 stdnum/cv/nif.py create mode 100644 tests/test_cv_nif.doctest diff --git a/stdnum/cv/__init__.py b/stdnum/cv/__init__.py new file mode 100644 index 00000000..46a904a6 --- /dev/null +++ b/stdnum/cv/__init__.py @@ -0,0 +1,24 @@ +# __init__.py - collection of Cape Verde 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 Cape Verde numbers.""" + +# provide aliases +from stdnum.cv import nif as vat # noqa: F401 diff --git a/stdnum/cv/nif.py b/stdnum/cv/nif.py new file mode 100644 index 00000000..2602d543 --- /dev/null +++ b/stdnum/cv/nif.py @@ -0,0 +1,107 @@ +# nif.py - functions for handling Cape Verde NIF 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 + +"""NIF (Número de Identificação Fiscal, Cape Verde tax number). + +This number consists of 9 digits, sometimes separated into three groups with +three digits each to make it easier to read, like XXX XXX XXX. + +The first digit indicates the type of person: + + * 1: Singular person, either resident or non resident. + * 2: Companies. + * 3: National entities. + * 4: International entities. + * 5: Other entities. + +The following six digits are either: + + * The Bilhete de Identidade number, for singular resident persons, or + * A sequence automatically assigned, for all other cases. + +The last two digits are control digits. + +More information: + +* https://portondinosilhas.gov.cv/images/igrp-portal/img/documentos/EA834694B09A15FAE044002128A60A02.pdf +* https://www.mf.gov.cv/documents/54571/273413/adenda_esclarecimentos_pdfs.pdf + + +>>> validate('200129775') +'200129775' +>>> validate('200 144 731') +'200144731' +>>> validate('253.656.575') +'253656575' +>>> validate('12345') +Traceback (most recent call last): + ... +InvalidLength: ... +>>> validate('VV3456789') +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> validate('923456789') +Traceback (most recent call last): + ... +InvalidComponent: ... +>>> format('200 144 731') +'200144731' +""" # noqa: E501 + +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 Cape Verde NIF number. + + This checks the length and formatting. + """ + number = compact(number) + if len(number) != 9: + raise InvalidLength() + if not isdigits(number): + raise InvalidFormat() + if number[0] not in ('1', '2', '3', '4', '5'): + raise InvalidComponent() + return number + + +def is_valid(number): + """Check if the number is a valid Cape Verde NIF number.""" + try: + return bool(validate(number)) + except ValidationError: + return False + + +def format(number): + """Reformat the number to the standard presentation format.""" + return compact(number) diff --git a/tests/test_cv_nif.doctest b/tests/test_cv_nif.doctest new file mode 100644 index 00000000..b78ab226 --- /dev/null +++ b/tests/test_cv_nif.doctest @@ -0,0 +1,278 @@ +test_cv_nif.doctest - more detailed doctests for stdnum.cv.nif 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.cv.nif module. It +tries to test more corner cases and detailed functionality that is not really +useful as module documentation. + +>>> from stdnum.cv import nif + + +Tests for some corner cases. + +>>> nif.validate('200129775') +'200129775' +>>> nif.validate('200 144 731') +'200144731' +>>> nif.validate('253.656.575') +'253656575' +>>> nif.validate('12345') +Traceback (most recent call last): + ... +InvalidLength: ... +>>> nif.validate('VV3456789') +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> nif.validate('923456789') +Traceback (most recent call last): + ... +InvalidComponent: ... +>>> nif.format('200129775') +'200129775' +>>> nif.format('200 144 731') +'200144731' +>>> nif.format('253.656.575') +'253656575' + + +These have been found online and should all be valid numbers. + +>>> numbers = ''' +... +... 200129775 +... 565191500 +... 254746420 +... 200146009 +... 353035572 +... 352538600 +... 350067813 +... 252214420 +... 275851400 +... 115203109 +... 350212929 +... 280150407 +... 157396274 +... 168920301 +... 100229964 +... 113553366 +... 152867899 +... 152611010 +... 281142602 +... 279721706 +... 171655206 +... 213942704 +... 170168506 +... 152967761 +... 200 144 731 +... 366911198 +... 268 445 206 +... 279859805 +... 200252208 +... 572484305 +... 138321582 +... 134913701 +... 117274208 +... 134741706 +... 143622706 +... 570648700 +... 120532603 +... 134642805 +... 136657400 +... 104633727 +... 106885014 +... 140548254 +... 130983659 +... 129055000 +... 130733628 +... 129968234 +... 136709206 +... 130815519 +... 155189220 +... 134143728 +... 252269080 +... 289478790 +... 255965001 +... 557577748 +... 102713251 +... 110649273 +... 131997149 +... 119178575 +... 134663802 +... 117075779 +... 117269549 +... 129042609 +... 112961789 +... 127606092 +... 104013699 +... 103435980 +... 130631205 +... 142227307 +... 130946907 +... 140091408 +... 117469041 +... 116135484 +... 138629609 +... 140229809 +... 564660132 +... 265002605 +... 351759581 +... 553331680 +... 350067813 +... 264 461 037 +... 200147838 +... 350767556 +... 200166972 +... 252214420 +... 172568900 +... 171673204 +... 106340298 +... 100532209 +... 121186482 +... 116309490 +... 265374278 +... 252293118 +... 200110322 +... 250 369 630 +... 562770755 +... 250260223 +... 200489631 +... 228360803 +... 239230604 +... 106008170 +... 268788200 +... 268141606 +... 238700607 +... 200216589 +... 252293118 +... 255292775 +... 288871197 +... 232863105 +... 253978343 +... 261 862 502 +... 200151606 +... 351977570 +... 200131735 +... 200503308 +... 252293118 +... 351759581 +... 152967761 +... 165831677 +... 162052863 +... 169633209 +... 121034208 +... 168091704 +... 129460958 +... 152568557 +... 139630406 +... 116749466 +... 140523774 +... 101847670 +... 113680040 +... 200252208 +... 200129775 +... 357083792 +... 200127055 +... 200 131 249 +... 105057177 +... 144427109 +... 353331112 +... 153243708 +... 253168694 +... 425555 +... 262693330 +... 200 100 106 +... 264836880 +... 264941322 +... 264950070 +... 253168694 +... 200 133 373 +... 200122177 +... 252300343 +... 200252208 +... 552565202 +... 255980159 +... 510489192 +... 572 939 108 +... 266940773 +... 256025860 +... 160882478 +... 160857368 +... 353331112 +... 555065502 +... 270149503 +... 171003705 +... 112141552 +... 572953208 +... 273068504 +... 172874602 +... 167997807 +... 171973801 +... 171908406 +... 254598641 +... 218800509 +... 112884857 +... 254746420 +... 253.656.575 +... 262958228 +... 270275606 +... 253978343 +... 200166972 +... 554 710 803 +... 568016408 +... 171154606 +... 105454095 +... 551405562 +... 107393948 +... 105232785 +... 106396161 +... 578115107 +... 133847837 +... 117288500 +... 121284573 +... 100921884 +... 100659896 +... 107056607 +... 100779603 +... 101179383 +... 112840809 +... 276283309 +... 275702600 +... 276963105 +... 200166972 +... 352485647 +... 361671342 +... 365766461 +... 355112574 +... 365671592 +... 365672564 +... 365766208 +... 365671916 +... 352497572 +... 365681636 +... 365672726 +... 363293558 +... 352596988 +... 365672130 +... 272198501 +... +... ''' +>>> [x for x in numbers.splitlines() if x and not nif.is_valid(x)] +[]