Example #1
0
    def __init__(self, file_id_mod, settings):
        """
        The file_id_mod should be 'A' for the first of the day, 'B' for the second
        and so on.
        """

        self.settings = settings

        try:
            self.header = Header(settings['immediate_dest'], settings['immediate_org'], file_id_mod,
                                 settings['immediate_dest_name'], settings['immediate_org_name'])
        except KeyError:
            raise Exception(
                'Settings require: "immediate_dest", "immediate_org", immediate_dest_name", and "immediate_org_name"')

        self.batches = list()
Example #2
0
class AchFile(object):
    """
    This class is what stores the ach data.  Its main external methods
    are `add_batch` and `render_to_string`.

    """

    def __init__(self, file_id_mod, settings):
        """
        The file_id_mod should be 'A' for the first of the day, 'B' for the second
        and so on.
        """

        self.settings = settings

        try:
            self.header = Header(settings['immediate_dest'], settings['immediate_org'], file_id_mod,
                                 settings['immediate_dest_name'], settings['immediate_org_name'])
        except KeyError:
            raise Exception(
                'Settings require: "immediate_dest", "immediate_org", immediate_dest_name", and "immediate_org_name"')

        self.batches = list()

    def add_batch(self, std_ent_cls_code, batch_entries=list(), credits=True, debits=False):
        """
        Use this to add batches to the file. For valid std_ent_cls_codes see:
        http://en.wikipedia.org/wiki/Automated_Clearing_House#SEC_codes
        """

        entry_desc = self.get_entry_desc(std_ent_cls_code)

        batch_count = len(self.batches) + 1

        datestamp = datetime.today().strftime('%y%m%d') #YYMMDD

        if credits and debits:
            serv_cls_code = '200'
        elif credits:
            serv_cls_code = '220'
        elif debits:
            serv_cls_code = '225'

        batch_header = BatchHeader(serv_cls_code=serv_cls_code, company_name=self.settings['immediate_org_name'],
                                   company_id=self.settings['company_id'], std_ent_cls_code=std_ent_cls_code,
                                   entry_desc=entry_desc, desc_date='', eff_ent_date=datestamp,
                                   orig_stat_code='1', orig_dfi_id=self.settings['immediate_dest'][:8],
                                   batch_id=batch_count)

        entries = list()
        entry_counter = 1

        for record in batch_entries:

            entry = EntryDetail(std_ent_cls_code)

            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]

            entry.dfi_acnt_num = record['account_number']
            entry.amount = int(round(float(record['amount']), 2) * 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)
            entry_counter += 1

        self.batches.append(FileBatch(batch_header, entries))
        self.set_control()


    def set_control(self):

        batch_count = len(self.batches)
        block_count = self.get_block_count(self.batches)
        entry_hash = self.get_entry_hash(self.batches)
        entadd_count = self.get_entadd_count(self.batches)
        debit_amount = self.get_debit_amount(self.batches)
        credit_amount = self.get_credit_amount(self.batches)

        self.control = FileControl(batch_count, block_count, entadd_count, entry_hash, debit_amount, credit_amount)

    def get_block_count(self, batches):

        return int(math.ceil(self.get_lines(batches) / 10.0))

    def get_lines(self, batches):
        header_count = 1
        control_count = 1
        batch_header_count = len(batches)
        batch_footer_count = batch_header_count

        entadd_count = self.get_entadd_count(batches)

        lines = header_count + control_count + batch_header_count + batch_footer_count + entadd_count

        return lines

    def get_entadd_count(self, batches):
        entadd_count = 0

        for batch in batches:
            entadd_count = entadd_count + int(batch.batch_control.entadd_count)

        return entadd_count

    def get_entry_hash(self, batches):
        entry_hash = 0

        for batch in batches:
            entry_hash = entry_hash + int(batch.batch_control.entry_hash)

        if len(str(entry_hash)) > 10:
            pos = len(str(entry_hash)) - 10
            entry_hash = str(entry_hash)[pos:]
        else:
            entry_hash = str(entry_hash)

        return entry_hash

    def get_debit_amount(self, batches):
        debit_amount = 0

        for batch in batches:
            debit_amount = debit_amount + int(batch.batch_control.debit_amount)

        return debit_amount

    def get_credit_amount(self, batches):
        credit_amount = 0

        for batch in batches:
            credit_amount = credit_amount + int(batch.batch_control.credit_amount)

        return credit_amount

    def get_nines(self, rows):
        nines = ''

        for i in range(rows):
            for l in range(94):
                nines += '9'
            if i == rows - 1: continue
            nines += "\n"

        return nines

    def get_entry_desc(self, std_ent_cls_code):

        if std_ent_cls_code == 'PPD':
            entry_desc = 'PAYROLL'
        elif std_ent_cls_code == 'CCD':
            entry_desc = 'DUES'
        else:
            entry_desc = 'OTHER'

        return entry_desc

    def render_to_string(self):
        """
        Renders a nacha file as a string
        """

        ret_string = self.header.get_row() + "\n"

        for batch in self.batches:
            ret_string += batch.render_to_string()

        ret_string += self.control.get_row() + "\n"

        lines = self.get_lines(self.batches)

        nine_lines = int(10 * (math.ceil(lines / 10.0) - (lines / 10.0)))

        ret_string += self.get_nines(nine_lines)

        return ret_string
