From 56e906a2c92cec574a1a9fed89b256824c56778c Mon Sep 17 00:00:00 2001 From: Leandro Regueiro Date: Thu, 12 Jan 2023 11:40:49 +0100 Subject: [PATCH] Add TIN number for Brunei Fixes #308 --- stdnum/bn/__init__.py | 24 ++++++++++ stdnum/bn/rocbn.py | 86 +++++++++++++++++++++++++++++++++++ tests/test_bn_rocbn.doctest | 91 +++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 stdnum/bn/__init__.py create mode 100644 stdnum/bn/rocbn.py create mode 100644 tests/test_bn_rocbn.doctest diff --git a/stdnum/bn/__init__.py b/stdnum/bn/__init__.py new file mode 100644 index 00000000..836a2962 --- /dev/null +++ b/stdnum/bn/__init__.py @@ -0,0 +1,24 @@ +# __init__.py - collection of Brunei 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 Brunei numbers.""" + +# provide aliases +from stdnum.bn import rocbn as vat # noqa: F401 diff --git a/stdnum/bn/rocbn.py b/stdnum/bn/rocbn.py new file mode 100644 index 00000000..69783dac --- /dev/null +++ b/stdnum/bn/rocbn.py @@ -0,0 +1,86 @@ +# rocbn.py - functions for handling Brunei ROCBN 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 + +"""ROCBN (Brunei tax number). + +This number consists of 8 digits, prepended by the letter 'P' for +Sole-Proprietorships or Partnerships, or by the letters 'RC' for Private +Limited Companies and Public Companies, or by the letters 'RFC' for Foreign +Branch of companies. + +More information: + +* https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Brunei-darussalam-TIN%20.pdf + +>>> validate('RC00004866') +'RC00004866' +>>> validate('RFC/00000772') +'RFC00000772' +>>> validate('12345') +Traceback (most recent call last): + ... +InvalidLength: ... +>>> format('RFC/00000772') +'RFC00000772' +""" # 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, ' -/').upper().strip() + + +def validate(number): + """Check if the number is a valid Brunei ROCBN number. + + This checks the length and formatting. + """ + number = compact(number) + if len(number) not in (9, 10, 11): + raise InvalidLength() + if not isdigits(number[-8:]): + raise InvalidFormat() + if len(number) == 9 and not number.startswith('P'): + raise InvalidFormat() + if len(number) == 10 and not number.startswith('RC'): + raise InvalidFormat() + if len(number) == 11 and not number.startswith('RFC'): + raise InvalidFormat() + return number + + +def is_valid(number): + """Check if the number is a valid Brunei ROCBN 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_bn_rocbn.doctest b/tests/test_bn_rocbn.doctest new file mode 100644 index 00000000..9e4df4c2 --- /dev/null +++ b/tests/test_bn_rocbn.doctest @@ -0,0 +1,91 @@ +test_bn_rocbn.doctest - more detailed doctests for stdnum.bn.rocbn 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.bn.rocbn module. It +tries to test more corner cases and detailed functionality that is not really +useful as module documentation. + +>>> from stdnum.bn import rocbn + + +Tests for some corner cases. + +>>> rocbn.validate('P00084071') +'P00084071' +>>> rocbn.validate('RC00004866') +'RC00004866' +>>> rocbn.validate('RC/6953/2007') +'RC69532007' +>>> rocbn.validate('RFC/00000772') +'RFC00000772' +>>> rocbn.validate('12345') +Traceback (most recent call last): + ... +InvalidLength: ... +>>> rocbn.validate('P1234567X') +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> rocbn.validate('RC1234567X') +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> rocbn.validate('RFC1234567X') +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> rocbn.validate('V12345678') +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> rocbn.validate('VV12345678') +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> rocbn.validate('VVV12345678') +Traceback (most recent call last): + ... +InvalidFormat: ... +>>> rocbn.format('P00084071') +'P00084071' +>>> rocbn.format('RC/6953/2007') +'RC69532007' +>>> rocbn.format('RFC/00000772') +'RFC00000772' + + +These have been found online and should all be valid numbers. + +>>> numbers = ''' +... +... P20008207 +... P00084071 +... P20006927 +... RC/6953/2007 +... RC/00006326 +... RC/00009742 +... RC00004866 +... RC00006420 +... RFC00000061 +... RFC/00000772 +... +... ''' +>>> [x for x in numbers.splitlines() if x and not rocbn.is_valid(x)] +[]