Class: Treasurer::Reporter::Account

Inherits:
Object
  • Object
show all
Defined in:
lib/treasurer/accounts.rb,
lib/treasurer/accounts.rb

Direct Known Subclasses

Equity, ReportAccount, SubAccount

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, reporter, runner, runs, external, options = {}) ⇒ Account

Returns a new instance of Account.



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/treasurer/accounts.rb', line 21

def initialize(name, reporter, runner, runs, external, options={})
  @name = name
  @reporter = reporter
  @runner = runner
  @currency = options[:currency]
  #@projected_accounts_info =Hash[projected_accounts_info.find_all{|k,v| v[:account] == name}]
  @external = external
  unless @runs = options[:input_runs] 
    @runs = runs.find_all do |r| 
      #p ['checking11', name, @currency, ACCOUNT_INFO[r.account]] if name == r.external_account and @currency and @external
      #@external ? r.external_account : r.account) == name}
      if not @external
        r. == name
      elsif @currency and info and cur = info[:currencies] and cur.size > 1
        #p ['checking11', name, @currency, ACCOUNT_INFO[r.account]] if name == r.external_account and @currency
        r. == name and acinfo = ACCOUNT_INFO[r.] and acinfo[:currencies] == [@currency]
      else 
        r. == name
      end
    end

    if should_report?
      if @external
        @report_runs = runs.find_all do |r|
          r. == name
        end
      else
        @report_runs = @runs
      end
    end
  else
    @report_runs = []
  end
  #p ['Accountinf', name, @currency, @runs.size, runs.size]
  info[:external] = external if info
end

Instance Attribute Details

#averageObject

Returns the value of attribute average.



20
21
22
# File 'lib/treasurer/accounts.rb', line 20

def average
  @average
end

#currencyObject (readonly)

Returns the value of attribute currency.



19
20
21
# File 'lib/treasurer/accounts.rb', line 19

def currency
  @currency
end

#externalObject (readonly)

Returns the value of attribute external.



19
20
21
# File 'lib/treasurer/accounts.rb', line 19

def external
  @external
end

#nameObject (readonly)

Returns the value of attribute name.



19
20
21
# File 'lib/treasurer/accounts.rb', line 19

def name
  @name
end

#original_currencyObject

Returns the value of attribute original_currency.



20
21
22
# File 'lib/treasurer/accounts.rb', line 20

def original_currency
  @original_currency
end

#projectionObject

Returns the value of attribute projection.



20
21
22
# File 'lib/treasurer/accounts.rb', line 20

def projection
  @projection
end

#runsObject (readonly)

Returns the value of attribute runs.



19
20
21
# File 'lib/treasurer/accounts.rb', line 19

def runs
  @runs
end

Instance Method Details

#available(date = @reporter.today) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/treasurer/accounts.rb', line 124

def available(date = @reporter.today)
  case type
  when :Asset, :Equity
    b = balance
    b - red_line
  when :Liability
    b = balance
    red_line - b
  else
    nil
  end
end

#balance(date = @reporter.today, options = {}) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/treasurer/accounts.rb', line 136

def balance(date = @reporter.today, options={}) 
  @balance_cache ||= {}
  if b = @balance_cache[[date,options]] 
    return b 
  end
  date_i = date.to_datetime.to_time.to_i
  #if !date
  #@runs.sort_by{|r| r.date}[-1].balance
  balance = nil
  if @external or not has_balance?
    #p ['name is ', name, type]
    #
    if type == :Expense
      balance = (@runs.find_all{|r| r.date <= date and r.date >= @reporter.start_date }.map{|r| money_in_sign * (r.deposit - r.withdrawal) * (@external ? -1 : 1)}.sum || 0.0)

    else
      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)
      balance += info[:opening_balance] if info[:opening_balance]
      balance
    end
    #Temporary....
    #0.0
  else
    #p ['name33 is ', name, type, @runs.size, @currency]
    nearest_time = @runs.map{|r| (r.date_i - date_i).to_f.abs}.sort[0]
    balance = @runs.find_all{|r| (r.date_i - date_i).to_f.abs == nearest_time}.sort_by{|r| r.id}[-1].balance
  end
  if options[:original_currency] and @original_currency and @original_currency!=currency
    balance = balance*EXCHANGE_RATES[[currency, @original_currency]]
  end
  @balance_cache[[date,options]]=balance
  balance
end

#balance_graph_stringObject

A string to include the balance graph in the document



368
369
370
371
372
373
# File 'lib/treasurer/accounts.rb', line 368

