Modificado todo

git-svn-id: https://192.168.0.254/svn/Rodax.redmine_rodax_crm/trunk@4 ff88604e-da85-c949-a72f-fc3aa3ba3724
This commit is contained in:
David Arranz 2012-01-03 21:11:44 +00:00
parent 2b75d1e4cf
commit 6c8dd60912
58 changed files with 1713 additions and 18 deletions

View File

@ -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', '<option></option>' + 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 }

View File

@ -0,0 +1,2 @@
module ContractContactsHelper
end

View File

@ -0,0 +1,2 @@
module ContractStatusesHelper
end

View File

@ -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

View File

@ -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]

View File

@ -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} %>

View File

@ -9,7 +9,9 @@
<% if !@project.nil? %>
<li><%= context_menu_link l(:label_deal_new), {:controller => 'deals', :action => 'new', :project_id => @project, :contact_id => @contact},
:class => 'icon-add-deal', :disabled => !@can[:create_deal] %></li>
<% if @contact.is_company? %>
<li><%= context_menu_link l(:label_contract_new), {:controller => 'contracts', :action => 'new', :project_id => @project, :contact_id => @contact},
:class => 'icon-add-contract', :disabled => !@can[:create_contract] %></li>
<% if @contact.is_company? %>
<li><%= context_menu_link l(:label_add_employee), {:controller => 'contacts', :action => 'new', :project_id => @project, :contact => {:company => @contact.name}},
:class => 'icon-add-employee', :disabled => !@can[:edit] %></li>
<% end %>

View File

@ -42,7 +42,7 @@
<%= select_tag :deal_status_id, options_from_collection_for_select(@project.assignable_users, :id, :name), :onchange => "this.form.submit();" %>
</div>
</fieldset>
<% end %>
</div>

View File

@ -79,5 +79,21 @@ api.contact do
end
end if (@contact.related_deals + @contact.deals).present? && User.current.allowed_to?(:view_deals, @project)
api.array :contracts do
(@contact.related_contracts + @contact.contracts).each do |contract|
api.contract do
api.id contract.id
api.price contract.price
api.currency contract.currency
api.price_type contract.price_type
api.name contract.name
api.project(:id => contract.project.id, :name => contract.project.name)
api.status(:id => contract.status.id, :name => contract.status.name)
api.background contract.background
api.created_on contract.created_on
api.updated_on contract.updated_on
end
end
end if (@contact.related_contracts + @contact.contracts).present? && User.current.allowed_to?(:view_contracts, @project)
end

View File

@ -64,7 +64,8 @@
<!-- <% contacts_tabs = [{:name => 'general', :partial => 'common/recently_viewed', :label => :label_note_plural},
{:name => 'tags', :partial => 'employees', :label => :label_tags_plural},
{:name => 'deal_statuses', :partial => 'attributes', :label => :label_deal_status_plural}
{:name => 'deal_statuses', :partial => 'attributes', :label => :label_deal_status_plural},
{:name => 'contract_statuses', :partial => 'attributes', :label => :label_contract_status_plural}
] %>
<%= render_tabs contacts_tabs %> -->
@ -93,6 +94,7 @@
<%= render :partial => 'common/notes_attachments', :object => @contact.notes_attachments %>
<%= call_hook(:view_contacts_sidebar_after_notes_attachments, :contact => @contact) %>
<%= render :partial => 'deals/related_deals', :object => @contact.all_visible_deals %>
<%= render :partial => 'contracts/related_contracts', :object => @contact.all_visible_contracts %>
<%= render :partial => 'employees' %>
<% if !@contact.background.blank? %>
<h3><%= l(:label_contact_background_info) %></h3>

View File

@ -0,0 +1,5 @@
<%= error_messages_for 'category' %>
<div class="box tabular">
<p><%= f.text_field :name, :size => 30, :required => true %></p>
</div>

View File

@ -0,0 +1,15 @@
<h2><%=l(:label_contract_category)%>: <%=h @category.name %></h2>
<% form_tag({}) do %>
<div class="box">
<p><strong><%= l(:text_contract_category_destroy_question, @contract_count) %></strong></p>
<p><label><%= radio_button_tag 'todo', 'nullify', true %> <%= l(:text_contract_category_destroy_assignments) %></label><br />
<% if @categories.size > 0 %>
<label><%= radio_button_tag 'todo', 'reassign', false %> <%= l(:text_contract_category_reassign_to) %></label>:
<%= select_tag 'reassign_to_id', options_from_collection_for_select(@categories, 'id', 'name') %></p>
<% end %>
</div>
<%= submit_tag l(:button_apply) %>
<%= link_to l(:button_cancel), :controller => 'projects', :action => 'settings', :id => @project, :tab => 'contacts' %>
<% end %>

View File

@ -0,0 +1,6 @@
<h2><%=l(:label_deal_category)%></h2>
<% labelled_form_for :category, @category, :url => { :action => 'edit', :id => @category } do |f| %>
<%= render :partial => 'contract_categories/form', :locals => { :f => f } %>
<%= submit_tag l(:button_save) %>
<% end %>

View File

@ -0,0 +1 @@
<h2>ContractCategories#index</h2>

View File

@ -0,0 +1,6 @@
<h2><%=l(:label_issue_category_new)%></h2>
<% labelled_form_for :category, @category, :url => { :action => 'new', :project_id => @project } do |f| %>
<%= render :partial => 'contract_categories/form', :locals => { :f => f } %>
<%= submit_tag l(:button_create) %>
<% end %>

View File

@ -0,0 +1,36 @@
<% if @contract.all_contacts.any? %>
<div id="contract_contacts">
<div class="contextual">
<%= link_to_remote l(:button_add),
:url => {:controller => 'contract_contacts',
:action => 'add',
:project_id => @project,
:contract_id => @contract} if User.current.allowed_to?({:controller => 'contract_contacts', :action => 'add'}, @project) %>
</div>
<h3><%= l(:label_contractor_plural) %></h3>
<% unless !(@show_form == "true") %>
<% form_remote_tag(
:url => {:controller => 'contract_contacts',
:action => 'add',
:contract_id => @contract,
:project_id => @project},
:method => :post,
:html => {:id => 'add-contact-form'}) do |f| %>
<p>
<%= 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)} ---" %>
<br>
<%= submit_tag l(:button_add) %>
<%= toggle_link l(:button_cancel), 'add-contact-form'%>
</p>
<% end %>
<% end %>
<%= render :partial => 'common/contact_data', :object => @contract.contact %>
<% @contract.related_contacts.each do |contact| %>
<%= render :partial => 'common/contact_data', :object => contact, :locals => {:actions => remove_contractor_link(contact)} %>
<% end %>
</div>
<% end %>

