lunchmoney 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.github/dependabot.yml +18 -0
  4. data/.github/workflows/build_and_publish_yard_docs.yml +47 -0
  5. data/.github/workflows/ci.yml +58 -0
  6. data/.github/workflows/dependabot-rbi-updater.yml +43 -0
  7. data/.github/workflows/publish_gem.yml +31 -0
  8. data/.gitignore +62 -0
  9. data/.rubocop.yml +45 -0
  10. data/.ruby-version +1 -0
  11. data/.toys/.toys.rb +10 -0
  12. data/.toys/ci.rb +22 -0
  13. data/.toys/rbi.rb +60 -0
  14. data/.toys/rubocop.rb +10 -0
  15. data/.toys/spoom.rb +15 -0
  16. data/.toys/typecheck.rb +5 -0
  17. data/.yardopts +2 -0
  18. data/Appraisals +22 -0
  19. data/Gemfile +25 -0
  20. data/Gemfile.lock +174 -0
  21. data/LICENSE +21 -0
  22. data/README.md +57 -0
  23. data/bin/console +16 -0
  24. data/bin/rubocop +27 -0
  25. data/bin/setup +8 -0
  26. data/bin/spoom +27 -0
  27. data/bin/srb +27 -0
  28. data/bin/tapioca +27 -0
  29. data/bin/toys +27 -0
  30. data/bin/yard +27 -0
  31. data/lib/lunchmoney/api.rb +147 -0
  32. data/lib/lunchmoney/api_call.rb +109 -0
  33. data/lib/lunchmoney/assets/asset.rb +89 -0
  34. data/lib/lunchmoney/assets/asset_calls.rb +96 -0
  35. data/lib/lunchmoney/budget/budget.rb +74 -0
  36. data/lib/lunchmoney/budget/budget_calls.rb +82 -0
  37. data/lib/lunchmoney/budget/config.rb +38 -0
  38. data/lib/lunchmoney/budget/data.rb +42 -0
  39. data/lib/lunchmoney/categories/category/category.rb +52 -0
  40. data/lib/lunchmoney/categories/category/child_category.rb +42 -0
  41. data/lib/lunchmoney/categories/category_calls.rb +195 -0
  42. data/lib/lunchmoney/configuration.rb +26 -0
  43. data/lib/lunchmoney/crypto/crypto/crypto.rb +43 -0
  44. data/lib/lunchmoney/crypto/crypto/crypto_base.rb +65 -0
  45. data/lib/lunchmoney/crypto/crypto_calls.rb +49 -0
  46. data/lib/lunchmoney/data_object.rb +25 -0
  47. data/lib/lunchmoney/errors.rb +19 -0
  48. data/lib/lunchmoney/exceptions.rb +19 -0
  49. data/lib/lunchmoney/plaid_accounts/plaid_account.rb +73 -0
  50. data/lib/lunchmoney/plaid_accounts/plaid_account_calls.rb +38 -0
  51. data/lib/lunchmoney/recurring_expenses/recurring_expense/recurring_expense.rb +65 -0
  52. data/lib/lunchmoney/recurring_expenses/recurring_expense/recurring_expense_base.rb +29 -0
  53. data/lib/lunchmoney/recurring_expenses/recurring_expense_calls.rb +28 -0
  54. data/lib/lunchmoney/tags/tag/tag.rb +20 -0
  55. data/lib/lunchmoney/tags/tag/tag_base.rb +21 -0
  56. data/lib/lunchmoney/tags/tag_calls.rb +20 -0
  57. data/lib/lunchmoney/transactions/transaction/child_transaction.rb +31 -0
  58. data/lib/lunchmoney/transactions/transaction/split.rb +24 -0
  59. data/lib/lunchmoney/transactions/transaction/transaction.rb +156 -0
  60. data/lib/lunchmoney/transactions/transaction/transaction_base.rb +52 -0
  61. data/lib/lunchmoney/transactions/transaction/transaction_modification_base.rb +30 -0
  62. data/lib/lunchmoney/transactions/transaction/update_transaction.rb +43 -0
  63. data/lib/lunchmoney/transactions/transaction_calls.rb +218 -0
  64. data/lib/lunchmoney/user/user.rb +36 -0
  65. data/lib/lunchmoney/user/user_calls.rb +19 -0
  66. data/lib/lunchmoney/validators.rb +43 -0
  67. data/lib/lunchmoney/version.rb +7 -0
  68. data/lib/lunchmoney.rb +54 -0
  69. data/lunchmoney.gemspec +34 -0
  70. data/sorbet/config +5 -0
  71. data/sorbet/rbi/annotations/.gitattributes +1 -0
  72. data/sorbet/rbi/annotations/activesupport.rbi +410 -0
  73. data/sorbet/rbi/annotations/faraday.rbi +17 -0
  74. data/sorbet/rbi/annotations/mocha.rbi +34 -0
  75. data/sorbet/rbi/annotations/rainbow.rbi +269 -0
  76. data/sorbet/rbi/annotations/webmock.rbi +9 -0
  77. data/sorbet/rbi/dsl/.gitattributes +1 -0
  78. data/sorbet/rbi/dsl/active_support/callbacks.rbi +22 -0
  79. data/sorbet/rbi/gems/.gitattributes +1 -0
  80. data/sorbet/rbi/gems/activesupport@7.1.3.rbi +18004 -0
  81. data/sorbet/rbi/gems/addressable@2.8.6.rbi +1993 -0
  82. data/sorbet/rbi/gems/appraisal@2.5.0.rbi +621 -0
  83. data/sorbet/rbi/gems/ast@2.4.2.rbi +584 -0
  84. data/sorbet/rbi/gems/base64@0.2.0.rbi +508 -0
  85. data/sorbet/rbi/gems/bigdecimal@3.1.6.rbi +77 -0
  86. data/sorbet/rbi/gems/coderay@1.1.3.rbi +3426 -0
  87. data/sorbet/rbi/gems/concurrent-ruby@1.2.3.rbi +11590 -0
  88. data/sorbet/rbi/gems/connection_pool@2.4.1.rbi +8 -0
  89. data/sorbet/rbi/gems/crack@0.4.5.rbi +144 -0
  90. data/sorbet/rbi/gems/dotenv@2.8.1.rbi +234 -0
  91. data/sorbet/rbi/gems/drb@2.2.0.rbi +1346 -0
  92. data/sorbet/rbi/gems/erubi@1.12.0.rbi +145 -0
  93. data/sorbet/rbi/gems/faraday-net_http@3.1.0.rbi +146 -0
  94. data/sorbet/rbi/gems/faraday@2.9.0.rbi +2911 -0
  95. data/sorbet/rbi/gems/hashdiff@1.1.0.rbi +352 -0
  96. data/sorbet/rbi/gems/i18n@1.14.1.rbi +2325 -0
  97. data/sorbet/rbi/gems/json@2.7.1.rbi +1561 -0
  98. data/sorbet/rbi/gems/language_server-protocol@3.17.0.3.rbi +14237 -0
  99. data/sorbet/rbi/gems/method_source@1.0.0.rbi +272 -0
  100. data/sorbet/rbi/gems/minitest@5.21.2.rbi +2197 -0
  101. data/sorbet/rbi/gems/mocha@2.1.0.rbi +3934 -0
  102. data/sorbet/rbi/gems/mutex_m@0.2.0.rbi +93 -0
  103. data/sorbet/rbi/gems/net-http@0.4.1.rbi +4068 -0
  104. data/sorbet/rbi/gems/netrc@0.11.0.rbi +158 -0
  105. data/sorbet/rbi/gems/parallel@1.24.0.rbi +280 -0
  106. data/sorbet/rbi/gems/parser@3.3.0.5.rbi +5472 -0
  107. data/sorbet/rbi/gems/prettier_print@1.2.1.rbi +951 -0
  108. data/sorbet/rbi/gems/prism@0.19.0.rbi +29883 -0
  109. data/sorbet/rbi/gems/pry-sorbet@0.2.1.rbi +966 -0
  110. data/sorbet/rbi/gems/pry@0.14.2.rbi +10077 -0
  111. data/sorbet/rbi/gems/public_suffix@5.0.4.rbi +935 -0
  112. data/sorbet/rbi/gems/racc@1.7.3.rbi +161 -0
  113. data/sorbet/rbi/gems/rack@3.0.8.rbi +5183 -0
  114. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +402 -0
  115. data/sorbet/rbi/gems/rake@13.1.0.rbi +3027 -0
  116. data/sorbet/rbi/gems/rbi@0.1.6.rbi +2922 -0
  117. data/sorbet/rbi/gems/regexp_parser@2.9.0.rbi +3771 -0
  118. data/sorbet/rbi/gems/rexml@3.2.6.rbi +4781 -0
  119. data/sorbet/rbi/gems/rubocop-ast@1.30.0.rbi +7117 -0
  120. data/sorbet/rbi/gems/rubocop-minitest@0.34.5.rbi +2576 -0
  121. data/sorbet/rbi/gems/rubocop-rails@2.23.1.rbi +9175 -0
  122. data/sorbet/rbi/gems/rubocop-shopify@2.14.0.rbi +8 -0
  123. data/sorbet/rbi/gems/rubocop-sorbet@0.7.6.rbi +1510 -0
  124. data/sorbet/rbi/gems/rubocop@1.60.1.rbi +57356 -0
  125. data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1317 -0
  126. data/sorbet/rbi/gems/ruby2_keywords@0.0.5.rbi +8 -0
  127. data/sorbet/rbi/gems/spoom@1.2.4.rbi +3777 -0
  128. data/sorbet/rbi/gems/syntax_tree@6.2.0.rbi +23136 -0
  129. data/sorbet/rbi/gems/tapioca@0.12.0.rbi +3506 -0
  130. data/sorbet/rbi/gems/thor@1.3.0.rbi +4312 -0
  131. data/sorbet/rbi/gems/toys-core@0.15.4.rbi +9462 -0
  132. data/sorbet/rbi/gems/toys@0.15.4.rbi +243 -0
  133. data/sorbet/rbi/gems/tzinfo@2.0.6.rbi +5917 -0
  134. data/sorbet/rbi/gems/unicode-display_width@2.5.0.rbi +65 -0
  135. data/sorbet/rbi/gems/uri@0.13.0.rbi +2327 -0
  136. data/sorbet/rbi/gems/vcr@6.2.0.rbi +3036 -0
  137. data/sorbet/rbi/gems/webmock@3.19.1.rbi +1768 -0
  138. data/sorbet/rbi/gems/yard-sorbet@0.8.1.rbi +428 -0
  139. data/sorbet/rbi/gems/yard@0.9.34.rbi +18084 -0
  140. data/sorbet/shims/module.rbi +6 -0
  141. data/sorbet/tapioca/require.rb +10 -0
  142. metadata +228 -0