def balance_graph_string
  #accshort = name.gsub(/\s/, '')
  #"\\begin{center}\\includegraphics[width=3.0in]{#{name}_balance.eps}\\end{center}"
  #"\\begin{center}\\includegraphics[width=0.9\\textwidth]{#{name}_balance.eps}\\end{center}"
  "\\myfigure{#{name_c_file}_balance.pdf}"
end

#cacheObject



246
247
248
# File 'lib/treasurer/accounts.rb', line 246

def cache
  @cache ||={}
end

#currency_labelObject



181
182
183
184
185
186
187
# File 'lib/treasurer/accounts.rb', line 181

def currency_label
  if currency
    " (#{currency}#{@original_currency ? "<-#@original_currency" : ""})"
  else
    ''
  end
end

#deposited(today, days_before, &block) ⇒ Object



169
170
171
172
173
# File 'lib/treasurer/accounts.rb', line 169

def deposited(today, days_before, &block)
  #p ['name223344 is ', name_c, today, days_before]
  #@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
  @runs.find_all{|r| r.days_ago(today) < days_before and r.date <= today and (!block or yield(r)) }.map{|r| (@external) ? r.withdrawal : r.deposit }.sum || 0
end

#discretionaryObject



221
222
223
# File 'lib/treasurer/accounts.rb', line 221

def discretionary
  info and info[:discretionary]
end

#generate_report_accountObject

Make the account object that does the reporting. This only gets called if we are doing a currency conversion and this account is in the reeport currency.



61
62
63
64
65
66
# File 'lib/treasurer/accounts.rb', line 61

def 
  p [name_c, @report_runs.class, @runs.class]
  @report_account = ReportAccount.new(@name, @reporter, @runner, nil, @external, {currency: @currency, input_runs: @report_runs})
  @report_account.instance_variable_set(:@currency, @reporter.report_currency||currency)
  @report_account.instance_variable_set(:@original_currency, currency)
end

#has_balance?Boolean

Returns:

  • (Boolean)


121
122
123
# File 'lib/treasurer/accounts.rb', line 121

def has_balance?
  not @runs.find{|r| not r.has_balance?} 
end

#infoObject



224
225
226
# File 'lib/treasurer/accounts.rb', line 224

def info
  ACCOUNT_INFO[name] ||= {}
end

#linked_projected_account_infoObject

(discretionary ? @reporter.sum_regular(=> info, date) : 0.0)



236
237
238
239
240
241
242
243
244
245
# File 'lib/treasurer/accounts.rb', line 236

def 
  #Hash[@reporter.projected_accounts_info.find_all{|ext_ac,inf| inf[:linked_account] == name and ext_ac.currency == currency}]
  Hash[@reporter.projected_accounts_info.find_all{|ext_ac,inf| 
    la = inf[:linked_account] and (
      la == name or
      (la.kind_of? Hash and la[original_currency] and 
       la[original_currency] == name and ext_ac.original_currency == original_currency)
    )
  }]
end

#money_in_signObject



213
214
215
216
217
218
219
220
# File 'lib/treasurer/accounts.rb', line 213

def money_in_sign
  case type
  when :Liability, :Income
    -1.0
  else
    1.0
  end
end

#name_cObject



189
190
191
# File 'lib/treasurer/accounts.rb', line 189

def name_c
  name + currency_label
end

#name_c_fileObject



192
193
194
# File 'lib/treasurer/accounts.rb', line 192

def name_c_file
  name_c.to_s.gsub(/[: ()<-]/, '_')
end

#non_discretionary_projected_balance(date) ⇒ Object



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'lib/treasurer/accounts.rb', line 249

