Skip to content

Commit a8a714a

Browse files
authored
Merge pull request #1719 from adamdecaf/feat-partial-addenda98-parse
addenda98: support returning partial parsing of corrected data
2 parents dba7411 + e523199 commit a8a714a

File tree

2 files changed

+210
-60
lines changed

2 files changed

+210
-60
lines changed

addenda98.go

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -292,16 +292,35 @@ type CorrectedData struct {
292292
Identification string
293293
}
294294

295+
type correctedDataOptions struct {
296+
ReturnPartialData bool
297+
}
298+
299+
type correctedDataOption func(conf *correctedDataOptions)
300+
301+
func PartialCorrectedData() correctedDataOption {
302+
return func(conf *correctedDataOptions) {
303+
conf.ReturnPartialData = true
304+
}
305+
}
306+
295307
// ParseCorrectedData returns a struct with some fields filled in depending on the Addenda98's
296308
// Code and CorrectedData. Fields are trimmed when populated in this struct.
297-
func (addenda98 *Addenda98) ParseCorrectedData() *CorrectedData {
309+
func (addenda98 *Addenda98) ParseCorrectedData(options ...correctedDataOption) *CorrectedData {
298310
if addenda98 == nil {
299311
return nil
300312
}
313+
301314
cc := addenda98.ChangeCodeField()
302315
if cc == nil {
303316
return nil
304317
}
318+
319+
var conf correctedDataOptions
320+
for _, opt := range options {
321+
opt(&conf)
322+
}
323+
305324
data := addenda98.IATCorrectedDataField()
306325
switch cc.Code {
307326
case "C01": // Incorrect DFI Account Number
@@ -315,14 +334,23 @@ func (addenda98 *Addenda98) ParseCorrectedData() *CorrectedData {
315334
}
316335

317336
case "C03": // Incorrect Routing Number and Incorrect DFI Account Number
337+
var out CorrectedData
338+
318339
parts := strings.Fields(data)
319-
if len(parts) == 2 {
320-
return &CorrectedData{
321-
RoutingNumber: parts[0],
322-
AccountNumber: parts[1],
323-
}
340+
if len(parts) > 0 {
341+
out.RoutingNumber = parts[0]
342+
}
343+
if len(parts) > 1 {
344+
out.AccountNumber = parts[1]
345+
return &out
324346
}
325347

348+
// Return partial data only if we're asked to
349+
if conf.ReturnPartialData {
350+
return &out
351+
}
352+
return nil
353+
326354
case "C04": // Incorrect Individual Name
327355
if v := first(22, data); v != "" {
328356
return &CorrectedData{Name: v}
@@ -354,31 +382,55 @@ func (addenda98 *Addenda98) ParseCorrectedData() *CorrectedData {
354382
}
355383
}
356384
}
385+
386+
// Return partial data only if we're asked to
387+
if conf.ReturnPartialData {
388+
return &out
389+
}
390+
357391
return nil
358392

359393
case "C07": // Incorrect Routing Number, Incorrect DFI Account Number, and Incorrect Tranaction Code
360-
var cd CorrectedData
394+
var out CorrectedData
395+
if len(data) == 0 {
396+
return nil
397+
}
398+
399+
var parts []string
361400
if n := len(data); n > 9 {
362-
cd.RoutingNumber = data[:9]
363-
} else {
401+
out.RoutingNumber = data[:9]
402+
403+
parts = strings.Fields(data[9:])
404+
}
405+
// Return nothing if we have extra data
406+
if len(parts) > 2 && !conf.ReturnPartialData {
364407
return nil
365408
}
366-
parts := strings.Fields(data[9:])
367-
if len(parts) == 2 {
409+
410+
// Accumulate part by part
411+
if len(parts) > 0 {
412+
out.AccountNumber = parts[0]
413+
}
414+
if len(parts) > 1 {
368415
if n, err := strconv.Atoi(parts[1]); err == nil {
369-
cd.AccountNumber = parts[0]
370-
cd.TransactionCode = n
371-
return &cd
416+
out.TransactionCode = n
417+
return &out
372418
}
373-
} else {
374-
return nil
375419
}
376420

421+
// Return partial data only if we're asked to
422+
if conf.ReturnPartialData {
423+
return &out
424+
}
425+
426+
return nil
427+
377428
case "C09": // Incorrect Individual Identification Number
378429
if v := first(22, data); v != "" {
379430
return &CorrectedData{Identification: v}
380431
}
381432
}
433+
382434
// The Code/Correction is either unsupported or wasn't parsed correctly
383435
return nil
384436
}

addenda98_test.go

Lines changed: 142 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package ach
1919

2020
import (
21+
"fmt"
2122
"strings"
2223
"testing"
2324

@@ -358,55 +359,152 @@ func TestCorrectedData__first(t *testing.T) {
358359
}
359360

360361
func TestCorrectedData__ParseCorrectedData(t *testing.T) {
361-
run := func(code, data string) *CorrectedData {
362+
run := func(code, data string, options ...correctedDataOption) *CorrectedData {
362363
add := NewAddenda98()
363364
add.ChangeCode = code
364365
add.CorrectedData = data
365-
return add.ParseCorrectedData()
366+
return add.ParseCorrectedData(options...)
367+
}
368+
369+
cases := []struct {
370+
changeCode string
371+
correctedData string
372+
options []correctedDataOption
373+
374+
expected *CorrectedData
375+
}{
376+
{
377+
changeCode: "C01",
378+
correctedData: "123456789 ",
379+
expected: &CorrectedData{AccountNumber: "123456789"},
380+
},
381+
{
382+
changeCode: "C02",
383+
correctedData: "987654320 ",
384+
expected: &CorrectedData{RoutingNumber: "987654320"},
385+
},
386+
{
387+
changeCode: "C03",
388+
correctedData: "987654320 123456",
389+
expected: &CorrectedData{
390+
AccountNumber: "123456",
391+
RoutingNumber: "987654320",
392+
},
393+
},
394+
{
395+
changeCode: "C03",
396+
correctedData: "987654320 123456",
397+
expected: &CorrectedData{
398+
AccountNumber: "123456",
399+
RoutingNumber: "987654320",
400+
},
401+
},
402+
{
403+
changeCode: "C04",
404+
correctedData: "Jane Doe",
405+
expected: &CorrectedData{
406+
Name: "Jane Doe",
407+
},
408+
},
409+
{
410+
changeCode: "C05",
411+
correctedData: "22 other",
412+
expected: &CorrectedData{
413+
TransactionCode: 22,
414+
},
415+
},
416+
{
417+
changeCode: "C06",
418+
correctedData: "123456789 22",
419+
expected: &CorrectedData{
420+
AccountNumber: "123456789",
421+
TransactionCode: 22,
422+
},
423+
},
424+
{
425+
changeCode: "C07",
426+
correctedData: "987654320 12345 22",
427+
expected: &CorrectedData{
428+
RoutingNumber: "987654320",
429+
AccountNumber: "12345",
430+
TransactionCode: 22,
431+
},
432+
},
433+
{
434+
changeCode: "C07",
435+
correctedData: "9876543201242415 22",
436+
expected: &CorrectedData{
437+
RoutingNumber: "987654320",
438+
AccountNumber: "1242415",
439+
TransactionCode: 22,
440+
},
441+
},
442+
{
443+
changeCode: "C07",
444+
correctedData: "9876543201242415 22",
445+
expected: &CorrectedData{
446+
RoutingNumber: "987654320",
447+
AccountNumber: "1242415",
448+
TransactionCode: 22,
449+
},
450+
},
451+
{
452+
changeCode: "C07",
453+
correctedData: "1234",
454+
expected: nil,
455+
},
456+
{
457+
changeCode: "C07",
458+
correctedData: "1234",
459+
options: []correctedDataOption{PartialCorrectedData()},
460+
expected: &CorrectedData{
461+
RoutingNumber: "1234 ",
462+
},
463+
},
464+
{
465+
changeCode: "C07",
466+
correctedData: "987654320 1234 1234 1234",
467+
expected: nil,
468+
},
469+
{
470+
changeCode: "C07",
471+
correctedData: "987654320 1234 1234 1234",
472+
options: []correctedDataOption{PartialCorrectedData()},
473+
expected: &CorrectedData{
474+
AccountNumber: "1234",
475+
RoutingNumber: "987654320",
476+
TransactionCode: 1234,
477+
},
478+
},
479+
{
480+
changeCode: "C09",
481+
correctedData: "21345678 ",
482+
expected: &CorrectedData{
483+
Identification: "21345678",
484+
},
485+
},
486+
{
487+
changeCode: "C99",
488+
correctedData: " ",
489+
expected: nil,
490+
},
491+
{
492+
changeCode: "C99",
493+
correctedData: " ",
494+
options: []correctedDataOption{PartialCorrectedData()},
495+
expected: nil,
496+
},
497+
}
498+
499+
for _, tc := range cases {
500+
name := fmt.Sprintf("%s/%s", tc.changeCode, strings.TrimSpace(tc.correctedData))
501+
502+
t.Run(name, func(t *testing.T) {
503+
got := run(tc.changeCode, tc.correctedData, tc.options...)
504+
require.Equal(t, tc.expected, got)
505+
})
366506
}
367507

368-
if v := run("C01", "123456789 "); v.AccountNumber != "123456789" {
369-
t.Errorf("%#v", v)
370-
}
371-
if v := run("C02", "987654320 "); v.RoutingNumber != "987654320" {
372-
t.Errorf("%#v", v)
373-
}
374-
if v := run("C03", "987654320 123456"); v.AccountNumber != "123456" || v.RoutingNumber != "987654320" {
375-
t.Errorf("%#v", v)
376-
}
377-
if v := run("C03", "987654320 123456"); v.AccountNumber != "123456" || v.RoutingNumber != "987654320" {
378-
t.Errorf("%#v", v)
379-
}
380-
if v := run("C04", "Jane Doe"); v.Name != "Jane Doe" {
381-
t.Errorf("%#v", v)
382-
}
383-
if v := run("C05", "22 other"); v.TransactionCode != 22 {
384-
t.Errorf("%#v", v)
385-
}
386-
if v := run("C06", "123456789 22"); v.AccountNumber != "123456789" || v.TransactionCode != 22 {
387-
t.Errorf("%#v", v)
388-
}
389-
if v := run("C07", "987654320 12345 22"); v.RoutingNumber != "987654320" || v.AccountNumber != "12345" || v.TransactionCode != 22 {
390-
t.Errorf("%#v", v)
391-
}
392-
if v := run("C07", "9876543201242415 22"); v.RoutingNumber != "987654320" || v.AccountNumber != "1242415" || v.TransactionCode != 22 {
393-
t.Errorf("%#v", v)
394-
}
395-
if v := run("C07", "9876543201242415 22"); v.RoutingNumber != "987654320" || v.AccountNumber != "1242415" || v.TransactionCode != 22 {
396-
t.Errorf("%#v", v)
397-
}
398-
if v := run("C07", "1234"); v != nil {
399-
t.Errorf("expected nil: %v", v)
400-
}
401-
if v := run("C07", "987654320 1234 1234 1234"); v != nil {
402-
t.Errorf("expected nil: %v", v)
403-
}
404-
if v := run("C09", "21345678 "); v.Identification != "21345678" {
405-
t.Errorf("%#v", v)
406-
}
407-
if v := run("C99", " "); v != nil {
408-
t.Error("expected nil CorrectedData")
409-
}
410508
}
411509

412510
func TestCorrectedData__WriteCorrectionData(t *testing.T) {

0 commit comments

Comments
 (0)