Repository: moov-io/ach
Pull request #1279 (2023-08-11 19:40:01)

fix: Corrected Data is 29 characters, not 22
Fixes https://github.com/moov-io/ach/issues/1278 


Patch:
diff --git a/addenda98.go b/addenda98.go
index e05e8593..3d37dce7 100644
--- a/addenda98.go
+++ b/addenda98.go
@@ -318,32 +318,34 @@ func first(size int, data string) string {
 	return strings.TrimSpace(data[:size])
 }
 
+const correctedDataCharLength = 29
+
 // ParseCorrectedData returns the string properlty formatted and justified for an
 // Addenda98.CorrectedData field. The code must be an official NACHA change code.
 func WriteCorrectionData(code string, data *CorrectedData) string {
 	pad := &converters{}
 	switch strings.ToUpper(code) {
 	case "C01":
-		return pad.alphaField(data.AccountNumber, 22)
+		return pad.alphaField(data.AccountNumber, correctedDataCharLength)
 	case "C02":
-		return pad.alphaField(data.RoutingNumber, 22)
+		return pad.alphaField(data.RoutingNumber, correctedDataCharLength)
 	case "C03":
-		spaces := strings.Repeat(" ", 22-len(data.RoutingNumber)-len(data.AccountNumber))
+		spaces := strings.Repeat(" ", correctedDataCharLength-len(data.RoutingNumber)-len(data.AccountNumber))
 		return fmt.Sprintf("%s%s%s", data.RoutingNumber, spaces, data.AccountNumber)
 	case "C04":
-		return pad.alphaField(data.Name, 22)
+		return pad.alphaField(data.Name, correctedDataCharLength)
 	case "C05":
-		return pad.alphaField(strconv.Itoa(data.TransactionCode), 22)
+		return pad.alphaField(strconv.Itoa(data.TransactionCode), correctedDataCharLength)
 	case "C06":
 		txcode := strconv.Itoa(data.TransactionCode)
-		spaces := strings.Repeat(" ", 22-len(data.AccountNumber)-len(txcode))
+		spaces := strings.Repeat(" ", correctedDataCharLength-len(data.AccountNumber)-len(txcode))
 		return fmt.Sprintf("%s%s%s", data.AccountNumber, spaces, txcode)
 	case "C07":
 		txcode := strconv.Itoa(data.TransactionCode)
-		spaces := strings.Repeat(" ", 22-9-len(data.AccountNumber)-len(txcode))
+		spaces := strings.Repeat(" ", correctedDataCharLength-9-len(data.AccountNumber)-len(txcode))
 		return fmt.Sprintf("%s%s%s%s", data.RoutingNumber, data.AccountNumber, spaces, txcode)
 	case "C09":
-		return pad.alphaField(data.Identification, 22)
+		return pad.alphaField(data.Identification, correctedDataCharLength)
 	}
-	return pad.alphaField("", 22)
+	return pad.alphaField("", correctedDataCharLength)
 }