View File

@ -0,0 +1,35 @@
<%= error_messages_for 'contract_status' %>
<div class="box tabular">
<!--[form:contract_status]-->
<p><label for="contract_status_name"><%=l(:field_name)%><span class="required"> *</span></label>
<%= text_field 'contract_status', 'name' %></p>
<p><label for="tag_color"><%=l(:field_color)%></label>
<%= text_field 'contract_status', 'color_name', :class => "colorpicker" %>
<!--[eoform:optvalue]-->
</p>
<script>
// var picker = new ColourPicker("picker", "trigger");
$$('.colorpicker').each(function(el){var p = new ColorPicker(el)});
</script>
<p><label for="contract_status_is_closed"><%=l(:field_contract_status_is_closed)%></label>
<%= check_box 'contract_status', 'is_closed' %></p>
<p><label for="contract_status_is_default"><%=l(:field_is_default)%></label>
<%= check_box 'contract_status', 'is_default' %></p>
<%= call_hook(:view_contract_statuses_form, :contract_status => @contract_status) %>
<!--[eoform:contract_status]-->
</div>
<% content_for :header_tags do %>
<%= javascript_include_tag :contacts, :plugin => 'redmine_contacts' %>
<%= stylesheet_link_tag :contacts, :plugin => 'redmine_contacts' %>
<% end %>

View File

@ -0,0 +1,6 @@
<h2><%= link_to l(:label_contract_status_plural), :controller => 'contract_statuses', :action => 'index' %> &#187; <%=h @contract_status %></h2>
<% form_tag({:action => 'update', :id => @contract_status}, :class => "tabular") do %>
<%= render :partial => 'form' %>
<%= submit_tag l(:button_save) %>
<% end %>

View File

@ -0,0 +1,35 @@
<div class="contextual">
<%= link_to l(:label_contract_status_new), {:action => 'new'}, :class => 'icon icon-add' %>
</div>
<h2><%=l(:label_contract_status_plural)%></h2>
<table class="list">
<thead><tr>
<th><%=l(:field_status)%></th>
<th><%=l(:field_is_default)%></th>
<th><%=l(:field_is_closed)%></th>
<th><%=l(:button_sort)%></th>
<th></th>
</tr></thead>
<tbody>
<% for status in @contract_statuses %>
<tr class="<%= cycle("odd", "even") %>">
<td><%= link_to status.name, :action => 'edit', :id => status %></td>
<td align="center"><%= checked_image status.is_default? %></td>
<td align="center"><%= checked_image status.is_closed? %></td>
<td align="center" style="width:15%;"><%= reorder_links('contract_status', {:action => 'update', :id => status}) %></td>
<td class="buttons">
<%= link_to(l(:button_delete), { :action => 'destroy', :id => status },
:method => :post,
:confirm => l(:text_are_you_sure),
:class => 'icon icon-del') %>
</td>
</tr>
<% end %>
</tbody>
</table>
<p class="pagination"><%= pagination_links_full @contract_status_pages %></p>
<% html_title(l(:label_contract_status_plural)) -%>

View File

@ -0,0 +1,6 @@
<h2><%= link_to l(:label_contract_status_plural), :controller => 'contract_statuses', :action => 'index' %> &#187; <%=l(:label_contract_status_new)%></h2>
<% form_tag({:action => 'create'}, :class => "tabular") do %>
<%= render :partial => 'form' %>
<%= submit_tag l(:button_create) %>
<% end %>

View File

@ -0,0 +1,14 @@
<% if @contract.custom_values.any? %>
<div id="attributes">
<h3><%= l(:label_contract) %></h3>
<table class="attributes">
<% @contract.custom_values.each do |custom_value| %>
<% if !custom_value.value.blank? %>
<tr> <th class = "custom_field"><%= custom_value.custom_field.name %>:</th><td> <%=h show_value(custom_value) %></td> </tr>
<% end %>
<% end %>
</table>
</div>
<% end %>

View File

@ -0,0 +1,26 @@
<% if contract_statuses.any? %>
<div id="contracts_statistics">
<% if !(@project && !authorize_for(:sale_funel, :index)) %>
<div class="contextual">
<%= link_to l(:label_sale_funel), {:controller => 'sale_funel', :action => 'index', :project_id => @project} %>
</div>
<% end %>
<h3><%= l(:label_statistics) %></h3>
<table class="contracts_statistics">
<% contract_statuses.each do |contract_status| %>
<tr>
<td>
<span class="contract-status" style=<%= "background-color:#{contract_status.color_name};color:white;" %> >
<%= h "#{contract_status.name}(#{@project ? @project.contracts.count(:conditions => {:status_id => contract_status.id}) : Contract.count(:conditions => {:status_id => contract_status.id})})" %>
</span>
</td>
<td>
<strong>
<%= @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)) %>
</strong>
</td>
</tr>
<% end %>
</table>
</div>
<% end %>

View File

@ -0,0 +1,5 @@
<span class="filter-condition">
available_custom_fields
<%= label_tag l(:label_assigned_to) + " " %>
<%= select_tag :assigned_to_id, options_for_select(Contract.available_users(@project).collect{|u| [u.name, u.id.to_s]}.insert(0, [""]), params[:assigned_to_id]) %>
</span>

View File

@ -0,0 +1,3 @@
<p><%= form.check_box :is_for_all %></p>
<p><%= form.check_box :is_filter %></p>
<p><%= form.check_box :visible, :label => l(:label_contacts_show_in_list) %></p>

View File

