ffcrm_cloudfuji 0.1.7 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/app/assets/javascripts/ffcrm_cloudfuji.js +1 -0
- data/app/assets/javascripts/lead_scoring.js.coffee +42 -0
- data/app/assets/stylesheets/ffcrm_cloudfuji.css +3 -0
- data/app/assets/stylesheets/lead_scoring.css.scss +47 -0
- data/app/controllers/admin/lead_scoring_controller.rb +49 -0
- data/app/helpers/admin/lead_scoring_helper.rb +10 -0
- data/app/models/lead_scoring_rule.rb +6 -0
- data/app/models/lead_scoring_rule_count.rb +4 -0
- data/app/views/admin/lead_scoring/_errors.html.haml +8 -0
- data/app/views/admin/lead_scoring/_form.html.haml +14 -0
- data/app/views/admin/lead_scoring/index.html.haml +19 -0
- data/app/views/leads/_lead.html.haml +66 -0
- data/app/views/leads/_sidebar_show_lead_scoring.html.haml +8 -0
- data/config/initializers/inflections.rb +3 -0
- data/config/routes.rb +20 -11
- data/db/migrate/20120515194445_add_lead_scoring_rules.rb +25 -0
- data/ffcrm_cloudfuji.gemspec +2 -2
- data/lib/fat_free_crm/cloudfuji/event_observers/lead_scoring_observer.rb +38 -0
- data/lib/fat_free_crm/cloudfuji/view_hooks.rb +12 -0
- data/lib/fat_free_crm/cloudfuji.rb +9 -1
- data/lib/ffcrm_cloudfuji/engine.rb +21 -0
- data/lib/{fat_free_crm/cloudfuji → ffcrm_cloudfuji}/version.rb +1 -1
- data/lib/ffcrm_cloudfuji.rb +1 -1
- data/spec/event_observers/lead_scoring_observer_spec.rb +87 -0
- data/spec/factories/lead_scoring_factories.rb +9 -0
- data/spec/internal/db/schema.rb +17 -1
- data/spec/spec_helper.rb +2 -0
- metadata +85 -83
- data/lib/fat_free_crm/cloudfuji/engine.rb +0 -6
@@ -0,0 +1 @@
|
|
1
|
+
//= require lead_scoring
|
@@ -0,0 +1,42 @@
|
|
1
|
+
(($) ->
|
2
|
+
class @LeadScoring
|
3
|
+
constructor: (@templates = {}) ->
|
4
|
+
|
5
|
+
add_fields: (button, content) ->
|
6
|
+
new_id = new Date().getTime()
|
7
|
+
regexp = new RegExp('new_lead_scoring_rule', 'g')
|
8
|
+
$('ul#lead_scoring_rules').append(content.replace(regexp, new_id))
|
9
|
+
# Setup event autocomplete for new field
|
10
|
+
this.event_autocomplete('input#lead_scoring_rules_'+new_id+'_event')
|
11
|
+
|
12
|
+
remove_fields: (button) ->
|
13
|
+
container = $(button).closest('li.lead_scoring_rule')
|
14
|
+
index = container.data('index')
|
15
|
+
# If rule has no id, just remove
|
16
|
+
if $("#lead_scoring_rules_"+index+"_id").length == 0
|
17
|
+
container.remove()
|
18
|
+
else
|
19
|
+
# If rule has an id, mark container as hidden and add _destroy field
|
20
|
+
container.hide()
|
21
|
+
container.append($('<input type="text" name="lead_scoring_rules['+index+'][_destroy]" value="yes">'))
|
22
|
+
|
23
|
+
event_autocomplete: (selector = 'input.lead_scoring_event') ->
|
24
|
+
$(selector).autocomplete({source: observed_cloudfuji_events, minLength: 0})
|
25
|
+
# Show all events on focus, if input is empty
|
26
|
+
$(selector).focus ->
|
27
|
+
$(this).autocomplete "search", "" if $(this).val() == ""
|
28
|
+
|
29
|
+
$(document).ready ->
|
30
|
+
lead_scoring = new LeadScoring()
|
31
|
+
# Initialize autocomplete for events
|
32
|
+
lead_scoring.event_autocomplete()
|
33
|
+
|
34
|
+
$("button.add_lead_scoring_rule").live "click", ->
|
35
|
+
lead_scoring.add_fields this, $(this).data("content")
|
36
|
+
false
|
37
|
+
|
38
|
+
$(".remove_lead_scoring_rule").live "click", ->
|
39
|
+
lead_scoring.remove_fields this
|
40
|
+
false
|
41
|
+
|
42
|
+
) jQuery
|
@@ -0,0 +1,47 @@
|
|
1
|
+
li.lead_scoring_rule {
|
2
|
+
font-size: 13px;
|
3
|
+
|
4
|
+
&:last-child {
|
5
|
+
border-bottom: none;
|
6
|
+
}
|
7
|
+
|
8
|
+
input {
|
9
|
+
font-size: 13px;
|
10
|
+
margin: 0 4px;
|
11
|
+
vertical-align: middle;
|
12
|
+
margin-bottom: 3px;
|
13
|
+
}
|
14
|
+
.lead_scoring_event { width: 160px; }
|
15
|
+
.lead_scoring_once {
|
16
|
+
width: 14px;
|
17
|
+
margin-left: 2px;
|
18
|
+
}
|
19
|
+
.lead_scoring_match { width: 190px; }
|
20
|
+
.lead_scoring_points { width: 35px; }
|
21
|
+
|
22
|
+
.field_with_errors {
|
23
|
+
display: inline;
|
24
|
+
input {
|
25
|
+
background-color: #ffdddd;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
ul#lead_scoring_rules {
|
31
|
+
a.remove_lead_scoring_rule {
|
32
|
+
margin-right: 6px;
|
33
|
+
img {
|
34
|
+
vertical-align: middle;
|
35
|
+
margin-bottom: 4px;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
.buttonbar.lead_scoring_buttons {
|
41
|
+
width: 845px;
|
42
|
+
float: left;
|
43
|
+
font-size: 13px;
|
44
|
+
input[type='submit'], button { font-size: 14px; }
|
45
|
+
.save_or_cancel { float: right; }
|
46
|
+
button { float: left; }
|
47
|
+
}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class Admin::LeadScoringController < Admin::ApplicationController
|
2
|
+
before_filter :require_user
|
3
|
+
before_filter "set_current_tab('admin/lead_scoring')", :only => [ :index, :update ]
|
4
|
+
|
5
|
+
# GET /admin/lead_scoring
|
6
|
+
#----------------------------------------------------------------------------
|
7
|
+
def index
|
8
|
+
@lead_scoring_rules = LeadScoringRule.all
|
9
|
+
@lead_scoring_rules << LeadScoringRule.new if @lead_scoring_rules.empty?
|
10
|
+
|
11
|
+
respond_to do |format|
|
12
|
+
format.html # index.html.haml
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# PUT /admin/lead_scoring
|
17
|
+
#----------------------------------------------------------------------------
|
18
|
+
def update
|
19
|
+
# Create rules without ids,
|
20
|
+
# destroy rules with '_destroy' param,
|
21
|
+
# update rules with ids
|
22
|
+
@lead_scoring_rules = []
|
23
|
+
params[:lead_scoring_rules].each do |index, data|
|
24
|
+
if data["id"].blank?
|
25
|
+
@lead_scoring_rules << LeadScoringRule.create(data)
|
26
|
+
else
|
27
|
+
if rule = LeadScoringRule.find_by_id(data["id"])
|
28
|
+
if data["_destroy"]
|
29
|
+
rule.destroy
|
30
|
+
else
|
31
|
+
rule.update_attributes data
|
32
|
+
@lead_scoring_rules << rule
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
if @lead_scoring_rules.all?(&:valid?)
|
39
|
+
flash[:notice] = "All rules were saved successfully."
|
40
|
+
else
|
41
|
+
flash[:error] = render_to_string(:partial => "errors").html_safe
|
42
|
+
end
|
43
|
+
|
44
|
+
respond_to do |format|
|
45
|
+
format.html { render "index" }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Admin::LeadScoringHelper
|
2
|
+
|
3
|
+
def add_lead_scoring_rule_button(f)
|
4
|
+
fields = f.fields_for "new_lead_scoring_rule", LeadScoringRule.new(:points => 0) do |builder|
|
5
|
+
render("form", :r => builder, :index => "new_lead_scoring_rule")
|
6
|
+
end
|
7
|
+
button_tag 'Add New Rule', :class => "add_lead_scoring_rule", "data-content" => "#{fields}"
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
- invalid_rules = @lead_scoring_rules.select{|r| r.errors.any? }
|
2
|
+
- if invalid_rules.any?
|
3
|
+
There were (#{invalid_rules.size}) invalid rules:
|
4
|
+
%br
|
5
|
+
- invalid_rules.each_with_index do |rule, i|
|
6
|
+
%strong= (i + 1).to_s << "."
|
7
|
+
= rule.errors.full_messages.join(", ")
|
8
|
+
%br
|
@@ -0,0 +1,14 @@
|
|
1
|
+
%li.lead_scoring_rule{"data-index" => index}
|
2
|
+
= link_to image_tag('delete.png', :size => '16x16', :alt => 'Remove Rule'), nil, :class => "remove_lead_scoring_rule"
|
3
|
+
|
4
|
+
= r.hidden_field(:id) unless r.object.new_record?
|
5
|
+
|
6
|
+
If event:
|
7
|
+
= r.text_field :event, :class => "lead_scoring_event"
|
8
|
+
matches:
|
9
|
+
= r.text_field :match, :class => "lead_scoring_match"
|
10
|
+
change score by
|
11
|
+
= r.text_field :points, :class => "lead_scoring_points"
|
12
|
+
points.
|
13
|
+
Only once per lead:
|
14
|
+
= r.check_box :once, :class => "lead_scoring_once"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
:javascript
|
2
|
+
var observed_cloudfuji_events = #{Cloudfuji::Data.observed_events.map(&:to_s).inspect};
|
3
|
+
|
4
|
+
.title
|
5
|
+
Lead Scoring
|
6
|
+
|
7
|
+
.list
|
8
|
+
= form_for :lead_scoring_rules do |f|
|
9
|
+
%ul#lead_scoring_rules
|
10
|
+
- @lead_scoring_rules.each_with_index do |rule, i|
|
11
|
+
= f.fields_for i.to_s, rule do |r|
|
12
|
+
= render "form", :r => r, :index => i
|
13
|
+
|
14
|
+
.buttonbar.lead_scoring_buttons
|
15
|
+
= add_lead_scoring_rule_button(f)
|
16
|
+
.save_or_cancel
|
17
|
+
= submit_tag 'Save', :name => :save
|
18
|
+
#{t :or}
|
19
|
+
= link_to "Cancel", admin_lead_scoring_index_path
|
@@ -0,0 +1,66 @@
|
|
1
|
+
%li.highlight[lead]
|
2
|
+
- if lead.status
|
3
|
+
.strip{:class => lead.status}= t lead.status
|
4
|
+
- else
|
5
|
+
.strip{:style => "color: gray;"}= t :other
|
6
|
+
|
7
|
+
%ul.tools
|
8
|
+
- if can?(:update, lead)
|
9
|
+
%li= link_to_edit(lead)
|
10
|
+
|
11
|
+
- if %w(converted rejected).include?(lead.status)
|
12
|
+
%li
|
13
|
+
%font{ :color => 'silver' }= t :convert
|
14
|
+
- elsif can?(:update, lead)
|
15
|
+
%li= link_to_convert(lead)
|
16
|
+
|
17
|
+
- if lead.status == "rejected"
|
18
|
+
%li
|
19
|
+
%font{ :color => 'silver' }= t :reject
|
20
|
+
- elsif can?(:update, lead)
|
21
|
+
%li= link_to_reject(lead)
|
22
|
+
|
23
|
+
- if shown_on_landing_page?
|
24
|
+
%li= link_to_discard(lead)
|
25
|
+
|
26
|
+
- if can?(:destroy, lead)
|
27
|
+
%li= link_to_delete(lead)
|
28
|
+
|
29
|
+
.indent
|
30
|
+
= link_to_if can?(:read, lead), lead.full_name(@current_user.preference[:leads_naming]), lead
|
31
|
+
%tt
|
32
|
+
- if lead.company? && lead.title?
|
33
|
+
= t(:works_at, :job_title => lead.title, :company => lead.company)
|
34
|
+
- else
|
35
|
+
= lead.company if lead.company?
|
36
|
+
= lead.title if lead.title?
|
37
|
+
- if lead.referred_by?
|
38
|
+
–
|
39
|
+
== #{t :referred_by_small} #{lead.referred_by}
|
40
|
+
|
41
|
+
- unless @current_user.preference[:leads_outline] == "brief"
|
42
|
+
%dt
|
43
|
+
= stars_for(lead)
|
44
|
+
|
|
45
|
+
- if can?(:read, lead)
|
46
|
+
- if lead.email.present?
|
47
|
+
= link_to_email(lead.email)
|
48
|
+
|
|
49
|
+
- if lead.phone.present?
|
50
|
+
== #{t :phone_small}:
|
51
|
+
%b= lead.phone
|
52
|
+
|
|
53
|
+
- if lead.mobile.present?
|
54
|
+
== #{t :mobile_small}:
|
55
|
+
%b= lead.mobile
|
56
|
+
|
|
57
|
+
== Score:
|
58
|
+
%b= lead.score
|
59
|
+
|
|
60
|
+
= t(:added_ago, time_ago_in_words(lead.created_at))
|
61
|
+
|
62
|
+
- if lead.tag_list.present?
|
63
|
+
%dt
|
64
|
+
.tags= tags_for_index(lead)
|
65
|
+
|
66
|
+
= hook(:lead_bottom, self, :lead => lead)
|
data/config/routes.rb
CHANGED
@@ -1,14 +1,23 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
r.set.routes.reject! {|r| %w(login logout).include? r.name }
|
4
|
-
r.set.send "clear_cache!"
|
5
|
-
|
6
|
-
begin
|
7
|
-
Rails.application.routes.draw do
|
1
|
+
Rails.application.routes.draw do
|
2
|
+
begin
|
8
3
|
cloudfuji_routes
|
9
|
-
|
4
|
+
|
5
|
+
if Cloudfuji::Platform.on_cloudfuji?
|
6
|
+
# Remove existing authentication routes
|
7
|
+
r = Rails.application.routes
|
8
|
+
r.set.routes.reject! {|r| %w(login logout).include? r.name }
|
9
|
+
r.set.send "clear_cache!"
|
10
|
+
# Setup cloudfuji authentication routes
|
11
|
+
cloudfuji_authentication_routes
|
12
|
+
end
|
13
|
+
rescue => e
|
14
|
+
puts "Error loading the Cloudfuji routes:"
|
15
|
+
puts "#{e.inspect}"
|
16
|
+
end
|
17
|
+
|
18
|
+
namespace :admin do
|
19
|
+
resources :lead_scoring, :only => :index do
|
20
|
+
post :update, :on => :collection
|
21
|
+
end
|
10
22
|
end
|
11
|
-
rescue => e
|
12
|
-
puts "Error loading the Cloudfuji routes:"
|
13
|
-
puts "#{e.inspect}"
|
14
23
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class AddLeadScoringRules < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
create_table :lead_scoring_rules do |t|
|
4
|
+
t.string :event
|
5
|
+
t.string :match
|
6
|
+
t.integer :points, :default => 0
|
7
|
+
t.boolean :once
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
|
11
|
+
create_table :lead_scoring_rule_counts do |t|
|
12
|
+
t.integer :lead_id
|
13
|
+
t.integer :lead_scoring_rule_id
|
14
|
+
t.integer :count, :default => 0
|
15
|
+
end
|
16
|
+
|
17
|
+
add_column :leads, :score, :integer, :default => 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def down
|
21
|
+
drop_table :lead_scoring_rules
|
22
|
+
drop_table :lead_scoring_rule_counts
|
23
|
+
remove_column :leads, :score
|
24
|
+
end
|
25
|
+
end
|
data/ffcrm_cloudfuji.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
$:.push File.expand_path('../lib', __FILE__)
|
3
|
-
require '
|
3
|
+
require 'ffcrm_cloudfuji/version'
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = 'ffcrm_cloudfuji'
|
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.add_development_dependency 'capybara'
|
17
17
|
s.add_development_dependency 'combustion'
|
18
18
|
|
19
|
-
s.add_dependency 'cloudfuji', '>= 0.0.
|
19
|
+
s.add_dependency 'cloudfuji', '>= 0.0.42'
|
20
20
|
s.add_dependency 'authlogic_cloudfuji', '~> 0.9'
|
21
21
|
|
22
22
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module FatFreeCRM
|
2
|
+
module Cloudfuji
|
3
|
+
module EventObservers
|
4
|
+
class LeadScoringObserver < ::Cloudfuji::EventObserver
|
5
|
+
# Fire for all events
|
6
|
+
def catch_all
|
7
|
+
data = params['data']
|
8
|
+
email = data['email'] || data['recipient']
|
9
|
+
# Look up Lead by email address
|
10
|
+
if lead = Lead.find_by_email(email)
|
11
|
+
event_name = "#{params['category']}_#{params['event']}"
|
12
|
+
|
13
|
+
LeadScoringRule.find_all_by_event(event_name).each do |rule|
|
14
|
+
# Find how many times this rule has been applied to this Lead
|
15
|
+
count = LeadScoringRuleCount.find_by_lead_id_and_lead_scoring_rule_id(lead, rule) ||
|
16
|
+
LeadScoringRuleCount.new(:lead => lead, :lead_scoring_rule => rule)
|
17
|
+
|
18
|
+
# Don't apply this rule more than once if :once flag is set
|
19
|
+
unless rule.once && count.count > 0
|
20
|
+
# If :match is present, only apply the rule if data matches string
|
21
|
+
if rule.match.blank? || params['data'].inspect.include?(rule.match)
|
22
|
+
lead.without_versioning do
|
23
|
+
lead.update_attribute :score, lead.score + rule.points
|
24
|
+
end
|
25
|
+
# Add history event to lead, to record change of score
|
26
|
+
lead.versions.create! :event => "Rule for '#{event_name}': Score changed by #{rule.points} points. (New total: #{lead.score})"
|
27
|
+
# Increment and save count of rule/lead applications
|
28
|
+
count.count += 1
|
29
|
+
count.save
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module FatFreeCRM
|
2
|
+
module Cloudfuji
|
3
|
+
class CloudfujiViewHooks < ::FatFreeCRM::Callback::Base
|
4
|
+
|
5
|
+
# Add lead scoring to summary on lead show page
|
6
|
+
def show_lead_sidebar_bottom(view, context)
|
7
|
+
view.render(:partial => 'leads/sidebar_show_lead_scoring', :locals => {:lead => context[:lead]})
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -4,11 +4,12 @@ module FatFreeCRM
|
|
4
4
|
def enable_cloudfuji!
|
5
5
|
load_observers!
|
6
6
|
extend_user!
|
7
|
+
extend_lead!
|
7
8
|
setup_authentication!
|
8
9
|
end
|
9
10
|
|
10
11
|
def extend_user!
|
11
|
-
puts "Extending the
|
12
|
+
puts "Extending the User model"
|
12
13
|
User.instance_eval do
|
13
14
|
include ::Cloudfuji::UserHelper
|
14
15
|
|
@@ -33,6 +34,13 @@ module FatFreeCRM
|
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
37
|
+
def extend_lead!
|
38
|
+
puts "Extending the Lead model"
|
39
|
+
Lead.instance_eval do
|
40
|
+
has_many :lead_scoring_rule_counts
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
36
44
|
def load_observers!
|
37
45
|
Dir[File.expand_path("../cloudfuji/event_observers/*.rb", __FILE__)].each { |file| require file }
|
38
46
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module FatFreeCRM
|
2
|
+
module Cloudfuji
|
3
|
+
class Engine < Rails::Engine
|
4
|
+
config.to_prepare do
|
5
|
+
require 'fat_free_crm/cloudfuji/view_hooks'
|
6
|
+
|
7
|
+
# Add Lead Scoring tab
|
8
|
+
begin
|
9
|
+
unless FatFreeCRM::Tabs.admin.any? {|t| t[:text] == "Lead Scoring" }
|
10
|
+
FatFreeCRM::Tabs.admin << {
|
11
|
+
:text => "Lead Scoring",
|
12
|
+
:url => { :controller => "admin/lead_scoring" }
|
13
|
+
}
|
14
|
+
end
|
15
|
+
rescue TypeError
|
16
|
+
puts "You must migrate your settings table."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/ffcrm_cloudfuji.rb
CHANGED
@@ -0,0 +1,87 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
include FatFreeCRM::Cloudfuji::EventObservers
|
4
|
+
|
5
|
+
describe LeadScoringObserver do
|
6
|
+
before do
|
7
|
+
@observer = LeadScoringObserver.new
|
8
|
+
@observer.params = {
|
9
|
+
'category' => 'customer',
|
10
|
+
'event' => 'had_tea',
|
11
|
+
"data" => {
|
12
|
+
"email" => "test_lead@example.com",
|
13
|
+
"type_of_tea" => "Jasmine"
|
14
|
+
}
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def find_rule_count(lead, rule)
|
19
|
+
LeadScoringRuleCount.find_by_lead_id_and_lead_scoring_rule_id(lead, rule)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should increment a lead's score when an event is fired" do
|
23
|
+
user = FactoryGirl.create(:user, :ido_id => "1234")
|
24
|
+
@lead = FactoryGirl.create(:lead, :email => 'test_lead@example.com', :user => user, :campaign => nil)
|
25
|
+
|
26
|
+
@rule = FactoryGirl.create :lead_scoring_rule,
|
27
|
+
:event => "customer_had_tea",
|
28
|
+
:points => 20
|
29
|
+
|
30
|
+
@observer.catch_all
|
31
|
+
@lead.reload
|
32
|
+
@lead.versions.last.event.should include("Rule for 'customer_had_tea': Score changed by 20 points")
|
33
|
+
@lead.score.should == 20
|
34
|
+
find_rule_count(@lead, @rule).count.should == 1
|
35
|
+
|
36
|
+
# Should increment score twice
|
37
|
+
@observer.catch_all
|
38
|
+
@lead.reload
|
39
|
+
@lead.score.should == 40
|
40
|
+
find_rule_count(@lead, @rule).count.should == 2
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should increment a lead's score only when matching data is present" do
|
44
|
+
user = FactoryGirl.create(:user, :ido_id => "1234")
|
45
|
+
@lead = FactoryGirl.create(:lead, :email => 'test_lead@example.com', :user => user, :campaign => nil)
|
46
|
+
|
47
|
+
@rule = FactoryGirl.create :lead_scoring_rule,
|
48
|
+
:event => "customer_had_tea",
|
49
|
+
:points => 15,
|
50
|
+
:match => "Russian Earl Grey"
|
51
|
+
|
52
|
+
@observer.catch_all
|
53
|
+
@lead.reload
|
54
|
+
@lead.score.should == 0
|
55
|
+
# No LeadScoringRuleCount should be created yet
|
56
|
+
find_rule_count(@lead, @rule).should == nil
|
57
|
+
|
58
|
+
@observer.params['data']['type_of_tea'] = "Russian Earl Grey"
|
59
|
+
@observer.catch_all
|
60
|
+
|
61
|
+
@lead.reload
|
62
|
+
@lead.score.should == 15
|
63
|
+
find_rule_count(@lead, @rule).count.should == 1
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should not decrement a lead's score twice if the rule should only be applied once" do
|
67
|
+
user = FactoryGirl.create(:user, :ido_id => "1234")
|
68
|
+
@lead = FactoryGirl.create(:lead, :email => 'test_lead@example.com', :user => user, :campaign => nil)
|
69
|
+
|
70
|
+
@rule = FactoryGirl.create :lead_scoring_rule,
|
71
|
+
:event => "customer_had_tea",
|
72
|
+
:points => -10,
|
73
|
+
:once => true
|
74
|
+
|
75
|
+
@observer.catch_all
|
76
|
+
@lead.reload
|
77
|
+
@lead.versions.last.event.should include("Rule for 'customer_had_tea': Score changed by -10 points")
|
78
|
+
@lead.score.should == -10
|
79
|
+
find_rule_count(@lead, @rule).count.should == 1
|
80
|
+
|
81
|
+
# Should only increment score once
|
82
|
+
@observer.catch_all
|
83
|
+
@lead.reload
|
84
|
+
@lead.score.should == -10
|
85
|
+
find_rule_count(@lead, @rule).count.should == 1
|
86
|
+
end
|
87
|
+
end
|
data/spec/internal/db/schema.rb
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended to check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(:version =>
|
14
|
+
ActiveRecord::Schema.define(:version => 20120515194445) do
|
15
15
|
|
16
16
|
create_table "account_contacts", :force => true do |t|
|
17
17
|
t.integer "account_id"
|
@@ -227,6 +227,21 @@ ActiveRecord::Schema.define(:version => 20120510025219) do
|
|
227
227
|
add_index "fields", ["field_group_id"], :name => "index_fields_on_field_group_id"
|
228
228
|
add_index "fields", ["name"], :name => "index_fields_on_name"
|
229
229
|
|
230
|
+
create_table "lead_scoring_rule_counts", :force => true do |t|
|
231
|
+
t.integer "lead_id"
|
232
|
+
t.integer "lead_scoring_rule_id"
|
233
|
+
t.integer "count", :default => 0
|
234
|
+
end
|
235
|
+
|
236
|
+
create_table "lead_scoring_rules", :force => true do |t|
|
237
|
+
t.string "event"
|
238
|
+
t.string "match"
|
239
|
+
t.integer "points", :default => 0
|
240
|
+
t.boolean "once"
|
241
|
+
t.datetime "created_at", :null => false
|
242
|
+
t.datetime "updated_at", :null => false
|
243
|
+
end
|
244
|
+
|
230
245
|
create_table "leads", :force => true do |t|
|
231
246
|
t.integer "user_id"
|
232
247
|
t.integer "campaign_id"
|
@@ -255,6 +270,7 @@ ActiveRecord::Schema.define(:version => 20120510025219) do
|
|
255
270
|
t.string "background_info"
|
256
271
|
t.string "skype", :limit => 128
|
257
272
|
t.text "subscribed_users"
|
273
|
+
t.integer "score", :default => 0
|
258
274
|
end
|
259
275
|
|
260
276
|
add_index "leads", ["assigned_to"], :name => "index_leads_on_assigned_to"
|
data/spec/spec_helper.rb
CHANGED
@@ -11,6 +11,8 @@ require 'ffaker'
|
|
11
11
|
# Load factories from fat_free_crm
|
12
12
|
ffcrm_spec = Gem::Specification.find_by_name("fat_free_crm")
|
13
13
|
Dir[ffcrm_spec.gem_dir + "/spec/factories/*.rb"].each {|factory| require factory }
|
14
|
+
# Load factories from spec/factories
|
15
|
+
Dir[File.expand_path("../factories/*.rb", __FILE__)].each {|factory| require factory }
|
14
16
|
|
15
17
|
Combustion.initialize!
|
16
18
|
# Reload User model after schema is loaded,
|
metadata
CHANGED
@@ -1,160 +1,162 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: ffcrm_cloudfuji
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.7
|
3
|
+
version: !ruby/object:Gem::Version
|
5
4
|
prerelease:
|
5
|
+
version: 0.2.0
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Sean Grove
|
9
9
|
- Nathan Broadbent
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
|
14
|
+
date: 2012-05-17 00:00:00 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
16
17
|
name: rspec-rails
|
17
|
-
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
18
19
|
none: false
|
19
|
-
requirements:
|
20
|
+
requirements:
|
20
21
|
- - ~>
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version:
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "2.6"
|
23
24
|
type: :development
|
24
25
|
prerelease: false
|
25
|
-
version_requirements:
|
26
|
-
|
27
|
-
requirements:
|
28
|
-
- - ~>
|
29
|
-
- !ruby/object:Gem::Version
|
30
|
-
version: '2.6'
|
31
|
-
- !ruby/object:Gem::Dependency
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
32
28
|
name: capybara
|
33
|
-
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
34
30
|
none: false
|
35
|
-
requirements:
|
36
|
-
- -
|
37
|
-
- !ruby/object:Gem::Version
|
38
|
-
version:
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
39
35
|
type: :development
|
40
36
|
prerelease: false
|
41
|
-
version_requirements:
|
42
|
-
|
43
|
-
requirements:
|
44
|
-
- - ! '>='
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: '0'
|
47
|
-
- !ruby/object:Gem::Dependency
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
48
39
|
name: combustion
|
49
|
-
requirement: !ruby/object:Gem::Requirement
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
50
41
|
none: false
|
51
|
-
requirements:
|
52
|
-
- -
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version:
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
55
46
|
type: :development
|
56
47
|
prerelease: false
|
57
|
-
version_requirements:
|
58
|
-
|
59
|
-
requirements:
|
60
|
-
- - ! '>='
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
version: '0'
|
63
|
-
- !ruby/object:Gem::Dependency
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
64
50
|
name: cloudfuji
|
65
|
-
requirement: !ruby/object:Gem::Requirement
|
51
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
66
52
|
none: false
|
67
|
-
requirements:
|
68
|
-
- -
|
69
|
-
- !ruby/object:Gem::Version
|
70
|
-
version: 0.0.
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 0.0.42
|
71
57
|
type: :runtime
|
72
58
|
prerelease: false
|
73
|
-
version_requirements:
|
74
|
-
|
75
|
-
requirements:
|
76
|
-
- - ! '>='
|
77
|
-
- !ruby/object:Gem::Version
|
78
|
-
version: 0.0.41
|
79
|
-
- !ruby/object:Gem::Dependency
|
59
|
+
version_requirements: *id004
|
60
|
+
- !ruby/object:Gem::Dependency
|
80
61
|
name: authlogic_cloudfuji
|
81
|
-
requirement: !ruby/object:Gem::Requirement
|
62
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
82
63
|
none: false
|
83
|
-
requirements:
|
64
|
+
requirements:
|
84
65
|
- - ~>
|
85
|
-
- !ruby/object:Gem::Version
|
86
|
-
version:
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0.9"
|
87
68
|
type: :runtime
|
88
69
|
prerelease: false
|
89
|
-
version_requirements:
|
90
|
-
none: false
|
91
|
-
requirements:
|
92
|
-
- - ~>
|
93
|
-
- !ruby/object:Gem::Version
|
94
|
-
version: '0.9'
|
70
|
+
version_requirements: *id005
|
95
71
|
description: Integrates Fat Free CRM with the Cloudfuji hosting platform.
|
96
72
|
email: s@cloudfuji.com
|
97
73
|
executables: []
|
74
|
+
|
98
75
|
extensions: []
|
76
|
+
|
99
77
|
extra_rdoc_files: []
|
100
|
-
|
78
|
+
|
79
|
+
files:
|
101
80
|
- .gitignore
|
102
81
|
- .travis.yml
|
103
82
|
- Gemfile
|
104
83
|
- MIT-LICENSE
|
105
84
|
- README
|
106
85
|
- Rakefile
|
86
|
+
- app/assets/javascripts/ffcrm_cloudfuji.js
|
87
|
+
- app/assets/javascripts/lead_scoring.js.coffee
|
88
|
+
- app/assets/stylesheets/ffcrm_cloudfuji.css
|
89
|
+
- app/assets/stylesheets/lead_scoring.css.scss
|
90
|
+
- app/controllers/admin/lead_scoring_controller.rb
|
91
|
+
- app/helpers/admin/lead_scoring_helper.rb
|
92
|
+
- app/models/lead_scoring_rule.rb
|
93
|
+
- app/models/lead_scoring_rule_count.rb
|
94
|
+
- app/views/admin/lead_scoring/_errors.html.haml
|
95
|
+
- app/views/admin/lead_scoring/_form.html.haml
|
96
|
+
- app/views/admin/lead_scoring/index.html.haml
|
97
|
+
- app/views/leads/_lead.html.haml
|
98
|
+
- app/views/leads/_sidebar_show_lead_scoring.html.haml
|
107
99
|
- config.ru
|
108
100
|
- config/database.yml
|
109
101
|
- config/initializers/cloudfuji.rb
|
102
|
+
- config/initializers/inflections.rb
|
110
103
|
- config/routes.rb
|
111
104
|
- config/settings.yml
|
112
105
|
- db/migrate/20120227184555_add_cloudfuji_fields_to_users.rb
|
106
|
+
- db/migrate/20120515194445_add_lead_scoring_rules.rb
|
113
107
|
- ffcrm_cloudfuji.gemspec
|
114
108
|
- install.rb
|
115
109
|
- lib/fat_free_crm/cloudfuji.rb
|
116
|
-
- lib/fat_free_crm/cloudfuji/engine.rb
|
117
110
|
- lib/fat_free_crm/cloudfuji/event_observers/app_observer.rb
|
118
111
|
- lib/fat_free_crm/cloudfuji/event_observers/customer_observer.rb
|
119
112
|
- lib/fat_free_crm/cloudfuji/event_observers/email_observer.rb
|
113
|
+
- lib/fat_free_crm/cloudfuji/event_observers/lead_scoring_observer.rb
|
120
114
|
- lib/fat_free_crm/cloudfuji/event_observers/payment_observer.rb
|
121
115
|
- lib/fat_free_crm/cloudfuji/event_observers/user_observer.rb
|
122
|
-
- lib/fat_free_crm/cloudfuji/
|
116
|
+
- lib/fat_free_crm/cloudfuji/view_hooks.rb
|
123
117
|
- lib/ffcrm_cloudfuji.rb
|
118
|
+
- lib/ffcrm_cloudfuji/engine.rb
|
119
|
+
- lib/ffcrm_cloudfuji/version.rb
|
124
120
|
- spec/event_observers/app_observer_spec.rb
|
125
121
|
- spec/event_observers/customer_observer_spec.rb
|
122
|
+
- spec/event_observers/lead_scoring_observer_spec.rb
|
123
|
+
- spec/factories/lead_scoring_factories.rb
|
126
124
|
- spec/internal/config/database.yml
|
127
125
|
- spec/internal/db/schema.rb
|
128
126
|
- spec/spec_helper.rb
|
129
127
|
- uninstall.rb
|
130
128
|
homepage: http://cloudfuji.com
|
131
129
|
licenses: []
|
130
|
+
|
132
131
|
post_install_message:
|
133
132
|
rdoc_options: []
|
134
|
-
|
133
|
+
|
134
|
+
require_paths:
|
135
135
|
- lib
|
136
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
137
|
none: false
|
138
|
-
requirements:
|
139
|
-
- -
|
140
|
-
- !ruby/object:Gem::Version
|
141
|
-
|
142
|
-
segments:
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
hash: 3520181376732569927
|
142
|
+
segments:
|
143
143
|
- 0
|
144
|
-
|
145
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
version: "0"
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
146
|
none: false
|
147
|
-
requirements:
|
148
|
-
- -
|
149
|
-
- !ruby/object:Gem::Version
|
150
|
-
|
151
|
-
segments:
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
hash: 3520181376732569927
|
151
|
+
segments:
|
152
152
|
- 0
|
153
|
-
|
153
|
+
version: "0"
|
154
154
|
requirements: []
|
155
|
+
|
155
156
|
rubyforge_project:
|
156
|
-
rubygems_version: 1.8.
|
157
|
+
rubygems_version: 1.8.17
|
157
158
|
signing_key:
|
158
159
|
specification_version: 3
|
159
160
|
summary: Fat Free CRM - Cloudfuji Integration
|
160
161
|
test_files: []
|
162
|
+
|