society-self-service/sstreasury/aba.py

98 lines
4.9 KiB
Python

# 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.gnu.org/licenses/>.
# 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')