Repository: moov-io/ach
Pull request #900 (2021-04-15 22:39:13)

change notes for v1.7.0 release
Still needs to be rebased after #899 is merged.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e57af600..57d44543 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+## v1.7.0 (Released 2021-04-16)
+
+ADDITIONS
+
+- webui: add button to support JSON to ACH conversions
+- achcli: add `describe` package to allow human-readable printing to be reused in other tools
+- batch: add validation option, `bypassCompanyIdentificationMatch`, to allow batches in which the `CompanyIdentification` field in the batch header and control do not match
+
 ## v1.6.5 (Released 2021-04-15)
 
 IMPROVEMENTS
diff --git a/batch.go b/batch.go
index 9cb7a80b..f45e6c90 100644
--- a/batch.go
+++ b/batch.go
@@ -320,11 +320,13 @@ func (batch *Batch) verify() error {
 			return batch.Error("ServiceClassCode",
 				NewErrBatchHeaderControlEquality(batch.Header.ServiceClassCode, batch.Control.ServiceClassCode))
 		}
-		// Company Identification must match the Company ID from the batch header record
-		if batch.Header.CompanyIdentification != batch.Control.CompanyIdentification {
+		// Company Identification in the batch header and control must match if bypassCompanyIdentificationMatch is not enabled.
+		if batch.Header.CompanyIdentification != batch.Control.CompanyIdentification &&
+			!(batch.validateOpts != nil && batch.validateOpts.BypassCompanyIdentificationMatch) {
 			return batch.Error("CompanyIdentification",
 				NewErrBatchHeaderControlEquality(batch.Header.CompanyIdentification, batch.Control.CompanyIdentification))
 		}
+
 		// Control ODFIIdentification must be the same as batch header
 		if batch.Header.ODFIIdentification != batch.Control.ODFIIdentification {
 			return batch.Error("ODFIIdentification",
diff --git a/file.go b/file.go
index f64d2292..70633c0d 100644
--- a/file.go
+++ b/file.go
@@ -564,6 +564,10 @@ type ValidateOpts struct {
 
 	// AllowZeroBatches allows the file to have zero batches
 	AllowZeroBatches bool `json:"allowZeroBatches"`
+
+	// BypassCompanyIdentificationMatch allows batches in which the Company Identification field
+	// in the batch header and control do not match.
+	BypassCompanyIdentificationMatch bool `json:"bypassCompanyIdentificationMatch"`
 }
 
 // ValidateWith performs NACHA format rule checks on each record according to their specification
diff --git a/server/files.go b/server/files.go
index 81f974da..f71fc60c 100644
--- a/server/files.go
+++ b/server/files.go
@@ -114,16 +114,18 @@ func decodeCreateFileRequest(_ context.Context, request *http.Request) (interfac
 	}
 
 	const (
-		requireABAOrigin  = "requireABAOrigin"
-		bypassOrigin      = "bypassOrigin"
-		bypassDestination = "bypassDestination"
-		allowZeroBatches  = "allowZeroBatches"
+		requireABAOrigin                 = "requireABAOrigin"
+		bypassOrigin                     = "bypassOrigin"
+		bypassDestination                = "bypassDestination"
+		bypassCompanyIdentificationMatch = "bypassCompanyIdentificationMatch"
+		allowZeroBatches                 = "allowZeroBatches"
 	)
 
 	validationNames := []string{
 		requireABAOrigin,
 		bypassOrigin,
 		bypassDestination,
+		bypassCompanyIdentificationMatch,
 		allowZeroBatches,
 	}
 
@@ -148,6 +150,8 @@ func decodeCreateFileRequest(_ context.Context, request *http.Request) (interfac
 			req.validateOpts.BypassOriginValidation = true
 		case bypassDestination:
 			req.validateOpts.BypassDestinationValidation = true
+		case bypassCompanyIdentificationMatch:
+			req.validateOpts.BypassCompanyIdentificationMatch = true
 		case allowZeroBatches:
 			req.validateOpts.AllowZeroBatches = true
 		}


Test patch:
diff --git a/batch_test.go b/batch_test.go
index fb3989d0..7d45904f 100644
--- a/batch_test.go
+++ b/batch_test.go
@@ -1464,3 +1464,23 @@ func TestBatch__CustomTraceNumbers(t *testing.T) {
 		t.Errorf("unexpected trace number: %v", batch.Entries[0].TraceNumber)
 	}
 }
+
+func TestBatch__CompanyIdentificationMismatch(t *testing.T) {
+	file := mockFilePPD()
+	batcher := file.Batches[0]
+	batcher.SetValidation(&ValidateOpts{
+		BypassCompanyIdentificationMatch: true,
+	})
+
+	bh := batcher.GetHeader()
+	bh.CompanyIdentification = "121042882"
+
+	bc := batcher.GetControl()
+	bc.CompanyIdentification = "111111111"
+
+	err := batcher.Validate()
+	if err != nil {
+		t.Fatalf("unexpected validation error: %v", err)
+	}
+
+}
diff --git a/server/files_test.go b/server/files_test.go
index 968b99f7..346c1b49 100644
--- a/server/files_test.go
+++ b/server/files_test.go
@@ -111,6 +111,12 @@ func TestFiles__decodeCreateFileRequest__validateOpts(t *testing.T) {
 		query  string
 		expect ach.ValidateOpts
 	}{
+		{
+			query: "?bypassCompanyIdentificationMatch=true",
+			expect: ach.ValidateOpts{
+				BypassCompanyIdentificationMatch: true,
+			},
+		},
 		{
 			query: "?bypassOrigin=true&requireABAOrigin=true",
 			expect: ach.ValidateOpts{