@ -0,0 +1,38 @@
<%= error_messages_for 'contract' %>
<div class = "box tabular">
<p><%= f.text_field :name, :label=>l(:field_contract_name), :size => 80, :required => true %></p>
<p class = "notes"><%= f.text_area :background , :cols => 80, :rows => 8, :class => 'wiki-edit', :label=>l(:field_contract_background) %></p> <%= wikitoolbar_for 'contract_background' %>
<% if @project.contract_statuses.any? %>
<p><%= f.select :status_id, collection_for_status_select, :include_blank => false, :selected => @contract.status_id.to_s, :label=>l(:field_contact_status) %></p>
<% end %>
<% unless @project.contract_categories.empty? %>
<p><%= 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') %></p>
<% end %>
<p>
<%= 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') %>
</p>
<p>
<%= 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 %>
</p>
<% @contract.custom_field_values.each do |value| %>
<p>
<%= custom_field_tag_with_label :contract, value %>
</p>
<% end -%>
<p><%= f.select :assigned_to_id, (@project.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true, :label => l(:label_assigned_to) %></p>
</div>

View File

@ -0,0 +1,54 @@
<% form_tag({}) do -%>
<%= hidden_field_tag 'back_url', url_for(params) %>
<%= hidden_field_tag 'project_id', @project.id if @project %>
<% unless @contracts.empty? %>
<table class="contacts index">
<tbody>
<% @contracts.each do |contract| %>
<tr class="hascontextmenu">
<td class="checkbox">
<%= check_box_tag "ids[]", contract.id, false, :onclick => "toggleContact(event, this);" %>
</td>
<td class="avatar">
<%= link_to avatar_to(contract, :size => "32"), {:controller => 'contracts', :action => 'show', :id => contract.id}, :id => "avatar" %>
</td>
<td class="name">
<h1 class="contract_name"><%= link_to contract.name, :controller => 'contracts', :action => 'show', :id => contract.id %></h1>
<h2>
<%= link_to_source(contract.contact) if contract.contact %>
</h2>
</td>
<td class="info">
<div class="contract-sum"><strong><%= contract_price(contract) %></strong>
<% if contract.status && contract.project.contract_statuses.any? %>
<span class="contract-status tags" style = <%= "background-color:#{contract.status.color_name};color:white;" %> >
<%= h contract.status %>
</span>
<% end %>
</div>
<div class="description" >
<%= h contract.category %>
</div>
</td>
</tr>
<% end %>
<tr class="total">
<th/>
<th/>
<th class="title"> <%= "#{l(:label_total)} (#{@contracts_count}):" %> </th>
<th class="sum">
<%= contracts_sum_to_currency(@contracts_sum).gsub(' / ', '<br/>') %>
</th>
</tr>
</tbody>
</table>
<%= contacts_paginator @contracts_pages, :params => {:project_id => params[:project_id]} if @contracts_pages %>
<% else %>
<p class="nodata"><%=l(:label_no_data)%></p>
<% end %>
<% end %>
<%= context_menu url_for( {:controller => "contracts", :action => "context_menu"} )%>

View File

@ -0,0 +1,28 @@
<div id="contracts">
<div class="contextual">
<%= link_to_if_authorized l(:label_contract_new), {:controller => 'contracts', :action => 'new', :project_id => @project, :contact_id => @contact} %>
</div>
<h3><%= "#{l(:label_contract_plural)}" %> <%= " - #{h number_to_currency(related_contracts.sum{|d| d.price || 0})}" if false %></h3>
<% if related_contracts.any? %>
<table class="related_contracts">
<% related_contracts.each do |contract| %>
<tr>
<td class="name" style="vertical-align: top;">
<h4>
<%= link_to contract.name, {:controller => 'contracts', :action => 'show', :id => contract.id } %>
</h4>
<!-- %= contract_price(contract) % -->
<% if contract.status %>
<span class="contract-status" style = <%= "background-color:#{contract.status.color_name};color:white;" %> >
<%= h contract.status %>
</span>
<% end %>
</td>
</tr>
<% end %>
</table>
<% end %>
</div>

View File

@ -0,0 +1,80 @@
<h2><%= l(:label_bulk_edit_selected_contracts) %></h2>
<div class="box" id="duplicates">
<ul>
<% @contracts.each do |contract| %>
<li>
<%= avatar_to contract, :size => "16" %>
<%= link_to contract.full_name, polymorphic_url(contract) %>
<%= "(#{contract_price(contract)}) " unless contract.price.blank? %>
<% if contract.status %>
<span class="contract-status" style = <%= "background-color:#{contract.status.color_name};color:white;" %> >
<%= h contract.status %>
</span>
<% end %>
</li>
<% end %>
</ul>
</div>
<% form_tag(:action => 'bulk_update') do %>
<%= @contracts.collect {|i| hidden_field_tag('ids[]', i.id)}.join %>
<div class="box tabular">
<fieldset class="attributes">
<legend><%= l(:label_change_properties) %></legend>
<% if @available_statuses.any? %>
<p>
<label><%= l(:field_status) %></label>
<%= select_tag('contract[status_id]', content_tag('option', l(:label_no_change_option), :value => '') +
options_from_collection_for_select(@available_statuses, :id, :name)) %>
</p>
<% end %>
<% if @available_categories.any? %>
<p>
<label><%= l(:field_category) %></label>
<%= select_tag('contract[category_id]', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_none), :value => 'none') +
options_from_collection_for_select(@available_categories, :id, :name)) %>
</p>
<% end %>
<p>
<label><%= l(:label_assigned_to) %></label>
<%= select_tag('contract[assigned_to_id]', content_tag('option', l(:label_no_change_option), :value => '') +
content_tag('option', l(:label_nobody), :value => 'none') +
options_from_collection_for_select(@assignables, :id, :name)) %>
</p>
<p>
<label><%= l(:field_contract_currency) %></label>
<%= select_tag "contract[currency]", options_for_select(collection_for_currencies_select.insert(0, ['', '']), '') %>
</p>
<% @contracts.first.custom_field_values.each do |value| %>
<p>
<% value.value = '' %>
<%= custom_field_tag_with_label :contact, value %>
</p>
<% end -%>
</fieldset>
<fieldset><legend><%= l(:field_notes) %></legend>
<%= text_area_tag 'note[content]', '', :cols => 60, :rows => 10, :class => 'wiki-edit' %>
<%= wikitoolbar_for 'note_content' %>
</fieldset>
</div>
<p><%= submit_tag l(:button_submit) %></p>
<% end %>
<% content_for :header_tags do %>
<%= javascript_include_tag :contacts, :plugin => 'redmine_contacts' %>
<%= stylesheet_link_tag :contacts, :plugin => 'redmine_contacts' %>
<% end %>

View File

