A script to convert CSV exported from Sparebanken Sør to a format YNAB can import
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

125 行
4.5KB

  1. import ynab
  2. import logging
  3. def select_budget(api_client):
  4. budgets_api = ynab.BudgetsApi(api_client)
  5. budgets = budgets_api.get_budgets().data.budgets
  6. if len(budgets) == 1:
  7. budget = budgets[0]
  8. else:
  9. budget_options = ", ".join(
  10. [
  11. f"{i}: {budget.name}"
  12. for i, budget in enumerate(budgets)
  13. if 'Archived' not in budget.name
  14. ]
  15. )
  16. budget_index = input(f"Which budget? ({budget_options}) ")
  17. budget = budgets[int(budget_index)]
  18. return budget
  19. def select_account(api_client, budget):
  20. accounts_api = ynab.AccountsApi(api_client)
  21. accounts = accounts_api.get_accounts(budget.id).data.accounts
  22. if len(accounts) == 1:
  23. account = accounts[0]
  24. else:
  25. account_options = ", ".join(
  26. [
  27. f"{i}: {account.name}"
  28. for i, account in enumerate(accounts)
  29. if not account.closed
  30. ]
  31. )
  32. account_index = input(f"Which account? ({account_options}) ")
  33. account = accounts[int(account_index)]
  34. return account
  35. def add_transactions(api_client, budget, account, data):
  36. transaction_api = ynab.TransactionsApi(api_client)
  37. transactions = []
  38. skipped = 0
  39. amount_factor = 1000
  40. start_date = data['Date'].min()
  41. logging.debug(f"Looking for existing transactions since {start_date}")
  42. existing_transactions = transaction_api.get_transactions_by_account(budget.id, account.id, since_date=start_date).data.transactions
  43. for i, row in data.iterrows():
  44. try:
  45. date = row['Date'].date()
  46. import_id = f"IMPORT:{date}:{row['Inflow']}:{row['Outflow']}:{i}"
  47. amount = int((row['Inflow'] - row['Outflow']) * amount_factor)
  48. for existing in existing_transactions:
  49. skip = False
  50. if existing.import_id == import_id:
  51. skip = True
  52. elif existing.var_date == date and existing.amount == amount and (existing.cleared == 'cleared') == row['Cleared']:
  53. if (row['Payee'].lower() in existing.payee_name.lower()) or (row['Memo'] in existing.memo.lower()):
  54. skip = True
  55. else:
  56. skip = input(f"On {date}, is {row['Payee']} {row['Memo']} the same as {existing.payee_name} {existing.memo} (y/N)").lower() == 'y'
  57. if skip:
  58. skipped += 1
  59. break
  60. else:
  61. transaction = ynab.models.new_transaction.NewTransaction(
  62. account_id = account.id,
  63. var_date = date,
  64. amount = amount,
  65. payee_name = row['Payee'],
  66. memo = row['Memo'],
  67. cleared = 'cleared' if row['Cleared'] else 'uncleared',
  68. import_id = import_id if row['Cleared'] else None,
  69. )
  70. transactions.append(transaction)
  71. except:
  72. logging.error(f"Could not format transaction {row} for YNAB")
  73. raise
  74. if skipped > 0:
  75. print(f"Skipped {skipped} existing transactions")
  76. if len(transactions) == 0:
  77. print("No transactions to import")
  78. return
  79. print(f"Adding {len(transactions)} transactions to account {account.name} in budget {budget.name}:")
  80. print(f"{'Date':<12}{'Payee':<50}{'Memo':<50}{'Amount':10}{'Status':<11}{'Import ID'}")
  81. for transaction in transactions:
  82. print(f"{transaction.var_date} {transaction.payee_name:<50}{transaction.memo:<50}{transaction.amount/amount_factor:<10}{transaction.cleared:<11}{transaction.import_id}")
  83. confirmed = input(f"Import transactions to {budget.name} - {account.name}? (y/N) ")
  84. if confirmed.lower() != 'y':
  85. print("Stopping processing")
  86. exit(1)
  87. transaction_data = ynab.PostTransactionsWrapper()
  88. transaction_data.transactions = transactions
  89. response = transaction_api.create_transaction(budget.id, transaction_data)
  90. logging.debug(f"Created transactions")
  91. def write_to_api(file_path, bank_name, ynab_data, api_token):
  92. configuration = ynab.Configuration(
  93. access_token = api_token
  94. )
  95. with ynab.ApiClient(configuration) as api_client:
  96. print(f"Pushing transactions from {bank_name} ({file_path}) to YNAB")
  97. budget = select_budget(api_client)
  98. account = select_account(api_client, budget)
  99. add_transactions(api_client, budget, account, ynab_data)