From 4533a83e7f01e0de7b5e6ab50aee7ec48b0bbb07 Mon Sep 17 00:00:00 2001 From: Tuukka Tolvanen Date: Fri, 19 Dec 2025 15:53:28 +0200 Subject: [PATCH 1/2] Add DE Leitweg-ID --- stdnum/de/leitweg.py | 100 ++++++++++++++++++++++++++++++++++ tests/test_de_leitweg.doctest | 71 ++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 stdnum/de/leitweg.py create mode 100644 tests/test_de_leitweg.doctest diff --git a/stdnum/de/leitweg.py b/stdnum/de/leitweg.py new file mode 100644 index 00000000..cc532118 --- /dev/null +++ b/stdnum/de/leitweg.py @@ -0,0 +1,100 @@ +# leitweg.py - functions for handling Leitweg-ID +# coding: utf-8 +# +# Copyright (C) 2025 Holvi Payment Services Oy +# +# 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 + +"""Leitweg-ID, a buyer reference or routing identifier for electronic invoices. + +For the successful transmission of an electronic invoice as invoicing party or +sender, a unique identification and addressing of the invoice recipient is +required. The Leitweg-ID must be transmitted as a mandatory requirement for +electronic invoicing to public contracting authorities in the federal +administration. + +More information: + +* https://leitweg-id.de/ +* https://de.wikipedia.org/wiki/Leitweg-ID +* https://xeinkauf.de/app/uploads/2022/11/Leitweg-ID-Formatspezifikation-v2-0-2-1.pdf + +>>> validate('991-03730-19') +'991-03730-19' +>>> validate('1-03730-19') +Traceback (most recent call last): + ... +InvalidFormat: ... +""" + +from __future__ import annotations + +import re + +from stdnum.exceptions import * + + +pattern = re.compile(r'[0-9]{2,12}(-[0-9A-Z]{,30})?-[0-9]{2}') + + +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 number.strip().upper() # no valid separators, dashes part of the format + + +def validate(number: str) -> str: + """ + Check if the number provided is valid. This checks the format, state or + federal government code, and check digits. + """ + if not isinstance(number, str): + raise InvalidFormat() + + number = compact(number) + + # 2.1 Bestandteile der Leitweg-ID + if not 5 <= len(number) <= 46: + raise InvalidLength() + + # 2.1 Bestandteile der Leitweg-ID + if not re.fullmatch(pattern, number): + raise InvalidFormat() + + # 2.2.1 Kennzahl des Bundeslandes/des Bundes + if not number[:2] in { + '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', + '11', '12', '13', '14', '15', '16', '99', + }: + raise InvalidComponent() + + # 2.4 Prüfziffer + indices = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + if int(''.join(str(indices.index(c)) for c in number if c != '-')) % 97 != 1: + raise InvalidChecksum() + + return number + + +def is_valid(number: str) -> bool: + """Check if the number provided is valid. This checks the length and + check digit.""" + try: + return bool(validate(number)) + except ValidationError: + return False diff --git a/tests/test_de_leitweg.doctest b/tests/test_de_leitweg.doctest new file mode 100644 index 00000000..2f688902 --- /dev/null +++ b/tests/test_de_leitweg.doctest @@ -0,0 +1,71 @@ +test_de_leitweg.doctest - more detailed doctests for the stdnum.de.leitweg module + +Copyright (C) 2015 Holvi Payment Services Oy + +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.de.leitweg module. It +tries to validate a number of numbers that have been found online. + +>>> from stdnum.de import leitweg +>>> from stdnum.exceptions import * + + +These are present in the verzeichnis.leitweg-id.de directory and should all be valid numbers. + +>>> valid_numbers = ''' +... +... 16066069-0001-38 +... 13-L75810002000-60 +... 057660004004-31001-55 +... 991-03730-19 +... 992-90009-96 +... 08315033-ESCHBACH6626-66 +... 09274154-NFH-05 +... 05370032-WDF5271-06 +... 09778137-ETTRINGEN868331262-55 +... 08325024-787394066-26 +... 15088205-LEUNA6877-16 +... 09471131-GDEFD96158-46 +... 09780119-RATHAUSDIETMANNSRIED7247-90 +... 09176111-ADELSCHLAG-11 +... 09274193-WHM-62 +... 09177127-GEMEINDELENGDORF-24 +... 09176122-EGWEIL-38 +... 06533010-L357921748-84 +... 09176149-NASSENFELS-39 +... 09177131-NEUCHING-08 +... +... ''' +>>> [x for x in valid_numbers.splitlines() if x and not leitweg.is_valid(x)] +[] + + +Unknown Kennzahl des Bundes(landes). + +>>> leitweg.validate('55-55-20') +Traceback (most recent call last): + ... +InvalidComponent: ... + + +Invalid checksum. + +>>> leitweg.validate('992-90009-97') +Traceback (most recent call last): + ... +InvalidChecksum: ... From 668e621026819bf7a51fed97d2e2160727ec957d Mon Sep 17 00:00:00 2001 From: Tuukka Tolvanen <32448511+holvitolv@users.noreply.github.com> Date: Fri, 19 Dec 2025 16:24:35 +0200 Subject: [PATCH 2/2] Update test_de_leitweg.doctest to 2025 :D --- tests/test_de_leitweg.doctest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_de_leitweg.doctest b/tests/test_de_leitweg.doctest index 2f688902..627ea62b 100644 --- a/tests/test_de_leitweg.doctest +++ b/tests/test_de_leitweg.doctest @@ -1,6 +1,6 @@ test_de_leitweg.doctest - more detailed doctests for the stdnum.de.leitweg module -Copyright (C) 2015 Holvi Payment Services Oy +Copyright (C) 2025 Holvi Payment Services Oy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public