@ -0,0 +1,46 @@
<ul>
<% if !@contract.nil? %>
<li><%= context_menu_link l(:button_edit), {:controller => 'contracts', :action => 'edit', :id => @contract},
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
<% if User.current.logged? %>
<li><%= watcher_link(@contract, User.current) %></li>
<% end %>
<% else %>
<li><%= context_menu_link l(:button_edit), {:controller => 'contracts', :action => 'bulk_edit', :ids => @contracts.collect(&:id)},
:class => 'icon-edit', :disabled => !@can[:edit] %></li>
<% end %>
<% unless @project.nil? || @project.contract_categories.empty? -%>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_category) %></a>
<ul>
<% @project.contract_categories.each do |u| -%>
<li><%= context_menu_link u.name, {:controller => 'contracts', :action => 'bulk_update', :ids => @contracts.collect(&:id), :contract => {'category_id' => u}, :back_url => @back}, :method => :post,
:selected => (@contract && u == @contract.category), :disabled => !@can[:edit] %></li>
<% end -%>
<li><%= context_menu_link l(:label_none), {:controller => 'contracts', :action => 'bulk_update', :ids => @contracts.collect(&:id), :contract => {'category_id' => 'none'}, :back_url => @back}, :method => :post,
:selected => (@contract && @contract.category.nil?), :disabled => !@can[:edit] %></li>
</ul>
</li>
<% end -%>
<% unless @project.nil? || @project.contract_statuses.empty? -%>
<li class="folder">
<a href="#" class="submenu"><%= l(:field_contact_status) %></a>
<ul>
<% @project.contract_statuses.each do |s| -%>
<li><%= context_menu_link s.name, {:controller => 'contracts', :action => 'bulk_update', :ids => @contracts.collect(&:id), :contract => {'status_id' => s}, :back_url => @back}, :method => :post,
:selected => (@contract && s == @contract.status), :disabled => !@can[:edit] %></li>
<% end -%>
</ul>
</li>
<% end -%>
<li><%= context_menu_link l(:button_delete), {:controller => 'contracts', :action => 'bulk_destroy', :ids => @contracts.collect(&:id), :project_id => @project},
:method => :post, :confirm => l(:text_are_you_sure), :class => 'icon-del', :disabled => !@can[:delete] %></li>
</ul>

View File

@ -0,0 +1,6 @@
<h2><%= l(:label_contract_edit_information) %></h2>
<% labelled_form_for :contract, @contract, :url => {:action => 'update', :id => @contract} do |f| %>
<%= render :partial => 'form', :locals => {:f => f} %>
<%= submit_tag l(:button_save) -%>
<% end -%>

View File

@ -0,0 +1,100 @@
<% content_for(:header_tags) do %>
<%= javascript_include_tag :contacts, :plugin => 'redmine_contacts' %>
<%= stylesheet_link_tag :contacts, :plugin => 'redmine_contacts' %>
<%= stylesheet_link_tag :contacts_sidebar, :plugin => 'redmine_contacts' %>
<meta name = "format-detection" content = "telephone=no">
<% end %>
<% content_for :sidebar do %>
<%= render :partial => 'common/sidebar' %>
<%= render :partial => 'contracts_statistics' %>
<%= render :partial => 'notes/last_notes', :object => @last_notes %>
<%= render :partial => 'common/recently_viewed' %>
<% end %>
<div class="contextual">
<%= link_to_if_authorized l(:label_contract_new), {:controller => 'contracts', :action => 'new', :project_id => @project}, :class => 'icon icon-add' %>
</div>
<div class="filters">
<% form_tag(params, :id => "query_form") do %>
<%= hidden_field_tag('project_id', @project.to_param) if @project %>
<% no_filters = ((params[:status_id].blank? || params[:status_id] == 'o') && params[:period].blank? && params[:assigned_to_id].blank? && params[:category_id].blank?) %>
<h2 class="contacts_header">
<span id='scope_header' class="scope_title">
<%= l(:label_contract_plural) %>
</span>
<span class="live_search">
<%= label_tag :search, l(:label_search), :id => "search_overlabel" %>
<%= text_field_tag(:search, params[:search], :autocomplete => "off", :size => "35", :class => "live_search_field", :onfocus => "Element.hide('search_overlabel'); return false;", :onblur => "if (this.value == '') {Element.show('search_overlabel');}" ) %>
<!-- , :onfocus => "if (this.value == 'Поиск') {this.value = '';}", :onblur => "if (this.value == '') {this.value ='Поиск'; this.style.color='#aaa'}" -->
<%= observe_field("search",
:frequency => 2,
:update => 'contact_list',
:url => {:controller => 'contracts', :action => 'index', :project_id => @project },
:with => "Form.serialize('query_form')") %>
</span>
</h2>
<fieldset id="filters" class="collapsible <%= 'collapsed' if no_filters %>">
<legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
<div style="<%= 'display: none;' if no_filters %>">
<p>
<% if !contract_statuses.empty? %>
<span class="filter-condition">
<%= label_tag l(:label_contract_status) + " " %>
<%= select_tag :status_id, options_for_select(collection_for_status_select.insert(0, [l(:label_open_issues), "o"]).insert(0, [l(:label_all), ""]), params[:status_id]) %>
</span>
<% end %>
<span class="filter-condition">
<%= label_tag l(:label_created_on) + " "%>
<%= select_tag 'period', options_for_period_select(params[:period]) %>
</span>
<% if @project && !@project.contract_categories.empty? %>
<span class="filter-condition">
<%= label_tag l(:label_contract_category) + " "%>
<%= select_tag 'category_id', options_for_select(@project.contract_categories.collect {|c| [c.name, c.id.to_s]}.insert(0, [""]), params[:category_id]) %>
</span>
<% end %>
<span class="filter-condition">
<%= label_tag l(:label_assigned_to) + " " %>
<%= select_tag :assigned_to_id, options_for_select(Contract.available_users(@project).collect{|u| [u.name, u.id.to_s]}.insert(0, [""]), params[:assigned_to_id]) %>
</span>
<!-- <%= render :partial => 'custom_field_filter' %> -->
</p>
</div>
</fieldset>
<p class="buttons hide-when-print">
<%= link_to_remote l(:button_apply),
{ :url => {},
:update => "contact_list",
:with => "Form.serialize('query_form')"
}, :class => 'icon icon-checked' %>
<%= link_to l(:button_clear),
{:project_id => @project, :set_filter => 1 },
:method => :get,
:update => "contact_list",
:class => 'icon icon-reload' %>
</p>
<% end %>
</div>
<div id="contact_list">
<%= render :partial => 'list' %>
</div>
<% html_title l(:label_contract_plural) %>

