def migrate_main(): print('migrate YNAB4 to pynYNAB') """Migrate a YNAB4 budget transaction history to nYNAB, using pyynab""" parser = configargparse.getArgumentParser('pynYNAB') parser.description = inspect.getdoc(migrate_main) parser.add_argument('budget', metavar='BudgetPath', type=str, help='The budget .ynab4 directory') args = parser.parse_args() test_common_args(args) budget_base_name = os.path.basename(args.budget) budget_path = os.path.dirname(args.budget) budget_name = re.match(r"(?P<budget_name>.*)~[A-Z0-9]{8}\.ynab4", budget_base_name).groupdict().get('budget_name') if args.budgetname is not None: budget_name = args.budgetname thisynab = YNAB(budget_path, budget_name) client = clientfromargs(args, reset=True) for ynab4_account in thisynab.accounts: account = Account( name=ynab4_account.name, account_type=ynab4_account.type.value, on_budget=ynab4_account.on_budget, sortable_index=random.randint(-50000, 50000), ) mindate = min([ ynab4transaction.date for ynab4transaction in thisynab.transactions if ynab4transaction.account == ynab4_account ]) client.add_account(account, 0, mindate) for master_category in thisynab.master_categories: master_entity = MasterCategory(name=master_category.name, sortable_index=random.randint( -50000, 50000)) client.budget.be_master_categories.append(master_entity) for category in master_category.categories: entity = Subcategory(name=category.name, entities_master_category_id=master_entity.id, sortable_index=random.randint(-50000, 50000)) client.budget.be_subcategories.append(entity) client.sync() for ynab4_payee in thisynab.payees: payee = Payee(name=ynab4_payee.name) client.budget.be_payees.append(payee) client.sync() for ynab4transaction in thisynab.transactions: transaction = Transaction() pass
def push_transactions(nordea_transactions, args): print("******** FETCHING DATA FROM YNAB ********") client = clientfromargs(args) client.sync() with client.session.no_autoflush: account = None for be_account in client.budget.be_accounts: if be_account.account_name == 'Checking': account = be_account break if not account: print("Could not find checking account") sys.exit() store_categories(client.budget.be_subcategories) new_transactions = [] new_payees = [] for nordea_transaction in nordea_transactions: subcategory_id = get_subcategory_for_transaction( nordea_transaction) payee = find_existing_payee(nordea_transaction.target, client.budget.be_payees) if payee is None: for new_payee in new_payees: if new_payee.name == nordea_transaction.target: payee = new_payee if payee is None: payee = create_new_payee(nordea_transaction.target) new_payees.append(payee) new_transaction = get_ynab_transaction(nordea_transaction, account.id, subcategory_id, payee.id) new_transactions.append(new_transaction) print("\n\n******** SENDING DATA TO YNAB ********\n\n") print("Pushing new payees:\n") for new_payee in new_payees: client.budget.be_payees.append(new_payee) client.push(len(new_payees)) print("Done...\n") print("Syncing...\n") client.sync() bar = Bar('Sending transactions', max=len(new_transactions)) for transaction in new_transactions: client.add_transaction(transaction) bar.next() bar.finish() client.sync() print("******** DONE ********")
import itertools import random from datetime import datetime from pynYNAB.Client import clientfromargs from pynYNAB.schema.budget import Transaction from pynYNAB.scripts.config import parser # used to ping the nYNAB API to check that the sync works args = parser.parse_known_args()[0] N = 2 client = clientfromargs(args) client.sync() account = next(acc for acc in client.budget.be_accounts) for _ in itertools.repeat(None, N): transaction = Transaction(cleared='Uncleared', date=datetime.now(), entities_account_id=account.id, amount=random.randrange(-10, 10)) client.add_transaction(transaction) client.sync()
def reload(self): parser = configargparse.getArgumentParser('pynYNAB') args = parser.parse_known_args()[0] self.client = clientfromargs(args)
def testiimport(self): parser = configargparse.getArgumentParser('pynYNAB') args = parser.parse_known_args()[0] args.ofxfile = os.path.join(tempfile.gettempdir(), 'data.ofx') args.logginglevel = 'debug' self.client = clientfromargs(args) content = """OFXHEADER:100 DATA:OFXSGML VERSION:102 SECURITY:NONE ENCODING:USASCII CHARSET:1252 COMPRESSION:NONE OLDFILEUID:NONE NEWFILEUID:NONE <OFX> <SIGNONMSGSRSV1> <SONRS> <STATUS> <CODE>0 <SEVERITY>INFO </STATUS> <DTSERVER>20130313133728 <LANGUAGE>FRA </SONRS> </SIGNONMSGSRSV1> <BANKMSGSRSV1> <STMTTRNRS> <TRNUID>29939615002 <STATUS> <CODE>0 <SEVERITY>INFO </STATUS> <STMTRS> <CURDEF>EUR <BANKACCTFROM> <BANKID>11706 <BRANCHID>41029 <ACCTID>29939615002 <ACCTTYPE>CHECKING </BANKACCTFROM> <BANKTRANLIST> <DTSTART>20130128000000 <DTEND>20130314235959 <STMTTRN> <TRNTYPE>CHECK <DTPOSTED>20130312 <TRNAMT>-491.09 <FITID>13071099780237330004 <CHECKNUM>0003445 <NAME>CHEQUE <MEMO>CHEQUE </STMTTRN> </BANKTRANLIST> <LEDGERBAL> <BALAMT>-6348.01 <DTASOF>20130312 </LEDGERBAL> <AVAILBAL> <BALAMT>-6348.01 <DTASOF>20130312 </AVAILBAL> </STMTRS> </STMTTRNRS> </BANKMSGSRSV1> </OFX>""" with open(args.ofxfile, 'w') as f: f.writelines(content) imported_date = datetime.now().date() testaccount = 'TEST' account = util_get_empty_account_by_name_if_doesnt_exist( self.client, testaccount) key = '11706 41029 29939615002' account.note = 'great note key[%s]key' % key self.client.sync() payee = util_add_payee_by_name_if_doesnt_exist(self.client, 'CHEQUE') amount = -491.09 transaction = Transaction(entities_account=account, date=datetime(year=2013, month=3, day=12).date(), entities_payee=payee, imported_payee=payee, source='Imported', check_number='0003445', memo='CHEQUE 13071099780237330004', amount=amount, imported_date=imported_date) do_ofximport(args) self.reload() self.assertIn( transaction, self.client.budget.be_transactions, msg='couldnt find an imported transaction after ofx import')
from memory_profiler import memory_usage, profile from sqlalchemy import create_engine from pynYNAB.Client import clientfromargs from pynYNAB.schema.budget import Transaction class Args(object): email = 'email' password = '******' nynabconnection = None budgetname = 'test budget' client = clientfromargs(Args(), sync=False) #client.session.bind.echo = True @profile def func(): print('test_sync') for i in range(0, 5000): client.budget.be_transactions.append(Transaction()) client.session.commit() client.budget.clear_changed_entities() print('Entities in the client: %i' % ( sum([len(getattr(client.budget, f)) for f in client.budget.listfields]) + sum([ len(getattr(client.catalog, f)) for f in client.catalog.listfields ])))
def setUp(self): args = parser.parse_known_args()[0] self.client = clientfromargs(args, sync=False) self.client.sync_catalog() self.client.select_budget(test_budget_name)
def do_csvimport(args, client=None): if client is None: client = clientfromargs(args) logger = get_logger(args) logger.debug('selected schema %s' % (args.schema,)) if os.path.exists(args.schema): schemafile = args.schema else: schemafile = os.path.join(schemas_dir, args.schema + '.json') if not os.path.exists(schemafile): logger.error('This schema doesn''t exist in csv_schemas') exit(-1) try: schema = SchemaModel(schemafile, case_insensitive_headers=True) with open(schemafile, 'r') as sf: schemacontent = json.load(sf) try: nheaders = schemacontent['nheaders'] except KeyError: nheaders = 1 except InvalidSchemaError: logger.error('Invalid CSV schema') raise logger.debug('schema headers %s' % schema.headers) if 'account' not in schema.headers and args.accountname is None: logger.error('This schema does not have an account column and no account name was provided') exit(-1) accounts = {x.account_name: x for x in client.budget.be_accounts} payees = {p.name: p for p in client.budget.be_payees} mastercategories_perid = {m.id: m for m in client.budget.be_master_categories} subcategories = {} for s in client.budget.be_subcategories: m = mastercategories_perid[s.entities_master_category_id] subcategories[m.name + ':' + s.name] = s def getaccount(accountname): try: logger.debug('searching for account %s' % accountname) return accounts[accountname] except KeyError: logger.error('Couldn''t find this account: %s' % accountname) exit(-1) def getpayee(payeename): try: logger.debug('searching for payee %s' % payeename) return payees[payeename] except KeyError: logger.debug('Couldn''t find this payee: %s' % payeename) payee = Payee(name=payeename) client.budget.be_payees.append(payee) return payee def getsubcategory(categoryname): try: logger.debug('searching for subcategory %s' % categoryname) return subcategories[categoryname] except KeyError: logger.debug('Couldn''t find this category: %s' % categoryname) exit(-1) entities_account_id = None if 'account' not in schema.headers: entities_account_id = getaccount(args.accountname).id amount = None if 'inflow' in schema.headers and 'outflow' in schema.headers: pass elif 'amount' in schema.headers: pass else: logger.error('This schema doesn''t provide an amount column or (inflow,outflow) columns') exit(-1) csvrow = namedtuple('CSVrow', field_names=schema.headers) transactions = [] imported_date = datetime.now().date() logger.debug('OK starting the import from %s ' % os.path.abspath(args.csvfile)) with open(args.csvfile, 'r') as inputfile: header = [] for i in range(0, nheaders): header.append(inputfile.readline()) for row in csv.reader(inputfile): if sys.version[0] == '2': row = [cell.decode('utf-8') for cell in row] if all(map(lambda x: x.strip() == '', row)): continue logger.debug('read line %s' % row) result = csvrow(*list(schema.convert_row(*row, fail_fast=True))) if 'account' in schema.headers: entities_account_id = getaccount(result.account).id if entities_account_id is None: logger.error( 'No account id, the account %s in the an account column was not recognized' % result.account) exit(-1) if 'inflow' in schema.headers and 'outflow' in schema.headers: amount = result.inflow - result.outflow elif 'amount' in schema.headers: amount = result.amount if 'category' in schema.headers and result.category: entities_subcategory_id = getsubcategory(result.category).id else: entities_subcategory_id = None if 'payee' in schema.headers: imported_payee = result.payee else: imported_payee = '' entities_payee_id = getpayee(imported_payee).id if 'memo' in schema.headers: memo = result.memo else: memo = '' transaction = Transaction( entities_account_id=entities_account_id, amount=amount, date=result.date, entities_payee_id=entities_payee_id, entities_subcategory_id=entities_subcategory_id, imported_date=imported_date, imported_payee=imported_payee, memo=memo, source="Imported" ) if args.import_duplicates or (not transaction in client.budget.be_transactions): logger.debug('Appending transaction %s ' % transaction.get_dict()) transactions.append(transaction) else: logger.debug('Duplicate transaction found %s ' % transaction.get_dict()) client.add_transactions(transactions)
import itertools import random from datetime import datetime from pynYNAB.Client import clientfromargs from pynYNAB.schema.budget import Transaction from pynYNAB.scripts.config import parser # used to ping the nYNAB API to check that the sync works args = parser.parse_known_args()[0] N=2 client = clientfromargs(args) client.sync() account = next(acc for acc in client.budget.be_accounts) for _ in itertools.repeat(None, N): transaction = Transaction( cleared='Uncleared', date=datetime.now(), entities_account_id=account.id, amount=random.randrange(-10,10) ) client.add_transaction(transaction) client.sync()
def test_sync(): print('test_live') args = parser.parse_known_args()[0] client = clientfromargs(args) client.sync()
def migrate_main(): print('migrate YNAB4 to pynYNAB') """Migrate a YNAB4 budget transaction history to nYNAB, using pyynab""" parser = configargparse.getArgumentParser('pynYNAB') parser.description=inspect.getdoc(migrate_main) parser.add_argument('budget', metavar='BudgetPath', type=str, help='The budget .ynab4 directory') args = parser.parse_args() test_common_args(args) budget_base_name=os.path.basename(args.budget) budget_path=os.path.dirname(args.budget) budget_name=re.match(r"(?P<budget_name>.*)~[A-Z0-9]{8}\.ynab4",budget_base_name).groupdict().get('budget_name') if args.budgetname is not None: budget_name=args.budgetname thisynab = YNAB(budget_path,budget_name) client = clientfromargs(args, reset=True) for ynab4_account in thisynab.accounts: account=Account( name=ynab4_account.name, account_type=ynab4_account.type.value, on_budget=ynab4_account.on_budget, sortable_index=random.randint(-50000, 50000), ) mindate=min([ynab4transaction.date for ynab4transaction in thisynab.transactions if ynab4transaction.account == ynab4_account]) client.add_account(account, 0, mindate) for master_category in thisynab.master_categories: master_entity = MasterCategory( name=master_category.name, sortable_index=random.randint(-50000, 50000) ) client.budget.be_master_categories.append(master_entity) for category in master_category.categories: entity = Subcategory( name=category.name, entities_master_category_id=master_entity.id, sortable_index=random.randint(-50000, 50000) ) client.budget.be_subcategories.append(entity) client.sync() for ynab4_payee in thisynab.payees: payee=Payee( name=ynab4_payee.name ) client.budget.be_payees.append(payee) client.sync() for ynab4transaction in thisynab.transactions: transaction=Transaction( ) pass
def reload(self): # parser = configargparse.getArgumentParser('pynYNAB') args = parser.parse_known_args()[0] self.client = clientfromargs(args)
def test_sync(): print('test_sync') args = parser.parse_known_args()[0] client = clientfromargs(args) client.sync()
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.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)
def do_csvimport(args,client=None): if client is None: client = clientfromargs(args) logger=get_logger(args) logger.debug('selected schema %s' % (args.schema,)) if os.path.exists(args.schema): schemafile = args.schema else: schemafile = os.path.join(schemas_dir, args.schema + '.json') if not os.path.exists(schemafile): logger.error('This schema doesn''t exist in csv_schemas') exit(-1) try: schema = SchemaModel(schemafile, case_insensitive_headers=True) with open(schemafile,'r') as sf: schemacontent = json.load(sf) try: nheaders = schemacontent['nheaders'] except KeyError: nheaders = 1 except InvalidSchemaError: logger.error('Invalid CSV schema') raise logger.debug('schema headers %s' % schema.headers) if 'account' not in schema.headers and args.accountname is None: logger.error('This schema does not have an account column and no account name was provided') exit(-1) accounts = {x.account_name: x for x in client.budget.be_accounts} payees = {p.name: p for p in client.budget.be_payees} mastercategories_perid = {m.id: m for m in client.budget.be_master_categories} subcategories = {} for s in client.budget.be_subcategories: m=mastercategories_perid[s.entities_master_category_id] subcategories[m.name+':'+s.name]=s def getaccount(accountname): try: logger.debug('searching for account %s' % accountname) return accounts[accountname] except KeyError: logger.error('Couldn''t find this account: %s' % accountname) exit(-1) def getpayee(payeename): try: logger.debug('searching for payee %s' % payeename) return payees[payeename] except KeyError: logger.debug('Couldn''t find this payee: %s' % payeename) payee=Payee(name=payeename) client.budget.be_payees.append(payee) return payee def getsubcategory(categoryname): try: logger.debug('searching for subcategory %s' % categoryname) return subcategories[categoryname] except KeyError: get_logger(args).debug('Couldn''t find this category: %s' % categoryname) exit(-1) if 'account' not in schema.headers: entities_account_id = getaccount(args.accountname).id if 'inflow' in schema.headers and 'outflow' in schema.headers: pass elif 'amount' in schema.headers: pass else: logger.error('This schema doesn''t provide an amount column or (inflow,outflow) columns') exit(-1) csvrow = namedtuple('CSVrow', field_names=schema.headers) transactions = [] imported_date=datetime.now().date() get_logger(args).debug('OK starting the import from %s '%os.path.abspath(args.csvfile)) with open(args.csvfile, 'r') as inputfile: header = inputfile.readline() for row in csv.reader(inputfile): if sys.version[0] == '2': row = [cell.decode('utf-8') for cell in row] get_logger(args).debug('read line %s' % row) result = csvrow(*list(schema.convert_row(*row, fail_fast=True))) if 'account' in schema.headers: entities_account_id = getaccount(result.account).id if 'inflow' in schema.headers and 'outflow' in schema.headers: amount = result.inflow - result.outflow elif 'amount' in schema.headers: amount = result.amount else: get_logger(args).error('Couldn''t find this account: %s' % args.accountname) exit(-1) if 'category' in schema.headers and result.category: entities_subcategory_id = getsubcategory(result.category).id else: entities_subcategory_id = None if 'payee' in schema.headers: imported_payee=result.payee else: imported_payee='' entities_payee_id = getpayee(imported_payee).id if 'memo' in schema.headers: memo=result.memo else: memo='' transaction=Transaction( entities_account_id=entities_account_id, amount=amount, date=result.date, entities_payee_id=entities_payee_id, entities_subcategory_id=entities_subcategory_id, imported_date=imported_date, imported_payee=imported_payee, memo=memo, source="Imported" ) if args.import_duplicates or (not client.budget.be_transactions.containsduplicate(transaction)): get_logger(args).debug('Appending transaction %s '%transaction.getdict()) transactions.append(transaction) else: get_logger(args).debug('Duplicate transaction found %s '%transaction.getdict()) client.add_transactions(transactions)
def testiimport(self): parser = configargparse.getArgumentParser('pynYNAB') args = parser.parse_known_args()[0] args.ofxfile = os.path.join(tempfile.gettempdir(), 'data.ofx') args.logginglevel = 'debug' self.client = clientfromargs(args) content = """OFXHEADER:100 DATA:OFXSGML VERSION:102 SECURITY:NONE ENCODING:USASCII CHARSET:1252 COMPRESSION:NONE OLDFILEUID:NONE NEWFILEUID:NONE <OFX> <SIGNONMSGSRSV1> <SONRS> <STATUS> <CODE>0 <SEVERITY>INFO </STATUS> <DTSERVER>20130313133728 <LANGUAGE>FRA </SONRS> </SIGNONMSGSRSV1> <BANKMSGSRSV1> <STMTTRNRS> <TRNUID>29939615002 <STATUS> <CODE>0 <SEVERITY>INFO </STATUS> <STMTRS> <CURDEF>EUR <BANKACCTFROM> <BANKID>11706 <BRANCHID>41029 <ACCTID>29939615002 <ACCTTYPE>CHECKING </BANKACCTFROM> <BANKTRANLIST> <DTSTART>20130128000000 <DTEND>20130314235959 <STMTTRN> <TRNTYPE>CHECK <DTPOSTED>20130312 <TRNAMT>-491.09 <FITID>13071099780237330004 <CHECKNUM>0003445 <NAME>CHEQUE <MEMO>CHEQUE </STMTTRN> </BANKTRANLIST> <LEDGERBAL> <BALAMT>-6348.01 <DTASOF>20130312 </LEDGERBAL> <AVAILBAL> <BALAMT>-6348.01 <DTASOF>20130312 </AVAILBAL> </STMTRS> </STMTTRNRS> </BANKMSGSRSV1> </OFX>""" with open(args.ofxfile, 'w') as f: f.writelines(content) imported_date = datetime.now().date() testaccount = 'TEST' account = self.util_get_empty_account_by_name_if_doesnt_exist( testaccount) key = '11706 41029 29939615002' account.note = 'great note key[%s]key' % key self.client.budget.be_accounts.modify(account) self.client.sync() def getTr(date, payee, amount, memo, account): return Transaction( entities_account_id=account.id, date=date, entities_payee_id=self.util_add_payee_by_name_if_doesnt_exist( payee).id, imported_payee=payee, source='Imported', check_number='0003445', memo=memo, amount=amount, cash_amount=amount, imported_date=imported_date) Transactions = [ getTr( datetime(year=2013, month=3, day=12).date(), 'CHEQUE', -491.09, 'CHEQUE 13071099780237330004', account), ] do_ofximport(args) self.reload() for tr in Transactions: print('Should have been imported:') print(json.dumps(tr, cls=ComplexEncoder)) print('Found in the register:') print( json.dumps([ tr2 for tr2 in self.client.budget.be_transactions if tr2.amount == tr.amount ], cls=ComplexEncoder)) self.assertTrue( self.client.budget.be_transactions.containsduplicate(tr), msg= 'couldnt find a transaction with the same hash after ofx import' )
def testiimport(self): parser = configargparse.getArgumentParser('pynYNAB') args=parser.parse_known_args()[0] args.ofxfile = os.path.join(tempfile.gettempdir(),'data.ofx') args.logginglevel= 'debug' self.client=clientfromargs(args) content="""OFXHEADER:100 DATA:OFXSGML VERSION:102 SECURITY:NONE ENCODING:USASCII CHARSET:1252 COMPRESSION:NONE OLDFILEUID:NONE NEWFILEUID:NONE <OFX> <SIGNONMSGSRSV1> <SONRS> <STATUS> <CODE>0 <SEVERITY>INFO </STATUS> <DTSERVER>20130313133728 <LANGUAGE>FRA </SONRS> </SIGNONMSGSRSV1> <BANKMSGSRSV1> <STMTTRNRS> <TRNUID>29939615002 <STATUS> <CODE>0 <SEVERITY>INFO </STATUS> <STMTRS> <CURDEF>EUR <BANKACCTFROM> <BANKID>11706 <BRANCHID>41029 <ACCTID>29939615002 <ACCTTYPE>CHECKING </BANKACCTFROM> <BANKTRANLIST> <DTSTART>20130128000000 <DTEND>20130314235959 <STMTTRN> <TRNTYPE>CHECK <DTPOSTED>20130312 <TRNAMT>-491.09 <FITID>13071099780237330004 <CHECKNUM>0003445 <NAME>CHEQUE <MEMO>CHEQUE </STMTTRN> </BANKTRANLIST> <LEDGERBAL> <BALAMT>-6348.01 <DTASOF>20130312 </LEDGERBAL> <AVAILBAL> <BALAMT>-6348.01 <DTASOF>20130312 </AVAILBAL> </STMTRS> </STMTTRNRS> </BANKMSGSRSV1> </OFX>""" with open(args.ofxfile,'w') as f: f.writelines(content) imported_date=datetime.now().date() testaccount='TEST' account=self.util_get_empty_account_by_name_if_doesnt_exist(testaccount) key = '11706 41029 29939615002' account.note='great note key[%s]key' %key self.client.budget.be_accounts.modify(account) self.client.sync() def getTr(date,payee,amount,memo,account): return Transaction( entities_account_id=account.id, date=date, entities_payee_id=self.util_add_payee_by_name_if_doesnt_exist(payee).id, imported_payee=payee, source='Imported', check_number='0003445', memo=memo, amount=amount, cash_amount=amount, imported_date=imported_date ) Transactions = [ getTr(datetime(year=2013, month=3, day=12).date(),'CHEQUE',-491.09,'CHEQUE 13071099780237330004',account), ] do_ofximport(args) self.reload() for tr in Transactions: print('Should have been imported:') print(json.dumps(tr,cls=ComplexEncoder)) print('Found in the register:') print(json.dumps([tr2 for tr2 in self.client.budget.be_transactions if tr2.amount==tr.amount],cls=ComplexEncoder)) self.assertTrue(self.client.budget.be_transactions.containsduplicate(tr), msg='couldnt find a transaction with the same hash after ofx import')
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)