treasurer 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6aa2a2dc0c5bcd8c4cc5bc4bc905e4c99e317497
4
- data.tar.gz: 0208ef5153cfb46947fce5ca8079ead628180fb2
3
+ metadata.gz: 65f2e9677098bdac4956e3562dd33f66317c52df
4
+ data.tar.gz: 34118648a3281e1274a00f953887283937af5d76
5
5
  SHA512:
6
- metadata.gz: 891b64d30e9cc11a6fe2793b88751e20bfb84630a14c39370f35bea6d485b47621be03ee06ee9db3d5ab77f159c7bf5458e9dd6af76396e06b4b63d9192cd6cb
7
- data.tar.gz: 3694e108f72e3f72b90f2e21660f5939a52b4c931e5b0f4d196f3a2e358ad58e5e5d3b297a4e17dbf1f485e20d211ac66320e5748946f7c0ba63266536b0438f
6
+ metadata.gz: 728db7d25466eb3f09bb2e47f0336dee0ea762624b899f7cc521584c17629de66818f584afbbefbdbeb4d351110381984b8075581c4df2da725a903bae864287
7
+ data.tar.gz: c1837491048206c26c5dc37104201ce2b5d232bbdb85135b0e2e3e482495d1ad52dcfbe17d2662fe860c4c0b06ac32be32c08fe9f3d8e16c90289339ff614f54
data/Gemfile CHANGED
@@ -3,7 +3,7 @@ source "http://rubygems.org"
3
3
  # Example:
4
4
  # gem "activesupport", ">= 2.3.5"
5
5
  gem "coderunner", ">= 0.14.16"
6
- gem "budgetcrmod", ">= 0.0.0"
6
+ gem "budgetcrmod", ">= 0.2.0"
7
7
  gem "command-line-flunky", ">= 1.0.0"
8
8
 
9
9
  # Add dependencies to develop your gem here.
@@ -12,6 +12,7 @@ group :development do
12
12
  gem "shoulda", ">= 0"
13
13
  gem "rdoc", "~> 3.12"
14
14
  gem "bundler", "~> 1.0"
15
- gem "jeweler", "~> 2.0.1"
15
+ gem "jeweler", "~> 2.3.1"
16
16
  gem "simplecov", ">= 0"
17
+ gem "ruby-prof", ">= 0.15"
17
18
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
@@ -2,39 +2,39 @@
2
2
  class Treasurer::Reporter
3
3
  module Analysis
4
4
  # Within the range of the report, return a list
5
- # of the dates of the beginning of each budget
5
+ # of the dates of the beginning of each account
6
6
  # period, along with a list of the expenditures
7
7
  # for each period and a list of the items within
8
8
  # each period
9
- def budget_expenditure(budget, budget_info, options={})
9
+ def account_expenditure(account, account_info, options={})
10
10
  dates = []
11
11
  expenditures = []
12
- budget_items = []
13
- date = budget_info[:end]||@today
14
- start_date = [(budget_info[:start]||@start_date), @start_date].max
12
+ account_items = []
13
+ date = account_info[:end]||@today
14
+ start_date = [(account_info[:start]||@start_date), @start_date].max
15
15
  expenditure = 0
16
16
  items_temp = []
17
- items = @runner.component_run_list.values.find_all{|r| r.budget == budget and r.in_date(budget_info)}
17
+ items = @runner.component_run_list.values.find_all{|r| r.external_account == account and r.in_date(account_info)}
18
18
  #ep ['items', items]
19
- #ep ['budget', budget]
19
+ #ep ['account', account]
20
20
  counter = 0
21
- if not budget_info[:period]
21
+ if not account_info[:period]
22
22
  dates.push date
23
- budget_items.push items
24
- expenditures.push (items.map{|r| r.debit - r.credit}+[0]).sum
23
+ account_items.push items
24
+ expenditures.push (items.map{|r| (r.deposit - r.withdrawal) * (account_info[:external] ? -1 : 1)}+[0]).sum
25
25
  else
26
26
 
27
- case budget_info[:period][1]
27
+ case account_info[:period][1]
28
28
  when :month
29
29
  while date > @start_date
30
30
  items_temp += items.find_all{|r| r.date == date}
31
- if date.mday == (budget_info[:monthday] or 1)
31
+ if date.mday == (account_info[:monthday] or 1)
32
32
  counter +=1
33
- if counter % budget_info[:period][0] == 0
34
- expenditure = (items_temp.map{|r| r.debit - r.credit}+[0]).sum
33
+ if counter % account_info[:period][0] == 0
34
+ expenditure = (items_temp.map{|r| (r.deposit - r.withdrawal) * (account_info[:external] ? -1 : 1)}+[0]).sum
35
35
  dates.push date
36
36
  expenditures.push expenditure
37
- budget_items.push items_temp
37
+ account_items.push items_temp
38
38
  items_temp = []
39
39
  expenditure = 0
40
40
  end
@@ -44,13 +44,13 @@ module Analysis
44
44
  when :day
45
45
  while date > @start_date
46
46
  items_temp += items.find_all{|r| r.date == date}
47
- #expenditure += (budget_items[-1].map{|r| r.debit}+[0]).sum
47
+ #expenditure += (account_items[-1].map{|r| r.debit}+[0]).sum
48
48
  counter +=1
49
- if counter % budget_info[:period][0] == 0
50
- expenditure = (items_temp.map{|r| r.debit - r.credit}+[0]).sum
49
+ if counter % account_info[:period][0] == 0
50
+ expenditure = (items_temp.map{|r| (r.deposit - r.withdrawal) * (account_info[:external] ? -1 : 1)}+[0]).sum
51
51
  dates.push date
52
52
  expenditures.push expenditure
53
- budget_items.push items_temp
53
+ account_items.push items_temp
54
54
  items_temp = []
55
55
  expenditure = 0
56
56
  end
@@ -59,45 +59,46 @@ module Analysis
59
59
  end
60
60
  end
61
61
 
62
- [dates, expenditures, budget_items]
62
+ [dates, expenditures, account_items]
63
63
 
64
64
  end
