async-firefly 1.0.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 (44) hide show
  1. checksums.yaml +7 -0
  2. data/lib/async/firefly/about.rb +79 -0
  3. data/lib/async/firefly/accounts.rb +129 -0
  4. data/lib/async/firefly/attachments.rb +53 -0
  5. data/lib/async/firefly/autocomplete.rb +435 -0
  6. data/lib/async/firefly/available_budgets.rb +59 -0
  7. data/lib/async/firefly/bills.rb +107 -0
  8. data/lib/async/firefly/budgets.rb +165 -0
  9. data/lib/async/firefly/categories.rb +63 -0
  10. data/lib/async/firefly/charts.rb +73 -0
  11. data/lib/async/firefly/client.rb +89 -0
  12. data/lib/async/firefly/configurations.rb +62 -0
  13. data/lib/async/firefly/currencies.rb +71 -0
  14. data/lib/async/firefly/data.rb +37 -0
  15. data/lib/async/firefly/error.rb +57 -0
  16. data/lib/async/firefly/escape.rb +9 -0
  17. data/lib/async/firefly/exchange_rates.rb +77 -0
  18. data/lib/async/firefly/insight.rb +185 -0
  19. data/lib/async/firefly/links.rb +69 -0
  20. data/lib/async/firefly/nested.rb +75 -0
  21. data/lib/async/firefly/object_groups.rb +33 -0
  22. data/lib/async/firefly/page.rb +139 -0
  23. data/lib/async/firefly/piggy_banks.rb +153 -0
  24. data/lib/async/firefly/preferences.rb +58 -0
  25. data/lib/async/firefly/recurrences.rb +169 -0
  26. data/lib/async/firefly/redacted.rb +32 -0
  27. data/lib/async/firefly/representation.rb +99 -0
  28. data/lib/async/firefly/rule_groups.rb +55 -0
  29. data/lib/async/firefly/rules.rb +105 -0
  30. data/lib/async/firefly/search.rb +40 -0
  31. data/lib/async/firefly/submit.rb +139 -0
  32. data/lib/async/firefly/summary.rb +45 -0
  33. data/lib/async/firefly/sums.rb +17 -0
  34. data/lib/async/firefly/tags.rb +57 -0
  35. data/lib/async/firefly/transactions.rb +231 -0
  36. data/lib/async/firefly/user_groups.rb +49 -0
  37. data/lib/async/firefly/users.rb +35 -0
  38. data/lib/async/firefly/version.rb +5 -0
  39. data/lib/async/firefly/webhooks.rb +101 -0
  40. data/lib/async/firefly/wrapper.rb +65 -0
  41. data/lib/async/firefly.rb +2 -0
  42. data/license.md +21 -0
  43. data/readme.md +498 -0
  44. metadata +110 -0
