manage bookmarks, bugfixes
[smr.git] / gui / app / models / order.rb
blob181e7d324e2dd735a042b56ce00fdfd9e05ed438
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/>.
16 require 'smr/link.rb'
18 class Order < ActiveRecord::Base
19     include Smr::Extensions::Link
20     include Smr::Extensions::DateTimeWrapper
22         belongs_to :Position, :foreign_key=>'id_position'
23         has_many   :PositionRevision, :foreign_key=>'id_order'
24         has_many   :DocumentAssign, :foreign_key=>'id_order'
25         has_one    :Portfolio, :through => :Position
26         has_one    :Security, :through => :Position
27         has_one    :Organization, :through => :Security
29     # define scopes and ransack aliases for finding things
30     scope :executed, -> { where('quote>0') }
31     scope :pending,  -> { where(:quote=>0, :is_canceled=>[false, nil]) }
32     scope :canceled, -> { where(:is_canceled=>true) }
34     ransack_alias :ransackcolumns, :comment_or_Security_brief_or_Security_symbol_or_Organization_name
36     # structural validations
37     validates_associated :Position
39     # data validations
40     validates :type, inclusion: { in: %w(buy sale), message: "%{value} is not a valid Order type" }
41     validates :type, presence: true
42     validates :addon, inclusion: { in: %w(none StopLoss StopBuy), message: "%{value} is not a valid Order AddOn option" }
44     validates :shares, numericality: { greater_than: 0 }
45     validates :limit, numericality: { greater_than: 0 }
46     validates :provision, numericality: true
47     validates :courtage, numericality: true
48     validates :expense, numericality: true
50     self.inheritance_column = :none
52     ##
53     # Time when it was executed or False in case it is not
54     def time_executed
55         return false unless is_executed?
56         self.PositionRevision.first.time_created
57     end
59     # returns Order status as human readable string
60     def status
61         s = Array.new
62         _is_expired = false
64         if is_canceled
65             s << 'canceled'
66         elsif expire > 0 and not is_executed?
67             if time_expire > Time.now then
68                 s << 'will expire on %s' % time_expire.to_date
69             else
70                 s << 'expired on %s' % time_expire.to_date
71                 _is_expired = true
72             end
73         end
75         if not _is_expired and quote == 0
76             s << 'active'
77         elsif quote > 0
78             s << 'executed'
79         end
81         if accrued_interest > 0
82             if is_purchase?
83                 s << 'paid %.2f interest' % accrued_interest
84             else
85                 s << 'received %.2f interest' % accrued_interest
86             end
87         end
89         return s.join(', ')
90     end
92     # tells humans what this Order is about
93     def to_s
94         if triggered_by_order then
95             '%s for %s.' % [
96                 self.PositionRevision.first.describe_type,
97                 get_trigger_order.Position.Security
98             ]
99         elsif self.Position.is_cash_position?
100             self.PositionRevision.first.describe_type
101         else
102             addon = if self.addon!='none' then ' ' + self.addon else nil end
103             limit = if self.limit > 0 then 'limit %.4f'%self.limit else 'at market' end
104             quote = if self.quote > 0 then 'at quote %.4f'%self.quote else false end
106             '%s %.2f shares %s%s' % [
107                 self.type, self.shares, (quote||limit), addon
108             ]
109         end
110     end
112     ##
113     # tell whether this Order purchases shares when being executed
114     def is_purchase?
115         type == 'buy'
116     end
118     ##
119     # tell whether this Order sells shares when being executed
120     def is_sale?
121         type == 'sale'
122     end
124     ##
125     # tell whether this Order was executed
126     def is_executed?
127         quote > 0
128     end
130     ##
131     # tell whether this Order has been canceled
132     def is_canceled?
133         is_canceled
134     end
136     ##
137     # tell whether this Order is still pending the decision of being executed,
138     # canceled or expire.
139     def is_pending?
140         quote == 0 and not is_canceled
141     end
143     ##
144     # volume of this order
145     def volume
146         self.shares * self.quote
147     end
149     ##
150     # total charges recorded on this order
151     def charges
152         self.provision + self.courtage + self.expense
153     end
155     ##
156     # charges per 1k of order volume
157     def charges_per_kilo
158         self.charges / (self.volume / 1000)
159     end
161     ##
162     # Tell it there is a Document assigned.
163     def has_document?
164         if self.DocumentAssign.first.is_a?(DocumentAssign) then true else false end
165     end
166     
167     ##
168     # Document.id of assigned Smr::UploadedFile or +nil+. Use #has_document?
169     # first.
170     def id_document
171         self.DocumentAssign.first.id_document
172     end
174     ##
175     # return Order that triggered /this/ Order or False.
176     def get_trigger_order
177         if triggered_by_order then
178             Order.find(triggered_by_order)
179         else false end
180     end