Skip to content

Commit

Permalink
Merge pull request moov-io#382 from moov-io/Batch-Validation-Transact…
Browse files Browse the repository at this point in the history
…ion-Code-matches-Service-Class-Code

Batch validation transaction code matches service class code
  • Loading branch information
bkmoovio authored Nov 27, 2018
2 parents ead9f0f + aeb0381 commit 6fee36a
Show file tree
Hide file tree
Showing 48 changed files with 431 additions and 57 deletions.
2 changes: 1 addition & 1 deletion advEntryDetail_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ func TestADVEDFieldInclusionSequenceNumber(t *testing.T) {
// TestADVEDBadTransactionCode validates TransactionCode field inclusion
func TestBadTransactionCode(t *testing.T) {
entry := mockADVEntryDetail()
entry.TransactionCode = 1
entry.TransactionCode = 27
if err := entry.Validate(); err != nil {
if e, ok := err.(*FieldError); ok {
if e.FieldName != "TransactionCode" {
Expand Down
29 changes: 29 additions & 0 deletions batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -757,3 +757,32 @@ func (batch *Batch) IsADV() bool {
ok := batch.GetHeader().StandardEntryClassCode == "ADV"
return ok
}

// ValidTranCodeForServiceClassCode validates a TransactionCode is valid for a ServiceClassCode
func (batch *Batch) ValidTranCodeForServiceClassCode(entry *EntryDetail) error {
// ADV should use ADVEntryDetail
// ADV Transaction Codes are 81, 82, 83, 84, 85, 86, 87, 88
switch entry.TransactionCode {
case 81, 82, 83, 84, 85, 86, 87, 88:
msg := fmt.Sprintf(msgBatchServiceClassTranCode, entry.TransactionCode, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg}
}

switch batch.Header.ServiceClassCode {
case 280:
msg := fmt.Sprintf(msgBatchServiceClassTranCode, batch.Header.ServiceClassCode, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ServiceClassCode", Msg: msg}
case 200:
case 220:
if entry.CreditOrDebit() == "D" {
msg := fmt.Sprintf(msgBatchServiceClassTranCode, entry.TransactionCode, batch.Header.ServiceClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg}
}
case 225:
if entry.CreditOrDebit() == "C" {
msg := fmt.Sprintf(msgBatchServiceClassTranCode, entry.TransactionCode, batch.Header.ServiceClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg}
}
}
return nil
}
5 changes: 4 additions & 1 deletion batchACK.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ func (batch *BatchACK) Validate() error {
msg := fmt.Sprintf(msgBatchTransactionCode, entry.TransactionCode, "ACK")
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg}
}

// Verify the TransactionCode is valid for a ServiceClassCode
if err := batch.ValidTranCodeForServiceClassCode(entry); err != nil {
return err
}
// Verify Addenda* FieldInclusion based on entry.Category and batchHeader.StandardEntryClassCode
if err := batch.addendaFieldInclusion(entry); err != nil {
return err
Expand Down
15 changes: 15 additions & 0 deletions batchACK_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,18 @@ func TestBatchACKAddendum99Category(t *testing.T) {
}
}
}

// TestBatchACKValidTranCodeForServiceClassCode validates a transactionCode based on ServiceClassCode
func TestBatchACKValidTranCodeForServiceClassCode(t *testing.T) {
mockBatch := mockBatchACK()
mockBatch.GetHeader().ServiceClassCode = 225
if err := mockBatch.Create(); err != nil {
if e, ok := err.(*BatchError); ok {
if e.FieldName != "TransactionCode" {
t.Errorf("%T: %s", err, err)
}
} else {
t.Errorf("%T: %s", err, err)
}
}
}
1 change: 1 addition & 0 deletions batchADV_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func mockBatchADVHeader() *BatchHeader {
bh.CompanyIdentification = "121042882"
bh.CompanyEntryDescription = "Accounting"
bh.ODFIIdentification = "12104288"
bh.OriginatorStatusCode = 0
return bh
}

Expand Down
6 changes: 5 additions & 1 deletion batchARC.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (batch *BatchARC) Validate() error {

// ARC detail entries can only be a debit, ServiceClassCode must allow debits
switch batch.Header.ServiceClassCode {
case 200, 220, 280:
case 200, 220:
msg := fmt.Sprintf(msgBatchServiceClassCode, batch.Header.ServiceClassCode, "ARC")
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ServiceClassCode", Msg: msg}
}
Expand All @@ -69,6 +69,10 @@ func (batch *BatchARC) Validate() error {
msg := fmt.Sprintf(msgBatchCheckSerialNumber, "ARC")
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CheckSerialNumber", Msg: msg}
}
// Verify the TransactionCode is valid for a ServiceClassCode
if err := batch.ValidTranCodeForServiceClassCode(entry); err != nil {
return err
}
// Verify Addenda* FieldInclusion based on entry.Category and batchHeader.StandardEntryClassCode
if err := batch.addendaFieldInclusion(entry); err != nil {
return err
Expand Down
5 changes: 4 additions & 1 deletion batchATX.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ func (batch *BatchATX) Validate() error {
msg := fmt.Sprintf(msgBatchATXAddendaCount, addendaRecords, len(entry.Addenda05))
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaCount", Msg: msg}
}

// Verify the TransactionCode is valid for a ServiceClassCode
if err := batch.ValidTranCodeForServiceClassCode(entry); err != nil {
return err
}
// Verify Addenda* FieldInclusion based on entry.Category and batchHeader.StandardEntryClassCode
if err := batch.addendaFieldInclusion(entry); err != nil {
return err
Expand Down
15 changes: 15 additions & 0 deletions batchATX_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -618,3 +618,18 @@ func TestBatchATXAddendum99(t *testing.T) {
}
}
}

// TestBatchATXValidTranCodeForServiceClassCode validates a transactionCode based on ServiceClassCode
func TestBatchATXValidTranCodeForServiceClassCode(t *testing.T) {
mockBatch := mockBatchATX()
mockBatch.GetHeader().ServiceClassCode = 225
if err := mockBatch.Create(); err != nil {
if e, ok := err.(*BatchError); ok {
if e.FieldName != "TransactionCode" {
t.Errorf("%T: %s", err, err)
}
} else {
t.Errorf("%T: %s", err, err)
}
}
}
6 changes: 5 additions & 1 deletion batchBOC.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (batch *BatchBOC) Validate() error {

// BOC detail entries can only be a debit, ServiceClassCode must allow debits
switch batch.Header.ServiceClassCode {
case 200, 220, 280:
case 200, 220:
msg := fmt.Sprintf(msgBatchServiceClassCode, batch.Header.ServiceClassCode, "RCK")
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ServiceClassCode", Msg: msg}
}
Expand All @@ -74,6 +74,10 @@ func (batch *BatchBOC) Validate() error {
msg := fmt.Sprintf(msgBatchCheckSerialNumber, "BOC")
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "CheckSerialNumber", Msg: msg}
}
// Verify the TransactionCode is valid for a ServiceClassCode
if err := batch.ValidTranCodeForServiceClassCode(entry); err != nil {
return err
}
// Verify Addenda* FieldInclusion based on entry.Category and batchHeader.StandardEntryClassCode
if err := batch.addendaFieldInclusion(entry); err != nil {
return err
Expand Down
5 changes: 4 additions & 1 deletion batchCCD.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ func (batch *BatchCCD) Validate() error {
msg := fmt.Sprintf(msgBatchAddendaCount, len(entry.Addenda05), 1, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaCount", Msg: msg}
}

// Verify the TransactionCode is valid for a ServiceClassCode
if err := batch.ValidTranCodeForServiceClassCode(entry); err != nil {
return err
}
// Verify Addenda* FieldInclusion based on entry.Category and batchHeader.StandardEntryClassCode
if err := batch.addendaFieldInclusion(entry); err != nil {
return err
Expand Down
34 changes: 33 additions & 1 deletion batchCCD_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
// mockBatchCCDHeader creates a CCD batch header
func mockBatchCCDHeader() *BatchHeader {
bh := NewBatchHeader()
bh.ServiceClassCode = 220
bh.ServiceClassCode = 225
bh.StandardEntryClassCode = "CCD"
bh.CompanyName = "Your Company, inc"
bh.CompanyIdentification = "121042882"
Expand Down Expand Up @@ -303,3 +303,35 @@ func BenchmarkBatchCCDReceivingCompanyField(b *testing.B) {
testBatchCCDReceivingCompanyField(b)
}
}

// TestBatchCCDValidTranCodeForServiceClassCode validates a transactionCode based on ServiceClassCode
func TestBatchCCDValidTranCodeForServiceClassCode(t *testing.T) {
mockBatch := mockBatchCCD()
mockBatch.GetHeader().ServiceClassCode = 220
if err := mockBatch.Create(); err != nil {
if e, ok := err.(*BatchError); ok {
if e.FieldName != "TransactionCode" {
t.Errorf("%T: %s", err, err)
}
} else {
t.Errorf("%T: %s", err, err)
}
}
}

// TestBatchCCDAddenda02 validates BatchCCD cannot have Addenda02
func TestBatchCCDAddenda02(t *testing.T) {
mockBatch := mockBatchCCD()
mockBatch.Entries[0].AddendaRecordIndicator = 1
mockBatch.GetEntries()[0].Addenda02 = mockAddenda02()
mockBatch.Create()
if err := mockBatch.Validate(); err != nil {
if e, ok := err.(*BatchError); ok {
if e.FieldName != "Addenda02" {
t.Errorf("%T: %s", err, err)
}
} else {
t.Errorf("%T: %s", err, err)
}
}
}
6 changes: 5 additions & 1 deletion batchCIE.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (batch *BatchCIE) Validate() error {

// CIE detail entries can only be a debit, ServiceClassCode must allow debits
switch batch.Header.ServiceClassCode {
case 200, 225, 280:
case 200, 225:
msg := fmt.Sprintf(msgBatchServiceClassCode, batch.Header.ServiceClassCode, "CIE")
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ServiceClassCode", Msg: msg}
}
Expand All @@ -62,6 +62,10 @@ func (batch *BatchCIE) Validate() error {
msg := fmt.Sprintf(msgBatchRequiredAddendaCount, len(entry.Addenda05), 1, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaCount", Msg: msg}
}
// Verify the TransactionCode is valid for a ServiceClassCode
if err := batch.ValidTranCodeForServiceClassCode(entry); err != nil {
return err
}
// Verify Addenda* FieldInclusion based on entry.Category and batchHeader.StandardEntryClassCode
if err := batch.addendaFieldInclusion(entry); err != nil {
return err
Expand Down
17 changes: 17 additions & 0 deletions batchCIE_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,3 +489,20 @@ func TestBatchCIEAddenda(t *testing.T) {
}
}
}

// TestBatchCIEAddenda02 validates BatchCIE cannot have Addenda02
func TestBatchCIEAddenda02(t *testing.T) {
mockBatch := mockBatchCIE()
mockBatch.Entries[0].AddendaRecordIndicator = 1
mockBatch.GetEntries()[0].Addenda02 = mockAddenda02()
mockBatch.Create()
if err := mockBatch.Validate(); err != nil {
if e, ok := err.(*BatchError); ok {
if e.FieldName != "Addenda02" {
t.Errorf("%T: %s", err, err)
}
} else {
t.Errorf("%T: %s", err, err)
}
}
}
12 changes: 4 additions & 8 deletions batchCOR.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ func (batch *BatchCOR) Validate() error {
msg := fmt.Sprintf(msgBatchSECType, batch.Header.StandardEntryClassCode, "COR")
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "StandardEntryClassCode", Msg: msg}
}

if batch.Header.ServiceClassCode == 280 {
msg := fmt.Sprintf(msgBatchSECType, batch.Header.ServiceClassCode, "COR")
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "ServiceClassCode", Msg: msg}
}
// The Amount field must be zero
// batch.verify calls batch.isBatchAmount which ensures the batch.Control values are accurate.
if batch.Control.TotalCreditEntryDollarAmount != 0 || batch.Control.TotalDebitEntryDollarAmount != 0 {
Expand Down Expand Up @@ -102,13 +105,6 @@ func (batch *BatchCOR) isAddenda98() error {
if entry.Addenda98 == nil {
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addenda98", Msg: msgBatchCORAddenda}
}
// Addenda98 must be Validated
if err := entry.Addenda98.Validate(); err != nil {
// convert the field error in to a batch error for a consistent api
if e, ok := err.(*FieldError); ok {
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: e.FieldName, Msg: e.Msg}
}
}
}
return nil
}
36 changes: 36 additions & 0 deletions batchCOR_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,3 +411,39 @@ func TestBatchCORCategoryNOCAddenda98(t *testing.T) {
}
}
}

// TestBatchCORValidTranCodeForServiceClassCode validates a transactionCode based on ServiceClassCode
func TestBatchCORValidTranCodeForServiceClassCode(t *testing.T) {
mockBatch := mockBatchCOR()
mockBatch.GetHeader().ServiceClassCode = 280
if err := mockBatch.Create(); err != nil {
if e, ok := err.(*BatchError); ok {
if e.FieldName != "ServiceClassCode" {
t.Errorf("%T: %s", err, err)
}
} else {
t.Errorf("%T: %s", err, err)
}
}
}

// TestBatchCORInvalidAddenda98 validates that an error is returned if Addenda98 is invalid
func TestBatchCORTestBatchCORInvalidAddenda98(t *testing.T) {
mockBatch := NewBatchCOR(mockBatchCORHeader())
mockBatch.AddEntry(mockCOREntryDetail())
mockBatch.GetEntries()[0].Category = CategoryNOC
addenda98 := mockAddenda98()
addenda98.recordType = "03"
mockBatch.GetEntries()[0].Addenda98 = addenda98

mockBatch.Entries[0].AddendaRecordIndicator = 1
if err := mockBatch.Create(); err != nil {
if e, ok := err.(*BatchError); ok {
if e.FieldName != "recordType" {
t.Errorf("%T: %s", err, err)
}
} else {
t.Errorf("%T: %s", err, err)
}
}
}
5 changes: 4 additions & 1 deletion batchCTX.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ func (batch *BatchCTX) Validate() error {
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "Addendum", Msg: msg}
default:
}

// Verify the TransactionCode is valid for a ServiceClassCode
if err := batch.ValidTranCodeForServiceClassCode(entry); err != nil {
return err
}
// Verify Addenda* FieldInclusion based on entry.Category and batchHeader.StandardEntryClassCode
if err := batch.addendaFieldInclusion(entry); err != nil {
return err
Expand Down
32 changes: 32 additions & 0 deletions batchCTX_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -585,3 +585,35 @@ func TestBatchCTXAddendum99(t *testing.T) {
}
}
}

// TestBatchCTXValidTranCodeForServiceClassCode validates a transactionCode based on ServiceClassCode
func TestBatchCTXValidTranCodeForServiceClassCode(t *testing.T) {
mockBatch := mockBatchCTX()
mockBatch.GetHeader().ServiceClassCode = 225
if err := mockBatch.Create(); err != nil {
if e, ok := err.(*BatchError); ok {
if e.FieldName != "TransactionCode" {
t.Errorf("%T: %s", err, err)
}
} else {
t.Errorf("%T: %s", err, err)
}
}
}

// TestBatchCTXAddenda02 validates BatchCTX cannot have Addenda02
func TestBatchCTXAddenda02(t *testing.T) {
mockBatch := mockBatchCTX()
mockBatch.Entries[0].AddendaRecordIndicator = 1
mockBatch.GetEntries()[0].Addenda02 = mockAddenda02()
mockBatch.Create()
if err := mockBatch.Validate(); err != nil {
if e, ok := err.(*BatchError); ok {
if e.FieldName != "Addenda02" {
t.Errorf("%T: %s", err, err)
}
} else {
t.Errorf("%T: %s", err, err)
}
}
}
4 changes: 4 additions & 0 deletions batchDNE.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ func (batch *BatchDNE) Validate() error {
msg := fmt.Sprintf(msgBatchAddendaCount, len(entry.Addenda05), 1, batch.Header.StandardEntryClassCode)
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "AddendaCount", Msg: msg}
}
// Verify the TransactionCode is valid for a ServiceClassCode
if err := batch.ValidTranCodeForServiceClassCode(entry); err != nil {
return err
}
// Verify Addenda* FieldInclusion based on entry.Category and batchHeader.StandardEntryClassCode
if err := batch.addendaFieldInclusion(entry); err != nil {
return err
Expand Down
5 changes: 4 additions & 1 deletion batchENR.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ func (batch *BatchENR) Validate() error {
msg := fmt.Sprintf(msgBatchTransactionCode, entry.TransactionCode, "ENR")
return &BatchError{BatchNumber: batch.Header.BatchNumber, FieldName: "TransactionCode", Msg: msg}
}

// Verify the TransactionCode is valid for a ServiceClassCode
if err := batch.ValidTranCodeForServiceClassCode(entry); err != nil {
return err
}
// ENR must have one Addenda05
// Verify Addenda* FieldInclusion based on entry.Category and batchHeader.StandardEntryClassCode
if err := batch.addendaFieldInclusion(entry); err != nil {
Expand Down
Loading

0 comments on commit 6fee36a

Please sign in to comment.