def non_discretionary_projected_balance(date)
  #ep ['FUTURE_INCOME', FUTURE_INCOME, name] if FUTURE_INCOME.size > 0
  if not (@futures and @regulars)
    @futures = Marshal.load(Marshal.dump(FUTURE_TRANSFERS))
    @regulars = Marshal.load(Marshal.dump(REGULAR_TRANSFERS))
    [@regulars, @futures].each do |transfers|
      @accounts_hash = @reporter.accounts_hash
      transfers.each do |accs, trans|
        next unless accs.include? name
        trans.each do |item, details|
          if details[:currency] and details[:currency] != currency
            #p ['LAGT(O', details[:currency], currency, details, name_c, item]
            details[:size] *= EXCHANGE_RATES[[details[:currency], currency]]
          end
        end
      end
    end
  end
       
      
  cache[[:non_discretionary_projected_balance, date]] ||= 
    balance +
    #@reporter.sum_regular(REGULAR_EXPENDITURE[name], date) + 
    #@reporter.sum_regular(REGULAR_INCOME[name], date) -  
    #@reporter.sum_future(FUTURE_EXPENDITURE[name], date) + 
    #@reporter.sum_future(FUTURE_INCOME[name], date) + 
    (@futures.keys.find_all{|from,to| to == name}.map{|key|
      @reporter.sum_future(@futures[key], date) * money_in_sign
    }.sum||0) - 
    (@futures.keys.find_all{|from,to| from == name}.map{|key|
      @reporter.sum_future( @futures[key], date) * money_in_sign
    }.sum||0) +
    (@regulars.keys.find_all{|from,to| to == name}.map{|key|
      @reporter.sum_regular(@regulars[key], date) * money_in_sign
    }.sum||0) - 
    (@regulars.keys.find_all{|from,to| from == name}.map{|key|
      @reporter.sum_regular( @regulars[key], date) * money_in_sign
    }.sum||0)  
end

#opening_balanceObject



118
119
120
# File 'lib/treasurer/accounts.rb', line 118

def opening_balance
  (info && info[:opening_balance]) || 0.0
end

#opening_dateObject



115
116
117
# File 'lib/treasurer/accounts.rb', line 115

def opening_date
  (info && info[:start]) || @runs.map{|r| r.date}.min
end

#projected_balance(date) ⇒ Object



227
228
229
230
231
232
233
234
235
# File 'lib/treasurer/accounts.rb', line 227

def projected_balance(date)
  #return 0.0 if @external # Temporary Hack
  #ep ['projected', @reporter.projected_accounts_info]
  raise "Only should be called for Asset and Liability accounts" unless [:Asset, :Liability].include? type
  non_discretionary_projected_balance(date)  -
    @reporter.sum_regular(, date)

  #(discretionary ? @reporter.sum_regular({name => info}, date) : 0.0)
end

#red_line(date = @reporter.today) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/treasurer/accounts.rb', line 99

def red_line(date=@reporter.today)
  if Treasurer::LocalCustomisations.instance_methods.include? :red_line
    val = super(name, date)
    if rc = @reporter.report_currency and rc != @original_currency
      er = EXCHANGE_RATES[[@original_currency,rc]]
      #p ['AAAAAAA', name, @original_currency, er, val, rc]
      val *= er
    end
    val
  else 
    0.0
  end
end

#report_accountObject

The object that actually does the reporting. If there is no currency conversion this is just self. A separate report object is needed as just lumping all the different currencies together across the board results in double counting.

The report account is used for reporting information regarding a particular account. The main accoun is used for calculations e.g. Equity



77
78
79
# File 'lib/treasurer/accounts.rb', line 77

def 
  @report_account || self
end

#report_startObject



112
113
114
# File 'lib/treasurer/accounts.rb', line 112

def report_start
  @reporter.today - @reporter.days_before
end

#should_report?Boolean

Should I report? If there is no currency conversion all accounts report. If there is currency conversion only non-external accounts and accounts in the right currency report.

Returns:

  • (Boolean)


85
86
87
# File 'lib/treasurer/accounts.rb', line 85

def should_report?
  !@reporter.report_currency or !@external or (@original_currency||currency) == @reporter.report_currency
end

#sub_accountsObject



88
89
90
# File 'lib/treasurer/accounts.rb', line 88

def sub_accounts
  @sub_accounts ||= @runs.map{|r| r.}.uniq.compact.map{|acc| SubAccount.new(acc, @reporter, @runner, @runs, @external, currency: @currency)}
end

#summary_line(today, days_before) ⇒ Object



207
208
209
210
211
212
# File 'lib/treasurer/accounts.rb', line 207

def summary_line(today, days_before)

  <<EOF
  #{name_c} & #{balance.to_tex} & #{deposited(today, days_before).to_tex} & #{withdrawn(today, days_before).to_tex} 
EOF
end

#summary_table(today, days_before) ⇒ Object



196
197
198
199
200
201
202
203
204
205
206
# File 'lib/treasurer/accounts.rb', line 196

def summary_table(today, days_before)

  <<EOF
