thefox-wallet 0.17.1 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.editorconfig +5 -4
- data/.env.example +4 -0
- data/.gitignore +6 -9
- data/.gitlab-ci.yml +53 -43
- data/.travis.yml +17 -15
- data/Gemfile +4 -4
- data/README.md +37 -13
- data/Vagrantfile +34 -0
- data/bin/build_coverage.sh +24 -0
- data/bin/dev +1 -1
- data/bin/dev_data.sh +18 -0
- data/bin/dev_setup.sh +21 -0
- data/bin/install.sh +51 -0
- data/bin/release.sh +37 -0
- data/bin/test.sh +16 -0
- data/bin/uninstall.sh +24 -0
- data/bin/wallet +104 -104
- data/lib/wallet/command.rb +57 -57
- data/lib/wallet/command_add.rb +113 -113
- data/lib/wallet/command_categories.rb +13 -13
- data/lib/wallet/command_clear.rb +13 -13
- data/lib/wallet/command_csv.rb +30 -30
- data/lib/wallet/command_html.rb +20 -20
- data/lib/wallet/command_list.rb +168 -173
- data/lib/wallet/entry.rb +137 -137
- data/lib/wallet/version.rb +9 -9
- data/lib/wallet/wallet.rb +1051 -1051
- data/thefox-wallet.gemspec +23 -23
- data/wallet.sublime-project +8 -8
- metadata +12 -6
- data/.ackrc +0 -7
- data/Makefile +0 -29
- data/Makefile.common +0 -58
data/lib/wallet/command_add.rb
CHANGED
@@ -2,117 +2,117 @@
|
|
2
2
|
require 'date'
|
3
3
|
|
4
4
|
module TheFox::Wallet
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
5
|
+
|
6
|
+
# Add a new Entry.
|
7
|
+
class AddCommand < Command
|
8
|
+
|
9
|
+
NAME = 'add'
|
10
|
+
|
11
|
+
def run
|
12
|
+
if @options[:entry_category].nil?
|
13
|
+
@options[:entry_category] = 'default'
|
14
|
+
end
|
15
|
+
|
16
|
+
if @options[:is_interactively]
|
17
|
+
# Interactive User Input
|
18
|
+
|
19
|
+
print "title: [#{@options[:entry_title]}] "
|
20
|
+
title_t = STDIN.gets.strip
|
21
|
+
if title_t.length > 0
|
22
|
+
# Search for '%d' in title.
|
23
|
+
if @options[:entry_title] =~ /%d/
|
24
|
+
# Like sprintf.
|
25
|
+
@options[:entry_title] = @options[:entry_title] % title_t.split(',').map{ |s| s.strip }
|
26
|
+
else
|
27
|
+
@options[:entry_title] = title_t
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
print "date: [#{@options[:entry_date]}] "
|
32
|
+
date_t = STDIN.gets.strip
|
33
|
+
if date_t.length > 0
|
34
|
+
@options[:entry_date] = date_t
|
35
|
+
end
|
36
|
+
|
37
|
+
print "revenue: [#{@options[:entry_revenue]}] "
|
38
|
+
revenue_t = revenue(STDIN.gets.strip)
|
39
|
+
if !revenue_t.nil?
|
40
|
+
@options[:entry_revenue] = revenue_t
|
41
|
+
end
|
42
|
+
|
43
|
+
print "expense: [#{@options[:entry_expense]}] "
|
44
|
+
expense_t = expense(STDIN.gets.strip)
|
45
|
+
if !expense_t.nil?
|
46
|
+
@options[:entry_expense] = expense_t
|
47
|
+
end
|
48
|
+
|
49
|
+
print "category: [#{@options[:entry_category]}] "
|
50
|
+
category_t = STDIN.gets.strip
|
51
|
+
if category_t.length > 0
|
52
|
+
@options[:entry_category] = category_t
|
53
|
+
end
|
54
|
+
|
55
|
+
print "comment: [#{@options[:entry_comment]}] "
|
56
|
+
comment_t = STDIN.gets.strip
|
57
|
+
if comment_t.length > 0
|
58
|
+
@options[:entry_comment] = comment_t
|
59
|
+
end
|
60
|
+
|
61
|
+
puts '-' * 20
|
62
|
+
end
|
63
|
+
|
64
|
+
if @options[:entry_title].nil?
|
65
|
+
raise "Option --title is required for command '#{NAME}'"
|
66
|
+
end
|
67
|
+
|
68
|
+
# --force option
|
69
|
+
# --id option
|
70
|
+
is_unique = !@options[:force] && @options[:entry_id]
|
71
|
+
|
72
|
+
puts "id: '#{@options[:entry_id]}'"
|
73
|
+
puts "title: '#{@options[:entry_title]}'"
|
74
|
+
puts "date: " << Date.parse(@options[:entry_date]).to_s
|
75
|
+
puts "revenue: " << NUMBER_FORMAT % @options[:entry_revenue]
|
76
|
+
puts "expense: " << NUMBER_FORMAT % @options[:entry_expense]
|
77
|
+
puts "balance: " << NUMBER_FORMAT % [@options[:entry_revenue] + @options[:entry_expense]]
|
78
|
+
puts "category: #{@options[:entry_category]}"
|
79
|
+
puts "comment: '#{@options[:entry_comment]}'"
|
80
|
+
puts "force: #{@options[:force] ? 'YES' : 'NO'}"
|
81
|
+
puts "unique: #{is_unique ? 'YES' : 'NO'} (#{is_unique})"
|
82
|
+
|
83
|
+
# Create new Entry.
|
84
|
+
entry = Entry.new(@options[:entry_id], @options[:entry_title], @options[:entry_date], @options[:entry_revenue], @options[:entry_expense], @options[:entry_category], @options[:entry_comment])
|
85
|
+
|
86
|
+
# Initialize Wallet.
|
87
|
+
wallet = Wallet.new(@options[:wallet_path])
|
88
|
+
|
89
|
+
# Add Entry to Wallet.
|
90
|
+
added = wallet.add(entry, is_unique)
|
91
|
+
|
92
|
+
puts "added: #{added ? 'YES' : 'NO'}"
|
93
|
+
|
94
|
+
added
|
95
|
+
end
|
96
|
+
|
97
|
+
# @TODO replace with Wumber.
|
98
|
+
def revenue(revenue_s)
|
99
|
+
if !revenue_s.nil? && revenue_s.length > 0
|
100
|
+
# Replace , with . in numbers. '1,23' means '1.23'.
|
101
|
+
# eval the revenue so calculations are solved.
|
102
|
+
eval(revenue_s.to_s.gsub(/,/, '.')).to_f.round(NUMBER_ROUND).abs
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# @TODO replace with Wumber.
|
107
|
+
def expense(expense_s)
|
108
|
+
if !expense_s.nil? && expense_s.length > 0
|
109
|
+
# Replace , with . in numbers. '1,23' means '1.23'.
|
110
|
+
# eval the revenue so calculations are solved.
|
111
|
+
# Expenses are always minus.
|
112
|
+
-eval(expense_s.to_s.gsub(/,/, '.')).to_f.round(NUMBER_ROUND).abs
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
118
|
end
|
@@ -1,16 +1,16 @@
|
|
1
1
|
|
2
2
|
module TheFox::Wallet
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
3
|
+
|
4
|
+
# List all used categories.
|
5
|
+
class CategoriesCommand < Command
|
6
|
+
|
7
|
+
NAME = 'categories'
|
8
|
+
|
9
|
+
def run
|
10
|
+
wallet = Wallet.new(@options[:wallet_path])
|
11
|
+
puts wallet.categories
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
16
|
end
|
data/lib/wallet/command_clear.rb
CHANGED
@@ -2,17 +2,17 @@
|
|
2
2
|
require 'pathname'
|
3
3
|
|
4
4
|
module TheFox::Wallet
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
5
|
+
|
6
|
+
# Clear temp and cache files.
|
7
|
+
class ClearCommand < Command
|
8
|
+
|
9
|
+
NAME = 'clear'
|
10
|
+
|
11
|
+
def run
|
12
|
+
wallet = Wallet.new(@options[:wallet_path])
|
13
|
+
wallet.logger = @options[:logger]
|
14
|
+
wallet.clear
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
18
|
end
|
data/lib/wallet/command_csv.rb
CHANGED
@@ -1,33 +1,33 @@
|
|
1
1
|
|
2
2
|
module TheFox::Wallet
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
3
|
+
|
4
|
+
# Import or export to/from CSV file format.
|
5
|
+
class CsvCommand < Command
|
6
|
+
|
7
|
+
NAME = 'csv'
|
8
|
+
|
9
|
+
def run
|
10
|
+
if @options[:path].nil?
|
11
|
+
raise "Option --path is required for command '#{NAME}'"
|
12
|
+
end
|
13
|
+
|
14
|
+
wallet = Wallet.new(@options[:wallet_path])
|
15
|
+
wallet.logger = @options[:logger]
|
16
|
+
|
17
|
+
# Import by default.
|
18
|
+
if @options[:is_import] || !@options[:is_export]
|
19
|
+
# Import
|
20
|
+
@options[:logger].info("import csv #{@options[:path]} ...") if @options[:logger]
|
21
|
+
wallet.import_csv_file(@options[:path])
|
22
|
+
@options[:logger].info("import csv #{@options[:path]} done") if @options[:logger]
|
23
|
+
elsif @options[:is_export]
|
24
|
+
# Export
|
25
|
+
@options[:logger].info("export csv #{@options[:path]} ...") if @options[:logger]
|
26
|
+
wallet.export_csv_file(@options[:path])
|
27
|
+
@options[:logger].info("export csv #{@options[:path]} done") if @options[:logger]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
33
|
end
|
data/lib/wallet/command_html.rb
CHANGED
@@ -2,24 +2,24 @@
|
|
2
2
|
require 'pathname'
|
3
3
|
|
4
4
|
module TheFox::Wallet
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
5
|
+
|
6
|
+
# Exports a wallet as HTML.
|
7
|
+
# List all years in an index HTML file and all months for each year.
|
8
|
+
# Generates a HTML file for each month based on entries.
|
9
|
+
class HtmlCommand < Command
|
10
|
+
|
11
|
+
NAME = 'html'
|
12
|
+
|
13
|
+
def run
|
14
|
+
if @options[:path]
|
15
|
+
html_path = Pathname.new(@options[:path]).expand_path
|
16
|
+
end
|
17
|
+
|
18
|
+
wallet = Wallet.new(@options[:wallet_path])
|
19
|
+
wallet.logger = @options[:logger]
|
20
|
+
wallet.generate_html(html_path, @options[:entry_date_start], @options[:entry_date_end], @options[:entry_category])
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
25
|
end
|
data/lib/wallet/command_list.rb
CHANGED
@@ -1,176 +1,171 @@
|
|
1
1
|
|
2
2
|
module TheFox::Wallet
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
puts out
|
172
|
-
end
|
173
|
-
|
174
|
-
end
|
175
|
-
|
3
|
+
|
4
|
+
# List entries. Per default this command lists all entries of today.
|
5
|
+
# @TODO Use terminal-table for output? https://github.com/tj/terminal-table
|
6
|
+
class ListCommand < Command
|
7
|
+
|
8
|
+
NAME = 'list'
|
9
|
+
|
10
|
+
def run
|
11
|
+
puts
|
12
|
+
|
13
|
+
wallet = Wallet.new(@options[:wallet_path])
|
14
|
+
entries = wallet.entries(@options[:entry_date], @options[:entry_category].to_s)
|
15
|
+
|
16
|
+
# Get max length of all columns.
|
17
|
+
entries_l = entries
|
18
|
+
.map{ |day_name, day_items| day_items.count }
|
19
|
+
.inject{ |sum, n| sum + n }
|
20
|
+
.to_s
|
21
|
+
.length
|
22
|
+
title_l = entries
|
23
|
+
.map{ |month_item| month_item[1].map{ |day_item| day_item['title'].length }}
|
24
|
+
.flatten
|
25
|
+
.max
|
26
|
+
.to_i
|
27
|
+
revenue_l = entries
|
28
|
+
.map{ |month_item| month_item[1].map{ |day_item| (NUMBER_FORMAT % day_item['revenue']).length } }
|
29
|
+
.flatten
|
30
|
+
.max
|
31
|
+
.to_i
|
32
|
+
expense_l = entries
|
33
|
+
.map{ |month_item| month_item[1].map{ |day_item| (NUMBER_FORMAT % day_item['expense']).length } }
|
34
|
+
.flatten
|
35
|
+
.max
|
36
|
+
.to_i
|
37
|
+
balance_l = entries
|
38
|
+
.map{ |month_item| month_item[1].map{ |day_item| (NUMBER_FORMAT % day_item['balance']).length } }
|
39
|
+
.flatten
|
40
|
+
.max
|
41
|
+
.to_i
|
42
|
+
category_l = entries
|
43
|
+
.map{ |month_item| month_item[1].map{ |day_item| day_item['category'].length } }
|
44
|
+
.flatten
|
45
|
+
.max
|
46
|
+
.to_i
|
47
|
+
comment_l = entries
|
48
|
+
.map{ |month_item| month_item[1].map{ |day_item| day_item['comment'].length } }
|
49
|
+
.flatten
|
50
|
+
.max
|
51
|
+
.to_i
|
52
|
+
|
53
|
+
has_category_col = entries.map{ |month_item| month_item[1].map{ |day_item| day_item['category'] } }.flatten.select{ |i| i != 'default' }.count > 0
|
54
|
+
has_comment_col = entries
|
55
|
+
.map{ |month_item| month_item[1].map{ |day_item| day_item['comment'] } }
|
56
|
+
.flatten
|
57
|
+
.select{ |i| i != '' }
|
58
|
+
.count > 0
|
59
|
+
|
60
|
+
# Limit some columns to a maximum length.
|
61
|
+
if title_l < 6
|
62
|
+
title_l = 6
|
63
|
+
end
|
64
|
+
if title_l > 25
|
65
|
+
title_l = 25
|
66
|
+
end
|
67
|
+
|
68
|
+
if revenue_l < 7
|
69
|
+
revenue_l = 7
|
70
|
+
end
|
71
|
+
if expense_l < 7
|
72
|
+
expense_l = 7
|
73
|
+
end
|
74
|
+
if balance_l < 7
|
75
|
+
balance_l = 7
|
76
|
+
end
|
77
|
+
if category_l < 8
|
78
|
+
category_l = 8
|
79
|
+
end
|
80
|
+
if comment_l > 25
|
81
|
+
comment_l = 25
|
82
|
+
end
|
83
|
+
|
84
|
+
# Format String for each column.
|
85
|
+
entries_f = "%#{entries_l}s"
|
86
|
+
title_f = "%-#{title_l}s"
|
87
|
+
revenue_f = "%#{revenue_l}s"
|
88
|
+
expense_f = "%#{expense_l}s"
|
89
|
+
balance_f = "%#{balance_l}s"
|
90
|
+
category_f = "%-#{category_l}s"
|
91
|
+
comment_f = "%-#{comment_l}s"
|
92
|
+
|
93
|
+
# Create a table header.
|
94
|
+
header = ''
|
95
|
+
header << '#' * entries_l << ' '
|
96
|
+
header << 'Date ' << ' ' * 7
|
97
|
+
header << title_f % 'Title' << ' '
|
98
|
+
header << revenue_f % 'Revenue' << ' '
|
99
|
+
header << expense_f % 'Expense' << ' '
|
100
|
+
header << balance_f % 'Balance'
|
101
|
+
if has_category_col
|
102
|
+
header << ' ' << category_f % 'Category'
|
103
|
+
end
|
104
|
+
if has_comment_col
|
105
|
+
header << ' ' << comment_f % 'Comment'
|
106
|
+
end
|
107
|
+
|
108
|
+
header_l = header.length
|
109
|
+
header.sub!(/ +$/, '')
|
110
|
+
|
111
|
+
# Print table header.
|
112
|
+
puts header
|
113
|
+
puts '-' * header_l
|
114
|
+
|
115
|
+
# Sums
|
116
|
+
revenue_total = 0.0
|
117
|
+
expense_total = 0.0
|
118
|
+
balance_total = 0.0
|
119
|
+
|
120
|
+
entry_no = 0
|
121
|
+
|
122
|
+
# Iterate all days.
|
123
|
+
entries.sort.each do |day_name, day_items|
|
124
|
+
# Iterate all entries of a day.
|
125
|
+
day_items.each do |entry|
|
126
|
+
entry_no += 1
|
127
|
+
|
128
|
+
title = entry['title']
|
129
|
+
if title.length >= 25
|
130
|
+
title = title[0, 22] << '...'
|
131
|
+
end
|
132
|
+
|
133
|
+
revenue_total += entry['revenue']
|
134
|
+
expense_total += entry['expense']
|
135
|
+
balance_total += entry['balance']
|
136
|
+
|
137
|
+
category = entry['category'] == 'default' ? '' : entry['category']
|
138
|
+
|
139
|
+
comment = entry['comment']
|
140
|
+
if comment.length >= 25
|
141
|
+
comment = comment[0, 22] << '...'
|
142
|
+
end
|
143
|
+
|
144
|
+
out = ''
|
145
|
+
out << entries_f % entry_no
|
146
|
+
out << ' ' << '%10s' % entry['date']
|
147
|
+
out << ' ' << title_f % title
|
148
|
+
out << ' ' << revenue_f % (NUMBER_FORMAT % entry['revenue'])
|
149
|
+
out << ' ' << expense_f % (NUMBER_FORMAT % entry['expense'])
|
150
|
+
out << ' ' << balance_f % (NUMBER_FORMAT % entry['balance'])
|
151
|
+
out << ' ' << category_f % category if has_category_col
|
152
|
+
out << ' ' << comment_f % comment if has_comment_col
|
153
|
+
|
154
|
+
out.sub!(/ +$/, '')
|
155
|
+
puts out
|
156
|
+
end
|
157
|
+
end
|
158
|
+
puts
|
159
|
+
|
160
|
+
out = ''
|
161
|
+
out << ' ' * (12 + entries_l)
|
162
|
+
out << ' ' << title_f % 'TOTAL'
|
163
|
+
out << ' ' << revenue_f % (NUMBER_FORMAT % revenue_total)
|
164
|
+
out << ' ' << expense_f % (NUMBER_FORMAT % expense_total)
|
165
|
+
out << ' ' << balance_f % (NUMBER_FORMAT % balance_total)
|
166
|
+
puts out
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
176
171
|
end
|