@@ -0,0 +1,65 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module LunchMoney
5
+ # https://lunchmoney.dev/#crypto-object
6
+ class CryptoBase < LunchMoney::DataObject
7
+ include LunchMoney::Validators
8
+
9
+ sig { returns(T.nilable(Integer)) }
10
+ attr_accessor :id, :zabo_account_id
11
+
12
+ sig { returns(String) }
13
+ attr_reader :source, :created_at
14
+
15
+ sig { returns(String) }
16
+ attr_accessor :name, :balance
17
+
18
+ sig { returns(T.nilable(String)) }
19
+ attr_accessor :display_name, :institution_name
20
+
21
+ # Valid crypto source types
22
+ VALID_SOURCES = T.let(
23
+ [
24
+ "synced",
25
+ "manual",
26
+ ],
27
+ T::Array[String],
28
+ )
29
+
30
+ sig do
31
+ params(
32
+ created_at: String,
33
+ source: String,
34
+ name: String,
35
+ balance: String,
36
+ institution_name: T.nilable(String),
37
+ id: T.nilable(Integer),
38
+ zabo_account_id: T.nilable(Integer),
39
+ display_name: T.nilable(String),
40
+ ).void
41
+ end
42
+ def initialize(created_at:, source:, name:, balance:, institution_name: nil, id: nil, zabo_account_id: nil,
43
+ display_name: nil)
44
+ super()
45
+ @created_at = T.let(validate_iso8601!(created_at), String)
46
+ @source = T.let(validate_one_of!(source, VALID_SOURCES), String)
47
+ @name = name
48
+ @balance = balance
49
+ @institution_name = institution_name
50
+ @id = id
51
+ @zabo_account_id = zabo_account_id
52
+ @display_name = display_name
53
+ end
54
+
55
+ sig { params(name: String).void }
56
+ def source=(name)
57
+ @source = validate_one_of!(name, VALID_SOURCES)
58
+ end
59
+
60
+ sig { params(time: String).void }
61
+ def created_at=(time)
62
+ @created_at = validate_iso8601!(time)
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,49 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "crypto/crypto_base"
5
+ require_relative "crypto/crypto"
6
+
7
+ module LunchMoney
8
+ # https://lunchmoney.dev/#crypto
9
+ class CryptoCalls < ApiCall
10
+ sig { returns(T.any(T::Array[LunchMoney::Crypto], LunchMoney::Errors)) }
11
+ def crypto
12
+ response = get("crypto")
13
+
14
+ api_errors = errors(response)
15
+ return api_errors if api_errors.present?
16
+
17
+ response.body[:crypto].map do |crypto|
18
+ LunchMoney::Crypto.new(**crypto)
19
+ end
20
+ end
21
+
22
+ sig do
23
+ params(
24
+ crypto_id: Integer,
25
+ name: T.nilable(String),
26
+ display_name: T.nilable(String),
27
+ institution_name: T.nilable(String),
28
+ balance: T.nilable(String),
29
+ currency: T.nilable(String),
30
+ ).returns(T.any(LunchMoney::CryptoBase, LunchMoney::Errors))
31
+ end
32
+ def update_crypto(crypto_id, name: nil, display_name: nil, institution_name: nil, balance: nil, currency: nil)
33
+ params = clean_params({
34
+ name:,
35
+ display_name:,
36
+ institution_name:,
37
+ balance:,
38
+ currency:,
39
+ })
40
+
41
+ response = put("crypto/manual/#{crypto_id}", params)
42
+
43
+ api_errors = errors(response)
44
+ return api_errors if api_errors.present?
45
+
46
+ LunchMoney::CryptoBase.new(**response.body)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,25 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module LunchMoney
5
+ # Base data object for the objects returned and used when calling the LunchMoney API
6
+ class DataObject
7
+ sig { params(symbolize_keys: T::Boolean).returns(T::Hash[String, T.untyped]) }
8
+ def serialize(symbolize_keys: false)
9
+ ivars = instance_variables
10
+
11
+ output = {}
12
+
13
+ ivars.each do |ivar|
14
+ key = ivar.to_s.gsub("@", "")
15
+ key = key.to_sym if symbolize_keys
16
+
17
+ value = instance_variable_get(ivar)
18
+
19
+ output[key] = value
20
+ end
21
+
22
+ output
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module LunchMoney
5
+ # This class is used to represent errors returned directly from the LunchMoney API
6
+ class Errors
7
+ sig { returns(T::Array[String]) }
8
+ attr_accessor :messages
9
+
10
+ sig { params(message: T.nilable(String)).void }
11
+ def initialize(message: nil)
12
+ @messages = T.let([], T::Array[String])
13
+
14
+ @messages << message unless message.nil?
15
+ end
16
+
17
+ delegate :[], :<<, :each, :to_a, :first, :last, :empty?, :present?, to: :@messages
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module LunchMoney
5
+ # Base exception class for exceptions raised by this gem
6
+ class Exception < StandardError; end
7
+
8
+ # Exception raised when an API Key appears to be invalid
9
+ class InvalidApiKey < Exception; end
10
+
11
+ # Exception raised when an object attribute is invalid
12
+ class InvalidObjectAttribute < Exception; end
13
+
14
+ # Exception raised when a query parameter is invalid
15
+ class InvalidQueryParameter < Exception; end
16
+
17
+ # Exception raised when an essential argument is missing
18
+ class MissingArgument < Exception; end
19
+ end
@@ -0,0 +1,73 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module LunchMoney
5
+ # https://lunchmoney.dev/#plaid-accounts-object
6
+ class PlaidAccount < LunchMoney::DataObject
7
+ sig { returns(Integer) }
8
+ attr_accessor :id
9
+
10
+ sig { returns(String) }
11
+ attr_accessor :date_linked,
12
+ :name,
13
+ :type,
14
+ :mask,
15
+ :institution_name,
16
+ :status,
17
+ :balance,
18
+ :currency,
19
+ :balance_last_update,
20
+ :display_name,
21
+ :plaid_last_successful_update
22
+
23
+ sig { returns(T.nilable(String)) }
24
+ attr_accessor :subtype, :import_start_date, :last_fetch, :last_import
25
+
26
+ sig { returns(T.nilable(Integer)) }
27
+ attr_accessor :limit
28
+
29
+ sig do
30
+ params(
31
+ date_linked: String,
32
+ name: String,
33
+ type: String,
34
+ mask: String,
35
+ institution_name: String,
36
+ status: String,
37
+ balance: String,
38
+ currency: String,
39
+ balance_last_update: String,
40
+ display_name: String,
41
+ id: Integer,
42
+ plaid_last_successful_update: String,
43
+ last_import: T.nilable(String),
44
+ limit: T.nilable(Integer),
45
+ subtype: T.nilable(String),
46
+ import_start_date: T.nilable(String),
47
+ last_fetch: T.nilable(String),
48
+ ).void
49
+ end
50
+ def initialize(date_linked:, name:, type:, mask:, institution_name:, status:, balance:, currency:,
51
+ balance_last_update:, display_name:, id:, plaid_last_successful_update:, last_import: nil, limit: nil,
52
+ subtype: nil, import_start_date: nil, last_fetch: nil)
53
+ super()
54
+ @id = id
55
+ @date_linked = date_linked
56
+ @name = name
57
+ @display_name = display_name
58
+ @type = type
59
+ @subtype = subtype
60
+ @mask = mask
61
+ @institution_name = institution_name
62
+ @status = status
63
+ @limit = limit
64
+ @balance = balance
65
+ @currency = currency
66
+ @balance_last_update = balance_last_update
67
+ @import_start_date = import_start_date
68
+ @last_import = last_import
69
+ @last_fetch = last_fetch
70
+ @plaid_last_successful_update = plaid_last_successful_update
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,38 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "plaid_account"
5
+
6
+ module LunchMoney
7
+ # https://lunchmoney.dev/#plaid-accounts
8
+ class PlaidAccountCalls < ApiCall
9
+ sig { returns(T.any(T::Array[LunchMoney::PlaidAccount], LunchMoney::Errors)) }
10
+ def plaid_accounts
11
+ response = get("plaid_accounts")
12
+
13
+ api_errors = errors(response)
14
+ return api_errors if api_errors.present?
15
+
16
+ response.body[:plaid_accounts].map do |plaid_account|
17
+ LunchMoney::PlaidAccount.new(**plaid_account)
18
+ end
19
+ end
20
+
21
+ sig do
22
+ params(
23
+ start_date: T.nilable(String),
24
+ end_date: T.nilable(String),
25
+ plaid_account_id: T.nilable(Integer),
26
+ ).returns(T.any(T::Boolean, LunchMoney::Errors))
27
+ end
28
+ def plaid_accounts_fetch(start_date: nil, end_date: nil, plaid_account_id: nil)
29
+ params = clean_params({ start_date:, end_date:, plaid_account_id: })
30
+ response = post("plaid_accounts/fetch", params)
31
+
32
+ api_errors = errors(response)
33
+ return api_errors if api_errors.present?
34
+
35
+ response.body
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,65 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module LunchMoney
5
+ # https://lunchmoney.dev/#recurring-expenses-object
6
+ class RecurringExpense < RecurringExpenseBase
7
+ sig { returns(Integer) }
8
+ attr_accessor :id
9
+
10
+ sig { returns(T.nilable(String)) }
11
+ attr_accessor :start_date, :end_date, :description, :original_name
12
+
13
+ sig { returns(String) }
14
+ attr_accessor :cadence, :billing_date, :type, :source, :created_at
15
+
16
+ sig { returns(T.nilable(Integer)) }
17
+ attr_accessor :plaid_account_id, :asset_id, :transaction_id, :category_id
18
+
19
+ sig do
20
+ params(
21
+ cadence: String,
22
+ payee: String,
23
+ amount: String,
24
+ currency: String,
25
+ billing_date: String,
26
+ type: String,
27
+ source: String,
28
+ id: Integer,
29
+ created_at: String,
30
+ category_id: T.nilable(Integer),
31
+ start_date: T.nilable(String),
32
+ end_date: T.nilable(String),
33
+ description: T.nilable(String),
34
+ original_name: T.nilable(String),
35
+ plaid_account_id: T.nilable(Integer),
36
+ asset_id: T.nilable(Integer),
37
+ transaction_id: T.nilable(Integer),
38
+ to_base: T.nilable(Number),
39
+ ).void
40
+ end
41
+ def initialize(cadence:, payee:, amount:, currency:, billing_date:, type:, source:, id:, created_at:,
42
+ category_id: nil, start_date: nil, end_date: nil, description: nil, original_name: nil, plaid_account_id: nil,
43
+ asset_id: nil, transaction_id: nil, to_base: nil)
44
+ super(payee:, amount:, currency:, to_base:)
45
+ @cadence = cadence
46
+ @payee = payee
47
+ @amount = amount
48
+ @currency = currency
49
+ @billing_date = billing_date
50
+ @type = type
51
+ @source = source
52
+ @id = id
53
+ @category_id = category_id
54
+ @created_at = created_at
55
+ @start_date = start_date
56
+ @end_date = end_date
57
+ @description = description
58
+ @original_name = original_name
59
+ @plaid_account_id = plaid_account_id
60
+ @asset_id = asset_id
61
+ @transaction_id = transaction_id
62
+ @to_base = to_base
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,29 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module LunchMoney
5
+ # https://lunchmoney.dev/#recurring-expenses-object
6
+ class RecurringExpenseBase < LunchMoney::DataObject
7
+ sig { returns(String) }
8
+ attr_accessor :payee, :currency, :amount
9
+
10
+ sig { returns(T.nilable(Number)) }
11
+ attr_accessor :to_base
12
+
13
+ sig do
14
+ params(
15
+ payee: String,
16
+ amount: String,
17
+ currency: String,
18
+ to_base: T.nilable(Number),
19
+ ).void
20
+ end
21
+ def initialize(payee:, amount:, currency:, to_base:)
22
+ super()
23
+ @payee = payee
24
+ @amount = amount
25
+ @currency = currency
26
+ @to_base = to_base
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "recurring_expense/recurring_expense_base"
5
+ require_relative "recurring_expense/recurring_expense"
6
+
7
+ module LunchMoney
8
+ # https://lunchmoney.dev/#recurring-expenses
9
+ class RecurringExpenseCalls < ApiCall
10
+ sig do
11
+ params(
12
+ start_date: T.nilable(String),
13
+ end_date: T.nilable(String),
14
+ ).returns(T.any(T::Array[LunchMoney::RecurringExpense], LunchMoney::Errors))
15
+ end
16
+ def recurring_expenses(start_date: nil, end_date: nil)
17
+ params = clean_params({ start_date:, end_date: })
18
+ response = get("recurring_expenses", query_params: params)
19
+
20
+ api_errors = errors(response)
21
+ return api_errors if api_errors.present?
22
+
23
+ response.body[:recurring_expenses].map do |recurring_expense|
24
+ LunchMoney::RecurringExpense.new(**recurring_expense)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module LunchMoney
5
+ # https://lunchmoney.dev/#tags-object
6
+ class Tag < TagBase
7
+ sig { returns(T.nilable(String)) }
8
+ attr_accessor :description
9
+
10
+ sig { returns(T::Boolean) }
11
+ attr_accessor :archived
12
+
13
+ sig { params(id: Integer, name: String, archived: T::Boolean, description: T.nilable(String)).void }
14
+ def initialize(id:, name:, archived:, description: nil)
15
+ super(id:, name:)
16
+ @archived = archived
17
+ @description = description
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module LunchMoney
5
+ # https://lunchmoney.dev/#tags-object without some fields. This is used within field returns of other objects like
6
+ # field returns of other objects like transactions
7
+ class TagBase < LunchMoney::DataObject
8
+ sig { returns(Integer) }
9
+ attr_accessor :id
10
+
11
+ sig { returns(String) }
12
+ attr_accessor :name
13
+
14
+ sig { params(id: Integer, name: String).void }
15
+ def initialize(id:, name:)
16
+ super()
17
+ @id = id
18
+ @name = name
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "tag/tag_base"
5
+ require_relative "tag/tag"
6
+
7
+ module LunchMoney
8
+ # https://lunchmoney.dev/#tags
9
+ class TagCalls < ApiCall
10
+ sig { returns(T.any(T::Array[LunchMoney::Tag], LunchMoney::Errors)) }
11
+ def tags
12
+ response = get("tags")
13
+
14
+ api_errors = errors(response)
15
+ return api_errors if api_errors.present?
16
+
17
+ response.body.map { |tag| LunchMoney::Tag.new(**tag) }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module LunchMoney
5
+ # Slimmed down version of https://lunchmoney.dev/#transaction-object used in the
6
+ # `children` field of a transaction object with an additional `formatted_date`` field
7
+ class ChildTransaction < TransactionBase
8
+ sig { returns(String) }
9
+ attr_accessor :formatted_date
10
+
11
+ sig do
12
+ params(
13
+ id: Integer,
14
+ date: String,
15
+ amount: String,
16
+ currency: String,
17
+ to_base: Number,
18
+ payee: String,
19
+ formatted_date: String,
20
+ notes: T.nilable(String),
21
+ asset_id: T.nilable(Integer),
22
+ plaid_account_id: T.nilable(Integer),
23
+ ).void
24
+ end
25
+ def initialize(id:, date:, amount:, currency:, to_base:, payee:, formatted_date:, notes: nil, asset_id: nil,
26
+ plaid_account_id: nil)
27
+ super(id:, date:, amount:, currency:, to_base:, payee:, notes:, asset_id:, plaid_account_id:)
28
+ @formatted_date = formatted_date
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module LunchMoney
5
+ # Object used to split a transaction when updating https://lunchmoney.dev/#update-transaction
6
+ class Split < TransactionModificationBase
7
+ sig { returns(T.any(Number, String)) }
8
+ attr_accessor :amount
9
+
10
+ sig do
11
+ params(
12
+ amount: T.any(Number, String),
13
+ payee: T.nilable(String),
14
+ date: T.nilable(String),
15
+ category_id: T.nilable(Integer),
16
+ notes: T.nilable(String),
17
+ ).void
18
+ end
19
+ def initialize(amount:, payee: nil, date: nil, category_id: nil, notes: nil)
20
+ super(payee:, date:, category_id:, notes:)
21
+ @amount = amount
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,156 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module LunchMoney
5
+ # https://lunchmoney.dev/#transaction-object
6
+ class Transaction < TransactionBase
7
+ sig { returns(T.nilable(Integer)) }
8
+ attr_accessor :category_id,
9
+ :category_group_id,
10
+ :recurring_id,
11
+ :parent_id,
12
+ :group_id,
13
+ :external_id
14
+
15
+ sig { returns(String) }
16
+ attr_accessor :created_at,
17
+ :updated_at,
18
+ :status,
19
+ :source,
20
+ :display_name,
21
+ :account_display_name
22
+
23
+ sig { returns(T.nilable(String)) }
24
+ attr_accessor :category_name,
25
+ :category_group_name,
26
+ :original_name,
27
+ :recurring_payee,
28
+ :recurring_description,
29
+ :recurring_cadence,
30
+ :recurring_type,
31
+ :recurring_amount,
32
+ :recurring_currency,
33
+ :asset_institution_name,
34
+ :asset_name,
35
+ :asset_display_name,
36
+ :asset_status,
37
+ :plaid_account_name,
38
+ :plaid_account_mask,
39
+ :institution_name,
40
+ :plaid_account_display_name,
41
+ :plaid_metadata,
42
+ :display_notes
43
+
44
+ sig { returns(T::Boolean) }
45
+ attr_accessor :is_income, :exclude_from_budget, :exclude_from_totals, :is_pending, :has_children, :is_group
46
+
47
+ sig { returns(T::Array[LunchMoney::TagBase]) }
48
+ attr_accessor :tags
49
+
50
+ sig { returns(T.nilable(T::Array[LunchMoney::ChildTransaction])) }
51
+ attr_accessor :children
52
+
53
+ sig do
54
+ params(
55
+ id: Integer,
56
+ date: String,
57
+ amount: String,
58
+ currency: String,
59
+ to_base: Number,
60
+ payee: String,
61
+ is_income: T::Boolean,
62
+ exclude_from_budget: T::Boolean,
63
+ exclude_from_totals: T::Boolean,
64
+ created_at: String,
65
+ updated_at: String,
66
+ status: String,
67
+ is_pending: T::Boolean,
68
+ has_children: T::Boolean,
69
+ is_group: T::Boolean,
70
+ source: String,
71
+ display_name: String,
72
+ account_display_name: String,
73
+ tags: T::Array[LunchMoney::TagBase],
74
+ category_id: T.nilable(Integer),
75
+ category_name: T.nilable(String),
76
+ category_group_id: T.nilable(Integer),
77
+ category_group_name: T.nilable(String),
78
+ notes: T.nilable(String),
79
+ original_name: T.nilable(String),
80
+ recurring_id: T.nilable(Integer),
81
+ recurring_payee: T.nilable(String),
82
+ recurring_description: T.nilable(String),
83
+ recurring_cadence: T.nilable(String),
84
+ recurring_type: T.nilable(String),
85
+ recurring_amount: T.nilable(String),
86
+ recurring_currency: T.nilable(String),
87
+ parent_id: T.nilable(Integer),
88
+ group_id: T.nilable(Integer),
89
+ asset_id: T.nilable(Integer),
90
+ asset_institution_name: T.nilable(String),
91
+ asset_name: T.nilable(String),
92
+ asset_display_name: T.nilable(String),
93
+ asset_status: T.nilable(String),
94
+ plaid_account_id: T.nilable(Integer),
95
+ plaid_account_name: T.nilable(String),
96
+ plaid_account_mask: T.nilable(String),
97
+ institution_name: T.nilable(String),
98
+ plaid_account_display_name: T.nilable(String),
99
+ plaid_metadata: T.nilable(String),
100
+ display_notes: T.nilable(String),
101
+ external_id: T.nilable(Integer),
102
+ children: T.nilable(T::Array[LunchMoney::ChildTransaction]),
103
+ ).void
104
+ end
105
+ def initialize(id:, date:, amount:, currency:, to_base:, payee:, is_income:, exclude_from_budget:,
106
+ exclude_from_totals:, created_at:, updated_at:, status:, is_pending:, has_children:, is_group:, source:,
107
+ display_name:, account_display_name:, tags:, category_id: nil, category_name: nil, category_group_id: nil,
108
+ category_group_name: nil, notes: nil, original_name: nil, recurring_id: nil, recurring_payee: nil,
109
+ recurring_description: nil, recurring_cadence: nil, recurring_type: nil, recurring_amount: nil,
110
+ recurring_currency: nil, parent_id: nil, group_id: nil, asset_id: nil, asset_institution_name: nil,
111
+ asset_name: nil, asset_display_name: nil, asset_status: nil, plaid_account_id: nil, plaid_account_name: nil,
112
+ plaid_account_mask: nil, institution_name: nil, plaid_account_display_name: nil, plaid_metadata: nil,
113
+ display_notes: nil, external_id: nil, children: nil)
114
+ super(id:, date:, amount:, currency:, to_base:, payee:, notes:, asset_id:, plaid_account_id:)
115
+ @is_income = is_income
116
+ @exclude_from_budget = exclude_from_budget
117
+ @exclude_from_totals = exclude_from_totals
118
+ @created_at = created_at
119
+ @updated_at = updated_at
120
+ @status = status
121
+ @is_pending = is_pending
122
+ @has_children = has_children
123
+ @is_group = is_group
124
+ @source = source
125
+ @display_name = display_name
126
+ @account_display_name = account_display_name
127
+ @tags = tags
128
+ @category_id = category_id
129
+ @category_name = category_name
130
+ @category_group_id = category_group_id
131
+ @category_group_name = category_group_name
132
+ @original_name = original_name
133
+ @recurring_id = recurring_id
134
+ @recurring_payee = recurring_payee
135
+ @recurring_description = recurring_description
136
+ @recurring_cadence = recurring_cadence
137
+ @recurring_type = recurring_type
138
+ @recurring_amount = recurring_amount
139
+ @recurring_currency = recurring_currency
140
+ @parent_id = parent_id
141
+ @group_id = group_id
142
+ @asset_institution_name = asset_institution_name
143
+ @asset_name = asset_name
144
+ @asset_display_name = asset_display_name
145
+ @asset_status = asset_status
146
+ @plaid_account_name = plaid_account_name
147
+ @plaid_account_mask = plaid_account_mask
148
+ @institution_name = institution_name
149
+ @plaid_account_display_name = plaid_account_display_name
150
+ @plaid_metadata = plaid_metadata
151
+ @display_notes = display_notes
152
+ @children = children
153
+ @external_id = external_id
154
+ end
155
+ end
156
+ end