|
- import ynab
- import logging
-
- def select_budget(api_client):
- budgets_api = ynab.BudgetsApi(api_client)
- budgets = budgets_api.get_budgets().data.budgets
-
- if len(budgets) == 1:
- budget = budgets[0]
- else:
- budget_options = ", ".join(
- [
- f"{i}: {budget.name}"
- for i, budget in enumerate(budgets)
- if 'Archived' not in budget.name
- ]
- )
- budget_index = input(f"Which budget? ({budget_options}) ")
- budget = budgets[int(budget_index)]
-
- return budget
-
-
- def select_account(api_client, budget):
- accounts_api = ynab.AccountsApi(api_client)
- accounts = accounts_api.get_accounts(budget.id).data.accounts
-
- if len(accounts) == 1:
- account = accounts[0]
- else:
- account_options = ", ".join(
- [
- f"{i}: {account.name}"
- for i, account in enumerate(accounts)
- if not account.closed
- ]
- )
- account_index = input(f"Which account? ({account_options}) ")
- account = accounts[int(account_index)]
-
- return account
-
-
- def add_transactions(api_client, budget, account, data):
- transaction_api = ynab.TransactionsApi(api_client)
-
- transactions = []
- skipped = 0
- amount_factor = 1000
-
- start_date = data['Date'].min()
-
- logging.debug(f"Looking for existing transactions since {start_date}")
- existing_transactions = transaction_api.get_transactions_by_account(budget.id, account.id, since_date=start_date).data.transactions
-
- for i, row in data.iterrows():
- try:
- date = row['Date'].date()
- import_id = f"IMPORT:{date}:{row['Inflow']}:{row['Outflow']}:{i}"
- amount = int((row['Inflow'] - row['Outflow']) * amount_factor)
-
- for existing in existing_transactions:
- skip = False
- if existing.import_id == import_id:
- skip = True
- elif existing.var_date == date and existing.amount == amount and (existing.cleared == 'cleared') == row['Cleared']:
- if (row['Payee'].lower() in existing.payee_name.lower()) or (row['Memo'] in existing.memo.lower()):
- skip = True
- else:
- skip = input(f"On {date}, is {row['Payee']} {row['Memo']} the same as {existing.payee_name} {existing.memo} (y/N)").lower() == 'y'
-
- if skip:
- skipped += 1
- break
- else:
- transaction = ynab.models.new_transaction.NewTransaction(
- account_id = account.id,
- var_date = date,
- amount = amount,
- payee_name = row['Payee'],
- memo = row['Memo'],
- cleared = 'cleared' if row['Cleared'] else 'uncleared',
- import_id = import_id if row['Cleared'] else None,
- )
-
- transactions.append(transaction)
- except:
- logging.error(f"Could not format transaction {row} for YNAB")
- raise
-
- if skipped > 0:
- print(f"Skipped {skipped} existing transactions")
-
- if len(transactions) == 0:
- print("No transactions to import")
- return
-
- print(f"Adding {len(transactions)} transactions to account {account.name} in budget {budget.name}:")
- print(f"{'Date':<12}{'Payee':<50}{'Memo':<50}{'Amount':10}{'Status':<11}{'Import ID'}")
- for transaction in transactions:
- print(f"{transaction.var_date} {transaction.payee_name:<50}{transaction.memo:<50}{transaction.amount/amount_factor:<10}{transaction.cleared:<11}{transaction.import_id}")
-
- confirmed = input(f"Import transactions to {budget.name} - {account.name}? (y/N) ")
- if confirmed.lower() != 'y':
- print("Stopping processing")
- exit(1)
-
- transaction_data = ynab.PostTransactionsWrapper()
- transaction_data.transactions = transactions
- response = transaction_api.create_transaction(budget.id, transaction_data)
-
- logging.debug(f"Created transactions")
-
-
- def write_to_api(file_path, bank_name, ynab_data, api_token):
- configuration = ynab.Configuration(
- access_token = api_token
- )
-
- with ynab.ApiClient(configuration) as api_client:
- print(f"Pushing transactions from {bank_name} ({file_path}) to YNAB")
- budget = select_budget(api_client)
- account = select_account(api_client, budget)
- add_transactions(api_client, budget, account, ynab_data)
|