65
- # Work out the average spend from the budget and include it in the budget info
66
- def budgets_with_averages(budgets, options={})
67
- projected_budgets = budgets.dup
68
- projected_budgets.each{|key,v| projected_budgets[key]=projected_budgets[key].dup}
69
- projected_budgets.each do |budget, budget_info|
70
- #budget_info = budgets[budget]
71
- dates, expenditures, items = budget_expenditure(budget, budget_info)
72
- budget_info[:average] = expenditures.mean rescue 0.0
65
+ # Work out the average spend from the account and include it in the account info
66
+ def accounts_with_averages(accounts, options={})
67
+ projected_accounts_info = accounts.dup
68
+ projected_accounts_info.each{|key,v| projected_accounts_info[key]=projected_accounts_info[key].dup}
69
+ projected_accounts_info.each do |account, account_info|
70
+ #account_info = accounts[account]
71
+ dates, expenditures, items = account_expenditure(account, account_info)
72
+ account_info[:average] = expenditures.mean rescue 0.0
73
73
  end
74
- projected_budgets
74
+ projected_accounts_info
75
75
  end
76
- # Work out the projected spend from the budget and include it in the budget info
77
- def budgets_with_projections(budgets, options={})
78
- projected_budgets = budgets.dup
79
- projected_budgets.each{|key,v| projected_budgets[key]=projected_budgets[key].dup}
80
- projected_budgets.each do |budget, budget_info|
81
- #budget_info = budgets[budget]
82
- dates, expenditures, items = budget_expenditure(budget, budget_info)
83
- budget_info[:projection] = expenditures.mean rescue 0.0
76
+ # Work out the projected spend from the account and include it in the account info
77
+ def accounts_with_projections(accounts, options={})
78
+ projected_accounts_info = accounts.dup
79
+ projected_accounts_info.each{|key,v| projected_accounts_info[key]=projected_accounts_info[key].dup}
80
+ projected_accounts_info.each do |account, account_info|
81
+ #account_info = accounts[account]
82
+ dates, expenditures, items = account_expenditure(account, account_info)
83
+ account_info[:projection] = expenditures.mean rescue 0.0
84
84
  end
85
- projected_budgets
85
+ projected_accounts_info
86
86
  end
87
- # Get a list of budgets to be included in the report
88
- # i.e. budgets with non-empty expenditure
89
- def get_actual_budgets
90
- @actual_budgets = BUDGETS.dup
91
- BUDGETS.keys.each do |budget|
92
- @actual_budgets.delete(budget) if budget_expenditure(budget, BUDGETS[budget])[0].size == 0
93
- end
94
- end
95
- # Find all discretionary budgets and estimate the future
96
- # expenditure from that budget based on past
87
+ ## Get a list of accounts to be included in the report
88
+ ## i.e. accounts with non-empty expenditure
89
+ #def get_actual_accounts
90
+ #@actual_accounts = ACCOUNT_INFO.dup
91
+ #ACCOUNT_INFO.keys.each do |account|
92
+ #@actual_accounts.delete(account) if account_expenditure(account, ACCOUNT_INFO[account])[0].size == 0
93
+ #end
94
+ #end
95
+ # Find all discretionary accounts and estimate the future
96
+ # expenditure from that account based on past
97
97
  # expenditure (currently only a simple average)
98
- def get_projected_budgets
99
- @projected_budgets = Hash[@actual_budgets.dup.find_all{|k,v| v[:discretionary]}]
100
- @projected_budgets = budgets_with_projections(@projected_budgets)
98
+ def get_projected_accounts
99
+ @projected_accounts_info = Hash[ACCOUNT_INFO.dup.find_all{|k,v| v[:discretionary]}]
100
+ @projected_accounts_info = accounts_with_projections(@projected_accounts_info)
101
+ #@projected_accounts_info = @accounts.find_all{|acc| info = ACCOUNT_INFO[acc.name] and info[:discretionary]}
101
102
  end
102
103
  # Calculate the sum of all items within future
103
104
  # items that fall before end_date
@@ -117,7 +118,7 @@ module Analysis
117
118
  sum
118
119
  end
119
120
  # Sum every future occurence of the given
120
- # regular items that falls within the budget period
121
+ # regular items that falls within the account period
121
122
  def sum_regular(regular_items, end_date, options={})
122
123
  #end_date = @today + @days_ahead
123
124
  sum = regular_items.inject(0) do |sum, (name, item)|
@@ -179,9 +180,9 @@ module Analysis
179
180
 
180
181
 
181
182
 
182
- #ep ['name2234', name, info, @projected_budget_factor] if info[:discretionary]
183
+ #ep ['name2234', name, info, @projected_account_factor] if info[:discretionary]
183
184
 
184
- value + nunits * (info[:size]||info[:projection]*(@projected_budget_factor||1.0))
185
+ value + nunits * (info[:size]||info[:projection]*(@projected_account_factor||1.0))
185
186
 
186
187
  end
187
188
  sum + value
@@ -19,9 +19,12 @@ class << self
19
19
  raise "This folder has not been set up to use with Treasurer; please initialise a folder with treasurer init" unless FileTest.exist? '.code_runner_script_defaults.rb' and eval(File.read('.code_runner_script_defaults.rb'))[:code] == 'budget'
20
20
  end
21
21
  def create_report(copts = {})
22
+ reporter = fetch_reporter(copts)
23
+ reporter.report()
24
+ end
25
+ def fetch_reporter(copts = {})
22
26
  load_treasurer_folder
23
27
  reporter = Reporter.new(CodeRunner.fetch_runner(h: :component), days_before: copts[:b]||360, days_ahead: copts[:a]||180, today: copts[:t])
24
- reporter.report()
25
28
  end
26
29
  def init_root_folder(folder, copts={})
27
30
  raise "Folder already exists" if FileTest.exist? folder
@@ -10,7 +10,7 @@ REGULAR_TRANSFERS = {
10
10
  house: {size: 600, period: [1, :month], monthday: 20, end: Date.parse("01/07/2013")},
11
11
  },
12
12
  [:Income, :FirstBank] =>{
13
- pay: {size: 1000, period: [1, :month], monthday: 1, end: Date.parse("01/07/2014")},
13
+ pay: {size: 1200, period: [1, :month], monthday: 1, end: Date.parse("01/07/2014")},
14
14
  },
15
15
 
16
16
  }
@@ -19,7 +19,7 @@ REGULAR_TRANSFERS.default = {}
19
19
 
