Skip to content

Commit

Permalink
all: convert TraceNumber from int to string
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdecaf committed Nov 27, 2018
1 parent 5c2ba64 commit e126202
Show file tree
Hide file tree
Showing 20 changed files with 64 additions and 56 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## v0.5.0 (Unreleased)

BREAKING CHANGES

- `TraceNumber` has been changed from `int` to a `string`. (See [#366](https://github.com/moov-io/ach/issues/366))
- Previously zero-prefixed ABA routing numbers would have their leading zero truncated.

## v0.4.1 (Unreleased)

ADDITIONS
Expand Down
8 changes: 4 additions & 4 deletions addenda02.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type Addenda02 struct {
// TerminalState Identifies the state in which the electronic terminal is located
TerminalState string `json:"terminalState"`
// TraceNumber Standard Entry Detail Trace Number
TraceNumber int `json:"traceNumber,omitempty"`
TraceNumber string `json:"traceNumber,omitempty"`
// validator is composed for data validation
validator
// converters is composed for ACH to GoLang Converters
Expand Down Expand Up @@ -87,7 +87,7 @@ func (addenda02 *Addenda02) Parse(record string) {
// 78-79
addenda02.TerminalState = strings.TrimSpace(record[77:79])
// 80-94
addenda02.TraceNumber = addenda02.parseNumField(record[79:94])
addenda02.TraceNumber = strings.TrimSpace(record[79:94])
}

// String writes the Addenda02 struct to a 94 character string.
Expand Down Expand Up @@ -257,7 +257,7 @@ func (addenda02 *Addenda02) TerminalStateField() string {
return addenda02.alphaField(addenda02.TerminalState, 2)
}

// TraceNumberField returns a space padded traceNumber string
// TraceNumberField returns a space padded TraceNumber string
func (addenda02 *Addenda02) TraceNumberField() string {
return addenda02.numericField(addenda02.TraceNumber, 15)
return addenda02.stringField(addenda02.TraceNumber, 15)
}
2 changes: 1 addition & 1 deletion addenda02_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func mockAddenda02() *Addenda02 {
addenda02.TerminalLocation = "Target Store 0049"
addenda02.TerminalCity = "PHILADELPHIA"
addenda02.TerminalState = "PA"
addenda02.TraceNumber = 121042880000123
addenda02.TraceNumber = "121042880000123"
return addenda02
}

Expand Down
6 changes: 3 additions & 3 deletions addenda98.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type Addenda98 struct {
// CorrectedData
CorrectedData string `json:"correctedData"`
// TraceNumber matches the Entry Detail Trace Number of the entry being returned.
TraceNumber int `json:"traceNumber,omitempty"`
TraceNumber string `json:"traceNumber,omitempty"`

// validator is composed for data validation
validator
Expand Down Expand Up @@ -83,7 +83,7 @@ func (addenda98 *Addenda98) Parse(record string) {
// 36-64
addenda98.CorrectedData = strings.TrimSpace(record[35:64])
// 80-94
addenda98.TraceNumber = addenda98.parseNumField(record[79:94])
addenda98.TraceNumber = strings.TrimSpace(record[79:94])
}

// String writes the Addenda98 struct to a 94 character string
Expand Down Expand Up @@ -151,7 +151,7 @@ func (addenda98 *Addenda98) CorrectedDataField() string {

// TraceNumberField returns a zero padded traceNumber string
func (addenda98 *Addenda98) TraceNumberField() string {
return addenda98.numericField(addenda98.TraceNumber, 15)
return addenda98.stringField(addenda98.TraceNumber, 15)
}

func makeChangeCodeDict() map[string]*changeCode {
Expand Down
6 changes: 3 additions & 3 deletions addenda98_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func mockAddenda98() *Addenda98 {
addenda98.OriginalTrace = 12345
addenda98.OriginalDFI = "9101298"
addenda98.CorrectedData = "1918171614"
addenda98.TraceNumber = 91012980000088
addenda98.TraceNumber = "91012980000088"

return addenda98
}
Expand Down Expand Up @@ -42,8 +42,8 @@ func testAddenda98Parse(t testing.TB) {
if addenda98.CorrectedData != "1918171614" {
t.Errorf("expected %v got %v", "1918171614", addenda98.CorrectedData)
}
if addenda98.TraceNumber != 91012980000088 {
t.Errorf("expected %v got %v", 91012980000088, addenda98.TraceNumber)
if addenda98.TraceNumber != "091012980000088" {
t.Errorf("expected %v got %v", "091012980000088", addenda98.TraceNumber)
}
}

Expand Down
8 changes: 4 additions & 4 deletions addenda99.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ type Addenda99 struct {
// AddendaInformation
AddendaInformation string `json:"addendaInformation,omitempty"`
// TraceNumber matches the Entry Detail Trace Number of the entry being returned.
TraceNumber int `json:"traceNumber,omitempty"`
TraceNumber string `json:"traceNumber,omitempty"`

// validator is composed for data validation
validator
Expand Down Expand Up @@ -95,7 +95,7 @@ func (Addenda99 *Addenda99) Parse(record string) {
// 36-79
Addenda99.AddendaInformation = strings.TrimSpace(record[35:79])
// 80-94
Addenda99.TraceNumber = Addenda99.parseNumField(record[79:94])
Addenda99.TraceNumber = strings.TrimSpace(record[79:94])
}

// String writes the Addenda99 struct to a 94 character string
Expand Down Expand Up @@ -186,9 +186,9 @@ func (Addenda99 *Addenda99) IATAddendaInformationField() string {
return Addenda99.alphaField(Addenda99.AddendaInformation[9:44], 34)
}

// TraceNumberField returns a zero padded traceNumber string
// TraceNumberField returns a zero padded TraceNumber string
func (Addenda99 *Addenda99) TraceNumberField() string {
return Addenda99.numericField(Addenda99.TraceNumber, 15)
return Addenda99.stringField(Addenda99.TraceNumber, 15)
}

func makeReturnCodeDict() map[string]*returnCode {
Expand Down
6 changes: 3 additions & 3 deletions addenda99_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ func testAddenda99Parse(t testing.TB) {
if addenda99.AddendaInformation != "Authorization revoked" {
t.Errorf("expected: %v got: %v", "Authorization revoked", addenda99.AddendaInformation)
}
if addenda99.TraceNumber != 91012980000066 {
t.Errorf("expected: %v got: %v", 91012980000066, addenda99.TraceNumber)
if addenda99.TraceNumber != "091012980000066" {
t.Errorf("expected: %v got: %v", "091012980000066", addenda99.TraceNumber)
}
}

Expand Down Expand Up @@ -240,7 +240,7 @@ func BenchmarkAddenda99AddendaInformationField(b *testing.B) {

func testAddenda99TraceNumberField(t testing.TB) {
addenda99 := mockAddenda99()
addenda99.TraceNumber = 91012980000066
addenda99.TraceNumber = "91012980000066"
exp := "091012980000066"
if addenda99.TraceNumberField() != exp {
t.Errorf("expected %v received %v", exp, addenda99.TraceNumberField())
Expand Down
4 changes: 1 addition & 3 deletions batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -486,10 +486,8 @@ func (batch *Batch) calculateADVBatchAmounts() (credit int, debit int) {
// isSequenceAscending Individual Entry Detail Records within individual batches must
// be in ascending Trace Number order (although Trace Numbers need not necessarily be consecutive).
func (batch *Batch) isSequenceAscending() error {

lastSeq := -1

if !batch.IsADV() {
lastSeq := "0"
for _, entry := range batch.Entries {
if entry.TraceNumber <= lastSeq {
msg := fmt.Sprintf(msgBatchAscending, entry.TraceNumber, lastSeq)
Expand Down
4 changes: 2 additions & 2 deletions batchControl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func testParseBatchControl(t testing.TB) {
ODFIIdentification: "7640125"}
r.addCurrentBatch(NewBatchPPD(&bh))

r.currentBatch.AddEntry(&EntryDetail{TransactionCode: 27, Amount: 10500, RDFIIdentification: "5320001", TraceNumber: 76401255655291})
r.currentBatch.AddEntry(&EntryDetail{TransactionCode: 27, Amount: 10500, RDFIIdentification: "5320001", TraceNumber: "76401255655291"})
if err := r.parseBatchControl(); err != nil {
t.Errorf("%T: %s", err, err)
}
Expand Down Expand Up @@ -123,7 +123,7 @@ func testBCString(t testing.TB) {
ODFIIdentification: "7640125"}
r.addCurrentBatch(NewBatchPPD(&bh))

r.currentBatch.AddEntry(&EntryDetail{TransactionCode: 27, Amount: 10500, RDFIIdentification: "5320001", TraceNumber: 76401255655291})
r.currentBatch.AddEntry(&EntryDetail{TransactionCode: 27, Amount: 10500, RDFIIdentification: "5320001", TraceNumber: "76401255655291"})
if err := r.parseBatchControl(); err != nil {
t.Errorf("%T: %s", err, err)
}
Expand Down
11 changes: 7 additions & 4 deletions batch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ func testCreditBatchIsBatchAmount(t testing.TB) {
e2 := mockEntryDetail()
e2.TransactionCode = 22
e2.Amount = 100
e2.TraceNumber = e1.TraceNumber + 10
// replace last 2 of TraceNumber
e2.TraceNumber = e1.TraceNumber[:13] + "10"
mockBatch.AddEntry(e2)
if err := mockBatch.build(); err != nil {
t.Errorf("%T: %s", err, err)
Expand Down Expand Up @@ -143,7 +144,8 @@ func testSavingsBatchIsBatchAmount(t testing.TB) {
e2 := mockEntryDetail()
e2.TransactionCode = 37
e2.Amount = 100
e2.TraceNumber = e1.TraceNumber + 10
// replace last 2 of TraceNumber
e2.TraceNumber = e1.TraceNumber[:13] + "10"

mockBatch.AddEntry(e2)
if err := mockBatch.build(); err != nil {
Expand Down Expand Up @@ -351,7 +353,7 @@ func BenchmarkBatchIsAddendaSeqAscending(b *testing.B) {
func testBatchIsSequenceAscending(t testing.TB) {
mockBatch := mockBatch()
e3 := mockEntryDetail()
e3.TraceNumber = 1
e3.TraceNumber = "1"
mockBatch.AddEntry(e3)
mockBatch.GetControl().EntryAddendaCount = 2
if err := mockBatch.verify(); err != nil {
Expand Down Expand Up @@ -471,7 +473,8 @@ func testBatchCategoryForwardReturn(t testing.TB) {
entry := mockEntryDetail()
entry.Addenda99 = mockAddenda99()
entry.Category = CategoryReturn
entry.TraceNumber = entry.TraceNumber + 10
// replace last 2 of TraceNumber
entry.TraceNumber = entry.TraceNumber[:13] + "10"
entry.AddendaRecordIndicator = 1
mockBatch.AddEntry(entry)
if err := mockBatch.build(); err != nil {
Expand Down
11 changes: 5 additions & 6 deletions entryDetail.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ type EntryDetail struct {
// For addenda Records, the Trace Number will be identical to the Trace Number
// in the associated Entry Detail Record, since the Trace Number is associated
// with an entry or item rather than a physical record.
TraceNumber int `json:"traceNumber,omitempty"`
TraceNumber string `json:"traceNumber,omitempty"`
// Addenda02 for use with StandardEntryClassCode MTE, POS, and SHR
Addenda02 *Addenda02 `json:"addenda02,omitempty"`
// Addenda05 for use with StandardEntryClassCode: ACK, ATX, CCD, CIE, CTX, DNE, ENR, WEB, PPD, TRX.
Expand Down Expand Up @@ -134,7 +134,7 @@ func (ed *EntryDetail) Parse(record string) {
ed.AddendaRecordIndicator = ed.parseNumField(record[78:79])
// 80-94 An internal identification (alphanumeric) that you use to uniquely identify
// this Entry Detail Record This number should be unique to the transaction and will help identify the transaction in case of an inquiry
ed.TraceNumber = ed.parseNumField(record[79:94])
ed.TraceNumber = strings.TrimSpace(record[79:94])
}

// String writes the EntryDetail struct to a 94 character string.
Expand Down Expand Up @@ -233,7 +233,7 @@ func (ed *EntryDetail) fieldInclusion() error {
Msg: msgFieldInclusion + ", did you use NewEntryDetail()?",
}
}
if ed.TraceNumber == 0 {
if ed.TraceNumber == "" {
return &FieldError{
FieldName: "TraceNumber",
Value: ed.TraceNumberField(),
Expand All @@ -253,8 +253,7 @@ func (ed *EntryDetail) SetRDFI(rdfi string) *EntryDetail {

// SetTraceNumber takes first 8 digits of ODFI and concatenates a sequence number onto the TraceNumber
func (ed *EntryDetail) SetTraceNumber(ODFIIdentification string, seq int) {
trace := ed.stringField(ODFIIdentification, 8) + ed.numericField(seq, 7)
ed.TraceNumber = ed.parseNumField(trace)
ed.TraceNumber = ed.stringField(ODFIIdentification, 8) + ed.numericField(seq, 7)
}

// RDFIIdentificationField get the rdfiIdentification with zero padding
Expand Down Expand Up @@ -502,7 +501,7 @@ func (ed *EntryDetail) ItemTypeIndicator() string {

// TraceNumberField returns a zero padded TraceNumber string
func (ed *EntryDetail) TraceNumberField() string {
return ed.numericField(ed.TraceNumber, 15)
return ed.stringField(ed.TraceNumber, 15)
}

// CreditOrDebit returns a "C" for credit or "D" for debit based on the entry TransactionCode
Expand Down
4 changes: 2 additions & 2 deletions entryDetail_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func testMockEntryDetail(t testing.TB) {
if entry.IndividualName != "Wade Arnold" {
t.Error("IndividualName dependent default value has changed")
}
if entry.TraceNumber != 121042880000001 {
if entry.TraceNumber != "121042880000001" {
t.Errorf("TraceNumber dependent default value has changed %v", entry.TraceNumber)
}
}
Expand Down Expand Up @@ -519,7 +519,7 @@ func BenchmarkEDFieldInclusionIndividualName(b *testing.B) {
// testEDFieldInclusionTraceNumber validates trace number field inclusion
func testEDFieldInclusionTraceNumber(t testing.TB) {
entry := mockEntryDetail()
entry.TraceNumber = 0
entry.TraceNumber = "0"
if err := entry.Validate(); err != nil {
if e, ok := err.(*FieldError); ok {
if !strings.Contains(e.Msg, msgFieldInclusion) {
Expand Down
2 changes: 1 addition & 1 deletion iatBatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ func (batch *IATBatch) calculateBatchAmounts() (credit int, debit int) {
// isSequenceAscending Individual Entry Detail Records within individual batches must
// be in ascending Trace Number order (although Trace Numbers need not necessarily be consecutive).
func (batch *IATBatch) isSequenceAscending() error {
lastSeq := -1
lastSeq := "-1"
for _, entry := range batch.Entries {
if entry.TraceNumber <= lastSeq {
msg := fmt.Sprintf(msgBatchAscending, entry.TraceNumber, lastSeq)
Expand Down
10 changes: 6 additions & 4 deletions iatBatch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func mockIATAddenda98() *Addenda98 {
addenda98.OriginalTrace = 231380100000001
addenda98.OriginalDFI = "12104288"
addenda98.CorrectedData = "89722-C3"
addenda98.TraceNumber = 121042880000001
addenda98.TraceNumber = "121042880000001"
return addenda98
}

Expand Down Expand Up @@ -618,7 +618,8 @@ func testIATBatchCreditIsBatchAmount(t testing.TB) {
e2 := mockIATEntryDetail()
e2.TransactionCode = 22
e2.Amount = 5000
e2.TraceNumber = e1.TraceNumber + 10
// replace last 2 of TraceNumber
e2.TraceNumber = e1.TraceNumber[:13] + "10"
mockBatch.AddEntry(e2)
mockBatch.Entries[1].Addenda10 = mockAddenda10()
mockBatch.Entries[1].Addenda11 = mockAddenda11()
Expand Down Expand Up @@ -665,7 +666,8 @@ func testIATBatchDebitIsBatchAmount(t testing.TB) {
e2 := mockIATEntryDetail()
e2.TransactionCode = 27
e2.Amount = 5000
e2.TraceNumber = e1.TraceNumber + 10
// replace last 2 of TraceNumber
e2.TraceNumber = e1.TraceNumber[:13] + "10"
mockBatch.AddEntry(e2)
mockBatch.Entries[1].Addenda10 = mockAddenda10()
mockBatch.Entries[1].Addenda11 = mockAddenda11()
Expand Down Expand Up @@ -959,7 +961,7 @@ func BenchmarkIATBatchisEntryHash(b *testing.B) {
func testIATBatchIsSequenceAscending(t testing.TB) {
mockBatch := mockIATBatch(t)
e2 := mockIATEntryDetail()
e2.TraceNumber = 1
e2.TraceNumber = "1"
mockBatch.AddEntry(e2)
mockBatch.Entries[1].Addenda10 = mockAddenda10()
mockBatch.Entries[1].Addenda11 = mockAddenda11()
Expand Down
13 changes: 6 additions & 7 deletions iatEntryDetail.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ type IATEntryDetail struct {
// For addenda Records, the Trace Number will be identical to the Trace Number
// in the associated Entry Detail Record, since the Trace Number is associated
// with an entry or item rather than a physical record.
TraceNumber int `json:"traceNumber,omitempty"`
TraceNumber string `json:"traceNumber,omitempty"`
// Addenda10 is mandatory for IAT entries
//
// The Addenda10 Record identifies the Receiver of the transaction and the dollar amount of
Expand Down Expand Up @@ -165,7 +165,7 @@ func (ed *IATEntryDetail) Parse(record string) {
ed.AddendaRecordIndicator = ed.parseNumField(record[78:79])
// 80-94 An internal identification (alphanumeric) that you use to uniquely identify
// this Entry Detail Record This number should be unique to the transaction and will help identify the transaction in case of an inquiry
ed.TraceNumber = ed.parseNumField(record[79:94])
ed.TraceNumber = strings.TrimSpace(record[79:94])
}

// String writes the EntryDetail struct to a 94 character string.
Expand Down Expand Up @@ -263,7 +263,7 @@ func (ed *IATEntryDetail) fieldInclusion() error {
Msg: msgFieldInclusion + ", did you use NewIATEntryDetail()?",
}
}
if ed.TraceNumber == 0 {
if ed.TraceNumber == "" {
return &FieldError{
FieldName: "TraceNumber",
Value: ed.TraceNumberField(),
Expand All @@ -283,16 +283,15 @@ func (ed *IATEntryDetail) SetRDFI(rdfi string) *IATEntryDetail {

// SetTraceNumber takes first 8 digits of ODFI and concatenates a sequence number onto the TraceNumber
func (ed *IATEntryDetail) SetTraceNumber(ODFIIdentification string, seq int) {
trace := ed.stringField(ODFIIdentification, 8) + ed.numericField(seq, 7)
ed.TraceNumber = ed.parseNumField(trace)
ed.TraceNumber = ed.stringField(ODFIIdentification, 8) + ed.numericField(seq, 7)
}

// RDFIIdentificationField get the rdfiIdentification with zero padding
func (ed *IATEntryDetail) RDFIIdentificationField() string {
return ed.stringField(ed.RDFIIdentification, 8)
}

// AddendaRecordsField returns a zero padded TraceNumber string
// AddendaRecordsField returns a zero padded AddendaRecords string
func (ed *IATEntryDetail) AddendaRecordsField() string {
return ed.numericField(ed.AddendaRecords, 4)
}
Expand Down Expand Up @@ -328,7 +327,7 @@ func (ed *IATEntryDetail) SecondaryOFACSreeningIndicatorField() string {

// TraceNumberField returns a zero padded TraceNumber string
func (ed *IATEntryDetail) TraceNumberField() string {
return ed.numericField(ed.TraceNumber, 15)
return ed.stringField(ed.TraceNumber, 15)
}

// AddAddenda17 appends an Addenda17 to the IATEntryDetail
Expand Down
4 changes: 2 additions & 2 deletions iatEntryDetail_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func testMockIATEntryDetail(t testing.TB) {
if entry.Amount != 100000 {
t.Error("Amount dependent default value has changed")
}
if entry.TraceNumber != 231380100000001 {
if entry.TraceNumber != "231380100000001" {
t.Errorf("TraceNumber dependent default value has changed %v", entry.TraceNumber)
}
}
Expand Down Expand Up @@ -452,7 +452,7 @@ func BenchmarkIATEDDFIAccountNumber(b *testing.B) {
// testIATEDTraceNumber validates IATEntryDetail TraceNumber fieldInclusion
func testIATEDTraceNumber(t testing.TB) {
iatEd := mockIATEntryDetail()
iatEd.TraceNumber = 0
iatEd.TraceNumber = "0"
if err := iatEd.Validate(); err != nil {
if e, ok := err.(*FieldError); ok {
if e.FieldName != "TraceNumber" {
Expand Down
Loading

0 comments on commit e126202

Please sign in to comment.