diff --git a/app/controllers/contract_categories_controller.rb b/app/controllers/contract_categories_controller.rb new file mode 100644 index 0000000..6d1e72e --- /dev/null +++ b/app/controllers/contract_categories_controller.rb @@ -0,0 +1,68 @@ +class ContractCategoriesController < ApplicationController + unloadable + menu_item :settings + model_object ContractCategory + before_filter :find_model_object, :except => :new + before_filter :find_project_from_association, :except => :new + before_filter :find_project_by_project_id, :only => :new + before_filter :authorize + + verify :method => :post, :only => :destroy + + def new + @category = @project.contract_categories.build(params[:category]) + if request.post? + if @category.save + respond_to do |format| + format.html do + flash[:notice] = l(:notice_successful_create) + redirect_to :controller => 'projects', :action => 'settings', :tab => 'contacts', :id => @project + end + format.js do + # IE doesn't support the replace_html rjs method for select box options + render(:update) {|page| page.replace "contract_category_id", + content_tag('select', '' + options_from_collection_for_select(@project.contract_categories, 'id', 'name', @category.id), :id => 'contract_category_id', :name => 'contract[category_id]') + } + end + end + else + respond_to do |format| + format.html + format.js do + render(:update) {|page| page.alert(@category.errors.full_messages.join('\n')) } + end + end + end + end + end + + def edit + if request.post? and @category.update_attributes(params[:category]) + flash[:notice] = l(:notice_successful_update) + redirect_to :controller => 'projects', :action => 'settings', :tab => 'contacts', :id => @project + end + end + + def destroy + @contract_count = @category.contracts.size + if @contract_count == 0 + # No contract assigned to this category + @category.destroy + redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'contacts' + elsif params[:todo] + reassign_to = @project.contract_categories.find_by_id(params[:reassign_to_id]) if params[:todo] == 'reassign' + @category.destroy(reassign_to) + redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'contacts' + end + @categories = @project.contract_categories - [@category] + end + + private + # Wrap ApplicationController's find_model_object method to set + # @category instead of just @contract_category + def find_model_object + super + @category = @object + @project = @category.project + end +end diff --git a/app/controllers/contract_contacts_controller.rb b/app/controllers/contract_contacts_controller.rb new file mode 100644 index 0000000..e0891e4 --- /dev/null +++ b/app/controllers/contract_contacts_controller.rb @@ -0,0 +1,56 @@ +class ContractContactsController < ApplicationController + unloadable + + before_filter :find_project_by_project_id, :authorize + before_filter :find_contact, :only => :delete + before_filter :find_contract + + helper :contracts + + def add + @show_form = "true" + + if params[:contact_id] && request.post? then + find_contact + if !@contract.all_contacts.include?(@contact) + @contract.related_contacts << @contact + @contract.save + end + end + + respond_to do |format| + format.html { redirect_to :back } + format.js do + render :update do |page| + page.replace_html 'contract_contacts', :partial => 'contract_contacts/contacts' + end + end + end + end + + def delete + @contract.related_contacts.delete(@contact) + respond_to do |format| + format.html { redirect_to :back } + format.js do + render :update do |page| + page.replace_html 'contract_contacts', :partial => 'contract_contacts/contacts' + end + end + end + end + + + private + def find_contact + @contact = Contact.find(params[:contact_id]) + rescue ActiveRecord::RecordNotFound + render_404 + end + + def find_contract + @contract = Contract.find(params[:contract_id]) + rescue ActiveRecord::RecordNotFound + render_404 + end +end \ No newline at end of file diff --git a/app/controllers/contract_statuses_controller.rb b/app/controllers/contract_statuses_controller.rb new file mode 100644 index 0000000..349fa53 --- /dev/null +++ b/app/controllers/contract_statuses_controller.rb @@ -0,0 +1,63 @@ +class ContractStatusesController < ApplicationController + unloadable + + layout 'admin' + + before_filter :require_admin, :except => :assing_to_project + before_filter :find_project_by_project_id, :authorize, :only => :assing_to_project + + verify :method => :post, :only => [ :destroy, :create, :update, :move ], + :redirect_to => { :action => :index } + + def index + @contract_status_pages, @contract_statuses = paginate :contract_statuses, :per_page => 25, :order => "position" + render :action => "index", :layout => false if request.xhr? + end + + def new + @contract_status = ContractStatus.new + end + + def create + @contract_status = ContractStatus.new(params[:contract_status]) + if @contract_status.save + flash[:notice] = l(:notice_successful_create) + redirect_to :action =>"plugin", :id => "contacts", :controller => "settings", :tab => 'contract_statuses' + else + render :action => 'new' + end + end + + def edit + @contract_status = ContractStatus.find(params[:id]) + end + + def update + @contract_status = ContractStatus.find(params[:id]) + if @contract_status.update_attributes(params[:contract_status]) + flash[:notice] = l(:notice_successful_update) + redirect_to :action =>"plugin", :id => "contacts", :controller => "settings", :tab => 'contract_statuses' + else + render :action => 'edit' + end + end + + def destroy + ContractStatus.find(params[:id]).destroy + redirect_to :action =>"plugin", :id => "contacts", :controller => "settings", :tab => 'contract_statuses' + rescue + flash[:error] = l(:error_unable_delete) + redirect_to :action =>"plugin", :id => "contacts", :controller => "settings", :tab => 'contract_statuses' + end + + def assing_to_project + + if request.put? + @project.contract_statuses = !params[:contract_statuses].blank? ? ContractStatus.find(params[:contract_statuses]) : [] + @project.save + flash[:notice] = l(:notice_successful_update) + end + redirect_to :controller => 'projects', :action => 'settings', :tab => 'contracts', :id => @project + end + +end diff --git a/app/controllers/contracts_controller.rb b/app/controllers/contracts_controller.rb new file mode 100644 index 0000000..453695b --- /dev/null +++ b/app/controllers/contracts_controller.rb @@ -0,0 +1,247 @@ +class ContractsController < ApplicationController + unloadable + + PRICE_TYPE_PULLDOWN = [l(:label_price_fixed_bid), l(:label_price_per_hour)] + + before_filter :find_contract, :only => [:show, :edit, :update, :destroy] + before_filter :find_project_by_project_id, :only => [:new, :create] + before_filter :bulk_find_contracts, :only => [:bulk_update, :bulk_edit, :bulk_destroy, :context_menu] + before_filter :authorize, :except => [:index] + before_filter :find_optional_project, :only => [:index] + # before_filter :find_contracts, :only => :index + before_filter :update_contract_from_params, :only => [:edit, :update] + before_filter :build_new_contract_from_params, :only => [:new, :create] + before_filter :find_contract_attachments, :only => :show + + helper :attachments + helper :contacts + helper :notes + helper :timelog + helper :watchers + helper :custom_fields + include WatchersHelper + include ContractsHelper + + def new + @contract = Contract.new + @contract.contact = Contact.find(params[:contact_id]) if params[:contact_id] + @contract.assigned_to = User.current + end + + def create + @contract = Contract.new(params[:contract]) + # @contract.contacts = [Contact.find(params[:contacts])] + @contract.project = @project + @contract.author = User.current + @contract.init_contract_process(User.current) + if @contract.save + flash[:notice] = l(:notice_successful_create) + redirect_to :action => "show", :id => @contract + else + render :action => "new" + end + end + + def update + @contract.init_contract_process(User.current) + if @contract.update_attributes(params[:contract]) + # @contract.contacts = [Contact.find(params[:contacts])] if params[:contacts] + flash[:notice] = l(:notice_successful_update) + respond_to do |format| + format.html { redirect_to :action => "show", :id => @contract } + format.xml { } + end + else + respond_to do |format| + format.html { render :action => "edit"} + end + end + + end + + def edit + respond_to do |format| + format.html { } + format.xml { } + end + end + + def index + retrieve_contracts_query + params[:status_id] = "o" unless params.has_key?(:status_id) + find_contracts + respond_to do |format| + format.html{ request.xhr? ? render( :partial => "list", :layout => false, :locals => {:contracts => @contracts}) : last_notes } + format.xml { render :xml => @contracts} + format.json { render :text => @contracts.to_json, :layout => false } + end + end + + def show + @note = ContractNote.new + respond_to do |format| + format.html { @contract.viewed } + format.xml { } + end + end + + def destroy + if @contract.destroy + flash[:notice] = l(:notice_successful_delete) + else + flash[:error] = l(:notice_unsuccessful_save) + end + redirect_to :action => "index", :project_id => params[:project_id] + + end + + def context_menu + @contract = @contracts.first if (@contracts.size == 1) + @can = {:edit => User.current.allowed_to?(:edit_contracts, @projects), + :delete => User.current.allowed_to?(:delete_contracts, @projects) + } + + # @back = back_url + render :layout => false + end + + def bulk_destroy + @contracts.each do |contract| + begin + contract.reload.destroy + rescue ::ActiveRecord::RecordNotFound # raised by #reload if issue no longer exists + # nothing to do, issue was already deleted (eg. by a parent) + end + end + respond_to do |format| + format.html { redirect_back_or_default(:action => 'index', :project_id => @project) } + format.api { head :ok } + end + end + + def bulk_edit + @available_statuses = @projects.map(&:contract_statuses).inject{|memo,w| memo & w} + @available_categories = @projects.map(&:contract_categories).inject{|memo,w| memo & w} + @assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a} + end + + + def bulk_update + unsaved_contract_ids = [] + @contracts.each do |contract| + contract.reload + contract.init_contract_process(User.current) + unless contract.update_attributes(parse_params_for_bulk_contract_attributes(params)) + # Keep unsaved issue ids to display them in flash error + unsaved_contract_ids << contract.id + end + if params[:note] && !params[:note][:content].blank? + note = ContractNote.new(params[:note]) + note.author = User.current + contract.notes << note + end + + end + set_flash_from_bulk_issue_save(@contracts, unsaved_contract_ids) + redirect_back_or_default({:controller => 'contracts', :action => 'index', :project_id => @project}) + + end + + private + + def last_notes(count=5) + # TODO: ????????? ???????? ???? ? ???????? ??? ? ?????? acts-as-noteble + scope = ContractNote.scoped({}) + scope = scope.scoped(:conditions => ["#{Contract.table_name}.project_id = ?", @project.id]) if @project + + @last_notes = scope.visible.find(:all, + :limit => count, + :order => "#{ContractNote.table_name}.created_on DESC") + end + + + def build_new_contract_from_params + end + + def update_contract_from_params + end + + def find_contract_attachments + @contract_attachments = Attachment.find(:all, + :conditions => { :container_type => "Note", :container_id => @contract.notes.map(&:id)}, + :order => "created_on DESC") + end + + + def find_contracts(pages=true) + retrieve_date_range(params[:period].to_s) + + scope = Contract.scoped({}) + scope = scope.scoped(:conditions => ["#{Contract.table_name}.project_id = ?", @project.id]) if @project + scope = scope.scoped(:conditions => ["#{Contract.table_name}.status_id = ?", params[:status_id]]) if (!params[:status_id].blank? && params[:status_id] != "o") + scope = scope.scoped(:conditions => ["#{Contract.table_name}.category_id = ?", params[:category_id]]) if !params[:category_id].blank? + scope = scope.scoped(:conditions => ["#{Contract.table_name}.assigned_to_id = ?", params[:assigned_to_id]]) if !params[:assigned_to_id].blank? + scope = scope.scoped(:conditions => ["#{Contract.table_name}.created_on BETWEEN ? AND ?", @from, @to]) if (@from && @to) + + params[:search].split(' ').collect{ |search_string| scope = scope.live_search(search_string) } if !params[:search].blank? + scope = scope.visible + scope = scope.scoped(:include => :status, :conditions => ["#{ContractStatus.table_name}.is_closed = ?", false]) if (params[:status_id] == "o") + scope = scope.scoped(:order => :status_id) + + + @contracts_count = scope.count + + if pages + @contracts_sum = scope.sum(:price, :group => :currency) + + page_size = params[:page_size].blank? ? 20 : params[:page_size].to_i + @contracts_pages = Paginator.new(self, @contracts_count, page_size, params[:page]) + @offset = @contracts_pages.current.offset + @limit = @contracts_pages.items_per_page + + scope = scope.scoped :limit => @limit, :offset => @offset + @contracts = scope + + fake_name = @contracts.first.price if @contracts.length > 0 #without this patch paging does not work + end + + scope + + end + + # Filter for bulk issue operations + def bulk_find_contracts + @contracts = Contract.find_all_by_id(params[:id] || params[:ids], :include => :project) + raise ActiveRecord::RecordNotFound if @contracts.empty? + if @contracts.detect {|contract| !contract.visible?} + deny_access + return + end + @projects = @contracts.collect(&:project).compact.uniq + @project = @projects.first if @projects.size == 1 + rescue ActiveRecord::RecordNotFound + render_404 + end + + + def find_contract + @contract = Contract.find(params[:id], :include => [:project, :status, :category]) + @project ||= @contract.project + # if !(params[:project_id] == @project.identifier) + # params[:project_id] = @project.identifier + # redirect_to params + # end + rescue ActiveRecord::RecordNotFound + render_404 + end + + def parse_params_for_bulk_contract_attributes(params) + attributes = (params[:contract] || {}).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 + + + +end diff --git a/app/controllers/contracts_tasks_controller.rb b/app/controllers/contracts_tasks_controller.rb new file mode 100644 index 0000000..3150bed --- /dev/null +++ b/app/controllers/contracts_tasks_controller.rb @@ -0,0 +1,121 @@ +class ContractsTasksController < ApplicationController + unloadable + + before_filter :find_project_by_project_id, :authorize, :except => [:index] + before_filter :find_optional_project, :only => :index + before_filter :find_contract, :except => [:index, :add, :close] + before_filter :find_issue, :except => [:index, :new] + + def index + cond = "(1=1)" + # cond = "issues.assigned_to_id = #{User.current.id}" + cond << " and issues.project_id = #{@project.id}" if @project + cond << " and (issues.assigned_to_id = #{params[:assigned_to]})" unless params[:assigned_to].blank? + + @contracts_issues = Issue.visible.find(:all, + :joins => "INNER JOIN contracts_issues ON issues.id = contracts_issues.issue_id", + # :group => :issue_id, + :conditions => cond, + :order => "issues.due_date") + @users = assigned_to_users + end + + def new + issue = Issue.new + issue.subject = params[:task_subject] + issue.project = @project + issue.tracker_id = params[:task_tracker] + issue.author = User.current + issue.due_date = params[:due_date] + issue.assigned_to_id = params[:assigned_to] + issue.description = params[:task_description] + issue.status = IssueStatus.default + if issue.save + flash[:notice] = l(:notice_successful_add) + @contract.issues << issue + @contract.save + redirect_to :back + return + else + redirect_to :back + end + end + + + def add + @show_form = "true" + + if params[:contract_id] && request.post? then + find_contract + @contract.issues << @issue + @contract.save + end + + respond_to do |format| + format.html { redirect_to :back } + format.js do + render :update do |page| + page.replace_html 'issue_contracts', :partial => 'issues/contracts' + end + end + end + end + + def delete + @issue.contracts.delete(@contract) + respond_to do |format| + format.html { redirect_to :back } + format.js do + render :update do |page| + page.replace_html 'issue_contracts', :partial => 'issues/contracts' + end + end + end + end + + def close + @issue.status = IssueStatus.find(:first, :conditions => { :is_closed => true }) + @issue.save + respond_to do |format| + format.js do + render :update do |page| + page["issue_#{params[:issue_id]}"].visual_effect :fade + end + end + format.html {redirect_to :back } + end + + end + + private + + def find_contract + @contract = Contract.find(params[:contract_id]) + rescue ActiveRecord::RecordNotFound + render_404 + end + + def find_issue + @issue = Issue.find(params[:issue_id]) + rescue ActiveRecord::RecordNotFound + render_404 + end + + def assigned_to_users + user_values = [] + project = @project + user_values << ["<< #{l(:label_all)} >>", ""] + user_values << ["<< #{l(:label_me)} >>", User.current.id] if User.current.logged? + if project + user_values += project.users.sort.collect{|s| [s.name, s.id.to_s] } + else + project_ids = Project.all(:conditions => Project.visible_condition(User.current)).collect(&:id) + if project_ids.any? + # members of the user's projects + user_values += User.active.find(:all, :conditions => ["#{User.table_name}.id IN (SELECT DISTINCT user_id FROM members WHERE project_id IN (?))", project_ids]).sort.collect{|s| [s.name, s.id.to_s] } + end + end + end + + +end \ No newline at end of file diff --git a/app/helpers/contacts_helper.rb b/app/helpers/contacts_helper.rb index 968af0f..ba79381 100644 --- a/app/helpers/contacts_helper.rb +++ b/app/helpers/contacts_helper.rb @@ -74,6 +74,11 @@ module ContactsHelper return {:controller => 'deals', :action => 'show', :id => deal.id } end + def contract_url(contract) + return {:controller => 'contracts', :action => 'show', :id => contract.id } + end + + def note_source_url(note_source) polymorphic_url(note_source) # return {:controller => note_source.class.name.pluralize.downcase, :action => 'show', :project_id => @project, :id => note_source.id } diff --git a/app/helpers/contract_contacts_helper.rb b/app/helpers/contract_contacts_helper.rb new file mode 100644 index 0000000..13686d4 --- /dev/null +++ b/app/helpers/contract_contacts_helper.rb @@ -0,0 +1,2 @@ +module ContractContactsHelper +end diff --git a/app/helpers/contract_statuses_helper.rb b/app/helpers/contract_statuses_helper.rb new file mode 100644 index 0000000..b4c38f5 --- /dev/null +++ b/app/helpers/contract_statuses_helper.rb @@ -0,0 +1,2 @@ +module ContractStatusesHelper +end diff --git a/app/helpers/contracts_helper.rb b/app/helpers/contracts_helper.rb new file mode 100644 index 0000000..83a5dfb --- /dev/null +++ b/app/helpers/contracts_helper.rb @@ -0,0 +1,104 @@ +module ContractsHelper + def collection_for_status_select + contract_statuses.collect{|s| [s.name, s.id.to_s]} + end + + def collection_for_currencies_select + [:en, :de, :'en-GB', :ru].each_with_index.collect{|l, index| [I18n.translate(:'number.currency.format.unit', :locale => l), index]} + end + + def contract_status_options_for_select(select="") + options_for_select(collection_for_status_select, select) + end + + def contracts_sum_to_currency(contracts_sum) + contracts_sum.map{|c| content_tag(:span, contract_price_to_currency(c[1], c[0].to_i), :style => "white-space: nowrap;")}.join(' / ') + end + + def contract_price_to_currency(price, currency) + case currency + when 0 + locale = :en + when 1 + locale = :de + when 2 + locale = :'en-GB' + when 3 + locale = :ru + else + end + + !locale.blank? ? number_to_currency(price, :locale => locale.to_sym, :precision => 2) : number_with_delimiter(price, :delimiter => ' ', :precision => 2) + end + + def contract_price(contract) + contract_price_to_currency(contract.price, contract.currency) + end + + def contract_statuses + (!@project.blank? ? @project.contract_statuses : ContractStatus.all(:order => "position")) || [] + end + + def remove_contractor_link(contact) + link_to_remote(image_tag('delete.png'), + :url => {:controller => "contract_contacts", :action => 'delete', :project_id => @project, :contract_id => @contract, :contact_id => contact}, + :method => :delete, + :confirm => l(:text_are_you_sure), + :html => {:class => "delete", :title => l(:button_delete) }) if User.current.allowed_to?(:edit_contracts, @project) + end + + def retrieve_date_range(period) + @from, @to = nil, nil + case period + when 'today' + @from = @to = Date.today + when 'yesterday' + @from = @to = Date.today - 1 + when 'current_week' + @from = Date.today - (Date.today.cwday - 1)%7 + @to = @from + 6 + when 'last_week' + @from = Date.today - 7 - (Date.today.cwday - 1)%7 + @to = @from + 6 + when '7_days' + @from = Date.today - 7 + @to = Date.today + when 'current_month' + @from = Date.civil(Date.today.year, Date.today.month, 1) + @to = (@from >> 1) - 1 + when 'last_month' + @from = Date.civil(Date.today.year, Date.today.month, 1) << 1 + @to = (@from >> 1) - 1 + when '30_days' + @from = Date.today - 30 + @to = Date.today + when 'current_year' + @from = Date.civil(Date.today.year, 1, 1) + @to = Date.civil(Date.today.year, 12, 31) + end + + @from, @to = @from, @to + 1 if (@from && @to) + + end + + def retrieve_contracts_query + # debugger + # params.merge!(session[:contracts_query]) + # session[:contracts_query] = {:project_id => @project.id, :status_id => params[:status_id], :category_id => params[:category_id], :assigned_to_id => params[:assigned_to_id]} + if params[:status_id] || !params[:period].blank? || !params[:category_id].blank? || !params[:assigned_to_id].blank? + session[:contracts_query] = {:project_id => (@project ? @project.id : nil), + :status_id => params[:status_id], + :category_id => params[:category_id], + :period => params[:period], + :assigned_to_id => params[:assigned_to_id]} + else + if api_request? || params[:set_filter] || session[:contracts_query].nil? || session[:contracts_query][:project_id] != (@project ? @project.id : nil) + session[:contracts_query] = {} + else + params.merge!(session[:contracts_query]) + end + end + end + + +end diff --git a/app/models/contact.rb b/app/models/contact.rb index 69edccb..488f7f5 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -12,7 +12,9 @@ class Contact < ActiveRecord::Base belongs_to :assigned_to, :class_name => 'User', :foreign_key => 'assigned_to_id' has_and_belongs_to_many :issues, :order => "#{Issue.table_name}.due_date", :uniq => true has_many :deals, :order => "#{Deal.table_name}.status_id", :uniq => true + has_many :contracts, :order => "#{Contract.table_name}.status_id", :uniq => true has_and_belongs_to_many :related_deals, :class_name => 'Deal', :order => "#{Deal.table_name}.status_id", :uniq => true + has_and_belongs_to_many :related_contracts, :class_name => 'Contract', :order => "#{Contract.table_name}.status_id", :uniq => true has_and_belongs_to_many :projects, :uniq => true belongs_to :author, :class_name => 'User', :foreign_key => 'author_id' has_one :avatar, :class_name => "Attachment", :as => :container, :conditions => "#{Attachment.table_name}.description = 'avatar'", :dependent => :destroy @@ -87,10 +89,18 @@ class Contact < ActiveRecord::Base def all_deals @all_deals ||= (self.deals + self.related_deals ).uniq.sort!{|x, y| x.status_id <=> y.status_id } end + + def all_contracts + @all_contracts ||= (self.contracts + self.related_contracts ).uniq.sort!{|x, y| x.status_id <=> y.status_id } + end def all_visible_deals(usr=User.current) @all_deals ||= (self.deals.visible(usr) + self.related_deals.visible(usr)).uniq.sort!{|x, y| x.status_id <=> y.status_id } end + + def all_visible_contracts(usr=User.current) + @all_contracts ||= (self.contracts.visible(usr) + self.related_contracts.visible(usr)).uniq.sort!{|x, y| x.status_id <=> y.status_id } + end def self.available_tags(options = {}) name_like = options[:name_like] diff --git a/app/views/common/_sidebar.html.erb b/app/views/common/_sidebar.html.erb index db564f1..b5e4265 100644 --- a/app/views/common/_sidebar.html.erb +++ b/app/views/common/_sidebar.html.erb @@ -7,6 +7,10 @@ <%= link_to l(:label_deal_plural), { :controller => 'deals', :action => 'index', :project_id => @project} %> | <% end %> +<% if !(@project && !authorize_for(:contracts, :index)) %> +<%= link_to l(:label_contract_plural), { :controller => 'contracts', :action => 'index', :project_id => @project} %> + | +<% end %> <% if !(@project && !authorize_for(:contacts_tasks, :index)) %> <%= link_to l(:label_issue_plural), { :controller => 'contacts_tasks', :action => 'index', :project_id => @project} %> diff --git a/app/views/contacts/context_menu.html.erb b/app/views/contacts/context_menu.html.erb index f3e9dc1..de9b674 100644 --- a/app/views/contacts/context_menu.html.erb +++ b/app/views/contacts/context_menu.html.erb @@ -9,7 +9,9 @@ <% if !@project.nil? %>
<%= f.text_field :name, :size => 30, :required => true %>
+<%= l(:text_contract_category_destroy_question, @contract_count) %>
+
+<% if @categories.size > 0 %>
+:
+<%= select_tag 'reassign_to_id', options_from_collection_for_select(@categories, 'id', 'name') %>
+ <%= select_tag :contact_id, options_for_select(@project.contacts.sort!{|x, y| x.name <=> y.name }.collect {|m| [m.name, m.id]}), :prompt => "--- #{l(:actionview_instancetag_blank_option)} ---" %>
+
+ <%= submit_tag l(:button_add) %>
+ <%= toggle_link l(:button_cancel), 'add-contact-form'%>
+
+<%= text_field 'contract_status', 'name' %>
+ + ++<%= text_field 'contract_status', 'color_name', :class => "colorpicker" %> + +
+ + + ++<%= check_box 'contract_status', 'is_closed' %>
+ ++<%= check_box 'contract_status', 'is_default' %>
+ +<%= call_hook(:view_contract_statuses_form, :contract_status => @contract_status) %> + + +| <%=l(:field_status)%> | +<%=l(:field_is_default)%> | +<%=l(:field_is_closed)%> | +<%=l(:button_sort)%> | ++ |
|---|---|---|---|---|
| <%= link_to status.name, :action => 'edit', :id => status %> | +<%= checked_image status.is_default? %> | +<%= checked_image status.is_closed? %> | +<%= reorder_links('contract_status', {:action => 'update', :id => status}) %> | ++ <%= link_to(l(:button_delete), { :action => 'destroy', :id => status }, + :method => :post, + :confirm => l(:text_are_you_sure), + :class => 'icon icon-del') %> + | +
<%= pagination_links_full @contract_status_pages %>
+ +<% html_title(l(:label_contract_status_plural)) -%> diff --git a/app/views/contract_statuses/new.html.erb b/app/views/contract_statuses/new.html.erb new file mode 100644 index 0000000..3228fbf --- /dev/null +++ b/app/views/contract_statuses/new.html.erb @@ -0,0 +1,6 @@ +| <%= custom_value.custom_field.name %>: | <%=h show_value(custom_value) %> |
|---|
| + > + <%= h "#{contract_status.name}(#{@project ? @project.contracts.count(:conditions => {:status_id => contract_status.id}) : Contract.count(:conditions => {:status_id => contract_status.id})})" %> + + | ++ + <%= @project ? contracts_sum_to_currency(@project.contracts.sum(:price, :conditions => {:status_id => contract_status.id}, :group => :currency)) : contracts_sum_to_currency(Contract.sum(:price, :conditions => {:status_id => contract_status.id}, :group => :currency)) %> + + | +
<%= form.check_box :is_for_all %>
+<%= form.check_box :is_filter %>
+<%= form.check_box :visible, :label => l(:label_contacts_show_in_list) %>
\ No newline at end of file diff --git a/app/views/contracts/_form.html.erb b/app/views/contracts/_form.html.erb new file mode 100644 index 0000000..0ba7814 --- /dev/null +++ b/app/views/contracts/_form.html.erb @@ -0,0 +1,38 @@ +<%= error_messages_for 'contract' %> +<%= f.text_field :name, :label=>l(:field_contract_name), :size => 80, :required => true %>
+ +<%= f.text_area :background , :cols => 80, :rows => 8, :class => 'wiki-edit', :label=>l(:field_contract_background) %>
<%= wikitoolbar_for 'contract_background' %> + + <% if @project.contract_statuses.any? %> +<%= f.select :status_id, collection_for_status_select, :include_blank => false, :selected => @contract.status_id.to_s, :label=>l(:field_contact_status) %>
+ <% end %> + <% unless @project.contract_categories.empty? %> +<%= f.select :category_id, (@project.contract_categories.collect {|c| [c.name, c.id]}), :include_blank => true %> + <%= prompt_to_remote(image_tag('add.png', :style => 'vertical-align: middle;'), + l(:label_contract_category_new), + 'category[name]', + {:controller => 'contract_categories', :action => 'new', :project_id => @project}, + :title => l(:label_contract_category_new), + :tabindex => 199) if authorize_for('contract_categories', 'new') %>
+ <% end %> ++ <%= f.select :contact_id, (@project.contacts.visible.collect {|p| [ p.name, p.id ] }), :include_blank => true, :label=>l(:field_contract_contact) %> + <%= link_to image_tag('add.png', :style => 'vertical-align: middle;'), + {:controller => 'contacts', :action => 'new', :project_id => @project}, + :confirm => l(:text_are_you_sure), :title => l(:label_contact_new) if authorize_for('contacts', 'new') %> +
++ <%= f.text_field :price, :label => l(:field_contract_price), :size => 10 %> + <%= select_tag "contract[currency]", options_for_select(collection_for_currencies_select.insert(0, ['', '']), @contract.currency), :include_blank => true %> + +
+ + <% @contract.custom_field_values.each do |value| %> ++ <%= custom_field_tag_with_label :contract, value %> +
+ <% end -%> + +<%= f.select :assigned_to_id, (@project.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true, :label => l(:label_assigned_to) %>
+| + <%= check_box_tag "ids[]", contract.id, false, :onclick => "toggleContact(event, this);" %> + | ++ <%= link_to avatar_to(contract, :size => "32"), {:controller => 'contracts', :action => 'show', :id => contract.id}, :id => "avatar" %> + | +
+ <%= link_to contract.name, :controller => 'contracts', :action => 'show', :id => contract.id %>++ <%= link_to_source(contract.contact) if contract.contact %> + ++ |
+
+
+ <%= contract_price(contract) %>
+ <% if contract.status && contract.project.contract_statuses.any? %>
+
+ <% end %>
+
+
+ <%= h contract.category %>
+
+ |
+
| + | + | <%= "#{l(:label_total)} (#{@contracts_count}):" %> | +
+ <%= contracts_sum_to_currency(@contracts_sum).gsub(' / ', ' ') %> + |
+
|---|
<%=l(:label_no_data)%>
+ <% end %> +<% end %> + +<%= context_menu url_for( {:controller => "contracts", :action => "context_menu"} )%> diff --git a/app/views/contracts/_related_contracts.html.erb b/app/views/contracts/_related_contracts.html.erb new file mode 100644 index 0000000..1f4df75 --- /dev/null +++ b/app/views/contracts/_related_contracts.html.erb @@ -0,0 +1,28 @@ +
+ + <%= link_to contract.name, {:controller => 'contracts', :action => 'show', :id => contract.id } %> ++ + <% if contract.status %> + > + <%= h contract.status %> + + <% end %> + |
+
<%= submit_tag l(:button_submit) %>
+<% end %> + +<% content_for :header_tags do %> + <%= javascript_include_tag :contacts, :plugin => 'redmine_contacts' %> + <%= stylesheet_link_tag :contacts, :plugin => 'redmine_contacts' %> +<% end %> diff --git a/app/views/contracts/context_menu.html.erb b/app/views/contracts/context_menu.html.erb new file mode 100644 index 0000000..9bd8346 --- /dev/null +++ b/app/views/contracts/context_menu.html.erb @@ -0,0 +1,46 @@ +| <%= avatar_to(@contract, :size => "64") %> | +
+ <%= h @contract.contact.name + ": " if @contract.contact %> <%= @contract.name %>+<%= h @contract.category %> + <% if @contract.status && @project.contract_statuses.any? %> +
+ >
+ <%= h @contract.status %>
+
+ <% if authorize_for('contracts', 'edit') %>
+
+ <%= link_to l(:label_contract_change_status), {}, :onclick => "Element.show('edit_status_form'); Element.hide('contract-status'); return false;", :id => 'edit_status_link' %>
+
+ <% end %>
+
+ <% form_tag( {:controller => 'contracts',
+ :action => 'update',
+ :project_id => @project,
+ :id => @contract },
+ :multipart => true,
+ :id => "edit_status_form",
+ :style => "display:none; size: 100%" ) do %>
+ <%= select :contract, :status_id, options_for_select(collection_for_status_select, @contract.status_id.to_s), { :include_blank => false } %>
+ <%= submit_tag l(:button_save) %>
+ <%= link_to l(:button_cancel), {}, :onclick => "Element.hide('edit_status_form'); Element.show('contract-status'); return false;" %>
+ + + <% end %> + <% end %> + |
+ <% if !@contract.price.blank? %>
+
+
|
+ <% end %>
+
+
<%= link_to l(:label_contract_view_all), + :controller => 'contracts', + :action => 'index', + :assigned_to_id => User.current.id %>
+<% end %> + +<% content_for(:header_tags) do %> + <%= javascript_include_tag :contacts, :plugin => 'redmine_contacts' %> + <%= stylesheet_link_tag :contacts, :plugin => 'redmine_contacts' %> +<% end %> \ No newline at end of file diff --git a/app/views/projects/_contacts_settings.html.erb b/app/views/projects/_contacts_settings.html.erb index 14ef552..2cea693 100644 --- a/app/views/projects/_contacts_settings.html.erb +++ b/app/views/projects/_contacts_settings.html.erb @@ -2,11 +2,17 @@- + <%= hidden_field_tag('contacts_settings[contacts_show_deals_tab]', 0) %> <%= check_box_tag 'contacts_settings[contacts_show_deals_tab]', 1, ContactsSetting[:contacts_show_deals_tab, @project.id].to_i > 0 %>
++ + <%= hidden_field_tag('contacts_settings[contacts_show_contracts_tab]', 0) %> + <%= check_box_tag 'contacts_settings[contacts_show_contracts_tab]', 1, ContactsSetting[:contacts_show_contracts_tab, @project.id].to_i > 0 %> +
+<%= hidden_field_tag('contacts_settings[contacts_show_on_projects_show]', 0) %> diff --git a/app/views/projects/_contract_statuses.html.erb b/app/views/projects/_contract_statuses.html.erb new file mode 100644 index 0000000..ce16282 --- /dev/null +++ b/app/views/projects/_contract_statuses.html.erb @@ -0,0 +1,28 @@ +
| <%= l(:field_name) %> | +<%=l(:field_is_default)%> | +<%=l(:field_contract_status_is_closed)%> | +<%= l(:field_active) %> | +
|---|---|---|---|
| <%= h(status.name) %> | +<%= checked_image status.is_default? %> | +<%= checked_image status.is_closed? %> | ++ <%= check_box_tag "contract_statuses[]", status.id , @project.contract_statuses.include?(status) %> + | +
| <%= l(:field_name) %> | ++ |
|---|---|
| <%= link_to_if_authorized h(category.name), { :controller => 'contract_categories', :action => 'edit', :id => category } %> | ++ <%= link_to_if_authorized l(:button_delete), {:controller => 'contract_categories', :action => 'destroy', :id => category}, :method => :post, :confirm => l(:text_are_you_sure), :class => 'icon icon-del' %> + | +
<%= l(:label_no_data) %>
+<% end %> + +<%= link_to_if_authorized l(:label_contract_category_new), :controller => 'contract_categories', :action => 'new', :project_id => @project %>
+ +<%= render :partial => "projects/contract_statuses" %> \ No newline at end of file diff --git a/app/views/projects/settings.rhtml b/app/views/projects/settings.rhtml index 0f055b6..8e47513 100644 --- a/app/views/projects/settings.rhtml +++ b/app/views/projects/settings.rhtml @@ -12,6 +12,10 @@ if @project.module_enabled?(:contacts_module) :action => :manage_contacts, :partial => 'projects/deals_settings', :label => :label_deal_plural }) + tabs.push({ :name => 'contracts', + :action => :manage_contacts, + :partial => 'projects/contracts_settings', + :label => :label_contract_plural }) end diff --git a/app/views/settings/_contacts_contract_statuses.html.erb b/app/views/settings/_contacts_contract_statuses.html.erb new file mode 100644 index 0000000..0e49c1b --- /dev/null +++ b/app/views/settings/_contacts_contract_statuses.html.erb @@ -0,0 +1,34 @@ +| <%=l(:field_status)%> | +<%=l(:field_is_default)%> | +<%=l(:field_contract_status_is_closed)%> | +<%=l(:button_sort)%> | ++ |
|---|---|---|---|---|
| <%= link_to status.name, :controller => "contract_statuses", :action => 'edit', :id => status %> | +<%= checked_image status.is_default? %> | +<%= checked_image status.is_closed? %> | +<%= reorder_links('contract_status', {:controller => "contract_statuses", :action => 'update', :id => status}) %> | ++ <%= link_to(l(:button_delete), {:controller => "contract_statuses", :action => 'destroy', :id => status }, + :method => :post, + :confirm => l(:text_are_you_sure), + :class => 'icon icon-del') %> + | +
<%= l(:label_note_plural) %>
+