ffcrm_cloudfuji 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|