From ed2d42d31c9d0c3c77e7fa75090d571453874eb8 Mon Sep 17 00:00:00 2001 From: halorrr Date: Tue, 30 Jan 2024 20:23:54 -0500 Subject: [PATCH] add better typing to recurring field on budget returns --- lib/lunchmoney/budget/budget.rb | 5 +- lib/lunchmoney/budget/budget_calls.rb | 4 ++ .../recurring_expense.rb | 10 +-- .../recurring_expense_base.rb | 29 +++++++++ .../recurring_expense_calls.rb | 3 +- test/cassettes/budget/budgets_success.yml | 63 ++++++++++--------- 6 files changed, 77 insertions(+), 37 deletions(-) rename lib/lunchmoney/recurring_expenses/{ => recurring_expense}/recurring_expense.rb (85%) create mode 100644 lib/lunchmoney/recurring_expenses/recurring_expense/recurring_expense_base.rb diff --git a/lib/lunchmoney/budget/budget.rb b/lib/lunchmoney/budget/budget.rb index bd4fb08..a8e2c16 100644 --- a/lib/lunchmoney/budget/budget.rb +++ b/lib/lunchmoney/budget/budget.rb @@ -33,6 +33,9 @@ class Budget < LunchMoney::DataObject sig { returns(T.nilable(T::Hash[Symbol, LunchMoney::Config])) } attr_accessor :config + sig { returns(T.nilable({ list: T::Array[LunchMoney::RecurringExpense] })) } + attr_accessor :recurring + sig do params( is_income: T::Boolean, @@ -47,7 +50,7 @@ class Budget < LunchMoney::DataObject group_id: T.nilable(Integer), is_group: T.nilable(T::Boolean), config: T.nilable(T::Hash[Symbol, LunchMoney::Config]), - recurring: T.untyped, # TODO: Better type this field + recurring: T.nilable({ list: T::Array[LunchMoney::RecurringExpense] }), ).void end def initialize(is_income:, exclude_from_budget:, exclude_from_totals:, data:, category_name:, order:, archived:, diff --git a/lib/lunchmoney/budget/budget_calls.rb b/lib/lunchmoney/budget/budget_calls.rb index 4e66cd5..156b144 100644 --- a/lib/lunchmoney/budget/budget_calls.rb +++ b/lib/lunchmoney/budget/budget_calls.rb @@ -35,6 +35,10 @@ def budgets(start_date:, end_date:, currency: nil) end end + if budget[:recurring] + budget[:recurring][:list]&.map! { |recurring| LunchMoney::RecurringExpenseBase.new(**recurring) } + end + LunchMoney::Budget.new(**budget) end end diff --git a/lib/lunchmoney/recurring_expenses/recurring_expense.rb b/lib/lunchmoney/recurring_expenses/recurring_expense/recurring_expense.rb similarity index 85% rename from lib/lunchmoney/recurring_expenses/recurring_expense.rb rename to lib/lunchmoney/recurring_expenses/recurring_expense/recurring_expense.rb index 4b2c9f8..ad1f34f 100644 --- a/lib/lunchmoney/recurring_expenses/recurring_expense.rb +++ b/lib/lunchmoney/recurring_expenses/recurring_expense/recurring_expense.rb @@ -3,7 +3,7 @@ module LunchMoney # https://lunchmoney.dev/#recurring-expenses-object - class RecurringExpense < LunchMoney::DataObject + class RecurringExpense < RecurringExpenseBase sig { returns(Integer) } attr_accessor :id @@ -11,7 +11,7 @@ class RecurringExpense < LunchMoney::DataObject attr_accessor :start_date, :end_date, :description, :original_name sig { returns(String) } - attr_accessor :cadence, :payee, :currency, :billing_date, :type, :source, :amount, :created_at + attr_accessor :cadence, :billing_date, :type, :source, :created_at sig { returns(T.nilable(Integer)) } attr_accessor :plaid_account_id, :asset_id, :transaction_id, :category_id @@ -35,12 +35,13 @@ class RecurringExpense < LunchMoney::DataObject plaid_account_id: T.nilable(Integer), asset_id: T.nilable(Integer), transaction_id: T.nilable(Integer), + to_base: T.nilable(Integer), ).void end def initialize(cadence:, payee:, amount:, currency:, billing_date:, type:, source:, id:, created_at:, category_id: nil, start_date: nil, end_date: nil, description: nil, original_name: nil, plaid_account_id: nil, - asset_id: nil, transaction_id: nil) - super() + asset_id: nil, transaction_id: nil, to_base: nil) + super(payee:, amount:, currency:, to_base:) @cadence = cadence @payee = payee @amount = amount @@ -58,6 +59,7 @@ def initialize(cadence:, payee:, amount:, currency:, billing_date:, type:, sourc @plaid_account_id = plaid_account_id @asset_id = asset_id @transaction_id = transaction_id + @to_base = to_base end end end diff --git a/lib/lunchmoney/recurring_expenses/recurring_expense/recurring_expense_base.rb b/lib/lunchmoney/recurring_expenses/recurring_expense/recurring_expense_base.rb new file mode 100644 index 0000000..6ca1ac8 --- /dev/null +++ b/lib/lunchmoney/recurring_expenses/recurring_expense/recurring_expense_base.rb @@ -0,0 +1,29 @@ +# typed: strict +# frozen_string_literal: true + +module LunchMoney + # https://lunchmoney.dev/#recurring-expenses-object + class RecurringExpenseBase < LunchMoney::DataObject + sig { returns(String) } + attr_accessor :payee, :currency, :amount + + sig { returns(T.nilable(Integer)) } + attr_accessor :to_base + + sig do + params( + payee: String, + amount: String, + currency: String, + to_base: T.nilable(Integer), + ).void + end + def initialize(payee:, amount:, currency:, to_base:) + super() + @payee = payee + @amount = amount + @currency = currency + @to_base = to_base + end + end +end diff --git a/lib/lunchmoney/recurring_expenses/recurring_expense_calls.rb b/lib/lunchmoney/recurring_expenses/recurring_expense_calls.rb index 43da4a1..db0e64c 100644 --- a/lib/lunchmoney/recurring_expenses/recurring_expense_calls.rb +++ b/lib/lunchmoney/recurring_expenses/recurring_expense_calls.rb @@ -1,7 +1,8 @@ # typed: strict # frozen_string_literal: true -require_relative "recurring_expense" +require_relative "recurring_expense/recurring_expense_base" +require_relative "recurring_expense/recurring_expense" module LunchMoney # https://lunchmoney.dev/#recurring-expenses diff --git a/test/cassettes/budget/budgets_success.yml b/test/cassettes/budget/budgets_success.yml index 4fbb0a6..fe52e92 100644 --- a/test/cassettes/budget/budgets_success.yml +++ b/test/cassettes/budget/budgets_success.yml @@ -23,9 +23,9 @@ http_interactions: Server: - Cowboy Report-To: - - '{"group":"heroku-nel","max_age":3600,"endpoints":[{"url":"https://nel.heroku.com/reports?ts=1706644112&sid=1b10b0ff-8a76-4548-befa-353fc6c6c045&s=C6x3RJy9MGfKIt6YUMvrjSSrk5vnyTWw%2BbV%2FZBRms3U%3D"}]}' + - '{"group":"heroku-nel","max_age":3600,"endpoints":[{"url":"https://nel.heroku.com/reports?ts=1706662978&sid=1b10b0ff-8a76-4548-befa-353fc6c6c045&s=HddDbbWTKEry8ryvt2DAzcArpNl435zdxcHRQdvhgTU%3D"}]}' Reporting-Endpoints: - - heroku-nel=https://nel.heroku.com/reports?ts=1706644112&sid=1b10b0ff-8a76-4548-befa-353fc6c6c045&s=C6x3RJy9MGfKIt6YUMvrjSSrk5vnyTWw%2BbV%2FZBRms3U%3D + - heroku-nel=https://nel.heroku.com/reports?ts=1706662978&sid=1b10b0ff-8a76-4548-befa-353fc6c6c045&s=HddDbbWTKEry8ryvt2DAzcArpNl435zdxcHRQdvhgTU%3D Nel: - '{"report_to":"heroku-nel","max_age":3600,"success_fraction":0.005,"failure_fraction":0.05,"response_headers":["Via"]}' Connection: @@ -39,9 +39,9 @@ http_interactions: Content-Type: - application/json; charset=utf-8 Etag: - - W/"5704-rHjgkaaDMCnHT1Y46LJFYLoov2c" + - W/"5924-zP4jOVsOMIWb6R1Nl72IcrGATFo" Date: - - Tue, 30 Jan 2024 19:48:33 GMT + - Wed, 31 Jan 2024 01:02:58 GMT Transfer-Encoding: - chunked Via: @@ -54,44 +54,45 @@ http_interactions: Purchases","category_id":777024,"category_group_name":"Entertainment","group_id":777021,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":12,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Bars, Clubs","category_id":777025,"category_group_name":"Entertainment","group_id":777021,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":13,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Books","category_id":777026,"category_group_name":"Entertainment","group_id":777021,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":14,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Gaming","category_id":777028,"category_group_name":"Entertainment","group_id":777021,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":15,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Media","category_id":777029,"category_group_name":"Entertainment","group_id":777021,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":16,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Misc. Entertainment","category_id":777030,"category_group_name":"Entertainment","group_id":777021,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":17,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Movies","category_id":777031,"category_group_name":"Entertainment","group_id":777021,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":18,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Food","category_id":777038,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":19,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Groceries","category_id":777040,"category_group_name":"Food","group_id":777038,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":20,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Restaurants","category_id":777041,"category_group_name":"Food","group_id":777038,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":21,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"General - Spending","category_id":777042,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":22,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Freelance + Spending","category_id":777042,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":22,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0},"2024-01-01":{"num_transactions":0,"spending_to_base":0,"budget_to_base":100,"budget_amount":100,"budget_currency":"cad"}},"config":null},{"category_name":"Freelance Overhead","category_id":777045,"category_group_name":"General Spending","group_id":777042,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":23,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Clothing","category_id":777044,"category_group_name":"General Spending","group_id":777042,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":24,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Amazon","category_id":777043,"category_group_name":"General Spending","group_id":777042,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":25,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Gifts","category_id":777046,"category_group_name":"General Spending","group_id":777042,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":26,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Hardware Stores","category_id":777047,"category_group_name":"General Spending","group_id":777042,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":27,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Moving","category_id":777048,"category_group_name":"General Spending","group_id":777042,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":28,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Shopping","category_id":777049,"category_group_name":"General - Spending","group_id":777042,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":29,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null,"recurring":{"list":[{"payee":"Expense + Spending","group_id":777042,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":29,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0},"2024-01-01":{"num_transactions":0,"spending_to_base":0,"budget_to_base":100,"budget_amount":100,"budget_currency":"cad"}},"config":null,"recurring":{"list":[{"payee":"Expense that Recurs","amount":"250.0000","currency":"cad","to_base":250}]}},{"category_name":"Household","category_id":777050,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":30,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0,"budget_to_base":400.99,"budget_amount":400.99,"budget_currency":"cad"}},"config":null},{"category_name":"Auto Maintenance","category_id":777051,"category_group_name":"Household","group_id":777050,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":31,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Auto Payment","category_id":777052,"category_group_name":"Household","group_id":777050,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":32,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Furniture / Appliances","category_id":777053,"category_group_name":"Household","group_id":777050,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":33,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Government / Legal","category_id":777054,"category_group_name":"Household","group_id":777050,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":34,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Home","category_id":777055,"category_group_name":"Household","group_id":777050,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":35,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Insurance","category_id":777056,"category_group_name":"Household","group_id":777050,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":36,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Property - Costs","category_id":777057,"category_group_name":"Household","group_id":777050,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":37,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Utilities","category_id":777058,"category_group_name":"Household","group_id":777050,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":38,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Personal - Care Stuff","category_id":777070,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":50,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Skincare + Costs","category_id":777057,"category_group_name":"Household","group_id":777050,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":37,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Utilities","category_id":777058,"category_group_name":"Household","group_id":777050,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":38,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"New + Category Test","category_id":784589,"category_group_name":null,"group_id":null,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":50,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Personal + Care Stuff","category_id":777070,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":51,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Skincare & Beauty Products","category_id":777082,"category_group_name":"Personal - Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":51,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Makeup","category_id":777074,"category_group_name":"Personal - Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":52,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Dental","category_id":777071,"category_group_name":"Personal - Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":53,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Fitness","category_id":777072,"category_group_name":"Personal - Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":54,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Hair","category_id":777073,"category_group_name":"Personal - Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":55,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Medication","category_id":777075,"category_group_name":"Personal - Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":56,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Misc + Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":52,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Makeup","category_id":777074,"category_group_name":"Personal + Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":53,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Dental","category_id":777071,"category_group_name":"Personal + Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":54,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Fitness","category_id":777072,"category_group_name":"Personal + Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":55,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Hair","category_id":777073,"category_group_name":"Personal + Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":56,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Medication","category_id":777075,"category_group_name":"Personal + Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":57,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Misc Beauty Services","category_id":777076,"category_group_name":"Personal Care - Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":57,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Misc. - Personal Care","category_id":777077,"category_group_name":"Personal Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":58,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Paramedical","category_id":777078,"category_group_name":"Personal - Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":59,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Personal - Care","category_id":777079,"category_group_name":"Personal Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":60,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Pharmacies","category_id":777080,"category_group_name":"Personal - Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":61,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Self-improvement","category_id":777081,"category_group_name":"Personal - Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":62,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Vision","category_id":777083,"category_group_name":"Personal - Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":63,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Pets","category_id":777084,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":64,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Misc. - Pet","category_id":777085,"category_group_name":"Pets","group_id":777084,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":65,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Pet - Services","category_id":777086,"category_group_name":"Pets","group_id":777084,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":66,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Veterinary","category_id":777087,"category_group_name":"Pets","group_id":777084,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":67,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"School","category_id":777088,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":68,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Childcare","category_id":777089,"category_group_name":"School","group_id":777088,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":69,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"School - / Daycare Photos","category_id":777091,"category_group_name":"School","group_id":777088,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":70,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"School - Activities","category_id":777090,"category_group_name":"School","group_id":777088,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":71,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Subscriptions","category_id":777092,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":72,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Domains","category_id":777093,"category_group_name":"Subscriptions","group_id":777092,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":73,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Media - Subscriptions","category_id":777094,"category_group_name":"Subscriptions","group_id":777092,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":74,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Memberships","category_id":777095,"category_group_name":"Subscriptions","group_id":777092,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":75,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Recurring - Apps","category_id":777096,"category_group_name":"Subscriptions","group_id":777092,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":76,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Transportation","category_id":777097,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":77,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Gasoline","category_id":777098,"category_group_name":"Transportation","group_id":777097,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":78,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Misc. - Transportation","category_id":777099,"category_group_name":"Transportation","group_id":777097,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":79,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Parking","category_id":777100,"category_group_name":"Transportation","group_id":777097,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":80,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Public","category_id":777101,"category_group_name":"Transportation","group_id":777097,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":81,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Ridesharing","category_id":777102,"category_group_name":"Transportation","group_id":777097,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":82,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Travel","category_id":777103,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":83,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Personal - Travel","category_id":777104,"category_group_name":"Travel","group_id":777103,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":84,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Work - Travel","category_id":777105,"category_group_name":"Travel","group_id":777103,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":85,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null}]' - recorded_at: Tue, 30 Jan 2024 19:48:33 GMT + Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":58,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Misc. + Personal Care","category_id":777077,"category_group_name":"Personal Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":59,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Paramedical","category_id":777078,"category_group_name":"Personal + Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":60,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Personal + Care","category_id":777079,"category_group_name":"Personal Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":61,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Pharmacies","category_id":777080,"category_group_name":"Personal + Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":62,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Self-improvement","category_id":777081,"category_group_name":"Personal + Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":63,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Vision","category_id":777083,"category_group_name":"Personal + Care Stuff","group_id":777070,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":64,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Pets","category_id":777084,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":65,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Misc. + Pet","category_id":777085,"category_group_name":"Pets","group_id":777084,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":66,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Pet + Services","category_id":777086,"category_group_name":"Pets","group_id":777084,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":67,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Veterinary","category_id":777087,"category_group_name":"Pets","group_id":777084,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":68,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"School","category_id":777088,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":69,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Childcare","category_id":777089,"category_group_name":"School","group_id":777088,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":70,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"School + / Daycare Photos","category_id":777091,"category_group_name":"School","group_id":777088,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":71,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"School + Activities","category_id":777090,"category_group_name":"School","group_id":777088,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":72,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Subscriptions","category_id":777092,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":73,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Domains","category_id":777093,"category_group_name":"Subscriptions","group_id":777092,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":74,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Media + Subscriptions","category_id":777094,"category_group_name":"Subscriptions","group_id":777092,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":75,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Memberships","category_id":777095,"category_group_name":"Subscriptions","group_id":777092,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":76,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Recurring + Apps","category_id":777096,"category_group_name":"Subscriptions","group_id":777092,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":77,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Transportation","category_id":777097,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":78,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Gasoline","category_id":777098,"category_group_name":"Transportation","group_id":777097,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":79,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Misc. + Transportation","category_id":777099,"category_group_name":"Transportation","group_id":777097,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":80,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Parking","category_id":777100,"category_group_name":"Transportation","group_id":777097,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":81,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Public","category_id":777101,"category_group_name":"Transportation","group_id":777097,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":82,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Ridesharing","category_id":777102,"category_group_name":"Transportation","group_id":777097,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":83,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Travel","category_id":777103,"category_group_name":null,"group_id":null,"is_group":true,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":84,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Personal + Travel","category_id":777104,"category_group_name":"Travel","group_id":777103,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":85,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null},{"category_name":"Work + Travel","category_id":777105,"category_group_name":"Travel","group_id":777103,"is_group":null,"is_income":false,"exclude_from_budget":false,"exclude_from_totals":false,"order":86,"archived":false,"data":{"2023-01-01":{"num_transactions":0,"spending_to_base":0}},"config":null}]' + recorded_at: Wed, 31 Jan 2024 01:02:58 GMT recorded_with: VCR 6.2.0