lists.arthurdejong.org
RSS feed

python-stdnum branch master updated. 1.18-29-gf58e08d

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

python-stdnum branch master updated. 1.18-29-gf58e08d



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  f58e08d1f082554d427da82bea9691872ea51dfa (commit)
      from  d0f4c1a5998b63a76089be5797acff1e489ccd86 (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=f58e08d1f082554d427da82bea9691872ea51dfa

commit f58e08d1f082554d427da82bea9691872ea51dfa
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sun Aug 13 18:36:31 2023 +0200

    Validate European VAT numbers with EU or IM prefix
    
    Closes https://github.com/arthurdejong/python-stdnum/pull/417

diff --git a/stdnum/eu/oss.py b/stdnum/eu/oss.py
new file mode 100644
index 0000000..e9d0349
--- /dev/null
+++ b/stdnum/eu/oss.py
@@ -0,0 +1,120 @@
+# oss.py - functions for handling European VAT numbers
+# coding: utf-8
+#
+# Copyright (C) 2023 Arthur de Jong
+# Copyright (C) 2023 Sergi Almacellas Abellana
+#
+# 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
+
+"""OSS (European VAT on e-Commerce - One Stop Shop).
+
+This covers number that have been issued under the non-union scheme and the
+import scheme of the European VAT on e-Commerce one stop shop. Numbers under
+the non-union scheme are assigned to foreign companies providing digital
+services to European Union consumers. Numbers under the import scheme are
+assigned for importing goods from a third country (through an intermediary).
+
+This is also called MOSS (mini One Stop Shop) and VoeS (VAT on e-Services).
+The number under the import scheme is also called IOSS (Import One Stop Shop)
+number.
+
+Numbers under the non-union scheme are in the format EUxxxyyyyyz. For the
+import scheme the format is IMxxxyyyyyyz. An intermediary will also get an
+number in the format INxxxyyyyyyz but that may not be used as VAT
+identification number.
+
+There appears to be a check digit algorithm but the FITSDEV2-SC12-TS-Mini1SS
+(Mini-1SS – Technical Specifications) document that describes the algorithm
+appears to not be publicly available.
+
+More information:
+
+* https://vat-one-stop-shop.ec.europa.eu/one-stop-shop/register-oss_en
+* 
https://europa.eu/youreurope/business/taxation/vat/vat-digital-services-moss-scheme/index_en.htm
+* https://eur-lex.europa.eu/legal-content/EN/TXT/HTML/?uri=CELEX:32002L0038
+
+>>> validate('EU 372022452')
+'EU372022452'
+"""
+
+
+from stdnum.exceptions import *
+from stdnum.util import clean, isdigits
+
+
+ISO_3166_1_MEMBER_STATES = (
+    '040',  # Austria
+    '056',  # Belgium
+    '100',  # Bulgaria
+    '191',  # Croatia
+    '196',  # Cyprus
+    '203',  # Czechia
+    '208',  # Denmark
+    '233',  # Estonia
+    '246',  # Finland
+    '250',  # France
+    '276',  # Germany
+    '300',  # Greece
+    '348',  # Hungary
+    '372',  # Ireland
+    '380',  # Italy
+    '428',  # Latvia
+    '440',  # Lithuania
+    '442',  # Luxembourg
+    '470',  # Malta
+    '528',  # Netherland
+    '616',  # Poland
+    '620',  # Portugal
+    '642',  # Romania
+    '703',  # Slovakia
+    '705',  # Slovenia
+    '724',  # Spain
+    '752',  # Sweden
+    '900',  # N. Ireland
+)
+"""The collection of member state codes (for MSI) that may make up a VAT 
number."""
+
+
+def compact(number):
+    """Compact European VAT Number"""
+    return clean(number, ' -').upper().strip()
+
+
+def validate(number):
+    """Validate European VAT Number"""
+    number = compact(number)
+    if number.startswith('EU'):
+        if len(number) != 11:
+            raise InvalidLength()
+    elif number.startswith('IM'):
+        if len(number) != 12:
+            raise InvalidLength()
+    else:
+        raise InvalidComponent()
+    if not isdigits(number[2:]):
+        raise InvalidFormat()
+    if number[2:5] not in ISO_3166_1_MEMBER_STATES:
+        raise InvalidComponent()
+    return number
+
+
+def is_valid(number):
+    """Check if the number is a valid VAT number. This performs the
+    country-specific check for the number."""
+    try:
+        return bool(validate(number))
+    except ValidationError:
+        return False
diff --git a/stdnum/eu/vat.py b/stdnum/eu/vat.py
index 074ec89..33d18b8 100644
--- a/stdnum/eu/vat.py
+++ b/stdnum/eu/vat.py
@@ -39,6 +39,7 @@ that country.
 ['nl']
 """
 
+from stdnum.eu import oss
 from stdnum.exceptions import *
 from stdnum.util import clean, get_cc_module, get_soap_client
 
@@ -62,6 +63,8 @@ def _get_cc_module(cc):
     """Get the VAT number module based on the country code."""
     # Greece uses a "wrong" country code
     cc = cc.lower()
+    if cc in ('eu', 'im'):
+        return oss
     if cc == 'el':
         cc = 'gr'
     if cc not in MEMBER_STATES:
diff --git a/tests/test_eu_oss.doctest b/tests/test_eu_oss.doctest
new file mode 100644
index 0000000..23889a4
--- /dev/null
+++ b/tests/test_eu_oss.doctest
@@ -0,0 +1,68 @@
+test_eu_oss.doctest - more detailed doctests for the stdnum.eu.oss module
+
+Copyright (C) 2023 Arthur de Jong
+Copyright (C) 2023 Sergi Almacellas Abellana
+
+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.eu.oss module.
+
+>>> from stdnum.eu import oss
+
+#>>> from stdnum.exceptions import *
+
+
+Extra tests for some corner cases.
+
+>>> oss.validate('AA826010755')  # first digits wrong
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> oss.validate('EU372A22452')  # some digits not numeric
+Traceback (most recent call last):
+    ...
+InvalidFormat: ...
+>>> oss.validate('EU123010755')  # MSI wrong
+Traceback (most recent call last):
+    ...
+InvalidComponent: ...
+>>> oss.validate('EU3720000224')
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
+>>> oss.validate('IM372022452')
+Traceback (most recent call last):
+    ...
+InvalidLength: ...
+
+
+
+These have been found online and should all be valid numbers. Interestingly
+the VIES VAT number validation does not support validating numbers issued
+under the non-union or import schemes.
+
+https://en.wikipedia.org/wiki/VAT_identification_number also lists
+EU826010755 for Godaddy and EU826009064 for AWS but neither seem to be valid.
+
+>>> numbers = '''
+...
+... EU372022452
+... IM3720000224
+...
+... '''
+>>> [x for x in numbers.splitlines() if x and not oss.is_valid(x)]
+[]
diff --git a/tests/test_eu_vat.doctest b/tests/test_eu_vat.doctest
index cde37af..7f81ec4 100644
--- a/tests/test_eu_vat.doctest
+++ b/tests/test_eu_vat.doctest
@@ -258,6 +258,8 @@ These have been found online and should all be valid 
numbers.
 ... EL: 094279805
 ... El 800 179 925
 ...
+... EU372022452
+...
 ... FI 02459042
 ... FI 0982651-1
 ... FI 10320534

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

Summary of changes:
 stdnum/eu/oss.py          | 120 ++++++++++++++++++++++++++++++++++++++++++++++
 stdnum/eu/vat.py          |   3 ++
 tests/test_eu_oss.doctest |  68 ++++++++++++++++++++++++++
 tests/test_eu_vat.doctest |   2 +
 4 files changed, 193 insertions(+)
 create mode 100644 stdnum/eu/oss.py
 create mode 100644 tests/test_eu_oss.doctest


hooks/post-receive
-- 
python-stdnum