diff --git a/stdnum/kz/__init__.py b/stdnum/kz/__init__.py new file mode 100644 index 00000000..b69a9caf --- /dev/null +++ b/stdnum/kz/__init__.py @@ -0,0 +1,24 @@ +# __init__.py - collection of Kazakhstan 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 Kazakhstan numbers.""" + +# provide aliases +from stdnum.kz import bin as vat # noqa: F401 diff --git a/stdnum/kz/bin.py b/stdnum/kz/bin.py new file mode 100644 index 00000000..c9eba247 --- /dev/null +++ b/stdnum/kz/bin.py @@ -0,0 +1,115 @@ +# bin.py - functions for handling Kazakhstan BIN 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 + +"""BIN or "БСН" (Business Identification Number, "бизнес-сәйкестендіру нөмірі", +Kazakhstan tax number). + +This number consists of 12 digits and consists of five parts: + +* First part: consists of 4 digits representing the year (the last two digits) + and month of registration or re-registration. +* Second part: consists of 1 digit representing the type of legal entity or + individual entrepreneur (C): + 4 - for resident legal entities; + 5 - for non-resident legal entities; + 6 - for IP (C). +* Third part: consists of 1 digit providing additional information: + 0 - head unit of a legal entity or individual entrepreneur (S); + 1 - branch of a legal entity or individual entrepreneur (S); + 2 - representation of a legal entity or individual entrepreneur (S); + 3 - a peasant (farm) farm operating on the basis of joint entrepreneurship. +* Fourth part: consists of 5 digits and is a serial number. +* Fifth part: consists of 1 check digit. + +More information: + +* https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Kazakhstan-TIN.pdf + +>>> validate('940140000385') +'940140000385' +>>> validate('990 140 004 654') +'990140004654' +>>> validate('12345') +Traceback (most recent call last): + ... +InvalidLength: ... +>>> validate('12345678901X') +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> validate('949940000385') +Traceback (most recent call last): + ... +InvalidComponent: ... +>>> validate('940190000385') +Traceback (most recent call last): + ... +InvalidComponent: ... +>>> validate('940149000385') +Traceback (most recent call last): + ... +InvalidComponent: ... +>>> format('990 140 004 654') +'990140004654' +""" # 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 Kazakhstan BIN number. + + This checks the length and formatting. + """ + number = compact(number) + if len(number) != 12: + raise InvalidLength() + if not isdigits(number): + raise InvalidFormat() + if int(number[2:4]) not in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12): + raise InvalidComponent() + if number[4] not in ('4', '5', '6'): + raise InvalidComponent() + if number[5] not in ('0', '1', '2', '3'): + raise InvalidComponent() + return number + + +def is_valid(number): + """Check if the number is a valid Kazakhstan BIN 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_kz_bin.doctest b/tests/test_kz_bin.doctest new file mode 100644 index 00000000..8a74bf47 --- /dev/null +++ b/tests/test_kz_bin.doctest @@ -0,0 +1,335 @@ +test_kz_bin.doctest - more detailed doctests for stdnum.kz.bin 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.kz.bin module. It +tries to test more corner cases and detailed functionality that is not really +useful as module documentation. + +>>> from stdnum.kz import bin + + +Tests for some corner cases. + +>>> bin.validate('940140000385') +'940140000385' +>>> bin.validate('990 140 004 654') +'990140004654' +>>> bin.format('990 140 004 654') +'990140004654' +>>> bin.validate('12345') +Traceback (most recent call last): + ... +InvalidLength: ... +>>> bin.validate('12345678901X') +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> bin.validate('949940000385') +Traceback (most recent call last): + ... +InvalidComponent: ... +>>> bin.validate('940190000385') +Traceback (most recent call last): + ... +InvalidComponent: ... +>>> bin.validate('940149000385') +Traceback (most recent call last): + ... +InvalidComponent: ... + + +These have been found online and should all be valid numbers. + +>>> numbers = ''' +... +... 940140000385 +... 171140900016 +... 011140004703 +... 981240000488 +... 031140003104 +... 980740000057 +... 050540004455 +... 011140001654 +... 100240019642 +... 181141900093 +... 990 140 004 654 +... 991240015834 +... 200240900095 +... 210840900029 +... 930440000156 +... 101241001053 +... 061241010270 +... 150440002169 +... 941240000341 +... 011140001666 +... 190740015218 +... 131040003151 +... 140640004517 +... 030840002763 +... 160740009199 +... 930340000261 +... 980340000290 +... 990240003283 +... 140 840 008 587 +... 220140006999 +... 110140001589 +... 960240007379 +... 090440007304 +... 961040000685 +... 910940000091 +... 910940000081 +... 990740001453 +... 960540000294 +... 900740000062 +... 960540000303 +... 990640001380 +... 930640000331 +... 960140000468 +... 081240013779 +... 141040004756 +... 141040030110 +... 981140001105 +... 981140001115 +... 020440002422 +... 180840019420 +... 141140001547 +... 910740000113 +... 910740000044 +... 910740000143 +... 910740000153 +... 910740000133 +... 910740000123 +... 040840002339 +... 091240012910 +... 150340016393 +... 180840002049 +... 051140003851 +... 090440002968 +... 090440002978 +... 090440003074 +... 150740005302 +... 141140001150 +... 940540000528 +... 000140001813 +... 940640000492 +... 050340004626 +... 991240001567 +... 950340000572 +... 961140000232 +... 021140002283 +... 900440000031 +... 910940000071 +... 900440000051 +... 021140002134 +... 961240001086 +... 961240001036 +... 961240001016 +... 980740001213 +... 980740001362 +... 980740001322 +... 090440004151 +... 141140000836 +... 980540000991 +... 970140000975 +... 980540000971 +... 980640000955 +... 970240000890 +... 980540001018 +... 971040000994 +... 970140000985 +... 980640000995 +... 961240000860 +... 980640000975 +... 980640000915 +... 940740000595 +... 141140000549 +... 020740001571 +... 920140000232 +... 020740001600 +... 900340000058 +... 020740001620 +... 900540000065 +... 950140000624 +... 020740001757 +... 940340000273 +... 990640001251 +... 000340001800 +... 020740001551 +... 020740001452 +... 080740008619 +... 990840001338 +... 991140001791 +... 020740001581 +... 090440000545 +... 180840003632 +... 141140002664 +... 950240000648 +... 940740000614 +... 920440000352 +... 961240000992 +... 970240000979 +... 910840000107 +... 931040000104 +... 090440011223 +... 141140001140 +... 990840001328 +... 991040001551 +... 991240001498 +... 991040001563 +... 990840001318 +... 990940001212 +... 991140001781 +... 000840001333 +... 980340001050 +... 991040001526 +... 991240001438 +... 990940001252 +... 990840000944 +... 990340000553 +... 991040001514 +... 991140001800 +... 991140000317 +... 991140001810 +... 990940001202 +... 141140002347 +... 141140001517 +... 940740000624 +... 991140001860 +... 950140000644 +... 931240000449 +... 940140000533 +... 940540000469 +... 950440000615 +... 940540000518 +... 941040000522 +... 950440000625 +... 941240000669 +... 941240000659 +... 090440011898 +... 141140001329 +... 980640000886 +... 950540000400 +... 950140000634 +... 950540000440 +... 941240000649 +... 950540000371 +... 950540000351 +... 981240000953 +... 980940001111 +... 950740000498 +... 900640000099 +... 970140000955 +... 961240000890 +... 900540000055 +... 900540000035 +... 900540000015 +... 900540000045 +... 970140000965 +... 031240002328 +... 141140001775 +... 980740001154 +... 941240000699 +... 961040000467 +... 940640000472 +... 961040000665 +... 961040000615 +... 961040000635 +... 090440021657 +... 141140001229 +... 960740000477 +... 960440000469 +... 960440000489 +... 970840000831 +... 970140001031 +... 960440000439 +... 960440000409 +... 021140002085 +... 960740000453 +... 970140001100 +... 980840001306 +... 021140001999 +... 960440000419 +... 960440000429 +... 961040000576 +... 970440000430 +... 970840000811 +... 941240000679 +... 961240000979 +... 961240001006 +... 900340000028 +... 990640001291 +... 990640001300 +... 990640001281 +... 990640001271 +... 120940010470 +... 990640001231 +... 070940008327 +... 040140002670 +... 141140000456 +... 021140001929 +... 980940001161 +... 980940001171 +... 980940001131 +... 980940001240 +... 980940001191 +... 980940001210 +... 980940001200 +... 980940001151 +... 980940001181 +... 980940001141 +... 980940001220 +... 980940001230 +... 141140001051 +... 980740001203 +... 980740001134 +... 980740001312 +... 980740001144 +... 980740001114 +... 980740001174 +... 980740001233 +... 980740000364 +... 120940007261 +... 980740001302 +... 980740001223 +... 980740001243 +... 980740001263 +... 980740001293 +... 141140001205 +... 021140001897 +... 021040001777 +... 021140002055 +... 021140002075 +... 990540001199 +... 021240002336 +... 021140001959 +... 021140001979 +... 021140001909 +... 021140001919 +... 021140001969 +... 021140001873 +... 020840001515 +... 021040001787 +... 180840015606 +... 180840013401 +... 210740028187 +... +... ''' +>>> [x for x in numbers.splitlines() if x and not bin.is_valid(x)] +[]