treasurer 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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.