8000 Add New Zealand bank account number · yyht/python-stdnum@3f953f3 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3f953f3

Browse files
committed
Add New Zealand bank account number
1 parent 4e25e31 commit 3f953f3

File tree

5 files changed

+2795
-0
lines changed

5 files changed

+2795
-0
lines changed

stdnum/nz/__init__.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# __init__.py - collection of New Zealand numbers
2+
# coding: utf-8
3+
#
4+
# Copyright (C) 2019 Arthur de Jong
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+
"""Collection of New Zealand numbers."""

stdnum/nz/bankaccount.py

Lines changed: 155 additions & 0 deletions
A3E2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# bankaccount.py - functions for handling New Zealand bank account numbers
2+
# coding: utf-8
3+
#
4+
# Copyright (C) 2019 Arthur de Jong
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+
"""New Zealand bank account number
22+
23+
The New Zealand bank account numbers consist of 16 digits. The first two
24+
represent the bank, followed by four for the branch, seven digits for the
25+
account base number and three for the account type.
26+
27+
More information:
28+
29+
* https://en.wikipedia.org/wiki/New_Zealand_bank_account_number
30+
31+
>>> validate('01-0242-0100194-00')
32+
'0102420100194000'
33+
>>> validate('01-0242-0100195-00') # invalid check digits
34+
Traceback (most recent call last):
35+
...
36+
InvalidChecksum: ...
37+
>>> validate('01-9999-0100197-00') # invalid branch
38+
Traceback (most recent call last):
39+
...
40+
InvalidComponent: ...
41+
>>> format('0102420100194000')
42+
'01-0242-0100194-000'
43+
"""
44+
45+
from stdnum.exceptions import *
46+
from stdnum.util import clean
47+
48+
49+
# The following algorithms and weights were taken from:
50+
# https://www.ird.govt.nz/software-providers/explore-products-contents/reporting/withholding-taxes/rwt-and-nrwt-certificate-filing-options.html#02
51+
# with the modification to use max 7 digits for the account base
52+
# instead of 8 (and leaving out algorithm C).
53+
54+
# The algorithm to choose based on the bank
55+
_algorithms = {
56+
'01': 'A', '02': 'A', '03': 'A', '04': 'A', '06': 'A', '08': 'D',
57+
'09': 'E', '10': 'A', '11': 'A', '12': 'A', '13': 'A', '14': 'A',
58+
'15': 'A', '16': 'A', '17': 'A', '18': 'A', '19': 'A', '20': 'A',
59+
'21': 'A', '22': 'A', '23': 'A', '24': 'A', '25': 'F', '26': 'G',
60+
'27': 'A', '28': 'G', '29': 'G', '30': 'A', '31': 'X', '33': 'F',
61+
'35': 'A', '38': 'A',
62+
}
63+
64+
# The different weights for the different checksum algorithms
65+
_weights = {
66+
'A': (0, 0, 6, 3, 7, 9, 0, 10, 5, 8, 4, 2, 1, 0, 0, 0),
67+
'B': (0, 0, 0, 0, 0, 0, 0, 10, 5, 8, 4, 2, 1, 0, 0, 0),
68+
'D': (0, 0, 0, 0, 0, 0, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0),
69+
'E': (0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 4, 3, 2, 0, 0, 1),
70+
'F': (0, 0, 0, 0, 0, 0, 1, 7, 3, 1, 7, 3, 1, 0, 0, 0),
71+
'G': (0, 0, 0, 0, 0, 0, 1, 3, 7, 1, 3, 7, 1, 3, 7, 1),
72+
'X': (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
73+
}
74+
75+
# The moduli to use per algorithm
76+
_moduli = {
77+
'A': (11, 11),
78+
'B': (11, 11),
79+
'D': (11, 11),
80+
'E': (9, 11),
81+
'F': (10, 10),
82+
'G': (9, 10),
83+
'X': (1, 1),
84+
}
85+
86+
87+
def compact(number):
88+
"""Convert the number to the minimal representation. This strips the
89+
number of any valid separators and removes surrounding whitespace."""
90+
number = clean(number).strip().replace(' ', '-').split('-')
91+
if len(number) == 4:
92+
# zero pad the different sections if they are found
93+
lengths = (2, 4, 7, 3)
94+
return ''.join(n.zfill(l) for n, l in zip(number, lengths))
95+
else:
96+
# otherwise zero pad the account type
97+
number = ''.join(number)
98+
return number[:13] + number[13:].zfill(3)
99+
100+
101+
def info(number):
102+
"""Return a dictionary of data about the supplied number. This typically
103+
returns the name of the bank and branch and a BIC if it is valid."""
104+
number = compact(number)
105+
from stdnum import numdb
106+
info = {}
107+
for nr, found in numdb.get('nz/banks').info(number):
108+
info.update(found)
109+
return info
110+
111+
112+
def _calc_checksum(number):
113+
# pick the algorithm and parameters
114+
algorithm = _algorithms.get(number[:2], 'X')
115+
if algorithm == 'A' and number[6:13] >= '0990000':
116+
algorithm = 'B'
117+
weights = _weights[algorithm]
118+
mod1, mod2 = _moduli[algorithm]
119+
# calculate the checksum
120+
return sum(
121+
c % mod1 if c > mod1 else c for c in
122+
(w * int(n) for w, n in zip(weights, number))) % mod2
123+
124+
125+
def validate(number):
126+
"""Check if the number provided is a valid bank account number."""
127+
number = compact(number)
128+
if not number.isdigit():
129+
raise InvalidFormat()
130+
if len(number) != 16:
131+
raise InvalidLength()
132+
if _calc_checksum(number) != 0:
133+
raise InvalidChecksum()
134+
i = info(number)
135+
if 'bank' not in i or 'branch' not in i:
136+
raise InvalidComponent()
137+
return number
138+
139+
140+
def is_valid(number):
141+
"""Check if the number provided is a valid bank account number."""
142+
try:
143+
return bool(validate(number))
144+
except ValidationError:
145+
return False
146+
147+
148+
def format(number):
149+
"""Reformat the number to the standard presentation format."""
150+
number = compact(number)
151+
return '-'.join([
152+
number[:2],
153+
number[2:6],
154+
number[6:13],
155+
number[13:]])

0 commit comments

Comments
 (0)
0