manage bookmarks, bugfixes
[smr.git] / gui / app / controllers / position_controller.rb
blob435fc32505d26cf1fbba7e97ac0c1c306427ec5a
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 'uploaded_file'
19 # Controller for AssetPosition
21 # This also cares about orders and other things since an AssetPosition
22 # is more than just the Position model.
23 class PositionController < ApplicationController
25     ##
26     # Show details about a AssetPosition.
27     def show
28         @position = Smr::AssetPosition.new(params[:id].to_i, current_user.id, smr_browse_date)
29         redirect_to root_path if @position.is_cash_position?
31         if @position.is_new? then
32             smr_menu_addsubitem('assets', {
33                 '+ order'=>new_position_path + '?id_position=%i'%params[:id],
34                 '- sell off'=>'/close?id=%i' % params[:id]
35             })
36         elsif not @position.is_closed?
37             smr_menu_addsubitem('assets', {
38                 '+ order'=>new_position_path + '?id_position=%i' % params[:id],
39                 '+ dividend'=>position_path + '?add_dividend=true',
40                 '- sell off'=>'/close?id=%i' % params[:id],
41                 '+ document'=>position_path + '?add_document=true',
42             })
43         elsif @position.is_closed? and @position.time_closed > 1.month.ago
44             smr_menu_addsubitem('assets', {
45                 '+ dividend'=>position_path + '?add_dividend=true'
46             })
47         end
49         @page = smr_page
50         @documents, @total_pages = smr_paginate(
51             @page,
52             Smr::UploadedFiles.new(current_user.id, :id_position=>@position.id, :date=>smr_browse_date)
53         )
55         if params[:add_dividend] then
56             @dividend = Dividend.new(:date_exdate=>smr_browse_date - 2.days, :id_position=>@position.id)
57         end
58     end
60     ##
61     # conditionally make a new position
62     #
63     # Most of the time this will not create a new position but rather add an
64     # order to an existing position. It can perform one of these things
65     # actually:
66     #
67     # 1) make a new order on existing position
68     # 2) make new position in given portfolio with new order
69     # 3) make new position in selectable portfolio holding selectable security
70     #    order
71     #    - OR -
72     #    make new order on selected existing position
73     #
74     # In any case the +session+ is checked for :position_prefilled_order which
75     # will be used as basis for the new order, if present.
76     def new
77         if session[:position_prefilled_order] and session[:position_prefilled_order].is_a?(Order) then
78             @order = session[:position_prefilled_order]
79         else
80             @order = Order.new(:time_issued=>smr_browse_date, :time_expire=>(smr_browse_date+1.month).end_of_day)
81         end
83         if params[:id_position] then
84             # 1)
85             @position = Smr::AssetPosition.new(params[:id_position], current_user.id, smr_browse_date)
86         elsif params[:id_portfolio] then
87             # 2)
88             @portfolio = Portfolio.find_by_id(params[:id_portfolio])
89             @securities = Security.where.not(:id=>Smr::ID_CASH_SECURITY).all.to_a
90         else
91             # 3)
92             @portfolios = Portfolio.select(:id, :name).where(:id_user=>current_user.id).order(order: :desc)
93             @open_positions = Smr::Asset.new(current_user.id, smr_browse_date, :skip_cash_positions=>true).open_positions
94             @securities = Security.where.not(:id=>Smr::ID_CASH_SECURITY).all.to_a
95         end
96     end
98     ##
99     # creates an position/order by taking POST
100     #
101     # FIXME: Gummicode! Test carefully, ie issue on closed position?
102     def create
103         redirect = :back
105         order = Order.new(order_params)
106         order.time_issued = Time.parse params[:time_issued]
107         order.time_expire = Time.parse params[:time_expire]
109         if not params[:id_position].empty? then
110             # 1)
111             order.id_position = params[:id_position].to_i
112             redirect = position_path(order.id_position)
113         elsif params[:id_portfolio].empty? or params[:id_security].empty?  then
114             redirect_to :back, :notice=>'Please select some Security and Portfolio or use an existing Position to order.'
115             return
116         else
117             # 2) and 3)
118             position = Position.new(
119                 :id_portfolio=>params[:id_portfolio].to_i,
120                 :id_security=>params[:id_security].to_i,
121                 :comment=>''
122             )
123             position.save!
125             order.id_position=position.id
127             redirect = position_path(position.id)
128         end
130         order.save!
132         # handle execute
133         if params[:execute_now].to_i == 1 and params[:execute_quote].to_f > 0.0 then
134             execute_time = Time.parse(params[:execute_time])
135             t = Smr::Transaction.new(
136                     Position.find(order.id_position),
137                     current_user,
138                     :issue_time=>order.time_issued,
139                     :order=>order, :order_is_redemption=>(params[:is_redemption] || false)
140             )
141             t.execute(params[:execute_quote].to_f, execute_time)
142             n = t.to_s
144             if params[:close_position].to_i == 1 then
145                 stat = Smr::AssetPosition.new(order.id_position, current_user.id, smr_browse_date)
146                         .close(execute_time)
147                 n += ' Closed position because all shares were sold.' if stat.is_a?(TrueClass)
148             end
149         else
150             n = 'Issued new Order to %s %f shares.' % [order.type, order.shares]
151         end
153         # handle document
154         if params[:order][:document] then
155             doc = Smr::UploadedFile.store(current_user.id, params[:order][:document])
156             doc.assign(:order=>order.id)
157         end
159         redirect_to redirect, :notice=>n
160     end
162     ##
163     # update Position details
164     # - that is things that wont change the positions` status
165     def patch
166         if params[:id] and params[:comment]
167             p = Position.find(params[:id])
168             p.comment = params[:comment]
169             p.save
170         end
171         redirect_to :back
172     end
174     ##
175     # close this position at smr_browse_date
176     def close
177         p = Smr::AssetPosition.new(params[:id].to_i, current_user.id, smr_browse_date)
178         stat = p.close(smr_browse_date)
179         if stat.is_a?(TrueClass) then
180             n='Closed position #%i as of %s.' % [p.id, p.time_closed]
181             r=:back
182         elsif stat.is_a?(Order) then
183             session[:position_prefilled_order] = stat
184             n='Position holds shares. Can not close it right away. A new '\
185              +'order to sell them off has been pre-filled, please edit and '\
186              +'execute.'
187             r=new_position_path + '?id_position=%i' % p.id
188         else
189             n='Can not close position #%i because it is closed already.' % p.id
190             r=:back
191         end
192         redirect_to r, :notice=>n
193     end
195     def edit
196     end
198     def update
199     end
201     ##
202     # apply Order to Position at #smr_browse_date
203     def execute_order
204         execute_time = Time.parse(params[:execute_time])
205         p = Smr::AssetPosition.new(params[:id], current_user.id, smr_browse_date)
206         t = Smr::Transaction.new(p, current_user, :issue_time=>execute_time, :order=>Order.find(params[:id_order]))
207         t.execute(params[:execute_quote].to_f, execute_time)
209         # try to close the position, it may remain open unless empty
210         if t.order.is_sale?
211             Smr::AssetPosition.new(params[:id], current_user.id, execute_time+1.second).close
212         end
214         redirect_to :back, :notice=>t.to_s
215     end
217     ##
218     # cancel pending order by setting the canceled flag
219     def cancel_order
220         o = Order.find_by_id(params[:id_order])
221         o.is_canceled = 1
222         o.save!
223         redirect_to :back, :notice=>'Order #%i has been canceled' % o.id
224     end
226     protected
228     ##
229     # internal helper defining which parameters to accept for an order
230     def order_params
231       params.require(:order).permit(
232         :exchange, :shares, :limit, :addon, :provision, :courtage, :type,
233         :comment, :expense, :accrued_interest, :time_issued, :time_expire
234       )
235     end