20
20
  FUTURE_TRANSFERS = {
21
21
  [:Income, :SecondBank] =>{
22
- topup: {size: 100, date: Date.parse("26/09/2010")},
22
+ bonus: {size: 100, date: Date.parse("26/09/2010")},
23
23
  },
24
24
  [:FirstBank, :PersonalLoans] =>{
25
25
  payfriend: {size: 640, date: Date.parse("25/09/2010")},
@@ -32,12 +32,16 @@ FUTURE_TRANSFERS.default = {}
32
32
 
33
33
 
34
34
 
35
- BUDGETS = {
36
- Monthly: {account: :FirstBank, period: [1, :month], monthday: 1, start: nil, end: nil, discretionary: false},
37
- MonthlySecondBank: {account: :SecondBank, period: [1, :month], monthday: 1, start: nil, end: nil, discretionary: false},
38
- Weekly: {account: :FirstBank, period: [7, :day], monthday: nil, start: nil, end: nil, discretionary: true},
39
- WeeklySecondBank: {account: :SecondBank, period: [7, :day], monthday: nil, start: nil, end: nil, discretionary: true},
40
- MyHoliday: {account: :SecondBank, period: [1, :day], monthday: nil, start: Date.parse("02/12/2013"), end: Date.parse("2/01/2014"), discretionary: false},
35
+ ACCOUNT_INFO = {
36
+ Monthly: {linked_account: :FirstBank, period: [1, :month], monthday: 1, start: nil, end: nil, discretionary: false},
37
+ MonthlySecondBank: {linked_account: :SecondBank, period: [1, :month], monthday: 1, start: nil, end: nil, discretionary: false},
38
+ Weekly: {linked_account: :FirstBank, period: [7, :day], monthday: nil, start: nil, end: nil, discretionary: true},
39
+ WeeklySecondBank: {linked_account: :SecondBank, period: [7, :day], monthday: nil, start: nil, end: nil, discretionary: true},
40
+ MyHoliday: {linked_account: :SecondBank, period: [1, :day], monthday: nil, start: Date.parse("02/12/2013"), end: Date.parse("2/01/2014"), discretionary: false},
41
+ PersonalLoans: {type: :Liability},
42
+ FirstBank: {type: :Asset},
43
+ SecondBank: {type: :Asset},
44
+ Pay: {linked_account: :FirstBank, type: :Income},
41
45
  }
42
46
 
43
47
  def in_date(item)
@@ -60,16 +64,16 @@ end
60
64
  def red_line(account, date)
61
65
  case account
62
66
  when :FirstBank
63
- 300
67
+ -350
64
68
  when :SecondBank
65
- 500
69
+ 0
66
70
  else
67
71
  0
68
72
  end
69
73
  end
70
74
 
71
75
 
72
- def external_account
76
+ def sub_account
73
77
  case description
74
78
  when /co-op|sainsbury/i
75
79
  :Food
@@ -85,19 +89,21 @@ def external_account
85
89
  :Entertainment
86
90
  when /blackwell/i
87
91
  :Books
88
- when /norries/i
89
- :PersonalLoans
90
92
  else
91
93
  :Unknown
92
94
  end
93
95
  end
94
96
 
95
- def budget
97
+ def external_account
96
98
  case description
97
99
  when /Vodafone/i
98
100
  :Monthly
101
+ when /norries/i
102
+ :PersonalLoans
103
+ when /my employer/i
104
+ :Pay
99
105
  else
100
- case external_account
106
+ case sub_account
101
107
  when :Food, :Entertainment
102
108
  :Weekly
103
109
  when :Insurance, :Phone, :Rent
@@ -1,3 +1,13 @@
1
+ class Float
2
+ def to_s
3
+ sprintf("%.2f", self)
4
+ end
5
+ end
6
+ class Date
7
+ def inspect
8
+ "Date.parse('#{to_s}')"
9
+ end
10
+ end
1
11
  # Some thoughts on double entry accounting:
2
12
  #
3
13
  # assets - liabilities = equity
@@ -48,10 +58,13 @@ class Treasurer
48
58
  class Reporter
49
59
  #include LocalCustomisations
50
60
  attr_reader :today
51
- attr_reader :in_limit_discretionary_budget_factor
52
- attr_reader :stable_discretionary_budget_factor
53
- attr_accessor :projected_budget_factor
54
- attr_accessor :equity
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
55
68
  def initialize(runner, options)
56
69
  @runner = runner
57
70
  @days_ahead = options[:days_ahead]||180
@@ -59,48 +72,83 @@ class Reporter
59
72
  @today = options[:today]||Date.today
60
73
  @start_date = @today - @days_before
61
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
62
79
  @indateruns = @runs.find_all{|r| r.days_ago(@today) < @days_before}
63
- p 'accounts256',@runs.size, @runs.map{|r| r.account}.uniq
80
+ #p 'accounts256',@runs.size, @runs.map{|r| r.account}.uniq
64
81
 
65
82
  end
66
- def report
67
- get_actual_budgets
68
- get_projected_budgets
69
- accounts = @runs.map{|r| r.account}.uniq.map{|acc| Account.new(acc, self, @runner, @runs, @projected_budgets, false)}
70
- external_accounts = (@runs.map{|r| r.external_account}.uniq - accounts.map{|acc| acc.name}).map{|acc| Account.new(acc, self, @runner, @runs, @projected_budgets, true)}
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)}
71
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
72
91
  @accounts.unshift (@equity = Equity.new(self, @runner, @accounts))
73
- get_in_limit_discretionary_budget_factor
74
- get_stable_discretionary_budget_factor
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
75
98
  report = ""
76
99
  report << header
77
- report << '\begin{multicols}{2}'
100
+ #report << '\begin{multicols}{2}'
78
101
  report << account_summaries
79
- report << discretionary_budget_table
102
+ report << discretionary_account_table
80
103
  report << account_balance_graphs
81
104
  report << expense_account_summary
82
- report << budget_expenditure_graphs
83
- report << '\end{multicols}'
84
- #report << budget_resolutions
85
- report << budget_breakdown
105
+ report << account_expenditure_graphs
106
+ #report << '\end{multicols}'
107
+ ##report << account_resolutions
108
+ #report << account_breakdown
109
+
110
+ report << assumptions
86
111
  report << transactions_by_account
87
112
  report << footer
88
113
 
89
114
  File.open('report.tex', 'w'){|f| f.puts report}
90
- system "latex report.tex && latex report.tex"
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
91
130
  end
92
131
  class Account
93
132
  attr_reader :name, :external, :runs
94
- def initialize(name, reporter, runner, runs, projected_budgets, external)
133
+ def initialize(name, reporter, runner, runs, external)
95
134
  @name = name
96
135
  @reporter = reporter
97
136
  @runner = runner
98
- @projected_budgets =Hash[projected_budgets.find_all{|k,v| v[:account] == name}]
137
+ #@projected_accounts_info =Hash[projected_accounts_info.find_all{|k,v| v[:account] == name}]
99
138
  @external = external
100
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)}
101
144
  end
102
145
  def type
