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.
- checksums.yaml +7 -0
- data/lib/async/firefly/about.rb +79 -0
- data/lib/async/firefly/accounts.rb +129 -0
- data/lib/async/firefly/attachments.rb +53 -0
- data/lib/async/firefly/autocomplete.rb +435 -0
- data/lib/async/firefly/available_budgets.rb +59 -0
- data/lib/async/firefly/bills.rb +107 -0
- data/lib/async/firefly/budgets.rb +165 -0
- data/lib/async/firefly/categories.rb +63 -0
- data/lib/async/firefly/charts.rb +73 -0
- data/lib/async/firefly/client.rb +89 -0
- data/lib/async/firefly/configurations.rb +62 -0
- data/lib/async/firefly/currencies.rb +71 -0
- data/lib/async/firefly/data.rb +37 -0
- data/lib/async/firefly/error.rb +57 -0
- data/lib/async/firefly/escape.rb +9 -0
- data/lib/async/firefly/exchange_rates.rb +77 -0
- data/lib/async/firefly/insight.rb +185 -0
- data/lib/async/firefly/links.rb +69 -0
- data/lib/async/firefly/nested.rb +75 -0
- data/lib/async/firefly/object_groups.rb +33 -0
- data/lib/async/firefly/page.rb +139 -0
- data/lib/async/firefly/piggy_banks.rb +153 -0
- data/lib/async/firefly/preferences.rb +58 -0
- data/lib/async/firefly/recurrences.rb +169 -0
- data/lib/async/firefly/redacted.rb +32 -0
- data/lib/async/firefly/representation.rb +99 -0
- data/lib/async/firefly/rule_groups.rb +55 -0
- data/lib/async/firefly/rules.rb +105 -0
- data/lib/async/firefly/search.rb +40 -0
- data/lib/async/firefly/submit.rb +139 -0
- data/lib/async/firefly/summary.rb +45 -0
- data/lib/async/firefly/sums.rb +17 -0
- data/lib/async/firefly/tags.rb +57 -0
- data/lib/async/firefly/transactions.rb +231 -0
- data/lib/async/firefly/user_groups.rb +49 -0
- data/lib/async/firefly/users.rb +35 -0
- data/lib/async/firefly/version.rb +5 -0
- data/lib/async/firefly/webhooks.rb +101 -0
- data/lib/async/firefly/wrapper.rb +65 -0
- data/lib/async/firefly.rb +2 -0
- data/license.md +21 -0
- data/readme.md +498 -0
- 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
|
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
|
+
```
|