reworked dividend
[smr.git] / gui / lib / smr / dividend_income.rb
blobc4804309ce30fde9f49ec4a58adf4f376be1e63a
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/>.
17 module Smr  #:nodoc:
18   ##
19   # Dividend received on a AssetPosition at some point in time.
20   class DividendIncome
21       ##
22       # initialize with Smr::AssetPosition
23       def initialize (position)
24         raise 'position must be a Smr::AssetPosition' unless position.is_a?(Smr::AssetPosition)
25         @position = position
26         @payments = query_payments
27         @error = Array.new
28       end
30     public
32       ##
33       # Collection of DividendIncomeItem objects received.
34       attr_reader :payments
36       # returns String describing the error when some methode returned False
37       def error
38         @error.join(', ')
39       end
41       ##
42       # Returns total Dividend received, after tax and other costs.
43       def received
44         received = 0
45         @payments.each{|p| received += p.received_total }
46         received
47       end
49       def empty?
50         @payments.empty?
51       end
53       ##
54       # Add Dividend payment to this Position.
55       #
56       # :id_order should refer to the cash booking which represents the
57       # actually received amount (after tax and other costs).
58       def add_payment(paid, id_order, exdate=Time.now)
59         Dividend.new(
60             :exdate=>exdate.to_i, :paid=>paid, :id_order=>id_order,
61             :id_stock=>@position.id_stock, :id_position=>@position.id
62         ).save!
63       end
65     protected
67       ##
68       # Queries eligible Dividend payments to create DividendIncomeItem collection.
69       def query_payments
70         payments = Array.new
72         # OLD records
73         # - pre 20150403195501_enhance_dividend.rb migration
74         # - slow because revisions must be looped!
75         Dividend.where(:id_stock=>@position.stock.id, :id_order=>0, :id_position=>0)
76             .where('exdate <= %i' % @position.date).each do |d|
77             @position.revisions.each do |pr|
78                 payments[d.id] = Smr::DividendIncomeItem.new(d, :position_revision=>pr) if pr.date <= d.exdate
79             end
80         end
82         # NEW records
83         # - with received amount being a cash booking
84         Dividend.where(:id_position=>@position.id)
85             .where('exdate <= %i' % @position.date).order(exdate: :asc).each do |d|
86             payments[d.id] = Smr::DividendIncomeItem.new(d)
87         end
89         payments.compact! || Array.new
90       end
91   end
93   ##
94   # Represents a single Dividend payment.
95   class DividendIncomeItem
97     # Time when the security traded ex-Dividend and when payment was received.
98     attr_reader :exdate
99     attr_reader :valutadate
101     # amount received per share and in total at :valutadate
102     attr_reader :received
103     attr_reader :received_total
105     # amount the company has paid at :exdate
106     attr_reader :paid
108     # number of shares and invested amount of money at :exdate
109     attr_reader :shares
110     attr_reader :invested
112     ##
113     # Provide Dividend and, optionally, PositionRevision record to construct a payment.
114     def initialize(dividend, options={:position_revision=>false})
115         @exdate=dividend.time_exdate
116         @paid=dividend.paid
117         @id_order=dividend.id_order
119         if options[:position_revision]
120             # OLD records
121             # - unaware of exdate vs. valutadate
122             # - unaware of paid bs. actually received
123             @valutadate=@exdate
124             @shares=options[:position_revision].shares
125             @invested=options[:position_revision].invested
126             @received=dividend.paid
127             @received_total = @shares * @received
128         else
129             # NEW records
130             # - using cash booking
131             booking_rev = dividend.Order.PositionRevision.first
132             position_rev = PositionRevision
133                 .where(:id_position=>dividend.id_position)
134                 .where('date <= %i' % dividend.exdate)
135                 .order(date: :desc)
136                 .first
137             @valutadate = booking_rev.time
138             @received_total = dividend.Order.volume
139             @shares = position_rev.shares
140             @invested = position_rev.invested
141             @received = @received_total / @shares
142         end
143     end
145     ##
146     # tell whether there is a related Order available
147     def has_order?
148        @id_order != 0
149     end
151     ##
152     # wrapper to cash booking order, use #has_order? first
153     def order
154         Order.find(@id_order) if has_order?
155     end
156   end
158 end # module