103
- account_type(name)
146
+ #account_type(name)
147
+ if ACCOUNT_INFO[name] and type = ACCOUNT_INFO[name][:type]
148
+ type
149
+ else
150
+ :Expense
151
+ end
104
152
  end
105
153
  def red_line(date)
106
154
  if Treasurer::LocalCustomisations.instance_methods.include? :red_line
@@ -109,35 +157,58 @@ class Reporter
109
157
  0.0
110
158
  end
111
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
112
172
  def balance(date = @reporter.today)
173
+ date_i = date.to_datetime.to_time.to_i
113
174
  #if !date
114
175
  #@runs.sort_by{|r| r.date}[-1].balance
115
- if @external
176
+ if @external or not has_balance?
116
177
  #p ['name is ', name, type]
117
178
  #
118
- #@runs.find_all{|r| r.date < date}.map{|r| (r.deposit - r.withdrawal) * (@external ? -1 : 1)}.sum || 0.0
119
- # Temporary....
120
- 0.0
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
121
182
  else
122
- @runs.sort_by{|r| (r.date.to_datetime.to_time.to_i - date.to_datetime.to_time.to_i).to_f.abs}[0].balance
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
123
185
  end
124
186
  end
125
- def expenditure(today, days_before, &block)
126
- p ['name22 is ', name, type]
127
- @runs.find_all{|r| r.days_ago(today) < days_before and (!block or yield(r)) }.map{|r| @external ? r.withdrawal : r.deposit }.sum || 0
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
128
191
  end
129
- def income(today, days_before)
130
- @runs.find_all{|r| r.days_ago(today) < days_before }.map{|r| @external ? r.deposit : r.withdrawal }.sum || 0
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
131
195
  end
196
+
132
197
  def summary_table(today, days_before)
133
198
 
134
199
  <<EOF
135
200
  \\subsubsection{#{name}}
136
201
  \\begin{tabulary}{0.8\\textwidth}{ r | l}
137
202
  Balance & #{balance} \\\\
138
- Deposited & #{expenditure(today, days_before)} \\\\
139
- Withdrawn & #{income(today, days_before)} \\\\
203
+ Deposited & #{deposited(today, days_before)} \\\\
204
+ Withdrawn & #{withdrawn(today, days_before)} \\\\
140
205
  \\end{tabulary}
206
+ EOF
207
+ end
208
+ def summary_line(today, days_before)
209
+
210
+ <<EOF
211
+ #{name} & #{balance} & #{deposited(today, days_before)} & #{withdrawn(today, days_before)}
141
212
  EOF
142
213
  end
143
214
  def money_in_sign
@@ -148,10 +219,23 @@ EOF
148
219
  1.0
149
220
  end
150
221
  end
222
+ def discretionary
223
+ info and info[:discretionary]
224
+ end
225
+ def info
226
+ ACCOUNT_INFO[name] ||= {}
227
+ end
151
228
  def projected_balance(date)
152
- return 0.0 if @external # Temporary Hack
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
153
232
  non_discretionary_projected_balance(date) -
154
- @reporter.sum_regular(@projected_budgets, 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}]
155
239
  end
156
240
  def cache
157
241
  @cache ||={}
@@ -181,7 +265,7 @@ EOF
181
265
  # balance of the account
182
266
  def write_balance_graph(today, days_before, days_ahead)
183
267
  #accshort = name.gsub(/\s/, '')
184
- if not (@external or type == :Equity)
268
+ if not (@external or type == :Equity or not has_balance?)
185
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]'})
186
270
  else
187
271
  pastdates = (today-days_before..today).to_a
@@ -193,23 +277,25 @@ EOF
193
277
  kit2 = GraphKit.quick_create([futuredates.map{|d| d.to_time.to_i}, projection])
194
278
  red = futuredates.map{|date| red_line(date)}
195
279
  kit3 = GraphKit.quick_create([futuredates.map{|d| d.to_time.to_i}, red])
196
- @reporter.projected_budget_factor = @reporter.in_limit_discretionary_budget_factor
280
+ @reporter.projected_account_factor = @reporter.in_limit_discretionary_account_factor
197
281
  limit = futuredates.map{|date| projected_balance(date)}
198
282
  kit4 = GraphKit.quick_create([futuredates.map{|d| d.to_time.to_i}, limit])
199
- @reporter.projected_budget_factor = @reporter.stable_discretionary_budget_factor
200
- #ep ['projected_budget_factor!!!!', @reporter.projected_budget_factor]
283
+ @reporter.projected_account_factor = @reporter.stable_discretionary_account_factor
284
+ #ep ['projected_account_factor!!!!', @reporter.projected_account_factor]
201
285
  stable = futuredates.map{|date| projected_balance(date)}
202
286
  kit5 = GraphKit.quick_create([futuredates.map{|d| d.to_time.to_i}, stable])
203
287
  #exit
204
- @reporter.projected_budget_factor = nil
288
+ @reporter.projected_account_factor = nil
205
289
  kit += (kit2 + kit4 + kit5)
290
+ #kit += (kit2)
206
291
  kit = kit3 + kit
207
292
  kit.title = "Balance for #{name}"
208
293
  kit.xlabel = %['Date' offset 0,-2]
209
294
  kit.xlabel = nil
210
295
  kit.ylabel = "Balance"
296
+
211
297
 
212
- kit.data[0].gp.title = 'Limit'
298
+ #kit.data[0].gp.title = 'Limit'
213
299
  kit.data[1].gp.title = 'Previous'
214
300
  kit.data[2].gp.title = '0 GBP Discretionary'
215
301
  kit.data[2].gp.title = 'Projection'
@@ -218,14 +304,18 @@ EOF
218
304
  kit.data.each{|dk| dk.gp.with = "lp"}
219
305
  kit.gp.key = ' bottom left '
220
306
 
307
+ #(p kit; STDIN.gets) if name == :LloydsCreditCard
221
308
  CodeRunner::Budget.kit_time_format_x(kit)
222
309
 
223
- (kit).gnuplot_write("#{name}_balance.eps", size: "4.0in,3.0in")
310
+ (kit).gnuplot_write("#{name}_balance.eps", size: "4.0in,2.0in") #, latex: true)
311
+ #%x[epspdf #{name}_balance.eps]
224
312
  end
225
313
  # A string to include the balance graph in the document
226
314
  def balance_graph_string
227
315
  #accshort = name.gsub(/\s/, '')
228
- "\\begin{center}\\includegraphics[width=3.0in]{#{name}_balance.eps}\\end{center}"
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}"
229
319
  end
230
320
  end
231
321
  class Equity < Account
