Skip to content

Commit

Permalink
Merge pull request #352 from moov-io/release-v0.3
Browse files Browse the repository at this point in the history
Break apart EntryDetail.Addendum into 02, 05, 98, and 99 Addenda fields
  • Loading branch information
bkmoovio authored Nov 5, 2018
2 parents f807c3d + 1cde0c6 commit 7408814
Show file tree
Hide file tree
Showing 64 changed files with 726 additions and 679 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ script:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then test -z $(gofmt -s -l $GOFILES); fi
- go test ./...
- misspell -error -locale US $GOFILES
- gocyclo -over 19 $GOFILES
- gocyclo -over 25 $GOFILES
- golint -set_exit_status $GOFILES
- megacheck ./...
after_success:
Expand Down
6 changes: 1 addition & 5 deletions addenda02.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

// Addenda02 is a Addendumer addenda which provides business transaction information for Addenda Type
// Code 02 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard.
// It is used for following StandardEntryClassCode: MTE, POS, and SHR.
type Addenda02 struct {
// ID is a client defined string used as a reference to this record.
ID string `json:"id"`
Expand Down Expand Up @@ -209,11 +210,6 @@ func (addenda02 *Addenda02) fieldInclusion() error {
return nil
}

// TypeCode Defines the specific explanation and format for the addenda02 information
func (addenda02 *Addenda02) typeCode() string {
return addenda02.TypeCode
}

// ReferenceInformationOneField returns a space padded ReferenceInformationOne string
func (addenda02 *Addenda02) ReferenceInformationOneField() string {
return addenda02.alphaField(addenda02.ReferenceInformationOne, 7)
Expand Down
6 changes: 1 addition & 5 deletions addenda05.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

// Addenda05 is a Addendumer addenda which provides business transaction information for Addenda Type
// Code 05 in a machine readable format. It is usually formatted according to ANSI, ASC, X12 Standard.
// It is used for the following StandardEntryClassCode: ACK, ATX, CCD, CIE, CTX, DNE, ENR WEB, PPD, TRX.
type Addenda05 struct {
// ID is a client defined string used as a reference to this record.
ID string `json:"id"`
Expand Down Expand Up @@ -142,8 +143,3 @@ func (addenda05 *Addenda05) SequenceNumberField() string {
func (addenda05 *Addenda05) EntryDetailSequenceNumberField() string {
return addenda05.numericField(addenda05.EntryDetailSequenceNumber, 7)
}

// TypeCode Defines the specific explanation and format for the addenda05 information
func (addenda05 *Addenda05) typeCode() string {
return addenda05.TypeCode
}
5 changes: 2 additions & 3 deletions addenda05_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ func mockAddenda05() *Addenda05 {
addenda05.SequenceNumber = 1
addenda05.PaymentRelatedInformation = "This is an Addenda05"
addenda05.EntryDetailSequenceNumber = 0000001

return addenda05
}

Expand Down Expand Up @@ -43,12 +42,12 @@ func testParseAddenda05(t testing.TB) {
entryDetail := mockPPDEntryDetail()

//Add an addenda to the PPD EntryDetail
entryDetail.AddAddenda(addendaPPD)
entryDetail.AddAddenda05(addendaPPD)

// add the PPD entry detail to the batch
r.currentBatch.AddEntry(entryDetail)

record := r.currentBatch.GetEntries()[0].Addendum[0].(*Addenda05)
record := r.currentBatch.GetEntries()[0].Addenda05[0]

if record.recordType != "7" {
t.Errorf("RecordType Expected '7' got: %v", record.recordType)
Expand Down
222 changes: 178 additions & 44 deletions batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,20 @@ func (batch *batch) build() error {
entryCount := 0
seq := 1
for i, entry := range batch.Entries {
entryCount = entryCount + 1 + len(entry.Addendum)
entryCount = entryCount + 1

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

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

currentTraceNumberODFI, err := strconv.Atoi(entry.TraceNumberField()[:8])
if err != nil {
Expand All @@ -172,14 +185,13 @@ func (batch *batch) build() error {
}
seq++
addendaSeq := 1
for x := range entry.Addendum {
for _, a := range entry.Addenda05 {
// sequences don't exist in NOC or Return addenda
if a, ok := batch.Entries[i].Addendum[x].(*Addenda05); ok {
a.SequenceNumber = addendaSeq
a.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])
}
a.SequenceNumber = addendaSeq
a.EntryDetailSequenceNumber = batch.parseNumField(batch.Entries[i].TraceNumberField()[8:])
addendaSeq++
}

}

// build a BatchControl record
Expand Down Expand Up @@ -251,9 +263,25 @@ func (batch *batch) isFieldInclusion() error {
if err := entry.Validate(); err != nil {
return err
}
for _, addenda := range entry.Addendum {
if err := addenda.Validate(); err != nil {
return nil

if entry.Addenda02 != nil {
if err := entry.Addenda02.Validate(); err != nil {
return err
}
}
for _, addenda05 := range entry.Addenda05 {
if err := addenda05.Validate(); err != nil {
return err
}
}
if entry.Addenda98 != nil {
if err := entry.Addenda98.Validate(); err != nil {
return err
}
}
if entry.Addenda99 != nil {
if err := entry.Addenda99.Validate(); err != nil {
return err
}
}
}
Expand All @@ -266,7 +294,20 @@ func (batch *batch) isFieldInclusion() error {
func (batch *batch) isBatchEntryCount() error {
entryCount := 0
for _, entry := range batch.Entries {
entryCount = entryCount + 1 + len(entry.Addendum)
entryCount = entryCount + 1

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

if entry.Addenda99 != nil {
entryCount = entryCount + 1
}
}
if entryCount != batch.Control.EntryAddendaCount {
msg := fmt.Sprintf(msgBatchCalculatedControlEquality, entryCount, batch.Control.EntryAddendaCount)
Expand Down Expand Up @@ -369,30 +410,45 @@ func (batch *batch) isTraceNumberODFI() error {
// isAddendaSequence check multiple errors on addenda records in the batch entries
func (batch *batch) isAddendaSequence() error {
for _, entry := range batch.Entries {
if len(entry.Addendum) > 0 {

if entry.Addenda02 != nil {
if entry.AddendaRecordIndicator != 1 {
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaRecordIndicator", Msg: msgBatchAddendaIndicator}
}
}

if len(entry.Addenda05) > 0 {
// addenda without indicator flag of 1
if entry.AddendaRecordIndicator != 1 {
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaRecordIndicator", Msg: msgBatchAddendaIndicator}
}
lastSeq := -1
// check if sequence is ascending
for _, addenda := range entry.Addendum {
for _, a := range entry.Addenda05 {
// sequences don't exist in NOC or Return addenda
if a, ok := addenda.(*Addenda05); ok {

if a.SequenceNumber < lastSeq {
msg := fmt.Sprintf(msgBatchAscending, a.SequenceNumber, lastSeq)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "SequenceNumber", Msg: msg}
}
lastSeq = a.SequenceNumber
// check that we are in the correct Entry Detail
if !(a.EntryDetailSequenceNumberField() == entry.TraceNumberField()[8:]) {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, a.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
}

if a.SequenceNumber < lastSeq {
msg := fmt.Sprintf(msgBatchAscending, a.SequenceNumber, lastSeq)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "SequenceNumber", Msg: msg}
}
lastSeq = a.SequenceNumber
// check that we are in the correct Entry Detail
if !(a.EntryDetailSequenceNumberField() == entry.TraceNumberField()[8:]) {
msg := fmt.Sprintf(msgBatchAddendaTraceNumber, a.EntryDetailSequenceNumberField(), entry.TraceNumberField()[8:])
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TraceNumber", Msg: msg}
}
}
}
if entry.Addenda98 != nil {
if entry.AddendaRecordIndicator != 1 {
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaRecordIndicator", Msg: msgBatchAddendaIndicator}
}
}
if entry.Addenda99 != nil {
if entry.AddendaRecordIndicator != 1 {
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaRecordIndicator", Msg: msgBatchAddendaIndicator}
}
}
}
return nil
}
Expand All @@ -413,38 +469,116 @@ func (batch *batch) isCategory() error {
return nil
}

// categoryForwardAddenda02 verifies CategoryForward Addenda02 TypeCode is 02
func (batch *batch) categoryForwardAddenda02(entry *EntryDetail, addenda Addendumer) error {
if addenda.typeCode() != "02" {
msg := fmt.Sprintf(msgBatchTypeCode, addenda.typeCode(), "02", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TypeCode", Msg: msg}
// addendaFieldInclusion verifies Addenda* Field Inclusion based on entry.Category and
// batchHeader.StandardEntryClassCode
// Forward Entries:
// MTE, POS, and SHR can only have Addenda02
// ACK, ATX, CCD, CIE, CTX, DNE, ENR, WEB, PPD, TRX can only have Addenda05
// ARC, BOC, POP, RCK, TEL, TRC, XCK cannot have Addenda02 or Addenda05
// Notification of Change:
// COR and Addenda98
// Return:
// Addenda99
//
// ToDo: Implement Batch for MTE, TRX, TRC, XCK
//
func (batch *batch) addendaFieldInclusion(entry *EntryDetail) error {
switch entry.Category {
case CategoryForward:
if err := batch.addendaFieldInclusionForward(entry); err != nil {
return err
}
case CategoryNOC:
if err := batch.addendaFieldInclusionNOC(entry); err != nil {
return err
}
case CategoryReturn:
if err := batch.addendaFieldInclusionReturn(entry); err != nil {
return err
}
}
return nil
}

// categoryForwardAddenda05 verifies CategoryForward Addenda05 TypeCode is 05
func (batch *batch) categoryForwardAddenda05(entry *EntryDetail, addenda Addendumer) error {
if addenda.typeCode() != "05" {
msg := fmt.Sprintf(msgBatchTypeCode, addenda.typeCode(), "05", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TypeCode", Msg: msg}
// addendaFieldInclusionForward verifies Addenda* Field Inclusion for entry.Category Forward
func (batch *batch) addendaFieldInclusionForward(entry *EntryDetail) error {
switch batch.Header.StandardEntryClassCode {
case "MTE", "POS", "SHR":
if entry.Addenda05 != nil {
msg := fmt.Sprintf(msgBatchAddenda, "Addenda05", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda05", Msg: msg}
}
// ACK, ATX, CCD, CIE, CTX, DNE, ENR WEB, PPD, TRX can only have Addenda05
case "ACK", "ATX", "CCD", "CIE", "CTX", "DNE", "ENR", "WEB", "PPD", "TRX":
if entry.Addenda02 != nil {
msg := fmt.Sprintf(msgBatchAddenda, "Addenda02", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda02", Msg: msg}
}
case "ARC", "BOC", "COR", "POP", "RCK", "TEL", "TRC", "XCK":
if entry.Addenda02 != nil {
msg := fmt.Sprintf(msgBatchAddenda, "Addenda02", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda02", Msg: msg}
}
if entry.Addenda05 != nil {
msg := fmt.Sprintf(msgBatchAddenda, "Addenda05", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda05", Msg: msg}
}
}
if batch.Header.StandardEntryClassCode != "COR" {
if entry.Addenda98 != nil {
msg := fmt.Sprintf(msgBatchAddenda, "Addenda98", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda98", Msg: msg}
}
}
if entry.Addenda99 != nil {
msg := fmt.Sprintf(msgBatchAddenda, "Addenda99", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda99", Msg: msg}
}
return nil
}

// categoryNOCAddenda98 verifies CategoryNOC Addenda98 TypeCode is 98
func (batch *batch) categoryNOCAddenda98(entry *EntryDetail, addenda Addendumer) error {
if addenda.typeCode() != "98" {
msg := fmt.Sprintf(msgBatchTypeCode, addenda.typeCode(), "98", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TypeCode", Msg: msg}
// addendaFieldInclusionNOC verifies Addenda* Field Inclusion for entry.Category NOC
func (batch *batch) addendaFieldInclusionNOC(entry *EntryDetail) error {
if entry.Addenda02 != nil {
msg := fmt.Sprintf(msgBatchAddenda, "Addenda02", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda02", Msg: msg}
}
if entry.Addenda05 != nil {
msg := fmt.Sprintf(msgBatchAddenda, "Addenda05", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda05", Msg: msg}
}
if batch.Header.StandardEntryClassCode == "COR" {
if entry.Addenda98 == nil {
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda98", Msg: msgFieldInclusion}
}
} else {
if entry.Addenda98 != nil {
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda98", Msg: msgFieldInclusion}
}
}
if entry.Addenda99 != nil {
msg := fmt.Sprintf(msgBatchAddenda, "Addenda99", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda99", Msg: msg}
}
return nil
}

// categoryReturnAddenda99 verifies CategoryReturn Addenda99 TypeCode is 99
func (batch *batch) categoryReturnAddenda99(entry *EntryDetail, addenda Addendumer) error {
if addenda.typeCode() != "99" {
msg := fmt.Sprintf(msgBatchTypeCode, addenda.typeCode(), "99", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TypeCode", Msg: msg}
// addendaFieldInclusionReturn verifies Addenda* Field Inclusion for entry.Category Return
func (batch *batch) addendaFieldInclusionReturn(entry *EntryDetail) error {
if entry.Addenda02 != nil {
msg := fmt.Sprintf(msgBatchAddenda, "Addenda02", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda02", Msg: msg}
}
if entry.Addenda05 != nil {
msg := fmt.Sprintf(msgBatchAddenda, "Addenda05", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda05", Msg: msg}
}
if entry.Addenda98 != nil {
msg := fmt.Sprintf(msgBatchAddenda, "Addenda98", entry.Category, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda98", Msg: msg}
}
if entry.Addenda99 == nil {
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda99", Msg: msgFieldInclusion}
}
return nil
}
Loading

0 comments on commit 7408814

Please sign in to comment.