added user-selectable filters to SmrCashflowLog
[smr.git] / gui / lib / smr_cashflowlog.rb
blob6db77ee57c5078a4be26f9a9a92b3892f901d1f4
2 # This file is part of SMR.
4 # SMR is free software: you can redistribute it and/or modify it under the
5 # terms of the GNU General Public License as published by the Free Software
6 # Foundation, either version 3 of the License, or (at your option) any later
7 # version.
9 # SMR is distributed in the hope that it will be useful, but WITHOUT ANY
10 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License along with
14 # SMR.  If not, see <http://www.gnu.org/licenses/>.
18 # Log of Cashflows happened up to :date
20 # Cashflow is generated from Order executions and Dividend received.
21 class SmrCashflowLog
22     def initialize(start_date, end_date, id_user)
23         raise ':start_date must be of Time' unless start_date.is_a?(Time)
24         raise ':end_date must be of Time' unless end_date.is_a?(Time)
26         @start_date=start_date.to_i; @end_date=end_date.to_i; @id_user=id_user;
28         @filter = :none
29         @made_log = false
30         @log = Array.new
31     end
33  public
35     ##
36     # loops over SmrCashflowLogItems we have up to date
37     def each(&block)
38         generate_log if not @made_log
39         @log.each(&block)
40     end
41   
42     ##
43     # tell whether there is something
44     def empty?
45         generate_log if not @made_log
46         @log.empty?
47     end
49     ##
50     # list of filters that may be used on us. Also see set_filter().
51     def filters
52         {
53            'No Filter'            => :none,
54            'Dividend'             => :dividend,
55            'All Cost Charged'     => :charges,
56            'Provisions Charged'   => :provision,
57            'Courtage Charged'     => :courtage,
58            'Expenses Charged'     => :expense,
59            'Ordered Transactions' => :orders, 
60         }
61     end
63     ##
64     # apply a filter when creating the log, see filters()
65     def set_filter(filter)
66        raise 'filter must be one value from those filters() returns' unless self.filters.has_value?(filter)
67        @filter=filter
68     end
70     ##
71     # cumulated cashflow from all SmrCashflowLogItems
72     def total
73         generate_log if not @made_log
74         total = 0.0
75         @log.collect{|i| total += i.total }
76         total
77     end
79  protected
81     ##
82     # compose a log of cashflows from orders and dividend received
83     def generate_log
84         @made_log = true
85         @log.clear
87         log_orders = Array.new
88         log_dividends = Array.new
90         if [:none, :orders, :charges, :provision, :courtage, :expense].include?(@filter) then
91             # cashflow from transactions        
92             orders = Order.select('`order`.*', 'pr.date AS executed')
93                 .where(:issued=>(@start_date..@end_date))
94                 .where('quote > 0')
95                 .joins('LEFT JOIN position_revision pr ON pr.id_order = order.id')
96                 .joins('LEFT JOIN position p ON p.id = order.id_position')
97                 .joins('LEFT JOIN stock s ON s.id = p.id_stock')
98                 .order(issued: :desc)
99                 .limit(10)
101             orders.each do |o|
102                 case @filter
103                     when :charges
104                         tmp = Array.new
105                         tmp << '%.2f Provisions' % o.provision if o.provision > 0
106                         tmp << '%.2f Courtage' % o.courtage if o.courtage > 0
107                         tmp << '%.2f Expenses' % o.expense if o.expense > 0
109                         what = 'payed %s on Order #%i (%s)' % [tmp.join(', '), o.id, o.to_s]
110                         total = o.provision + o.courtage + o.expense
111                    
112                     when :provision 
113                         what = 'payed %.2f Provisions on Order #%i (%s)' % [o.provision, o.id, o.to_s]
114                         total = o.provision 
116                     when :courtage
117                         what = 'payed %.2f Courtage on Order #%i (%s)' % [o.courtage, o.id, o.to_s]
118                         total = o.courtage
120                     when :expense
121                         what = 'payed %.2f Expenses on Order #%i (%s)' % [o.expense, o.id, o.to_s]
122                         total = o.expense
124                     else # :orders and :none
125                         what = '%s in Order #%i' % [o.to_s, o.id]
126                         total = o.shares * o.quote
127                         total *= -1 if o.type == 'buy'
128                 end
129                 
130                 next if total == 0.0 # skip everything that did not create any cashflow
131                 log_orders << SmrCashflowLogItem.new(Time.at(o.executed), what, total, o.comment)
132             end
133         end
135         if [:none, :dividend].include?(@filter) then
136             # cashflow from dividend received
137             dividends = Dividend
138                 .select(:id, :date, :received, 'stock.name', 'position_revision.shares')
139                 .where(:date=>(@start_date..@end_date))
140                 .joins(Position: :PositionRevision)
141                 .joins(Position: :Portfolio)
142                 .joins(Position: :Stock)
143                 .where('portfolio.id_user' => @id_user)
144                 .where('(position.closed=0 OR position.closed>=dividend.date)')
145                 .where('dividend.received * position_revision.shares > 0')
146                 .order('dividend.date DESC, position_revision.date DESC')
147        
148             known = [] 
149             dividends.each do |d|
150                 if known[d.id].nil? then
151                     log_dividends << SmrCashflowLogItem.new(d.time,
152                         'received dividend of %f for %f shares from %s' % [d.received, d.shares, d.name],
153                         d.shares * d.received
154                     )
155                 end
156                 known[d.id] = true
157             end
158         end
160         # merge cashflows by date
161         @log = log_orders + log_dividends
162         @log.sort_by!{ |i| i.date}.reverse!
163     end
167 # Represents a single item of cashflow.
169 # Cashflow may be generated by an order, a dividend payment or something else.
170 # This class is made handy for presenting data in templates.
171 class SmrCashflowLogItem
172     def initialize(date, what, total, comment='')
173         @date=date; @what=what; @total=total; @comment = comment;
174     end
176     def date
177         @date
178     end
180     def what
181         @what
182     end
184     def total
185         @total
186     end
188     def comment
189         @comment
190     end