Test patch:
diff --git a/addenda98_test.go b/addenda98_test.go
index 9055edea..c9f2c21c 100644
--- a/addenda98_test.go
+++ b/addenda98_test.go
@@ -21,6 +21,7 @@ import (
 	"testing"
 
 	"github.com/moov-io/base"
+	"github.com/stretchr/testify/require"
 )
 
 func mockAddenda98() *Addenda98 {
@@ -372,6 +373,9 @@ func TestCorrectedData__ParseCorrectedData(t *testing.T) {
 	if v := run("C03", "987654320   123456"); v.AccountNumber != "123456" || v.RoutingNumber != "987654320" {
 		t.Errorf("%#v", v)
 	}
+	if v := run("C03", "987654320              123456"); v.AccountNumber != "123456" || v.RoutingNumber != "987654320" {
+		t.Errorf("%#v", v)
+	}
 	if v := run("C04", "Jane Doe"); v.Name != "Jane Doe" {
 		t.Errorf("%#v", v)
 	}
@@ -387,6 +391,9 @@ func TestCorrectedData__ParseCorrectedData(t *testing.T) {
 	if v := run("C07", "9876543201242415    22"); v.RoutingNumber != "987654320" || v.AccountNumber != "1242415" || v.TransactionCode != 22 {
 		t.Errorf("%#v", v)
 	}
+	if v := run("C07", "9876543201242415           22"); v.RoutingNumber != "987654320" || v.AccountNumber != "1242415" || v.TransactionCode != 22 {
+		t.Errorf("%#v", v)
+	}
 	if v := run("C07", "1234"); v != nil {
 		t.Errorf("expected nil: %v", v)
 	}
@@ -403,35 +410,27 @@ func TestCorrectedData__ParseCorrectedData(t *testing.T) {
 
 func TestCorrectedData__WriteCorrectionData(t *testing.T) {
 	data := &CorrectedData{AccountNumber: "12345"}
-	if v := WriteCorrectionData("C01", data); v != "12345                 " {
-		t.Errorf("C01 got %q (length=%d)", v, len(v))
-	}
+	require.Equal(t, "12345                        ", WriteCorrectionData("C01", data))
+
 	data = &CorrectedData{RoutingNumber: "987654320"}
-	if v := WriteCorrectionData("C02", data); v != "987654320             " {
-		t.Errorf("C02 got %q (length=%d)", v, len(v))
-	}
+	require.Equal(t, "987654320                    ", WriteCorrectionData("C02", data))
+
 	data = &CorrectedData{AccountNumber: "123", RoutingNumber: "987654320"}
-	if v := WriteCorrectionData("C03", data); v != "987654320          123" {
-		t.Errorf("C03 got %q (length=%d)", v, len(v))
-	}
+	require.Equal(t, "987654320                 123", WriteCorrectionData("C03", data))
+
 	data = &CorrectedData{Name: "Jane Doe"}
-	if v := WriteCorrectionData("C04", data); v != "Jane Doe              " {
-		t.Errorf("C04 got %q (length=%d)", v, len(v))
-	}
+	require.Equal(t, "Jane Doe                     ", WriteCorrectionData("C04", data))
+
 	data = &CorrectedData{TransactionCode: 22}
-	if v := WriteCorrectionData("C05", data); v != "22                    " {
-		t.Errorf("C05 got %q (length=%d)", v, len(v))
-	}
+	require.Equal(t, "22                           ", WriteCorrectionData("C05", data))
+
 	data = &CorrectedData{AccountNumber: "5421", TransactionCode: 27}
-	if v := WriteCorrectionData("C06", data); v != "5421                27" {
-		t.Errorf("C06 got %q (length=%d)", v, len(v))
-	}
+	require.Equal(t, "5421                       27", WriteCorrectionData("C06", data))
+
 	data = &CorrectedData{RoutingNumber: "987654320", AccountNumber: "5421", TransactionCode: 32}
-	if v := WriteCorrectionData("C07", data); v != "9876543205421       32" {
-		t.Errorf("C07 got %q (length=%d)", v, len(v))
-	}
+	require.Equal(t, "9876543205421              32", WriteCorrectionData("C07", data))
+
 	data = &CorrectedData{Identification: "FooBar"}
-	if v := WriteCorrectionData("C09", data); v != "FooBar                " {
-		t.Errorf("C09 got %q (length=%d)", v, len(v))
-	}
+	require.Equal(t, "FooBar                       ", WriteCorrectionData("C09", data))
+
 }
diff --git a/test/issues/issue1278_test.go b/test/issues/issue1278_test.go
new file mode 100644
index 00000000..bb796581
--- /dev/null
+++ b/test/issues/issue1278_test.go
@@ -0,0 +1,47 @@
+// Licensed to The Moov Authors under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. The Moov Authors licenses this file to you under
+// the Apache License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package issues
+
+import (
+	"testing"
+
+	"github.com/moov-io/ach"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestIssue1278(t *testing.T) {
+	data := &ach.CorrectedData{
+		AccountNumber: "cDgvNMS41YdAlj0XD",
+		RoutingNumber: "021000021",
+	}
+	output := ach.WriteCorrectionData("C03", data)
+
+	expected := "021000021   cDgvNMS41YdAlj0XD"
+	require.Equal(t, expected, output)
+
+	// Verify read works
+	addenda98 := &ach.Addenda98{
+		ChangeCode:    "C03",
+		CorrectedData: expected,
+	}
+	data = addenda98.ParseCorrectedData()
+	require.NotNil(t, data)
+	require.Equal(t, "021000021", data.RoutingNumber)
+	require.Equal(t, "cDgvNMS41YdAlj0XD", data.AccountNumber)
+}