Example #1
0
 def parse_statement(self, ns, node):
     """Parse a single Stmt node."""
     statement = BankStatement()
     self.add_value_from_node(ns, node, [
         './ns:Acct/ns:Id/ns:IBAN',
         './ns:Acct/ns:Id/ns:Othr/ns:Id',
     ], statement, 'local_account')
     self.add_value_from_node(ns, node, './ns:Id', statement,
                              'statement_id')
     self.add_value_from_node(ns, node, './ns:Acct/ns:Ccy', statement,
                              'local_currency')
     (statement.start_balance,
      statement.end_balance) = (self.get_balance_amounts(ns, node))
     transaction_nodes = node.xpath('./ns:Ntry', namespaces={'ns': ns})
     for entry_node in transaction_nodes:
         transaction = statement.create_transaction()
         self.parse_transaction(ns, entry_node, transaction)
     if statement['transactions']:
         execution_date = statement['transactions'][0].execution_date
         statement.date = datetime.strptime(execution_date, "%Y-%m-%d")
         # Prepend date of first transaction to improve id uniquenes
         if execution_date not in statement.statement_id:
             statement.statement_id = "%s-%s" % (execution_date,
                                                 statement.statement_id)
     return statement
Example #2
0
    def parse(self, data):
        """Parse bgmax bank statement file contents."""

        self.is_bgmax(data)
        iterator = BgMaxIterator(data)

        self.current_statement = BankStatement()
        self.current_statement.local_currency = 'SEK'
        self.current_statement.start_balance = 0.0
        self.current_statement.end_balance = 0.0

        for avsnitt in iterator:
            #~ _logger.warn("header: %s" % avsnitt.header)
            #~ _logger.warn("footer: %s" % avsnitt.footer)
            #~ _logger.warn("ins: %s" % avsnitt.ins)
            #~ _logger.warn("bet: %s" % avsnitt.bet)
            #~ _logger.warn("type: %s" % avsnitt.type)

            if not self.current_statement.local_account:
                self.current_statement.local_account = str(
                    int(
                        avsnitt.header.get('mottagarplusgiro', '').strip()
                        or avsnitt.header.get('mottagarbankgiro', '').strip()))
                if len(self.current_statement.local_account) == 8:
                    self.current_statement.local_account = self.current_statement.local_account[:4] + '-' + self.current_statement.local_account[
                        4:]
            #if not self.current_statement.local_currency:
            #    self.current_statement.local_currency = avsnitt.header.get('valuta').strip() or avsnitt.footer.get('valuta').strip()
            if not self.current_statement.statement_id:
                self.current_statement.statement_id = 'BgMax %s %s' % (
                    self.current_statement.local_account,
                    iterator.header['skrivdag'][:10])

            for ins in avsnitt.ins:
                transaction = self.current_statement.create_transaction()
                #~ if int(ins.get('bankgiro', 0)):
                #~ transaction.remote_account = str(int(ins.get('bankgiro', 0)))
                transaction.transferred_amount = float(ins.get('betbelopp',
                                                               0)) / 100
                self.current_statement.end_balance += transaction.transferred_amount

                date = avsnitt.footer.get('betalningsdag') or ''
                if date:
                    date = date[:4] + '-' + date[4:6] + '-' + date[6:]
                    self.current_statement.date = date
                    transaction.value_date = date
                transaction.remote_account = ins.get('bankgiro',
                                                     '').strip().lstrip('0')
                if len(transaction.remote_account) == 8:
                    transaction.remote_account = transaction.remote_account[:4] + '-' + transaction.remote_account[
                        4:]
                if ins.get('organisationsnummer'):
                    transaction.remote_owner = ins.get(
                        'organisationsnummer').strip()
                else:
                    transaction.remote_owner = ins.get('betalarens_namn',
                                                       '').strip()
                #~ transaction.message
                #~ transaction.remote_owner
                #~ transaction.name
                #~ transaction.note
                #~ transaction.value_date

                transaction.message = ins.get('betalarens_namn', '')
                transaction.eref = ins.get('referens') or ins.get('BGC-nummer')
                transaction.note = '\n'.join(ins.get('informationstext', []))
                if ins.get('betalarens_adress'):
                    transaction.note += ' %s, %s %s %s' % (
                        ins.get('betalarens_adress'),
                        ins.get('betalarens_postnr'),
                        ins.get('betalarens_ort'), ins.get('betalarens_land'))
                if ins.get('organisationsnummer'):
                    transaction.note += ' %s ' % ins.get('organisationsnummer')
                if ins.get('BGC-nummer'):
                    transaction.note += ' BGC %s ' % ins.get('BGC-nummer')
                if transaction.remote_account:
                    transaction.note += ' bg %s ' % transaction.remote_account

            self.statements.append(self.current_statement)

        if not iterator.check():
            _logger.error('BgMax-file error')
        #raise Warning(self.statements)
        #_logger.warning('currency_code %s' % self.statements[0].pop('currency_code'))
        #_logger.warning('transactions %s' % self.statements[0]['transactions'])

        return self.statements