@@ -285,30 +375,35 @@ Balance & #{balance} \\\\
285
375
  \\end{tabulary}
286
376
  EOF
287
377
  end
378
+ def summary_line(today, days_before)
379
+ "Equity & #{balance(today)} & & "
380
+ end
288
381
  end
289
382
  def account_summaries
290
383
  #ep 'accounts', @accounts.map{|a| a.name}
291
384
 
292
385
  <<EOF
293
386
  \\section{Summary of Accounts}
294
- \\subsection{Equity}
295
- #{@accounts.find{|acc| acc.type == :Equity }.summary_table(@today, @days_before)}
296
- \\subsection{Assets}
297
- #{@accounts.find_all{|acc| acc.type == :Asset }.map{|acc| acc.summary_table(@today, @days_before)}.join("\n\n") }
298
- \\subsection{Liabilities}
299
- #{@accounts.find_all{|acc| acc.type == :Liability }.map{|acc| acc.summary_table(@today, @days_before)}.join("\n\n") }
300
- \\subsection{Income}
301
- #{@accounts.find_all{|acc| acc.type == :Income }.map{|acc| acc.summary_table(@today, @days_before)}.join("\n\n") }
302
- \\subsection{Expenses}
303
- #{@accounts.find_all{|acc| acc.type == :Expense }.map{|acc| acc.summary_table(@today, @days_before)}.join("\n\n") }
387
+ #{[:Equity, :Asset, :Liability, :Income, :Expense].map{|type|
388
+ "\\subsection{#{type}}
389
+ \\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")}
304
396
  EOF
305
397
  end
306
398
  def account_balance_graphs
307
399
  <<EOF
308
400
  \\section{Graphs of Recent Balances}
309
- #{@accounts.find_all{|acc| acc.type != :Expense}.map{|acc|
310
- acc.write_balance_graph(@today, @days_before, @days_ahead)
311
- acc.balance_graph_string
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")
312
407
  }.join("\n\n")
313
408
  }
314
409
  EOF
@@ -317,58 +412,66 @@ EOF
317
412
  <<EOF
318
413
  \\section{Expense Account Summary}
319
414
  \\subsection{Budget Period}
320
- #{expense_pie_chart('budgetperiod'){|r| r.days_ago(@today) < @days_before}}
415
+ #{expense_pie_chart('accountperiod', @expense_accounts){|r| r.days_ago(@today) < @days_before}}
321
416
  \\subsection{Last Week}
322
- #{expense_pie_chart('lastweekexpenses'){|r| p ['r.daysago', r.days_ago(@today)]; r.days_ago(@today) < 7}}
417
+ #{expense_pie_chart('lastweekexpenses', @expense_accounts){|r|
418
+ #p ['r.daysago', r.days_ago(@today)];
419
+ r.days_ago(@today) < 7}}
323
420
  \\subsection{Last Month}
324
- #{expense_pie_chart('lastmonthexpenses'){|r| r.days_ago(@today) < 30}}
421
+ #{expense_pie_chart('lastmonthexpenses', @expense_accounts){|r| r.days_ago(@today) < 30}}
325
422
  \\subsection{Last Year}
326
- #{expense_pie_chart('lastyearexpenses'){|r| r.days_ago(@today) < 365}}
327
- \\section{Expense Accounts by Budget}
328
- #{@actual_budgets.map{|budget, budget_info|
329
- "\\subsection{#{budget}}
330
- #{expense_pie_chart(budget + 'expenses'){|r|r.days_ago(@today) < @days_before and r.budget == budget}}"
423
+ #{expense_pie_chart('lastyearexpenses', @expense_accounts){|r| r.days_ago(@today) < 365}}
424
+ \\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 }}"
331
429
  }.join("\n\n")}
332
-
333
430
  EOF
431
+
432
+ #EOF
334
433
  end
335
- def expense_pie_chart(name, &block)
336
- expaccs = @accounts.find_all{|acc| acc.type == :Expense}
337
- labels = expaccs.map{|acc| acc.name}
338
- exps = expaccs.map{|acc| acc.expenditure(@today, 50000, &block)}
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)}
339
438
  labels, exps = [labels, exps].transpose.find_all{|l, e| e != 0.0}.transpose
340
- return "No expenditure in budget period." if labels == nil
341
- ep ['labels22539', labels, exps]
439
+ #ep ['labels22539', name, labels, exps]
440
+ return "No expenditure in account period." if labels == nil
342
441
  kit = GraphKit.quick_create([exps])
343
442
  kit.data[0].gp.with = 'boxes'
344
443
  kit.gp.style = "fill solid"
345
444
  #pp ['kit222', kit, labels]
346
445
  i = -1
347
446
  kit.gp.xtics = "(#{labels.map{|l| %["#{l}" #{i+=1}]}.join(', ')}) rotate by 315"
348
- kit.gnuplot_write("#{name}.eps")
447
+ kit.gnuplot_write("#{name}.eps", size: "4.0in,2.0in")
448
+ #%x[ps2eps #{name}.ps]
349
449
 
350
- "\\begin{center}\\includegraphics[width=3.0in]{#{name}.eps}\\vspace{1em}\\end{center}"
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}"
351
453
  end
352
- def get_in_limit_discretionary_budget_factor
353
- @projected_budget_factor = 1.0
454
+ def get_in_limit_discretionary_account_factor
455
+ @projected_account_factor = 1.0
354
456
  loop do
355
457
  ok = true
356
458
  date = @today
357
459
  while date < @today + @days_ahead
358
460
  ok = false if @equity.projected_balance(date) < @equity.red_line(date)
359
461
  date += 1
360
- #ep ['projected_budget_factor', date, @equity.projected_balance(date), @equity.red_line(date), ok]
462
+ #ep ['projected_account_factor', date, @equity.projected_balance(date), @equity.red_line(date), ok]
361
463
  end
362
- @in_limit_discretionary_budget_factor = @projected_budget_factor
363
- break if (@projected_budget_factor == 0.0 or ok == true)
364
- @projected_budget_factor -= 0.01
365
- @projected_budget_factor -= 0.1
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]
366
469
  end
367
- @projected_budget_factor = nil
470
+ @projected_account_factor = nil
368
471
  #exit
369
472
  end
370
- def get_stable_discretionary_budget_factor
371
- @projected_budget_factor = 1.0
473
+ def get_stable_discretionary_account_factor
474
+ @projected_account_factor = 1.0
372
475
  loop do
373
476
  ok = true
374
477
  date = @today
