Skip to content

Commit acda255

Browse files
holvitolvarthurdejong
authored andcommitted
Add DE Leitweg-ID
Closes #491
1 parent c7a7fd1 commit acda255

File tree

2 files changed

+164
-0
lines changed

2 files changed

+164
-0
lines changed

stdnum/de/leitweg.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# leitweg.py - functions for handling Leitweg-ID
2+
# coding: utf-8
3+
#
4+
# Copyright (C) 2025 Holvi Payment Services Oy
5+
#
6+
# This library is free software; you can redistribute it and/or
7+
# modify it under the terms of the GNU Lesser General Public
8+
# License as published by the Free Software Foundation; either
9+
# version 2.1 of the License, or (at your option) any later version.
10+
#
11+
# This library is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
# Lesser General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU Lesser General Public
17+
# License along with this library; if not, write to the Free Software
18+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19+
# 02110-1301 USA
20+
21+
"""Leitweg-ID, a buyer reference or routing identifier for electronic invoices.
22+
23+
For the successful transmission of an electronic invoice as invoicing party or
24+
sender, a unique identification and addressing of the invoice recipient is
25+
required. The Leitweg-ID must be transmitted as a mandatory requirement for
26+
electronic invoicing to public contracting authorities in the federal
27+
administration.
28+
29+
More information:
30+
31+
* https://leitweg-id.de/
32+
* https://de.wikipedia.org/wiki/Leitweg-ID
33+
* https://xeinkauf.de/app/uploads/2022/11/Leitweg-ID-Formatspezifikation-v2-0-2-1.pdf
34+
35+
>>> validate('991-03730-19')
36+
'991-03730-19'
37+
>>> validate('1-03730-19')
38+
Traceback (most recent call last):
39+
...
40+
InvalidFormat: ...
41+
"""
42+
43+
from __future__ import annotations
44+
45+
import re
46+
47+
from stdnum.exceptions import *
48+
from stdnum.iso7064 import mod_97_10
49+
50+
51+
_pattern = re.compile(r'[0-9]{2,12}(-[0-9A-Z]{,30})?-[0-9]{2}')
52+
53+
54+
def compact(number: str) -> str:
55+
"""
56+
Convert the number to the minimal representation. This strips the
57+
number of any valid separators and removes surrounding whitespace.
58+
"""
59+
return number.strip().upper() # no valid separators, dashes part of the format
60+
61+
62+
def validate(number: str) -> str:
63+
"""
64+
Check if the number provided is valid. This checks the format, state or
65+
federal government code, and check digits.
66+
"""
67+
if not isinstance(number, str):
68+
raise InvalidFormat()
69+
number = compact(number)
70+
# 2.1 Bestandteile der Leitweg-ID
71+
if not 5 <= len(number) <= 46:
72+
raise InvalidLength()
73+
# 2.1 Bestandteile der Leitweg-ID
74+
if not re.fullmatch(_pattern, number):
75+
raise InvalidFormat()
76+
# 2.2.1 Kennzahl des Bundeslandes/des Bundes
77+
if not number[:2] in {
78+
'01', '02', '03', '04', '05', '06', '07', '08', '09', '10',
79+
'11', '12', '13', '14', '15', '16', '99',
80+
}:
81+
raise InvalidComponent()
82+
# 2.4 Prüfziffer
83+
mod_97_10.validate(number.replace('-', ''))
84+
return number
85+
86+
87+
def is_valid(number: str) -> bool:
88+
"""Check if the number provided is valid. This checks the length and
89+
check digit."""
90+
try:
91+
return bool(validate(number))
92+
except ValidationError:
93+
return False

tests/test_de_leitweg.doctest

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
test_de_leitweg.doctest - more detailed doctests for the stdnum.de.leitweg module
2+
3+
Copyright (C) 2025 Holvi Payment Services Oy
4+
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18+
02110-1301 USA
19+
20+
21+
This file contains more detailed doctests for the stdnum.de.leitweg module. It
22+
tries to validate a number of numbers that have been found online.
23+
24+
>>> from stdnum.de import leitweg
25+
>>> from stdnum.exceptions import *
26+
27+
28+
These are present in the verzeichnis.leitweg-id.de directory and should all be valid numbers.
29+
30+
>>> valid_numbers = '''
31+
...
32+
... 16066069-0001-38
33+
... 13-L75810002000-60
34+
... 057660004004-31001-55
35+
... 991-03730-19
36+
... 992-90009-96
37+
... 08315033-ESCHBACH6626-66
38+
... 09274154-NFH-05
39+
... 05370032-WDF5271-06
40+
... 09778137-ETTRINGEN868331262-55
41+
... 08325024-787394066-26
42+
... 15088205-LEUNA6877-16
43+
... 09471131-GDEFD96158-46
44+
... 09780119-RATHAUSDIETMANNSRIED7247-90
45+
... 09176111-ADELSCHLAG-11
46+
... 09274193-WHM-62
47+
... 09177127-GEMEINDELENGDORF-24
48+
... 09176122-EGWEIL-38
49+
... 06533010-L357921748-84
50+
... 09176149-NASSENFELS-39
51+
... 09177131-NEUCHING-08
52+
...
53+
... '''
54+
>>> [x for x in valid_numbers.splitlines() if x and not leitweg.is_valid(x)]
55+
[]
56+
57+
58+
Unknown Kennzahl des Bundes(landes).
59+
60+
>>> leitweg.validate('55-55-20')
61+
Traceback (most recent call last):
62+
...
63+
InvalidComponent: ...
64+
65+
66+
Invalid checksum.
67+
68+
>>> leitweg.validate('992-90009-97')
69+
Traceback (most recent call last):
70+
...
71+
InvalidChecksum: ...

0 commit comments

Comments
 (0)