Module: MercuryBanking::CLI::Reports
- Included in:
- Main
- Defined in:
- lib/mercury_banking/cli/reports.rb
Overview
Module for report-related commands
Class Method Summary collapse
-
.included(base) ⇒ Object
Add report-related commands to the CLI class.
Class Method Details
.included(base) ⇒ Object
Add report-related commands to the CLI class
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 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 172 173 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 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/mercury_banking/cli/reports.rb', line 8 def self.included(base) base.class_eval do # Methods that should not be exposed as commands no_commands do # The balance_sheet method has been moved to the Financials module end desc 'statement ACCOUNT_NAME', 'Generate a statement for a specific account and period' method_option :start, type: :string, default: '2020-01-01', desc: 'Start date for statement (YYYY-MM-DD)' method_option :end, type: :string, desc: 'End date for statement (YYYY-MM-DD)' method_option :format, type: :string, default: 'text', enum: %w[text json csv], desc: 'Output format' method_option :save, type: :string, desc: 'Save statement to specified file' method_option :debug, type: :boolean, default: false, desc: 'Show debug information about balance calculation' def statement(account_name) with_api_client do |client| start_date = [:start] end_date = [:end] format = [:format] debug = [:debug] # Find the account by name accounts = client.accounts account = accounts.find { |a| a["name"] == account_name } unless account puts "Error: Account '#{account_name}' not found. Available accounts:" accounts.each { |a| puts "- #{a['name']}" } return end # Get transactions for the account transactions = client.get_transactions(account["id"], start_date) # Filter by end date if specified if end_date end_date_obj = Date.parse(end_date) transactions = transactions.select do |t| transaction_date = Date.parse(t["postedAt"] || t["createdAt"]) transaction_date <= end_date_obj end end if transactions.empty? puts "No transactions found for the specified period." return end # Sort transactions by date transactions = transactions.sort_by do |t| date = t["postedAt"] || t["createdAt"] Time.parse(date) end # Calculate opening and closing balances current_balance = account["currentBalance"] closing_balance = current_balance puts "Debug: Current balance from Mercury API: $#{format('%.2f', current_balance)}" if debug # Subtract all transactions that occurred after the end date if end_date end_date_obj = Date.parse(end_date) after_end_date_transactions = transactions.select do |t| transaction_date = Date.parse(t["postedAt"] || t["createdAt"]) transaction_date > end_date_obj end if debug && after_end_date_transactions.any? puts "Debug: Transactions after end date (#{end_date}):" after_end_date_transactions.each do |t| date = t["postedAt"] ? Time.parse(t["postedAt"]).strftime("%Y-%m-%d") : Time.parse(t["createdAt"]).strftime("%Y-%m-%d") puts " #{date}: $#{format('%.2f', t['amount'])}" end end after_end_date_sum = after_end_date_transactions.sum { |t| t["amount"] } closing_balance = current_balance - after_end_date_sum if debug puts "Debug: Sum of transactions after end date: $#{format('%.2f', after_end_date_sum)}" puts "Debug: Closing balance at end date: $#{format('%.2f', closing_balance)}" end elsif debug puts "Debug: No end date specified, closing balance equals current balance" end # Calculate opening balance by subtracting all transactions in the period opening_balance = closing_balance puts "Debug: Transactions in the statement period:" if debug transactions_sum = 0 transactions.each do |t| amount = t["amount"] transactions_sum += amount if debug date = t["postedAt"] ? Time.parse(t["postedAt"]).strftime("%Y-%m-%d") : Time.parse(t["createdAt"]).strftime("%Y-%m-%d") puts " #{date}: $#{format('%.2f', amount)}" end opening_balance -= amount end if debug puts "Debug: Sum of transactions in period: $#{format('%.2f', transactions_sum)}" puts "Debug: Opening balance calculation: $#{format('%.2f', closing_balance)} - $#{format('%.2f', transactions_sum)} = $#{format( '%.2f', opening_balance )}" end # Generate statement based on format case format when 'text' generate_text_statement(account, transactions, opening_balance, closing_balance, start_date, end_date, [:save], debug) when 'json' generate_json_statement(account, transactions, opening_balance, closing_balance, start_date, end_date, [:save]) when 'csv' generate_csv_statement(account, transactions, opening_balance, closing_balance, start_date, end_date, [:save]) end end end private def generate_text_statement(account, transactions, opening_balance, closing_balance, start_date, end_date, save_path, debug = false) # Create statement header statement = [] statement << "Mercury Banking Statement" statement << "Account: #{account['name']} (#{account['accountNumber']})" statement << "Period: #{start_date} to #{end_date || 'present'}" statement << "Generated on: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}" statement << "" statement << "Opening Balance: $#{format('%.2f', opening_balance)}" statement << "" statement << "Transactions:" statement << ("-" * 80) statement << ("#{'Date'.ljust(12)}#{'Description'.ljust(40)}#{'Amount'.ljust(15)}Balance") statement << ("-" * 80) # Add transactions running_balance = opening_balance transactions.each do |t| date = t["postedAt"] ? Time.parse(t["postedAt"]).strftime("%Y-%m-%d") : Time.parse(t["createdAt"]).strftime("%Y-%m-%d") description = t["bankDescription"] || t["externalMemo"] || "Unknown transaction" description = description.length > 37 ? "#{description[0..34]}..." : description amount = t["amount"] running_balance += amount statement << (date.ljust(12) + description.ljust(40) + format("$%.2f", amount).rjust(15) + format("$%.2f", running_balance).rjust(15)) end statement << ("-" * 80) statement << "Closing Balance: $#{format('%.2f', closing_balance)}" if debug statement << "" statement << "Debug Information:" statement << "- Opening balance is calculated by taking the closing balance and subtracting all transactions in the period" statement << "- Closing balance is the current balance adjusted for any transactions after the end date" statement << "- Running balance starts with the opening balance and adds each transaction amount" end # Output or save if save_path File.write(save_path, statement.join("\n")) puts "Statement saved to #{save_path}" else puts statement.join("\n") end end def generate_json_statement(account, transactions, opening_balance, closing_balance, start_date, end_date, save_path) # Format transactions formatted_transactions = transactions.map do |t| { date: t["postedAt"] || t["createdAt"], description: t["bankDescription"] || t["externalMemo"] || "Unknown transaction", amount: t["amount"], status: t["status"] } end # Create statement object statement = { account: { name: account['name'], number: account['accountNumber'], type: account['kind'] }, period: { start_date: start_date, end_date: end_date }, balances: { opening: opening_balance, closing: closing_balance }, transactions: formatted_transactions, generated_at: Time.now.iso8601 } # Output or save json_output = JSON.pretty_generate(statement) if save_path File.write(save_path, json_output) puts "Statement saved to #{save_path}" else puts json_output end end def generate_csv_statement(_account, transactions, opening_balance, closing_balance, start_date, end_date, save_path) require 'csv' # Prepare CSV data csv_data = [] # Add header row csv_data << %w[Date Description Amount Balance Status] # Add opening balance row csv_data << [start_date, "Opening Balance", "", opening_balance, ""] # Add transactions running_balance = opening_balance transactions.each do |t| date = t["postedAt"] ? Time.parse(t["postedAt"]).strftime("%Y-%m-%d") : Time.parse(t["createdAt"]).strftime("%Y-%m-%d") description = t["bankDescription"] || t["externalMemo"] || "Unknown transaction" amount = t["amount"] running_balance += amount csv_data << [date, description, amount, running_balance, t["status"]] end # Add closing balance row csv_data << [end_date || Time.now.strftime("%Y-%m-%d"), "Closing Balance", "", closing_balance, ""] # Output or save csv_output = CSV.generate do |csv| csv_data.each { |row| csv << row } end if save_path File.write(save_path, csv_output) puts "Statement saved to #{save_path}" else puts csv_output end end end end |