View File

@ -0,0 +1,6 @@
<h2><%= l(:label_contract_new) %></h2>
<% labelled_form_for :contract, @contract, :url => {:action => 'create', :project_id => @project} do |f| %>
<%= render :partial => 'form', :locals => {:f => f} %>
<%= submit_tag l(:button_save) -%>
<% end -%>

View File

@ -0,0 +1,94 @@
<div class="contextual">
<% replace_watcher ||= 'watcher' %>
<%= watcher_tag(@contract, User.current, {:id => replace_watcher, :replace => ['watcher','watcher2']}) %>
<%= link_to_if_authorized l(:button_edit), {:controller => 'contracts', :action => 'edit', :id => @contract}, :class => 'icon icon-edit' unless @contract.nil? %>
<%= link_to_if_authorized l(:button_delete), {:controller => 'contracts', :action => 'destroy', :id => @contract}, :confirm => l(:text_are_you_sure), :method => :post, :class => 'icon icon-del' unless @contract.nil? %>
</div>
<h2><%= "#{l(:label_contract)} ##{@contract.id}" %></h2>
<div class="contract details">
<table class="subject_header">
<tr>
<td class="avatar"><%= avatar_to(@contract, :size => "64") %></td>
<td class="name" style="vertical-align: top;">
<h1><%= h @contract.contact.name + ": " if @contract.contact %> <%= @contract.name %></h1>
<p><%= h @contract.category %></p>
<% if @contract.status && @project.contract_statuses.any? %>
<div id="contract-status">
<span class="contract-status" style = <%= "background-color:#{@contract.status.color_name};color:white;" %> >
<%= h @contract.status %>
</span>
<% if authorize_for('contracts', 'edit') %>
<span class="contextual">
<%= link_to l(:label_contract_change_status), {}, :onclick => "Element.show('edit_status_form'); Element.hide('contract-status'); return false;", :id => 'edit_status_link' %>
</span>
<% end %>
</div>
<% 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;" %>
<br>
<% end %>
<% end %>
</td>
<% if !@contract.price.blank? %>
<td class="subject_info">
<ul>
<li class="price icon icon-money-dollar"><%= contract_price(@contract) %></li>
</ul>
</td>
<% end %>
</tr>
</table>
<% if authorize_for('notes', 'add_note') %>
<hr />
<%= render :partial => 'notes/add', :locals => {:note_source => @contract} %>
<% end %>
</div>
<div id="comments">
<h3><%= l(:label_note_plural) %></h3>
<div id="notes">
<%= render :partial => 'notes/note_item', :collection => @contract.notes, :locals => {:note_source => @contract} %>
</div>
</div>
<% content_for :sidebar do %>
<%= render :partial => 'common/sidebar' %>
<%= render :partial => 'attributes' %>
<%= render :partial => 'common/responsible_user', :object => @contract %>
<%= render :partial => 'contract_contacts/contacts' %>
<%= render :partial => 'common/notes_attachments', :object => @contract_attachments %>
<% if !@contract.background.blank? %>
<h3><%= l(:label_contact_background_info) %></h3>
<div class="wiki"><%= textilizable(@contract, :background) %></div>
<% end %>
<%= render :partial => 'common/recently_viewed' %>
<% end %>
<% html_title "#{l(:label_contract)} ##{@contract.id}: #{@contract.name}" %>
<% content_for :header_tags do %>
<%= javascript_include_tag :defaults %>
<%= javascript_include_tag :contacts, :plugin => 'redmine_contacts' %>
<%= stylesheet_link_tag :contacts, :plugin => 'redmine_contacts' %>
<%= stylesheet_link_tag :contacts_sidebar, :plugin => 'redmine_contacts' %>
<meta name = "format-detection" content = "telephone=no">
<% end %>

View File

@ -33,6 +33,8 @@
<%= link_to l(:label_contacts_view_all), { :controller => 'contacts', :action => 'index', :project_id => @project} %>
|
<%= link_to l(:label_deal_plural), { :controller => 'deals', :action => 'index', :project_id => @project} %>
|
<%= link_to l(:label_contract_plural), { :controller => 'contracts', :action => 'index', :project_id => @project} %>
<% end %>

View File

@ -29,7 +29,25 @@
<td><%= status.count %></td>
</tr>
<% end %>
</tbody>
<tr>
<td><%= l(:label_contracts_created) %></td>
<td><%= Contract.count(:conditions => {:author_id => @user.id, :created_on => from..to}) %></td>Contract.table_name}.price) AS total_sum",
:joins => "JOIN #{ContractStatus.table_name} ON #{Contract.table_name}.status_id = #{ContractStatus.table_name}.id",
:conditions => {:author_id => @user.id, :created_on => from..to},
:group => "#{ContractStatus.table_name}.name, #{ContractStatus.table_name}.color, #{Contract.table_name}.status_id").each do |status| %>
<tr>
<td style=<%= "background-color:#{status.status.color_name};color:white;"%>>
<span class="Contract-status" %> >
<%= h status.name %>
</span>
</td>
<td><%= status.count %></td>
</tr>
<% end %>
</tbody>
</table>

View File

@ -0,0 +1,32 @@
<h3><%= l(:label_my_contract_plural) %></h3>
<% contracts = Contract.visible.open.find(:all, :conditions => {:assigned_to_id => User.current.id}, :limit => 20) %>
<div class="box" id="duplicates">
<ul>
<% contracts.each do |contract| %>
<li>
<%= avatar_to contract, :size => "16" %>
<%= link_to contract.full_name, polymorphic_url(contract) %>
<%= "(#{contract_price(contract)}) " unless contract.price.blank? %>
<% if contract.status %>
<span class="contract-status" style = <%= "background-color:#{contract.status.color_name};color:white;" %> >
<%= h contract.status %>
</span>
<% end %>
</li>
<% end %>
</ul>
</div>
<% if contracts.length > 0 %>
<p class="small"><%= link_to l(:label_contract_view_all),
:controller => 'contracts',
:action => 'index',
:assigned_to_id => User.current.id %></p>
<% end %>
<% content_for(:header_tags) do %>
<%= javascript_include_tag :contacts, :plugin => 'redmine_contacts' %>
<%= stylesheet_link_tag :contacts, :plugin => 'redmine_contacts' %>
<% end %>

