// Licensed to The Moov Authors under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. The Moov Authors licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package ach import ( "fmt" "strings" "time" ) // BatchDNE is a batch file that handles SEC code Death Notification Entry (DNE) // United States Federal agencies (e.g. Social Security) use this to notify depository // financial institutions that the recipient of government benefit payments has died. // // Notes: // - Date of death always in positions 18-23 // - SSN (positions 38-46) are zero if no SSN // - Beneficiary payment starts at position 55 type BatchDNE struct { Batch } // NewBatchDNE returns a *BatchDNE func NewBatchDNE(bh *BatchHeader) *BatchDNE { batch := new(BatchDNE) batch.SetControl(NewBatchControl()) batch.SetHeader(bh) batch.SetID(bh.ID) return batch } // Validate ensures the batch meets NACHA rules specific to this batch type. func (batch *BatchDNE) Validate() error { if err := batch.verify(); err != nil { return err } // SEC code if batch.Header.StandardEntryClassCode != DNE { return batch.Error("StandardEntryClassCode", ErrBatchSECType, DNE) } // Range over Entries for _, entry := range batch.Entries { if entry.Amount != 0 { return batch.Error("Amount", ErrBatchAmountNonZero, entry.Amount) } switch entry.TransactionCode { case CheckingPrenoteCredit, SavingsPrenoteCredit: default: return batch.Error("TransactionCode", ErrBatchTransactionCode, entry.TransactionCode) } // DNE must have one Addenda05 if len(entry.Addenda05) != 1 { return batch.Error("AddendaCount", NewErrBatchAddendaCount(len(entry.Addenda05), 1)) } // Verify the Amount is valid for SEC code and TransactionCode if err := batch.ValidAmountForCodes(entry); err != nil { return err } // Verify the TransactionCode is valid for a ServiceClassCode if err := batch.ValidTranCodeForServiceClassCode(entry); err != nil { return err } // Verify Addenda* FieldInclusion based on entry.Category and batchHeader.StandardEntryClassCode if err := batch.addendaFieldInclusion(entry); err != nil { return err } } return nil } // Create will tabulate and assemble an ACH batch into a valid state. This includes // setting any posting dates, sequence numbers, counts, and sums. // // Create implementations are free to modify computable fields in a file and should // call the Batch's Validate function at the end of their execution. func (batch *BatchDNE) Create() error { // generates sequence numbers and batch control if err := batch.build(); err != nil { return err } return batch.Validate() } type DNEPaymentInformation struct { DateOfDeath time.Time CustomerSSN string // Amount is a two-decimal float value formatted as a string // Example: 123.45 Amount string } // ParseDNEPaymentInformation returns an DNEPaymentInformation for a given Addenda05 record. The information is parsed from the addenda's // PaymentRelatedInformation field. // // The returned information is not validated for correctness. func ParseDNEPaymentInformation(addenda05 *Addenda05) (*DNEPaymentInformation, error) { if addenda05 == nil { return nil, nil } fields := strings.Split(strings.TrimSuffix(addenda05.PaymentRelatedInformation, `\`), "*") if len(fields) != 6 { return nil, fmt.Errorf("unexpected %d fields", len(fields)) } dateOfDeath, err := time.Parse("010206", fields[1]) if err != nil { return nil, fmt.Errorf("parsing DateOfDeath: %w", err) } return &DNEPaymentInformation{ DateOfDeath: dateOfDeath, CustomerSSN: fields[3], Amount: fields[5], }, nil } func (info DNEPaymentInformation) String() string { return fmt.Sprintf(`DATE OF DEATH*%s*CUSTOMER SSN*%s*AMOUNT*%s\`, info.DateOfDeath.Format("010206"), info.CustomerSSN, info.Amount) }