added pagination gem, many fixes
[smr.git] / gui / lib / smr_cashflowlog.rb
blob9f432b2e9857221823ae0e695cb2199927af8ffe
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)
100             orders.each do |o|
101                 case @filter
102                     when :charges
103                         tmp = Array.new
104                         tmp << '%.2f Provisions' % o.provision if o.provision > 0
105                         tmp << '%.2f Courtage' % o.courtage if o.courtage > 0
106                         tmp << '%.2f Expenses' % o.expense if o.expense > 0
108                         what = 'payed %s on Order #%i (%s)' % [tmp.join(', '), o.id, o.to_s]
109                         total = o.provision + o.courtage + o.expense
110                    
111                     when :provision 
112                         what = 'payed %.2f Provisions on Order #%i (%s)' % [o.provision, o.id, o.to_s]
113                         total = o.provision 
115                     when :courtage
116                         what = 'payed %.2f Courtage on Order #%i (%s)' % [o.courtage, o.id, o.to_s]
117                         total = o.courtage
119                     when :expense
120                         what = 'payed %.2f Expenses on Order #%i (%s)' % [o.expense, o.id, o.to_s]
121                         total = o.expense
123                     else # :orders and :none
124                         what = '%s in Order #%i' % [o.to_s, o.id]
125                         total = o.shares * o.quote
126                         total *= -1 if o.type == 'buy'
127                 end
128                 
129                 next if total == 0.0 # skip everything that did not create any cashflow
130                 log_orders << SmrCashflowLogItem.new(Time.at(o.executed), what, total, o.comment)
131             end
132         end
134         if [:none, :dividend].include?(@filter) then
135             # cashflow from dividend received
136             Position.select(:id)
137                 .joins(:Portfolio)
138                 .where('portfolio.id_user'=>@id_user)
139                 .where('(position.closed=0 OR position.closed>=%i) OR (position.closed BETWEEN %i AND %i)' % [@end_date, @start_date, @end_date]).each do |p|        
140                 
141                 smrp = SmrPosition.new(p.id, @id_user, Time.at(@end_date))
142                 smrp.dividend.payments.each do |dp|
143                     next if dp[:time].to_i < @start_date
144                     next if dp[:total] == 0.0 # skips positions sold right before ex-Div date
145                     log_dividends << SmrCashflowLogItem.new(dp[:time],
146                         'received dividend of %.2f for %i shares from %s' % [dp[:received], dp[:shares], smrp.stock.name],
147                         dp[:total],
148                         'via %s' % smrp.portfolio.name 
149                     )
150                 end
151             end
152         end
154         # merge cashflows by date
155         @log = log_orders + log_dividends
156         @log.sort_by!{ |i| i.date}.reverse!
157     end
161 # Represents a single item of cashflow.
163 # Cashflow may be generated by an order, a dividend payment or something else.
164 # This class is made handy for presenting data in templates.
165 class SmrCashflowLogItem
166     def initialize(date, what, total, comment='')
167         @date=date; @what=what; @total=total; @comment = comment;
168     end
170     def date
171         @date
172     end
174     def what
175         @what
176     end
178     def total
179         @total
180     end
182     def comment
183         @comment
184     end