Example #3
0
class BgMaxParser(object):
    """Parser for BgMax bank statement import files."""

    #~ def __init__(self, data):
    #~ self.row = 0
    #~ self.data = unicode(data,'iso8859-1').splitlines()
    #~ self.rows = len(self.data)
    #~ self.header = {}
    #~ self.footer = {}
    #~ self.avsnitt = []
    #~ self.header_regex = '^01BGMAX'  # Start of header

    def is_bgmax(self, line):
        """determine if a line is the header of a statement"""
        if not bool(re.match(self.header_regex, line)):
            raise ValueError('File starting with %s does not seem to be a'
                             ' valid %s format bank statement.' %
                             (line[:12], 'BgMax'))

    def __init__(self):
        """Initialize parser - override at least header_regex.
        This in fact uses the ING syntax, override in others."""
        self.bgmax_type = 'General'
        self.header_regex = '^01BGMAX'  # Start of header
        self.current_statement = None
        self.current_transaction = None
        self.statements = []
        self.currency = ''
        self.balance_start = 0.0
        self.balance_end_real = 0.0
        self.balance_end = 0.0

    def parse(self, data):
        """Parse bgmax bank statement file contents."""

        self.is_bgmax(data)
        iterator = BgMaxIterator(data)

        self.current_statement = BankStatement()
        self.current_statement.local_currency = 'SEK'
        self.current_statement.start_balance = 0.0
        self.current_statement.end_balance = 0.0

        for avsnitt in iterator:
            #~ _logger.warn("header: %s" % avsnitt.header)
            #~ _logger.warn("footer: %s" % avsnitt.footer)
            #~ _logger.warn("ins: %s" % avsnitt.ins)
            #~ _logger.warn("bet: %s" % avsnitt.bet)
            #~ _logger.warn("type: %s" % avsnitt.type)

            if not self.current_statement.local_account:
                self.current_statement.local_account = str(
                    int(
                        avsnitt.header.get('mottagarplusgiro', '').strip()
                        or avsnitt.header.get('mottagarbankgiro', '').strip()))
                if len(self.current_statement.local_account) == 8:
                    self.current_statement.local_account = self.current_statement.local_account[:4] + '-' + self.current_statement.local_account[
                        4:]
            #if not self.current_statement.local_currency:
            #    self.current_statement.local_currency = avsnitt.header.get('valuta').strip() or avsnitt.footer.get('valuta').strip()
            if not self.current_statement.statement_id:
                self.current_statement.statement_id = 'BgMax %s %s' % (
                    self.current_statement.local_account,
                    iterator.header['skrivdag'][:10])

            for ins in avsnitt.ins:
                transaction = self.current_statement.create_transaction()
                #~ if int(ins.get('bankgiro', 0)):
                #~ transaction.remote_account = str(int(ins.get('bankgiro', 0)))
                transaction.transferred_amount = float(ins.get('betbelopp',
                                                               0)) / 100
                self.current_statement.end_balance += transaction.transferred_amount

                date = avsnitt.footer.get('betalningsdag') or ''
                if date:
                    date = date[:4] + '-' + date[4:6] + '-' + date[6:]
                    self.current_statement.date = date
                    transaction.value_date = date
                transaction.remote_account = ins.get('bankgiro',
                                                     '').strip().lstrip('0')
                if len(transaction.remote_account) == 8:
                    transaction.remote_account = transaction.remote_account[:4] + '-' + transaction.remote_account[
                        4:]
                if ins.get('organisationsnummer'):
                    transaction.remote_owner = ins.get(
                        'organisationsnummer').strip()
                else:
                    transaction.remote_owner = ins.get('betalarens_namn',
                                                       '').strip()
                #~ transaction.message
                #~ transaction.remote_owner
                #~ transaction.name
                #~ transaction.note
                #~ transaction.value_date

                transaction.message = ins.get('betalarens_namn', '')
                transaction.eref = ins.get('referens') or ins.get('BGC-nummer')
                transaction.note = '\n'.join(ins.get('informationstext', []))
                if ins.get('betalarens_adress'):
                    transaction.note += ' %s, %s %s %s' % (
                        ins.get('betalarens_adress'),
                        ins.get('betalarens_postnr'),
                        ins.get('betalarens_ort'), ins.get('betalarens_land'))
                if ins.get('organisationsnummer'):
                    transaction.note += ' %s ' % ins.get('organisationsnummer')
                if ins.get('BGC-nummer'):
                    transaction.note += ' BGC %s ' % ins.get('BGC-nummer')
                if transaction.remote_account:
                    transaction.note += ' bg %s ' % transaction.remote_account

            self.statements.append(self.current_statement)

        if not iterator.check():
            _logger.error('BgMax-file error')
        #raise Warning(self.statements)
        #_logger.warning('currency_code %s' % self.statements[0].pop('currency_code'))
        #_logger.warning('transactions %s' % self.statements[0]['transactions'])

        return self.statements
