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.
@@ -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