@@ -377,94 +480,102 @@ EOF
377
480
  #ok = false if @equity.projected_balance(date) < @equity.red_line(date)
378
481
  date += 1
379
482
  balances.push @equity.projected_balance(date)
380
- #ep ['projected_budget_factor', date, @equity.projected_balance(date), @equity.red_line(date), ok]
483
+ #ep ['projected_account_factor', date, @equity.projected_balance(date), @equity.red_line(date), ok]
381
484
  end
382
485
  ok = false if balances.mean < @equity.balance(@today)
383
- @stable_discretionary_budget_factor = @projected_budget_factor
384
- break if (@projected_budget_factor == 0.0 or ok == true)
385
- @projected_budget_factor -= 0.01
386
- @projected_budget_factor -= 0.1
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
387
490
  end
388
- @projected_budget_factor = nil
491
+ @projected_account_factor = nil
389
492
  #exit
390
493
  end
391
- def discretionary_budget_table
392
- discretionary_budgets = budgets_with_averages(@projected_budgets)
494
+ def discretionary_account_table
495
+ discretionary_accounts = accounts_with_averages(@projected_accounts_info)
393
496
 
394
497
  <<EOF
395
498
  \\section{Discretionary Budget Summary}
396
- \\begin{tabulary}{0.5\\textwidth}{ R | c c c c }
499
+ \\begin{tabulary}{0.9\\textwidth}{ R | c c c c }
397
500
  Budget & Average & Projection & Limit & Stable \\\\
398
- #{discretionary_budgets.map{|budget, info|
501
+ #{discretionary_accounts.map{|account, info|
399
502
  #ep info
400
- "#{budget} & #{info[:average]} & #{info[:projection]} & #{
401
- (info[:projection] * @in_limit_discretionary_budget_factor).round(2)} &
402
- #{(info[:projection] * @stable_discretionary_budget_factor).round(2)} \\\\"
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)} \\\\"
403
506
  }.join("\n\n")
404
507
  }
405
508
  \\end{tabulary}
406
509
  EOF
407
510
  end
408
- def budget_expenditure_graphs
511
+ def account_expenditure_graphs
409
512
  <<EOF
410
- \\section{Budget Expenditure}
411
- #{budget_and_transfer_graphs(@actual_budgets, {})}
513
+ \\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]}], {})}
412
515
  EOF
413
516
  end
414
- def budget_and_transfer_graphs(budgets, options)
415
- "#{budgets.map{|budget, budget_info|
416
- dates, expenditures, items = budget_expenditure(budget, budget_info)
417
- #ep ['budget', budget, dates, expenditures]
418
- kit = GraphKit.quick_create([dates.map{|d| d.to_time.to_i}, expenditures])
419
- kit.data.each{|dk| dk.gp.with="boxes"}
420
- kit.gp.style = "fill solid"
421
- kit.xlabel = nil
422
- kit.ylabel = "Expenditure"
423
- unless options[:transfers]
424
- kits = budgets_with_averages({budget => budget_info}).map{|budget, budget_info|
425
- #ep 'Budget is ', budget
426
- kit2 = GraphKit.quick_create([
427
- [dates[0], dates[-1]].map{|d| d.to_time.to_i},
428
- [budget_info[:average], budget_info[:average]]
429
- ])
430
- kit2.data[0].gp.with = 'lp lw 4'
431
- kit2
432
- }
433
- #$debug_gnuplot = true
434
- #kits.sum.gnuplot
435
- kit += kits.sum
436
-
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
+ ""
437
523
  else
438
- kit.data[0].y.data.map!{|expen| expen*-1.0}
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
543
+
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}"
439
556
  end
440
- kit.title = "#{budget} Expenditure with average (Total = #{kit.data[0].y.data.sum})"
441
- CodeRunner::Budget.kit_time_format_x(kit)
442
- #kit.gnuplot
443
- #ep ['kit1122', budget, kit]
444
- kit.gnuplot_write("#{budget}.eps")
445
- "\\begin{center}\\includegraphics[width=3.0in]{#{budget}.eps}\\vspace{1em}\\end{center}"
446
557
  }.join("\n\n")
447
558
  }"
448
559
  end
449
560
 
450
- def budget_resolutions
561
+ def account_resolutions
451
562
  <<EOF
452
563
  \\section{Budget Resolutions}
453
564
 
454
- This section sums items from budgets 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 budget.
565
+ 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.
455
566
 
456
- #{@actual_budgets.map{|budget, budget_info|
567
+ #{@actual_accounts.map{|account, account_info|
457
568
 
458
- "\\subsection{#{budget} }
569
+ "\\subsection{#{account} }
459
570
  \\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{r l}
460
571
  %\\hline
461
572
  Account Owed & Amount \\\\
462
573
  \\hline
463
574
  \\Tstrut
464
- #{budget_items = @indateruns.find_all{|r| r.budget == budget}
465
- alternate_accounts = budget_items.map{|r| r.account}.uniq - [budget_info[:account]]
575
+ #{account_items = @indateruns.find_all{|r| r.account == account}
576
+ alternate_accounts = account_items.map{|r| r.account}.uniq - [account_info[:account]]
466
577
  alternate_accounts.map{|acc|
467
- alternate_items = budget_items.find_all{|r| r.account == acc}
578
+ alternate_items = account_items.find_all{|r| r.account == acc}
468
579
  total = alternate_items.map{|r| r.withdrawal - r.deposit}.sum
469
580
  "#{acc} & #{total} \\\\"
470
581
  }.join("\n\n")
@@ -477,10 +588,10 @@ This section sums items from budgets drawn from an alternate account, i.e. it de
477
588
  \\vspace{1em}\n\n
478
589
 
479
590
  #{ alternate_accounts.map{|acc|
480
- alternate_items = budget_items.find_all{|r| r.account == acc}
591
+ alternate_items = account_items.find_all{|r| r.account == acc}
481
592
  alternate_items.pieces((alternate_items.size.to_f/50.to_f).ceil).map{|piece|
482
593
  "\\footnotesize\\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{ #{"c " * 4 + " L " + " r " * 3 }}
483
- #{budget}: & #{budget_info[:account]} & owes & #{acc} &&&&\\\\
594
+ #{account}: & #{account_info[:account]} & owes & #{acc} &&&&\\\\
484
595
  \\hline
485
596
 
486
597
  \\Tstrut
@@ -499,17 +610,30 @@ This section sums items from budgets drawn from an alternate account, i.e. it de
499
610
  }
500
611
  EOF
501
612
  end
502
- def budget_breakdown
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}
624
+ EOF
625
+ end
626
+ def account_breakdown
503
627
  <<EOF
