Skip to content

Commit

Permalink
batch: tabulate on build/create calls
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdecaf committed Dec 4, 2018
1 parent 7e47e36 commit b347d08
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 41 deletions.
53 changes: 25 additions & 28 deletions batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package ach

import (
"encoding/json"
"errors"
"fmt"
"strconv"
Expand All @@ -25,6 +26,23 @@ type Batch struct {
converters
}

func (b *Batch) UnmarshalJSON(p []byte) error {
b.Header = NewBatchHeader()
b.Control = NewBatchControl()
b.ADVControl = NewADVBatchControl()

type Alias Batch
aux := struct {
*Alias
}{
(*Alias)(b),
}
if err := json.Unmarshal(p, &aux); err != nil {
return err
}
return nil
}

// NewBatch takes a BatchHeader and returns a matching SEC code batch type that is a batcher. Returns an error if the SEC code is not supported.
func NewBatch(bh *BatchHeader) (Batcher, error) {
switch bh.StandardEntryClassCode {
Expand Down Expand Up @@ -190,20 +208,7 @@ func (batch *Batch) build() error {

if !batch.IsADV() {
for i, entry := range batch.Entries {
entryCount++

// Add in Addenda Count
if entry.Addenda02 != nil {
entryCount++
}
entryCount = entryCount + len(entry.Addenda05)
if entry.Addenda98 != nil {
entryCount++
}

if entry.Addenda99 != nil {
entryCount++
}
entryCount += 1 + entry.addendaCount()

This comment has been minimized.

Copy link
@bkmoovio

bkmoovio Dec 5, 2018

Contributor

Ironically golint gives a warning on n += 1 and suggests n++. I think I ran into that before.


currentTraceNumberODFI, err := strconv.Atoi(entry.TraceNumberField()[:8])
if err != nil {
Expand All @@ -217,7 +222,7 @@ func (batch *Batch) build() error {

// Add a sequenced TraceNumber if one is not already set. Have to keep original trance number Return and NOC entries
if currentTraceNumberODFI != batchHeaderODFI {
batch.Entries[i].SetTraceNumber(batch.Header.ODFIIdentification, seq)
entry.SetTraceNumber(batch.Header.ODFIIdentification, seq)
}
seq++
addendaSeq := 1
Expand Down Expand Up @@ -307,6 +312,10 @@ func (batch *Batch) GetEntries() []*EntryDetail {

// AddEntry appends an EntryDetail to the Batch
func (batch *Batch) AddEntry(entry *EntryDetail) {
if entry == nil {
return
}

batch.category = entry.Category
batch.Entries = append(batch.Entries, entry)
}
Expand Down Expand Up @@ -395,19 +404,7 @@ func (batch *Batch) isBatchEntryCount() error {

if !batch.IsADV() {
for _, entry := range batch.Entries {
entryCount++

// Add in Addenda Count
if entry.Addenda02 != nil {
entryCount++
}
entryCount = entryCount + len(entry.Addenda05)
if entry.Addenda98 != nil {
entryCount++
}
if entry.Addenda99 != nil {
entryCount++
}
entryCount += 1 + entry.addendaCount()
}
if entryCount != batch.Control.EntryAddendaCount {
msg := fmt.Sprintf(msgBatchCalculatedControlEquality, entryCount, batch.Control.EntryAddendaCount)
Expand Down
2 changes: 1 addition & 1 deletion batcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (e *BatchError) Error() string {
var (
// generic messages
msgBatchHeaderControlEquality = "header %v is not equal to control %v"
msgBatchCalculatedControlEquality = "calculated %v is out-of-balance with control %v"
msgBatchCalculatedControlEquality = "calculated %v is out-of-balance with batch control %v"
msgBatchAscending = "%v is less than last %v. Must be in ascending order"
// specific messages for error
msgBatchCompanyEntryDescription = "Company entry description %v is not valid for batch type %v"
Expand Down
19 changes: 19 additions & 0 deletions entryDetail.go
Original file line number Diff line number Diff line change
Expand Up @@ -497,3 +497,22 @@ func (ed *EntryDetail) CreditOrDebit() string {
func (ed *EntryDetail) AddAddenda05(addenda05 *Addenda05) {
ed.Addenda05 = append(ed.Addenda05, addenda05)
}

// addendaCount returns the count of Addenda records added onto this EntryDetail
func (ed *EntryDetail) addendaCount() (n int) {
if ed.Addenda02 != nil {
n += 1
}
for i := range ed.Addenda05 {
if ed.Addenda05[i] != nil {
n += 1
}
}
if ed.Addenda98 != nil {
n += 1
}
if ed.Addenda99 != nil {
n += 1
}
return n
}
23 changes: 17 additions & 6 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const (

// Errors strings specific to parsing a Batch container
var (
msgFileCalculatedControlEquality = "calculated %v is out-of-balance with control %v"
msgFileCalculatedControlEquality = "calculated %v is out-of-balance with file control %v"
// specific messages
msgRecordLength = "must be 94 characters and found %d"
msgFileBatchOutside = "outside of current batch"
Expand Down Expand Up @@ -118,11 +118,12 @@ func FileFromJSON(bs []byte) (*File, error) {

// Read FileHeader
header := fileHeader{
Header: NewFileHeader(),
Header: file.Header,
}
if err := json.NewDecoder(bytes.NewReader(bs)).Decode(&header); err != nil {
return nil, fmt.Errorf("problem reading FileHeader: %v", err)
}
file.Header = header.Header

if !file.IsADV() {
// Read FileControl
Expand All @@ -148,14 +149,15 @@ func FileFromJSON(bs []byte) (*File, error) {
if err := file.setBatchesFromJSON(bs); err != nil {
return nil, err
}
file.Header = header.Header

if !file.IsADV() {
file.Control.BatchCount = len(file.Batches)
} else {

file.ADVControl.BatchCount = len(file.Batches)
}

if err := file.Create(); err != nil {
return file, err
}
return file, nil
}

Expand All @@ -177,7 +179,7 @@ func (f *File) setBatchesFromJSON(bs []byte) error {
if err := json.Unmarshal(bs, &batches); err != nil {
return err
}
// Clear out any nil batchs
// Clear out any nil batchess
for i := range f.Batches {
if f.Batches[i] == nil {
f.Batches = append(f.Batches[:i], f.Batches[i+1:]...)
Expand All @@ -188,6 +190,9 @@ func (f *File) setBatchesFromJSON(bs []byte) error {
if batches.Batches[i] == nil {
continue
}
if err := batches.Batches[i].build(); err != nil {
return fmt.Errorf("batch %s: %v", batches.Batches[i].Header.ID, err)
}
f.Batches = append(f.Batches, batches.Batches[i])
}
return nil
Expand All @@ -214,6 +219,12 @@ func (f *File) Create() error {
totalCreditAmount := 0

for i, batch := range f.Batches {
if v := f.Batches[i].GetHeader(); v == nil {
f.Batches[i].SetHeader(NewBatchHeader())
}
if v := f.Batches[i].GetControl(); v == nil {
f.Batches[i].SetControl(NewBatchControl())
}

// create ascending batch numbers
f.Batches[i].GetHeader().BatchNumber = batchSeq
Expand Down
38 changes: 36 additions & 2 deletions file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,14 @@ func TestFile__readFromJson(t *testing.T) {
t.Fatal(err)
}

// Ensure the file is valid
if err := file.Create(); err != nil {
t.Error(err)
}
if err := file.Validate(); err != nil {
t.Error(err)
}

if file.ID != "adam-01" {
t.Errorf("file.ID: %s", file.ID)
}
Expand All @@ -422,15 +430,15 @@ func TestFile__readFromJson(t *testing.T) {
}
batch := file.Batches[0]
batchControl := batch.GetControl()
if batchControl.EntryAddendaCount != 2 {
if batchControl.EntryAddendaCount != 1 {
t.Errorf("EntryAddendaCount: %d", batchControl.EntryAddendaCount)
}

// Control
if file.Control.BatchCount != 1 {
t.Errorf("BatchCount: %d", file.Control.BatchCount)
}
if file.Control.EntryAddendaCount != 2 {
if file.Control.EntryAddendaCount != 1 {
t.Errorf("File Control EntryAddendaCount: %d", file.Control.EntryAddendaCount)
}
if file.Control.TotalDebitEntryDollarAmountInFile != 0 || file.Control.TotalCreditEntryDollarAmountInFile != 100000 {
Expand All @@ -448,3 +456,29 @@ func TestFile__readFromJson(t *testing.T) {
t.Error(err)
}
}

// TestFile__jsonFileNoControlBlobs will read an ach.File from its JSON form, but the JSON has no
// batchControl or fileControl sub-objects.
func TestFile__jsonFileNoControlBlobs(t *testing.T) {
path := filepath.Join("test", "testdata", "ppd-no-control-blobs-valid.json")
bs, err := ioutil.ReadFile(path)
if err != nil {
t.Fatal(err)
}

file, err := FileFromJSON(bs)
if err != nil {
t.Fatal(err)
}

if err := file.Create(); err != nil {
t.Fatal(err)
}
if err := file.Validate(); err != nil {
t.Fatal(err)
}

if file.ID != "adam-01" {
t.Errorf("file.ID: %s", file.ID)
}
}
1 change: 1 addition & 0 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ VERSION := $(shell grep -Eo '(v[0-9]+[\.][0-9]+[\.][0-9]+([-a-zA-Z0-9]*)?)' vers
build:
go fmt ./...
@mkdir -p ./bin/
go build github.com/moov-io/ach
CGO_ENABLED=0 go build -o ./bin/server github.com/moov-io/ach/cmd/server

generate: clean
Expand Down
2 changes: 1 addition & 1 deletion reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func NewReader(r io.Reader) *Reader {
// on the first character of each line. It also enforces ACH formatting rules and returns
// the appropriate error if issues are found.
//
// A parsed file may not be valid and callers should confirm with Validate(). Invalid files may
// A parsed file may not be valid and callers should confirm with Create() Validate(). Invalid files may
// be rejected by other Financial Institutions or ACH tools.
func (r *Reader) Read() (File, error) {
r.lineNum = 0
Expand Down
56 changes: 56 additions & 0 deletions test/testdata/ppd-no-control-blobs-valid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"id": "adam-01",
"fileHeader": {
"id": "adam-01",
"immediateDestination": "231380104",
"immediateOrigin": "121042882",
"fileCreationDate": "2018-10-08T00:00:00Z",
"fileCreationTime": "0000-01-01T00:00:00Z",
"fileIDModifier": "A",
"immediateDestinationName": "Citadel",
"immediateOriginName": "Wells Fargo"
},
"batches": [
{
"batchHeader": {
"id": "adam-01",
"serviceClassCode": 200,
"companyName": "Wells Fargo",
"companyIdentification": "121042882",
"standardEntryClassCode": "PPD",
"companyEntryDescription": "Trans. Des",
"effectiveEntryDate": "2018-10-09T00:00:00Z",
"ODFIIdentification": "12104288",
"batchNumber": 1
},
"entryDetails": [
{
"id": "adam-01",
"transactionCode": 22,
"RDFIIdentification": "23138010",
"checkDigit": "4",
"DFIAccountNumber": "81967038518 ",
"amount": 100000,
"identificationNumber": "#83738AB# ",
"individualName": "Steven Tander ",
"discretionaryData": " ",
"addendaRecordIndicator": 1,
"traceNumber": "121042880000001",
"category": "Forward",
"addenda05": [
{
"entryDetailSequenceNumber": 1,
"sequenceNumber": 1,
"paymentRelatedInformation": "Bonus for working on #OSS!",
"typeCode": "",
"id": "gvluehibyuuqdoajiqfn"
}
]
}
]
}
],
"IATBatches": null,
"NotificationOfChange": null,
"ReturnEntries": null
}
6 changes: 3 additions & 3 deletions test/testdata/ppd-valid.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@
"identificationNumber": "#83738AB# ",
"individualName": "Steven Tander ",
"discretionaryData": " ",
"addendaRecordIndicator": 1,
"addendaRecordIndicator": 0,
"traceNumber": "121042880000001",
"category": "Forward"
}
],
"batchControl": {
"id": "adam-01",
"serviceClassCode": 200,
"entryAddendaÇount": 2,
"entryAddendaÇount": 0,
"entryHash": 23138010,
"totalDebit": 0,
"totalCredit": 100000,
Expand All @@ -57,7 +57,7 @@
"id": "adam-01",
"batchCount": 1,
"blockCount": 1,
"entryAddendaCount": 2,
"entryAddendaCount": 0,
"entryHash": 23138010,
"totalDebit": 0,
"totalCredit": 100000
Expand Down

0 comments on commit b347d08

Please sign in to comment.