Coverage for presidio_analyzer / predefined_recognizers / country_specific / germany / de_tax_id_recognizer.py: 96%

27 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-03-29 09:03 +0000

1from typing import List, Optional 

2 

3from presidio_analyzer import Pattern, PatternRecognizer 

4 

5 

6class DeTaxIdRecognizer(PatternRecognizer): 

7 """ 

8 Recognizes German Steueridentifikationsnummer (Steuer-IdNr.). 

9 

10 The Steueridentifikationsnummer is a unique 11-digit personal tax identification 

11 number issued by the Bundeszentralamt für Steuern to every person registered in 

12 Germany. It does not change over a person's lifetime. 

13 

14 Legal basis: §§ 139a–139e Abgabenordnung (AO), in force since 2007. 

15 Data protection: DSGVO Art. 4 Nr. 1 (personenbezogene Daten), BDSG. 

16 

17 Format: 

18 - 11 digits 

19 - First digit: 1–9 (never 0) 

20 - Digit 11: check digit (ISO 7064 Mod 11, 10 variant) 

21 

22 Examples (fictitious): 86095742719, 12345678903 

23 

24 :param patterns: List of patterns to be used by this recognizer 

25 :param context: List of context words to increase confidence in detection 

26 :param supported_language: Language this recognizer supports 

27 :param supported_entity: The entity this recognizer can detect 

28 """ 

29 

30 PATTERNS = [ 

31 Pattern( 

32 "Steueridentifikationsnummer (High)", 

33 r"\b[1-9]\d{10}\b", 

34 0.5, 

35 ), 

36 ] 

37 

38 CONTEXT = [ 

39 "steueridentifikationsnummer", 

40 "steuer-id", 

41 "steuerid", 

42 "steuerliche identifikationsnummer", 

43 "steuerliche identifikation", 

44 "persönliche identifikationsnummer", 

45 "steuer identifikation", 

46 "idnr", 

47 "steuer-idnr", 

48 "steuernummer", 

49 "bzst", 

50 ] 

51 

52 def __init__( 

53 self, 

54 patterns: Optional[List[Pattern]] = None, 

55 context: Optional[List[str]] = None, 

56 supported_language: str = "de", 

57 supported_entity: str = "DE_TAX_ID", 

58 name: Optional[str] = None, 

59 ): 

60 patterns = patterns if patterns else self.PATTERNS 

61 context = context if context else self.CONTEXT 

62 super().__init__( 

63 supported_entity=supported_entity, 

64 patterns=patterns, 

65 context=context, 

66 supported_language=supported_language, 

67 name=name, 

68 ) 

69 

70 def validate_result(self, pattern_text: str) -> Optional[bool]: 

71 """ 

72 Validate the Steueridentifikationsnummer using the official checksum algorithm. 

73 

74 Algorithm: ISO 7064 Mod 11, 10 variant as specified by the 

75 Bundeszentralamt für Steuern. 

76 

77 :param pattern_text: the text to validate (11 digits) 

78 :return: True if valid, False if invalid 

79 """ 

80 if len(pattern_text) != 11 or not pattern_text.isdigit(): 

81 return False 

82 if pattern_text[0] == "0": 

83 return False 

84 

85 digits = [int(d) for d in pattern_text] 

86 

87 # Check that the first 10 digits do not consist of the same digit repeated 

88 if len(set(digits[:10])) == 1: 

89 return False 

90 

91 # ISO 7064 Mod 11, 10 checksum 

92 product = 10 

93 for i in range(10): 

94 total = (digits[i] + product) % 10 

95 if total == 0: 

96 total = 10 

97 product = (total * 2) % 11 

98 

99 check = 11 - product 

100 if check == 10: 

101 check = 0 

102 

103 return check == digits[10]