Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge remote-tracking branch 'unboxed/adding_team_tab' into adding_te…

…am_tab

Conflicts:
	Capfile
	Gemfile
	Gemfile.lock
	app/controllers/entities/accounts_controller.rb
	app/controllers/entities/campaigns_controller.rb
	app/controllers/entities/contacts_controller.rb
	app/controllers/entities_controller.rb
	app/controllers/home_controller.rb
	app/models/entities/opportunity.rb
	app/models/polymorphic/task.rb
	app/views/accounts/_new.html.haml
	app/views/campaigns/_new.html.haml
	app/views/contacts/_new.html.haml
	app/views/leads/_new.html.haml
	app/views/opportunities/_new.html.haml
	app/views/users/update.js.rjs
	app/views/versions/_versions.html.haml
	config/locales/en-GB_fat_free_crm.yml
	fat_free_crm.gemspec
	spec/acceptance/dashboard_spec.rb
	spec/acceptance/support/paths.rb
	spec/controllers/entities/accounts_controller_spec.rb
	spec/controllers/entities/campaigns_controller_spec.rb
	spec/controllers/entities/contacts_controller_spec.rb
	spec/controllers/entities/leads_controller_spec.rb
	spec/controllers/entities/opportunities_controller_spec.rb
	spec/controllers/home_controller_spec.rb
	spec/models/entities/opportunity_spec.rb
  • Loading branch information...
commit f8f861d87e197088c1384f1dd090a199c5151889 2 parents 708ae6f + aea0e9d
@steveyken steveyken authored
Showing with 416 additions and 85 deletions.
  1. +1 −1  Capfile
  2. +3 −35 Gemfile.lock
  3. +1 −1  app/controllers/home_controller.rb
  4. +7 −2 app/controllers/users_controller.rb
  5. +7 −5 app/models/entities/opportunity.rb
  6. +1 −1  app/models/observers/entity_observer.rb
  7. +4 −0 app/models/users/user.rb
  8. +6 −0 app/views/users/_user_report.html.haml
  9. +15 −0 app/views/users/opportunities_overview.html.haml
  10. +40 −0 config/deploy.rb
  11. +15 −0 config/deploy/production.rb
  12. +13 −0 config/deploy/staging.rb
  13. +4 −0 config/environments/development.rb
  14. +2 −0  config/environments/production.rb
  15. +2 −0  config/environments/staging.rb
  16. +6 −1 config/locales/en-GB_fat_free_crm.yml
  17. +6 −0 config/locales/en-US_fat_free_crm.yml
  18. +3 −0  config/routes.rb
  19. +10 −7 config/settings.default.yml
  20. +8 −2 lib/fat_free_crm/core_ext/array.rb
  21. +0 −1  spec/acceptance/accounts_spec.rb
  22. +0 −1  spec/acceptance/campaigns_spec.rb
  23. +0 −1  spec/acceptance/contacts_spec.rb
  24. +0 −1  spec/acceptance/leads_spec.rb
  25. +82 −0 spec/acceptance/opportunities_overview_spec.rb
  26. +0 −1  spec/acceptance/opportunities_spec.rb
  27. +3 −0  spec/acceptance/support/paths.rb
  28. +1 −2  spec/controllers/entities/leads_controller_spec.rb
  29. +12 −5 spec/controllers/home_controller_spec.rb
  30. +68 −0 spec/controllers/users_controller_spec.rb
  31. +11 −3 spec/factories/opportunity_factories.rb
  32. +33 −13 spec/models/entities/opportunity_spec.rb
  33. +4 −2 spec/models/observers/entity_observer_spec.rb
  34. +48 −0 spec/models/users/user_spec.rb
