treasurer 0.3.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,14 @@
1
+ require 'active_support/core_ext/integer/inflections'
2
+
1
3
  class Float
2
- def to_s
3
- sprintf("%.2f", self)
4
- end
4
+ def to_s
5
+ sprintf("%.2f", self)
6
+ end
5
7
  end
6
8
  class Date
7
- def inspect
8
- "Date.parse('#{to_s}')"
9
- end
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
- #include LocalCustomisations
60
- attr_reader :today
61
- attr_reader :in_limit_discretionary_account_factor
62
- attr_reader :stable_discretionary_account_factor
63
- attr_accessor :projected_account_factor
64
- attr_reader :accounts
65
- attr_reader :equity
66
- attr_reader :projected_accounts_info
67
- attr_reader :days_before
68
- def initialize(runner, options)
69
- @runner = runner
70
- @days_ahead = options[:days_ahead]||180
71
- @days_before = options[:days_before]||360
72
- @today = options[:today]||Date.today
73
- @start_date = @today - @days_before
74
- @runs = runner.component_run_list.values
75
-
76
- if run = @runs.find{|r| not r.external_account}
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
- <<EOF
211
- #{name} & #{balance} & #{deposited(today, days_before)} & #{withdrawn(today, days_before)}
212
- EOF
213
- end
214
- def money_in_sign
215
- case type
216
- when :Liability, :Income
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
- #kit.data[0].gp.title = 'Limit'
299
- kit.data[1].gp.title = 'Previous'
300
- kit.data[2].gp.title = '0 GBP Discretionary'
301
- kit.data[2].gp.title = 'Projection'
302
- kit.data[3].gp.title = 'Limit'
303
- kit.data[4].gp.title = 'Stable'
304
- kit.data.each{|dk| dk.gp.with = "lp"}
305
- kit.gp.key = ' bottom left '
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
- #(p kit; STDIN.gets) if name == :LloydsCreditCard
308
- CodeRunner::Budget.kit_time_format_x(kit)
139
+ report << assumptions
140
+ report << transactions_by_account
141
+ report << footer
309
142
 
310
- (kit).gnuplot_write("#{name}_balance.eps", size: "4.0in,2.0in") #, latex: true)
311
- #%x[epspdf #{name}_balance.eps]
312
- end
313
- # A string to include the balance graph in the document
314
- def balance_graph_string
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
- <<EOF
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
- "\\subsection{#{type}}
151
+ #{[:Equity, :Asset, :Liability, :Income, :Expense].map{|type|
152
+ "\\subsection{#{type}}
389
153
  \\begin{tabulary}{0.9\\textwidth}{ R | c | c | c}
390
- Account & Balance & Deposited & Withdrawn \\\\
391
- \\hline
392
- \\Tstrut
393
- #{@accounts.find_all{|acc| acc.type == type }.map{|acc| acc.summary_line(@today, @days_before)}.join("\\\\\n")}
394
- \\end{tabulary}"
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
- end
398
- def account_balance_graphs
399
- <<EOF
161
+ end
162
+ def account_balance_graphs
163
+ <<EOF
400
164
  \\section{Graphs of Recent Balances}
401
- #{[:Equity, :Asset, :Liability].map{|typ|
402
- "\\subsection{#{typ}}\\vspace{3em}" +
403
- @accounts.find_all{|acc| acc.type == typ}.map{|acc|
404
- acc.write_balance_graph(@today, @days_before, @days_ahead)
405
- acc.balance_graph_string
406
- }.join("\n\n")
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
- end
411
- def expense_account_summary
412
- <<EOF
174
+ end
175
+ def expense_account_summary
176
+ <<EOF
413
177
  \\section{Expense Account Summary}
414
178
  \\subsection{Budget Period}
415
- #{expense_pie_chart('accountperiod', @expense_accounts){|r| r.days_ago(@today) < @days_before}}
179
+ #{expense_pie_charts_by_currency('accountperiod', @expense_accounts){|r| r.days_ago(@today) < @days_before}}
416
180
  \\subsection{Last Week}
417
- #{expense_pie_chart('lastweekexpenses', @expense_accounts){|r|
418
- #p ['r.daysago', r.days_ago(@today)];
419
- r.days_ago(@today) < 7}}
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
- #{expense_pie_chart('lastmonthexpenses', @expense_accounts){|r| r.days_ago(@today) < 30}}
185
+ #{expense_pie_charts_by_currency('lastmonthexpenses', @expense_accounts){|r| r.days_ago(@today) < 30}}
422
186
  \\subsection{Last Year}
