ability to settle cashpositions
[smr.git] / gui / app / controllers / position_controller.rb
blobfe388880422e8c4eaa0e0e3fc808c90e6af3bf43
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(@page, Smr::UploadedFiles.new(current_user.id, :id_position=>@position.id))
52         if params[:add_dividend] then
53             @dividend = Dividend.new(:exdate=>smr_browse_date - 2.days, :id_position=>@position.id)
54         end
55     end
57     ##
58     # conditionally make a new position
59     #
60     # Most of the time this will not create a new position but rather add an
61     # order to an existing position. It can perform one of these things
62     # actually:
63     #
64     # 1) make a new order on existing position
65     # 2) make new position in given portfolio with new order
66     # 3) make new position in selectable portfolio holding selectable security
67     #    order
68     #    - OR -
69     #    make new order on selected existing position
70     #
71     # In any case the +session+ is checked for :position_prefilled_order which
72     # will be used as basis for the new order, if present.
73     def new
74         if session[:position_prefilled_order] and session[:position_prefilled_order].is_a?(Order) then
75             @order = session[:position_prefilled_order]
76         else
77             @order = Order.new(:time_issued=>smr_browse_date, :time_expire=>(smr_browse_date+1.month).end_of_day)
78         end
80         if params[:id_position] then
81             # 1)
82             @position = Smr::AssetPosition.new(params[:id_position], current_user.id, smr_browse_date)
83         elsif params[:id_portfolio] then
84             # 2)
85             @portfolio = Portfolio.find_by_id(params[:id_portfolio])
86             @securities = Security.where.not(:id=>Smr::ID_CASH_SECURITY).all.to_a
87         else
88             # 3)
89             @portfolios = Portfolio.select(:id, :name).where(:id_user=>current_user.id).order(order: :desc)
90             @open_positions = Smr::Asset.new(current_user.id, smr_browse_date, :skip_cash_positions=>true).open_positions
91             @securities = Security.where.not(:id=>Smr::ID_CASH_SECURITY).all.to_a
92         end
93     end
95     ##
96     # creates an position/order by taking POST
97     #
98     # FIXME: Gummicode! Test carefully, ie issue on closed position?
99     def create
100         redirect = :back
102         order = Order.new(order_params)
104         if not params[:id_position].empty? then
105             # 1)
106             order.id_position = params[:id_position].to_i
107             redirect = position_path(order.id_position)
108         elsif params[:id_portfolio].empty? or params[:id_security].empty?  then
109             redirect_to :back, :notice=>'Please select some Security and Portfolio or use an existing Position to order.'
110             return
111         else
112             # 2) and 3)
113             position = Position.new(
114                 :id_portfolio=>params[:id_portfolio].to_i,
115                 :id_security=>params[:id_security].to_i,
116                 :comment=>''
117             )
118             position.save!
120             order.id_position=position.id
122             redirect = position_path(position.id)
123         end
125         order.save!
127         # handle execute
128         if params[:execute_now].to_i == 1 and params[:execute_quote].to_f > 0.0 then
129             t = Smr::Transaction.new(
130                     Position.find(order.id_position),
131                     current_user,
132                     :order=>order, :order_is_redemption=>(params[:is_redemption] || false)
133             )
134             t.execute(params[:execute_quote].to_f, Time.parse(params[:execute_time]))
135             n = t.to_s
137             if params[:close_position].to_i == 1 then
138                 stat = Smr::AssetPosition.new(order.id_position, current_user.id, smr_browse_date)
139                         .close(Time.parse(params[:execute_time]))
140                 n += ' Closed position because all shares were sold.' if stat.is_a?(TrueClass)
141             end
142         else
143             n = 'Issued new Order to %s %f shares.' % [order.type, order.shares]
144         end
146         # handle document
147         if params[:order][:document] then
148             doc = Smr::UploadedFile.store(current_user.id, params[:order][:document])
149             doc.assign(:order=>order.id)
150         end
152         redirect_to redirect, :notice=>n
153     end
155     ##
156     # update Position details
157     # - that is things that wont change the positions` status
158     def patch
159         if params[:id] and params[:comment]
160             p = Position.find(params[:id])
161             p.comment = params[:comment]
162             p.save
163         end
164         redirect_to :back
165     end
167     ##
168     # close this position at smr_browse_date
169     def close
170         p = Smr::AssetPosition.new(params[:id].to_i, current_user.id, smr_browse_date)
171         stat = p.close(smr_browse_date)
172         if stat.is_a?(TrueClass) then
173             n='Closed position #%i as of %s.' % [p.id, p.time_closed]
174             r=:back
175         elsif stat.is_a?(Order) then
176             session[:position_prefilled_order] = stat
177             n='Position holds shares. Can not close it right away. A new '\
178              +'order to sell them off has been pre-filled, please edit and '\
179              +'execute.'
180             r=new_position_path + '?id_position=%i' % p.id
181         else
182             n='Can not close position #%i because it is closed already.' % p.id
183             r=:back
184         end
185         redirect_to r, :notice=>n
186     end
188     def edit
189     end
191     def update
192     end
194     ##
195     # apply Order to Position at #smr_browse_date
196     def execute_order
197         execute_time = Time.parse(params[:execute_time])
198         p = Smr::AssetPosition.new(params[:id], current_user.id)
199         t = Smr::Transaction.new(p, current_user, :issue_time=>execute_time, :order=>Order.find(params[:id_order]))
200         t.execute(params[:execute_quote].to_f, execute_time)
202         # try to close the position, it may remain open unless empty
203         if t.order.is_sale?
204             Smr::AssetPosition.new(params[:id], current_user.id, execute_time+1.second).close
205         end
207         redirect_to :back, :notice=>t.to_s
208     end
210     ##
211     # cancel pending order by setting the canceled flag
212     def cancel_order
213         o = Order.find_by_id(params[:id_order])
214         o.is_canceled = 1
215         o.save!
216         redirect_to :back, :notice=>'Order #%i has been canceled' % o.id
217     end
219     protected
221     ##
222     # internal helper defining which parameters to accept for an order
223     def order_params
224       params.require(:order).permit(
225         :exchange, :shares, :limit, :addon, :provision, :courtage, :type,
226         :comment, :expense, :accrued_interest, :issued, :expire
227       )
228     end