From a256b5d42431ae935363425c59513f5319c7c95e Mon Sep 17 00:00:00 2001 From: Leandro Regueiro Date: Sun, 11 Sep 2022 11:44:42 +0200 Subject: [PATCH] Add support for Pakisstan TIN Fixes #211. --- stdnum/pk/__init__.py | 24 ++++ stdnum/pk/ntn.py | 83 ++++++++++++ tests/test_pk_ntn.doctest | 257 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 364 insertions(+) create mode 100644 stdnum/pk/__init__.py create mode 100644 stdnum/pk/ntn.py create mode 100644 tests/test_pk_ntn.doctest diff --git a/stdnum/pk/__init__.py b/stdnum/pk/__init__.py new file mode 100644 index 00000000..a0245dfb --- /dev/null +++ b/stdnum/pk/__init__.py @@ -0,0 +1,24 @@ +# __init__.py - collection of Pakistan numbers +# coding: utf-8 +# +# Copyright (C) 2022 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 Pakistan numbers.""" + +# provide aliases +from stdnum.pk import ntn as vat # noqa: F401 diff --git a/stdnum/pk/ntn.py b/stdnum/pk/ntn.py new file mode 100644 index 00000000..45490e4c --- /dev/null +++ b/stdnum/pk/ntn.py @@ -0,0 +1,83 @@ +# ntn.py - functions for handling Pakistan NTN numbers +# coding: utf-8 +# +# Copyright (C) 2022 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 + +"""NTN (National Tax Number, نیشنل ٹیکس نمبر, Pakistan tax number). + +This number consists of 8 digits, the last being a check digit, usually +separated by a hyphen like XXXXXXX-X. + +Companies and associations of persons (AOP) are assigned a National Tax Number +or Registration Number when they e-enroll on the FBR Iris portal. + +More information: + +* https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Pakistan-TIN.pdf +* https://e.fbr.gov.pk/ + +>>> validate('3804142-1') +'38041421' +>>> validate('0822910 - 4') +'08229104' +>>> validate('12345') +Traceback (most recent call last): + ... +InvalidLength: ... +>>> format('38041421') +'3804142-1' +""" # 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, ' -') + + +def validate(number): + """Check if the number is a valid Pakistan NTN number. + + This checks the length, formatting and check digit. + """ + number = compact(number) + if len(number) != 8: + raise InvalidLength() + if not isdigits(number): + raise InvalidFormat() + return number + + +def is_valid(number): + """Check if the number is a valid Pakistan NTN 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[:-1], number[-1]]) diff --git a/tests/test_pk_ntn.doctest b/tests/test_pk_ntn.doctest new file mode 100644 index 00000000..d206ff32 --- /dev/null +++ b/tests/test_pk_ntn.doctest @@ -0,0 +1,257 @@ +test_pk_ntn.doctest - more detailed doctests for stdnum.pk.ntn module + +Copyright (C) 2022 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.pk.ntn module. It +tries to test more corner cases and detailed functionality that is not really +useful as module documentation. + +>>> from stdnum.pk import ntn + + +Tests for some corner cases. + +>>> ntn.validate('3804142-1') +'38041421' +>>> ntn.validate('0822910 - 4') +'08229104' +>>> ntn.validate('08229104') +'08229104' +>>> ntn.format('38041421') +'3804142-1' +>>> ntn.format('3804142-1') +'3804142-1' +>>> ntn.format('0822910 - 4') +'0822910-4' +>>> ntn.validate('12345') +Traceback (most recent call last): + ... +InvalidLength: ... +>>> ntn.validate('3804142-X') +Traceback (most recent call last): + ... +InvalidFormat: ... + + +These have been found online and should all be valid numbers. + +>>> numbers = ''' +... +... 3804142-1 +... 0801599-6 +... 0816469-0 +... 0698343-0 +... 0711795-7 +... 0801380-2 +... 2217220-3 +... 2168117-1 +... 0700271-8 +... 0711545-8 +... 2138500-9 +... 3027108-8 +... 0785983-0 +... 4368251-7 +... 1347561-4 +... 1543137-1 +... 0710060-4 +... 0658560-4 +... 0712242-0 +... 0698049-0 +... 2261899-6 +... 0710672-6 +... 1268238-1 +... 1496632-8 +... 0698202-6 +... 3004934-2 +... 0822910 - 4 +... 0660564-8 +... 2544314-3 +... 0657297-9 +... 0698187-9 +... 1158490-4 +... 0658678-3 +... 0912725-9 +... 1143539-9 +... 0711554-7 +... 0712418-0 +... 0999468-8 +... 2180652-7 +... 0711167-3 +... 0712140-7 +... 0709930-4 +... 0801137-7 +... 1868111-5 +... 0820781-0 +... 0709782-4 +... 0698190-9 +... 0711982-8 +... 0712821-5 +... 0676546-7 +... 0711020-7 +... 7443848-2 +... 0698469-0 +... 1688091-9 +... 3123405-4 +... 0815616-6 +... 1319140-3 +... 7757795-2 +... 3072746-4 +... 0133480-8 +... 2663705-7 +... 0657139-5 +... 0709694-1 +... 3093299-8 +... 6110823-3 +... 6110854-7 +... 6110874-0 +... 6111000-0 +... 6111008-8 +... 6111233-8 +... 6111402-6 +... 6111534-3 +... 6111639-0 +... 6117056-8 +... 6123199-4 +... 6123937-4 +... 6143138-8 +... 6208694-8 +... 6213642-6 +... 6238197-0 +... 6258534-6 +... 6258594-3 +... 6258628-1 +... 6258698-8 +... 6258699-0 +... 6258712-4 +... 6258719-2 +... 6258739-4 +... 6258851-8 +... 6258861-0 +... 6258868-7 +... 6258987-0 +... 6258988-1 +... 6259020-6 +... 6259077-0 +... 6259177-1 +... 6259188-3 +... 6259345-7 +... 6259393-1 +... 6259485-3 +... 6259580-8 +... 6259606-7 +... 6259614-6 +... 6259631-5 +... 6259636-1 +... 6259654-1 +... 6259673-2 +... 6259684-4 +... 6259702-4 +... 6259723-7 +... 6259761-0 +... 6259848-6 +... 6259872-3 +... 6259949-8 +... 6260025-3 +... 6260034-3 +... 6260067-0 +... 6260127-6 +... 6260162-5 +... 6260189-5 +... 6260285-2 +... 6260292-0 +... 6260294-2 +... 6260301-0 +... 6260306-5 +... 6260307-6 +... 6260308-7 +... 6260313-3 +... 6260314-4 +... 6260315-5 +... 6260324-5 +... 6260334-6 +... 6260421-3 +... 6260442-6 +... 6260623-7 +... 6260640-6 +... 6260664-3 +... 6260687-8 +... 6260720-5 +... 6260838-6 +... 6260840-8 +... 6260841-0 +... 6260882-5 +... 6261128-8 +... 6261138-0 +... 6261140-2 +... 6261155-8 +... 6261158-2 +... 6261165-0 +... 6262127-8 +... 6262231-4 +... 6262235-8 +... 6262277-5 +... 6262304-5 +... 6262338-3 +... 6262384-4 +... 6262397-8 +... 6262398-0 +... 6262426-1 +... 6262476-6 +... 6262509-3 +... 6262559-8 +... 6262697-2 +... 6262748-8 +... 6262838-8 +... 6262842-3 +... 6262845-6 +... 6262849-1 +... 6262995-3 +... 6263002-1 +... 6263010-0 +... 6263057-2 +... 6263064-0 +... 6263094-3 +... 6263157-3 +... 6263217-0 +... 6263229-3 +... 6263265-3 +... 6263268-6 +... 6263276-5 +... 6263300-2 +... 6263402-5 +... 6263404-7 +... 6263444-2 +... 6263619-6 +... 6263734-4 +... 6263752-4 +... 6263824-4 +... 6263826-6 +... 6263927-8 +... 6263930-2 +... 6263980-7 +... 6264015-6 +... 6264034-7 +... 6264038-2 +... 6264083-2 +... 6264107-8 +... 6264112-4 +... +... ''' +>>> [x for x in numbers.splitlines() if x and not ntn.is_valid(x)] +[]