# Society Self-Service # Copyright © 2018–2020 Yingtong Li (RunasSudo) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # https://www.brad-smith.info/blog/archives/405 # https://www.cemtexaba.com/aba-format/cemtex-aba-file-format-details # https://ddkonline.blogspot.com/2009/01/aba-bank-payment-file-format-australian.html class ABAException(Exception): pass def write_descriptive(f, reel_seq=1, bank_name='', user_name='', bank_code=0, description='', date=None): if reel_seq < 0 or reel_seq > 99: raise ABAException('Invalid Reel Sequence Number: {}'.format(reel_seq)) if len(bank_name) > 3: raise ABAException('Invalid Financial Institution abbreviation: {}'.format(bank_name)) if len(user_name) > 26: raise ABAException('Invalid User Preferred Specification: {}'.format(user_name)) if bank_code < 0 or bank_code > 999999: raise ABAException('Invalid User Identification Number: {}'.format(bank_code)) if len(description) > 12: raise ABAException('Invalid Description: {}'.format(description)) f.write(b'0') # Record Type 0 f.write(b' ' * 17) # Blank f.write('{:02}'.format(reel_seq).encode('ascii')) # Reel Sequence Number f.write('{: <3}'.format(bank_name).encode('ascii')) # Financial Institution abbreviation f.write(b' ' * 7) # Blank f.write('{: <26}'.format(user_name).encode('ascii')) # User Preferred Specification f.write('{:06}'.format(bank_code).encode('ascii')) # User Identification Number f.write('{: <12}'.format(description).encode('ascii')) # Description f.write(date.strftime('%d%m%y').encode('ascii')) # Date f.write(b' ' * 40) # Blank f.write(b'\r\n') def write_detail(f, dest_bsb='', dest_account='', indicator=' ', transaction_code=53, cents=0, dest_name='', reference='', src_bsb='', src_account='', src_name='', tax_withheld=0): dest_bsb = dest_bsb.replace('-', '').replace(' ', '') dest_account = dest_account.replace('-', '').replace(' ', '') src_bsb = src_bsb.replace('-', '').replace(' ', '') src_account = src_account.replace('-', '').replace(' ', '') if len(dest_bsb) != 6: raise ABAException('Invalid BSB: {}'.format(dest_bsb)) if len(dest_account) > 9: raise ABAException('Invalid Account Number: {}'.format(dest_account)) if len(indicator) != 1: raise ABAException('Invalid Indicator: {}'.format(indicator)) if transaction_code < 0 or transaction_code > 99: raise ABAException('Invalid Transaction Code: {}'.format(indicator)) if len(dest_name) > 32: raise ABAException('Invalid Title of Account: {}'.format(dest_name)) if len(reference) > 18: raise ABAException('Invalid Lodgement Reference: {}'.format(reference)) if len(src_bsb) != 6: raise ABAException('Invalid Trace BSB: {}'.format(src_bsb)) if len(src_account) > 9: raise ABAException('Invalid Trace Account Number: {}'.format(src_account)) if len(src_name) > 16: raise ABAException('Invalid Name of Remitter: {}'.format(src_name)) f.write(b'1') # Record Type 1 f.write('{}-{}'.format(dest_bsb[:3], dest_bsb[-3:]).encode('ascii')) # BSB f.write('{: >9}'.format(dest_account).encode('ascii')) # Account Number f.write(indicator.encode('ascii')) # Indicator f.write('{:02}'.format(transaction_code).encode('ascii')) # Transaction Code f.write('{:010}'.format(round(cents)).encode('ascii')) # Amount f.write('{: <32}'.format(dest_name).encode('ascii')) # Title of Account f.write('{: <18}'.format(reference).encode('ascii')) # Lodgement Reference f.write('{}-{}'.format(src_bsb[:3], src_bsb[-3:]).encode('ascii')) # Trace BSB f.write('{: >9}'.format(src_account).encode('ascii')) # Trace Account Number f.write('{: <16}'.format(src_name).encode('ascii')) # Name of Remitter f.write('{:08}'.format(round(tax_withheld)).encode('ascii')) # Amount of Withholding Tax f.write(b'\r\n') def write_total(f, credit_cents=0, num_detail_records=0): f.write(b'7') # Record Type 7 f.write(b'999-999') # BSB Format Filler f.write(b' ' * 12) # Blank f.write('{:010}'.format(round(credit_cents)).encode('ascii')) # File (User) Net Total Amount f.write('{:010}'.format(round(credit_cents)).encode('ascii')) # File (User) Credit Total Amount f.write(b'0' * 10) # File (User) Debit Total Amount f.write(b' ' * 24) # Blank f.write('{:06}'.format(num_detail_records).encode('ascii')) # File (user) count of Records Type 1 f.write(b' ' * 40) # Blank f.write(b'\r\n')