-
Notifications
You must be signed in to change notification settings - Fork 0
/
bankaccount.go
252 lines (197 loc) · 6.08 KB
/
bankaccount.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
package bankverification
import (
"errors"
"regexp"
"strconv"
"strings"
)
// bank account length consts.
const (
DEFAULT_BANK_ACCOUNT_MIN_LENGTH = 7
DEFAULT_BANK_ACCOUNT_MAX_LENGTH = 7
)
// BankAccount represents a bank account for validation.
type BankAccount struct {
value string
clearingNumbers []ClearingNumber
}
// NewBankAccount initialises a new bank account.
func NewBankAccount(number string) BankAccount {
return BankAccount{
value: number,
clearingNumbers: defaultClearingNumbers,
}
}
// SetClearingNumbers overrides the default clearing numbers with the provided ones.
func (b *BankAccount) SetClearingNumbers(clearingNumbers []ClearingNumber) {
b.clearingNumbers = clearingNumbers
}
// IsValid checks if a bank account is valid.
func (b BankAccount) IsValid() bool {
return len(b.ValidationErrors()) == 0
}
// ValidationErrors runs validations and return validation errors.
func (b BankAccount) ValidationErrors() []error {
validationErrors := []error{}
minSNLength, maxSNLength := b.getSerialNumberLength()
serialNumber := b.GetSerialNumber()
if len(serialNumber) < minSNLength {
validationErrors = append(validationErrors, errors.New(ERR_SERIAL_TOO_SHORT))
}
if len(serialNumber) > maxSNLength {
validationErrors = append(validationErrors, errors.New(ERR_SERIAL_TOO_LONG))
}
// Use a regular expression to check for invalid characters.
invalidChars := regexp.MustCompile(`[^\d -.]`)
if invalidChars.MatchString(b.GetAccountNumber()) {
validationErrors = append(validationErrors, errors.New(ERR_INVALID_CHARACTER_ACCOUNT))
}
if !b.isValidSerial() {
validationErrors = append(validationErrors, errors.New(ERR_INVALID_CHECKSUM))
}
if bankName, err := b.GetBank(); err != nil || bankName == "" {
validationErrors = append(validationErrors, errors.New(ERR_UNKNOWN_BANK_DATA))
}
return validationErrors
}
// GetBankData finds a clearing number for the bank account.
func (b BankAccount) GetBankData() (ClearingNumber, error) {
clearingNumber, _ := strconv.Atoi(b.value[0:4])
for i := range b.clearingNumbers {
interval := strings.Split(b.clearingNumbers[i].Interval, "..")
if len(interval) < 2 {
return ClearingNumber{}, errors.New("could not get lower and upper value from interval")
}
lower, _ := strconv.Atoi(interval[0])
upper, _ := strconv.Atoi(interval[1])
if lower <= clearingNumber && clearingNumber <= upper {
return b.clearingNumbers[i], nil
}
}
return ClearingNumber{}, errors.New("could not find clearing number")
}
// GetAccountNumber returns the account number.
func (b BankAccount) GetAccountNumber() string {
return b.value
}
// GetBank returns the bank associated to the bank number.
func (b BankAccount) GetBank() (string, error) {
bankData, err := b.GetBankData()
if err != nil {
return "", err
}
return bankData.BankName, nil
}
// GetClearingNumber calculates and returns the clearing number for a bank account.
func (b BankAccount) GetClearingNumber() (string, error) {
digits := b.digits()
parts := []string{digits[:4]}
if b.getChecksumForClearing() {
parts = append(parts, digits[4:5])
}
filteredParts := filter(parts, func(e string) bool {
return e != ""
})
return strings.Join(filteredParts, "-"), nil
}
// GetSerialNumber calculates and returns the serial number for a bank account.
func (b BankAccount) GetSerialNumber() string {
number := b.digits()[b.getClearingNumberLength():]
if len(number) == 0 {
return number
}
if b.isZeroFill() {
minLength, _ := b.getSerialNumberLength()
return padLeft(number, minLength, '0')
}
return number
}
// isValidSerial checks if a serial is valid.
func (b BankAccount) isValidSerial() bool {
bankData, err := b.GetBankData()
if err != nil {
return false
}
if bankData.AccountType.Algorithm == "" {
return true
}
if bankData.AccountType.Algorithm == AlgorithmMod10 {
return mod10(b.getValidationNumbers(bankData))
}
if bankData.AccountType.Algorithm == AlgorithmMod11 {
return mod11(b.getValidationNumbers(bankData))
}
return true
}
// getValidationNumbers returns numbers used for validation.
func (b BankAccount) getValidationNumbers(bankData ClearingNumber) string {
length := bankData.AccountType.WeightedNumbers
accountType := bankData.AccountType.Type
var numbers string
if accountType == AccountType1 || accountType == AccountType2 {
d := b.digits()
if len(d) > 0 {
numbers = d[len(d)-length:]
}
}
if accountType == AccountType3 || accountType == AccountType4 || accountType == AccountType5 {
sn := b.GetSerialNumber()
if len(sn) > 0 {
numbers = sn[len(sn)-length:]
}
}
return numbers
}
// getSerialNumberLength returns the min and max length for a serial number
// depending on the account number.
func (b BankAccount) getSerialNumberLength() (int, int) {
bankData, err := b.GetBankData()
if err != nil {
return DEFAULT_BANK_ACCOUNT_MIN_LENGTH, DEFAULT_BANK_ACCOUNT_MAX_LENGTH
}
if bankData.MinLength == 0 {
bankData.MinLength = DEFAULT_BANK_ACCOUNT_MIN_LENGTH
}
if bankData.MaxLength == 0 {
bankData.MaxLength = bankData.MinLength
}
return bankData.MinLength, bankData.MaxLength
}
// isZeroFill returns if a serial number should be zero filled.
func (b BankAccount) isZeroFill() bool {
bankData, err := b.GetBankData()
if err != nil {
return false
}
return bankData.Zerofill
}
// getChecksumForClearing validates checksum for clearing number.
func (b BankAccount) getChecksumForClearing() bool {
bankData, err := b.GetBankData()
if err != nil {
return false
}
if bankData.ChecksumForClearing {
if bankData.BankName != "Swedbank" {
return true
}
if bankData.AccountType.Algorithm == AlgorithmMod10 {
return mod10(b.digits()[5:])
} else if bankData.AccountType.Algorithm == AlgorithmMod11 {
return mod11(b.digits()[5:])
}
}
return false
}
// digits normalize bank account.
func (b BankAccount) digits() string {
re := regexp.MustCompile(`\D`)
return re.ReplaceAllString(b.value, "")
}
// getClearingNumberLength calculates clearing number length based on the checksum.
func (b BankAccount) getClearingNumberLength() int {
if b.getChecksumForClearing() {
return 5
}
return 4
}