View File

@ -2,11 +2,17 @@
<div class="tabular box">
<p>
<label><%= l(:label_show_deaks_tab) %></label>
<label><%= l(:label_show_deals_tab) %></label>
<%= 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 %>
</p>
<p>
<label><%= l(:label_show_contracts_tab) %></label>
<%= 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 %>
</p>
<p>
<label><%= l(:label_show_on_projects_show) %></label>
<%= hidden_field_tag('contacts_settings[contacts_show_on_projects_show]', 0) %>

View File

@ -0,0 +1,28 @@
<h3><%= l(:label_contract_status_plural) %></h3>
<% if ContractStatus.all.any? %>
<% form_tag({:controller => "contract_statuses", :action => "assing_to_project", :project_id => @project}, :method => :put, :class => "tabular") do %>
<table class="list">
<thead><tr>
<th><%= l(:field_name) %></th>
<th><%=l(:field_is_default)%></th>
<th><%=l(:field_contract_status_is_closed)%></th>
<th style="width:15%;"><%= l(:field_active) %></th>
</tr></thead>
<tbody>
<% ContractStatus.all.each do |status| %>
<tr class="<%= cycle 'odd', 'even' %>">
<td><span class="color" style="border: 1px solid #D7D7D7;background-color: <%= status.color_name %>;">&nbsp;&nbsp;&nbsp;&nbsp;</span> <%= h(status.name) %></td>
<td align="center" style="width:15%;"><%= checked_image status.is_default? %></td>
<td align="center" style="width:15%;"><%= checked_image status.is_closed? %></td>
<td align="center" style="width:15%;">
<%= check_box_tag "contract_statuses[]", status.id , @project.contract_statuses.include?(status) %>
</td>
</tr>
<% end %>
</tbody>
</table>
<%= submit_tag l(:button_save) %>
<% end %>
<% end %>

View File

@ -0,0 +1,28 @@
<h3><%= l(:label_contract_category_plural) %></h3>
<% if @project.contract_categories.any? %>
<table class="list">
<thead><tr>
<th><%= l(:field_name) %></th>
<th></th>
</tr></thead>
<tbody>
<% for category in @project.contract_categories %>
<% unless category.new_record? %>
<tr class="<%= cycle 'odd', 'even' %>">
<td><%= link_to_if_authorized h(category.name), { :controller => 'contract_categories', :action => 'edit', :id => category } %></td>
<td class="buttons">
<%= 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' %>
</td>
</tr>
<% end %>
<% end %>
</tbody>
</table>
<% else %>
<p class="nodata"><%= l(:label_no_data) %></p>
<% end %>
<p><%= link_to_if_authorized l(:label_contract_category_new), :controller => 'contract_categories', :action => 'new', :project_id => @project %></p>
<%= render :partial => "projects/contract_statuses" %>

View File

@ -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

View File

@ -0,0 +1,34 @@
<div class="contextual">
<%= link_to l(:label_contract_status_new), {:controller => "contract_statuses", :action => 'new'}, :class => 'icon icon-add' %>
</div>
<h3><%=l(:label_contract_status_plural)%></h3>
<table class="list">
<thead><tr>
<th><%=l(:field_status)%></th>
<th><%=l(:field_is_default)%></th>
<th><%=l(:field_contract_status_is_closed)%></th>
<th><%=l(:button_sort)%></th>
<th></th>
</tr></thead>
<tbody>
<% for status in ContractStatus.all( :order => "position") %>
<tr class="<%= cycle("odd", "even") %>">
<td><span class="color" style="border: 1px solid #D7D7D7;background-color: <%= status.color_name %>;">&nbsp;&nbsp;&nbsp;&nbsp;</span> <%= link_to status.name, :controller => "contract_statuses", :action => 'edit', :id => status %></td>
<td align="center" style="width:15%;"><%= checked_image status.is_default? %></td>
<td align="center" style="width:15%;"><%= checked_image status.is_closed? %></td>
<td align="center" style="width:15%;"><%= reorder_links('contract_status', {:controller => "contract_statuses", :action => 'update', :id => status}) %></td>
<td class="buttons">
<%= link_to(l(:button_delete), {:controller => "contract_statuses", :action => 'destroy', :id => status },
:method => :post,
:confirm => l(:text_are_you_sure),
:class => 'icon icon-del') %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% html_title(l(:label_contract_status_plural)) -%>

View File