\\subsubsection{#{name_c}}
\\begin{tabulary}{0.8\\textwidth}{ r | l}
Balance & #{balance} \\\\
Deposited & #{deposited(today, days_before)} \\\\
Withdrawn & #{withdrawn(today, days_before)} \\\\
\\end{tabulary}
EOF
end

#typeObject



91
92
93
94
95
96
97
98
# File 'lib/treasurer/accounts.rb', line 91

def type
  #account_type(name)
  if ACCOUNT_INFO[name] and type = ACCOUNT_INFO[name][:type]
    type
  else
    :Expense
  end
end

#withdrawn(today, days_before) ⇒ Object



174
175
176
177
# File 'lib/treasurer/accounts.rb', line 174

def withdrawn(today, days_before)
  #@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
  @runs.find_all{|r| r.days_ago(today) < days_before and r.date <= today }.map{|r| (@external) ? r.deposit : r.withdrawal }.sum || 0
end

#write_balance_graph(today, days_before, days_ahead) ⇒ Object

Write an eps graph to disk of past and projected balance of the account



290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/treasurer/accounts.rb', line 290

def write_balance_graph(today, days_before, days_ahead)
  #accshort = name.gsub(/\s/, '')
  if not (@external or type == :Equity or not has_balance?)
    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]'})
  else
    pastdates = (today-days_before..today).to_a
    balances = pastdates.map{|date| balance(date)}
    kit = GraphKit.quick_create([pastdates.map{|d| d.to_time.to_i}, balances])
  end
  futuredates = (today..today+days_ahead).to_a
  projection = futuredates.map{|date| projected_balance(date) }
  kit2 = GraphKit.quick_create([futuredates.map{|d| d.to_time.to_i}, projection])
  red = futuredates.map{|date| red_line(date)}
  kit3 = GraphKit.quick_create([futuredates.map{|d| d.to_time.to_i}, red])
  @reporter. = @reporter.[currency]
  limit = futuredates.map{|date| projected_balance(date)}
  kit4 = GraphKit.quick_create([futuredates.map{|d| d.to_time.to_i}, limit])
  @reporter. = @reporter.[currency]
  #ep ['projected_account_factor!!!!', @reporter.projected_account_factor]
  stable = futuredates.map{|date| projected_balance(date)}
  kit5 = GraphKit.quick_create([futuredates.map{|d| d.to_time.to_i}, stable])

  [kit2,kit4,kit5].each{|k| k.data[0].y.data[0] = balance(today)}
  #exit
  @reporter. = nil
  kit += ( kit4 + kit5 + kit2)
  #kit.yrange = [(m = kit.data.map{|dk| dk.y.data.min}.min; m-m.abs*0.1), (m=kit.data.map{|dk| dk.y.data.max}.max; m+m.abs*0.1)]
  #kit += (kit2)
  kit = kit3 + kit
  kit.title = "Balance for #{name_c}"
  kit.xlabel = %['Date' offset 0,-2]
  kit.xlabel = nil
  kit.ylabel = "Balance"
  kit.gp.mytics= "5"
  kit.gp.grid = "ytics mytics lw 2,lw 1"


  kit.data[0].gp.title = 'Limit'
  kit.data[1].gp.title = 'Previous'
  #kit.data[2].gp.title = '0 GBP Discretionary'
  kit.data[2].gp.title = 'Avoid Limit'
  kit.data[3].gp.title = 'Stable'
  kit.data[4].gp.title = 'Projection'
  kit.data.each{|dk| dk.gp.with = "l lw 5"}
  kit.data[4].gp.with = "l lw 5 dt 2 lc rgb 'black' "
  kit.gp.key = ' bottom left '
  kit.gp.key = ' rmargin samplen 2'
  kit.gp.decimalsign = 'locale "en_GB.UTF-8"'

  #bal, avail = balance, available     
    
  if avail = available
    kit.gp.label = [
      %[ "Balance \\n#{balance}\\n\\nAvailable\\n#{avail}" at screen 0.95, screen 0.5 right],
    ]
  end

  #(p kit; STDIN.gets) if name == :LloydsCreditCard
  CodeRunner::Budget.kit_time_format_x(kit)
  kit.gp.format.push %[y "%'.2f"]
  size = case type
         when :Equity
           "4.0in,4.0in"
         else
           "4.0in,2.5in"
         end

  fork do
    #(kit).gnuplot_write("#{name_c_file}_balance2.eps", size: size) #, latex: true)
    (kit).gnuplot_write("#{name_c_file}_balance.eps", size: size) #, latex: true)
    # Deprecate ps2epsi
    # system %[ps2epsi #{name_c_file}_balance2.eps #{name_c_file}_balance.eps]
    #system %[ps2eps < #{name_c_file}_balance2.eps > #{name_c_file}_balance.eps]
    system %[epstopdf #{name_c_file}_balance.eps]
  end
  #%x[epstopdf #{name}_balance.eps]
end