treasurer 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -2
- data/VERSION +1 -1
- data/lib/treasurer/accounts.rb +307 -0
- data/lib/treasurer/analysis.rb +30 -28
- data/lib/treasurer/commands.rb +10 -5
- data/lib/treasurer/report.rb +435 -589
- data/lib/treasurer.rb +4 -0
- data/treasurer.gemspec +10 -6
- metadata +19 -4
data/lib/treasurer/report.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
+
require 'active_support/core_ext/integer/inflections'
|
2
|
+
|
1
3
|
class Float
|
2
|
-
|
3
|
-
|
4
|
-
|
4
|
+
def to_s
|
5
|
+
sprintf("%.2f", self)
|
6
|
+
end
|
5
7
|
end
|
6
8
|
class Date
|
7
|
-
|
8
|
-
|
9
|
-
|
9
|
+
def inspect
|
10
|
+
"Date.parse('#{to_s}')"
|
11
|
+
end
|
10
12
|
end
|
11
13
|
# Some thoughts on double entry accounting:
|
12
14
|
#
|
@@ -55,639 +57,472 @@ end
|
|
55
57
|
#
|
56
58
|
# This seems obvious to me!!
|
57
59
|
class Treasurer
|
58
|
-
class Reporter
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
raise "External_account not specified for #{run.data_line}"
|
78
|
-
end
|
79
|
-
@indateruns = @runs.find_all{|r| r.days_ago(@today) < @days_before}
|
80
|
-
#p 'accounts256',@runs.size, @runs.map{|r| r.account}.uniq
|
81
|
-
|
82
|
-
end
|
83
|
-
def generate_accounts
|
84
|
-
accounts = @runs.map{|r| r.account}.uniq.map{|acc| Account.new(acc, self, @runner, @runs, false)}
|
85
|
-
external_accounts = (@runs.map{|r| r.external_account}.uniq - accounts.map{|acc| acc.name}).map{|acc| Account.new(acc, self, @runner, @runs, true)}
|
86
|
-
@accounts = accounts + external_accounts
|
87
|
-
@expense_accounts = @accounts.find_all{|acc| acc.type == :Expense}
|
88
|
-
get_projected_accounts
|
89
|
-
#p ['projected_accounts_info', @projected_accounts_info]
|
90
|
-
#exit
|
91
|
-
@accounts.unshift (@equity = Equity.new(self, @runner, @accounts))
|
92
|
-
end
|
93
|
-
def report
|
94
|
-
generate_accounts
|
95
|
-
#get_actual_accounts
|
96
|
-
get_in_limit_discretionary_account_factor
|
97
|
-
get_stable_discretionary_account_factor
|
98
|
-
report = ""
|
99
|
-
report << header
|
100
|
-
#report << '\begin{multicols}{2}'
|
101
|
-
report << account_summaries
|
102
|
-
report << discretionary_account_table
|
103
|
-
report << account_balance_graphs
|
104
|
-
report << expense_account_summary
|
105
|
-
report << account_expenditure_graphs
|
106
|
-
#report << '\end{multicols}'
|
107
|
-
##report << account_resolutions
|
108
|
-
#report << account_breakdown
|
109
|
-
|
110
|
-
report << assumptions
|
111
|
-
report << transactions_by_account
|
112
|
-
report << footer
|
113
|
-
|
114
|
-
File.open('report.tex', 'w'){|f| f.puts report}
|
115
|
-
system "pdflatex report.tex && pdflatex report.tex"
|
116
|
-
end
|
117
|
-
class Account
|
118
|
-
end
|
119
|
-
class SubAccount < Account
|
120
|
-
def initialize(name, reporter, runner, runs, external)
|
121
|
-
@name = name
|
122
|
-
@reporter = reporter
|
123
|
-
@runner = runner
|
124
|
-
#@projected_accounts_info =Hash[projected_accounts_info.find_all{|k,v| v[:account] == name}]
|
125
|
-
@external = external
|
126
|
-
@runs = runs.find_all{|r| r.sub_account == name}
|
127
|
-
info[:external] = external if info
|
128
|
-
#ep ['sub_accounts333', name, @runs.size, runs.size]
|
129
|
-
end
|
130
|
-
end
|
131
|
-
class Account
|
132
|
-
attr_reader :name, :external, :runs
|
133
|
-
def initialize(name, reporter, runner, runs, external)
|
134
|
-
@name = name
|
135
|
-
@reporter = reporter
|
136
|
-
@runner = runner
|
137
|
-
#@projected_accounts_info =Hash[projected_accounts_info.find_all{|k,v| v[:account] == name}]
|
138
|
-
@external = external
|
139
|
-
@runs = runs.find_all{|r| (@external ? r.external_account : r.account) == name}
|
140
|
-
info[:external] = external if info
|
141
|
-
end
|
142
|
-
def sub_accounts
|
143
|
-
@sub_accounts ||= @runs.map{|r| r.sub_account}.uniq.compact.map{|acc| SubAccount.new(acc, @reporter, @runner, @runs, @external)}
|
144
|
-
end
|
145
|
-
def type
|
146
|
-
#account_type(name)
|
147
|
-
if ACCOUNT_INFO[name] and type = ACCOUNT_INFO[name][:type]
|
148
|
-
type
|
149
|
-
else
|
150
|
-
:Expense
|
151
|
-
end
|
152
|
-
end
|
153
|
-
def red_line(date)
|
154
|
-
if Treasurer::LocalCustomisations.instance_methods.include? :red_line
|
155
|
-
super(name, date)
|
156
|
-
else
|
157
|
-
0.0
|
158
|
-
end
|
159
|
-
end
|
160
|
-
def report_start
|
161
|
-
@reporter.today - @reporter.days_before
|
162
|
-
end
|
163
|
-
def opening_date
|
164
|
-
(info && info[:start]) || @runs.map{|r| r.date}.min
|
165
|
-
end
|
166
|
-
def opening_balance
|
167
|
-
(info && info[:opening_balance]) || 0.0
|
168
|
-
end
|
169
|
-
def has_balance?
|
170
|
-
not @runs.find{|r| not r.has_balance?}
|
171
|
-
end
|
172
|
-
def balance(date = @reporter.today)
|
173
|
-
date_i = date.to_datetime.to_time.to_i
|
174
|
-
#if !date
|
175
|
-
#@runs.sort_by{|r| r.date}[-1].balance
|
176
|
-
if @external or not has_balance?
|
177
|
-
#p ['name is ', name, type]
|
178
|
-
#
|
179
|
-
opening_balance + (@runs.find_all{|r| r.date <= date and r.date >= opening_date }.map{|r| money_in_sign * (r.deposit - r.withdrawal) * (@external ? -1 : 1)}.sum || 0.0)
|
180
|
-
#Temporary....
|
181
|
-
#0.0
|
182
|
-
else
|
183
|
-
nearest_time = @runs.map{|r| (r.date_i - date_i).to_f.abs}.sort[0]
|
184
|
-
@runs.find_all{|r| (r.date_i - date_i).to_f.abs == nearest_time}.sort_by{|r| r.id}[-1].balance
|
185
|
-
end
|
186
|
-
end
|
187
|
-
def deposited(today, days_before, &block)
|
188
|
-
p ['name22 is ', name, type, @runs.size]
|
189
|
-
#@runs.find_all{|r| r.days_ago(today) < days_before and (!block or yield(r)) }.map{|r| (@external and not ([:Liability, :Income].include?(type))) ? r.withdrawal : r.deposit }.sum || 0
|
190
|
-
@runs.find_all{|r| r.days_ago(today) < days_before and (!block or yield(r)) }.map{|r| (@external) ? r.withdrawal : r.deposit }.sum || 0
|
191
|
-
end
|
192
|
-
def withdrawn(today, days_before)
|
193
|
-
#@runs.find_all{|r| r.days_ago(today) < days_before }.map{|r| (@external and not ([:Liability, :Income].include?(type))) ? r.deposit : r.withdrawal }.sum || 0
|
194
|
-
@runs.find_all{|r| r.days_ago(today) < days_before }.map{|r| (@external) ? r.deposit : r.withdrawal }.sum || 0
|
195
|
-
end
|
196
|
-
|
197
|
-
def summary_table(today, days_before)
|
198
|
-
|
199
|
-
<<EOF
|
200
|
-
\\subsubsection{#{name}}
|
201
|
-
\\begin{tabulary}{0.8\\textwidth}{ r | l}
|
202
|
-
Balance & #{balance} \\\\
|
203
|
-
Deposited & #{deposited(today, days_before)} \\\\
|
204
|
-
Withdrawn & #{withdrawn(today, days_before)} \\\\
|
205
|
-
\\end{tabulary}
|
206
|
-
EOF
|
207
|
-
end
|
208
|
-
def summary_line(today, days_before)
|
60
|
+
class Reporter
|
61
|
+
#include LocalCustomisations
|
62
|
+
attr_reader :today
|
63
|
+
attr_reader :in_limit_discretionary_account_factors
|
64
|
+
attr_reader :stable_discretionary_account_factors
|
65
|
+
attr_accessor :projected_account_factor
|
66
|
+
attr_reader :accounts
|
67
|
+
attr_reader :equity
|
68
|
+
attr_reader :projected_accounts_info
|
69
|
+
attr_reader :days_before
|
70
|
+
def initialize(runner, options)
|
71
|
+
@runner = runner
|
72
|
+
@days_ahead = options[:days_ahead]||180
|
73
|
+
@days_before = options[:days_before]||360
|
74
|
+
@today = options[:today]||Date.today
|
75
|
+
@start_date = @today - @days_before
|
76
|
+
@end_date = @today + @days_ahead
|
77
|
+
@runs = runner.component_run_list.values
|
78
|
+
@currencies = ACCOUNT_INFO.map{|k,v| v[:currencies]}.flatten.uniq
|
209
79
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
-1.0
|
218
|
-
else
|
219
|
-
1.0
|
220
|
-
end
|
221
|
-
end
|
222
|
-
def discretionary
|
223
|
-
info and info[:discretionary]
|
224
|
-
end
|
225
|
-
def info
|
226
|
-
ACCOUNT_INFO[name] ||= {}
|
227
|
-
end
|
228
|
-
def projected_balance(date)
|
229
|
-
#return 0.0 if @external # Temporary Hack
|
230
|
-
#ep ['projected', @reporter.projected_accounts_info]
|
231
|
-
raise "Only should be called for Asset and Liability accounts" unless [:Asset, :Liability].include? type
|
232
|
-
non_discretionary_projected_balance(date) -
|
233
|
-
@reporter.sum_regular(linked_projected_account_info, date)
|
234
|
-
|
235
|
-
#(discretionary ? @reporter.sum_regular({name => info}, date) : 0.0)
|
236
|
-
end
|
237
|
-
def linked_projected_account_info
|
238
|
-
Hash[@reporter.projected_accounts_info.find_all{|ac,inf| inf[:linked_account] == name}]
|
239
|
-
end
|
240
|
-
def cache
|
241
|
-
@cache ||={}
|
242
|
-
end
|
243
|
-
def non_discretionary_projected_balance(date)
|
244
|
-
#ep ['FUTURE_INCOME', FUTURE_INCOME, name] if FUTURE_INCOME.size > 0
|
245
|
-
cache[[:non_discretionary_projected_balance, date]] ||=
|
246
|
-
balance +
|
247
|
-
#@reporter.sum_regular(REGULAR_EXPENDITURE[name], date) +
|
248
|
-
#@reporter.sum_regular(REGULAR_INCOME[name], date) -
|
249
|
-
#@reporter.sum_future(FUTURE_EXPENDITURE[name], date) +
|
250
|
-
#@reporter.sum_future(FUTURE_INCOME[name], date) +
|
251
|
-
(FUTURE_TRANSFERS.keys.find_all{|from,to| to == name}.map{|key|
|
252
|
-
@reporter.sum_future(FUTURE_TRANSFERS[key], date) * money_in_sign
|
253
|
-
}.sum||0) -
|
254
|
-
(FUTURE_TRANSFERS.keys.find_all{|from,to| from == name}.map{|key|
|
255
|
-
@reporter.sum_future( FUTURE_TRANSFERS[key], date) * money_in_sign
|
256
|
-
}.sum||0) +
|
257
|
-
(REGULAR_TRANSFERS.keys.find_all{|from,to| to == name}.map{|key|
|
258
|
-
@reporter.sum_regular(REGULAR_TRANSFERS[key], date) * money_in_sign
|
259
|
-
}.sum||0) -
|
260
|
-
(REGULAR_TRANSFERS.keys.find_all{|from,to| from == name}.map{|key|
|
261
|
-
@reporter.sum_regular( REGULAR_TRANSFERS[key], date) * money_in_sign
|
262
|
-
}.sum||0)
|
263
|
-
end
|
264
|
-
# Write an eps graph to disk of past and projected
|
265
|
-
# balance of the account
|
266
|
-
def write_balance_graph(today, days_before, days_ahead)
|
267
|
-
#accshort = name.gsub(/\s/, '')
|
268
|
-
if not (@external or type == :Equity or not has_balance?)
|
269
|
-
kit = @runner.graphkit(['date.to_time.to_i', 'balance'], {conditions: "account == #{name.inspect} and days_ago(Date.parse(#{today.to_s.inspect})) < #{days_before} and days_ago(Date.parse(#{today.to_s.inspect})) > -1", sort: '[date, id]'})
|
270
|
-
else
|
271
|
-
pastdates = (today-days_before..today).to_a
|
272
|
-
balances = pastdates.map{|date| balance(date)}
|
273
|
-
kit = GraphKit.quick_create([pastdates.map{|d| d.to_time.to_i}, balances])
|
274
|
-
end
|
275
|
-
futuredates = (today..today+days_ahead).to_a
|
276
|
-
projection = futuredates.map{|date| projected_balance(date) }
|
277
|
-
kit2 = GraphKit.quick_create([futuredates.map{|d| d.to_time.to_i}, projection])
|
278
|
-
red = futuredates.map{|date| red_line(date)}
|
279
|
-
kit3 = GraphKit.quick_create([futuredates.map{|d| d.to_time.to_i}, red])
|
280
|
-
@reporter.projected_account_factor = @reporter.in_limit_discretionary_account_factor
|
281
|
-
limit = futuredates.map{|date| projected_balance(date)}
|
282
|
-
kit4 = GraphKit.quick_create([futuredates.map{|d| d.to_time.to_i}, limit])
|
283
|
-
@reporter.projected_account_factor = @reporter.stable_discretionary_account_factor
|
284
|
-
#ep ['projected_account_factor!!!!', @reporter.projected_account_factor]
|
285
|
-
stable = futuredates.map{|date| projected_balance(date)}
|
286
|
-
kit5 = GraphKit.quick_create([futuredates.map{|d| d.to_time.to_i}, stable])
|
287
|
-
#exit
|
288
|
-
@reporter.projected_account_factor = nil
|
289
|
-
kit += (kit2 + kit4 + kit5)
|
290
|
-
#kit += (kit2)
|
291
|
-
kit = kit3 + kit
|
292
|
-
kit.title = "Balance for #{name}"
|
293
|
-
kit.xlabel = %['Date' offset 0,-2]
|
294
|
-
kit.xlabel = nil
|
295
|
-
kit.ylabel = "Balance"
|
296
|
-
|
80
|
+
if run = @runs.find{|r| not r.external_account}
|
81
|
+
raise "External_account not specified for #{run.data_line}"
|
82
|
+
end
|
83
|
+
@indateruns = @runs.find_all{|r| r.days_ago(@today) < @days_before}
|
84
|
+
@stable_discretionary_account_factors = {}
|
85
|
+
@in_limit_discretionary_account_factors = {}
|
86
|
+
#p 'accounts256',@runs.size, @runs.map{|r| r.account}.uniq
|
297
87
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
88
|
+
end
|
89
|
+
def generate_accounts
|
90
|
+
accounts = @runs.map{|r| r.account}.uniq.map{|acc| Account.new(acc, self, @runner, @runs, false)}
|
91
|
+
external_accounts = (@runs.map{|r| r.external_account}.uniq - accounts.map{|acc| acc.name}).map{|acc| Account.new(acc, self, @runner, @runs, true)}
|
92
|
+
external_accounts = external_accounts.map do |acc|
|
93
|
+
if acc_inf = ACCOUNT_INFO[acc.name] and currencies = acc_inf[:currencies] and currencies.size > 1
|
94
|
+
raise "Only expense accounts can have multiple currencies: #{acc.name} has type #{acc.type}" unless acc.type == :Expense
|
95
|
+
new_accounts = currencies.map do |curr|
|
96
|
+
Account.new(acc.name, self, @runner, @runs, true, currency: curr)
|
97
|
+
end
|
98
|
+
new_accounts.delete_if{|a| a.runs.size == 0}
|
99
|
+
new_accounts
|
100
|
+
else
|
101
|
+
acc
|
102
|
+
end
|
103
|
+
end
|
104
|
+
external_accounts = external_accounts.flatten
|
105
|
+
@accounts = accounts + external_accounts
|
106
|
+
@expense_accounts = @accounts.find_all{|acc| acc.type == :Expense}
|
107
|
+
@accounts_hash = accounts.map{|acc| [acc.name, acc]}.to_h
|
108
|
+
get_projected_accounts
|
109
|
+
#p ['projected_accounts_info', @projected_accounts_info]
|
110
|
+
#exit
|
111
|
+
@equities = currency_list.map do |currency|
|
112
|
+
equity = Equity.new(self, @runner, @accounts.find_all{|acc| acc.currency == currency}, currency: currency)
|
113
|
+
@accounts.unshift (equity)
|
114
|
+
[currency, equity]
|
115
|
+
end
|
116
|
+
@equities = @equities.to_h
|
117
|
+
end
|
118
|
+
def report
|
119
|
+
generate_accounts
|
120
|
+
#get_actual_accounts
|
121
|
+
currency_list.each do |currency|
|
122
|
+
get_in_limit_discretionary_account_factor(currency)
|
123
|
+
get_stable_discretionary_account_factor(currency)
|
124
|
+
end
|
125
|
+
report = ""
|
126
|
+
report << header
|
127
|
+
#report << '\begin{multicols}{2}'
|
128
|
+
report << account_summaries
|
129
|
+
currency_list.each do |currency|
|
130
|
+
report << discretionary_account_table(currency)
|
131
|
+
end
|
132
|
+
report << account_balance_graphs
|
133
|
+
report << expense_account_summary
|
134
|
+
report << account_expenditure_graphs
|
135
|
+
#report << '\end{multicols}'
|
136
|
+
##report << account_resolutions
|
137
|
+
#report << account_breakdown
|
306
138
|
|
307
|
-
|
308
|
-
|
139
|
+
report << assumptions
|
140
|
+
report << transactions_by_account
|
141
|
+
report << footer
|
309
142
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
#accshort = name.gsub(/\s/, '')
|
316
|
-
#"\\begin{center}\\includegraphics[width=3.0in]{#{name}_balance.eps}\\end{center}"
|
317
|
-
#"\\begin{center}\\includegraphics[width=0.9\\textwidth]{#{name}_balance.eps}\\end{center}"
|
318
|
-
"\\myfigure{#{name}_balance.eps}"
|
319
|
-
end
|
320
|
-
end
|
321
|
-
class Equity < Account
|
322
|
-
def initialize(reporter, runner, accounts)
|
323
|
-
@reporter = reporter
|
324
|
-
@runner = runner
|
325
|
-
@accounts = accounts #.find_all{|acc| not acc.external}
|
326
|
-
end
|
327
|
-
def type
|
328
|
-
:Equity
|
329
|
-
end
|
330
|
-
def name
|
331
|
-
:Equity
|
332
|
-
end
|
333
|
-
def red_line(date)
|
334
|
-
@accounts.map{|acc|
|
335
|
-
case acc.type
|
336
|
-
when :Asset
|
337
|
-
acc.red_line(date)
|
338
|
-
when :Liability
|
339
|
-
-acc.red_line(date)
|
340
|
-
else
|
341
|
-
0.0
|
342
|
-
end
|
343
|
-
}.sum
|
344
|
-
end
|
345
|
-
def balance(date=@reporter.today)
|
346
|
-
@accounts.map{|acc|
|
347
|
-
case acc.type
|
348
|
-
when :Asset
|
349
|
-
acc.balance(date)
|
350
|
-
when :Liability
|
351
|
-
-acc.balance(date)
|
352
|
-
else
|
353
|
-
0.0
|
354
|
-
end
|
355
|
-
}.sum
|
356
|
-
end
|
357
|
-
def projected_balance(date=@reporter.today)
|
358
|
-
@accounts.map{|acc|
|
359
|
-
case acc.type
|
360
|
-
when :Asset
|
361
|
-
acc.projected_balance(date)
|
362
|
-
when :Liability
|
363
|
-
-acc.projected_balance(date)
|
364
|
-
else
|
365
|
-
0.0
|
366
|
-
end
|
367
|
-
}.sum
|
368
|
-
end
|
369
|
-
def summary_table(today, days_before)
|
143
|
+
File.open('report.tex', 'w'){|f| f.puts report}
|
144
|
+
system "lualatex report.tex && lualatex report.tex"
|
145
|
+
end
|
146
|
+
def account_summaries
|
147
|
+
#ep 'accounts', @accounts.map{|a| a.name}
|
370
148
|
|
371
|
-
|
372
|
-
\\subsubsection{#{name}}
|
373
|
-
\\begin{tabulary}{0.8\\textwidth}{ r | l}
|
374
|
-
Balance & #{balance} \\\\
|
375
|
-
\\end{tabulary}
|
376
|
-
EOF
|
377
|
-
end
|
378
|
-
def summary_line(today, days_before)
|
379
|
-
"Equity & #{balance(today)} & & "
|
380
|
-
end
|
381
|
-
end
|
382
|
-
def account_summaries
|
383
|
-
#ep 'accounts', @accounts.map{|a| a.name}
|
384
|
-
|
385
|
-
<<EOF
|
149
|
+
<<EOF
|
386
150
|
\\section{Summary of Accounts}
|
387
|
-
#{[:Equity, :Asset, :Liability, :Income, :Expense].map{|type|
|
388
|
-
|
151
|
+
#{[:Equity, :Asset, :Liability, :Income, :Expense].map{|type|
|
152
|
+
"\\subsection{#{type}}
|
389
153
|
\\begin{tabulary}{0.9\\textwidth}{ R | c | c | c}
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
}.join("\n\n")}
|
154
|
+
Account & Balance & Deposited & Withdrawn \\\\
|
155
|
+
\\hline
|
156
|
+
\\Tstrut
|
157
|
+
#{@accounts.find_all{|acc| acc.type == type }.map{|acc| acc.summary_line(@today, @days_before)}.join("\\\\\n")}
|
158
|
+
\\end{tabulary}"
|
159
|
+
}.join("\n\n")}
|
396
160
|
EOF
|
397
|
-
|
398
|
-
|
399
|
-
|
161
|
+
end
|
162
|
+
def account_balance_graphs
|
163
|
+
<<EOF
|
400
164
|
\\section{Graphs of Recent Balances}
|
401
|
-
#{[:Equity, :Asset, :Liability].map{|typ|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
}.join("\n\n")
|
408
|
-
}
|
165
|
+
#{[:Equity, :Asset, :Liability].map{|typ|
|
166
|
+
"\\subsection{#{typ}}\\vspace{3em}" +
|
167
|
+
@accounts.find_all{|acc| acc.type == typ}.map{|acc|
|
168
|
+
acc.write_balance_graph(@today, @days_before, @days_ahead)
|
169
|
+
acc.balance_graph_string
|
170
|
+
}.join("\n\n")
|
171
|
+
}.join("\n\n")
|
172
|
+
}
|
409
173
|
EOF
|
410
|
-
|
411
|
-
|
412
|
-
|
174
|
+
end
|
175
|
+
def expense_account_summary
|
176
|
+
<<EOF
|
413
177
|
\\section{Expense Account Summary}
|
414
178
|
\\subsection{Budget Period}
|
415
|
-
#{
|
179
|
+
#{expense_pie_charts_by_currency('accountperiod', @expense_accounts){|r| r.days_ago(@today) < @days_before}}
|
416
180
|
\\subsection{Last Week}
|
417
|
-
#{
|
418
|
-
|
419
|
-
|
181
|
+
#{expense_pie_charts_by_currency('lastweekexpenses', @expense_accounts){|r|
|
182
|
+
#p ['r.daysago', r.days_ago(@today)];
|
183
|
+
r.days_ago(@today) < 7}}
|
420
184
|
\\subsection{Last Month}
|
421
|
-
#{
|
185
|
+
#{expense_pie_charts_by_currency('lastmonthexpenses', @expense_accounts){|r| r.days_ago(@today) < 30}}
|
422
186
|
\\subsection{Last Year}
|
423
|
-
#{
|
187
|
+
#{expense_pie_charts_by_currency('lastyearexpenses', @expense_accounts){|r| r.days_ago(@today) < 365}}
|
424
188
|
\\section{Expense Account Breakdown}
|
425
|
-
#{@expense_accounts.map{|account|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
}.join("\n\n")}
|
189
|
+
#{@expense_accounts.map{|account|
|
190
|
+
#ep ['sub_accounts2124', account.sub_accounts.map{|sa| sa.name}]
|
191
|
+
"\\subsection{#{account.name_c}}
|
192
|
+
#{expense_pie_chart(account.name_c_file + 'breakdown', account.sub_accounts){|r|r.days_ago(@today) < @days_before }}"
|
193
|
+
}.join("\n\n")}
|
430
194
|
EOF
|
431
195
|
|
432
|
-
#EOF
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
kit.data[0].gp.with = 'boxes'
|
443
|
-
kit.gp.style = "fill solid"
|
444
|
-
#pp ['kit222', kit, labels]
|
445
|
-
i = -1
|
446
|
-
kit.gp.xtics = "(#{labels.map{|l| %["#{l}" #{i+=1}]}.join(', ')}) rotate by 315"
|
447
|
-
kit.gnuplot_write("#{name}.eps", size: "4.0in,2.0in")
|
448
|
-
#%x[ps2eps #{name}.ps]
|
196
|
+
#EOF
|
197
|
+
end
|
198
|
+
def currency_list(accounts=@accounts, &block)
|
199
|
+
p accounts.map{|acc| acc.name rescue nil}
|
200
|
+
if block_given?
|
201
|
+
accounts.find_all{|acc| acc.runs.find{|r| yield(r)}}.map{|acc| acc.currency}.uniq.compact
|
202
|
+
else
|
203
|
+
accounts.find_all{|acc| not acc.runs or acc.runs.find{|r| r.in_date(acc.info)}}.map{|acc| acc.currency}.uniq.compact
|
204
|
+
end
|
205
|
+
end
|
449
206
|
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
ep ['projected_account_factor', @projected_account_factor]
|
469
|
-
end
|
470
|
-
@projected_account_factor = nil
|
471
|
-
#exit
|
472
|
-
end
|
473
|
-
def get_stable_discretionary_account_factor
|
474
|
-
@projected_account_factor = 1.0
|
475
|
-
loop do
|
476
|
-
ok = true
|
477
|
-
date = @today
|
478
|
-
balances = []
|
479
|
-
while date < @today + @days_ahead
|
480
|
-
#ok = false if @equity.projected_balance(date) < @equity.red_line(date)
|
481
|
-
date += 1
|
482
|
-
balances.push @equity.projected_balance(date)
|
483
|
-
#ep ['projected_account_factor', date, @equity.projected_balance(date), @equity.red_line(date), ok]
|
484
|
-
end
|
485
|
-
ok = false if balances.mean < @equity.balance(@today)
|
486
|
-
@stable_discretionary_account_factor = @projected_account_factor
|
487
|
-
break if (@projected_account_factor == 0.0 or ok == true)
|
488
|
-
@projected_account_factor -= 0.01
|
489
|
-
@projected_account_factor -= 0.1
|
490
|
-
end
|
491
|
-
@projected_account_factor = nil
|
492
|
-
#exit
|
493
|
-
end
|
494
|
-
def discretionary_account_table
|
495
|
-
discretionary_accounts = accounts_with_averages(@projected_accounts_info)
|
207
|
+
def expense_pie_charts_by_currency(name, accounts, &block)
|
208
|
+
currencies = currency_list(accounts, &block)
|
209
|
+
return (
|
210
|
+
currencies.map do |curr|
|
211
|
+
str = ""
|
212
|
+
str << "\\subsubsection{#{curr}}\n" if curr
|
213
|
+
str << expense_pie_chart(name + curr.to_s, accounts.find_all{|acc| acc.currency == curr}, &block)
|
214
|
+
str
|
215
|
+
end
|
216
|
+
).join("\n\n")
|
217
|
+
end
|
218
|
+
def expense_pie_chart(name, accounts, &block)
|
219
|
+
#expaccs = accounts.find_all{|acc| acc.type == :Expense}
|
220
|
+
labels = accounts.map{|acc| acc.name}
|
221
|
+
exps = accounts.map{|acc| acc.deposited(@today, 50000, &block)}
|
222
|
+
labels, exps = [labels, exps].transpose.find_all{|l, e| e != 0.0}.transpose
|
223
|
+
#ep ['labels22539', name, labels, exps]
|
224
|
+
return "No expenditure in account period." if labels == nil
|
496
225
|
|
497
|
-
|
498
|
-
|
226
|
+
|
227
|
+
#sum = exps.sum
|
228
|
+
#angles = exps.map{|ex| ex/sum * 360.0}
|
229
|
+
#start_angles = angles.dup #inject(-angles[0]){|o,n| o+n}
|
230
|
+
##start_angles.map!{|a| a+(start_angles
|
231
|
+
#end_angles = angles.inject(-angles[0]){|o,n| o+n}
|
232
|
+
|
233
|
+
|
234
|
+
kit = GraphKit.quick_create([labels.size.times.to_a, exps])
|
235
|
+
kit.data[0].gp.with = 'boxes'
|
236
|
+
kit.gp.boxwidth = "#{0.8} absolute"
|
237
|
+
kit.gp.style = "fill solid"
|
238
|
+
kit.gp.yrange = "[#{[kit.data[0].x.data.min,0].min}:]"
|
239
|
+
#kit.gp.xrange = "[-1:#{labels.size+1}]"
|
240
|
+
kit.gp.xrange = "[-1:1]" if labels.size==1
|
241
|
+
kit.gp.grid = "ytics"
|
242
|
+
kit.xlabel = nil
|
243
|
+
kit.ylabel = nil
|
244
|
+
#pp ['kit222', kit, labels]
|
245
|
+
i = -1
|
246
|
+
kit.gp.xtics = "(#{labels.map{|l| %["#{l}" #{i+=1}]}.join(', ')}) rotate by 315"
|
247
|
+
fork do
|
248
|
+
kit.gnuplot_write("#{name}.eps", size: "4.0in,1.8in")
|
249
|
+
%x[epspdf #{name}.eps]
|
250
|
+
end
|
251
|
+
|
252
|
+
#"\\begin{center}\\includegraphics[width=3.0in]{#{name}.eps}\\vspace{1em}\\end{center}"
|
253
|
+
#"\\begin{center}\\includegraphics[width=0.9\\textwidth]{#{name}.eps}\\vspace{1em}\\end{center}"
|
254
|
+
"\\myfigure{#{name}.pdf}"
|
255
|
+
end
|
256
|
+
def get_in_limit_discretionary_account_factor(currency)
|
257
|
+
@projected_account_factor = 1.0
|
258
|
+
loop do
|
259
|
+
ok = true
|
260
|
+
date = @today
|
261
|
+
while date < @today + @days_ahead
|
262
|
+
ok = false if @equities[currency].projected_balance(date) < @equities[currency].red_line(date)
|
263
|
+
date += 1
|
264
|
+
#ep ['projected_account_factor', date, @equity.projected_balance(date), @equity.red_line(date), ok]
|
265
|
+
end
|
266
|
+
@in_limit_discretionary_account_factors[currency] = @projected_account_factor
|
267
|
+
ep ['projected_account_factor', @projected_account_factor, @equities[currency].projected_balance(date), currency, ok]
|
268
|
+
break if (@projected_account_factor <= 0.0 or ok == true)
|
269
|
+
@projected_account_factor -= 0.01
|
270
|
+
@projected_account_factor -= 0.04
|
271
|
+
end
|
272
|
+
@projected_account_factor = nil
|
273
|
+
#exit
|
274
|
+
end
|
275
|
+
def get_stable_discretionary_account_factor(currency)
|
276
|
+
@projected_account_factor = 1.0
|
277
|
+
loop do
|
278
|
+
ok = true
|
279
|
+
date = @today
|
280
|
+
balances = []
|
281
|
+
while date < @today + @days_ahead
|
282
|
+
#ok = false if @equity.projected_balance(date) < @equity.red_line(date)
|
283
|
+
date += 1
|
284
|
+
balances.push @equities[currency].projected_balance(date)
|
285
|
+
#ep ['projected_account_factor', date, balances.mean, @projected_account_factor, @equities[currency].balance(@today), ok]
|
286
|
+
end
|
287
|
+
ok = false if balances.mean < @equities[currency].balance(@today) - 0.001
|
288
|
+
@stable_discretionary_account_factors[currency] = @projected_account_factor
|
289
|
+
break if (@projected_account_factor <= 0.0 or ok == true)
|
290
|
+
@projected_account_factor -= 0.01
|
291
|
+
@projected_account_factor -= 0.1
|
292
|
+
end
|
293
|
+
@projected_account_factor = nil
|
294
|
+
#exit
|
295
|
+
end
|
296
|
+
def discretionary_account_table(currency)
|
297
|
+
discretionary_accounts = accounts_with_averages(@projected_accounts_info.find_all{|acc,inf| acc.currency == currency}.to_h)
|
298
|
+
|
299
|
+
<<EOF
|
300
|
+
\\section{Discretionary Budget Summary (#{currency})}
|
499
301
|
\\begin{tabulary}{0.9\\textwidth}{ R | c c c c }
|
500
302
|
Budget & Average & Projection & Limit & Stable \\\\
|
501
|
-
#{discretionary_accounts.map{|account, info|
|
502
|
-
|
503
|
-
|
504
|
-
(
|
505
|
-
|
506
|
-
|
507
|
-
}
|
303
|
+
#{discretionary_accounts.map{|account, info|
|
304
|
+
#ep info
|
305
|
+
"#{account.name_c} & #{account.average} & #{account.projection} & #{
|
306
|
+
(account.projection * @in_limit_discretionary_account_factors[currency]).round(2)} &
|
307
|
+
#{(account.projection * @stable_discretionary_account_factors[currency]).round(2)} \\\\"
|
308
|
+
}.join("\n\n")
|
309
|
+
}
|
508
310
|
\\end{tabulary}
|
509
311
|
EOF
|
510
|
-
|
511
|
-
|
512
|
-
|
312
|
+
end
|
313
|
+
def account_expenditure_graphs
|
314
|
+
<<EOF
|
513
315
|
\\section{Expenditure by Account Period}
|
514
|
-
#{
|
316
|
+
#{currency_list.map{|curr|
|
317
|
+
account_and_transfer_graphs(@expense_accounts.find_all{|acc|
|
318
|
+
acc.info and acc.info[:period] and acc.currency == curr
|
319
|
+
})
|
320
|
+
}.join("\n")}
|
515
321
|
EOF
|
516
|
-
|
517
|
-
|
518
|
-
"#{accounts.map{|account
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
322
|
+
end
|
323
|
+
def account_and_transfer_graphs(accounts, options={})
|
324
|
+
"#{accounts.map{|account|
|
325
|
+
account_info = account.info
|
326
|
+
#ep ['accountbadf', account, account_info]
|
327
|
+
dates, expenditures, _items = account_expenditure(account)
|
328
|
+
ep ['accountbadf', account.name_c, account_info, expenditures]
|
329
|
+
if dates.size == 0
|
330
|
+
""
|
331
|
+
else
|
332
|
+
#ep ['account', account, dates, expenditures]
|
333
|
+
kit = GraphKit.quick_create([dates.map{|d| d.to_time.to_i}, expenditures])
|
334
|
+
kit.data.each{|dk| dk.gp.with="boxes"}
|
335
|
+
kit.gp.style = "fill solid"
|
336
|
+
kit.xlabel = nil
|
337
|
+
kit.ylabel = "Expenditure"
|
338
|
+
kit.data[0].gp.with = 'boxes'
|
339
|
+
dat = kit.data[0].x.data
|
340
|
+
kit.gp.boxwidth = "#{(dat.max.to_f - dat.min.to_f)/dat.size * 0.8} absolute"
|
341
|
+
kit.gp.yrange = "[#{[kit.data[0].y.data.min,0].min}:]"
|
342
|
+
#kit.gp.xrange = "[-1:#{labels.size+1}]"
|
343
|
+
#kit.gp.xrange = "[-1:1]" if labels.size==1
|
344
|
+
kit.gp.grid = "ytics"
|
345
|
+
kit.xlabel = nil
|
346
|
+
kit.ylabel = nil
|
347
|
+
unless options[:transfers]
|
348
|
+
kits = accounts_with_averages({account => account_info}).map{|account, account_info|
|
349
|
+
#ep 'Budget is ', account
|
350
|
+
kit2 = GraphKit.quick_create([
|
351
|
+
[dates[0], dates[-1]].map{|d| d.to_time.to_i},
|
352
|
+
[account.average, account.average]
|
353
|
+
])
|
354
|
+
kit2.data[0].gp.with = 'l lw 5'
|
355
|
+
kit2
|
356
|
+
}
|
357
|
+
#$debug_gnuplot = true
|
358
|
+
#kits.sum.gnuplot
|
359
|
+
kit += kits.sum
|
543
360
|
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
end
|
557
|
-
}.
|
558
|
-
}"
|
559
|
-
|
361
|
+
else
|
362
|
+
kit.data[0].y.data.map!{|expen| expen*-1.0}
|
363
|
+
end
|
364
|
+
kit.title = "#{account.name_c} Expenditure with average (Total = #{kit.data[0].y.data.sum})"
|
365
|
+
CodeRunner::Budget.kit_time_format_x(kit)
|
366
|
+
#kit.gnuplot
|
367
|
+
#ep ['kit1122', account, kit]
|
368
|
+
fork do
|
369
|
+
kit.gnuplot_write("#{account.name_c_file}.eps", size: "4.0in,2.0in")
|
370
|
+
exec "epspdf #{account.name_c_file}.eps"
|
371
|
+
end
|
372
|
+
#%x[ps2eps #{account}.ps]
|
373
|
+
#"\\begin{center}\\includegraphics[width=3.0in]{#{account}.eps}\\vspace{1em}\\end{center}"
|
374
|
+
#"\\begin{center}\\includegraphics[width=0.9\\textwidth]{#{account}.eps}\\vspace{1em}\\end{center}"
|
375
|
+
"\\myfigure{#{account.name_c_file}.pdf}"
|
376
|
+
end
|
377
|
+
}.join("\n\n")
|
378
|
+
}"
|
379
|
+
end
|
560
380
|
|
561
|
-
|
562
|
-
|
381
|
+
def account_resolutions
|
382
|
+
<<EOF
|
563
383
|
\\section{Budget Resolutions}
|
564
384
|
|
565
385
|
This section sums items from accounts drawn from an alternate account, i.e. it determines how much should be transferred from one account to another as a result of expenditure from a given account.
|
566
386
|
|
567
|
-
#{@actual_accounts.map{|account, account_info|
|
387
|
+
#{@actual_accounts.map{|account, account_info|
|
568
388
|
|
569
|
-
"\\subsection{#{account} }
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
389
|
+
"\\subsection{#{account} }
|
390
|
+
\\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{r l}
|
391
|
+
%\\hline
|
392
|
+
Account Owed & Amount \\\\
|
393
|
+
\\hline
|
394
|
+
\\Tstrut
|
395
|
+
#{account_items = @indateruns.find_all{|r| r.account == account}
|
576
396
|
alternate_accounts = account_items.map{|r| r.account}.uniq - [account_info[:account]]
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
397
|
+
alternate_accounts.map{|acc|
|
398
|
+
alternate_items = account_items.find_all{|r| r.account == acc}
|
399
|
+
total = alternate_items.map{|r| r.withdrawal - r.deposit}.sum
|
400
|
+
"#{acc} & #{total} \\\\"
|
401
|
+
}.join("\n\n")
|
402
|
+
}
|
583
403
|
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
404
|
+
\\\\
|
405
|
+
\\hline
|
406
|
+
\\end{tabulary}
|
407
|
+
\\normalsize
|
408
|
+
\\vspace{1em}\n\n
|
589
409
|
|
590
|
-
#{ alternate_accounts.map{|acc|
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
410
|
+
#{ alternate_accounts.map{|acc|
|
411
|
+
alternate_items = account_items.find_all{|r| r.account == acc}
|
412
|
+
alternate_items.pieces((alternate_items.size.to_f/50.to_f).ceil).map{|piece|
|
413
|
+
"\\footnotesize\\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{ #{"c " * 4 + " L " + " r " * 3 }}
|
414
|
+
#{account}: & #{account_info[:account]} & owes & #{acc} &&&&\\\\
|
415
|
+
\\hline
|
596
416
|
|
597
|
-
|
417
|
+
\\Tstrut
|
598
418
|
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
"
|
609
|
-
}.join("\n\n")
|
610
|
-
}
|
419
|
+
#{piece.map{|r|
|
420
|
+
([:id] + CodeRunner::Budget.rcp.component_results - [:sc]).map{|res|
|
421
|
+
r.send(res).to_s.latex_escape
|
422
|
+
#rcp.component_results.map{|res| r.send(res).to_s.gsub(/(.{20})/, '\1\\\\\\\\').latex_escape
|
423
|
+
}.join(" & ")
|
424
|
+
}.join("\\\\\n")
|
425
|
+
}
|
426
|
+
\\end{tabulary}\\normalsize"}.join("\n\n")
|
427
|
+
}.join("\n\n")}
|
428
|
+
"
|
429
|
+
}.join("\n\n")
|
430
|
+
}
|
611
431
|
EOF
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
#{REGULAR_TRANSFERS.pretty_inspect.latex_escape}
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
#{FUTURE_TRANSFERS.pretty_inspect.latex_escape}
|
623
|
-
|
432
|
+
end
|
433
|
+
def assumptions
|
434
|
+
<<EOF
|
435
|
+
\\section{Assumptions for Projection}
|
436
|
+
\\subsection{Regular Transfers}
|
437
|
+
\\begin{lstlisting}[language=ruby]
|
438
|
+
#{REGULAR_TRANSFERS.pretty_inspect.latex_escape}
|
439
|
+
\\end{lstlisting}
|
440
|
+
\\subsection{One-off Transfers}
|
441
|
+
\\begin{lstlisting}[language=ruby]
|
442
|
+
#{FUTURE_TRANSFERS.pretty_inspect.latex_escape}
|
443
|
+
\\end{lstlisting}
|
624
444
|
EOF
|
625
|
-
|
626
|
-
|
627
|
-
|
445
|
+
end
|
446
|
+
def account_breakdown
|
447
|
+
<<EOF
|
628
448
|
\\section{SubAccount Breakdown}
|
629
|
-
#{(@actual_accounts).map{|account, account_info|
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
}.join("\n\n")
|
658
|
-
}
|
449
|
+
#{(@actual_accounts).map{|account, account_info|
|
450
|
+
dates, expenditures, account_items = account_expenditure(account, account_info)
|
451
|
+
#pp account, account_items.map{|items| items.map{|i| i.date.to_s}}
|
452
|
+
"\\subsection{#{account}}" +
|
453
|
+
account_items.zip(dates, expenditures).map{|items, date, expenditure|
|
454
|
+
if items.size > 0
|
455
|
+
"
|
456
|
+
\\small
|
457
|
+
\\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{ #{"c " * 3 + " L " + " r " * 2 + " c " }}
|
458
|
+
%\\hline
|
459
|
+
#{date.to_s.latex_escape} & & & Total & #{expenditure} & \\\\
|
460
|
+
\\hline
|
461
|
+
\\Tstrut
|
462
|
+
#{items.map{|r|
|
463
|
+
( CodeRunner::Budget.rcp.component_results + [:external_account] - [:sc, :balance ]).map{|res|
|
464
|
+
r.send(res).to_s.latex_escape
|
465
|
+
}.join(" & ")
|
466
|
+
}.join("\\\\\n")
|
467
|
+
}
|
468
|
+
\\\\
|
469
|
+
\\hline
|
470
|
+
\\end{tabulary}
|
471
|
+
\\normalsize
|
472
|
+
\\vspace{1em}\n\n"
|
473
|
+
else
|
474
|
+
""
|
475
|
+
end
|
476
|
+
}.join("\n\n")
|
477
|
+
}.join("\n\n")
|
478
|
+
}
|
659
479
|
EOF
|
660
|
-
|
480
|
+
end
|
661
481
|
|
662
|
-
|
663
|
-
|
482
|
+
def transactions_by_account
|
483
|
+
<<EOF
|
664
484
|
\\section{Recent Transactions}
|
665
|
-
#{@accounts.find_all{|acc| not acc.type == :Equity}.sort_by{|acc| acc.external ? 0 : 1}.map{|acc|
|
666
|
-
|
485
|
+
#{@accounts.find_all{|acc| not acc.type == :Equity}.sort_by{|acc| acc.external ? 0 : 1}.map{|acc|
|
486
|
+
"\\subsection{#{acc.name_c}}
|
667
487
|
\\tiny
|
668
|
-
|
669
|
-
|
488
|
+
#{all = acc.runs.find_all{|r| r.days_ago(@today) < @days_before}
|
489
|
+
case acc.type
|
490
|
+
when :Expense, :Income
|
491
|
+
all = all.sort_by{|r| [r.sub_account, r.date, r.id]}.reverse
|
492
|
+
else
|
493
|
+
all = all.sort_by{|r| [r.date, r.id]}.reverse
|
494
|
+
end
|
495
|
+
#ep ['acc', acc, 'ids', all.map{|r| r.id}, 'size', all.size]
|
670
496
|
all.pieces((all.size.to_f/50.to_f).ceil).map{|piece|
|
671
|
-
"\\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{ #{"c " * 3 + " l " + " r " * 3 + "l"}}
|
672
|
-
|
673
|
-
|
674
|
-
|
497
|
+
"\\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{ #{"c " * 3 + " l " + " r " * 3 + "l"}}
|
498
|
+
#{piece.map{|r|
|
499
|
+
(CodeRunner::Budget.rcp.component_results - [:sc] + [:sub_account]).map{|res|
|
500
|
+
entry = r.send(res).to_s.latex_escape
|
501
|
+
#if
|
502
|
+
entry = entry[0...25] if entry.length > 25
|
503
|
+
#if entry.length > 40
|
504
|
+
#entry = entry.split(/.{40}/).join(" \\newline ")
|
505
|
+
#end
|
506
|
+
entry
|
507
|
+
#rcp.component_results.map{|res| r.send(res).to_s.gsub(/(.{20})/, '\1\\\\\\\\').latex_escape
|
675
508
|
}.join(" & ")
|
676
|
-
}.join("\\\\\n")}
|
509
|
+
}.join("\\\\\n")}
|
677
510
|
\\end{tabulary}"}.join("\n\n")}"
|
678
|
-
}.join("\n\n")}
|
511
|
+
}.join("\n\n")}
|
679
512
|
EOF
|
680
|
-
|
513
|
+
end
|
681
514
|
|
682
|
-
|
683
|
-
|
515
|
+
def header
|
516
|
+
<<EOF
|
684
517
|
\\documentclass[a5paper]{article}
|
685
|
-
|
518
|
+
%\\usepackage[scale=0.9]{geometry}
|
519
|
+
\\usepackage[left=1cm,top=1cm,right=1cm,nohead,nofoot]{geometry}
|
686
520
|
%\\usepackage[cm]{fullpage}
|
687
521
|
\\usepackage{tabulary}
|
688
522
|
\\usepackage{graphicx}
|
689
523
|
\\usepackage{multicol}
|
690
524
|
\\usepackage{hyperref}
|
525
|
+
\\usepackage{libertine}
|
691
526
|
\\usepackage{xcolor,listings}
|
692
527
|
\\newcommand\\Tstrut{\\rule{0pt}{2.8ex}}
|
693
528
|
\\newcommand\\myfigure[1]{\\vspace*{0em}\\begin{center}
|
@@ -702,17 +537,28 @@ basicstyle=\\ttfamily\\color{black},
|
|
702
537
|
identifierstyle = \\ttfamily\\color{purple},
|
703
538
|
keywordstyle=\\ttfamily\\color{blue},
|
704
539
|
stringstyle=\\color{orange}}
|
540
|
+
\\usepackage[compact]{titlesec}
|
541
|
+
\\titlespacing{\\section}{0pt}{*0}{*0}
|
542
|
+
\\titlespacing{\\subsection}{0pt}{*0}{*2}
|
543
|
+
\\titlespacing{\\subsubsection}{0pt}{*0}{*0}
|
544
|
+
\\setlength{\\parskip}{0pt}
|
545
|
+
\\setlength{\\parsep}{0pt}
|
546
|
+
\\setlength{\\headsep}{0pt}
|
547
|
+
\\setlength{\\topskip}{0pt}
|
548
|
+
\\setlength{\\topmargin}{0pt}
|
549
|
+
\\setlength{\\topsep}{0pt}
|
550
|
+
\\setlength{\\partopsep}{0pt}
|
705
551
|
\\begin{document}
|
706
|
-
\\title{#{@
|
552
|
+
\\title{Budget Report from #{@start_date.strftime("%A #{@start_date.day.ordinalize} of %B %Y")} to #{@end_date.strftime("%A #{@end_date.day.ordinalize} of %B %Y")}}
|
707
553
|
\\maketitle
|
708
554
|
\\tableofcontents
|
709
555
|
EOF
|
710
|
-
|
711
|
-
|
712
|
-
|
556
|
+
end
|
557
|
+
def footer
|
558
|
+
<<EOF
|
713
559
|
\\end{document}
|
714
560
|
EOF
|
715
|
-
|
561
|
+
end
|
716
562
|
|
717
|
-
end
|
563
|
+
end
|
718
564
|
end
|