1from natural.constant import _, IBAN_ALPHABET 2from natural.constant import BBAN_RULES, BBAN_PATTERN, BBAN_MAP 3import re 4 5 6def bban_compact(number): 7 ''' 8 Printable compacted Basic Bank Account Number. Removes all the padding 9 characters. 10 11 :param number: string 12 13 >>> bban_compact('1234.56.78.90') 14 '1234567890' 15 >>> bban_compact('068-9999995-01') 16 '068999999501' 17 ''' 18 return re.sub(r'[-. ]', '', str(number)) 19 20 21def bban_base10(number): 22 ''' 23 Printable Basic Bank Account Number in base-10. 24 25 :param number: string 26 27 >>> bban_base10('01234567') 28 '45670123' 29 >>> bban_base10('ABCD') 30 '10111213' 31 ''' 32 number = bban_compact(number) 33 number = number[4:] + number[:4] 34 return ''.join([str(IBAN_ALPHABET.index(char)) for char in number]) 35 36 37def _bban_regex(structure): 38 return re.compile( 39 r'^%s$' % BBAN_PATTERN.sub( 40 lambda m: '%s{%s}' % (BBAN_MAP[m.group(2)], m.group(1)), 41 structure, 42 ) 43 ) 44 45 46def bban(value, country=None, validate=False): 47 ''' 48 Printable Basic Bank Account Number (BBAN) for the given country code. The 49 ``country`` must be a valid ISO 3166-2 country code. 50 51 :param value: string or int 52 :param country: string 53 54 >>> bban('068-9999995-01', 'BE') 55 '068999999501' 56 >>> bban('555', 'NL') 57 '555' 58 >>> bban('555', 'NL', validate=True) 59 Traceback (most recent call last): 60 ... 61 ValueError: Invalid BBAN, number does not match specification 62 >>> bban('123', 'XY', validate=True) 63 Traceback (most recent call last): 64 ... 65 ValueError: Invalid BBAN, country unknown 66 ''' 67 68 value = bban_compact(value) 69 70 if validate: 71 country = country.upper() 72 73 try: 74 rules = BBAN_RULES[country] 75 except KeyError: 76 raise ValueError(_('Invalid BBAN, country unknown')) 77 78 regex = _bban_regex(rules['bban']) 79 if not regex.match(value): 80 raise ValueError( 81 _('Invalid BBAN, number does not match specification') 82 ) 83 84 return value 85 86 87def iban(number, validate=False): 88 ''' 89 Printable International Bank Account Number (IBAN) as specified in ISO 90 13616. 91 92 :param number: string 93 94 >>> iban('BE43068999999501') 95 'BE43 0689 9999 9501' 96 >>> iban('XY32012341234123', validate=True) 97 Traceback (most recent call last): 98 ... 99 ValueError: Invalid IBAN, country unknown 100 >>> iban('BE43068999999502', validate=True) 101 Traceback (most recent call last): 102 ... 103 ValueError: Invalid IBAN, digits check failed 104 105 ''' 106 107 number = bban_compact(number) 108 if validate: 109 country = number[:2] 110 if country not in BBAN_RULES: 111 raise ValueError(_('Invalid IBAN, country unknown')) 112 113 # Do the 10-mod-97 check 114 digits = bban_base10(number) 115 if int(digits) % 97 != 1: 116 raise ValueError(_('Invalid IBAN, digits check failed')) 117 118 # Check BBAN for country 119 bban(number[4:], country, validate=True) 120 121 groups = [number[x:x + 4] for x in range(0, len(number), 4)] 122 return ' '.join(groups) 123