Skip to content

Commit d0a2b5a

Browse files
committed
ref
1 parent 0410fcd commit d0a2b5a

File tree

5 files changed

+183
-14
lines changed

5 files changed

+183
-14
lines changed

bik/bik.go

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,75 @@
11
package bik
22

33
import (
4-
"strconv"
4+
"fmt"
55

66
"github.com/sshaplygin/docs-code/models"
77
"github.com/sshaplygin/docs-code/utils"
88
)
99

10-
// Validate check to valid BIK format
11-
// example valid format is 044525225
12-
func Validate(bik string) (bool, error) {
13-
if len(bik) != 9 {
14-
return false, &models.CommonError{
10+
const codeLength = 9
11+
12+
type BIKStruct struct {
13+
country CountryCode
14+
territoryCode TerritoryCode
15+
unitNumber UnitConditionalNumber
16+
lastNumber LastAccountNumbers
17+
}
18+
19+
func NewBik(bik string) (*BIKStruct, error) {
20+
if len(bik) != codeLength {
21+
return nil, &models.CommonError{
1522
Method: packageName,
1623
Err: models.ErrInvalidLength,
1724
}
1825
}
1926

2027
bikArr, err := utils.StrToArr(bik)
2128
if err != nil {
22-
return false, err
29+
return nil, fmt.Errorf("cound't parse raw bik: %w", err)
2330
}
2431

25-
if bikArr[0] != 0 || bikArr[1] != 4 {
32+
return &BIKStruct{
33+
country: CountryCode(utils.SliceToInt(bikArr[0:2])),
34+
territoryCode: TerritoryCode(utils.SliceToInt(bikArr[2:4])),
35+
unitNumber: UnitConditionalNumber(utils.SliceToInt(bikArr[4:6])),
36+
lastNumber: LastAccountNumbers(utils.SliceToInt(bikArr[6:])),
37+
}, nil
38+
}
39+
40+
func (bs *BIKStruct) IsValid() (bool, error) {
41+
if bs == nil {
42+
return false, ErrNilBik
43+
}
44+
45+
if !bs.country.IsValid() {
2646
return false, ErrInvalidCountryCode
2747
}
2848

29-
// special code
30-
if bikArr[6] == 0 && bikArr[7] == 1 && bikArr[8] == 2 {
31-
return true, nil
49+
if !bs.territoryCode.IsValid() {
50+
return false, ErrInvalidTerritoryCode
3251
}
3352

34-
latestTriadStr := bik[6:]
35-
code, _ := strconv.Atoi(latestTriadStr)
53+
if !bs.unitNumber.IsValid() {
54+
return false, ErrInvalidUnitConditionalNumber
55+
}
56+
57+
if !bs.lastNumber.IsValid() {
58+
return false, ErrInvalidLastAccountNumbers
59+
}
60+
61+
return true, nil
62+
}
63+
64+
// Validate check to valid BIK format.
65+
// Example valid format is 044525225
66+
func Validate(bik string) (bool, error) {
67+
bikData, err := NewBik(bik)
68+
if err != nil {
69+
return false, fmt.Errorf("create bik model: %w", err)
70+
}
3671

37-
return code >= 50 && code < 1000, nil
72+
return bikData.IsValid()
3873
}
3974

4075
func Generate() string {

bik/errros.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@ package bik
33
import "errors"
44

55
var (
6+
// ErrNilBik try call methods for nil bik struct
7+
ErrNilBik = errors.New("nil bik struct")
8+
69
// ErrInvalidCountryCode invalid bik code country
710
ErrInvalidCountryCode = errors.New("invalid bik country code")
11+
12+
// ErrInvalidTerritoryCode invalid okato territorial code of the subject
13+
ErrInvalidTerritoryCode = errors.New("invalid okato city code")
14+
15+
// ErrInvalidUnitConditionalNumber invalid unit conditional number
16+
ErrInvalidUnitConditionalNumber = errors.New("invalid unit conditional number")
17+
18+
// ErrInvalidLastAccountNumbers invalid okato territorial code of the subject
19+
ErrInvalidLastAccountNumbers = errors.New("invalid last account number")
820
)

bik/models.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,77 @@
11
package bik
22

33
const packageName = "bik"
4+
5+
const (
6+
maxCountryCodeLength = 100
7+
maxTerritoryCodeLength = 100
8+
maxInitConditionalNumber = 100
9+
minLastAccountNumbers = 50
10+
maxLastAccountNumbers = 1000
11+
)
12+
13+
var (
14+
// directParticipationCounty - участник платежной системы с прямым участием
15+
directParticipationCounty CountryCode = 0
16+
17+
// indirectParticipationCounty - участник платежной системы с косвенным участием
18+
indirectParticipationCounty CountryCode = 1
19+
20+
// notMemberClientCBRF - клиент Банка России, не являющийся участником платежной системы
21+
notMemberClientCBRF CountryCode = 2
22+
23+
russiaCountryCode CountryCode = 4
24+
)
25+
26+
var supportedCountryCodes = map[CountryCode]string{
27+
directParticipationCounty: "Участник платежной системы с прямым участием",
28+
indirectParticipationCounty: "Участник платежной системы с косвенным участием",
29+
notMemberClientCBRF: "Клиент Банка России, не являющийся участником платежной системы",
30+
russiaCountryCode: "Код Российской Федерации",
31+
}
32+
33+
type (
34+
// CountryCode Required length 2.
35+
CountryCode int
36+
37+
// TerritoryCode OKATO code. Required length 2.
38+
TerritoryCode int
39+
40+
// UnitConditionalNumber required length 2.
41+
// The conditional number of the Bank of Russia settlement network division,
42+
// unique within the territorial institution of the Bank of Russia,
43+
// in which this division of the Bank of Russia settlement network operates,
44+
// or the conditional number of the structural division of the Bank of Russia.
45+
UnitConditionalNumber int
46+
47+
// LastAccountNumbers required length 3. It is last correspondent account of the bank. Possible values 050 до 999
48+
LastAccountNumbers int
49+
)
50+
51+
func (cc CountryCode) IsValid() bool {
52+
if cc > maxCountryCodeLength {
53+
return false
54+
}
55+
56+
_, ok := supportedCountryCodes[cc]
57+
58+
return ok
59+
}
60+
61+
func (tc TerritoryCode) IsValid() bool {
62+
return tc < maxTerritoryCodeLength
63+
}
64+
65+
func (ucn UnitConditionalNumber) IsValid() bool {
66+
return ucn < maxInitConditionalNumber
67+
}
68+
69+
const specialCode = 12
70+
71+
func (lan LastAccountNumbers) IsValid() bool {
72+
if lan == specialCode {
73+
return true
74+
}
75+
76+
return lan >= minLastAccountNumbers && lan < maxLastAccountNumbers
77+
}

utils/helpers.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,11 @@ func StrToArr(str string) ([]int, error) {
3232
}
3333
return arr, nil
3434
}
35+
36+
func SliceToInt(data []int) int {
37+
var res int
38+
for _, num := range data {
39+
res = res*10 + num
40+
}
41+
return res
42+
}

utils/helpers_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package utils
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func Test_SliceToInt(t *testing.T) {
10+
type testCase struct {
11+
name string
12+
in []int
13+
want int
14+
}
15+
16+
tests := []testCase{
17+
{
18+
"nil",
19+
nil,
20+
0,
21+
},
22+
{
23+
"102",
24+
[]int{1, 0, 2},
25+
102,
26+
},
27+
{
28+
"with zero prefix 02",
29+
[]int{0, 2},
30+
2,
31+
},
32+
}
33+
34+
for _, tc := range tests {
35+
tc := tc
36+
t.Run(tc.name, func(t *testing.T) {
37+
assert.Equal(t, tc.want, SliceToInt(tc.in))
38+
})
39+
}
40+
}

0 commit comments

Comments
 (0)