diff --git a/README.rst b/README.rst index c1f0a80..133ef5e 100644 --- a/README.rst +++ b/README.rst @@ -72,3 +72,65 @@ This returns the following NACHA file: 9000001000001000000040037014587000000015000000000002213 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 + + +Below is another example of what happens if the validation fails for one entry. + +.. code:: python + + + from ach.builder import AchFile + + settings = { + 'immediate_dest' : '123456789', # Your bank's routing number + 'immediate_org' : '123456789', # Bank assigned routing number + 'immediate_dest_name' : 'YOUR BANK', + 'immediate_org_name' : 'YOUR COMPANY', + 'company_id' : '1234567890', #tax number + } + + ach_file = AchFile('B',settings) #file Id mod + + entries = [ + { + 'type' : '27', + 'routing_number' : '********', # invalid + 'account_number' : '********', # invalid + 'amount' : '150.00', + 'name' : 'Billy Holiday', + }, + { + 'type' : '22', + 'routing_number' : '123232318', + 'account_number' : '123123123', + 'amount' : '12.13', + 'name' : 'Rachel Welch', + }, + ] + + print(ach_file.add_batch('PPD', entries, credits=True, debits=True)) + +This prints the following information: + +:: + +[({'routing_number': '********', 'amount': '150.00', 'type': '27', 'account_number': '********', 'name': 'Billy Holiday'}, AchError('field needs to be numeric characters only',))] + +Here is the ach file with the skipped entry. + +.. code:: python + + print ach_file.render_to_string() + +:: + + 101 123456780 1234567802008071448B094101YOUR BANK YOUR COMPANY + 5200YOUR COMPANY 1234567890PPDPAYROLL 200808 1123456780000001 + 622123232318123123123 0000001213 RACHEL WELCH 0123456780000001 + 820000000100123232310000000000000000000012131234567890 123456780000001 + 9000001000001000000010012323231000000000000000000001213 + 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 + 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 + 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 + 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 + 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 diff --git a/ach/builder.py b/ach/builder.py index b3d48f3..e84eae5 100644 --- a/ach/builder.py +++ b/ach/builder.py @@ -79,35 +79,38 @@ def add_batch(self, std_ent_cls_code, batch_entries=None, company_name=(company_name or self.settings['company_name'])[:16], ) - entries = [] + entries, failed_entry_errors = [], [] entry_counter = 1 for record in batch_entries: + try: + entry = EntryDetail( + std_ent_cls_code=std_ent_cls_code, + id_number=record.get('id_number', ''), + ) - entry = EntryDetail( - std_ent_cls_code=std_ent_cls_code, - id_number=record.get('id_number', ''), - ) - - entry.transaction_code = record.get('type') - entry.recv_dfi_id = record.get('routing_number') + entry.transaction_code = record.get('type') + entry.recv_dfi_id = record.get('routing_number') - if len(record['routing_number']) < 9: - entry.calc_check_digit() - else: - entry.check_digit = record['routing_number'][8] + if len(record['routing_number']) < 9: + entry.calc_check_digit() + else: + entry.check_digit = record['routing_number'][8] - entry.dfi_acnt_num = record['account_number'] - entry.amount = int(round(float(record['amount']) * 100)) - entry.ind_name = record['name'].upper()[:22] - entry.trace_num = self.settings['immediate_dest'][:8] \ - + entry.validate_numeric_field(entry_counter, 7) + entry.dfi_acnt_num = record['account_number'] + entry.amount = int(round(float(record['amount']) * 100)) + entry.ind_name = record['name'].upper()[:22] + entry.trace_num = self.settings['immediate_dest'][:8] \ + + entry.validate_numeric_field(entry_counter, 7) - entries.append((entry, record.get('addenda', []))) - entry_counter += 1 + entries.append((entry, record.get('addenda', []))) + entry_counter += 1 + except Exception as e: + failed_entry_errors.append((record, e)) self.batches.append(FileBatch(batch_header, entries)) self.set_control() + return failed_entry_errors def set_control(self): diff --git a/ach/data_types.py b/ach/data_types.py index 75c9568..000ab2f 100644 --- a/ach/data_types.py +++ b/ach/data_types.py @@ -62,10 +62,7 @@ def validate_alpha_numeric_field(self, field, length): """ str_length = str(length) - match = re.match( - r'([\w\s^!_@#$%&,*:./+\-]{1,' + str_length + '})', - field, - ) + match = re.match(r'([\w,\s]{1,' + str_length + '})', field) if match: if len(match.group(1)) < length: diff --git a/example.py b/example.py index 0112d04..5c20e2d 100644 --- a/example.py +++ b/example.py @@ -42,3 +42,28 @@ ach_file.add_batch('PPD', entries, credits=True, debits=True) print ach_file.render_to_string() + +# add_batch will skip failures and return them + +ach_file = AchFile('B', settings) #file Id mod + +entries = [ + { + 'type' : '27', + 'routing_number' : '********', # invalid + 'account_number' : '********', # invalid + 'amount' : '150.00', + 'name' : 'Billy Holiday', + }, + { + 'type' : '22', + 'routing_number' : '123232318', + 'account_number' : '123123123', + 'amount' : '12.13', + 'name' : 'Rachel Welch', + }, +] + +print ach_file.add_batch('PPD', entries, credits=True, debits=True) + +print ach_file.render_to_string() diff --git a/setup.py b/setup.py index 7821b6f..a2c239c 100644 --- a/setup.py +++ b/setup.py @@ -3,8 +3,8 @@ setup( name='carta-ach', author='Carta, Inc.', - author_email='jared.hobbs@carta.com', - version='0.4.5', + author_email='james.uejio@carta.com', + version='0.4.6', packages=[ 'ach', ],