423
- #{expense_pie_chart('lastyearexpenses', @expense_accounts){|r| r.days_ago(@today) < 365}}
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
- #ep ['sub_accounts2124', account.sub_accounts.map{|sa| sa.name}]
427
- "\\subsection{#{account.name}} +
428
- #{expense_pie_chart(account.name + 'breakdown', account.sub_accounts){|r|r.days_ago(@today) < @days_before }}"
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
- end
434
- def expense_pie_chart(name, accounts, &block)
435
- #expaccs = accounts.find_all{|acc| acc.type == :Expense}
436
- labels = accounts.map{|acc| acc.name}
437
- exps = accounts.map{|acc| acc.deposited(@today, 50000, &block)}
438
- labels, exps = [labels, exps].transpose.find_all{|l, e| e != 0.0}.transpose
439
- #ep ['labels22539', name, labels, exps]
440
- return "No expenditure in account period." if labels == nil
441
- kit = GraphKit.quick_create([exps])
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
- #"\\begin{center}\\includegraphics[width=3.0in]{#{name}.eps}\\vspace{1em}\\end{center}"
451
- #"\\begin{center}\\includegraphics[width=0.9\\textwidth]{#{name}.eps}\\vspace{1em}\\end{center}"
452
- "\\myfigure{#{name}.eps}"
453
- end
454
- def get_in_limit_discretionary_account_factor
455
- @projected_account_factor = 1.0
456
- loop do
457
- ok = true
458
- date = @today
459
- while date < @today + @days_ahead
460
- ok = false if @equity.projected_balance(date) < @equity.red_line(date)
461
- date += 1
462
- #ep ['projected_account_factor', date, @equity.projected_balance(date), @equity.red_line(date), ok]
463
- end
464
- @in_limit_discretionary_account_factor = @projected_account_factor
465
- break if (@projected_account_factor == 0.0 or ok == true)
466
- @projected_account_factor -= 0.01
467
- @projected_account_factor -= 0.04
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
- <<EOF
498
- \\section{Discretionary Budget Summary}
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
- #ep info
503
- "#{account} & #{info[:average]} & #{info[:projection]} & #{
504
- (info[:projection] * @in_limit_discretionary_account_factor).round(2)} &
505
- #{(info[:projection] * @stable_discretionary_account_factor).round(2)} \\\\"
506
- }.join("\n\n")
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
- end
511
- def account_expenditure_graphs
512
- <<EOF
312
+ end
313
+ def account_expenditure_graphs
314
+ <<EOF
513
315
  \\section{Expenditure by Account Period}
514
- #{account_and_transfer_graphs(Hash[@expense_accounts.find_all{|acc| acc.info and acc.info[:period]}.map{|acc| [acc.name, acc.info]}], {})}
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
- end
517
- def account_and_transfer_graphs(accounts, options)
518
- "#{accounts.map{|account, account_info|
519
- #ep ['accountbadf', account, account_info]
520
- dates, expenditures, items = account_expenditure(account, account_info)
521
- if dates.size == 0
522
- ""
523
- else
524
- #ep ['account', account, dates, expenditures]
525
- kit = GraphKit.quick_create([dates.map{|d| d.to_time.to_i}, expenditures])
526
- kit.data.each{|dk| dk.gp.with="boxes"}
527
- kit.gp.style = "fill solid"
528
- kit.xlabel = nil
529
- kit.ylabel = "Expenditure"
530
- unless options[:transfers]
531
- kits = accounts_with_averages({account => account_info}).map{|account, account_info|
532
- #ep 'Budget is ', account
533
- kit2 = GraphKit.quick_create([
534
- [dates[0], dates[-1]].map{|d| d.to_time.to_i},
535
- [account_info[:average], account_info[:average]]
536
- ])
537
- kit2.data[0].gp.with = 'lp lw 4'
538
- kit2
539
- }
540
- #$debug_gnuplot = true
541
- #kits.sum.gnuplot
542
- kit += kits.sum
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
- else
545
- kit.data[0].y.data.map!{|expen| expen*-1.0}
546
- end
547
- kit.title = "#{account} Expenditure with average (Total = #{kit.data[0].y.data.sum})"
548
- CodeRunner::Budget.kit_time_format_x(kit)
549
- #kit.gnuplot
550
- #ep ['kit1122', account, kit]
551
- kit.gnuplot_write("#{account}.eps", size: "4.0in,2.0in")
552
- #%x[ps2eps #{account}.ps]
553
- #"\\begin{center}\\includegraphics[width=3.0in]{#{account}.eps}\\vspace{1em}\\end{center}"
554
- #"\\begin{center}\\includegraphics[width=0.9\\textwidth]{#{account}.eps}\\vspace{1em}\\end{center}"
555
- "\\myfigure{#{account}.eps}"
556
- end
557
- }.join("\n\n")
558
- }"
559
- end
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
- def account_resolutions
562
- <<EOF
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
- \\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{r l}
571
- %\\hline
572
- Account Owed & Amount \\\\
573
- \\hline
574
- \\Tstrut
575
- #{account_items = @indateruns.find_all{|r| r.account == account}
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
- alternate_accounts.map{|acc|
578
- alternate_items = account_items.find_all{|r| r.account == acc}
579
- total = alternate_items.map{|r| r.withdrawal - r.deposit}.sum
580
- "#{acc} & #{total} \\\\"
581
- }.join("\n\n")
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
- \\hline
586
- \\end{tabulary}
587
- \\normalsize
588
- \\vspace{1em}\n\n
404
+ \\\\
405
+ \\hline
406
+ \\end{tabulary}
407
+ \\normalsize
408
+ \\vspace{1em}\n\n
589
409
 
