Repository: moov-io/ach
Pull request #1105 (2022-10-20 17:19:26)

feat: allow empty strings when allow missing file header opt is set

codecov-commenter: # [Codecov](https://codecov.io/gh/moov-io/ach/pull/1105?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=moov-io) Report
> Merging [#1105](https://codecov.io/gh/moov-io/ach/pull/1105?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=moov-io) (faa97ad) into [master](https://codecov.io/gh/moov-io/ach/commit/9afc3f1c86ee7c8893fc3287b874f09e5dc769cc?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=moov-io) (9afc3f1) will **decrease** coverage by `0.03%`.
> The diff coverage is `58.33%`.

> :exclamation: Current head faa97ad differs from pull request most recent head 764a7bb. Consider uploading reports for the commit 764a7bb to get more accurate results

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #1105      +/-   ##
==========================================
- Coverage   90.90%   90.86%   -0.04%     
==========================================
  Files          68       68              
  Lines        6396     6401       +5     
==========================================
+ Hits         5814     5816       +2     
- Misses        341      343       +2     
- Partials      241      242       +1     
```

</details>

Patch:
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 31fb3abc..1abcd7db 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,1 +1,2 @@
 * @adamdecaf
+* @darwinz
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5ac2a065..23dcc947 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,15 +1,3 @@
-## v1.21.2 (Released 2022-10-04)
-
-IMPROVEMENTS
-
-- fix: copy forward the validate options when segmenting a file
-- fix: passing validate opts to http handler
-- test: verify ValidateOpts are set in segment http response
-
-BUILD
-
-- meta: add feature request template
-
 ## v1.21.0 (Released 2022-10-03)
 
 IMPROVEMENTS
diff --git a/addenda98.go b/addenda98.go
index 25f5d0a5..f7d309c7 100644
--- a/addenda98.go
+++ b/addenda98.go
@@ -186,9 +186,9 @@ func makeChangeCodeDict() map[string]*ChangeCode {
 		{"C02", "Incorrect transit/routing number", "Once valid transit/routing number must be changed"},
 		{"C03", "Incorrect transit/routing number and bank account number", "Once valid transit/routing number must be changed and causes a change to bank account number structure"},
 		{"C04", "Bank account name change", "Customer has changed name or ODFI submitted name incorrectly"},
-		{"C05", "Incorrect transaction code", "Entry posted to demand account should contain savings transaction codes or vice versa"},
-		{"C06", "Incorrect bank account number and transit code", "Bank account number must be changed and transaction code should indicate posting to another account type (demand/savings)"},
-		{"C07", "Incorrect transit/routing number, bank account number and transaction code", "Changes required in three fields indicated"},
+		{"C05", "Incorrect payment code", "Entry posted to demand account should contain savings payment codes or vice versa"},
+		{"C06", "Incorrect bank account number and transit code", "Bank account number must be changed and payment code should indicate posting to another account type (demand/savings)"},
+		{"C07", "Incorrect transit/routing number, bank account number and payment code", "Changes required in three fields indicated"},
 		{"C09", "Incorrect individual ID number", "Individual's ID number is incorrect"},
 		{"C10", "Incorrect company name", "Company name is no longer valid and should be changed."},
 		{"C11", "Incorrect company identification", "Company ID is no longer valid and should be changed"},
diff --git a/cmd/achcli/describe/file.go b/cmd/achcli/describe/file.go
index b1e5284d..d4ddf00d 100644
--- a/cmd/achcli/describe/file.go
+++ b/cmd/achcli/describe/file.go
@@ -88,74 +88,6 @@ func File(ww io.Writer, file *ach.File, opts *Opts) {
 		}
 	}
 
-	// IATBatches
-	for i := range file.IATBatches {
-		iatBatch := file.IATBatches[i]
-		bh := iatBatch.GetHeader()
-		if bh != nil {
-			fmt.Fprintln(w, "\n  BatchNumber\tSECCode\tServiceClassCode\tIATIndicator\tDestinationCountryCode\tFE Indicator\tFE ReferenceIndicator\tFE Reference\tCompanyEntryDescription")
-			fmt.Fprintf(w, "  %d\t%s\t%d %s\t%s\t%s\t%s\t%d\t%s\t%s\n",
-				bh.BatchNumber,
-				bh.StandardEntryClassCode,
-				bh.ServiceClassCode,
-				serviceClassCodes[bh.ServiceClassCode],
-				bh.IATIndicator,
-				bh.ISODestinationCountryCode,
-				bh.ForeignExchangeIndicator,
-				bh.ForeignExchangeReferenceIndicator,
-				bh.ForeignExchangeReference,
-				bh.CompanyEntryDescription,
-			)
-
-			fmt.Fprintln(w, "\n    OriginatorIdentification\tISOOriginatingCurrencyCode\tISODestinationCurrencyCode\tODFIIdentification\tEffectiveEntryDate\tOriginatorStatusCode")
-			fmt.Fprintf(w, "    %s\t%s\t%s\t%s\t%s\t%d\n",
-				bh.OriginatorIdentification,
-				bh.ISOOriginatingCurrencyCode,
-				bh.ISODestinationCurrencyCode,
-				bh.ODFIIdentification,
-				bh.EffectiveEntryDate,
-				bh.OriginatorStatusCode,
-			)
-		}
-
-		entries := iatBatch.GetEntries()
-		for j := range entries {
-			fmt.Fprintln(w, "\n    TransactionCode\tRDFIIdentification\tAccountNumber\tAmount\tAddendaRecords\tTraceNumber\tCategory")
-
-			e := entries[j]
-			accountNumber := e.DFIAccountNumber
-			if opts.MaskAccountNumbers {
-				accountNumber = maskAccountNumber(strings.TrimSpace(accountNumber))
-			}
-
-			fmt.Fprintf(w, "    %d %s\t%s\t%s\t%d\t%d\t%s\t%s\n", e.TransactionCode, transactionCodes[e.TransactionCode], e.RDFIIdentification, accountNumber, e.Amount, e.AddendaRecords, e.TraceNumber, e.Category)
-
-			dumpAddenda10(w, e.Addenda10)
-			dumpAddenda11(w, e.Addenda11)
-			dumpAddenda12(w, e.Addenda12)
-			dumpAddenda13(w, e.Addenda13)
-			dumpAddenda14(w, e.Addenda14)
-			dumpAddenda15(w, e.Addenda15)
-			dumpAddenda16(w, e.Addenda16)
-
-			for i := range e.Addenda17 {
-				dumpAddenda17(w, e.Addenda17[i])
-			}
-			for i := range e.Addenda18 {
-				dumpAddenda18(w, e.Addenda18[i])
-			}
-
-			dumpAddenda98(w, e.Addenda98)
-			dumpAddenda99(w, e.Addenda99)
-		}
-
-		bc := iatBatch.GetControl()
-		if bc != nil {
-			fmt.Fprintln(w, "\n  ServiceClassCode\tEntryAddendaCount\tEntryHash\tTotalDebits\tTotalCredits\tMACCode\tODFIIdentification\tBatchNumber")
-			fmt.Fprintf(w, "  %d %s\t%d\t%d\t%d\t%d\t%s\t%s\t%d\n", bc.ServiceClassCode, serviceClassCodes[bh.ServiceClassCode], bc.EntryAddendaCount, bc.EntryHash, bc.TotalDebitEntryDollarAmount, bc.TotalCreditEntryDollarAmount, bc.MessageAuthenticationCode, bc.ODFIIdentification, bc.BatchNumber)
-		}
-	}
-
 	// FileControl
 	fmt.Fprintln(w, "\n  BatchCount\tBlockCount\tEntryAddendaCount\tTotalDebitAmount\tTotalCreditAmount")
 	fmt.Fprintf(w, "  %d\t%d\t%d\t%d\t%d\n", fc.BatchCount, fc.BlockCount, fc.EntryAddendaCount, fc.TotalDebitEntryDollarAmountInFile, fc.TotalCreditEntryDollarAmountInFile)
@@ -230,87 +162,6 @@ func dumpAddenda99(w *tabwriter.Writer, a *ach.Addenda99) {
 	fmt.Fprintf(w, "      %s\t%s\t%s\t%s\t%s\t%s\n", a.ReturnCode, a.OriginalTrace, a.DateOfDeath, a.OriginalDFI, a.AddendaInformation, a.TraceNumber)
 }
 
-func dumpAddenda10(w *tabwriter.Writer, a *ach.Addenda10) {
-	if a == nil {
-		return
-	}
-
-	fmt.Fprintln(w, "      TypeCode\tTransactionTypeCode\tForeignPaymentAmount\tForeignTraceNumber\tName\tEntryDetailSequenceNumber")
-	fmt.Fprintf(w, "      %s\t%s\t%d\t%s\t%s\t%d\n", a.TypeCode, a.TransactionTypeCode, a.ForeignPaymentAmount, a.ForeignTraceNumber, a.Name, a.EntryDetailSequenceNumber)
-}
-
-func dumpAddenda11(w *tabwriter.Writer, a *ach.Addenda11) {
-	if a == nil {
-		return
-	}
-
-	fmt.Fprintln(w, "      TypeCode\tOriginatorName\tOriginatorStreetAddress\tEntryDetailSequenceNumber")
-	fmt.Fprintf(w, "      %s\t%s\t%s\t%d\n", a.TypeCode, a.OriginatorName, a.OriginatorStreetAddress, a.EntryDetailSequenceNumber)
-}
-
-func dumpAddenda12(w *tabwriter.Writer, a *ach.Addenda12) {
-	if a == nil {
-		return
-	}
-
-	fmt.Fprintln(w, "      TypeCode\tOriginatorCityStateProvince\tOriginatorCountryPostalCode\tEntryDetailSequenceNumber")
-	fmt.Fprintf(w, "      %s\t%s\t%s\t%d\n", a.TypeCode, a.OriginatorCityStateProvince, a.OriginatorCountryPostalCode, a.EntryDetailSequenceNumber)
-}
-
-func dumpAddenda13(w *tabwriter.Writer, a *ach.Addenda13) {
-	if a == nil {
-		return
-	}
-
-	fmt.Fprintln(w, "      TypeCode\tODFIName\tODFIIDNumberQualifier\tODFIIdentification\tODFIBranchCountryCode\tEntryDetailSequenceNumber")
-	fmt.Fprintf(w, "      %s\t%s\t%s\t%s\t%s\t%d\n", a.TypeCode, a.ODFIName, a.ODFIIDNumberQualifier, a.ODFIIdentification, a.ODFIBranchCountryCode, a.EntryDetailSequenceNumber)
-}
-
-func dumpAddenda14(w *tabwriter.Writer, a *ach.Addenda14) {
-	if a == nil {
-		return
-	}
-
-	fmt.Fprintln(w, "      TypeCode\tRDFIName\tRDFIIDNumberQualifier\tRDFIIdentification\tRDFIBranchCountryCode\tEntryDetailSequenceNumber")
-	fmt.Fprintf(w, "      %s\t%s\t%s\t%s\t%s\t%d\n", a.TypeCode, a.RDFIName, a.RDFIIDNumberQualifier, a.RDFIIdentification, a.RDFIBranchCountryCode, a.EntryDetailSequenceNumber)
-}
-
-func dumpAddenda15(w *tabwriter.Writer, a *ach.Addenda15) {
-	if a == nil {
-		return
-	}
-
-	fmt.Fprintln(w, "      TypeCode\tReceiverIDNumber\tReceiverStreetAddress\tEntryDetailSequenceNumber")
-	fmt.Fprintf(w, "      %s\t%s\t%s\t%d\n", a.TypeCode, a.ReceiverIDNumber, a.ReceiverStreetAddress, a.EntryDetailSequenceNumber)
-}
-
-func dumpAddenda16(w *tabwriter.Writer, a *ach.Addenda16) {
-	if a == nil {
-		return
-	}
-
-	fmt.Fprintln(w, "      TypeCode\tReceiverCityStateProvince\tReceiverCountryPostalCode\tEntryDetailSequenceNumber")
-	fmt.Fprintf(w, "      %s\t%s\t%s\t%d\n", a.TypeCode, a.ReceiverCityStateProvince, a.ReceiverCountryPostalCode, a.EntryDetailSequenceNumber)
-}
-
-func dumpAddenda17(w *tabwriter.Writer, a *ach.Addenda17) {
-	if a == nil {
-		return
-	}
-
-	fmt.Fprintln(w, "      TypeCode\tPaymentRelatedInformation\tSequenceNumber\tEntryDetailSequenceNumber")
-	fmt.Fprintf(w, "      %s\t%s\t%d\t%d\n", a.TypeCode, a.PaymentRelatedInformation, a.SequenceNumber, a.EntryDetailSequenceNumber)
-}
-
-func dumpAddenda18(w *tabwriter.Writer, a *ach.Addenda18) {
-	if a == nil {
-		return
-	}
-
-	fmt.Fprintln(w, "      TypeCode\tForeignCorrespondentBankName\tForeignCorrespondentBankIDNumberQualifier\tForeignCorrespondentBankIDNumber\tForeignCorrespondentBankBranchCountryCode\tSequenceNumber\tEntryDetailSequenceNumber")
-	fmt.Fprintf(w, "      %s\t%s\t%s\t%s\t%s\t%d\t%d\n", a.TypeCode, a.ForeignCorrespondentBankName, a.ForeignCorrespondentBankIDNumberQualifier, a.ForeignCorrespondentBankIDNumber, a.ForeignCorrespondentBankBranchCountryCode, a.SequenceNumber, a.EntryDetailSequenceNumber)
-}
-
 func maskAccountNumber(s string) string {
 	length := utf8.RuneCountInString(s)
 	if length < 5 {
diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock
index b51ccfbb..d089d50f 100644
--- a/docs/Gemfile.lock
+++ b/docs/Gemfile.lock
@@ -222,11 +222,11 @@ GEM
       jekyll-seo-tag (~> 2.1)
     minitest (5.14.4)
     multipart-post (2.1.1)
-    nokogiri (1.13.9-arm64-darwin)
+    nokogiri (1.13.6-arm64-darwin)
       racc (~> 1.4)
-    nokogiri (1.13.9-x86_64-darwin)
+    nokogiri (1.13.6-x86_64-darwin)
       racc (~> 1.4)
-    nokogiri (1.13.9-x86_64-linux)
+    nokogiri (1.13.6-x86_64-linux)
       racc (~> 1.4)
     octokit (4.21.0)
       faraday (>= 0.9)
diff --git a/docs/_data/docs-menu.yml b/docs/_data/docs-menu.yml
index d5cd6b1f..96508502 100644
--- a/docs/_data/docs-menu.yml
+++ b/docs/_data/docs-menu.yml
@@ -27,15 +27,6 @@
     - name: SEC codes table
       link: /sec-codes-table/
 
-- label: Examples
-  items:
-    - name: All SEC Codes
-      link: https://github.com/moov-io/ach/blob/master/examples/
-    - name: Create file via HTTP
-      link: https://github.com/moov-io/ach/blob/master/examples/http/main.go
-    - name: Micro Entries
-      link: https://github.com/moov-io/ach/blob/master/examples/micro-entries/main.go
-
 - label: File operations
   items:
     - name: Custom validation
diff --git a/docs/_data/navigation.yml b/docs/_data/navigation.yml
index 5a5148fa..d7810c36 100644
--- a/docs/_data/navigation.yml
+++ b/docs/_data/navigation.yml
@@ -14,19 +14,13 @@
       link: https://github.com/moov-io/terms-dictionary
 - name: Other Projects
   dropdown:
-    - name: Moov ACHGateway
-      link: https://moov-io.github.io/achgateway/
-    - name: Moov ACH Test Harness
-      link: https://github.com/moov-io/ach-test-harness
-    - name: Moov Fed
-      link: https://moov-io.github.io/fed/
-    - name: Moov FinCEN
-      link: https://moov-io.github.io/fincen/
     - name: Moov Image Cash Letter
       link: https://moov-io.github.io/imagecashletter/
+    - name: Moov Wire
+      link: https://moov-io.github.io/wire/
     - name: Moov Metro 2
       link: https://moov-io.github.io/metro2/
+    - name: Moov Fed
+      link: https://moov-io.github.io/fed/
     - name: Moov Watchman
       link: https://moov-io.github.io/watchman/
-    - name: Moov Wire
-      link: https://moov-io.github.io/wire/
diff --git a/docs/changes.md b/docs/changes.md
index 2a4df8d4..3c5ffcd0 100644
--- a/docs/changes.md
+++ b/docs/changes.md
@@ -58,9 +58,9 @@ addenda98.TraceNumber = "91012980000088"
 | `C02` | Incorrect transit/routing number | Once valid transit/routing number must be changed |
 | `C03` | Incorrect transit/routing number and bank account number | Once valid transit/routing number must be changed and causes a change to bank account number structure |
 | `C04` | Bank account name change | Customer has changed name or ODFI submitted name incorrectly |
-| `C05` | Incorrect transaction code | Entry posted to demand account should contain savings transaction codes or vice versa |
-| `C06` | Incorrect bank account number and transit code | Bank account number must be changed and transaction code should indicate posting to another account type (demand/savings) |
-| `C07` | Incorrect transit/routing number, bank account number and transaction code | Changes required in three fields indicated |
+| `C05` | Incorrect payment code | Entry posted to demand account should contain savings payment codes or vice versa |
+| `C06` | Incorrect bank account number and transit code | Bank account number must be changed and payment code should indicate posting to another account type (demand/savings) |
+| `C07` | Incorrect transit/routing number, bank account number and payment code | Changes required in three fields indicated |
 | `C09` | Incorrect individual ID number | Individual's ID number is incorrect |
 | `C10` | Incorrect company name | Company name is no longer valid and should be changed |
 | `C11` | Incorrect company identification | Company ID is no longer valid and should be changed |
diff --git a/examples/micro-entries/main.go b/examples/micro-entries/main.go
deleted file mode 100644
index dd100ec4..00000000
--- a/examples/micro-entries/main.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// Package main shows an example of creating micro-deposits to validate an account.
-package main
-
-import (
-	"log"
-	"os"
-	"time"
-
-	"github.com/moov-io/ach"
-	"github.com/moov-io/ach/cmd/achcli/describe"
-)
-
-func main() {
-	est, _ := time.LoadLocation("America/New_York")
-	now := time.Now().In(est)
-
-	fh := ach.NewFileHeader()
-	fh.ImmediateDestination = "231380104" // Routing Number of the ACH Operator or receiving point to which the file is being sent
-	fh.ImmediateOrigin = "121042882"      // Routing Number of the ACH Operator or sending point that is sending the file
-	fh.FileCreationDate = now.Format("060102")
-	fh.ImmediateDestinationName = "Receiver Bank Name"
-	fh.ImmediateOriginName = "Origin Bank Name"
-
-	bh := ach.NewBatchHeader()
-	bh.ServiceClassCode = ach.MixedDebitsAndCredits
-	bh.CompanyName = "Name on Account" // The name of the company/person that has relationship with receiver
-	bh.CompanyIdentification = fh.ImmediateOrigin
-	bh.StandardEntryClassCode = ach.WEB                           // Or CCD
-	bh.CompanyEntryDescription = "AcctVerify"                     // will be on receiving accounts statement
-	bh.EffectiveEntryDate = now.AddDate(0, 0, 1).Format("060102") // YYMMDD
-	bh.ODFIIdentification = "121042882"                           // Originating Routing Number
-
-	credit1 := ach.NewEntryDetail()
-	credit1.TransactionCode = ach.CheckingCredit
-	credit1.SetRDFI("231380104")          // Receivers bank transit routing number
-	credit1.DFIAccountNumber = "12345678" // Receivers bank account number
-	credit1.Amount = 7                    // $0.07
-	credit1.SetTraceNumber(bh.ODFIIdentification, 1)
-	credit1.IndividualName = "Receiver Account Name" // Identifies the receiver of the transaction
-
-	credit2 := ach.NewEntryDetail()
-	credit2.TransactionCode = ach.CheckingCredit
-	credit2.SetRDFI("231380104")
-	credit2.DFIAccountNumber = "12345678"
-	credit2.Amount = 22 // $0.22
-	credit2.SetTraceNumber(bh.ODFIIdentification, 2)
-	credit2.IndividualName = "Receiver Account Name"
-
-	// Talk with your ODFI or partner bank if you need to debit your account (to fund the outgoing credits).
-
-	// Debit the account you're validating (optional)
-	debit := ach.NewEntryDetail()
-	debit.TransactionCode = ach.CheckingDebit
-	debit.SetRDFI("231380104")          // Receivers bank transit routing number
-	debit.DFIAccountNumber = "12345678" // Receivers bank account number
-	debit.Amount = credit1.Amount + credit2.Amount
-	debit.SetTraceNumber(bh.ODFIIdentification, 3)
-	debit.IndividualName = "Receiver Account Name" // Identifies the receiver of the transaction
-
-	batch := ach.NewBatchWEB(bh)
-	batch.AddEntry(credit1)
-	batch.AddEntry(credit2)
-	batch.AddEntry(debit)
-	err := batch.Create()
-	if err != nil {
-		log.Fatalf("ERROR building WEB batch: %v", err)
-	}
-
-	file := ach.NewFile()
-	file.SetHeader(fh)
-	file.AddBatch(batch)
-	err = file.Create()
-	if err != nil {
-		log.Fatalf("ERROR building file: %v", err)
-	}
-
-	describe.File(os.Stdout, file, nil)
-}
diff --git a/file.go b/file.go
index a03a6f17..e28646d8 100644
--- a/file.go
+++ b/file.go
@@ -929,11 +929,6 @@ func (f *File) SegmentFile(_ *SegmentFileConfiguration) (*File, *File, error) {
 	creditFile := NewFile()
 	debitFile := NewFile()
 
-	if f.validateOpts != nil {
-		creditFile.SetValidation(f.validateOpts)
-		debitFile.SetValidation(f.validateOpts)
-	}
-
 	if f.Batches != nil {
 		f.segmentFileBatches(creditFile, debitFile)
 	}
diff --git a/fileHeader.go b/fileHeader.go
index 5949007b..12649398 100644
--- a/fileHeader.go
+++ b/fileHeader.go
@@ -258,6 +258,10 @@ func (fh *FileHeader) ValidateWith(opts *ValidateOpts) error {
 // fieldInclusion validate mandatory fields are not default values. If fields are
 // invalid the ACH transfer will be returned.
 func (fh *FileHeader) fieldInclusion() error {
+	if fh.validateOpts != nil && fh.validateOpts.AllowMissingFileHeader {
+		return nil
+	}
+
 	if fh.ImmediateDestination == "" {
 		return fieldError("ImmediateDestination", ErrConstructor, fh.ImmediateDestinationField())
 	}
diff --git a/go.mod b/go.mod
index 2adf7cce..ce1c0dbc 100644
--- a/go.mod
+++ b/go.mod
@@ -10,10 +10,10 @@ require (
 	github.com/gorilla/mux v1.8.0
 	github.com/juju/ansiterm v0.0.0-20210929141451-8b71cc96ebdc
 	github.com/mattn/go-isatty v0.0.16
-	github.com/moov-io/base v0.36.0
+	github.com/moov-io/base v0.35.0
 	github.com/prometheus/client_golang v1.13.0
 	github.com/stretchr/testify v1.8.0
-	golang.org/x/oauth2 v0.1.0
+	golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1
 )
 
 require (
@@ -29,9 +29,9 @@ require (
 	github.com/prometheus/client_model v0.2.0 // indirect
 	github.com/prometheus/common v0.37.0 // indirect
 	github.com/prometheus/procfs v0.8.0 // indirect
-	github.com/rickar/cal/v2 v2.1.7 // indirect
-	golang.org/x/net v0.1.0 // indirect
-	golang.org/x/sys v0.1.0 // indirect
+	github.com/rickar/cal/v2 v2.1.6 // indirect
+	golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
+	golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
 	google.golang.org/appengine v1.6.7 // indirect
 	google.golang.org/protobuf v1.28.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/go.sum b/go.sum
index d33b17cd..c860c636 100644
--- a/go.sum
+++ b/go.sum
@@ -183,8 +183,6 @@ github.com/moov-io/base v0.34.1 h1:oOVHb+sFS0wj4aWBAr/LWdbgRIYI1RmP7XiclIWyREA=
 github.com/moov-io/base v0.34.1/go.mod h1:SKRDVIgWvK4lEPenNK4saVVg87z/L8wMDqrWnsdnkjg=
 github.com/moov-io/base v0.35.0 h1:24jZwU5WekI/Ukl9zirV06KwZ06SdAExLZGfDJXTdqs=
 github.com/moov-io/base v0.35.0/go.mod h1:Ej6hMuk94opovep+wCuYVFm7zXqS9T63EufmKB6wXTE=
-github.com/moov-io/base v0.36.0 h1:zfVHZAD8OC3B8E05M4TtXfvpnUsXIEw576/4xK33qpc=
-github.com/moov-io/base v0.36.0/go.mod h1:PWG3wJEs9hszZWn+1oZmXxnMIKzy85+qFETYbWBCK+k=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -219,8 +217,6 @@ github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5
 github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
 github.com/rickar/cal/v2 v2.1.6 h1:a3U6CmALL25lBT6KBosITXCnekcNzcQGqGnpRgsVldc=
 github.com/rickar/cal/v2 v2.1.6/go.mod h1:/fdlMcx7GjPlIBibMzOM9gMvDBsrK+mOtRXdTzUqV/A=
-github.com/rickar/cal/v2 v2.1.7 h1:2ePuyYfK828ei33IEOuqwDUVsup2wLNxwV+vQdfzrq8=
-github.com/rickar/cal/v2 v2.1.7/go.mod h1:/fdlMcx7GjPlIBibMzOM9gMvDBsrK+mOtRXdTzUqV/A=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@@ -311,7 +307,6 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
 golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -322,9 +317,6 @@ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j
 golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8=
 golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
 golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
-golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
-golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
-golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -377,8 +369,6 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
-golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/server/files.go b/server/files.go
index 488d3eb4..657c7f04 100644
--- a/server/files.go
+++ b/server/files.go
@@ -773,10 +773,9 @@ func decodeSegmentFileRequest(_ context.Context, r *http.Request) (interface{},
 	}
 
 	return segmentFileRequest{
-		File:         wrapper.File,
-		requestID:    moovhttp.GetRequestID(r),
-		opts:         wrapper.Opts,
-		validateOpts: wrapper.ValidateOpts,
+		File:      wrapper.File,
+		requestID: moovhttp.GetRequestID(r),
+		opts:      wrapper.Opts,
 	}, nil
 }
 
diff --git a/version.go b/version.go
index fab9ed89..7e630f8d 100644
--- a/version.go
+++ b/version.go
@@ -18,4 +18,4 @@
 package ach
 
 // Version Number
-const Version = "v1.21.2"
+const Version = "v1.21.0"


Test patch:
diff --git a/addenda98_test.go b/addenda98_test.go
index 9055edea..2f13fb84 100644
--- a/addenda98_test.go
+++ b/addenda98_test.go
@@ -44,7 +44,7 @@ func TestAddenda98_LookupChangecode(t *testing.T) {
 		if code.Code != "C05" {
 			t.Errorf("code.Code=%s", code.Code)
 		}
-		if code.Reason != "Incorrect transaction code" {
+		if code.Reason != "Incorrect payment code" {
 			t.Errorf("code.Reason=%s", code.Reason)
 		}
 	}
diff --git a/fileHeader_test.go b/fileHeader_test.go
index 31e61faf..8d4d24a6 100644
--- a/fileHeader_test.go
+++ b/fileHeader_test.go
@@ -18,6 +18,7 @@
 package ach
 
 import (
+	"github.com/stretchr/testify/require"
 	"strings"
 	"testing"
 	"time"
@@ -98,6 +99,16 @@ func TestFileHeader__ImmediateOrigin(t *testing.T) {
 	}
 }
 
+func TestFileHeader__ByPassingFileHeaderValidation(t *testing.T) {
+	header := NewFileHeader()
+	header.ImmediateDestination = ""
+	header.ImmediateOrigin = ""
+	header.SetValidation(&ValidateOpts{AllowMissingFileHeader: true})
+
+	err := header.fieldInclusion()
+	require.NoError(t, err)
+}
+
 func TestFileHeader__ImmediateDestination(t *testing.T) {
 	tests := []struct {
 		name                        string
diff --git a/server/files_test.go b/server/files_test.go
index e76442a8..edf6bc1c 100644
--- a/server/files_test.go
+++ b/server/files_test.go
@@ -793,8 +793,7 @@ func TestFiles__segmentFileEndpointValidateOpts(t *testing.T) {
 	router := MakeHTTPHandler(svc, repo, kitlog.NewNopLogger())
 
 	opts := &ach.ValidateOpts{
-		AllowUnorderedBatchNumbers: true,
-		CustomTraceNumbers:         true,
+		BypassOriginValidation: true,
 	}
 
 	bs, err := os.ReadFile(filepath.Join("..", "test", "testdata", "ppd-mixedDebitCredit-invalid.json"))
@@ -823,15 +822,12 @@ func TestFiles__segmentFileEndpointValidateOpts(t *testing.T) {
 	w.Flush()
 
 	var resp segmentedFilesResponse
-	require.NoError(t, json.NewDecoder(w.Body).Decode(&resp))
+	json.NewDecoder(w.Body).Decode(&resp)
 
 	require.NotEmpty(t, resp.CreditFileID)
 	require.NotNil(t, resp.CreditFile)
-	require.NotNil(t, resp.CreditFile.GetValidation())
-
 	require.NotEmpty(t, resp.DebitFileID)
 	require.NotNil(t, resp.DebitFile)
-	require.NotNil(t, resp.DebitFile.GetValidation())
 }
 
 // TestFiles__decodeSegmentFileIDRequest tests segmentFileIDEndpoints