Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allow credits in SHR batches #1535

Merged
merged 1 commit into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions batchSHR.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,21 @@ func (batch *BatchSHR) Validate() error {
return batch.Error("StandardEntryClassCode", ErrBatchSECType, SHR)
}

// SHR detail entries can only be a debit, ServiceClassCode must allow debits
// SHR entries can be debit, credit or mixed ServiceClassCode
switch batch.Header.ServiceClassCode {
case MixedDebitsAndCredits, CreditsOnly:
case MixedDebitsAndCredits, CreditsOnly, DebitsOnly:
// do nothing
default:
return batch.Error("ServiceClassCode", ErrBatchServiceClassCode, batch.Header.ServiceClassCode)
}

for _, entry := range batch.Entries {
// SHR detail entries must be a debit
if entry.CreditOrDebit() != "D" {
return batch.Error("TransactionCode", ErrBatchDebitOnly, entry.TransactionCode)
// SHR detail entries can be debit or credit
switch entry.CreditOrDebit() {
case "C", "D":
// do nothing
default:
return batch.Error("TransactionCode", ErrBatchTransactionCode, entry.TransactionCode)
}
if err := entry.isCardTransactionType(entry.DiscretionaryData); err != nil {
return batch.Error("CardTransactionType", ErrBatchInvalidCardTransactionType, entry.DiscretionaryData)
Expand Down
33 changes: 26 additions & 7 deletions batchSHR_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package ach

import (
"path/filepath"
"testing"

"github.com/moov-io/base"
Expand Down Expand Up @@ -220,22 +221,20 @@ func BenchmarkBatchSHRAutomatedAccountingAdvices(b *testing.B) {
}
}

// testBatchSHRTransactionCode validates BatchSHR TransactionCode is not a credit
// testBatchSHRTransactionCode validates BatchSHR TransactionCode is credit or debit
func testBatchSHRTransactionCode(t testing.TB) {
mockBatch := mockBatchSHR(t)
mockBatch.GetEntries()[0].TransactionCode = CheckingCredit
mockBatch.GetEntries()[0].TransactionCode = AutomatedAccountingAdvices
err := mockBatch.Create()
if !base.Match(err, ErrBatchDebitOnly) {
t.Errorf("%T: %s", err, err)
}
require.ErrorContains(t, err, "batch #1 (SHR) FieldError TransactionCode 280 is an invalid Transaction Code")
}

// TestBatchSHRTransactionCode tests validating BatchSHR TransactionCode is not a credit
// TestBatchSHRTransactionCode tests validating BatchSHR TransactionCode is a credit or debit
func TestBatchSHRTransactionCode(t *testing.T) {
testBatchSHRTransactionCode(t)
}

// BenchmarkBatchSHRTransactionCode benchmarks validating BatchSHR TransactionCode is not a credit
// BenchmarkBatchSHRTransactionCode benchmarks validating BatchSHR TransactionCode is a credit or debit
func BenchmarkBatchSHRTransactionCode(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
Expand Down Expand Up @@ -568,3 +567,23 @@ func TestBatchSHRTerminalState(t *testing.T) {
t.Errorf("%T: %s", err, err)
}
}

func TestBatchSHR_AcceptCredits(t *testing.T) {
file, err := ReadFile(filepath.Join("test", "testdata", "shr-credit.ach"))
require.ErrorContains(t, err, "*ach.FieldError CardExpirationDate 00 is an invalid month")
require.Len(t, file.Batches, 1)

b := file.Batches[0]
require.ErrorContains(t, b.Create(), "CardExpirationDate 00 is an invalid month")
require.ErrorContains(t, b.Validate(), "CardExpirationDate 00 is an invalid month")

bh := b.GetHeader()
require.Equal(t, SHR, bh.StandardEntryClassCode)
require.Equal(t, MixedDebitsAndCredits, bh.ServiceClassCode)

entries := b.GetEntries()
require.Len(t, entries, 14)

require.NoError(t, file.Create())
require.ErrorContains(t, err, "*ach.FieldError CardExpirationDate 00 is an invalid month")
}
32 changes: 32 additions & 0 deletions test/testdata/shr-credit.ach
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
101 041205259 6910001341311201446C094101Silverlake Bank & TrustASF APPLICATION SUPERVI
5200FDR DEBIT CARD 1470535472SHRVISA DEBIT1308221308220001111010170000033
62204120525983133 0000002689000074692168200Adams Electric 131111010170008354
702MVN3BV5969 000000915 HSN*HSN 265742136 800-284-3900 FL111010170008354
62204120525983133 000000389800007441295822BAdams Electric 131111010170008433
702E40VTL5533 000000914 O'REILLY #620 SAN ANTONIO TX111010170008433
62204120525983133 000000059400007449280823DAdams Electric 131111010170008552
702WMMKVM5999 00000091500000NBEAUTYFIRST #641002 SAN ANTONIO TX111010170008552
62204120525983133 000001512400007444500826ZAdams Electric 131111010170008581
7027JLPL15311 000000914 NORDSTROM DIRECT #0808 CEDAR RAPIDS IA111010170008581
62204120525983133 00000054110000743079282S6Adams Electric 131111010170008597
7026KQ8J05712 00000091400000NMATTFIRM GA 7999 HOUSTON TX111010170008597
62204120525983133 000000250000007416407820HAdams Electric 131111010170008699
7022E71KR3405 000000915 ENTERPRISE RENT-A-CAR SAN ANTONIO TX111010170008699
62204120525983133 000001338700007444500836ZAdams Electric 131111010170008785
702RX5G6D5310 000000915 STEIN-MART #0024 SAN ANTONIO TX111010170008785
62204120525983133 000000025900007438894826NAdams Electric 131111010170008883
702AVGY2W5251 000000915000000SUNSET RIDGE HOME & HD SAN ANTONIO TX111010170008883
62204120525983133 0000004999000074610438203Adams Electric 131111010170008915
702RFNPP94899 000000914 ONE TIME/DISH NETWORK 800-333-3474 CO111010170008915
62204120525983133 000001251300007416407820HAdams Electric 131111010170008963
7022E746G3405 000000915 ENTERPRISE RENT-A-CAR SAN ANTONIO TX111010170008963
62204120525983133 000000185400007449280823DAdams Electric 131111010170009170
702WMPNH47394 00000091400000NNATIONSRENT #316-L SAN ANTONIO TX111010170009170
62204120525983133 000000172700007416407822LAdams Electric 131111010170009254
702RQAP6G5310 000000915 TARGET 00010678 SAN ANTONIO TX111010170009254
62204120525983133 0000002699000074455018115Adams Electric 131111010170009269
70286H8HX5399 000000914045884TUESDAY MORNING #0340 SAN ANTONIO TX111010170009269
62204120525983133 000000106200007413829829HAdams Electric 131111010170009420
702D4HJ0B5211 000000914 LOWE'S #1088 SAN ANTONIO TX111010170009420
820000002800576873500000000000000000000687161470535472 111010170000033
9000665001188000105450247745675000256439381000842046309
Loading