Пример #1
0
def parse_ibkr_qfx(fn: str) -> None:
    """
    Parses the raw IBKR QFX file FN into a split representation, factoring out
    mastercard transactions to a separate CSV file.
    """

    out_qfx_fn = (basename := splitext(fn)[0]) + ".out.qfx"
    out_csv_fn = basename + ".csv"

    with open(fn, "rb") as f:
        parser = OFXTree()
        tree = parser.parse(f)

    transactions = []

    for tran in tree.findall(".//INVBANKTRAN"):
        ttype = tran.find("./STMTTRN/TRNTYPE").text
        if ttype == "ATM":
            transactions.append(write_atm_line(tran))
            tran.find(".//MEMO").text = "MasterCard Transaction"

    with open(fn, "r") as f_in, open(out_qfx_fn, "wb") as f_out:
        # copy header
        f_out.write("".join(f_in.readlines()[:9]).encode("utf-8"))
        parser.write(f_out)

    with open(out_csv_fn, "w") as f:
        f.write("\n".join(transactions))
Пример #2
0
def verify_ofximport(args):
    if not os.path.exists(args.ofxfile):
        LOG.error('input OFX file does not exist')
        exit(-1)

    tree = OFXTree()
    tree.parse(args.ofxfile)
    response = tree.convert()
    stmts = response.statements
    return stmts
Пример #3
0
    def __init__(self, ofx_file_or_path, parse_on_load: bool = True):
        self.FILE = ofx_file_or_path
        self.ofx_tree: OFXTree = OFXTree()
        self.ofx_data: OFX or None = None
        self.statements: List[STMTRS] or None = None
        self.NUMBER_OF_STATEMENTS: int or None = None

        if parse_on_load:
            self.parse_ofx()
Пример #4
0
    def build_journal(self, ofx_file, config):
        tree = OFXTree()
        tree.parse(ofx_file)
        ofx_obj = tree.convert()

        stop_words = config.get('stop_words', [])

        balance_assertions = []
        transactions = []

        # There may be multiple bank statements in one file
        for statement in ofx_obj.statements:
            try:
                routing = statement.account.bankid
            except:
                routing = 'na'

            account = statement.account.acctid
            currency = CURRENCY_LOOKUP[statement.currency]
            balance = statement.ledgerbal.balamt
            stmnt_date = strftime(statement.ledgerbal.dtasof, '%Y-%m-%d')

            a_assert = Posting(account=config['from'],
                               amount=balance,
                               currency=currency,
                               assertion=True)

            t_assert = Transaction(date=stmnt_date,
                                   payee='Balance for {}-{}'.format(
                                       ofx_obj.sonrs.org, account),
                                   postings=[a_assert])

            balance_assertions.append(t_assert)

            for transaction in statement.transactions:
                meta = []

                payee = transaction.name
                hash_payee = payee
                for word in stop_words:
                    payee = payee.replace(word, '')

                amount = transaction.trnamt
                trn_date = strftime(transaction.dtposted, '%Y-%m-%d')
                if transaction.refnum:
                    trn_id = transaction.refnum
                else:
                    trn_id = transaction.fitid

                # If check number is available add it as metadata
                check = transaction.checknum
                if check:
                    meta.append(('check', transaction.checknum))

                # Build md5 Hash of transaction
                check_hash = trn_id + trn_date + hash_payee + str(amount)
                hash_obj = hashlib.md5(check_hash.encode())
                uuid = hash_obj.hexdigest()
                meta.append(('UUID', uuid))
                meta.append(('Imported', strftime(now(), '%Y-%m-%d')))

                a_tran = Posting(account=config['from'],
                                 amount=amount,
                                 currency=currency)

                t_tran = Transaction(date=trn_date,
                                     payee=payee,
                                     postings=[a_tran],
                                     metadata=meta,
                                     account=account,
                                     uuid=uuid)

                # Need to process transactions further here
                # Either rules or Bayesian...

                transactions.append(t_tran)

        return balance_assertions, transactions
Пример #5
0
def get_statements(ofx_file):
    tree = OFXTree()
    tree.parse(ofx_file)
    response = tree.convert()
    response
    return response.statements[0].transactions
Пример #6
0
    def read(self, factory, files=[]) -> List[BankStatement]:
        logger.debug(files)

        bankStmts = []
        bsReader = factory.createReaderBankStatement()
        count_open = 0

        if (files):
            for file in files:
                content = file.read()
                file.seek(0)

                upperContent = str(content).upper()
                count_open += upperContent.count('<STMTTRN>')

                try:
                    tree = OFXTree()
                    tree.parse(file)
                    self.treatBradescoException(tree)

                    #check creditcard
                    root = tree.getroot()
                    options = {}
                    if (root.findall("CREDITCARDMSGSRSV1")):
                        options['creditcard'] = True

                        # KN-177 - Check if Bradesco credit card
                        ccstmttrnrs = root.findall(
                            'CREDITCARDMSGSRSV1')[0].findall('CCSTMTTRNRS')[0]
                        banktranlist = ccstmttrnrs.findall(
                            'CCSTMTRS')[0].findall('BANKTRANLIST')[0]
                        dtstart = banktranlist.findall('DTSTART')[0]
                        dtend = banktranlist.findall('DTEND')[0]
                        if dtstart.text == dtend.text:
                            options['bradesco'] = True
                    else:
                        options['creditcard'] = False

                    options['bancodobrasil'] = False
                    fi = root.findall("SIGNONMSGSRSV1")[0].findall(
                        "SONRS")[0].findall("FI")
                    if (fi):
                        org = fi[0].findall("ORG")
                        if (org != None and "Banco do Brasil" in org[0].text):
                            options['bancodobrasil'] = True

                    convertedTree = tree.convert()
                    bs = bsReader.read(factory, convertedTree, options)
                    bankStmts.append(bs)
                except IndexError:
                    # ofx nao consegue ler versao 220. Ler como XML
                    file.seek(0)
                    xmlFactory = XMLReaderFactory()
                    xmlReader = xmlFactory.createReaderController()
                    bs = xmlReader.read(xmlFactory, files=[file])
                    bankStmts.append(bs)

            total_count = 0
            for bankStmtList in bankStmts:
                for bs in bankStmtList:
                    total_count += len(bs.inflows)
                    total_count += len(bs.outflows)

            assert count_open == total_count

            return bankStmts

        bsNull = BankStatement()
        return [bsNull]
