Skip to content
Open
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
23 changes: 21 additions & 2 deletions batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -1201,12 +1201,15 @@ func (b *Batch) upsertOffsets() error {
}

// remove any Offset records already on the batch
var found *EntryDetail
for i := 0; i < len(b.Entries); i++ {
// TODO(adam): Should we remove this based on checking the last element is
// debit/credit and sums to all the other elements (which are mutually exclusive to
// the last record being debit or credit)?
// See: https://github.com/moov-io/ach/issues/540
if strings.EqualFold(b.Entries[i].IndividualName, offsetIndividualName) {
found = b.Entries[i] // save the EntryDetail to copy addenda records back onto

// fixup BatchControl records for our conditional after this for loop
if b.Entries[i].TransactionCode == CheckingCredit || b.Entries[i].TransactionCode == SavingsCredit {
b.Control.TotalCreditEntryDollarAmount -= b.Entries[i].Amount
Expand Down Expand Up @@ -1259,13 +1262,29 @@ func (b *Batch) upsertOffsets() error {

// Add both EntryDetails to our Batch and recalculate some fields
if debitED != nil {
// Copy any addenda records from a previous OFFSET EntryDetail
if found != nil {
if len(found.Addenda05) > 0 {
debitED.AddendaRecordIndicator = 1
debitED.Addenda05 = append(debitED.Addenda05, found.Addenda05...)
}
}

b.AddEntry(debitED)
b.Control.EntryAddendaCount += 1
b.Control.EntryAddendaCount += 1 + debitED.addendaCount() - found.addendaCount()
b.Control.TotalDebitEntryDollarAmount += debitED.Amount
}
if creditED != nil {
// Copy any addenda records from a previous OFFSET EntryDetail
if found != nil {
if len(found.Addenda05) > 0 {
creditED.AddendaRecordIndicator = 1
creditED.Addenda05 = append(creditED.Addenda05, found.Addenda05...)
}
}

b.AddEntry(creditED)
b.Control.EntryAddendaCount += 1
b.Control.EntryAddendaCount += 1 + creditED.addendaCount() - found.addendaCount()
b.Control.TotalCreditEntryDollarAmount += creditED.Amount
}
b.Header.ServiceClassCode = MixedDebitsAndCredits
Expand Down
4 changes: 4 additions & 0 deletions entryDetail.go
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,10 @@ func (ed *EntryDetail) AddAddenda05(addenda05 *Addenda05) {

// addendaCount returns the count of Addenda records added onto this EntryDetail
func (ed *EntryDetail) addendaCount() (n int) {
if ed == nil {
return 0
}

if ed.Addenda02 != nil {
n += 1
}
Expand Down
36 changes: 36 additions & 0 deletions test/issues/issue1656_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package issues

import (
"os"
"path/filepath"
"testing"

"github.com/moov-io/ach"
"github.com/moov-io/ach/cmd/achcli/describe"

"github.com/stretchr/testify/require"
)

func TestIssue1656(t *testing.T) {
file, err := ach.ReadJSONFile(filepath.Join("testdata", "issue1656.json"))
require.NoError(t, err)

if testing.Verbose() {
describe.File(os.Stdout, file, nil)
}

require.Len(t, file.Batches, 1)

entries := file.Batches[0].GetEntries()
require.Len(t, entries, 2)

// First Entry
require.Equal(t, "Jane Smith", entries[0].IndividualName)
require.Len(t, entries[0].Addenda05, 1)
require.Equal(t, "abc", entries[0].Addenda05[0].PaymentRelatedInformation)

// Second Entry
require.Equal(t, "OFFSET", entries[1].IndividualName)
require.Len(t, entries[1].Addenda05, 1)
require.Equal(t, "def", entries[1].Addenda05[0].PaymentRelatedInformation)
}
74 changes: 74 additions & 0 deletions test/issues/testdata/issue1656.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"fileHeader": {
"immediateDestination": "123456780",
"immediateOrigin": "123456780",
"fileCreationDate": "231219",
"fileCreationTime": "",
"fileIDModifier": "A",
"FormatCode": "1",
"immediateDestinationName": "Anonymous Bank",
"immediateOriginName": "Anonymous Bank"
},
"batches": [
{
"batchHeader": {
"serviceClassCode": 200,
"companyName": "Anonymous Bank",
"companyIdentification": "123456780",
"standardEntryClassCode": "CCD",
"companyEntryDescription": "Anonymous Bank",
"originatorStatusCode": 1,
"ODFIIdentification": "123456780",
"batchNumber": 1
},
"entryDetails": [
{
"transactionCode": 22,
"RDFIIdentification": "12345678",
"checkDigit": "0",
"DFIAccountNumber": "12345",
"amount": 50000,
"individualName": "Jane Smith",
"traceNumber": "123456780000001",
"addendaRecordIndicator": 1,
"addenda05": [
{
"typeCode": "05",
"paymentRelatedInformation": "abc",
"sequenceNumber": 1,
"entryDetailSequenceNumber": 10000
}
]
},
{
"transactionCode": 27,
"RDFIIdentification": "12345678",
"checkDigit": "0",
"DFIAccountNumber": "12345",
"amount": 50000,
"individualName": "OFFSET",
"discretionaryData": "For offset",
"traceNumber": "123456780000002",
"addendaRecordIndicator": 1,
"addenda05": [
{
"typeCode": "05",
"paymentRelatedInformation": "def",
"sequenceNumber": 1,
"entryDetailSequenceNumber": 10000
}
]
}
],
"offset": {
"routingNumber": "123456780",
"accountNumber": "12345",
"accountType": "checking",
"description": "For offset"
}
}
],
"IATBatches": null,
"NotificationOfChange": null,
"ReturnEntries": null
}
Loading