334 lines
11 KiB
Ruby
334 lines
11 KiB
Ruby
# This file is a part of Redmine CRM (redmine_contacts) plugin,
|
|
# customer relationship management plugin for Redmine
|
|
#
|
|
# Copyright (C) 2010-2018 RedmineUP
|
|
# http://www.redmineup.com/
|
|
#
|
|
# redmine_contacts is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# redmine_contacts is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with redmine_contacts. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
class DealsController < ApplicationController
|
|
unloadable
|
|
|
|
PRICE_TYPE_PULLDOWN = [l(:label_price_fixed_bid), l(:label_price_per_hour)]
|
|
|
|
before_action :find_deal, :only => [:show, :edit, :update, :destroy]
|
|
before_action :find_project, :only => [:new, :create, :update_form]
|
|
before_action :bulk_find_deals, :only => [:bulk_update, :bulk_edit, :bulk_destroy, :context_menu]
|
|
before_action :authorize, :except => [:index]
|
|
before_action :find_optional_project, :only => [:index]
|
|
before_action :update_deal_from_params, :only => [:edit, :update]
|
|
before_action :build_new_deal_from_params, :only => [:new, :update_form]
|
|
before_action :find_deal_attachments, :only => :show
|
|
skip_before_filter :authorize, :only => :add_product_line if RedmineContacts.products_plugin_installed?
|
|
|
|
accept_api_auth :index, :show, :create, :update, :destroy
|
|
|
|
helper :attachments
|
|
helper :timelog
|
|
helper :watchers
|
|
helper :custom_fields
|
|
helper :context_menus
|
|
helper :sort
|
|
helper :crm_queries
|
|
helper :notes
|
|
helper :queries
|
|
helper :calendars
|
|
include QueriesHelper
|
|
include CrmQueriesHelper
|
|
include WatchersHelper
|
|
include DealsHelper
|
|
include SortHelper
|
|
if RedmineContacts.products_plugin_installed?
|
|
include ProductsHelper
|
|
helper :products
|
|
end
|
|
|
|
def index
|
|
retrieve_crm_query('deal')
|
|
sort_init(@query.sort_criteria.empty? ? [['created_on', 'desc']] : @query.sort_criteria)
|
|
sort_update(@query.sortable_columns)
|
|
@query.sort_criteria = sort_criteria.to_a
|
|
|
|
if @query.valid?
|
|
case params[:format]
|
|
when 'csv', 'pdf'
|
|
@limit = Setting.issues_export_limit.to_i
|
|
when 'atom'
|
|
@limit = Setting.feeds_limit.to_i
|
|
when 'xml', 'json'
|
|
@offset, @limit = api_offset_and_limit
|
|
else
|
|
@limit = per_page_option
|
|
end
|
|
|
|
@deals_count = @query.object_count
|
|
@deals_scope = @query.objects_scope
|
|
@deal_amount = @query.deal_amount
|
|
@deal_weighted_amount = @query.weighted_amount
|
|
@deals_pages = Paginator.new @deals_count, @limit, params['page']
|
|
@offset ||= @deals_pages.offset
|
|
@deal_count_by_group = @query.object_count_by_group
|
|
@deals = @query.results_scope(
|
|
:include => [{ :contact => [:avatar, :projects, :address] }, :author],
|
|
:search => params[:search],
|
|
:order => sort_clause,
|
|
:limit => @limit,
|
|
:offset => @offset
|
|
)
|
|
|
|
if deals_list_style == 'crm_calendars/crm_calendar'
|
|
retrieve_crm_calendar(:start_date_field => 'due_date')
|
|
@calendar.events = @query.results_scope(
|
|
:include => [:contact],
|
|
:search => params[:search],
|
|
:conditions => ['due_date BETWEEN ? AND ?', @calendar.startdt, @calendar.enddt]
|
|
)
|
|
end
|
|
|
|
respond_to do |format|
|
|
format.html { request.xhr? ? render(:partial => deals_list_style, :layout => false) : last_notes }
|
|
format.api
|
|
format.atom { render_feed(@deals, :title => "#{@project || Setting.app_title}: #{l(:label_order_plural)}") }
|
|
format.csv { send_data(deals_to_csv(@deals), :type => 'text/csv; header=present', :filename => 'deals.csv') }
|
|
format.pdf { send_data(deals_to_pdf(@deals, @project, @query), :type => 'application/pdf', :filename => 'deals.pdf') }
|
|
end
|
|
else
|
|
respond_to do |format|
|
|
format.html { render(:template => 'deals/index', :layout => !request.xhr?) }
|
|
format.any(:atom, :csv, :pdf) { render(:nothing => true) }
|
|
format.api { render_validation_errors(@query) }
|
|
end
|
|
end
|
|
rescue ActiveRecord::RecordNotFound
|
|
render_404
|
|
end
|
|
|
|
def show
|
|
@note = DealNote.new(:created_on => Time.now)
|
|
respond_to do |format|
|
|
format.html do
|
|
@deal_issues = @deal.issues.visible
|
|
@deal.viewed
|
|
@deal_events = (@deal.deal_processes.where("#{DealProcess.table_name}.old_value IS NOT NULL").includes([:to, :from, :author]) | @deal.notes.includes([:attachments, :author])).map{|o| {:date => o.is_a?(DealProcess) ? o.created_at : o.created_on, :author => o.author, :object => o} }
|
|
@deal_events.sort! { |x, y| y[:date] <=> x[:date] }
|
|
end
|
|
format.api
|
|
end
|
|
end
|
|
|
|
def new
|
|
end
|
|
|
|
def create
|
|
@deal = Deal.new
|
|
@deal.safe_attributes = params[:deal]
|
|
@deal.project = @project
|
|
@deal.author ||= User.current
|
|
@deal.price = parsed_price(params[:deal][:price])
|
|
@deal.init_deal_process(User.current)
|
|
if @deal.save
|
|
flash[:notice] = l(:notice_successful_create)
|
|
respond_to do |format|
|
|
format.html { redirect_to(params[:continue] ? { :action => 'new' } : { :action => 'show', :id => @deal }) }
|
|
format.api { render :action => 'show', :status => :created, :location => deal_url(@deal) }
|
|
end
|
|
|
|
else
|
|
respond_to do |format|
|
|
format.html { render :action => 'new' }
|
|
format.api { render_validation_errors(@deal) }
|
|
end
|
|
end
|
|
end
|
|
|
|
def update
|
|
@deal.init_deal_process(User.current)
|
|
@deal.safe_attributes = params[:deal]
|
|
if @deal.save
|
|
# @deal.contacts = [Contact.find(params[:contacts])] if params[:contacts]
|
|
retrieve_crm_query('deal')
|
|
@deals_scope = @query.objects_scope
|
|
flash[:notice] = l(:notice_successful_update)
|
|
respond_to do |format|
|
|
format.html { redirect_back_or_default(:action => 'show', :id => @deal) }
|
|
format.api { render_api_ok }
|
|
format.js { render :update_total }
|
|
end
|
|
else
|
|
respond_to do |format|
|
|
format.html { render :action => 'edit' }
|
|
format.api { render_validation_errors(@deal) }
|
|
format.js { render "alert('Error!')" }
|
|
end
|
|
end
|
|
end
|
|
|
|
def edit
|
|
respond_to do |format|
|
|
format.html {}
|
|
format.xml {}
|
|
end
|
|
end
|
|
|
|
def destroy
|
|
if @deal.destroy
|
|
flash[:notice] = l(:notice_successful_delete)
|
|
respond_to do |format|
|
|
format.html { redirect_to :action => 'index', :project_id => params[:project_id] }
|
|
format.api { render_api_ok }
|
|
end
|
|
else
|
|
flash[:error] = l(:notice_unsuccessful_save)
|
|
end
|
|
end
|
|
|
|
def context_menu
|
|
@deal = @deals.first if @deals.size == 1
|
|
@can = { :edit => User.current.allowed_to?(:edit_deals, @projects),
|
|
:delete => User.current.allowed_to?(:delete_deals, @projects) }
|
|
|
|
@back = back_url
|
|
render :layout => false
|
|
end
|
|
|
|
def bulk_destroy
|
|
@deals.each do |deal|
|
|
begin
|
|
deal.reload.destroy
|
|
rescue ::ActiveRecord::RecordNotFound # raised by #reload if deal no longer exists
|
|
# nothing to do, deal was already deleted (eg. by a parent)
|
|
end
|
|
end
|
|
respond_to do |format|
|
|
format.html { redirect_back_or_default(:action => 'index', :project_id => params[:project_id]) }
|
|
format.api { head :ok }
|
|
end
|
|
end
|
|
|
|
def bulk_edit
|
|
@available_statuses = @projects.map(&:deal_statuses).inject { |memo, w| memo & w }
|
|
@custom_fields = DealCustomField.order(:name)
|
|
@available_categories = @projects.map(&:deal_categories).inject { |memo, w| memo & w }
|
|
@assignables = @projects.map(&:assignable_users).inject { |memo, a| memo & a }
|
|
end
|
|
|
|
def bulk_update
|
|
unsaved_deal_ids = []
|
|
@deals.each do |deal|
|
|
deal.reload
|
|
deal.init_deal_process(User.current)
|
|
deal.safe_attributes = parse_params_for_bulk_deal_attributes(params)
|
|
unless deal.save
|
|
# Keep unsaved deal ids to display them in flash error
|
|
unsaved_deal_ids << deal.id
|
|
end
|
|
if params[:note] && !params[:note][:content].blank?
|
|
note = DealNote.new
|
|
note.safe_attributes = params[:note]
|
|
note.author = User.current
|
|
deal.notes << note
|
|
end
|
|
end
|
|
set_flash_from_bulk_contact_save(@deals, unsaved_deal_ids)
|
|
redirect_back_or_default(:controller => 'deals', :action => 'index', :project_id => @project)
|
|
end
|
|
|
|
private
|
|
|
|
def last_notes(count = 5)
|
|
# TODO: Исправить говнокод этот и выделить все в плагин acts-as-noteble
|
|
scope = DealNote.where({})
|
|
scope = scope.where("#{Deal.table_name}.project_id = ?", @project.id) if @project
|
|
|
|
@last_notes = scope.visible.order("#{DealNote.table_name}.created_on DESC").limit(count)
|
|
end
|
|
|
|
def build_new_deal_from_params
|
|
if params[:id].blank?
|
|
@deal = Deal.new
|
|
@deal.assigned_to_id = User.current.id
|
|
@deal.name = params[:name] if params[:name]
|
|
@deal.contact = Contact.find(params[:contact_id]) if params[:contact_id]
|
|
if params[:copy_from]
|
|
begin
|
|
@copy_from = Deal.visible.find(params[:copy_from])
|
|
@deal.copy_from(@copy_from)
|
|
rescue ActiveRecord::RecordNotFound
|
|
render_404
|
|
return
|
|
end
|
|
end
|
|
else
|
|
@deal = Deal.visible.find(params[:id])
|
|
end
|
|
|
|
@deal.project = @project
|
|
@deal.author ||= User.current
|
|
@deal.safe_attributes = params[:deal]
|
|
|
|
@available_watchers = (@deal.project.users.sort + @deal.watcher_users).uniq
|
|
end
|
|
|
|
def update_deal_from_params
|
|
end
|
|
|
|
def update_form
|
|
end
|
|
|
|
def find_deal_attachments
|
|
@deal_attachments = Attachment.where(:container_type => 'Note', :container_id => @deal.notes.map(&:id)).order(:created_on)
|
|
end
|
|
|
|
def bulk_find_deals
|
|
@deals = Deal.where(:id => (params[:id] || params[:ids])).includes([:project, :contact])
|
|
raise ActiveRecord::RecordNotFound if @deals.empty?
|
|
if @deals.detect { |deal| !deal.visible? }
|
|
deny_access
|
|
return
|
|
end
|
|
@projects = @deals.collect(&:project).compact.uniq
|
|
@project = @projects.first if @projects.size == 1
|
|
rescue ActiveRecord::RecordNotFound
|
|
render_404
|
|
end
|
|
|
|
def find_deal
|
|
@deal = Deal.where(:id => params[:id]).includes([:project, :status, :category]).first
|
|
@project = @deal.project
|
|
rescue ActiveRecord::RecordNotFound
|
|
render_404
|
|
end
|
|
|
|
def find_project(project_id = nil)
|
|
project_id ||= (params[:deal] && params[:deal][:project_id]) || params[:project_id]
|
|
@project = Project.find(project_id)
|
|
rescue ActiveRecord::RecordNotFound
|
|
render_404
|
|
end
|
|
|
|
def parse_params_for_bulk_deal_attributes(params)
|
|
attributes = (params[:deal] || {}).reject { |_k, v| v.blank? }
|
|
attributes.keys.each { |k| attributes[k] = '' if attributes[k] == 'none' }
|
|
attributes[:custom_field_values].reject! { |_k, v| v.blank? } if attributes[:custom_field_values]
|
|
attributes
|
|
end
|
|
|
|
def parsed_price(price)
|
|
return unless price
|
|
price.gsub!(ContactsSetting.thousands_delimiter, '')
|
|
price.gsub!(ContactsSetting.decimal_separator, '.')
|
|
price.to_f
|
|
end
|
|
end
|