@ -1,5 +1,6 @@
div.contact {background:#f4e9f2; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
div.deal {background:#edfff2; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
div.contract {background:#edfff2; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7;}
form#add_task_form {background:#ffffff; display: block; padding:6px; margin-bottom:6px;border: 1px solid #d7d7d7; width: 92%;}
/********************/
@ -209,6 +210,7 @@ table.contacts.index tr.context-menu-selection h2 { color: #F8F8F8 !important; }
table.contacts.index tr.context-menu-selection td { color: #F8F8F8 !important; }
table.contacts.index tr.context-menu-selection span.tag a { border: 1px solid #EFEFEF; padding: 2px 3px; }
table.contacts.index tr.context-menu-selection span.deal-status { border: 1px solid #EFEFEF; padding: 2px 3px; }
table.contacts.index tr.context-menu-selection span.contract-status { border: 1px solid #EFEFEF; padding: 2px 3px; }
table.contacts.index tbody tr:hover { background-color:#ffffdd; }
@ -230,6 +232,7 @@ table.contacts.index th.title {text-align: right;}
table.contacts.index th.sum {text-align: left;}
table.contacts.deals.index td.avatar {padding: 10px 10px 10px 10px;}
table.contacts.contracts.index td.avatar {padding: 10px 10px 10px 10px;}
/*table.contacts.index td.avatar {margin-left: 50px;}*/
@ -246,6 +249,14 @@ table.contacts.index td.name h1.deal_name {
padding: 0;
}
table.contacts.index td.name h1.contract_name {
font-size: 16px;
font-weight: normal;
margin: 0;
padding: 0;
}
table.contacts.index td.name h1.selected { background-color: #ffb;}
table.contacts.index td.name h2.selected { background-color: #ffb;}
@ -269,19 +280,21 @@ div.Right td.name div.info {
/*Deals*/
div#deal-status {
div#deal-status, div#contract-status {
margin-top: 4px;
}
div.deal-sum {
div.deal-sum, div.contract-sum {
margin-bottom: 4px;
}
table.related_deals td.name h4 { letter-spacing: -1px; margin: 0px 0 4px 0; padding: 0; line-height: 1.1em;}
table.related_contracts td.name h4, table.related_deals td.name h4 {
letter-spacing: -1px; margin: 0px 0 4px 0; padding: 0; line-height: 1.1em;
}
div#deal-status span.contextual {float: none; padding-left: 0px;}
span.deal-status {
span.deal-status , span.contract-status {
padding: 3px 4px;
font-size: 10px;
/* display: block;*/
@ -293,6 +306,7 @@ dt.contact { background-image: url(../images/user_suit.png); }
.icon-money-dollar { background-image: url(../images/money_dollar.png); }
.icon-add-deal { background-image: url(../images/money.png); }
.icon-add-contract { background-image: url(../images/money.png); }
.icon-add-employee { background-image: url(../images/user_suit.png); }
.icon-link-break { background-image: url(../../../images/link_break.png); }
.icon-call { background-image: url(../images/phone.png); }
@ -316,7 +330,7 @@ div.wiki img.tumbnail {
/* NOTE_DATA */
/**************************************************************/
table.note_data, table.related_deals {
table.note_data, table.related_deals , table.related_contracts {
width: 100%;
}

View File

@ -191,8 +191,41 @@ en:
my_contacts_stats: Contacts statistics
label_add_into: Add into
label_delete_from: Delete from
label_show_deaks_tab: Show deals tab
label_show_deals_tab: Show deals tab
label_show_on_projects_show: Show contacts on projects overview
#2.2.1
label_contacts_show_in_list: Show in list
label_contacts_show_in_list: Show in list
#2.2.2-rodax
label_contract_category: Categoría de contrato
label_contract_category_plural: Categorías de contrato
label_contract_category_new: Nueva categoría
text_contract_category_destroy_assignments: Remove category assignments
text_contract_category_destroy_question: "Algunos contratos (%{count}) pertenecen a esta categoría. ¿Qué quieres hacer?"
text_contract_category_reassign_to: Reasignar contratos a esta categoría
text_contracts_destroy_confirmation: '¿Estás seguro de que quieres eliminar los contrato(s) seleccionados?'
label_contract_status_plural: Estados de contrato
label_contract_status: Estado de contrato
field_contract_status_is_closed: Cerrado
label_contract_status_new: Nuevo
label_my_contract_plural: Contratos abiertos asignados a mí
label_contract_view_all: Ver todos los contratos
label_contract_plural: Contratos
label_contract: Contrato
label_contract_new: Nuevo contrato
label_contract_edit_information: Cambiar información del contrato
label_contract_change_status: Cambiar estado del contrato
label_contract_pending: Pendiente
label_contract_won: Ganado
label_contract_lost: Perdido
field_contract_name: Nombre
field_contract_background: Trasfondo
field_contract_contact: Contacto
field_contract_price: Cuenta
my_contracts: My contracts
label_contracts_created: Contracts created
label_show_contracts_tab: Show contracts tab

View File

@ -158,11 +158,66 @@ es:
#2.0.3
label_add_contact: Add new contact in project
label_contact: Contact
field_age: Age
label_vcf_import: Import from vCard
label_contact: Contacto
field_age: Edad
label_vcf_import: Importar desde vCard
label_mail_from: From
permission_import_contacts: Import contacts
permission_import_contacts: Importar contactos
#2.1.0
field_company_name: Company name
label_recently_added_contacts: Recently added contacts
label_created_by_me: Contacts created by me
my_contacts: My contacts
my_deals: My deals
#2.2.0
label_note_type_email: Email
label_note_type_call: Call
label_note_type_meeting: Meeting
field_deal_currency: Currency
label_my_contacts_stats: Contacts statistics for this month
label_contacts_created: Contacts created
label_deals_created: Deals created
my_contacts_avatars: My contacts photos
my_contacts_stats: Contacts statistics
label_add_into: Add into
label_delete_from: Delete from
label_show_deals_tab: Show deals tab
label_show_on_projects_show: Show contacts on projects overview
#2.2.1
label_contacts_show_in_list: Show in list
#2.2.2-rodax
label_contract_plural: Categorías de contratos
label_contract_category: Categoría de contrato
label_contract_category_plural: Categorías de contrato
label_contract_category_new: Nueva categoría
text_contract_category_destroy_assignments: Remove category assignments
text_contract_category_destroy_question: "Algunos contratos (%{count}) pertenecen a esta categoría. ¿Qué quieres hacer?"
text_contract_category_reassign_to: Reasignar contratos a esta categoría
text_contracts_destroy_confirmation: '¿Estás seguro de que quieres eliminar los contrato(s) seleccionados?'
label_contract_status_plural: Estados de contrato
label_contract_status: Estado de contrato
field_contract_status_is_closed: Cerrado
label_contract_status_new: Nuevo
label_my_contract_plural: Contratos abiertos asignados a mí
label_contract_view_all: Ver todos los contratos
label_contract_plural: Contratos
label_contract: Contrato
label_contract_new: Nuevo contrato
label_contract_edit_information: Cambiar información del contrato
label_contract_change_status: Cambiar estado del contrato
label_contract_pending: Pendiente
label_contract_won: Ganado
label_contract_lost: Perdido
field_contract_name: Nombre
field_contract_background: Trasfondo
field_contract_contact: Contacto
field_contract_price: Cuenta
my_contracts: Mis contratos
label_contracts_created: Contractos creados
label_show_contracts_tab: Show contracts tab

View File

@ -38,6 +38,11 @@ ActionController::Routing::Routes.draw do |map|
categories.connect 'projects/:project_id/deal_categories/new', :action => 'new'
end
map.with_options :controller => 'contract_categories' do |categories|
categories.connect 'projects/:project_id/contract_categories/new', :action => 'new'
end
map.with_options :controller => 'sale_funel' do |sale_funel|
sale_funel.connect 'projects/:project_id/sale_funel', :action => 'index'
sale_funel.connect 'sale_funel', :action => 'index'
@ -54,6 +59,17 @@ ActionController::Routing::Routes.draw do |map|
deals_routes.connect "deals/:id/destroy", :conditions => { :method => :post}, :action => 'destroy', :id => /\d+/
deals_routes.connect "deals/:id/edit", :conditions => { :method => :get }, :action => 'edit', :id => /\d+/
end
map.with_options :controller => 'contracts' do |contracts_routes|
contracts_routes.connect "contracts", :conditions => { :method => :get }, :action => 'index'
contracts_routes.connect "projects/:project_id/contracts", :action => 'index'
contracts_routes.connect "projects/:project_id/contracts/create", :conditions => { :method => :post }, :action => 'create'
contracts_routes.connect "projects/:project_id/contracts/new", :conditions => { :method => :get }, :action => 'new'
contracts_routes.connect "contracts/:id", :conditions => { :method => :get }, :action => 'show', :id => /\d+/
contracts_routes.connect "contracts/:id/update", :conditions => { :method => :post }, :action => 'update', :id => /\d+/
contracts_routes.connect "contracts/:id/destroy", :conditions => { :method => :post}, :action => 'destroy', :id => /\d+/
contracts_routes.connect "contracts/:id/edit", :conditions => { :method => :get }, :action => 'edit', :id => /\d+/
end
map.with_options :controller => 'notes' do |notes_routes|
notes_routes.connect "notes/:note_id", :conditions => { :method => :get }, :action => 'show', :note_id => /\d+/

View File

@ -0,0 +1,14 @@
class CreateContactsContracts < ActiveRecord::Migration
def self.up
create_table :contacts_contracts, :id => false do |t|
t.integer :contract_id
t.integer :contact_id
end
add_index :contacts_contracts, [:contract_id, :contact_id]
end
def self.down
drop_table :contacts_contracts
end
end

24
init.rb
View File

@ -50,11 +50,21 @@ Redmine::Plugin.register :contacts do
:deal_contacts => [:add, :delete],
:notes => [:add_note, :destroy_note]
}
permission :delete_contracts, :contracts => [:destroy, :bulk_destroy]
permission :view_contracts, {
:contracts => [:index, :show, :context_menu],
}
permission :edit_contracts, {
:contracts => [:new, :create, :edit, :update, :add_attachment, :bulk_update, :bulk_edit],
:contract_contacts => [:add, :delete],
}
permission :manage_contacts, {
:projects => :settings,
:contacts_settings => :save,
:deal_categories => [:new, :edit, :destroy],
:deal_statuses => [:assing_to_project], :require => :member
:contract_categories => [:new, :edit, :destroy],
:deal_statuses => [:assing_to_project], :require => :member,
:contract_statuses => [:assing_to_project], :require => :member
}
permission :import_contacts, {}
end
@ -65,6 +75,11 @@ Redmine::Plugin.register :contacts do
:if => Proc.new{|p| ContactsSetting[:contacts_show_deals_tab, p.id].to_i > 0 },
:param => :project_id
menu :project_menu, :contracts, {:controller => 'contracts', :action => 'index' },
:caption => :label_contract_plural,
:if => Proc.new{|p| ContactsSetting[:contacts_show_contracts_tab, p.id].to_i > 0 },
:param => :project_id
menu :application_menu, :contacts,
{:controller => 'contacts', :action => 'index'},
:caption => :label_contact_plural,
@ -78,6 +93,12 @@ Redmine::Plugin.register :contacts do
:if => Proc.new{User.current.allowed_to?({:controller => 'deals', :action => 'index'},
nil, {:global => true})}
menu :application_menu, :contracts,
{:controller => 'contracts', :action => 'index'},
:caption => :label_contract_plural,
:param => :project_id,
:if => Proc.new{User.current.allowed_to?({:controller => 'contracts', :action => 'index'},
nil, {:global => true})}
menu :top_menu, :contacts, {:controller => 'contacts', :action => 'index'}, :caption => :contacts_title, :if => Proc.new {
User.current.allowed_to?({:controller => 'contacts', :action => 'index'}, nil, {:global => true})
@ -91,6 +112,7 @@ Redmine::Plugin.register :contacts do
Redmine::Search.map do |search|
search.register :contacts
search.register :deals
search.register :contracts
search.register :contact_notes
search.register :deal_notes
end

View File

@ -2,6 +2,7 @@ module RedmineContacts
module Hooks
class ViewsCustomFieldsHook < Redmine::Hook::ViewListener
render_on :view_custom_fields_form_deal_custom_field, :partial => "deals/custom_field_form"
render_on :view_custom_fields_form_contract_custom_field, :partial => "contracts/custom_field_form"
end
end
end

View File

@ -6,8 +6,11 @@ module RedmineContacts
unloadable # Send unloadable so it will not be unloaded in development
has_and_belongs_to_many :contacts, :order => "last_name, first_name", :uniq => true
has_many :deals, :dependent => :delete_all
has_many :contracts, :dependent => :delete_all
has_many :deal_categories, :dependent => :delete_all, :order => "#{DealCategory.table_name}.name"
has_many :contract_categories, :dependent => :delete_all, :order => "#{ContractCategory.table_name}.name"
has_and_belongs_to_many :deal_statuses, :order => "#{DealStatus.table_name}.position", :uniq => true
has_and_belongs_to_many :contract_statuses, :order => "#{ContractStatus.table_name}.position", :uniq => true
end
end
end

View File

@ -0,0 +1,8 @@
require File.dirname(__FILE__) + '/../test_helper'
class ContractCategoriesControllerTest < ActionController::TestCase
# Replace this with your real tests.
def test_truth
assert true
end
end

View File

@ -0,0 +1,8 @@
require File.dirname(__FILE__) + '/../test_helper'
class ContractContactsControllerTest < ActionController::TestCase
# Replace this with your real tests.
def test_truth
assert true
end
end

View File

@ -0,0 +1,8 @@
require File.dirname(__FILE__) + '/../test_helper'
class ContractStatusesControllerTest < ActionController::TestCase
# Replace this with your real tests.
def test_truth
assert true
end
end

View File

@ -0,0 +1,8 @@
require File.dirname(__FILE__) + '/../test_helper'
class ContractsControllerTest < ActionController::TestCase
# Replace this with your real tests.
def test_truth
assert true
end
end