mercury_banking 0.5.37 → 0.6.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.
@@ -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
- # Add reconciliation-related commands to the CLI class
6
- def self.included(base)
7
- base.class_eval do
8
- desc 'reconcile ACCOUNT_ID_OR_NUMBER TRANSACTION_ID', 'Mark a transaction as reconciled'
9
- def reconcile(account_identifier, transaction_id)
10
- with_api_client do |client|
11
- # Determine if we're dealing with an account ID or account number
12
- account_id = nil
13
- if account_identifier.match?(/^\d+$/) && !account_identifier.include?('-')
14
- begin
15
- account = client.find_account_by_number(account_identifier)
16
- account_id = account["id"]
17
- rescue => e
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
- desc 'unreconcile ACCOUNT_ID_OR_NUMBER TRANSACTION_ID', 'Mark a transaction as unreconciled'
52
- def unreconcile(account_identifier, transaction_id)
53
- with_api_client do |client|
54
- # Determine if we're dealing with an account ID or account number
55
- account_id = nil
56
- if account_identifier.match?(/^\d+$/) && !account_identifier.include?('-')
57
- begin
58
- account = client.find_account_by_number(account_identifier)
59
- account_id = account["id"]
60
- rescue => e
61
- # If not found by number, assume it's an ID
62
- account_id = account_identifier
63
- account = client.get_account(account_id)
64
- end
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
- desc 'reconciliation_status ACCOUNT_ID_OR_NUMBER', 'Show reconciliation status for an account'
95
- method_option :start, type: :string, default: '2020-01-01', desc: 'Start date for transactions (YYYY-MM-DD)'
96
- method_option :end, type: :string, desc: 'End date for transactions (YYYY-MM-DD)'
97
- method_option :format, type: :string, default: 'table', enum: ['table', 'json'], desc: 'Output format (table or json)'
98
- def reconciliation_status(account_identifier)
99
- with_api_client do |client|
100
- # Determine if we're dealing with an account ID or account number
101
- account_id = nil
102
- if account_identifier.match?(/^\d+$/) && !account_identifier.include?('-')
103
- begin
104
- account = client.find_account_by_number(account_identifier)
105
- account_id = account["id"]
106
- rescue => e
107
- # If not found by number, assume it's an ID
108
- account_id = account_identifier
109
- account = client.get_account(account_id)
110
- end
111
- else
112
- account_id = account_identifier
113
- account = client.get_account(account_id)
114
- end
115
-
116
- # Get transactions for the account
117
- start_date = options[:start]
118
- end_date = options[:end]
119
-
120
- transactions = client.get_transactions(account_id, start_date)
121
-
122
- # Filter by end date if specified
123
- if end_date
124
- end_date_obj = Date.parse(end_date)
125
- transactions = transactions.select do |t|
126
- transaction_date = t["postedAt"] ? Date.parse(t["postedAt"]) : Date.parse(t["createdAt"])
127
- transaction_date <= end_date_obj
128
- end
129
- end
130
-
131
- # Get reconciliation status
132
- reconciliation = MercuryBanking::Reconciliation.new
133
- status = reconciliation.get_reconciliation_status(account_id, transactions)
134
- summary = reconciliation.get_reconciliation_summary(account_id, transactions)
135
-
136
- if options[:json] || options[:format] == 'json'
137
- puts JSON.pretty_generate({
138
- 'account_id' => account_id,
139
- 'account_name' => account['name'],
140
- 'summary' => summary,
141
- 'transactions' => status
142
- })
143
- else
144
- # Display summary
145
- puts "Reconciliation Status for #{account['name']} (#{account['accountNumber']})"
146
- puts "Period: #{start_date} to #{end_date || 'present'}"
147
- puts
148
- puts "Summary:"
149
- puts "- Total Transactions: #{summary[:total_transactions]}"
150
- puts "- Reconciled: #{summary[:reconciled_count]} ($#{format("%.2f", summary[:reconciled_amount])})"
151
- puts "- Unreconciled: #{summary[:unreconciled_count]} ($#{format("%.2f", summary[:unreconciled_amount])})"
152
- puts
153
-
154
- # Display transactions
155
- rows = status.map do |t|
156
- date = t[:date] ? Time.parse(t[:date]).strftime("%Y-%m-%d") : "Unknown"
157
- description = t[:description].length > 30 ? "#{t[:description][0..27]}..." : t[:description]
158
- amount = format("$%.2f", t[:amount])
159
- reconciled = t[:reconciled] ? "✓" : " "
160
- reconciled_at = t[:reconciled_at] || ""
161
-
162
- [t[:transaction_id], date, description, amount, reconciled, reconciled_at]
163
- end
164
-
165
- table = ::Terminal::Table.new(
166
- headings: ['Transaction ID', 'Date', 'Description', 'Amount', 'Reconciled', 'Reconciled At'],
167
- rows: rows
168
- )
169
-
170
- puts table
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
- desc 'reconcile_all ACCOUNT_ID_OR_NUMBER', 'Mark all transactions as reconciled up to a specified date'
176
- method_option :date, type: :string, desc: 'Date up to which to reconcile transactions (YYYY-MM-DD). Defaults to current date.'
177
- method_option :start, type: :string, default: '2020-01-01', desc: 'Start date for transactions (YYYY-MM-DD)'
178
- method_option :dry_run, type: :boolean, default: false, desc: 'Show what would be reconciled without making changes'
179
- method_option :json, type: :boolean, default: false, desc: 'Output in JSON format'
180
- method_option :debug, type: :boolean, default: false, desc: 'Show debug information'
181
- def reconcile_all(account_identifier)
182
- with_api_client do |client|
183
- # Determine if we're dealing with an account ID or account number
184
- account_id = nil
185
- if account_identifier.match?(/^\d+$/) && !account_identifier.include?('-')
186
- begin
187
- account = client.find_account_by_number(account_identifier)
188
- account_id = account["id"]
189
- rescue => e
190
- # If not found by number, assume it's an ID
191
- account_id = account_identifier
192
- account = client.get_account(account_id)
193
- end
194
- else
195
- account_id = account_identifier
196
- account = client.get_account(account_id)
197
- end
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
- 'account_id' => account_id,
220
- 'account_name' => account['name'],
221
- 'transactions_reconciled' => 0,
222
- 'message' => "No transactions found to reconcile."
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 "No transactions found to reconcile for #{account['name']} (#{account['accountNumber']})."
158
+ puts "Transaction #{transaction_id} was already reconciled."
226
159
  end