Example #4
0
class MT940(object):
    """Inherit this class in your account_banking.parsers.models.parser,
    define functions to handle the tags you need to handle and adjust static
    variables as needed.

    At least, you should override handle_tag_61 and handle_tag_86.
    Don't forget to call super.

    handle_tag_* functions receive the remainder of the the line (that is,
    without ':XX:') and are supposed to write into self.current_transaction
    """
    def __init__(self):
        """Initialize parser - override at least header_regex.

        This in fact uses the ING syntax, override in others."""
        self.mt940_type = 'General'
        self.header_lines = 3  # Number of lines to skip
        self.header_regex = '^0000 01INGBNL2AXXXX|^{1'
        self.footer_regex = '^-}$|^-XXX$'  # Stop processing on seeing this
        self.tag_regex = '^:[0-9]{2}[A-Z]*:'  # Start of new tag
        self.current_statement = None
        self.current_transaction = None
        self.statements = []

    def is_mt940(self, line):
        """determine if a line is the header of a statement"""
        if not bool(re.match(self.header_regex, line)):
            raise ValueError('File starting with %s does not seem to be a'
                             ' valid %s MT940 format bank statement.' %
                             (line[:12], self.mt940_type))

    def parse(self, data):
        """Parse mt940 bank statement file contents."""
        self.is_mt940(data)
        iterator = data.replace('\r\n', '\n').split('\n').__iter__()
        line = None
        record_line = ''
        try:
            while True:
                if not self.current_statement:
                    self.handle_header(line, iterator)
                line = iterator.next()
                if not self.is_tag(line) and not self.is_footer(line):
                    record_line += line
                    continue
                if record_line:
                    self.handle_record(record_line)
                if self.is_footer(line):
                    self.handle_footer(line, iterator)
                    record_line = ''
                    continue
                record_line = line
        except StopIteration:
            pass
        if self.current_statement:
            if record_line:
                self.handle_record(record_line)
                record_line = ''
            self.statements.append(self.current_statement)
            self.current_statement = None
        return self.statements

    def is_footer(self, line):
        """determine if a line is the footer of a statement"""
        return line and bool(re.match(self.footer_regex, line))

    def is_tag(self, line):
        """determine if a line has a tag"""
        return line and bool(re.match(self.tag_regex, line))

    def handle_header(self, dummy_line, iterator):
        """skip header lines, create current statement"""
        for dummy_i in range(self.header_lines):
            iterator.next()
        self.current_statement = BankStatement()

    def handle_footer(self, dummy_line, dummy_iterator):
        """add current statement to list, reset state"""
        self.statements.append(self.current_statement)
        self.current_statement = None

    def handle_record(self, line):
        """find a function to handle the record represented by line"""
        tag_match = re.match(self.tag_regex, line)
        tag = tag_match.group(0).strip(':')
        if not hasattr(self, 'handle_tag_%s' % tag):
            logging.error('Unknown tag %s', tag)
            logging.error(line)
            return
        handler = getattr(self, 'handle_tag_%s' % tag)
        handler(line[tag_match.end():])

    def handle_tag_20(self, data):
        """Contains unique ? message ID"""
        pass

    def handle_tag_25(self, data):
        """Handle tag 25: local bank account information."""
        data = data.replace('EUR', '').replace('.', '').strip()
        self.current_statement.local_account = data

    def handle_tag_28C(self, data):
        """Sequence number within batch - normally only zeroes."""
        pass

    def handle_tag_60F(self, data):
        """get start balance and currency"""
        # For the moment only first 60F record
        # The alternative would be to split the file and start a new
        # statement for each 20: tag encountered.
        stmt = self.current_statement
        if not stmt.local_currency:
            stmt.local_currency = data[7:10]
            stmt.start_balance = str2amount(data[0], data[10:])

    def handle_tag_61(self, data):
        """get transaction values"""
        transaction = self.current_statement.create_transaction()
        self.current_transaction = transaction
        transaction.execution_date = datetime.strptime(data[:6], '%y%m%d')
        transaction.value_date = datetime.strptime(data[:6], '%y%m%d')
        #  ...and the rest already is highly bank dependent

    def handle_tag_62F(self, data):
        """Get ending balance, statement date and id.

        We use the date on the last 62F tag as statement date, as the date
        on the 60F record (previous end balance) might contain a date in
        a previous period.

        We generate the statement.id from the local_account and the end-date,
        this should normally be unique, provided there is a maximum of
        one statement per day.

        Depending on the bank, there might be multiple 62F tags in the import
        file. The last one counts.
        """
        stmt = self.current_statement
        stmt.end_balance = str2amount(data[0], data[10:])
        stmt.date = datetime.strptime(data[1:7], '%y%m%d')
        # Only replace logically empty (only whitespace or zeroes) id's:
        # But do replace statement_id's added before (therefore starting
        # with local_account), because we need the date on the last 62F
        # record.
        test_empty_id = re.sub(r'[\s0]', '', stmt.statement_id)
        if ((not test_empty_id)
                or (stmt.statement_id.startswith(stmt.local_account))):
            stmt.statement_id = '%s-%s' % (
                stmt.local_account,
                stmt.date.strftime('%Y-%m-%d'),
            )

    def handle_tag_64(self, data):
        """get current balance in currency"""
        pass

    def handle_tag_65(self, data):
        """get future balance in currency"""
        pass

    def handle_tag_86(self, data):
        """details for previous transaction, here most differences between
        banks occur"""
        pass
Example #5
0
 def handle_header(self, dummy_line, iterator):
     """skip header lines, create current statement"""
     for dummy_i in range(self.header_lines):
         iterator.next()
     self.current_statement = BankStatement()