@@ -0,0 +1,101 @@
1
+ require_relative "page"
2
+
3
+ module Async
4
+ module Firefly
5
+ class Webhook < Representation
6
+ def created_at = self.attributes[:created_at]
7
+
8
+ def updated_at = self.attributes[:updated_at]
9
+
10
+ def active = self.attributes[:active]
11
+
12
+ def title = self.attributes[:title]
13
+
14
+ def secret = self.attributes[:secret]
15
+
16
+ def triggers = self.attributes[:triggers]
17
+
18
+ def responses = self.attributes[:responses]
19
+
20
+ def deliveries = self.attributes[:deliveries]
21
+
22
+ def url = self.attributes[:url]
23
+
24
+ def active? = self.active == true
25
+
26
+ def messages(**parameters) = WebhookMessagePage.new(@resource.with(path: "messages", parameters: parameters))
27
+
28
+ def message(id) = WebhookMessage.new(@resource.with(path: "messages/" + Firefly.escape(id)))
29
+
30
+ def submit_messages = self.discard(@resource.with(path: "submit"), method: :post)
31
+
32
+ def trigger_transaction(transaction_id) = self.discard(@resource.with(path: "trigger-transaction/" + Firefly.escape(transaction_id)), method: :post)
33
+
34
+ def update(payload) = self.submit(Webhook, @resource, payload, method: :put)
35
+
36
+ def delete = self.discard(@resource)
37
+ end
38
+
39
+ class WebhookMessage < Representation
40
+ def created_at = self.attributes[:created_at]
41
+
42
+ def updated_at = self.attributes[:updated_at]
43
+
44
+ def sent = self.attributes[:sent]
45
+
46
+ def errored = self.attributes[:errored]
47
+
48
+ def webhook_id = self.attributes[:webhook_id]
49
+
50
+ def uuid = self.attributes[:uuid]
51
+
52
+ def message = self.attributes[:message]
53
+
54
+ def sent? = self.sent == true
55
+
56
+ def errored? = self.errored == true
57
+
58
+ def attempts(**parameters) = Collection.for(@resource, "attempts", WebhookAttemptPage, **parameters)
59
+
60
+ def attempt(id) = WebhookAttempt.new(@resource.with(path: "attempts/" + Firefly.escape(id)))
61
+
62
+ def delete = self.discard(@resource)
63
+ end
64
+
65
+ class WebhookAttempt < Representation
66
+ def created_at = self.attributes[:created_at]
67
+
68
+ def updated_at = self.attributes[:updated_at]
69
+
70
+ def webhook_message_id = self.attributes[:webhook_message_id]
71
+
72
+ def status_code = self.attributes[:status_code]
73
+
74
+ def logs = self.attributes[:logs]
75
+
76
+ def response = self.attributes[:response]
77
+
78
+ def delete = self.discard(@resource)
79
+ end
80
+
81
+ class WebhookPage < Page
82
+ MEMBER = Webhook
83
+ end
84
+
85
+ class WebhookMessagePage < Page
86
+ MEMBER = WebhookMessage
87
+ end
88
+
89
+ class WebhookAttemptPage < Page
90
+ MEMBER = WebhookAttempt
91
+ end
92
+
93
+ module Webhooks
94
+ def webhooks(**parameters) = Collection.for(self, "v1/webhooks", WebhookPage, **parameters)
95
+
96
+ def webhook(id) = Webhook.new(self.with(path: "v1/webhooks/" + Firefly.escape(id)))
97
+
98
+ def create_webhook(payload) = self.submit(Webhook, self.with(path: "v1/webhooks"), payload, identify: true)
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,65 @@
1
+ require "async/rest/wrapper/generic"
2
+ require "async/rest/wrapper/json"
3
+ require "protocol/http/body/buffered"
4
+ require "protocol/http/body/wrapper"
5
+
6
+ require_relative "error"
7
+
8
+ module Async
9
+ module Firefly
10
+ class Wrapper < Async::REST::Wrapper::JSON
11
+ VND_API_JSON = "application/vnd.api+json"
12
+
13
+ ACCEPT = [VND_API_JSON, Async::REST::Wrapper::JSON::APPLICATION_JSON].join(", ")
14
+
15
+ JSON_SUFFIX = "json"
16
+
17
+ def initialize(content_type = VND_API_JSON)
18
+ super
19
+ end
20
+
21
+ def response_for(resource, request) = resource.call(request)
22
+
23
+ def prepare_request(request, payload)
24
+ request.headers["accept"] ||= ACCEPT
25
+
26
+ if payload
27
+ request.headers["content-type"] = Async::REST::Wrapper::JSON::APPLICATION_JSON
28
+ request.body = ::Protocol::HTTP::Body::Buffered.new([::JSON.dump(payload)])
29
+ end
30
+ end
31
+
32
+ def parser_for(response)
33
+ content_type = response.headers["content-type"]
34
+
35
+ if content_type&.include?(JSON_SUFFIX)
36
+ Async::REST::Wrapper::JSON::Parser
37
+ end
38
+ end
39
+ end
40
+
41
+ class Content < Async::REST::Wrapper::Generic
42
+ OCTET_STREAM = "application/octet-stream"
43
+
44
+ def response_for(resource, request) = resource.call(request)
45
+
46
+ def prepare_request(request, payload)
47
+ request.headers["accept"] ||= OCTET_STREAM
48
+
49
+ if payload
50
+ request.body = ::Protocol::HTTP::Body::Buffered.new([payload.to_s])
51
+ end
52
+ end
53
+ end
54
+
55
+ class Binary < Wrapper
56
+ def prepare_request(request, payload)
57
+ request.headers["accept"] ||= ACCEPT
58
+
59
+ if payload
60
+ request.body = ::Protocol::HTTP::Body::Buffered.new([payload.to_s])
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,2 @@
1
+ require_relative "firefly/client"
2
+ require_relative "firefly/version"
data/license.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright, 2026, by Nathan Kidd.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/readme.md ADDED
@@ -0,0 +1,498 @@
1
+ # async-firefly
2
+
3
+ ```ruby
4
+ gem "async-firefly"
5
+ ```
6
+
7
+ ```ruby
8
+ require "async/firefly"
9
+
10
+ Async::Firefly::Client.open("https://firefly.example.com/api/") do |client|
11
+ firefly = client.authenticated(ENV.fetch("FIREFLY_TOKEN"))
12
+
13
+ firefly.about.version
14
+ firefly.about.api_version
15
+ firefly.about.driver
16
+ firefly.current_user.email
17
+ end
18
+ ```
19
+
20
+ ```ruby
21
+ Async::Firefly::Client.open do |client|
22
+ firefly = client.authenticated(ENV.fetch("FIREFLY_TOKEN"))
23
+ end
24
+ ```
25
+
26
+ ```ruby
27
+ firefly.accounts.map(&:name)
28
+ firefly.accounts(type: "asset").count
29
+ firefly.accounts(type: "expense").take(5).map(&:current_balance)
30
+
31
+ firefly.accounts.each_page(limit: 25) do |page|
32
+ page.records.size
33
+ page.current_page
34
+ page.total_pages
35
+ page.total
36
+ page.last?
37
+
38
+ page.each{|account| account.name}
39
+ end
40
+ ```
41
+
42
+ ```ruby
43
+ account = firefly.create_account({name: "Savings", type: "asset", account_role: "defaultAsset", currency_code: "EUR"})
44
+
45
+ account.id
46
+ account.name
47
+ account.current_balance
48
+ account.currency_code
49
+ account.iban
50
+ account.active?
51
+ account.asset?
52
+
53
+ firefly.account(account.id).name
54
+ firefly.account(account.id).update({name: "Rainy day", type: "asset"})
55
+ firefly.account(account.id).transactions(start: "2026-01-01", end: "2026-12-31").map(&:group_title)
56
+ firefly.account(account.id).attachments.map(&:filename)
57
+ firefly.account(account.id).piggy_banks.map(&:name)
58
+ firefly.account(account.id).delete
59
+ ```
60
+
61
+ ```ruby
62
+ transaction = firefly.create_transaction({
63
+ transactions: [{
64
+ type: "withdrawal",
65
+ date: "2026-10-17",
66
+ amount: "12.50",
67
+ description: "Coffee",
68
+ source_id: account.id,
69
+ destination_name: "Kiosk",
70
+ }],
71
+ })
72
+
73
+ transaction.id
74
+ transaction.group_title
75
+ transaction.user
76
+
77
+ split = transaction.transactions.first
78
+
79
+ split.description
80
+ split.amount
81
+ split.date
82
+ split.type
83
+ split.source_name
84
+ split.destination_name
85
+ split.category_name
86
+ split.reconciled
87
+
88
+ firefly.transactions(start: "2026-01-01", end: "2026-12-31", type: "withdrawal").map{|each| each.transactions.first.amount}
89
+ firefly.transaction(transaction.id).update({transactions: [{description: "Coffee and a bun"}]})
90
+ firefly.transaction(transaction.id).attachments.map(&:filename)
91
+ firefly.transaction(transaction.id).piggy_bank_events.map(&:amount)
92
+ firefly.transaction(transaction.id).delete
93
+
94
+ firefly.transaction_journal(1).links.map(&:link_type_name)
95
+ ```
96
+
97
+ ```ruby
98
+ bill = firefly.create_bill({name: "Rent", amount_min: "800", amount_max: "900", date: "2026-01-01", repeat_freq: "monthly"})
99
+
100
+ bill.name
101
+ bill.repeat_freq
102
+ bill.next_expected_match
103
+ bill.paid_dates
104
+ bill.active?
105
+
106
+ firefly.bills.map(&:name)
107
+ firefly.bill(bill.id).transactions.count
108
+ firefly.bill(bill.id).rules.map(&:title)
109
+ firefly.bill(bill.id).attachments.map(&:filename)
110
+ firefly.bill(bill.id).update({name: "Rent and service charge"})
111
+ firefly.bill(bill.id).delete
112
+
113
+ firefly.subscriptions.map(&:name)
114
+ ```
115
+
116
+ ```ruby
117
+ budget = firefly.create_budget({name: "Groceries", active: true})
118
+
119
+ budget.name
120
+ budget.auto_budget_type
121
+ budget.spent.map(&:sum)
122
+
123
+ limit = budget.create_limit({amount: "400", start: "2026-10-01", end: "2026-10-31"})
124
+
125
+ limit.amount
126
+ limit.start
127
+ limit.end
128
+ limit.period
129
+
130
+ budget.limits.map(&:amount)
131
+ budget.limit(limit.id).update({amount: "450"})
132
+ budget.limit(limit.id).transactions.count
133
+ budget.limit(limit.id).delete
134
+ budget.transactions.count
135
+ budget.attachments.map(&:filename)
136
+ firefly.budget(budget.id).delete
137
+
138
+ firefly.budgets.map(&:name)
139
+ firefly.budget_limits(start: "2026-01-01", end: "2026-12-31").map(&:amount)
140
+ firefly.transactions_without_budget.count
141
+ ```
142
+
143
+ ```ruby
144
+ category = firefly.create_category({name: "Travel"})
145
+
146
+ category.name
147
+ category.spent.map(&:sum)
148
+ category.earned.map(&:sum)
149
+ category.last_activity
150
+
151
+ firefly.categories.map(&:name)
152
+ firefly.category(category.id).transactions.count
153
+ firefly.category(category.id).attachments.map(&:filename)
154
+ firefly.category(category.id).update({name: "Holidays"})
155
+ firefly.category(category.id).delete
156
+ ```
157
+
158
+ ```ruby
159
+ tag = firefly.create_tag({tag: "reimbursable"})
160
+
161
+ tag.tag
162
+ tag.date
163
+ tag.description
164
+
165
+ firefly.tags.map(&:tag)
166
+ firefly.tag("reimbursable").transactions.count
167
+ firefly.tag("reimbursable").attachments.map(&:filename)
168
+ firefly.tag("reimbursable").update({tag: "expensed"})
169
+ firefly.tag("expensed").delete
170
+ ```
171
+
172
+ ```ruby
173
+ piggy_bank = firefly.create_piggy_bank({name: "New bike", accounts: [{id: account.id}], target_amount: "500"})
174
+
175
+ piggy_bank.name
176
+ piggy_bank.target_amount
177
+ piggy_bank.current_amount
178
+ piggy_bank.left_to_save
179
+ piggy_bank.percentage
180
+ piggy_bank.accounts.map(&:account_id)
181
+
182
+ firefly.piggy_banks.map(&:name)
183
+ firefly.piggy_bank(piggy_bank.id).events.map(&:amount)
184
+ firefly.piggy_bank(piggy_bank.id).linked_accounts.map(&:name)
185
+ firefly.piggy_bank(piggy_bank.id).attachments.map(&:filename)
186
+ firefly.piggy_bank(piggy_bank.id).update({name: "New bicycle"})
187
+ firefly.piggy_bank(piggy_bank.id).delete
188
+ ```
189
+
190
+ ```ruby
191
+ firefly.object_groups.map(&:title)
192
+ firefly.object_group(1).bills.map(&:name)
193
+ firefly.object_group(1).piggy_banks.map(&:name)
194
+ firefly.object_group(1).update({title: "Household", order: 1})
195
+ firefly.object_group(1).delete
196
+ ```
197
+
198
+ ```ruby
199
+ currency = firefly.currency("EUR")
200
+
201
+ currency.code
202
+ currency.name
203
+ currency.symbol
204
+ currency.decimal_places
205
+ currency.enabled?
206
+ currency.primary?
207
+
208
+ firefly.currencies.map(&:code)
209
+ firefly.primary_currency.code
210
+ firefly.default_currency.code
211
+ firefly.create_currency({code: "NOK", name: "Norwegian krone", symbol: "kr", decimal_places: 2})
212
+
213
+ firefly.currency("NOK").enable
214
+ firefly.currency("NOK").make_primary
215
+ firefly.currency("NOK").disable
216
+ firefly.currency("EUR").accounts.map(&:name)
217
+ firefly.currency("EUR").transactions.count
218
+ firefly.currency("EUR").exchange_rates.map(&:rate)
219
+ firefly.currency("NOK").delete
220
+ ```
221
+
222
+ ```ruby
223
+ rate = firefly.create_exchange_rate({from: "EUR", to: "GBP", date: "2026-10-17", rate: "0.87"})
224
+
225
+ rate.rate
226
+ rate.date
227
+ rate.from_currency_code
228
+ rate.to_currency_code
229
+
230
+ firefly.exchange_rates.map(&:rate)
231
+ firefly.exchange_rates_between("EUR", "GBP").map(&:date)
232
+ firefly.exchange_rates_on("EUR", "GBP", "2026-10-17").map(&:rate)
233
+ firefly.exchange_rate(rate.id).update({rate: "0.88"})
234
+ firefly.exchange_rate(rate.id).delete
235
+ ```
236
+
237
+ ```ruby
238
+ recurrence = firefly.create_recurrence({
239
+ type: "withdrawal",
240
+ title: "Rent",
241
+ first_date: "2026-11-01",
242
+ repetitions: [{type: "monthly", moment: "1"}],
243
+ transactions: [{amount: "850", description: "Rent", source_id: account.id}],
244
+ })
245
+
246
+ recurrence.title
247
+ recurrence.first_date
248
+ recurrence.latest_date
249
+ recurrence.repeat_until
250
+ recurrence.nr_of_repetitions
251
+ recurrence.active?
252
+ recurrence.repetitions.map(&:type)
253
+ recurrence.transactions.map(&:description)
254
+
255
+ firefly.recurrences.map(&:title)
256
+ firefly.recurrence(recurrence.id).transaction_history.count
257
+ firefly.recurrence(recurrence.id).fire
258
+ firefly.recurrence(recurrence.id).update({title: "Rent and parking"})
259
+ firefly.recurrence(recurrence.id).delete
260
+ ```
261
+
262
+ ```ruby
263
+ rule = firefly.create_rule({
264
+ title: "Tag coffee",
265
+ rule_group_id: "1",
266
+ trigger: "store-journal",
267
+ triggers: [{type: "description_contains", value: "coffee"}],
268
+ actions: [{type: "add_tag", value: "coffee"}],
269
+ })
270
+
271
+ rule.title
272
+ rule.trigger
273
+ rule.active?
274
+ rule.strict
275
+ rule.triggers.map(&:type)
276
+ rule.actions.map(&:value)
277
+
278
+ firefly.rules.map(&:title)
279
+ firefly.validate_rule_expression("description:coffee")
280
+ firefly.rule(rule.id).test(start: "2026-01-01", end: "2026-12-31").count
281
+ firefly.rule(rule.id).fire
282
+ firefly.rule(rule.id).update({title: "Tag all coffee"})
283
+ firefly.rule(rule.id).delete
284
+
285
+ firefly.rule_groups.map(&:title)
286
+ firefly.rule_group(1).rules.map(&:title)
287
+ firefly.rule_group(1).test.count
288
+ firefly.rule_group(1).fire
289
+ ```
290
+
291
+ ```ruby
292
+ webhook = firefly.create_webhook({
293
+ title: "Ledger mirror",
294
+ url: "https://example.com/hook",
295
+ trigger: "STORE_TRANSACTION",
296
+ response: "TRANSACTIONS",
297
+ delivery: "JSON",
298
+ })
299
+
300
+ webhook.title
301
+ webhook.url
302
+ webhook.triggers
303
+ webhook.responses
304
+ webhook.deliveries
305
+ webhook.active?
306
+ webhook.secret
307
+
308
+ firefly.webhooks.map(&:title)
309
+ firefly.webhook(webhook.id).submit_messages
310
+ firefly.webhook(webhook.id).trigger_transaction(1)
311
+ firefly.webhook(webhook.id).messages.map(&:uuid)
312
+
313
+ message = firefly.webhook(webhook.id).message(1)
314
+
315
+ message.sent?
316
+ message.errored?
317
+ message.attempts.map(&:status_code)
318
+ message.attempt(1).delete
319
+ message.delete
320
+
321
+ firefly.webhook(webhook.id).update({active: false})
322
+ firefly.webhook(webhook.id).delete
323
+ ```
324
+
325
+ ```ruby
326
+ attachment = firefly.create_attachment({filename: "receipt.pdf", attachable_type: "TransactionJournal", attachable_id: "1"})
327
+
328
+ attachment.filename
329
+ attachment.title
330
+ attachment.mime
331
+ attachment.size
332
+ attachment.download_url
333
+
334
+ firefly.attachment(attachment.id).upload_file(File.read("receipt.pdf"), content_type: "application/pdf")
335
+ firefly.attachment(attachment.id).download.bytes
336
+ firefly.attachment(attachment.id).download.media_type
337
+ firefly.attachments.map(&:filename)
338
+ firefly.attachment(attachment.id).update({title: "Receipt"})
339
+ firefly.attachment(attachment.id).delete
340
+ ```
341
+
342
+ ```ruby
343
+ link_type = firefly.create_link_type({name: "Refund", inward: "is refunded by", outward: "refunds"})
344
+
345
+ link_type.name
346
+ link_type.inward
347
+ link_type.outward
348
+ link_type.editable
349
+
350
+ link = firefly.create_transaction_link({link_type_id: link_type.id, inward_id: "1", outward_id: "2"})
351
+
352
+ link.link_type_name
353
+ link.inward_id
354
+ link.outward_id
355
+
356
+ firefly.link_types.map(&:name)
357
+ firefly.link_type(link_type.id).transactions.count
358
+ firefly.transaction_links.map(&:link_type_name)
359
+ firefly.transaction_link(link.id).update({notes: "Partial"})
360
+ firefly.transaction_link(link.id).delete
361
+ firefly.link_type(link_type.id).delete
362
+ ```
363
+
364
+ ```ruby
365
+ firefly.preferences.map(&:name)
366
+ firefly.preference("currencyPreference").data
367
+ firefly.preference("currencyPreference").update({data: "EUR"})
368
+ firefly.create_preference({name: "listPageSize", data: "100"})
369
+ ```
370
+
371
+ ```ruby
372
+ firefly.configurations.map(&:title)
373
+ firefly.configuration("configuration.is_demo_site").editable
374
+ firefly.configuration("configuration.is_demo_site").value_attribute
375
+ firefly.set_configuration("configuration.single_user_mode", {value: true})
376
+ ```
377
+
378
+ ```ruby
379
+ firefly.users.map(&:email)
380
+ firefly.user(1).role
381
+ firefly.user(1).blocked
382
+ firefly.create_user({email: "new@example.com", blocked: false})
383
+ firefly.user(1).update({email: "renamed@example.com"})
384
+ firefly.user(1).delete
385
+
386
+ firefly.user_groups.map(&:title)
387
+ firefly.user_group(1).members.map(&:user_email)
388
+ firefly.user_group(1).in_use
389
+ firefly.user_group(1).update({title: "Household"})
390
+ ```
391
+
392
+ ```ruby
393
+ firefly.autocomplete_accounts(query: "sav", limit: 10).map(&:name_with_balance)
394
+ firefly.autocomplete_bills(query: "rent").map(&:name)
395
+ firefly.autocomplete_categories(query: "tra").map(&:name)
396
+ firefly.autocomplete_tags(query: "reim").map(&:tag)
397
+ firefly.autocomplete_transactions(query: "coffee").map(&:description)
398
+ firefly.autocomplete_transaction_types(query: "with").map(&:name)
399
+ ```
400
+
401
+ ```ruby
402
+ firefly.expense_by_category(start: "2026-01-01", end: "2026-12-31").map{|entry| [entry.name, entry.difference_float]}
403
+ firefly.expense_by_budget(start: "2026-01-01", end: "2026-12-31").map(&:difference)
404
+ firefly.expense_total(start: "2026-01-01", end: "2026-12-31").map(&:difference_float)
405
+ firefly.expense_without_category(start: "2026-01-01", end: "2026-12-31").map(&:currency_code)
406
+ firefly.income_by_revenue_account(start: "2026-01-01", end: "2026-12-31").map(&:name)
407
+ firefly.income_total(start: "2026-01-01", end: "2026-12-31").map(&:difference)
408
+ firefly.transfer_by_asset_account(start: "2026-01-01", end: "2026-12-31").map(&:difference_float)
409
+ ```
410
+
411
+ ```ruby
412
+ firefly.balance_chart(start: "2026-01-01", end: "2026-12-31").map(&:label)
413
+ firefly.account_overview_chart(start: "2026-01-01", end: "2026-12-31").map{|set| [set.label, set.entries]}
414
+ firefly.category_overview_chart(start: "2026-01-01", end: "2026-12-31").map(&:currency_code)
415
+ firefly.budget_overview_chart(start: "2026-01-01", end: "2026-12-31").map(&:period)
416
+ ```
417
+
418
+ ```ruby
419
+ firefly.basic_summary(start: "2026-01-01", end: "2026-12-31").map{|entry| [entry.key, entry.monetary_value]}
420
+ firefly.basic_summary(start: "2026-01-01", end: "2026-12-31").map(&:value_parsed)
421
+ ```
422
+
423
+ ```ruby
424
+ firefly.export_transactions(start: "2026-01-01", end: "2026-12-31").bytes
425
+ firefly.export_accounts.media_type
426
+ firefly.export_budgets.bytes
427
+ firefly.bulk_update_transactions({where: {description: "coffee"}, update: {category_id: "7"}})
428
+ ```
429
+
430
+ ```ruby
431
+ firefly.search_accounts(query: "savings", field: "name").map(&:name)
432
+ firefly.search_transactions(query: "amount_gt:100").map{|each| each.transactions.first.amount}
433
+ firefly.count_transactions(description: "coffee")
434
+ ```
435
+
436
+ ```ruby
437
+ case firefly.account(1)
438
+ in {data: {id: String => id, attributes: {name: String => name}}}
439
+ [id, name]
440
+ end
441
+
442
+ case firefly.transaction(1)
443
+ in {data: {attributes: {transactions: [{description: String => description}, *]}}}
444
+ description
445
+ end
446
+
447
+ firefly.basic_summary(start: "2026-01-01", end: "2026-12-31").deconstruct_keys(nil).keys
448
+ ```
449
+
450
+ ```ruby
451
+ begin
452
+ firefly.create_account({})
453
+ rescue Async::Firefly::Failure => error
454
+ error.status
455
+ error.detail
456
+ error.exception_class
457
+ error.errors
458
+ error.invalid?
459
+ error.retry_after
460
+ end
461
+
462
+ begin
463
+ firefly.account(404).name
464
+ rescue Async::REST::ResponseError => error
465
+ error.response.status
466
+ rescue ::JSON::ParserError => error
467
+ error.message
468
+ end
469
+ ```
470
+
471
+ ```ruby
472
+ account = firefly.account(1)
473
+
474
+ account.current_balance
475
+ account = firefly.account(1) unless account.cacheable?
476
+ account.current_balance
477
+ ```
478
+
479
+ ```ruby
480
+ webhook = firefly.webhook(1)
481
+
482
+ webhook.inspect
483
+ webhook.value[:data][:attributes][:secret]
484
+ ```
485
+
486
+ ```ruby
487
+ Async::Firefly::Client.open do |client|
488
+ firefly = client.authenticated(ENV.fetch("FIREFLY_TOKEN"))
489
+
490
+ balances = firefly.accounts(type: "asset").map do |account|
491
+ Async do
492
+ firefly.account(account.id).current_balance
493
+ end
494
+ end
495
+
496
+ balances.map(&:wait)
497
+ end
498
+ ```