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,297 +1,222 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'mercury_banking/cli/base'
|
2
4
|
require 'mercury_banking/formatters/export_formatter'
|
3
|
-
require 'mercury_banking/reports/balance_sheet'
|
4
5
|
|
5
6
|
module MercuryBanking
|
6
7
|
module CLI
|
8
|
+
# Module for balance check related functionality
|
9
|
+
module BalanceSheetHelper
|
10
|
+
# Get current balances for all Mercury accounts
|
11
|
+
def get_mercury_balances(accounts)
|
12
|
+
mercury_balances = {}
|
13
|
+
accounts.each do |account|
|
14
|
+
# Use the simplified account name for easier matching with ledger accounts
|
15
|
+
account_name = account['name'].gsub(/•+\d+/, '').strip
|
16
|
+
mercury_balances[account_name] = account['currentBalance'].to_f
|
17
|
+
end
|
18
|
+
|
19
|
+
if options[:verbose]
|
20
|
+
puts "\nMercury account balances:"
|
21
|
+
mercury_balances.each do |name, balance|
|
22
|
+
puts " #{name}: $#{format('%.2f', balance)}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
mercury_balances
|
27
|
+
end
|
28
|
+
|
29
|
+
# Display cross-check between Mercury and ledger balances
|
30
|
+
def display_balance_cross_check(mercury_balances, ledger_balances, verbose)
|
31
|
+
puts "\n=== Balance Sheet Cross-Check ==="
|
32
|
+
puts "#{'Mercury Account'.ljust(30)}#{'Mercury Balance'.ljust(15)}#{'Ledger Balance'.ljust(15)}Difference"
|
33
|
+
puts "-" * 75
|
34
|
+
|
35
|
+
total_diff = 0
|
36
|
+
|
37
|
+
if verbose
|
38
|
+
puts "\nMatching accounts:"
|
39
|
+
puts "Mercury accounts: #{mercury_balances.keys.join(', ')}"
|
40
|
+
puts "Ledger accounts: #{ledger_balances.keys.join(', ')}"
|
41
|
+
end
|
42
|
+
|
43
|
+
mercury_balances.each do |account_name, mercury_balance|
|
44
|
+
# Find the corresponding ledger account
|
45
|
+
ledger_account_key = nil
|
46
|
+
ledger_balance = 0
|
47
|
+
|
48
|
+
# Try different matching strategies
|
49
|
+
if ledger_balances.key?("Assets:Mercury:#{account_name.split.first}")
|
50
|
+
# Direct match with first word (e.g., "Checking" or "Savings")
|
51
|
+
ledger_account_key = "Assets:Mercury:#{account_name.split.first}"
|
52
|
+
ledger_balance = ledger_balances[ledger_account_key]
|
53
|
+
elsif account_name.include?("Checking") && ledger_balances.keys.any? { |k| k.include?("Checking") }
|
54
|
+
# Match by account type
|
55
|
+
ledger_account_key = ledger_balances.keys.find { |k| k.include?("Checking") }
|
56
|
+
ledger_balance = ledger_balances[ledger_account_key]
|
57
|
+
elsif account_name.include?("Savings") && ledger_balances.keys.any? { |k| k.include?("Savings") }
|
58
|
+
# Match by account type
|
59
|
+
ledger_account_key = ledger_balances.keys.find { |k| k.include?("Savings") }
|
60
|
+
ledger_balance = ledger_balances[ledger_account_key]
|
61
|
+
end
|
62
|
+
|
63
|
+
# Debug information
|
64
|
+
if verbose
|
65
|
+
puts "\nMatching for Mercury account '#{account_name}':"
|
66
|
+
puts " Found match: #{ledger_account_key || 'None'}"
|
67
|
+
puts " Mercury balance: $#{format('%.2f', mercury_balance)}"
|
68
|
+
puts " Ledger balance: $#{format('%.2f', ledger_balance)}"
|
69
|
+
end
|
70
|
+
|
71
|
+
# Skip zero-balance accounts unless in verbose mode
|
72
|
+
if mercury_balance == 0 && ledger_balance == 0
|
73
|
+
next unless verbose
|
74
|
+
end
|
75
|
+
|
76
|
+
# Calculate difference
|
77
|
+
diff = mercury_balance - ledger_balance
|
78
|
+
total_diff += diff.abs
|
79
|
+
|
80
|
+
# Format for display
|
81
|
+
mercury_balance_str = format("$%.2f", mercury_balance)
|
82
|
+
ledger_balance_str = format("$%.2f", ledger_balance)
|
83
|
+
diff_str = format("$%.2f", diff)
|
84
|
+
|
85
|
+
# Add warning marker for differences
|
86
|
+
diff_marker = diff.abs > 0.01 ? " ⚠️" : ""
|
87
|
+
|
88
|
+
puts account_name.ljust(30) + mercury_balance_str.ljust(15) + ledger_balance_str.ljust(15) + diff_str + diff_marker
|
89
|
+
end
|
90
|
+
|
91
|
+
puts "-" * 75
|
92
|
+
puts "Total Discrepancy: #{format('$%.2f', total_diff)}"
|
93
|
+
|
94
|
+
if total_diff > 0.01
|
95
|
+
puts "\n⚠️ Warning: There are discrepancies between Mercury account balances and the ledger balance sheet."
|
96
|
+
puts "This could be due to:"
|
97
|
+
puts " - Transactions not yet recorded in the ledger"
|
98
|
+
puts " - Incorrect categorization of transactions"
|
99
|
+
puts " - Timing differences between when transactions were recorded"
|
100
|
+
else
|
101
|
+
puts "\n✓ Balance sheet matches Mercury account balances."
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
7
106
|
# Module for financial report commands
|
8
107
|
module Financials
|
9
108
|
# Define the financials command class
|
10
109
|
class FinancialsCommand < Thor
|
11
110
|
include MercuryBanking::CLI::Base
|
12
111
|
include MercuryBanking::Formatters::ExportFormatter
|
13
|
-
include MercuryBanking::
|
14
|
-
|
15
|
-
desc "
|
112
|
+
include MercuryBanking::CLI::BalanceSheetHelper
|
113
|
+
|
114
|
+
desc "balancecheck", "Check that transactions match the bank balance shown in Mercury"
|
16
115
|
method_option :format, type: :string, default: 'ledger', desc: 'Accounting format to use (ledger or beancount)'
|
17
|
-
method_option :
|
18
|
-
method_option :end, type: :string, desc: 'End date for transactions (YYYY-MM-DD)'
|
19
|
-
method_option :save_report, type: :string, desc: 'Save report output to specified file'
|
116
|
+
method_option :ledger_file, type: :string, desc: 'Ledger file to check against Mercury balances'
|
20
117
|
method_option :verbose, type: :boolean, default: false, desc: 'Show detailed debug information'
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
# Methods that should not be exposed as commands
|
28
|
-
no_commands do
|
29
|
-
# Implementation of the balance sheet functionality
|
30
|
-
def balance_sheet
|
31
|
-
# If a ledger file is provided, use it directly
|
32
|
-
if options[:ledger_file]
|
33
|
-
ledger_file = options[:ledger_file]
|
34
|
-
format = options[:format]
|
35
|
-
end_date = options[:end]
|
36
|
-
verbose = options[:verbose]
|
37
|
-
|
38
|
-
puts "Using existing ledger file: #{ledger_file}"
|
39
|
-
|
40
|
-
if !File.exist?(ledger_file)
|
41
|
-
puts "Error: Ledger file not found at #{ledger_file}"
|
42
|
-
return
|
43
|
-
end
|
44
|
-
|
45
|
-
# Generate balance sheet from the ledger file
|
46
|
-
case format
|
47
|
-
when 'ledger'
|
48
|
-
balance_sheet_output = generate_ledger_balance_sheet(ledger_file, end_date)
|
49
|
-
if balance_sheet_output
|
50
|
-
puts "\n=== Balance Sheet ===\n"
|
51
|
-
puts balance_sheet_output
|
52
|
-
|
53
|
-
# Save report to file if requested
|
54
|
-
if options[:save_report]
|
55
|
-
File.write(options[:save_report], balance_sheet_output)
|
56
|
-
puts "\nReport saved to #{options[:save_report]}"
|
57
|
-
end
|
58
|
-
else
|
59
|
-
puts "Failed to generate balance sheet from ledger file."
|
60
|
-
end
|
61
|
-
when 'beancount'
|
62
|
-
balance_sheet_output = generate_beancount_balance_sheet(ledger_file, end_date)
|
63
|
-
if balance_sheet_output
|
64
|
-
puts "\n=== Balance Sheet ===\n"
|
65
|
-
puts balance_sheet_output
|
66
|
-
|
67
|
-
# Save report to file if requested
|
68
|
-
if options[:save_report]
|
69
|
-
File.write(options[:save_report], balance_sheet_output)
|
70
|
-
puts "\nReport saved to #{options[:save_report]}"
|
71
|
-
end
|
72
|
-
else
|
73
|
-
puts "Failed to generate balance sheet from beancount file."
|
74
|
-
end
|
75
|
-
else
|
76
|
-
puts "Unsupported format: #{format}. Please use 'ledger' or 'beancount'."
|
77
|
-
end
|
78
|
-
|
118
|
+
def balancecheck
|
119
|
+
with_api_client do |client|
|
120
|
+
accounts = client.accounts
|
121
|
+
|
122
|
+
unless options[:ledger_file] && File.exist?(options[:ledger_file])
|
123
|
+
puts "Error: You must provide a valid ledger file to check. Use --ledger-file option."
|
79
124
|
return
|
80
125
|
end
|
126
|
+
|
127
|
+
# Get balances from the ledger file
|
128
|
+
ledger_file = options[:ledger_file]
|
129
|
+
format = options[:format] || File.extname(ledger_file).delete('.').downcase
|
81
130
|
|
82
|
-
#
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
mercury_balances = {}
|
89
|
-
accounts.each do |account|
|
90
|
-
mercury_balances[account['name']] = account['currentBalance']
|
91
|
-
end
|
92
|
-
|
93
|
-
# Get all transactions for the balance sheet
|
94
|
-
start_date = options[:start]
|
95
|
-
end_date = options[:end]
|
96
|
-
format = options[:format]
|
97
|
-
verbose = options[:verbose]
|
98
|
-
|
99
|
-
date_range = "since #{start_date}"
|
100
|
-
date_range += " until #{end_date}" if end_date
|
101
|
-
|
102
|
-
puts "Fetching transactions for all accounts #{date_range}..."
|
103
|
-
|
104
|
-
transactions = client.get_all_transactions(start_date)
|
105
|
-
|
106
|
-
# Filter by end date if specified
|
107
|
-
if end_date
|
108
|
-
end_date_obj = Date.parse(end_date)
|
109
|
-
transactions = transactions.select do |t|
|
110
|
-
transaction_date = t["postedAt"] ? Date.parse(t["postedAt"]) : Date.parse(t["createdAt"])
|
111
|
-
transaction_date <= end_date_obj
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
if transactions.empty?
|
116
|
-
puts "No transactions found to generate balance sheet."
|
117
|
-
return
|
118
|
-
end
|
119
|
-
|
120
|
-
# Create a temporary file
|
121
|
-
require 'tempfile'
|
122
|
-
temp_file = Tempfile.new(['mercury_transactions', ".#{format}"])
|
123
|
-
output_file = temp_file.path
|
124
|
-
|
125
|
-
# Export transactions in the specified format
|
126
|
-
case format
|
127
|
-
when 'ledger'
|
128
|
-
export_to_ledger(transactions, output_file, [], verbose)
|
129
|
-
balance_sheet_output = generate_ledger_balance_sheet(output_file, end_date)
|
130
|
-
when 'beancount'
|
131
|
-
export_to_beancount(transactions, output_file, [], verbose)
|
132
|
-
balance_sheet_output = generate_beancount_balance_sheet(output_file, end_date)
|
133
|
-
else
|
134
|
-
puts "Unsupported format: #{format}. Please use 'ledger' or 'beancount'."
|
135
|
-
temp_file.unlink
|
136
|
-
return
|
137
|
-
end
|
138
|
-
|
139
|
-
# Parse the balance sheet output to extract account balances
|
140
|
-
ledger_balances = parse_balance_sheet_output(balance_sheet_output, format, verbose)
|
141
|
-
|
142
|
-
# Cross-check Mercury balances with ledger balances
|
143
|
-
puts "\n=== Balance Sheet Cross-Check ==="
|
144
|
-
puts "Mercury Account".ljust(30) + "Mercury Balance".ljust(15) + "Ledger Balance".ljust(15) + "Difference"
|
145
|
-
puts "-" * 75
|
146
|
-
|
147
|
-
total_diff = 0
|
148
|
-
mercury_balances.each do |account_name, mercury_balance|
|
149
|
-
# Find the corresponding ledger account (might be prefixed with Assets:)
|
150
|
-
ledger_account_key = ledger_balances.keys.find { |k| k.include?(account_name) }
|
151
|
-
|
152
|
-
# Debug information
|
153
|
-
if verbose
|
154
|
-
puts "Looking for Mercury account '#{account_name}' in ledger accounts:"
|
155
|
-
ledger_balances.keys.each do |k|
|
156
|
-
puts " - #{k} (match: #{k.include?(account_name) ? 'Yes' : 'No'})"
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
ledger_balance = ledger_account_key ? ledger_balances[ledger_account_key] : 0
|
161
|
-
|
162
|
-
# Calculate difference
|
163
|
-
diff = mercury_balance - ledger_balance
|
164
|
-
total_diff += diff.abs
|
165
|
-
|
166
|
-
# Format for display
|
167
|
-
mercury_balance_str = format("$%.2f", mercury_balance)
|
168
|
-
ledger_balance_str = format("$%.2f", ledger_balance)
|
169
|
-
diff_str = format("$%.2f", diff)
|
170
|
-
|
171
|
-
# Add warning marker for differences
|
172
|
-
diff_marker = diff.abs > 0.01 ? " ⚠️" : ""
|
173
|
-
|
174
|
-
puts account_name.ljust(30) + mercury_balance_str.ljust(15) + ledger_balance_str.ljust(15) + diff_str + diff_marker
|
175
|
-
end
|
176
|
-
|
177
|
-
puts "-" * 75
|
178
|
-
puts "Total Discrepancy: #{format("$%.2f", total_diff)}"
|
179
|
-
|
180
|
-
if total_diff > 0.01
|
181
|
-
puts "\n⚠️ Warning: There are discrepancies between Mercury account balances and the ledger balance sheet."
|
182
|
-
puts "This could be due to:"
|
183
|
-
puts " - Transactions not yet recorded in the ledger"
|
184
|
-
puts " - Incorrect categorization of transactions"
|
185
|
-
puts " - Timing differences between when transactions were recorded"
|
186
|
-
else
|
187
|
-
puts "\n✓ Balance sheet matches Mercury account balances."
|
188
|
-
end
|
189
|
-
|
190
|
-
# Save report to file if requested
|
191
|
-
if options[:save_report] && balance_sheet_output
|
192
|
-
full_report = balance_sheet_output + "\n\n" + "=== Balance Sheet Cross-Check ===\n"
|
193
|
-
mercury_balances.each do |account_name, mercury_balance|
|
194
|
-
ledger_account_key = ledger_balances.keys.find { |k| k.include?(account_name) }
|
195
|
-
ledger_balance = ledger_account_key ? ledger_balances[ledger_account_key] : 0
|
196
|
-
diff = mercury_balance - ledger_balance
|
197
|
-
full_report += "#{account_name}: Mercury $#{format("%.2f", mercury_balance)} vs Ledger $#{format("%.2f", ledger_balance)} (Diff: $#{format("%.2f", diff)})\n"
|
198
|
-
end
|
199
|
-
|
200
|
-
File.write(options[:save_report], full_report)
|
201
|
-
puts "\nReport saved to #{options[:save_report]}"
|
202
|
-
end
|
203
|
-
|
204
|
-
# Clean up temporary file
|
205
|
-
temp_file.unlink
|
131
|
+
# Get ledger balances
|
132
|
+
ledger_balances = get_ledger_balances(ledger_file, format)
|
133
|
+
|
134
|
+
if ledger_balances.empty?
|
135
|
+
puts "No account balances found in the ledger file."
|
136
|
+
return
|
206
137
|
end
|
138
|
+
|
139
|
+
# Get current balances for all Mercury accounts
|
140
|
+
mercury_balances = get_mercury_balances(accounts)
|
141
|
+
|
142
|
+
# Display cross-check between Mercury and ledger balances
|
143
|
+
display_balance_cross_check(mercury_balances, ledger_balances, options[:verbose])
|
207
144
|
end
|
208
145
|
end
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
method_option :verbose, type: :boolean, default: false, desc: 'Show detailed debug information'
|
216
|
-
def incomestatement
|
217
|
-
# Access the parent class to call with_api_client
|
218
|
-
parent_class = self.class.parent_class
|
219
|
-
parent_instance = parent_class.new
|
220
|
-
|
221
|
-
parent_instance.with_api_client do |client|
|
222
|
-
# Get all transactions for the income statement
|
223
|
-
start_date = options[:start]
|
224
|
-
end_date = options[:end]
|
225
|
-
format = options[:format]
|
226
|
-
verbose = options[:verbose]
|
227
|
-
|
228
|
-
date_range = "since #{start_date}"
|
229
|
-
date_range += " until #{end_date}" if end_date
|
146
|
+
|
147
|
+
# Methods that should not be exposed as commands
|
148
|
+
no_commands do
|
149
|
+
# Get balances from ledger file
|
150
|
+
def get_ledger_balances(ledger_file, format)
|
151
|
+
ledger_balances = {}
|
230
152
|
|
231
|
-
|
153
|
+
# Get balance sheet output from the ledger file
|
154
|
+
balance_sheet_output = case format
|
155
|
+
when 'ledger'
|
156
|
+
`ledger -f #{ledger_file} balance Assets:Mercury`
|
157
|
+
when 'beancount'
|
158
|
+
`bean-report #{ledger_file} balances`
|
159
|
+
else
|
160
|
+
puts "Unsupported format: #{format}. Please use 'ledger' or 'beancount'."
|
161
|
+
return {}
|
162
|
+
end
|
232
163
|
|
233
|
-
|
164
|
+
puts "Raw ledger output:" if options[:verbose]
|
165
|
+
puts balance_sheet_output if options[:verbose]
|
234
166
|
|
235
|
-
#
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
167
|
+
# Directly parse the specific format we know ledger outputs
|
168
|
+
balance_sheet_output.each_line do |line|
|
169
|
+
line = line.strip
|
170
|
+
# Skip empty lines
|
171
|
+
next if line.empty?
|
172
|
+
|
173
|
+
# Split the line on whitespace but preserve the account path
|
174
|
+
parts = line.split(/\s+/, 2) # Split on first whitespace
|
175
|
+
if parts.length == 2
|
176
|
+
amount_part = parts[0]
|
177
|
+
account_part = parts[1]
|
178
|
+
|
179
|
+
# Parse amount - remove $ and , characters
|
180
|
+
amount = amount_part.gsub(/[$,]/, '').to_f
|
181
|
+
|
182
|
+
# Store in the hash
|
183
|
+
ledger_balances[account_part] = amount
|
184
|
+
|
185
|
+
puts "Matched: #{account_part} = $#{amount}" if options[:verbose]
|
241
186
|
end
|
242
187
|
end
|
243
188
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
parent_instance.export_to_ledger(transactions, output_file, [], verbose)
|
253
|
-
income_statement_output = parent_instance.generate_ledger_reports(output_file, 'income', nil, end_date)
|
254
|
-
when 'beancount'
|
255
|
-
parent_instance.export_to_beancount(transactions, output_file, [], verbose)
|
256
|
-
income_statement_output = parent_instance.generate_beancount_reports(output_file, 'income', nil, end_date)
|
257
|
-
else
|
258
|
-
puts "Unsupported format: #{format}. Please use 'ledger' or 'beancount'."
|
259
|
-
temp_file.unlink
|
260
|
-
return
|
261
|
-
end
|
262
|
-
|
263
|
-
# Display the income statement
|
264
|
-
puts income_statement_output
|
265
|
-
|
266
|
-
# Save the report to a file if requested
|
267
|
-
if options[:save_report]
|
268
|
-
File.open(options[:save_report], 'w') do |file|
|
269
|
-
file.puts income_statement_output
|
189
|
+
if options[:verbose]
|
190
|
+
puts "Parsed ledger balances:"
|
191
|
+
if ledger_balances.empty?
|
192
|
+
puts " No account balances found in the ledger file."
|
193
|
+
else
|
194
|
+
ledger_balances.each do |account, amount|
|
195
|
+
puts " #{account}: $#{format('%.2f', amount)}"
|
196
|
+
end
|
270
197
|
end
|
271
|
-
puts "\nReport saved to #{options[:save_report]}"
|
272
198
|
end
|
273
199
|
|
274
|
-
|
275
|
-
temp_file.unlink
|
200
|
+
ledger_balances
|
276
201
|
end
|
277
202
|
end
|
278
|
-
|
203
|
+
|
279
204
|
# Store reference to parent class
|
280
205
|
class << self
|
281
206
|
attr_accessor :parent_class
|
282
207
|
end
|
283
208
|
end
|
284
|
-
|
209
|
+
|
285
210
|
# Add financial report commands to the CLI class
|
286
211
|
def self.included(base)
|
287
212
|
base.class_eval do
|
288
213
|
# Register the financials command
|
289
214
|
desc "financials SUBCOMMAND", "Financial reporting commands"
|
290
215
|
subcommand "financials", FinancialsCommand
|
291
|
-
|
216
|
+
|
292
217
|
# Set the parent class for the subcommand
|
293
218
|
FinancialsCommand.parent_class = base
|
294
|
-
|
219
|
+
|
295
220
|
# Remove the old methods with underscores if they exist
|
296
221
|
remove_method :financials_balancesheet if method_defined?(:financials_balancesheet)
|
297
222
|
remove_method :financials_incomestatement if method_defined?(:financials_incomestatement)
|
@@ -299,4 +224,4 @@ module MercuryBanking
|
|
299
224
|
end
|
300
225
|
end
|
301
226
|
end
|
302
|
-
end
|
227
|
+
end
|