class AchFile(object):
    """
    This class is what stores the ach data.  Its main external methods
    are `add_batch` and `render_to_string`.

    """

    def __init__(self, file_id_mod, settings):
        """
        The file_id_mod should be 'A' for the first of the day, 'B' for the second
        and so on.
        """

        self.settings = settings

        try:
            self.header = Header(settings['immediate_dest'], settings['immediate_org'], file_id_mod,
                                 settings['immediate_dest_name'], settings['immediate_org_name'])
        except KeyError:
            raise Exception(
                'Settings require: "immediate_dest", "immediate_org", immediate_dest_name", and "immediate_org_name"')

        self.batches = list()

    def add_batch(self, std_ent_cls_code, batch_entries=list(), credits=True, debits=False, desc_date='', entry_date=None):
        """
        Use this to add batches to the file. For valid std_ent_cls_codes see:
        http://en.wikipedia.org/wiki/Automated_Clearing_House#SEC_codes
        """

        entry_desc = self.get_entry_desc(std_ent_cls_code)

        batch_count = len(self.batches) + 1

        if entry_date is None:
            entry_date = banking_days_from_now(2).strftime('%y%m%d')#datetime.today().strftime('%y%m%d') #YYMMDD

        if credits and debits:
            serv_cls_code = '200'
        elif credits:
            serv_cls_code = '220'
        elif debits:
            serv_cls_code = '225'

        batch_header = BatchHeader(serv_cls_code=serv_cls_code, company_name=self.settings['immediate_org_name'],
                                   company_id=self.settings['company_id'], std_ent_cls_code=std_ent_cls_code,
                                   entry_desc=entry_desc, desc_date=desc_date, eff_ent_date=entry_date,
                                   orig_stat_code='1', orig_dfi_id=self.settings['immediate_dest'][:8],
                                   batch_id=batch_count)

        entries = list()
        entry_counter = 1

        for record in batch_entries:

            entry = EntryDetail(std_ent_cls_code)

            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]

            entry.dfi_acnt_num = record['account_number']
            entry.amount = int(round(float(record['amount']), 2) * 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)
            entry_counter += 1

        self.batches.append(FileBatch(batch_header, entries))
        self.set_control()


    def set_control(self):

        batch_count = len(self.batches)
        block_count = self.get_block_count(self.batches)
        entry_hash = self.get_entry_hash(self.batches)
        entadd_count = self.get_entadd_count(self.batches)
        debit_amount = self.get_debit_amount(self.batches)
        credit_amount = self.get_credit_amount(self.batches)

        self.control = FileControl(batch_count, block_count, entadd_count, entry_hash, debit_amount, credit_amount)

    def get_block_count(self, batches):

        return int(math.ceil(self.get_lines(batches) / 10.0))

    def get_lines(self, batches):
        header_count = 1
        control_count = 1
        batch_header_count = len(batches)
        batch_footer_count = batch_header_count

        entadd_count = self.get_entadd_count(batches)

        lines = header_count + control_count + batch_header_count + batch_footer_count + entadd_count

        return lines

    def get_entadd_count(self, batches):
        entadd_count = 0

        for batch in batches:
            entadd_count = entadd_count + int(batch.batch_control.entadd_count)

        return entadd_count

    def get_entry_hash(self, batches):
        entry_hash = 0

        for batch in batches:
            entry_hash = entry_hash + int(batch.batch_control.entry_hash)

        if len(str(entry_hash)) > 10:
            pos = len(str(entry_hash)) - 10
            entry_hash = str(entry_hash)[pos:]
        else:
            entry_hash = str(entry_hash)

        return entry_hash

    def get_debit_amount(self, batches):
        debit_amount = 0

        for batch in batches:
            debit_amount = debit_amount + int(batch.batch_control.debit_amount)

        return debit_amount

    def get_credit_amount(self, batches):
        credit_amount = 0

        for batch in batches:
            credit_amount = credit_amount + int(batch.batch_control.credit_amount)

        return credit_amount

    def get_nines(self, rows):
        nines = ''

        for i in range(rows):
            for l in range(94):
                nines += '9'
            #if i == rows - 1: continue
            nines += "\r\n"

        return nines

    def get_entry_desc(self, std_ent_cls_code):

        if std_ent_cls_code == 'PPD':
            entry_desc = 'PAYROLL'
        elif std_ent_cls_code == 'CCD':
            entry_desc = 'DUES'
        else:
            entry_desc = 'OTHER'

        return entry_desc

    def render_to_string(self):
        """
        Renders a nacha file as a string
        """

        ret_string = self.header.get_row() + "\r\n"

        for batch in self.batches:
            ret_string += batch.render_to_string()

        ret_string += self.control.get_row() + "\r\n"

        lines = self.get_lines(self.batches)

        nine_lines = int(10 * (math.ceil(lines / 10.0) - (lines / 10.0)))

        ret_string += self.get_nines(nine_lines)

        return ret_string


    def pprint_line(self, name, val):
        width = 27
        length = len(val)
        val = str(val + "|").ljust(width)
        return "  %s|%slen:%s\n" % (name.ljust(width), val, length)


    def pretty_print(self):
        raw = self.render_to_string()
        out = ''
        for line in raw.split('\n'):
            if line[0] == '1':
                out += "====== FILE HEADER ======\n"
                out += self.pprint_line('ImmediateDest', line[3:13])
                out += self.pprint_line('ImmediateOrg', line[13:23])
                out += self.pprint_line('ImmediateDestName', line[40:63])
                out += self.pprint_line('ImmediateOrgName', line[63:86])
            if line[0] == '5':
                out += "====== BATCH HEADER ======\n"
                out += self.pprint_line('ServiceClassCode', line[1:4])
                out += self.pprint_line('CompDescDate', line[63:69])
                out += self.pprint_line('EffEntryDate', line[69:75])
                out += self.pprint_line('OrigDFIid', line[79:87])
            if line[0] == '6':
                out += "====== ENTRY DETAIL ======\n"                
                out += self.pprint_line('RecvDFIid', line[3:11])
                out += self.pprint_line('RecvDFIcheck', line[11])
                out += self.pprint_line('RecvDFIacct', line[12:29])
            if line[0] == '8':
                out += "====== BATCH CONTROL ======\n"
                out += self.pprint_line('ServiceClassCode', line[1:4])
                out += self.pprint_line('CompanyId', line[44:54])
        return out