mercury_banking 0.5.38 → 0.7.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 +4 -4
- data/.rubocop.yml +46 -2
- data/CHANGELOG.md +16 -0
- data/Gemfile +13 -12
- data/Gemfile.lock +1 -1
- data/Rakefile +3 -1
- data/bin/console +1 -0
- data/bin/mercury +2 -1
- data/lib/mercury_banking/api.rb +26 -23
- data/lib/mercury_banking/cli/accounts.rb +24 -24
- data/lib/mercury_banking/cli/base.rb +48 -26
- data/lib/mercury_banking/cli/financials.rb +177 -252
- data/lib/mercury_banking/cli/reconciliation.rb +284 -371
- data/lib/mercury_banking/cli/reports.rb +82 -74
- data/lib/mercury_banking/cli/transactions.rb +60 -62
- data/lib/mercury_banking/cli.rb +56 -51
- data/lib/mercury_banking/formatters/export_formatter.rb +99 -97
- data/lib/mercury_banking/formatters/table_formatter.rb +32 -30
- data/lib/mercury_banking/multi.rb +43 -37
- data/lib/mercury_banking/recipient.rb +17 -9
- data/lib/mercury_banking/reconciliation.rb +57 -58
- data/lib/mercury_banking/reports/balance_sheet.rb +210 -218
- data/lib/mercury_banking/reports/reconciliation.rb +114 -100
- data/lib/mercury_banking/utils/command_utils.rb +3 -1
- data/lib/mercury_banking/version.rb +3 -1
- data/lib/mercury_banking.rb +2 -3
- data/mercury_banking.gemspec +15 -12
- metadata +40 -39
@@ -1,406 +1,319 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module MercuryBanking
|
2
4
|
module CLI
|
3
5
|
# Module for reconciliation-related commands
|
4
6
|
module Reconciliation
|
5
|
-
#
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
# If not found by number, assume it's an ID
|
19
|
-
account_id = account_identifier
|
20
|
-
account = client.get_account(account_id)
|
21
|
-
end
|
22
|
-
else
|
23
|
-
account_id = account_identifier
|
24
|
-
account = client.get_account(account_id)
|
25
|
-
end
|
26
|
-
|
27
|
-
# Get the transaction to verify it exists
|
28
|
-
transaction = client.transaction(account_id, transaction_id)
|
29
|
-
|
30
|
-
# Mark the transaction as reconciled
|
31
|
-
reconciliation = MercuryBanking::Reconciliation.new
|
32
|
-
result = reconciliation.mark_reconciled(account_id, transaction_id)
|
33
|
-
|
34
|
-
if options[:json]
|
35
|
-
puts JSON.pretty_generate({
|
36
|
-
'account_id' => account_id,
|
37
|
-
'transaction_id' => transaction_id,
|
38
|
-
'reconciled' => true,
|
39
|
-
'already_reconciled' => !result
|
40
|
-
})
|
41
|
-
else
|
42
|
-
if result
|
43
|
-
puts "Transaction #{transaction_id} marked as reconciled."
|
44
|
-
else
|
45
|
-
puts "Transaction #{transaction_id} was already reconciled."
|
46
|
-
end
|
47
|
-
end
|
7
|
+
# Helper module for account operations
|
8
|
+
module AccountHelpers
|
9
|
+
# Resolve account identifier to account ID
|
10
|
+
def resolve_account_id(client, account_identifier)
|
11
|
+
if account_identifier.match?(/^\d+$/) && !account_identifier.include?('-')
|
12
|
+
begin
|
13
|
+
account = client.find_account_by_number(account_identifier)
|
14
|
+
account["id"]
|
15
|
+
rescue StandardError
|
16
|
+
# If not found by number, assume it's an ID
|
17
|
+
account_id = account_identifier
|
18
|
+
client.get_account(account_id) # Just verify the account exists
|
19
|
+
account_id
|
48
20
|
end
|
21
|
+
else
|
22
|
+
account_id = account_identifier
|
23
|
+
client.get_account(account_id) # Just verify the account exists
|
24
|
+
account_id
|
49
25
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
else
|
66
|
-
account_id = account_identifier
|
67
|
-
account = client.get_account(account_id)
|
68
|
-
end
|
69
|
-
|
70
|
-
# Get the transaction to verify it exists
|
71
|
-
transaction = client.transaction(account_id, transaction_id)
|
72
|
-
|
73
|
-
# Mark the transaction as unreconciled
|
74
|
-
reconciliation = MercuryBanking::Reconciliation.new
|
75
|
-
result = reconciliation.mark_unreconciled(account_id, transaction_id)
|
76
|
-
|
77
|
-
if options[:json]
|
78
|
-
puts JSON.pretty_generate({
|
79
|
-
'account_id' => account_id,
|
80
|
-
'transaction_id' => transaction_id,
|
81
|
-
'reconciled' => false,
|
82
|
-
'was_reconciled' => result
|
83
|
-
})
|
84
|
-
else
|
85
|
-
if result
|
86
|
-
puts "Transaction #{transaction_id} marked as unreconciled."
|
87
|
-
else
|
88
|
-
puts "Transaction #{transaction_id} was not reconciled."
|
89
|
-
end
|
90
|
-
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Helper module for transaction operations
|
30
|
+
module TransactionHelpers
|
31
|
+
# Filter transactions by date
|
32
|
+
def filter_transactions_by_date(transactions, _start_date, end_date)
|
33
|
+
filtered_transactions = transactions
|
34
|
+
|
35
|
+
# Filter by end date if specified
|
36
|
+
if end_date
|
37
|
+
end_date_obj = Date.parse(end_date)
|
38
|
+
filtered_transactions = filtered_transactions.select do |t|
|
39
|
+
transaction_date = Date.parse(t["postedAt"] || t["createdAt"])
|
40
|
+
transaction_date <= end_date_obj
|
91
41
|
end
|
92
42
|
end
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
end
|
43
|
+
|
44
|
+
filtered_transactions
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Helper module for display operations
|
49
|
+
module DisplayHelpers
|
50
|
+
# Display reconciliation table
|
51
|
+
def display_reconciliation_table(account, status, summary, start_date, end_date)
|
52
|
+
puts "Reconciliation Status for #{account['name']} (#{account['accountNumber']})"
|
53
|
+
puts "Period: #{start_date} to #{end_date || 'present'}"
|
54
|
+
puts
|
55
|
+
puts "Summary:"
|
56
|
+
puts "- Total Transactions: #{summary[:total_transactions]}"
|
57
|
+
puts "- Reconciled: #{summary[:reconciled_count]} ($#{format('%.2f', summary[:reconciled_amount])})"
|
58
|
+
puts "- Unreconciled: #{summary[:unreconciled_count]} ($#{format('%.2f', summary[:unreconciled_amount])})"
|
59
|
+
puts
|
60
|
+
|
61
|
+
# Display transactions
|
62
|
+
rows = status.map do |t|
|
63
|
+
date = t[:date] ? Time.parse(t[:date]).strftime("%Y-%m-%d") : "Unknown"
|
64
|
+
description = t[:description].length > 30 ? "#{t[:description][0..27]}..." : t[:description]
|
65
|
+
amount = format("$%.2f", t[:amount])
|
66
|
+
reconciled = t[:reconciled] ? "✓" : " "
|
67
|
+
reconciled_at = t[:reconciled_at] || ""
|
68
|
+
|
69
|
+
[t[:transaction_id], date, description, amount, reconciled, reconciled_at]
|
70
|
+
end
|
71
|
+
|
72
|
+
table = ::Terminal::Table.new(
|
73
|
+
headings: ['Transaction ID', 'Date', 'Description', 'Amount', 'Reconciled', 'Reconciled At'],
|
74
|
+
rows: rows
|
75
|
+
)
|
76
|
+
|
77
|
+
puts table
|
78
|
+
end
|
79
|
+
|
80
|
+
# Display debug information
|
81
|
+
def display_debug_info(account_id, account, reconcile_date, transactions, unreconciled)
|
82
|
+
puts "Debug information:"
|
83
|
+
puts "- Account ID: #{account_id}"
|
84
|
+
puts "- Account Name: #{account['name']}"
|
85
|
+
puts "- Reconcile Date: #{reconcile_date}"
|
86
|
+
puts "- Total Transactions: #{transactions.length}"
|
87
|
+
puts "- Unreconciled Transactions: #{unreconciled.length}"
|
88
|
+
end
|
89
|
+
|
90
|
+
# Display reconciliation information
|
91
|
+
def display_reconciliation_info(account, reconcile_date, start_date, unreconciled, options)
|
92
|
+
if options[:json]
|
93
|
+
result = {
|
94
|
+
'account_id' => account['id'],
|
95
|
+
'account_name' => account['name'],
|
96
|
+
'reconcile_date' => reconcile_date.to_s,
|
97
|
+
'total_transactions' => unreconciled.length,
|
98
|
+
'transactions_to_reconcile' => unreconciled.map { |t| t[:transaction_id] },
|
99
|
+
'dry_run' => options[:dry_run]
|
100
|
+
}
|
101
|
+
puts JSON.pretty_generate(result)
|
102
|
+
else
|
103
|
+
puts "Reconciling transactions for #{account['name']} (#{account['accountNumber']})"
|
104
|
+
puts "Date range: #{start_date} to #{reconcile_date}"
|
105
|
+
puts "Found #{unreconciled.length} unreconciled transactions"
|
106
|
+
puts
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Helper module for reconciliation operations
|
112
|
+
module ReconciliationHelpers
|
113
|
+
# Reconcile transactions
|
114
|
+
def reconcile_transactions(account_id, unreconciled, reconciliation, options)
|
115
|
+
reconciled_count = 0
|
116
|
+
unreconciled.each do |transaction|
|
117
|
+
transaction_id = transaction[:transaction_id]
|
118
|
+
if reconciliation.mark_reconciled(account_id, transaction_id)
|
119
|
+
reconciled_count += 1
|
120
|
+
puts "Reconciled transaction #{transaction_id}" unless options[:json]
|
172
121
|
end
|
173
122
|
end
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
# Get transactions for the account
|
200
|
-
start_date = options[:start]
|
201
|
-
puts "Fetching transactions for account #{account['name']} (#{account['accountNumber']}) since #{start_date}..." if options[:debug]
|
202
|
-
transactions = client.get_transactions(account_id, start_date)
|
203
|
-
puts "Found #{transactions.size} total transactions" if options[:debug]
|
204
|
-
|
205
|
-
# Filter by date if specified
|
206
|
-
cutoff_date = options[:date] ? Date.parse(options[:date]) : Date.today
|
207
|
-
puts "Using cutoff date: #{cutoff_date}" if options[:debug]
|
208
|
-
|
209
|
-
# Filter transactions that are on or before the cutoff date
|
210
|
-
transactions_to_reconcile = transactions.select do |t|
|
211
|
-
transaction_date = t["postedAt"] ? Date.parse(t["postedAt"]) : Date.parse(t["createdAt"])
|
212
|
-
transaction_date <= cutoff_date
|
213
|
-
end
|
214
|
-
puts "Found #{transactions_to_reconcile.size} transactions on or before cutoff date" if options[:debug]
|
215
|
-
|
216
|
-
if transactions_to_reconcile.empty?
|
123
|
+
|
124
|
+
return if options[:json]
|
125
|
+
|
126
|
+
puts
|
127
|
+
puts "Reconciled #{reconciled_count} transactions"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Module for reconcile command
|
132
|
+
module ReconcileCommand
|
133
|
+
def self.included(base)
|
134
|
+
base.class_eval do
|
135
|
+
desc 'reconcile ACCOUNT_ID_OR_NUMBER TRANSACTION_ID', 'Mark a transaction as reconciled'
|
136
|
+
def reconcile(account_identifier, transaction_id)
|
137
|
+
with_api_client do |client|
|
138
|
+
# Resolve account identifier to account ID
|
139
|
+
account_id = resolve_account_id(client, account_identifier)
|
140
|
+
|
141
|
+
# Verify the transaction exists but don't need to store it
|
142
|
+
client.transaction(account_id, transaction_id)
|
143
|
+
|
144
|
+
# Mark the transaction as reconciled
|
145
|
+
reconciliation = MercuryBanking::Reconciliation.new
|
146
|
+
result = reconciliation.mark_reconciled(account_id, transaction_id)
|
147
|
+
|
217
148
|
if options[:json]
|
218
149
|
puts JSON.pretty_generate({
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
150
|
+
'account_id' => account_id,
|
151
|
+
'transaction_id' => transaction_id,
|
152
|
+
'reconciled' => true,
|
153
|
+
'already_reconciled' => !result
|
154
|
+
})
|
155
|
+
elsif result
|
156
|
+
puts "Transaction #{transaction_id} marked as reconciled."
|
224
157
|
else
|
225
|
-
puts "
|
158
|
+
puts "Transaction #{transaction_id} was already reconciled."
|
226
159
|
end
|
227
|
-
return
|
228
160
|
end
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Module for unreconcile command
|
167
|
+
module UnreconcileCommand
|
168
|
+
def self.included(base)
|
169
|
+
base.class_eval do
|
170
|
+
desc 'unreconcile ACCOUNT_ID_OR_NUMBER TRANSACTION_ID', 'Mark a transaction as unreconciled'
|
171
|
+
def unreconcile(account_identifier, transaction_id)
|
172
|
+
with_api_client do |client|
|
173
|
+
# Resolve account identifier to account ID
|
174
|
+
account_id = resolve_account_id(client, account_identifier)
|
175
|
+
|
176
|
+
# Verify the transaction exists but don't need to store it
|
177
|
+
client.transaction(account_id, transaction_id)
|
178
|
+
|
179
|
+
# Mark the transaction as unreconciled
|
180
|
+
reconciliation = MercuryBanking::Reconciliation.new
|
181
|
+
result = reconciliation.mark_unreconciled(account_id, transaction_id)
|
182
|
+
|
240
183
|
if options[:json]
|
241
184
|
puts JSON.pretty_generate({
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
185
|
+
'account_id' => account_id,
|
186
|
+
'transaction_id' => transaction_id,
|
187
|
+
'reconciled' => false,
|
188
|
+
'was_reconciled' => result
|
189
|
+
})
|
190
|
+
elsif result
|
191
|
+
puts "Transaction #{transaction_id} marked as unreconciled."
|
247
192
|
else
|
248
|
-
puts "
|
249
|
-
end
|
250
|
-
return
|
251
|
-
end
|
252
|
-
|
253
|
-
# Display what will be reconciled
|
254
|
-
if options[:json]
|
255
|
-
transactions_data = transactions_to_reconcile.map do |t|
|
256
|
-
{
|
257
|
-
'id' => t["id"],
|
258
|
-
'date' => t["postedAt"] || t["createdAt"],
|
259
|
-
'description' => t["bankDescription"] || t["externalMemo"] || "Unknown transaction",
|
260
|
-
'amount' => t["amount"]
|
261
|
-
}
|
262
|
-
end
|
263
|
-
|
264
|
-
puts JSON.pretty_generate({
|
265
|
-
'account_id' => account_id,
|
266
|
-
'account_name' => account['name'],
|
267
|
-
'cutoff_date' => cutoff_date.to_s,
|
268
|
-
'transactions_to_reconcile' => transactions_data,
|
269
|
-
'dry_run' => options[:dry_run]
|
270
|
-
})
|
271
|
-
else
|
272
|
-
puts "Reconciliation for #{account['name']} (#{account['accountNumber']})"
|
273
|
-
puts "Transactions to reconcile up to #{cutoff_date}:"
|
274
|
-
puts
|
275
|
-
|
276
|
-
total_amount = 0
|
277
|
-
|
278
|
-
rows = transactions_to_reconcile.map do |t|
|
279
|
-
date = t["postedAt"] ? Time.parse(t["postedAt"]).strftime("%Y-%m-%d") : Time.parse(t["createdAt"]).strftime("%Y-%m-%d")
|
280
|
-
description = t["bankDescription"] || t["externalMemo"] || "Unknown transaction"
|
281
|
-
description = description.length > 30 ? "#{description[0..27]}..." : description
|
282
|
-
amount = t["amount"]
|
283
|
-
total_amount += amount
|
284
|
-
|
285
|
-
[t["id"], date, description, format("$%.2f", amount)]
|
193
|
+
puts "Transaction #{transaction_id} was not reconciled."
|
286
194
|
end
|
287
|
-
|
288
|
-
table = ::Terminal::Table.new(
|
289
|
-
headings: ['Transaction ID', 'Date', 'Description', 'Amount'],
|
290
|
-
rows: rows
|
291
|
-
)
|
292
|
-
|
293
|
-
puts table
|
294
|
-
puts
|
295
|
-
puts "Total transactions: #{transactions_to_reconcile.size}"
|
296
|
-
puts "Total amount: #{format("$%.2f", total_amount)}"
|
297
|
-
puts
|
298
|
-
puts "This is a #{options[:dry_run] ? 'DRY RUN' : 'LIVE RUN'}"
|
299
195
|
end
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Module for reconciliation_status command
|
202
|
+
module ReconciliationStatusCommand
|
203
|
+
def self.included(base)
|
204
|
+
base.class_eval do
|
205
|
+
desc 'reconciliation_status ACCOUNT_ID_OR_NUMBER', 'Show reconciliation status for an account'
|
206
|
+
method_option :start, type: :string, default: '2020-01-01', desc: 'Start date for transactions (YYYY-MM-DD)'
|
207
|
+
method_option :end, type: :string, desc: 'End date for transactions (YYYY-MM-DD)'
|
208
|
+
method_option :format, type: :string, default: 'table', enum: %w[table json],
|
209
|
+
desc: 'Output format (table or json)'
|
210
|
+
def reconciliation_status(account_identifier)
|
211
|
+
with_api_client do |client|
|
212
|
+
# Resolve account identifier to account ID
|
213
|
+
account_id = resolve_account_id(client, account_identifier)
|
214
|
+
account = client.get_account(account_id)
|
215
|
+
|
216
|
+
# Get transactions for the account
|
217
|
+
start_date = options[:start]
|
218
|
+
end_date = options[:end]
|
219
|
+
|
220
|
+
transactions = client.get_transactions(account_id, start_date)
|
221
|
+
|
222
|
+
# Filter transactions by date
|
223
|
+
transactions = filter_transactions_by_date(transactions, start_date, end_date)
|
224
|
+
|
225
|
+
# Get reconciliation status
|
226
|
+
reconciliation = MercuryBanking::Reconciliation.new
|
227
|
+
status = reconciliation.get_reconciliation_status(account_id, transactions)
|
228
|
+
summary = reconciliation.get_reconciliation_summary(account_id, transactions)
|
229
|
+
|
230
|
+
if options[:json] || options[:format] == 'json'
|
311
231
|
puts JSON.pretty_generate({
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
232
|
+
'account_id' => account_id,
|
233
|
+
'account_name' => account['name'],
|
234
|
+
'summary' => summary,
|
235
|
+
'transactions' => status
|
236
|
+
})
|
317
237
|
else
|
318
|
-
|
319
|
-
end
|
320
|
-
else
|
321
|
-
if !options[:json]
|
322
|
-
puts "Dry run completed. No transactions were reconciled."
|
323
|
-
puts "Run without --dry-run to reconcile these transactions."
|
238
|
+
display_reconciliation_table(account, status, summary, start_date, end_date)
|
324
239
|
end
|
325
240
|
end
|
326
241
|
end
|
327
242
|
end
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
end
|
348
|
-
else
|
349
|
-
account_id = account_identifier
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Module for reconcile_all command
|
247
|
+
module ReconcileAllCommand
|
248
|
+
def self.included(base)
|
249
|
+
base.class_eval do
|
250
|
+
desc 'reconcile_all ACCOUNT_ID_OR_NUMBER', 'Mark all transactions as reconciled up to a specified date'
|
251
|
+
method_option :date, type: :string,
|
252
|
+
desc: 'Date up to which to reconcile transactions (YYYY-MM-DD). Defaults to current date.'
|
253
|
+
method_option :start, type: :string, default: '2020-01-01', desc: 'Start date for transactions (YYYY-MM-DD)'
|
254
|
+
method_option :dry_run, type: :boolean, default: false,
|
255
|
+
desc: 'Show what would be reconciled without making changes'
|
256
|
+
method_option :json, type: :boolean, default: false, desc: 'Output in JSON format'
|
257
|
+
method_option :debug, type: :boolean, default: false, desc: 'Show debug information'
|
258
|
+
def reconcile_all(account_identifier)
|
259
|
+
with_api_client do |client|
|
260
|
+
# Resolve account identifier to account ID
|
261
|
+
account_id = resolve_account_id(client, account_identifier)
|
350
262
|
account = client.get_account(account_id)
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
end_date_obj = Date.parse(end_date)
|
364
|
-
transactions = transactions.select do |t|
|
365
|
-
transaction_date = t["postedAt"] ? Date.parse(t["postedAt"]) : Date.parse(t["createdAt"])
|
366
|
-
transaction_date <= end_date_obj
|
263
|
+
|
264
|
+
# Get the date up to which to reconcile transactions
|
265
|
+
reconcile_date = options[:date] ? Date.parse(options[:date]) : Date.today
|
266
|
+
start_date = options[:start]
|
267
|
+
|
268
|
+
# Get transactions for the account
|
269
|
+
transactions = client.get_transactions(account_id, start_date)
|
270
|
+
|
271
|
+
# Filter transactions up to the reconcile date
|
272
|
+
transactions_to_reconcile = transactions.select do |t|
|
273
|
+
transaction_date = Date.parse(t["postedAt"] || t["createdAt"])
|
274
|
+
transaction_date <= reconcile_date
|
367
275
|
end
|
276
|
+
|
277
|
+
# Get current reconciliation status
|
278
|
+
reconciliation = MercuryBanking::Reconciliation.new
|
279
|
+
status = reconciliation.get_reconciliation_status(account_id, transactions_to_reconcile)
|
280
|
+
|
281
|
+
# Filter for unreconciled transactions
|
282
|
+
unreconciled = status.reject { |t| t[:reconciled] }
|
283
|
+
|
284
|
+
# Display debug information if requested
|
285
|
+
display_debug_info(account_id, account, reconcile_date, transactions_to_reconcile, unreconciled) if options[:debug]
|
286
|
+
|
287
|
+
# Display reconciliation information
|
288
|
+
display_reconciliation_info(account, reconcile_date, start_date, unreconciled, options)
|
289
|
+
|
290
|
+
# If this is a dry run, don't actually reconcile anything
|
291
|
+
if options[:dry_run]
|
292
|
+
puts "DRY RUN: No transactions will be reconciled"
|
293
|
+
return
|
294
|
+
end
|
295
|
+
|
296
|
+
# Reconcile transactions
|
297
|
+
reconcile_transactions(account_id, unreconciled, reconciliation, options)
|
368
298
|
end
|
369
|
-
|
370
|
-
# Get reconciled transactions
|
371
|
-
reconciliation = MercuryBanking::Reconciliation.new
|
372
|
-
reconciled_ids = reconciliation.get_reconciled_transactions(account_id)
|
373
|
-
|
374
|
-
# Filter transactions if not exporting all
|
375
|
-
unless export_all
|
376
|
-
transactions = transactions.select { |t| reconciled_ids.include?(t["id"]) }
|
377
|
-
end
|
378
|
-
|
379
|
-
if transactions.empty?
|
380
|
-
puts "No #{export_all ? '' : 'reconciled '}transactions found to export."
|
381
|
-
return
|
382
|
-
end
|
383
|
-
|
384
|
-
# Determine output file path
|
385
|
-
output_file = options[:output] || "reconciled_transactions.#{format}"
|
386
|
-
|
387
|
-
# Export transactions in the specified format
|
388
|
-
case format
|
389
|
-
when 'ledger'
|
390
|
-
export_to_ledger(transactions, output_file, reconciled_ids, false)
|
391
|
-
when 'beancount'
|
392
|
-
export_to_beancount(transactions, output_file, reconciled_ids, false)
|
393
|
-
when 'hledger'
|
394
|
-
export_to_hledger(transactions, output_file, reconciled_ids)
|
395
|
-
when 'csv'
|
396
|
-
export_to_csv(transactions, output_file, reconciled_ids)
|
397
|
-
end
|
398
|
-
|
399
|
-
puts "Exported #{transactions.count} transaction(s) to #{output_file} in #{format} format."
|
400
299
|
end
|
401
300
|
end
|
402
301
|
end
|
403
302
|
end
|
303
|
+
|
304
|
+
# Add reconciliation-related commands to the CLI class
|
305
|
+
def self.included(base)
|
306
|
+
base.class_eval do
|
307
|
+
include MercuryBanking::CLI::Reconciliation::AccountHelpers
|
308
|
+
include MercuryBanking::CLI::Reconciliation::TransactionHelpers
|
309
|
+
include MercuryBanking::CLI::Reconciliation::DisplayHelpers
|
310
|
+
include MercuryBanking::CLI::Reconciliation::ReconciliationHelpers
|
311
|
+
include MercuryBanking::CLI::Reconciliation::ReconcileCommand
|
312
|
+
include MercuryBanking::CLI::Reconciliation::UnreconcileCommand
|
313
|
+
include MercuryBanking::CLI::Reconciliation::ReconciliationStatusCommand
|
314
|
+
include MercuryBanking::CLI::Reconciliation::ReconcileAllCommand
|
315
|
+
end
|
316
|
+
end
|
404
317
|
end
|
405
318
|
end
|
406
|
-
end
|
319
|
+
end
|