lists.arthurdejong.org
RSS feed

python-stdnum branch master updated. 2.2-8-g23be163

[Date Prev][Date Next] [Thread Prev][Thread Next]

python-stdnum branch master updated. 2.2-8-g23be163



This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "python-stdnum".

The branch, master has been updated
       via  23be163d33c1444d499b95559a6aceac2064bff4 (commit)
       via  9f1bc4ab59a92506f46d2633d36b904174809af4 (commit)
      from  8a212c7cd1324f326ed30f61b7b2ac43cdf69572 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
https://arthurdejong.org/git/python-stdnum/commit/?id=23be163d33c1444d499b95559a6aceac2064bff4

commit 23be163d33c1444d499b95559a6aceac2064bff4
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Tue Aug 11 18:45:36 2026 +0200

    Implement check digit validation for Oman VAT number
    
    This algorithm only covers the last 5 digits for which weights could be
    reliably determined. This was done based one numbers collected and
    tested on the validation service.

diff --git a/stdnum/om/vat.py b/stdnum/om/vat.py
index ae18abd..b1a6220 100644
--- a/stdnum/om/vat.py
+++ b/stdnum/om/vat.py
@@ -2,6 +2,7 @@
 # coding: utf-8
 #
 # Copyright (C) 2026 Devashish Moghe
+# Copyright (C) 2026 Arthur de Jong
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -32,6 +33,10 @@ More information:
 
 >>> validate('OM1100006083')
 'OM1100006083'
+>>> validate('OM1100006084')
+Traceback (most recent call last):
+    ...
+InvalidChecksum: ...
 >>> validate('OM 1100 0060 83')
 'OM1100006083'
 >>> validate('110000608312')  # missing prefix
@@ -66,6 +71,14 @@ def compact(number: str) -> str:
     return clean(number, ' -').upper().strip()
 
 
+def calc_check_digit(number: str) -> str:
+    """Calculate the check digit."""
+    # We only know the weights of the last 5 digits so we skip the first 4
+    weights = (1, 6, 3, 7, 9)
+    check = (1 + sum(w * int(n) for w, n in zip(weights, number[6:]))) % 11
+    return 'X' if check == 10 else str(check)
+
+
 def validate(number: str) -> str:
     """Check if the number is a valid Oman VAT number. This checks the
     length and formatting."""
@@ -74,6 +87,8 @@ def validate(number: str) -> str:
         raise InvalidLength()
     if not _vatin_re.match(number):
         raise InvalidFormat()
+    if calc_check_digit(number) != number[-1]:
+        raise InvalidChecksum()
     return number
 
 
diff --git a/tests/test_om_vat.doctest b/tests/test_om_vat.doctest
index 2ec8e2a..c11c900 100644
--- a/tests/test_om_vat.doctest
+++ b/tests/test_om_vat.doctest
@@ -59,7 +59,22 @@ These have been found online and should all be valid numbers.
 ... OM1100006083
 ... OM1100006999
 ... OM1100011333
+... OM1100012379
 ... OM1100018164
