Repository: moov-io/ach
Pull request #960 (2021-07-19 16:55:48)

Keep settlement date when parsing files.

vxio: Appreciate the PR @eweise , I'm going to merge this in!

Patch:
diff --git a/batchHeader.go b/batchHeader.go
index a53c1728..5297d29b 100644
--- a/batchHeader.go
+++ b/batchHeader.go
@@ -179,8 +179,9 @@ func (bh *BatchHeader) Parse(record string) {
 	// 70-75 Date transactions are to be posted to the receivers' account.
 	// You almost always want the transaction to post as soon as possible, so put tomorrow's date in YYMMDD format
 	bh.EffectiveEntryDate = bh.validateSimpleDate(record[69:75])
-	// 76-79 Always blank (just fill with spaces)
-	bh.settlementDate = "   "
+	// 76-78 Always blank if creating batches (just fill with spaces).
+	// Set to file value when parsing. Julian day format.
+	bh.settlementDate = bh.validateSettlementDate(record[75:78])
 	// 79-79 Always 1
 	bh.OriginatorStatusCode = bh.parseNumField(record[78:79])
 	// 80-87 Your ODFI's routing number without the last digit. The last digit is simply a
@@ -204,7 +205,7 @@ func (bh *BatchHeader) String() string {
 	buf.WriteString(bh.CompanyEntryDescriptionField())
 	buf.WriteString(bh.CompanyDescriptiveDateField())
 	buf.WriteString(bh.EffectiveEntryDateField())
-	buf.WriteString(bh.settlementDateField())
+	buf.WriteString(bh.SettlementDateField())
 	buf.WriteString(fmt.Sprintf("%v", bh.OriginatorStatusCode))
 	buf.WriteString(bh.ODFIIdentificationField())
 	buf.WriteString(bh.BatchNumberField())
@@ -358,7 +359,7 @@ func (bh *BatchHeader) BatchNumberField() string {
 	return bh.numericField(bh.BatchNumber, 7)
 }
 
-func (bh *BatchHeader) settlementDateField() string {
+func (bh *BatchHeader) SettlementDateField() string {
 	return bh.alphaField(bh.settlementDate, 3)
 }
 
diff --git a/iatBatchHeader.go b/iatBatchHeader.go
index ef6d21fb..e225aa57 100644
--- a/iatBatchHeader.go
+++ b/iatBatchHeader.go
@@ -235,8 +235,8 @@ func (iatBh *IATBatchHeader) Parse(record string) {
 	// 70-75 Date transactions are to be posted to the receivers' account.
 	// You almost always want the transaction to post as soon as possible, so put tomorrow's date in YYMMDD format
 	iatBh.EffectiveEntryDate = iatBh.validateSimpleDate(record[69:75])
-	// 76-79 Always blank (just fill with spaces)
-	iatBh.settlementDate = "   "
+	// 76-78 Always blank (just fill with spaces)
+	iatBh.settlementDate = iatBh.validateSettlementDate(record[75:78])
 	// 79-79 Always 1
 	iatBh.OriginatorStatusCode = iatBh.parseNumField(record[78:79])
 	// 80-87 Your ODFI's routing number without the last digit. The last digit is simply a
diff --git a/validators.go b/validators.go
index eb61a884..64a2b638 100644
--- a/validators.go
+++ b/validators.go
@@ -496,3 +496,23 @@ func CheckRoutingNumber(routingNumber string) error {
 func (v *validator) roundUp10(n int) int {
 	return int(math.Ceil(float64(n)/10.0)) * 10
 }
+
+func (v *validator) validateSettlementDate(s string) string {
+	emptyField := "   "
+
+	if s == emptyField || len(s) != len(emptyField) {
+		return emptyField
+	}
+
+	day, err := strconv.Atoi(s)
+	if err != nil {
+		return emptyField
+	}
+
+	if day < 1 || day > 366 {
+		return emptyField
+	}
+
+	return s
+
+}


Test patch:
diff --git a/batchHeader_test.go b/batchHeader_test.go
index 2c2501e7..4ef4757a 100644
--- a/batchHeader_test.go
+++ b/batchHeader_test.go
@@ -112,8 +112,8 @@ func testParseBatchHeader(t testing.TB) {
 	if record.EffectiveEntryDateField() != "190730" {
 		t.Errorf("EffectiveEntryDate Expected '190730' got: %v", record.EffectiveEntryDateField())
 	}
-	if record.settlementDate != "   " {
-		t.Errorf("SettlementDate Expected '   ' got: %v", record.settlementDate)
+	if record.SettlementDateField() != "   " {
+		t.Errorf("SettlementDate Expected '   ' got: %v", record.SettlementDateField())
 	}
 	if record.OriginatorStatusCode != 1 {
 		t.Errorf("OriginatorStatusCode Expected 1 got: %v", record.OriginatorStatusCode)
diff --git a/validators_test.go b/validators_test.go
index 4bdeb078..5783302f 100644
--- a/validators_test.go
+++ b/validators_test.go
@@ -162,3 +162,27 @@ func TestValidators__isAlphanumeric(t *testing.T) {
 		})
 	}
 }
+
+func TestValidators__validateJulianDay(t *testing.T) {
+	empty := "   "
+	cases := map[string]string{
+		// invalid
+		"":      empty,
+		"   ":   empty,
+		"01":    empty,
+		"01234": empty,
+		"XXX":   empty,
+		"000":   empty,
+		"367":   empty,
+		// valid
+		"001": "001",
+		"020": "020",
+		"366": "366",
+	}
+	v := validator{}
+	for input, valid := range cases {
+		if v.validateSettlementDate(input) != valid {
+			t.Errorf("julian day=%s failed", input)
+		}
+	}
+}