590
- #{ alternate_accounts.map{|acc|
591
- alternate_items = account_items.find_all{|r| r.account == acc}
592
- alternate_items.pieces((alternate_items.size.to_f/50.to_f).ceil).map{|piece|
593
- "\\footnotesize\\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{ #{"c " * 4 + " L " + " r " * 3 }}
594
- #{account}: & #{account_info[:account]} & owes & #{acc} &&&&\\\\
595
- \\hline
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
- \\Tstrut
417
+ \\Tstrut
598
418
 
599
- #{piece.map{|r|
600
- ([:id] + CodeRunner::Budget.rcp.component_results - [:sc]).map{|res|
601
- r.send(res).to_s.latex_escape
602
- #rcp.component_results.map{|res| r.send(res).to_s.gsub(/(.{20})/, '\1\\\\\\\\').latex_escape
603
- }.join(" & ")
604
- }.join("\\\\\n")
605
- }
606
- \\end{tabulary}\\normalsize"}.join("\n\n")
607
- }.join("\n\n")}
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
- end
613
- def assumptions
614
- <<EOF
615
- \\section{Assumptions for Projection}
616
- \\subsection{Regular Transfers}
617
- \\begin{lstlisting}[language=ruby]
618
- #{REGULAR_TRANSFERS.pretty_inspect.latex_escape}
619
- \\end{lstlisting}
620
- \\subsection{One-off Transfers}
621
- \\begin{lstlisting}[language=ruby]
622
- #{FUTURE_TRANSFERS.pretty_inspect.latex_escape}
623
- \\end{lstlisting}
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
- end
626
- def account_breakdown
627
- <<EOF
445
+ end
446
+ def account_breakdown
447
+ <<EOF
628
448
  \\section{SubAccount Breakdown}
629
- #{(@actual_accounts).map{|account, account_info|
630
- dates, expenditures, account_items = account_expenditure(account, account_info)
631
- #pp account, account_items.map{|items| items.map{|i| i.date.to_s}}
632
- "\\subsection{#{account}}" +
633
- account_items.zip(dates, expenditures).map{|items, date, expenditure|
634
- if items.size > 0
635
- "
636
- \\tiny
637
- \\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{ #{"c " * 3 + " L " + " r " * 2 + " c " }}
638
- %\\hline
639
- #{date.to_s.latex_escape} & & & Total & #{expenditure} & \\\\
640
- \\hline
641
- \\Tstrut
642
- #{items.map{|r|
643
- ( CodeRunner::Budget.rcp.component_results + [:external_account] - [:sc, :balance ]).map{|res|
644
- r.send(res).to_s.latex_escape
645
- }.join(" & ")
646
- }.join("\\\\\n")
647
- }
648
- \\\\
649
- \\hline
650
- \\end{tabulary}
651
- \\normalsize
652
- \\vspace{1em}\n\n"
653
- else
654
- ""
655
- end
656
- }.join("\n\n")
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
- end
480
+ end
661
481
 
662
- def transactions_by_account
663
- <<EOF
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
- "\\subsection{#{acc.name}}
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
- #{all = acc.runs.find_all{|r| r.days_ago(@today) < @days_before}.sort_by{|r| [r.sub_account, r.date, r.id]}.reverse
669
- #ep ['acc', acc, 'ids', all.map{|r| r.id}, 'size', all.size]
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
- #{piece.map{|r|
673
- (CodeRunner::Budget.rcp.component_results - [:sc] + [:sub_account]).map{|res| r.send(res).to_s.latex_escape
674
- #rcp.component_results.map{|res| r.send(res).to_s.gsub(/(.{20})/, '\1\\\\\\\\').latex_escape
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
- end
513
+ end
681
514
 
682
- def header
683
- <<EOF
515
+ def header
516
+ <<EOF
684
517
  \\documentclass[a5paper]{article}
685
- \\usepackage[scale=0.9]{geometry}
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{#{@days_before}-day Budget Report}
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
- end
711
- def footer
712
- <<EOF
556
+ end
557
+ def footer
558
+ <<EOF
713
559
  \\end{document}
714
560
  EOF
715
- end
561
+ end
716
562
 
717
- end
563
+ end
718
564
  end