227
- return
228
160
  end
229
-
230
- # Get already reconciled transactions
231
- reconciliation = MercuryBanking::Reconciliation.new
232
- reconciled_ids = reconciliation.get_reconciled_transactions(account_id)
233
- puts "Found #{reconciled_ids.size} already reconciled transactions" if options[:debug]
234
-
235
- # Filter out already reconciled transactions
236
- transactions_to_reconcile = transactions_to_reconcile.reject { |t| reconciled_ids.include?(t["id"]) }
237
- puts "Found #{transactions_to_reconcile.size} transactions to reconcile" if options[:debug]
238
-
239
- if transactions_to_reconcile.empty?
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
- 'account_id' => account_id,
243
- 'account_name' => account['name'],
244
- 'transactions_reconciled' => 0,
245
- 'message' => "All transactions are already reconciled up to #{cutoff_date}."
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 "All transactions are already reconciled up to #{cutoff_date} for #{account['name']} (#{account['accountNumber']})."
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
- # Mark transactions as reconciled if not a dry run
302
- unless options[:dry_run]
303
- reconciled_count = 0
304
-
305
- transactions_to_reconcile.each do |t|
306
- result = reconciliation.mark_reconciled(account_id, t["id"])
307
- reconciled_count += 1 if result
308
- end
309
-
310
- if options[:json]
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
- 'account_id' => account_id,
313
- 'account_name' => account['name'],
314
- 'transactions_reconciled' => reconciled_count,
315
- 'message' => "Successfully reconciled #{reconciled_count} transactions."
316
- })
232
+ 'account_id' => account_id,
233
+ 'account_name' => account['name'],
234
+ 'summary' => summary,
235
+ 'transactions' => status
236
+ })
317
237
  else
318
- puts "Successfully reconciled #{reconciled_count} transactions."
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
- desc 'export_reconciled ACCOUNT_ID_OR_NUMBER', 'Export reconciled transactions to a ledger file'
330
- method_option :start, type: :string, default: '2020-01-01', desc: 'Start date for transactions (YYYY-MM-DD)'
331
- method_option :end, type: :string, desc: 'End date for transactions (YYYY-MM-DD)'
332
- method_option :format, type: :string, default: 'ledger', enum: ['ledger', 'beancount', 'hledger', 'csv'], desc: 'Export format'
333
- method_option :output, type: :string, desc: 'Output file path (defaults to reconciled_transactions.<format>)'
334
- method_option :all, type: :boolean, default: false, desc: 'Export all transactions with reconciliation status'
335
- def export_reconciled(account_identifier)
336
- with_api_client do |client|
337
- # Determine if we're dealing with an account ID or account number
338
- account_id = nil
339
- if account_identifier.match?(/^\d+$/) && !account_identifier.include?('-')
340
- begin
341
- account = client.find_account_by_number(account_identifier)
342
- account_id = account["id"]
343
- rescue => e
344
- # If not found by number, assume it's an ID
345
- account_id = account_identifier
346
- account = client.get_account(account_id)
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
- end
352
-
353
- # Get transactions for the account
354
- start_date = options[:start]
355
- end_date = options[:end]
356
- format = options[:format]
357
- export_all = options[:all]
358
-
359
- transactions = client.get_transactions(account_id, start_date)
360
-
361
- # Filter by end date if specified
362
- if end_date
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