lunchmoney 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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