Skip to content

Commit

Permalink
Merge pull request #59 from moov-io/issue-53
Browse files Browse the repository at this point in the history
Implement funds type, summary section
  • Loading branch information
adamdecaf authored Feb 2, 2023
2 parents 8709e41 + 589e427 commit b02621a
Show file tree
Hide file tree
Showing 23 changed files with 1,054 additions and 414 deletions.
6 changes: 4 additions & 2 deletions cmd/bai2/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ var Parse = &cobra.Command{

var err error

scan := lib.NewBai2Scanner(bytes.NewReader(documentBuffer))
f := lib.NewBai2()
err = f.Read(lib.NewBai2Scanner(bytes.NewReader(documentBuffer)))
err = f.Read(&scan)
if err != nil {
return err
}
Expand All @@ -83,8 +84,9 @@ var Print = &cobra.Command{

var err error

scan := lib.NewBai2Scanner(bytes.NewReader(documentBuffer))
f := lib.NewBai2()
err = f.Read(lib.NewBai2Scanner(bytes.NewReader(documentBuffer)))
err = f.Read(&scan)
if err != nil {
return err
}
Expand Down
128 changes: 61 additions & 67 deletions pkg/lib/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ package lib

import (
"bytes"
"errors"
"fmt"
"strings"

"github.com/moov-io/bai2/pkg/util"
)
Expand Down Expand Up @@ -36,19 +36,15 @@ func NewAccount() *Account {
// Account Format
type Account struct {
// Account Identifier
AccountNumber string `json:"accountNumber"`
CurrencyCode string `json:"currencyCode,omitempty"`
TypeCode string `json:"typeCode,omitempty"`
Amount string `json:"amount,omitempty"`
ItemCount int64 `json:"itemCount,omitempty"`
FundsType string `json:"fundsType,omitempty"`
Composite []string `json:"composite,omitempty"`
AccountNumber string `json:"accountNumber"`
CurrencyCode string `json:"currencyCode,omitempty"`
Summaries []AccountSummary `json:"summaries,omitempty"`

// Account Trailer
AccountControlTotal string `json:"accountControlTotal"`
NumberRecords int64 `json:"numberRecords"`

Details []TransactionDetail
Details []Detail

header accountIdentifier
trailer accountTrailer
Expand All @@ -59,11 +55,7 @@ func (r *Account) copyRecords() {
r.header = accountIdentifier{
AccountNumber: r.AccountNumber,
CurrencyCode: r.CurrencyCode,
TypeCode: r.TypeCode,
Amount: r.Amount,
ItemCount: r.ItemCount,
FundsType: r.FundsType,
Composite: r.Composite,
Summaries: r.Summaries,
}

r.trailer = accountTrailer{
Expand All @@ -80,7 +72,7 @@ func (r *Account) String(opts ...int64) string {
var buf bytes.Buffer
buf.WriteString(r.header.string(opts...) + "\n")
for i := range r.Details {
buf.WriteString(r.Details[i].string(opts...) + "\n")
buf.WriteString(r.Details[i].String(opts...) + "\n")
}
buf.WriteString(r.trailer.string())

Expand All @@ -96,7 +88,7 @@ func (r *Account) Validate() error {
}

for i := range r.Details {
if err := r.Details[i].validate(); err != nil {
if err := r.Details[i].Validate(); err != nil {
return err
}
}
Expand All @@ -108,94 +100,96 @@ func (r *Account) Validate() error {
return nil
}

func (r *Account) Read(scan Bai2Scanner, input string, lineNum int) (int, string, error) {
func (r *Account) Read(scan *Bai2Scanner, useCurrentLine bool) error {
if scan == nil {
return errors.New("invalid bai2 scanner")
}

var detail *TransactionDetail
parseAccountIdentifier := func(raw string) error {
if raw == "" {
return nil
}

for line := scan.ScanLine(input); line != ""; line = scan.ScanLine(input) {
newRecord := accountIdentifier{}
_, err := newRecord.parse(raw)
if err != nil {
return fmt.Errorf("ERROR parsing account identifier on line %d (%v)", scan.GetLineIndex(), err)
}

input = ""
r.AccountNumber = newRecord.AccountNumber
r.CurrencyCode = newRecord.CurrencyCode
r.Summaries = newRecord.Summaries

// don't expect new line
line = strings.TrimSpace(strings.ReplaceAll(line, "\n", ""))
return nil
}

var rawData string
find := false
isBreak := false

for line := scan.ScanLine(useCurrentLine); line != ""; line = scan.ScanLine(useCurrentLine) {
// find record code
if len(line) < 3 {
lineNum++
continue
}

useCurrentLine = false
switch line[:2] {
case util.AccountIdentifierCode:

lineNum++
newRecord := accountIdentifier{}
_, err := newRecord.parse(line)
if err != nil {
return lineNum, line, fmt.Errorf("ERROR parsing account identifier on line %d - %v", lineNum, err)
if find {
isBreak = true
break
}

r.AccountNumber = newRecord.AccountNumber
r.CurrencyCode = newRecord.CurrencyCode
r.TypeCode = newRecord.TypeCode
r.Amount = newRecord.Amount
r.ItemCount = newRecord.ItemCount
r.FundsType = newRecord.FundsType
r.Composite = newRecord.Composite
rawData = line
find = true

case util.ContinuationCode:
rawData = rawData[:len(rawData)-1] + "," + line[3:]

case util.AccountTrailerCode:
if err := parseAccountIdentifier(rawData); err != nil {
return err
} else {
rawData = ""
}

lineNum++
newRecord := accountTrailer{}
_, err := newRecord.parse(line)
if err != nil {
return lineNum, line, fmt.Errorf("ERROR parsing account trailer on line %d - %v", lineNum, err)
return fmt.Errorf("ERROR parsing account trailer on line %d (%v)", scan.GetLineIndex(), err)
}

r.AccountControlTotal = newRecord.AccountControlTotal
r.NumberRecords = newRecord.NumberRecords

if detail != nil {
r.Details = append(r.Details, *detail)
}

return lineNum, "", nil
return nil

case util.TransactionDetailCode:

lineNum++
if detail != nil {
r.Details = append(r.Details, *detail)
detail = nil
if err := parseAccountIdentifier(rawData); err != nil {
return err
} else {
rawData = ""
}

detail = NewTransactionDetail()
_, err := detail.parse(line)
detail := NewDetail()
err := detail.Read(scan, true)
if err != nil {
return lineNum, line, fmt.Errorf("ERROR parsing transaction detail on line %d - %v", lineNum, err)
return err
}

case util.ContinuationCode:

lineNum++
newRecord := continuationRecord{}
_, err := newRecord.parse(line)
if err != nil {
return lineNum, line, fmt.Errorf("ERROR parsing continuation on line %d - %v", lineNum, err)
}

if detail == nil {
r.Composite = append(r.Composite, newRecord.Composite...)
} else {
detail.Composite = append(detail.Composite, newRecord.Composite...)
}
r.Details = append(r.Details, *detail)
useCurrentLine = true

default:
return fmt.Errorf("ERROR parsing file on line %d (unabled to read record type %s)", scan.GetLineIndex(), line[0:2])

return lineNum, line, nil
}

if isBreak {
break
}
}

return lineNum, "", nil
return nil
}
68 changes: 36 additions & 32 deletions pkg/lib/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,30 @@ package lib

import (
"bytes"
"github.com/stretchr/testify/require"
"testing"

"github.com/stretchr/testify/require"
)

/*
func TestAccountWithSampleData1(t *testing.T) {
raw := `
03,10200123456,CAD,040,+000000000000,,,045,+000000000000,,/
88,100,000000000111500,00002,V,060317,,400,000000000111500,00004,V,060317,/
16,108,000000000011500,V,060317,,,,TFR 1020 0345678 /
16,108,000000000100000,V,060317,,,,MONTREAL /
49,+00000000000446000,9/
03,9876543210,,010,-500000,,,100,1000000,,,400,2000000,,,190/
88,500000,,,110,1000000,,,072,500000,,,074,500000,,,040/
88,-1500000,,/
16,115,500000,S,,200000,300000,,,LOCK BOX NO.68751/
49,4000000,5/
98,+00000000001280000,2,25/
`
scan := NewBai2Scanner(bytes.NewReader([]byte(raw)))
account := Account{}
lineNum, line, err := account.Read(NewBai2Scanner(bytes.NewReader([]byte(raw))), "", 0)
err := account.Read(&scan, false)
require.NoError(t, err)
require.NoError(t, account.Validate())
require.Equal(t, 5, lineNum)
require.Equal(t, "", line)
require.Equal(t, 5, scan.GetLineIndex())
require.Equal(t, "", scan.GetLine())
}
func TestAccountWithSampleData2(t *testing.T) {
Expand All @@ -39,45 +42,46 @@ func TestAccountWithSampleData2(t *testing.T) {
98,+00000000001280000,2,25/
`
scan := NewBai2Scanner(bytes.NewReader([]byte(raw)))
account := Account{}
lineNum, line, err := account.Read(NewBai2Scanner(bytes.NewReader([]byte(raw))), "", 0)
err := account.Read(&scan, false)
require.NoError(t, err)
require.NoError(t, account.Validate())
require.Equal(t, 4, lineNum)
require.Equal(t, "98,+00000000001280000,2,25/", line)
require.Equal(t, 5, scan.GetLineIndex())
}
*/

func TestAccountOutputWithContinuationRecord(t *testing.T) {

raw := `
03,10200123456,CAD,040,+000000000000,,,045,+000000000000,,/
88,100,000000000111500,00002,V,060317,,400,000000000111500,00004,V,060317,/
88,100,000000000111500,00002,V,060317,,400,000000000111500,00004,V,060317,/
16,108,000000000011500,V,060317,,,,TFR 1020 0345678 /
16,108,000000000100000,V,060317,,,,MONTREAL /
49,+00000000000446000,9/
03,9876543210,,010,-500000,,,100,1000000,,,400,2000000,,,190/
88,500000,,,110,1000000,,,072,500000,,,074,500000,,,040/
88,-1500000,,/
16,115,500000,S,,200000,300000,,,LOCK BOX NO.68751/
49,4000000,5/
`

scan := NewBai2Scanner(bytes.NewReader([]byte(raw)))
account := Account{}
lineNum, line, err := account.Read(NewBai2Scanner(bytes.NewReader([]byte(raw))), "", 0)
err := account.Read(&scan, false)
require.NoError(t, err)
require.NoError(t, account.Validate())
require.Equal(t, 6, lineNum)
require.Equal(t, "", line)
require.Equal(t, 5, scan.GetLineIndex())
require.Equal(t, "49,4000000,5/", scan.GetLine())

result := account.String()
expectedResult := `03,10200123456,CAD,040,+000000000000,,,045,+000000000000,,,100,000000000111500,00002,V,060317,,400,000000000111500,00004,V,060317,,100,000000000111500,00002,V,060317,,400,000000000111500,00004,V,060317,/
16,108,000000000011500,V,060317,,,,TFR 1020 0345678 /
16,108,000000000100000,V,060317,,,,MONTREAL /
49,+00000000000446000,9/`
expectedResult := `03,9876543210,,010,-500000,,,100,1000000,,,400,2000000,,,190,500000,,,110,1000000,,,072,500000,,,074,500000,,,040,-1500000,,/
16,115,500000,S,0,200000,300000,,,LOCK BOX NO.68751/
49,4000000,5/`
require.Equal(t, expectedResult, result)

result = account.String(80)
expectedResult = `03,10200123456,CAD,040,+000000000000,,,045,+000000000000,,,100,000000000111500/
88,00002,V,060317,,400,000000000111500,00004,V,060317,,100,000000000111500/
88,00002,V,060317,,400,000000000111500,00004,V,060317,/
16,108,000000000011500,V,060317,,,,TFR 1020 0345678 /
16,108,000000000100000,V,060317,,,,MONTREAL /
49,+00000000000446000,9/`
result = account.String(50)
expectedResult = `03,9876543210,,010,-500000,,,100,1000000,,,400/
88,2000000,,,190,500000,,,110,1000000,,,072/
88,500000,,,074,500000,,,040,-1500000,,/
16,115,500000,S,0,200000,300000,,/
88,LOCK BOX NO.68751/
49,4000000,5/`
require.Equal(t, expectedResult, result)

}
Loading

0 comments on commit b02621a

Please sign in to comment.