Пример #7
0
def do_ofximport(args, client=None):
    delta = 0
    if client is None:
        client = clientfromargs(args)

    tree = OFXTree()
    tree.parse(args.ofxfile)
    response = tree.convert()
    stmts = response.statements

    accounts = client.budget.be_accounts
    accountvsnotes = {
        account.note: account
        for account in accounts if account.note is not None
    }

    for stmt in stmts:
        key = stmt.account.bankid + ' ' + stmt.account.branchid + ' ' + stmt.account.acctid
        if all(key not in note for note in accountvsnotes):
            if len(accounts) == 0:
                print('No accounts available in this budget')
                exit(-1)

            # ask user input for which bank account this is, then save it into the account note in nYNAB
            account = client.select_account_ui()

            # Save the selection in the nYNAB account note
            addon = 'key[' + key + ']key'
            if account.note is not None:
                account.note += addon
            else:
                account.note = addon
        else:
            for note in accountvsnotes:
                if key in note:
                    account = accountvsnotes[note]

                    imported_date = datetime.now().date()

                    for ofx_transaction in stmt.transactions:
                        payee_name = ofx_transaction.name if ofx_transaction.payee is None else ofx_transaction.payee
                        try:
                            payee = next(p for p in client.budget.be_payees
                                         if p.name == payee_name)
                        except StopIteration:
                            payee = Payee(name=payee_name)
                            client.budget.be_payees.append(payee)
                            delta += 1

                        # use ftid so we don't import duplicates
                        if not any(
                                ofx_transaction.fitid in transaction.memo for
                                transaction in client.budget.be_transactions
                                if transaction.memo is not None and
                                transaction.entities_account_id == account.id):
                            transaction = Transaction(
                                date=ofx_transaction.dtposted,
                                memo=ofx_transaction.memo + '    ' +
                                ofx_transaction.fitid,
                                imported_payee=payee_name,
                                entities_payee_id=payee.id,
                                imported_date=imported_date,
                                source="Imported",
                                check_number=ofx_transaction.checknum,
                                amount=float(ofx_transaction.trnamt),
                                entities_account_id=account.id)
                            client.budget.be_transactions.append(transaction)
                            delta += 1

    return delta
Пример #8
0
def get_stmts(ofxfile):
    tree = OFXTree()
    tree.parse(ofxfile)
    response = tree.convert()
    return response.statements
Пример #9
0
def do_ofximport(args, client = None):
    if client is None:
        client = clientfromargs(args)

    tree = OFXTree()
    tree.parse(args.ofxfile)
    response = tree.convert()
    stmts = response.statements

    accounts = client.budget.be_accounts
    accountvsnotes={account.note:account for account in accounts if account.note is not None}

    for stmt in stmts:
        key = stmt.account.bankid + ' ' + stmt.account.branchid + ' ' + stmt.account.acctid
        if all(key not in note for note in accountvsnotes):
            if len(accounts) == 0:
                print('No accounts available in this budget')
                exit(-1)

            # ask user input for which bank account this is, then save it into the account note in nYNAB
            account = client.select_account_ui()

            # Save the selection in the nYNAB account note
            addon = 'key[' + key + ']key'
            if account.note is not None:
                account.note += addon
            else:
                account.note = addon
            client.budget.be_accounts.modify(account)
            client.sync()
        else:
            for note in accountvsnotes:
                if key in note:
                    account=accountvsnotes[note]

                    imported_date=datetime.now().date()

                    for ofx_transaction in stmt.transactions:
                        payee_name = ofx_transaction.name if ofx_transaction.payee is None else ofx_transaction.payee
                        try:
                            payee = next(p for p in client.budget.be_payees if p.name == payee_name)
                        except StopIteration:
                            payee=Payee(
                                name=payee_name
                            )
                            client.budget.be_payees.append(payee)
                            client.sync()

                        # use ftid so we don't import duplicates
                        if not any(ofx_transaction.fitid in transaction.memo for transaction in client.budget.be_transactions if
                                   transaction.memo is not None and transaction.entities_account_id == account.id):

                            transaction = Transaction(
                                date=ofx_transaction.dtposted,
                                memo=ofx_transaction.memo + '    '+ofx_transaction.fitid,
                                imported_payee=payee_name,
                                entities_payee_id=payee.id,
                                imported_date=imported_date,
                                source="Imported",
                                check_number=ofx_transaction.checknum,
                                amount=float(ofx_transaction.trnamt),
                                entities_account_id=account.id
                            )
                            client.add_transaction(transaction)