treasurer 0.3.0 → 0.5.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/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
|