1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
|
# excise.py - functions for handling EU Excise numbers
# coding: utf-8
#
# Copyright (C) 2023 Cédric Krier
#
# 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
"""European Excise Number
The excise duty identification number assigned to businesses authorised to
operate with excise goods (e.g. alcohol, tobacco, energy products, etc.).
The number is issued by the national customs or tax authority of the Member
State where the business is established. The number consists of a two-letter
country code followed by up to 13 alphanumeric characters.
More information:
* https://ec.europa.eu/taxation_customs/dds2/seed/
>>> validate('LU 987ABC')
'LU00000987ABC'
"""
from __future__ import annotations
from stdnum.eu.vat import MEMBER_STATES
from stdnum.exceptions import *
from stdnum.util import NumberValidationModule, clean, get_cc_module
_country_modules = dict()
def _get_cc_module(cc: str) -> NumberValidationModule | None:
"""Get the Excise number module based on the country code."""
cc = cc.lower()
if cc not in MEMBER_STATES:
raise InvalidComponent()
if cc not in _country_modules:
_country_modules[cc] = get_cc_module(cc, 'excise')
return _country_modules[cc]
def compact(number: str) -> str:
"""Convert the number to the minimal representation. This strips the number
of any valid separators and removes surrounding whitespace."""
number = clean(number, ' ').upper().strip()
if len(number) < 13:
number = number[:2] + number[2:].zfill(11)
return number
def validate(number: str) -> str:
"""Check if the number is a valid Excise number."""
number = compact(number)
if len(number) != 13:
raise InvalidLength()
module = _get_cc_module(number[:2])
if module:
module.validate(number)
return number
def is_valid(number: str) -> bool:
"""Check if the number is a valid excise number."""
try:
return bool(validate(number))
except ValidationError:
return False
|