+... OM1100019343
+... OM110002816X
+... OM1100038165
+... OM1100255952
+... OM1100355953
+... OM1100426520
+... OM1100426600
+... OM1100426619
+... OM1100426627
+... OM1100426707
+... OM1100426723
+... OM1100427603
+... OM1100455954
+... OM1200362117
 ...
 ... '''
 >>> [x for x in numbers.splitlines() if x and not vat.is_valid(x)]

https://arthurdejong.org/git/python-stdnum/commit/?id=9f1bc4ab59a92506f46d2633d36b904174809af4

commit 9f1bc4ab59a92506f46d2633d36b904174809af4
Author: DMZ22 <devashishmoghe@gmail.com>
Date:   Fri Jul 24 10:02:15 2026 +0530

    Add Oman VAT number (VATIN) validation
    
    The Oman VAT identification number (VATIN), issued by the Oman Tax
    Authority, consists of the letters OM followed by 10 digits (12
    characters in total). It has no check digit and is verified online
    through the tax authority portal, so this validates length and
    formatting.
    
    Partially addresses #408 (the VAT/VATIN form; the 7-digit entity "tax
    card number" is an unstructured all-digit sequence for which format
    validation adds little value).
    
    Relates to https://github.com/arthurdejong/python-stdnum/issues/408
    Closes https://github.com/arthurdejong/python-stdnum/pull/505

diff --git a/stdnum/om/__init__.py b/stdnum/om/__init__.py
new file mode 100644
index 0000000..5b3f395
--- /dev/null
+++ b/stdnum/om/__init__.py
@@ -0,0 +1,19 @@
+# __init__.py - collection of Omani numbers
+# coding: utf-8
+#
+# Copyright (C) 2026 Devashish Moghe
+#
+# 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, see <https://www.gnu.org/licenses/>.
+
+"""Collection of Omani numbers."""
diff --git a/stdnum/om/vat.py b/stdnum/om/vat.py
new file mode 100644
index 0000000..ae18abd
--- /dev/null
+++ b/stdnum/om/vat.py
@@ -0,0 +1,85 @@
+# vat.py - functions for handling Oman VAT numbers
+# coding: utf-8
+#
+# Copyright (C) 2026 Devashish Moghe
+#
+# 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, see <https://www.gnu.org/licenses/>.
+
+"""VAT (Oman value added tax number).
+
+The Oman VAT identification number (VATIN) is issued by the Oman Tax
+Authority to businesses registered for value added tax. It consists of the
+letters ``OM`` followed by 10 digits (12 characters in total).
+
+There is no check digit; a number can be verified online through the Oman
+Tax Authority portal.
+
+More information:
+
+* https://tms.taxoman.gov.om/portal/vat-tax
+* https://tms.taxoman.gov.om/portal/vatin-validation
+
+>>> validate('OM1100006083')
+'OM1100006083'
+>>> validate('OM 1100 0060 83')
+'OM1100006083'
+>>> validate('110000608312')  # missing prefix
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> validate('OM110000608')  # too short
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
+>>> validate('OM11000060AB')
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+"""
+
+from __future__ import annotations
+
+import re
+
+from stdnum.exceptions import *
+from stdnum.util import clean
+
+
+# the VATIN consists of the OM prefix followed by ten digits
+_vatin_re = re.compile(r'^OM[0-9]{9}[0-9X]$')
+
+
+def compact(number: str) -> str:
+    """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: str) -> str:
+    """Check if the number is a valid Oman VAT number. This checks the
+    length and formatting."""
+    number = compact(number)
+    if len(number) != 12:
+        raise InvalidLength()
+    if not _vatin_re.match(number):
+        raise InvalidFormat()
+    return number
+
+
+def is_valid(number: str) -> bool:
+    """Check if the number is a valid Oman VAT number."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
diff --git a/tests/test_om_vat.doctest b/tests/test_om_vat.doctest
new file mode 100644
index 0000000..2ec8e2a
--- /dev/null
+++ b/tests/test_om_vat.doctest
@@ -0,0 +1,66 @@
+test_om_vat.doctest - more detailed doctests for the stdnum.om.vat module
+
+Copyright (C) 2026 Devashish Moghe
+
+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, see <https://www.gnu.org/licenses/>.
+
+
+This file contains more detailed doctests for the stdnum.om.vat module. It
+tries to validate a number of numbers that have been found online.
+
+>>> from stdnum.om import vat
+>>> from stdnum.exceptions import *
+
+
+Tests for some corner cases.
+
+>>> vat.validate('OM1100006083')
+'OM1100006083'
+>>> vat.validate('om1100006083')
+'OM1100006083'
+>>> vat.compact('OM 1100 0060 83')
+'OM1100006083'
+>>> vat.is_valid('OM1100006083')
+True
+>>> vat.is_valid('OM110000608')
+False
+>>> vat.validate('OM110000608')
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
+>>> vat.validate('110000608312')
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> vat.validate('OM11000060AB')
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+
+
+These have been found online and should all be valid numbers.
+
+>>> numbers = '''
+...
+... OM1100002920
+... OM110000378X
+... OM1100005523
+... OM1100006083
+... OM1100006999
+... OM1100011333
+... OM1100018164
+...
+... '''
+>>> [x for x in numbers.splitlines() if x and not vat.is_valid(x)]
+[]

-----------------------------------------------------------------------

Summary of changes:
 stdnum/{gr => om}/__init__.py                      |   6 +-
 stdnum/om/vat.py                                   | 100 +++++++++++++++++++++
 .../{test_si_emso.doctest => test_om_vat.doctest}  |  75 +++++++++-------
 3 files changed, 144 insertions(+), 37 deletions(-)
 copy stdnum/{gr => om}/__init__.py (85%)
 create mode 100644 stdnum/om/vat.py
 copy tests/{test_si_emso.doctest => test_om_vat.doctest} (51%)


hooks/post-receive
-- 
python-stdnum