504
- \\section{Budget and Transfer Breakdown}
505
- #{(@actual_budgets).map{|budget, budget_info|
506
- dates, expenditures, budget_items = budget_expenditure(budget, budget_info)
507
- #pp budget, budget_items.map{|items| items.map{|i| i.date.to_s}}
508
- "\\subsection{#{budget}}" +
509
- budget_items.zip(dates, expenditures).map{|items, date, expenditure|
628
+ \\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|
510
634
  if items.size > 0
511
635
  "
512
- \\footnotesize
636
+ \\tiny
513
637
  \\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{ #{"c " * 3 + " L " + " r " * 2 + " c " }}
514
638
  %\\hline
515
639
  #{date.to_s.latex_escape} & & & Total & #{expenditure} & \\\\
@@ -540,13 +664,13 @@ EOF
540
664
  \\section{Recent Transactions}
541
665
  #{@accounts.find_all{|acc| not acc.type == :Equity}.sort_by{|acc| acc.external ? 0 : 1}.map{|acc|
542
666
  "\\subsection{#{acc.name}}
543
- \\footnotesize
544
- #{all = acc.runs.find_all{|r| r.days_ago(@today) < @days_before}.sort_by{|r| [r.date, r.id]}.reverse
667
+ \\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
545
669
  #ep ['acc', acc, 'ids', all.map{|r| r.id}, 'size', all.size]
546
670
  all.pieces((all.size.to_f/50.to_f).ceil).map{|piece|
547
- "\\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{ #{"c " * 3 + " L " + " r " * 3 + "l"}}
671
+ "\\setlength{\\parindent}{0cm}\n\n\\begin{tabulary}{0.99\\textwidth}{ #{"c " * 3 + " l " + " r " * 3 + "l"}}
548
672
  #{piece.map{|r|
549
- (CodeRunner::Budget.rcp.component_results - [:sc] + [:budget]).map{|res| r.send(res).to_s.latex_escape
673
+ (CodeRunner::Budget.rcp.component_results - [:sc] + [:sub_account]).map{|res| r.send(res).to_s.latex_escape
550
674
  #rcp.component_results.map{|res| r.send(res).to_s.gsub(/(.{20})/, '\1\\\\\\\\').latex_escape
551
675
  }.join(" & ")
552
676
  }.join("\\\\\n")}
@@ -557,13 +681,27 @@ EOF
557
681
 
558
682
  def header
559
683
  <<EOF
560
- \\documentclass{article}
561
- \\usepackage[cm]{fullpage}
684
+ \\documentclass[a5paper]{article}
685
+ \\usepackage[scale=0.9]{geometry}
686
+ %\\usepackage[cm]{fullpage}
562
687
  \\usepackage{tabulary}
563
688
  \\usepackage{graphicx}
564
689
  \\usepackage{multicol}
565
- %\\usepackage{hyperlink}
690
+ \\usepackage{hyperref}
691
+ \\usepackage{xcolor,listings}
566
692
  \\newcommand\\Tstrut{\\rule{0pt}{2.8ex}}
693
+ \\newcommand\\myfigure[1]{\\vspace*{0em}\\begin{center}
694
+
695
+ \\includegraphics[width=0.9\\textwidth]{#1}
696
+
697
+ \\end{center}\\vspace*{0em}
698
+
699
+ }
700
+ \\lstset{%
701
+ basicstyle=\\ttfamily\\color{black},
702
+ identifierstyle = \\ttfamily\\color{purple},
703
+ keywordstyle=\\ttfamily\\color{blue},
704
+ stringstyle=\\color{orange}}
567
705
  \\begin{document}
568
706
  \\title{#{@days_before}-day Budget Report}
569
707
  \\maketitle
@@ -7,3 +7,4 @@ Date,Type,Sort Code,Account Number,Description,In,Out,Balance,
7
7
  01/09/2010,DEB,222222,88888888,THE MAGGIE ARMS,,48.00,1518.30
8
8
  01/09/2010,DEB,222222,88888888,BARLEY MOW,,6.60,1566.30
9
9
  30/08/2010,DD,222222,88888888,PAYPAL PAYMENT,,11.91,1572.90
10
+ 30/08/2010,FPO,222222,88888888,MY EMPLOYER,1000.0,,1560.99
@@ -1,6 +1,10 @@
1
1
  require 'helper'
2
+ require 'ruby-prof'
3
+ #require 'ruby-prof/test'
2
4
 
3
- class TestTreasurer < Test::Unit::TestCase
5
+ class TestTreaurer < Test::Unit::TestCase
6
+ #include RubyProf::Test
7
+ #PROFILE_OPTIONS[:
4
8
  def testfolder
5
9
  'test/myaccount'
6
10
  end
@@ -13,7 +17,16 @@ class TestTreasurer < Test::Unit::TestCase
13
17
  Treasurer.add_file('../otheraccountstatement.csv', 'SecondBank', {})
14
18
  Treasurer.add_folder_of_files('../multiple')
15
19
  Treasurer.status h: :component
20
+ RubyProf.start
16
21
  Treasurer.create_report t: Date.parse('2010-09-07'), b: 40, a: 35
22
+ result = RubyProf.stop
23
+ result.eliminate_methods!([/Array#map/, /Array#each/])
24
+ printer = RubyProf::GraphHtmlPrinter.new(result)
25
+ File.open('timing.html', 'w'){|f| printer.print(f, {})}
26
+ reporter = Treasurer.fetch_reporter(t: Date.parse('2010-09-07'), b: 40, a: 35)
27
+ reporter.generate_accounts
28
+ assert_equal(382.08, reporter.equity.balance.round(2))
29
+ assert_equal(724.33, reporter.equity.projected_balance(Date.parse('2010-10-09')).round(2))
17
30
  end
18
31
  #FileUtils.rm_r(testfolder) if FileTest.exist? testfolder
19
32
  end
data/treasurer.gemspec CHANGED
@@ -2,19 +2,19 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: treasurer 0.2.0 ruby lib
5
+ # stub: treasurer 0.3.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
- s.name = "treasurer"
9
- s.version = "0.2.0"
8
+ s.name = "treasurer".freeze
9
+ s.version = "0.3.0"
10
10
 
11
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
- s.require_paths = ["lib"]
13
- s.authors = ["Edmund Highcock"]
14
- s.date = "2014-06-02"
15
- s.description = "A simple command line tool for managing accounts and finances. Easily import internet banking spreadsheets and generate sophisticated reports and projections."
16
- s.email = "edmundhighcock@users.sourceforge.net"
17
- s.executables = ["treasurer"]
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib".freeze]
13
+ s.authors = ["Edmund Highcock".freeze]
14
+ s.date = "2018-02-27"
15
+ s.description = "A simple command line tool for managing accounts and finances. Easily import internet banking spreadsheets and generate sophisticated reports and projections.".freeze
16
+ s.email = "edmundhighcock@users.sourceforge.net".freeze
17
+ s.executables = ["treasurer".freeze]
18
18
  s.extra_rdoc_files = [
19
19
  "LICENSE.txt",
20
20
  "README.rdoc"
@@ -40,42 +40,45 @@ Gem::Specification.new do |s|
40
40
  "test/test_treasurer.rb",
41
41
  "treasurer.gemspec"
42
42
  ]
43
- s.homepage = "http://github.com/edmundhighcock/treasurer"
44
- s.licenses = ["GPLv3"]
45
- s.rubygems_version = "2.2.2"
46
- s.summary = "A simple command line tool for managing accounts and finances."
43
+ s.homepage = "http://github.com/edmundhighcock/treasurer".freeze
44
+ s.licenses = ["GPLv3".freeze]
45
+ s.rubygems_version = "2.6.8".freeze
46
+ s.summary = "A simple command line tool for managing accounts and finances.".freeze
47
47
 
48
48
  if s.respond_to? :specification_version then
49
49
  s.specification_version = 4
50
50
 
51
51
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
52
- s.add_runtime_dependency(%q<coderunner>, [">= 0.14.16"])
53
- s.add_runtime_dependency(%q<budgetcrmod>, [">= 0.0.0"])
54
- s.add_runtime_dependency(%q<command-line-flunky>, [">= 1.0.0"])
55
- s.add_development_dependency(%q<shoulda>, [">= 0"])
56
- s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
57
- s.add_development_dependency(%q<bundler>, ["~> 1.0"])
58
- s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
59
- s.add_development_dependency(%q<simplecov>, [">= 0"])
52
+ s.add_runtime_dependency(%q<coderunner>.freeze, [">= 0.14.16"])
53
+ s.add_runtime_dependency(%q<budgetcrmod>.freeze, [">= 0.2.0"])
54
+ s.add_runtime_dependency(%q<command-line-flunky>.freeze, [">= 1.0.0"])
55
+ s.add_development_dependency(%q<shoulda>.freeze, [">= 0"])
56
+ s.add_development_dependency(%q<rdoc>.freeze, ["~> 3.12"])
57
+ s.add_development_dependency(%q<bundler>.freeze, ["~> 1.0"])
58
+ s.add_development_dependency(%q<jeweler>.freeze, ["~> 2.3.1"])
59
+ s.add_development_dependency(%q<simplecov>.freeze, [">= 0"])
60
+ s.add_development_dependency(%q<ruby-prof>.freeze, [">= 0.15"])
60
61
  else
61
- s.add_dependency(%q<coderunner>, [">= 0.14.16"])
62
- s.add_dependency(%q<budgetcrmod>, [">= 0.0.0"])
63
- s.add_dependency(%q<command-line-flunky>, [">= 1.0.0"])
64
- s.add_dependency(%q<shoulda>, [">= 0"])
65
- s.add_dependency(%q<rdoc>, ["~> 3.12"])
66
- s.add_dependency(%q<bundler>, ["~> 1.0"])
67
- s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
68
- s.add_dependency(%q<simplecov>, [">= 0"])
62
+ s.add_dependency(%q<coderunner>.freeze, [">= 0.14.16"])
63
+ s.add_dependency(%q<budgetcrmod>.freeze, [">= 0.2.0"])
64
+ s.add_dependency(%q<command-line-flunky>.freeze, [">= 1.0.0"])
65
+ s.add_dependency(%q<shoulda>.freeze, [">= 0"])
66
+ s.add_dependency(%q<rdoc>.freeze, ["~> 3.12"])
67
+ s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
68
+ s.add_dependency(%q<jeweler>.freeze, ["~> 2.3.1"])
69
+ s.add_dependency(%q<simplecov>.freeze, [">= 0"])
70
+ s.add_dependency(%q<ruby-prof>.freeze, [">= 0.15"])
69
71
  end
70
72
  else
71
- s.add_dependency(%q<coderunner>, [">= 0.14.16"])
72
- s.add_dependency(%q<budgetcrmod>, [">= 0.0.0"])
73
- s.add_dependency(%q<command-line-flunky>, [">= 1.0.0"])
74
- s.add_dependency(%q<shoulda>, [">= 0"])
75
- s.add_dependency(%q<rdoc>, ["~> 3.12"])
76
- s.add_dependency(%q<bundler>, ["~> 1.0"])
77
- s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
78
- s.add_dependency(%q<simplecov>, [">= 0"])
73
+ s.add_dependency(%q<coderunner>.freeze, [">= 0.14.16"])
74
+ s.add_dependency(%q<budgetcrmod>.freeze, [">= 0.2.0"])
75
+ s.add_dependency(%q<command-line-flunky>.freeze, [">= 1.0.0"])
76
+ s.add_dependency(%q<shoulda>.freeze, [">= 0"])
77
+ s.add_dependency(%q<rdoc>.freeze, ["~> 3.12"])
78
+ s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
79
+ s.add_dependency(%q<jeweler>.freeze, ["~> 2.3.1"])
80
+ s.add_dependency(%q<simplecov>.freeze, [">= 0"])
81
+ s.add_dependency(%q<ruby-prof>.freeze, [">= 0.15"])
79
82
  end
80
83
  end
81
84
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: treasurer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Edmund Highcock
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-02 00:00:00.000000000 Z
11
+ date: 2018-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: coderunner
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.0.0
33
+ version: 0.2.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 0.0.0
40
+ version: 0.2.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: command-line-flunky
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 2.0.1
103
+ version: 2.3.1
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 2.0.1
110
+ version: 2.3.1
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: simplecov
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: ruby-prof
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0.15'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0.15'
125
139
  description: A simple command line tool for managing accounts and finances. Easily
126
140
  import internet banking spreadsheets and generate sophisticated reports and projections.
127
141
  email: edmundhighcock@users.sourceforge.net
@@ -171,7 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
171
185
  version: '0'
172
186
  requirements: []
173
187
  rubyforge_project:
174
- rubygems_version: 2.2.2
188
+ rubygems_version: 2.6.8
175
189
  signing_key:
176
190
  specification_version: 4
177
191
  summary: A simple command line tool for managing accounts and finances.