View
2  Capfile
@@ -2,4 +2,4 @@ load 'deploy'
# Uncomment if you are using Rails' asset pipeline
# load 'deploy/assets'
Dir['vendor/gems/*/recipes/*.rb','vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
-load 'config/deploy' # remove this line to skip loading any of the default tasks
+load 'config/deploy' # remove this line to skip loading any of the default tasks
View
38 Gemfile.lock
@@ -49,21 +49,8 @@ GEM
bundler
cancan (1.6.7)
capistrano (2.13.5)
- highline
- net-scp (>= 1.0.0)
- net-sftp (>= 2.0.0)
- net-ssh (>= 2.0.14)
- net-ssh-gateway (>= 1.1.0)
capistrano_colors (0.5.5)
capybara (1.1.3)
- mime-types (>= 1.16)
- nokogiri (>= 1.3.3)
- rack (>= 1.0.0)
- rack-test (>= 0.5.4)
- selenium-webdriver (~> 2.0)
- xpath (~> 0.1.4)
- childprocess (0.3.6)
- ffi (~> 1.0, >= 1.0.6)
chosen-rails_ffcrm (0.9.5)
railties (~> 3.0)
thor (~> 0.14)
@@ -103,10 +90,8 @@ GEM
factory_girl (~> 3.0.0)
railties (>= 3.0.0)
ffaker (1.14.0)
- ffi (1.1.5)
haml (3.1.7)
- headless (0.2.2)
- highline (1.6.15)
+ headless (0.3.1)
hike (1.2.1)
htmlentities (4.3.1)
i18n (0.6.1)
@@ -117,8 +102,6 @@ GEM
json (1.7.5)
kgio (2.7.4)
libv8 (3.3.10.4)
- libwebsocket (0.1.5)
- addressable
linecache (0.46)
rbx-require-relative (> 0.0.4)
mail (2.4.4)
@@ -128,14 +111,7 @@ GEM
method_source (0.7.1)
mime-types (1.19)
multi_json (1.3.7)
- net-scp (1.0.4)
- net-ssh (>= 1.99.1)
- net-sftp (2.0.5)
- net-ssh (>= 2.0.9)
- net-ssh (2.6.1)
- net-ssh-gateway (1.1.0)
- net-ssh (>= 1.99.1)
- nokogiri (1.5.5)
+ nokogiri (1.5.2)
paper_trail (2.6.3)
activerecord (~> 3.0)
railties (~> 3.0)
@@ -218,19 +194,13 @@ GEM
ruby-debug-base (~> 0.10.4.0)
ruby-debug-base (0.10.4)
linecache (>= 0.3)
- rubyzip (0.9.9)
- sass (3.2.1)
+ sass (3.1.16)
sass-rails (3.2.5)
railties (~> 3.2.0)
sass (>= 3.1.10)
tilt (~> 1.3)
select2-rails (3.2.1)
thor (~> 0.14)
- selenium-webdriver (2.26.0)
- childprocess (>= 0.2.5)
- libwebsocket (~> 0.1.3)
- multi_json (~> 1.0)
- rubyzip
simple_form (2.0.2)
actionpack (~> 3.0)
activemodel (~> 3.0)
@@ -266,8 +236,6 @@ GEM
valium (0.5.0)
activerecord (>= 3.0.2)
will_paginate (3.0.3)
- xpath (0.1.4)
- nokogiri (~> 1.3)
PLATFORMS
ruby
View
2  app/controllers/home_controller.rb
@@ -27,7 +27,7 @@ def index
@activities = get_activities
@my_tasks = Task.visible_on_dashboard(current_user).by_due_at
- @my_opportunities = Opportunity.visible_on_dashboard(current_user).by_closes_on
+ @my_opportunities = Opportunity.visible_on_dashboard(current_user).by_closes_on.by_amount
@my_accounts = Account.visible_on_dashboard(current_user).by_name
respond_with(@activities)
end
View
9 app/controllers/users_controller.rb
@@ -19,7 +19,7 @@ class UsersController < ApplicationController
before_filter :require_no_user, :only => [ :new, :create ]
before_filter :require_user, :only => [ :show, :redraw ]
- before_filter :set_current_tab, :only => [ :show ] # Don't hightlight any tabs.
+ before_filter :set_current_tab, :only => [ :show, :opportunities_overview ] # Don't hightlight any tabs.
before_filter :require_and_assign_user, :except => [ :new, :create, :show, :avatar, :upload_avatar ]
before_filter :assign_given_or_current_user, :only => [ :show, :avatar, :upload_avatar, :edit, :update ]
@@ -149,7 +149,12 @@ def redraw
render(:update) { |page| page.redirect_to user_path(current_user) }
end
-private
+ def opportunities_overview
+ @users_with_opportunities = User.have_assigned_opportunities.order(:first_name)
+ @unassigned_opportunities = Opportunity.unassigned.pipeline.order(:stage)
+ end
+
+ private
#----------------------------------------------------------------------------
def require_and_assign_user
View
12 app/models/entities/opportunity.rb
@@ -53,12 +53,13 @@ class Opportunity < ActiveRecord::Base
scope :state, lambda { |filters|
where('stage IN (?)' + (filters.delete('other') ? ' OR stage IS NULL' : ''), filters)
}
- scope :created_by, lambda { |user| where('user_id = ?', user.id) }
+ scope :created_by, lambda { |user| where('user_id = ?', user.id) }
scope :assigned_to, lambda { |user| where('assigned_to = ?', user.id) }
- scope :won, where("opportunities.stage = 'won'")
- scope :lost, where("opportunities.stage = 'lost'")
- scope :not_lost, where("opportunities.stage <> 'lost'")
- scope :pipeline, where("opportunities.stage IS NULL OR (opportunities.stage != 'won' AND opportunities.stage != 'lost')")
+ scope :won, where("opportunities.stage = 'won'")
+ scope :lost, where("opportunities.stage = 'lost'")
+ scope :not_lost, where("opportunities.stage <> 'lost'")
+ scope :pipeline, where("opportunities.stage IS NULL OR (opportunities.stage != 'won' AND opportunities.stage != 'lost')")
+ scope :unassigned, where("opportunities.assigned_to IS NULL")
# Search by name OR id
scope :text_search, lambda { |query|
@@ -77,6 +78,7 @@ class Opportunity < ActiveRecord::Base
}
scope :by_closes_on, order(:closes_on)
+ scope :by_amount, order('opportunities.amount DESC')
uses_user_permissions
acts_as_commentable
View
2  app/models/observers/entity_observer.rb
@@ -14,7 +14,7 @@ def after_update(item)
private
def send_notification_to_assignee(item)
- UserMailer.assigned_entity_notification(item, current_user) if item.assignee.present? && current_user.present?
+ UserMailer.assigned_entity_notification(item, current_user).deliver if item.assignee.present? && current_user.present?
end
def current_user
View
4 app/models/users/user.rb
@@ -65,6 +65,7 @@ class User < ActiveRecord::Base
has_many :leads
has_many :contacts
has_many :opportunities
+ has_many :assigned_opportunities, :class_name => 'Opportunity', :foreign_key => 'assigned_to'
has_many :permissions, :dependent => :destroy
has_many :preferences, :dependent => :destroy
has_and_belongs_to_many :groups
@@ -85,6 +86,9 @@ class User < ActiveRecord::Base
accessible_by(User.current_ability)
}
+ scope :have_assigned_opportunities, joins("INNER JOIN opportunities ON users.id = opportunities.assigned_to").
+ where("opportunities.stage <> 'lost' AND opportunities.stage <> 'won'")
+
acts_as_authentic do |c|
c.session_class = Authentication
c.validates_uniqueness_of_login_field_options = { :message => :username_taken }
View
6 app/views/users/_user_report.html.haml
@@ -0,0 +1,6 @@
+%li{:id => dom_id(user_report)}
+ .title
+ %span#title
+ = user_report.full_name
+ %ul.list#opportunities
+ = render :partial => "opportunities/opportunity", :collection => user_report.assigned_opportunities.pipeline.order("opportunities.amount DESC")
View
15 app/views/users/opportunities_overview.html.haml
@@ -0,0 +1,15 @@
+= styles_for :opportunity
+
+
+- if @users_with_opportunities.all.any? || @unassigned_opportunities.all.any?
+ %ul
+ = render :partial => "user_report", :collection => @users_with_opportunities
+ - if @unassigned_opportunities.all.any?
+ #unassigned
+ .title
+ %span#title
+ = t(:unassigned_opportunities)
+ %ul.list#opportunities
+ = render :partial => "opportunities/opportunity", :collection => @unassigned_opportunities
+- else
+ = t(:no_opportunities_found)
View
40 config/deploy.rb
@@ -0,0 +1,40 @@
+set :stages, %w(staging production)
+set :default_stage, "staging"
+
+set :repository, "git@github.com:unboxed/fat_free_crm.git"
+set :application, "ffcrm"
+set :user, "ffcrm"
+set :deploy_to, "/home/#{user}/#{application}"
+
+require "bundler/capistrano"
+begin
+ require 'capistrano/ext/multistage'
+rescue LoadError
+ puts "Could not load capistrano multistage extension. Make sure you have installed the capistrano-ext gem"
+end
+
+default_run_options[:pty] = true
+
+set :scm, :git
+set :ssh_options, { :forward_agent => true }
+set :deploy_via, :remote_cache
+set :use_sudo, false
+
+namespace :deploy do
+ task :start do ; end
+ task :stop do ; end
+ task :restart, :roles => :app, :except => { :no_release => true } do
+ run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}"
+ end
+
+ # Clean up old releases (by default keeps last 5)
+ after "deploy:update_code", "deploy:cleanup"
+
+ after "deploy:finalize_update", "deploy:symlink_configs"
+ task :symlink_configs, :roles => :app do
+ run "ln -fs #{shared_path}/config/database.yml #{latest_release}/config/database.yml"
+ run "ln -fs #{shared_path}/config/settings.yml #{latest_release}/config/settings.yml"
+ run "ln -fs #{shared_path}/config/ldap.yml #{latest_release}/config/ldap.yml"
+ run "ln -fs #{shared_path}/config/ldap_attributes_map.yml #{latest_release}/config/ldap_attributes_map.yml"
+ end
+end
View
15 config/deploy/production.rb
@@ -0,0 +1,15 @@
+set :rails_env, "production"
+
+set :rails_server, "prod2.unboxedconsulting.com"
+
+role :app, rails_server
+role :web, rails_server
+role :db, rails_server, :primary => true
+
+if ENV['TAG'].nil?
+ puts "No tag specified for production deploy. Set the TAG env variable to the tag to deploy."
+ puts "e.g. TAG='REL-1.2' cap production deploy:migrations"
+ exit 1
+else
+ set :branch, "#{ENV['TAG']}"
+end
View
13 config/deploy/staging.rb
@@ -0,0 +1,13 @@
+set :rails_env, "staging"
+
+set :rails_server, "stage3.unboxedconsulting.com"
+
+role :app, rails_server
+role :web, rails_server
+role :db, rails_server, :primary => true
+
+if ENV['BRANCH'].nil?
+ set :branch, "master"
+else
+ set :branch, "#{ENV['BRANCH']}"
+end
View
4 config/environments/development.rb
@@ -35,5 +35,9 @@
# Expands the lines which load the assets
config.assets.debug = true
+
+ # Don't care if the mailer can't send
+ config.action_mailer.default_url_options = { :host => 'localhost:3000' }
+ config.action_mailer.raise_delivery_errors = false
end
end
View
2  config/environments/production.rb
@@ -57,5 +57,7 @@
# Send deprecation notices to registered listeners
config.active_support.deprecation = :notify
+
+ config.action_mailer.default_url_options = {:host => 'ffcrm.unboxedconsulting.com', :protocol => 'https'}
end
end
View
2  config/environments/staging.rb
@@ -10,5 +10,7 @@
# Full error reports
config.consider_all_requests_local = true
+
+ config.action_mailer.default_url_options = {:host => 'ffcrm.ubxdstage.com', :protocol => 'https'}
end
end
View
7 config/locales/en-GB_fat_free_crm.yml
@@ -22,6 +22,7 @@ en-GB:
tab_accounts: Accounts
tab_contacts: Contacts
tab_opportunities: Opportunities
+ tab_team: Team
admin_tab_groups: Groups
admin_tab_users: Users
@@ -479,6 +480,11 @@ en-GB:
view_assigned_tasks: view assigned tasks
view_pending_tasks: view pending tasks
+ # Views -> Team.
+ #----------------------------------------------------------------------------
+ unassigned_opportunities: Unassigned Opportunities
+ no_opportunities_found: There are currently no outstanding opportunities.
+
# Views -> Home.
#----------------------------------------------------------------------------
action_completed: completed
@@ -757,7 +763,6 @@ en-GB:
previous_label: "&#8592; Previous"
next_label: "Next &#8594;"
page_gap: "&hellip;"
-
page_entries_info:
single_page:
zero: "No %{plural} found"
View
6 config/locales/en-US_fat_free_crm.yml
@@ -24,6 +24,7 @@ en-US:
tab_accounts: Accounts
tab_contacts: Contacts
tab_opportunities: Opportunities
+ tab_team: Team
admin_tab_groups: Groups
admin_tab_users: Users
@@ -524,6 +525,11 @@ en-US:
view_assigned_tasks: view assigned tasks
view_pending_tasks: view pending tasks
+ # Views -> Team.
+ #----------------------------------------------------------------------------
+ unassigned_opportunities: Unassigned Opportunities
+ no_opportunities_found: There are currently no outstanding opportunities.
+
# Views -> Home.
#----------------------------------------------------------------------------
action_completed: completed
View
3  config/routes.rb
@@ -144,6 +144,9 @@
collection do
match :auto_complete
end
+ collection do
+ get :opportunities_overview
+ end
end
namespace :admin do
View
17 config/settings.default.yml
@@ -206,13 +206,16 @@
# Main and Admin Tabs
#------------------------------------------------------------------------------
:tabs: [
- { :active : true, :text : :tab_dashboard, :url : { :controller : "home" } },
- { :active : false, :text : :tab_tasks, :url : { :controller : "tasks" } },
- { :active : false, :text : :tab_campaigns, :url : { :controller : "campaigns" } },
- { :active : false, :text : :tab_leads, :url : { :controller : "leads" } },
- { :active : false, :text : :tab_accounts, :url : { :controller : "accounts" } },
- { :active : false, :text : :tab_contacts, :url : { :controller : "contacts" } },
- { :active : false, :text : :tab_opportunities, :url : { :controller : "opportunities" } }
+ { :active : true, :text : :tab_dashboard, :url : { :controller : "home" } },
+ { :active : false, :text : :tab_tasks, :url : { :controller : "tasks" } },
+ { :active : false, :text : :tab_campaigns, :url : { :controller : "campaigns" } },
+ { :active : false, :text : :tab_leads, :url : { :controller : "leads" } },
+ { :active : false, :text : :tab_accounts, :url : { :controller : "accounts" } },
+ { :active : false, :text : :tab_contacts, :url : { :controller : "contacts" } },
+ { :active : false, :text : :tab_opportunities, :url : { :controller : "opportunities" } },
+ { :active : false, :text : :tab_team, :url : { :controller : "users",
+ :action : "opportunities_overview" } }
+
]
:admin_tabs: [
View
10 lib/fat_free_crm/core_ext/array.rb
@@ -58,11 +58,17 @@ def to_csv
klass = first.class
columns = klass.columns.map(&:name).reject { |column| column =~ /password|token/ }
-
+ columns << 'tags'
CSV.generate do |csv|
csv << columns.map { |column| klass.human_attribute_name(column) }
each do |item|
- csv << columns.map { |column| item.send(column) }
+ csv << columns.map do |column|
+ if column == 'tags'
+ item.tag_list.join(' ')
+ else
+ item.send(column)
+ end
+ end
end
end
end
View
1  spec/acceptance/accounts_spec.rb
@@ -71,7 +71,6 @@
page.should have_content('A new account *editted*')
click_link "Dashboard"
- page.should have_content("Bill Murray viewed account A new account *editted*")
page.should have_content("Bill Murray updated account A new account *editted*")
end
View
1  spec/acceptance/campaigns_spec.rb
@@ -64,7 +64,6 @@
page.should have_content('My Even Cooler Campaign')
click_link 'Dashboard'
- page.should have_content("Bill Murray viewed campaign My Even Cooler Campaign")
page.should have_content("Bill Murray updated campaign My Even Cooler Campaign")
end
View
1  spec/acceptance/contacts_spec.rb
@@ -67,7 +67,6 @@
page.should have_content('Test Subject')
click_link 'Dashboard'
- page.should have_content("Bill Murray viewed contact Test Subject")
page.should have_content("Bill Murray updated contact Test Subject")
end
View
1  spec/acceptance/leads_spec.rb
@@ -76,7 +76,6 @@
page.should have_content('+44 0987 654321')
click_link "Dashboard"
- page.should have_content("Bill Murray viewed lead Mrs Lead")
page.should have_content("Bill Murray updated lead Mrs Lead")
end
View
82 spec/acceptance/opportunities_overview_spec.rb
@@ -0,0 +1,82 @@
+require File.expand_path("../acceptance_helper.rb", __FILE__)
+
+feature 'Opportunities Overview', %q{
+ In order to keep track of my team's responsibilities
+ As a user
+ I want to see an overview of opportunities broken down by user
+} do
+
+ background do
+ @me = FactoryGirl.create(:user)
+
+ login_as_user(@me)
+ end
+
+ scenario "Accessing Opportunity Overview via the nav" do
+ visit homepage
+ within "#tabs" do
+ click_link "Team"
+ end
+
+ current_path.should == opportunity_overview_page
+ end
+
+ scenario "Viewing Opportunity Overview when all opportunities have been assigned" do
+ user1 = FactoryGirl.create(:user, :first_name => "Brian", :last_name => 'Doyle-Murray', :id => 64)
+ FactoryGirl.create(:opportunity, :name => "Acting", :stage => 'prospecting', :assignee => user1)
+ FactoryGirl.create(:opportunity, :name => "Directing", :stage => 'won', :assignee => user1)
+
+ user2 = FactoryGirl.create(:user, :first_name => "Dean", :last_name => 'Stockwell', :id => 86)
+ account1 = FactoryGirl.create(:account, :name => 'Quantum Leap')
+ FactoryGirl.create(:opportunity, :name => "Leaping", :stage => 'prospecting', :account => account1, :assignee => user2)
+ FactoryGirl.create(:opportunity, :name => "Return Home", :stage => 'prospecting', :account => account1, :assignee => user2)
+
+ user3 = FactoryGirl.create(:user, :first_name => "Chris", :last_name => 'Jarvis', :id => 16)
+ FactoryGirl.create(:opportunity, :stage => 'won', :assignee => user3)
+ FactoryGirl.create(:opportunity, :stage => 'lost', :assignee => user3)
+
+ visit opportunity_overview_page
+
+ within "#user_64" do
+ page.should have_selector('.title', :text => 'Brian Doyle-Murray')
+ page.should have_content('Acting')
+ page.should_not have_content('Directing')
+ end
+
+ within "#user_86" do
+ page.should have_selector('.title', :text => 'Dean Stockwell')
+ page.should have_content('Leaping from Quantum Leap')
+ page.should have_content('Return Home from Quantum Leap')
+ end
+
+ page.should_not have_selector('#user_16')
+
+ page.should_not have_selector('#unassigned')
+ end
+
+ scenario "Viewing Opportunity Overview when all opportunities are unassigned" do
+ FactoryGirl.create(:opportunity, :name => "Acting", :stage => 'prospecting', :assignee => nil)
+ FactoryGirl.create(:opportunity, :name => "Presenting", :stage => 'won', :assignee => nil)
+
+ visit opportunity_overview_page
+
+ within "#unassigned" do
+ page.should have_selector('.title', :text => 'Unassigned Opportunities')
+ page.should have_content('Acting')
+ page.should_not have_content('Presenting')
+ end
+ end
+
+ scenario "Viewing Opportunity Overview when there are no opportunities in the pipeline" do
+ FactoryGirl.create(:opportunity, :name => "Presenting", :stage => 'lost', :assignee => FactoryGirl.create(:user))
+ FactoryGirl.create(:opportunity, :name => "Eating", :stage => 'won', :assignee => nil)
+
+ visit opportunity_overview_page
+
+ page.should have_content('There are currently no outstanding opportunities.')
+ within "#main" do
+ page.should_not have_content("Presenting")
+ page.should_not have_content("Eating")
+ end
+ end
+end
View
1  spec/acceptance/opportunities_spec.rb
@@ -67,7 +67,6 @@
page.should have_content('An Even Cooler Opportunity')
click_link "Dashboard"
- page.should have_content("Bill Murray viewed opportunity An Even Cooler Opportunity")
page.should have_content("Bill Murray updated opportunity An Even Cooler Opportunity")
end
View
3  spec/acceptance/support/paths.rb
@@ -31,6 +31,9 @@ def tasks_page
def groups_page
admin_groups_path
+
+ def opportunity_overview_page
+ opportunities_overview_users_path
end
end
View
3  spec/controllers/entities/leads_controller_spec.rb
@@ -378,11 +378,10 @@
xhr :put, :create, :lead => { :first_name => "Billy", :last_name => "Bones"}, :campaign => @campaign.id
assigns[:campaign].should == @campaign
end
-
+
it "should add a new comment to the newly created lead when specified" do
@lead = FactoryGirl.create(:lead)
Lead.stub!(:new).and_return(@lead)
-
xhr :post, :create, :lead => { :first_name => "Test", :last_name => "Lead" }, :comment_body => "This is an important lead."
@lead.reload.comments.map(&:comment).should include("This is an important lead.")
end
View
17 spec/controllers/home_controller_spec.rb
@@ -10,11 +10,18 @@
end
it "should get a list of activities" do
- @activity = FactoryGirl.create(:version, :item => FactoryGirl.create(:account, :user => current_user))
- controller.should_receive(:get_activities).once.and_return([ @activity ])
+ activity = FactoryGirl.create(:version, :item => FactoryGirl.create(:account, :user => current_user))
+ controller.should_receive(:get_activities).once.and_return([ activity ])
+ get :index
+ assigns[:activities].should == [ activity ]
+ end
+
+ it "should not include views in the list of activities" do
+ activity = FactoryGirl.create(:version, :item => FactoryGirl.create(:account, :user => @current_user), :event => "view")
+ controller.should_receive(:get_activities).once.and_return([])
get :index
- assigns[:activities].should == [ @activity ]
+ assigns[:activities].should == []
end
it "should get a list of my tasks ordered by due_at" do
@@ -44,8 +51,8 @@
opportunity_3 = FactoryGirl.create(:opportunity, :name => "Third Opportunity", :closes_on => 5.days.from_now, :assigned_to => current_user.id, :stage => 'proposal')
opportunity_4 = FactoryGirl.create(:opportunity, :name => "Fourth Opportunity", :closes_on => 50.days.from_now, :assigned_to => nil, :user_id => current_user.id, :stage => 'proposal')
- FactoryGirl.create(:opportunity, :name => "Someone else's Opportunity", :assigned_to => FactoryGirl.create(:user).id, :stage => 'proposal')
- FactoryGirl.create(:opportunity, :name => "Not my opportunity", :assigned_to => FactoryGirl.create(:user).id, :stage => 'proposal')
+ FactoryGirl.create(:opportunity_in_pipeline, :name => "Someone else's Opportunity", :assigned_to => FactoryGirl.create(:user).id, :stage => 'proposal')
+ FactoryGirl.create(:opportunity_in_pipeline, :name => "Not my opportunity", :assigned_to => FactoryGirl.create(:user).id, :stage => 'proposal')
get :index
assigns[:my_opportunities].should == [opportunity_3, opportunity_2, opportunity_1, opportunity_4]
View
68 spec/controllers/users_controller_spec.rb
@@ -336,4 +336,72 @@
end
+ # GET /users/opportunities
+ # GET /users/opportunities.xml HTML
+ #----------------------------------------------------------------------------
+ describe "responding to GET opportunities_overview" do
+ before(:each) do
+ require_user
+ @user = @current_user
+ @user.update_attributes(:first_name => "Apple", :last_name => "Boy")
+ end
+
+ it "should assign @users" do
+ FactoryGirl.create(:opportunity, :stage => "prospecting", :assignee => @user)
+ xhr :get, :opportunities_overview
+ assigns[:users_with_opportunities].should == [@current_user]
+ end
+
+ it "@users should be ordered by name" do
+ FactoryGirl.create(:opportunity, :stage => "prospecting", :assignee => @user)
+
+ user1 = FactoryGirl.create(:user, :first_name => "Zebra", :last_name => "Stripes")
+ FactoryGirl.create(:opportunity, :stage => "prospecting", :assignee => user1)
+
+ user2 = FactoryGirl.create(:user, :first_name => "Bilbo", :last_name => "Magic")
+ FactoryGirl.create(:opportunity, :stage => "prospecting", :assignee => user2)
+
+ xhr :get, :opportunities_overview
+
+ assigns[:users_with_opportunities].should == [@user, user2, user1]
+ end
+
+ it "should assign @unassigned_opportunities with only open unassigned opportunities" do
+ @o1 = FactoryGirl.create(:opportunity, :stage => "prospecting", :assignee => nil)
+ @o2 = FactoryGirl.create(:opportunity, :stage => "won", :assignee => nil)
+ @o3 = FactoryGirl.create(:opportunity, :stage => "prospecting", :assignee => nil)
+
+ xhr :get, :opportunities_overview
+
+ assigns[:unassigned_opportunities].should include(@o1, @o3)
+ assigns[:unassigned_opportunities].should_not include(@o2)
+ end
+
+ it "@unassigned_opportunities should be ordered by stage" do
+ @o1 = FactoryGirl.create(:opportunity, :stage => "proposal", :assignee => nil)
+ @o2 = FactoryGirl.create(:opportunity, :stage => "prospecting", :assignee => nil)
+ @o3 = FactoryGirl.create(:opportunity, :stage => "negotiation", :assignee => nil)
+
+ xhr :get, :opportunities_overview
+
+ assigns[:unassigned_opportunities].should == [@o3, @o1, @o2]
+ end
+
+ it "should not include users who have no assigned opportunities" do
+ xhr :get, :opportunities_overview
+ assigns[:users_with_opportunities].should == []
+ end
+
+ it "should not include users who have no open assigned opportunities" do
+ FactoryGirl.create(:opportunity, :stage => "won", :assignee => @user)
+
+ xhr :get, :opportunities_overview
+ assigns[:users_with_opportunities].should == []
+ end
+
+ it "should render opportunities overview" do
+ xhr :get, :opportunities_overview
+ response.should render_template("users/opportunities_overview")
+ end
+ end
end
View
14 spec/factories/opportunity_factories.rb
@@ -1,7 +1,7 @@
FactoryGirl.define do
factory :opportunity do
- user
- campaign
+ user
+ campaign
account { FactoryGirl.create(:account) }
assigned_to nil
name { Faker::Lorem.sentence[0,64] }
@@ -17,4 +17,12 @@
updated_at { FactoryGirl.generate(:time) }
created_at { FactoryGirl.generate(:time) }
end
-end
+
+ factory :opportunity_in_pipeline, :parent => :opportunity do
+ stage { %w(prospecting analysis presentation proposal negotiation final_review).sample }
+ end
+
+ factory :open_opportunity, :parent => :opportunity do
+ stage { %w(prospecting analysis presentation proposal negotiation final_review).sample }
+ end
+end
View
46 spec/models/entities/opportunity_spec.rb
@@ -94,7 +94,7 @@
end
end
- describe "Named scopes" do
+ describe "Scopes" do
it "should find non-closed opportunities" do
Opportunity.delete_all
@opportunities = [
@@ -110,6 +110,19 @@
Opportunity.lost.sum(:amount).should == 6
Opportunity.sum(:amount).should == 12
end
+
+ context "unassigned" do
+ let(:unassigned_opportunity){ FactoryGirl.create(:opportunity, :assignee => nil)}
+ let(:assigned_opportunity){ FactoryGirl.create(:opportunity, :assignee => FactoryGirl.create(:user))}
+
+ it "includes unassigned opportunities" do
+ Opportunity.unassigned.should include(unassigned_opportunity)
+ end
+
+ it "does not include opportunities assigned to a user" do
+ Opportunity.unassigned.should_not include(assigned_opportunity)
+ end
+ end
end
describe "Attach" do
@@ -192,13 +205,13 @@
context "visible_on_dashboard" do
before :each do
@user = FactoryGirl.create(:user)
- @o1 = FactoryGirl.create(:opportunity, :user => @user, :stage => 'analysis')
- @o2 = FactoryGirl.create(:opportunity, :user => @user, :assignee => FactoryGirl.create(:user), :stage => 'analysis')
- @o3 = FactoryGirl.create(:opportunity, :user => FactoryGirl.create(:user), :assignee => @user, :stage => 'analysis')
- @o4 = FactoryGirl.create(:opportunity, :user => FactoryGirl.create(:user), :assignee => FactoryGirl.create(:user), :stage => 'analysis')
- @o5 = FactoryGirl.create(:opportunity, :user => FactoryGirl.create(:user), :assignee => @user, :stage => 'analysis')
- @o6 = FactoryGirl.create(:opportunity, :user => @user, :stage => 'lost')
- @o7 = FactoryGirl.create(:opportunity, :user => @user, :stage => 'won')
+ @o1 = FactoryGirl.create(:opportunity_in_pipeline, :user => @user)
+ @o2 = FactoryGirl.create(:opportunity_in_pipeline, :user => @user, :assignee => FactoryGirl.create(:user))
+ @o3 = FactoryGirl.create(:opportunity_in_pipeline, :user => FactoryGirl.create(:user), :assignee => @user)
+ @o4 = FactoryGirl.create(:opportunity_in_pipeline, :user => FactoryGirl.create(:user), :assignee => FactoryGirl.create(:user))
+ @o5 = FactoryGirl.create(:opportunity_in_pipeline, :user => FactoryGirl.create(:user), :assignee => @user)
+ @o6 = FactoryGirl.create(:opportunity, :assignee => @user, :stage => 'won')
+ @o7 = FactoryGirl.create(:opportunity, :assignee => @user, :stage => 'lost')
end
it "should show opportunities which have been created by the user and are unassigned" do
@@ -216,12 +229,9 @@
it "should not show opportunities which are created by the user but assigned" do
Opportunity.visible_on_dashboard(@user).should_not include(@o2)
end
-
- it "should not show opportunities which have been lost" do
+
+ it "does not include won or lost opportunities" do
Opportunity.visible_on_dashboard(@user).should_not include(@o6)
- end
-
- it "should not show opportunities which have been won" do
Opportunity.visible_on_dashboard(@user).should_not include(@o7)
end
end
@@ -236,6 +246,16 @@
end
end
+ context "by_amount" do
+ let(:o1) { FactoryGirl.create(:opportunity, :amount => 50000) }
+ let(:o2) { FactoryGirl.create(:opportunity, :amount => 10000) }
+ let(:o3) { FactoryGirl.create(:opportunity, :amount => 750000) }
+
+ it "should show opportunities ordered by amount" do
+ Opportunity.by_amount.should == [o3, o1, o2]
+ end
+ end
+
context "not lost" do
let(:o1) { FactoryGirl.create(:opportunity, :stage => 'won') }
let(:o2) { FactoryGirl.create(:opportunity, :stage => 'lost') }
View
6 spec/models/observers/entity_observer_spec.rb
@@ -6,6 +6,7 @@
let(:assignee) { FactoryGirl.create(:user) }
let(:assigner) { FactoryGirl.create(:user) }
let!(:entity) { FactoryGirl.build(entity_type, :user => assigner, :assignee => assignee) }
+ let(:mail) { mock('mail', :deliver => true) }
before :each do
PaperTrail.stub(:whodunnit).and_return(assigner)
@@ -16,7 +17,7 @@
end
it "sends notification to the assigned user for entity" do
- UserMailer.should_receive(:assigned_entity_notification).with(entity, assigner)
+ UserMailer.should_receive(:assigned_entity_notification).with(entity, assigner).and_return(mail)
end
it "does not notify anyone if the entity is created and assigned to no-one" do
@@ -34,13 +35,14 @@
let(:assignee) { FactoryGirl.create(:user) }
let(:assigner) { FactoryGirl.create(:user) }
let!(:entity) { FactoryGirl.create(entity_type, :user => FactoryGirl.create(:user)) }
+ let(:mail) { mock('mail', :deliver => true) }
before :each do
PaperTrail.stub(:whodunnit).and_return(assigner)
end
it "notifies the new owner if the entity is re-assigned" do
- UserMailer.should_receive(:assigned_entity_notification).with(entity, assigner)
+ UserMailer.should_receive(:assigned_entity_notification).with(entity, assigner).and_return(mail)
entity.update_attributes(:assignee => assignee)
end
View
48 spec/models/users/user_spec.rb
@@ -118,6 +118,54 @@
@user.should_not be_suspended
end
+ context "scopes" do
+ describe "have_assigned_opportunities" do
+ before :each do
+ @user1 = FactoryGirl.create(:user)
+ FactoryGirl.create(:open_opportunity, :assignee => @user1)
+
+ @user2 = FactoryGirl.create(:user)
+
+ @user3 = FactoryGirl.create(:user)
+ FactoryGirl.create(:opportunity, :assignee => @user3, :stage => 'won')
+
+ @user4 = FactoryGirl.create(:user)
+ FactoryGirl.create(:opportunity, :assignee => @user4, :stage => 'lost')
+ end
+
+ it "includes users with assigned opportunities" do
+ User.have_assigned_opportunities.should include(@user1)
+ end
+
+ it "excludes users without any assigned opportunities" do
+ User.have_assigned_opportunities.should_not include(@user2)
+ end
+
+ it "excludes users with opportunities that have been won or lost" do
+ User.have_assigned_opportunities.should_not include(@user3)
+ User.have_assigned_opportunities.should_not include(@user4)
+ end
+ end
+ end
+
+ context "instance methods" do
+ describe "assigned_opportunities" do
+ before :each do
+ @user = FactoryGirl.create(:user)
+ @opportunity1 = FactoryGirl.create(:opportunity, :assignee => @user)
+ @opportunity2 = FactoryGirl.create(:opportunity, :assignee => FactoryGirl.create(:user))
+ end
+
+ it "includes opportunities assigned to user" do
+ @user.assigned_opportunities.should include(@opportunity1)
+ end
+
+ it "does not include opportunities assigned to another user" do
+ @user.assigned_opportunities.should_not include(@opportunity2)
+ end
+ end
+ end
+
describe "Setting I18n.locale" do
before do
@user = FactoryGirl.create(:user)
Please sign in to comment.
Something went wrong with that request. Please try again.