e9_crm 0.1.21 → 0.1.22
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/controllers/e9_crm/campaigns_controller.rb +60 -17
- data/app/controllers/e9_crm/contacts_controller.rb +5 -0
- data/app/controllers/e9_crm/deals_controller.rb +14 -36
- data/app/controllers/e9_crm/offers_controller.rb +2 -2
- data/app/controllers/e9_crm/resources_controller.rb +4 -0
- data/app/helpers/e9_crm/campaigns_helper.rb +14 -3
- data/app/helpers/e9_crm/deals_helper.rb +17 -18
- data/app/models/campaign.rb +74 -6
- data/app/models/contact.rb +16 -1
- data/app/models/dated_cost.rb +1 -0
- data/app/models/deal.rb +8 -58
- data/app/models/no_campaign.rb +1 -1
- data/app/models/page_view.rb +6 -5
- data/app/views/e9_crm/affiliate_campaigns/_form_inner.html.haml +7 -1
- data/app/views/e9_crm/{deals → campaigns}/_reports_table.html.haml +7 -5
- data/app/views/e9_crm/campaigns/_table.html.haml +1 -1
- data/app/views/e9_crm/campaigns/reports.html.haml +17 -9
- data/app/views/e9_crm/{deals → campaigns}/reports.js.erb +0 -0
- data/app/views/e9_crm/contacts/_form_inner.html.haml +0 -3
- data/app/views/e9_crm/contacts/_table.html.haml +1 -1
- data/app/views/e9_crm/contacts/edit.html.haml +6 -0
- data/app/views/e9_crm/contacts/show.html.haml +3 -3
- data/app/views/e9_crm/deals/_form_inner.html.haml +1 -17
- data/app/views/e9_crm/deals/_header.html.haml +2 -0
- data/app/views/e9_crm/deals/_leads_header.html.haml +1 -1
- data/app/views/e9_crm/deals/_leads_table.html.haml +1 -1
- data/app/views/e9_crm/deals/_table.html.haml +1 -1
- data/app/views/e9_crm/deals/show.html.haml +23 -4
- data/app/views/e9_crm/leads/_form.html.haml +5 -5
- data/app/views/e9_crm/offers/_form_inner.html.haml +0 -1
- data/app/views/e9_crm/page_views/_table.html.haml +1 -1
- data/app/views/e9_crm/resources/_table.html.haml +1 -1
- data/app/views/e9_crm/resources/index.html.haml +1 -1
- data/app/views/e9_crm/sales_campaigns/_form_inner.html.haml +8 -2
- data/app/views/record_attributes/_user.html.haml +2 -2
- data/config/locales/e9.en.yml +2 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +1 -1
- data/lib/e9_crm/controller.rb +2 -2
- data/lib/e9_crm/rack/contact_auto_completer.rb +2 -2
- data/lib/e9_crm/tracking_controller.rb +2 -29
- data/lib/e9_crm/version.rb +1 -1
- metadata +5 -5
- data/app/views/e9_crm/deals/reports.html.haml +0 -19
@@ -2,43 +2,86 @@ class E9Crm::CampaignsController < E9Crm::ResourcesController
|
|
2
2
|
defaults :resource_class => Campaign
|
3
3
|
include E9Rails::Controllers::Orderable
|
4
4
|
|
5
|
+
helper :"e9_crm/deals"
|
6
|
+
|
5
7
|
before_filter :redirect_new_to_subclass, :only => :new
|
6
8
|
|
7
|
-
|
9
|
+
before_filter :redirect_for_default_from_time, :only => :reports
|
8
10
|
|
9
|
-
|
11
|
+
prepend_before_filter :set_reports_index_title, :only => :reports
|
12
|
+
|
13
|
+
filter_access_to :reports, :require => :read, :context => :admin
|
14
|
+
|
15
|
+
self.should_paginate_index = false
|
10
16
|
|
11
17
|
has_scope :active, :only => :index, :default => 'true' do |_, scope, value|
|
12
18
|
scope.active(E9.true_value?(value))
|
13
19
|
end
|
14
20
|
|
15
|
-
|
21
|
+
##
|
22
|
+
# Reports
|
23
|
+
#
|
24
|
+
|
25
|
+
has_scope :reports, :only => :reports, :default => 'true' do |controller, scope, _|
|
26
|
+
if (args = controller.params.values_at(:from, :until)).any?
|
27
|
+
scope.reports(*args)
|
28
|
+
else
|
29
|
+
scope.reports
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
has_scope :of_group, :as => :group, :only => :reports
|
34
|
+
|
35
|
+
has_scope :type, :only => :reports do |_, scope, value|
|
16
36
|
scope.of_type("#{value}_campaign".classify)
|
17
37
|
end
|
18
38
|
|
39
|
+
def reports
|
40
|
+
index!
|
41
|
+
end
|
42
|
+
|
19
43
|
protected
|
20
44
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
45
|
+
def redirect_new_to_subclass
|
46
|
+
types = %w(advertising affiliate email sales)
|
47
|
+
type = types.member?(params[:type]) ? params[:type] : types.first
|
48
|
+
redirect_to send("new_#{type}_campaign_path")
|
49
|
+
end
|
26
50
|
|
27
|
-
|
28
|
-
|
51
|
+
def collection_scope
|
52
|
+
if reports?
|
53
|
+
end_of_association_chain
|
29
54
|
|
30
|
-
|
55
|
+
else
|
31
56
|
end_of_association_chain.typed
|
32
57
|
.joins("left outer join campaign_groups on campaign_groups.id = campaigns.campaign_group_id")
|
33
58
|
.select("campaigns.*, campaign_groups.name campaign_group_name")
|
34
59
|
end
|
60
|
+
end
|
35
61
|
|
36
|
-
|
37
|
-
|
38
|
-
|
62
|
+
def default_ordered_on
|
63
|
+
'type,campaign_group_name,name'
|
64
|
+
end
|
39
65
|
|
40
|
-
|
41
|
-
|
42
|
-
|
66
|
+
def default_ordered_dir
|
67
|
+
'ASC'
|
68
|
+
end
|
43
69
|
|
70
|
+
def reports?
|
71
|
+
params[:action] == 'reports'
|
72
|
+
end
|
73
|
+
|
74
|
+
def set_reports_index_title
|
75
|
+
@index_title = I18n.t(:index_title, :scope => 'e9.e9_crm.reports')
|
76
|
+
end
|
77
|
+
|
78
|
+
def redirect_for_default_from_time
|
79
|
+
format = request.format.blank? || request.format == Mime::ALL ? Mime::HTML : request.format
|
80
|
+
|
81
|
+
if format.html? && params[:from].blank?
|
82
|
+
url = params.slice(:controller, :action)
|
83
|
+
url.merge!(:from => Date.today.strftime('%Y/%m'))
|
84
|
+
redirect_to url and return false
|
85
|
+
end
|
86
|
+
end
|
44
87
|
end
|
@@ -22,6 +22,7 @@ class E9Crm::ContactsController < E9Crm::ResourcesController
|
|
22
22
|
|
23
23
|
skip_before_filter :authenticate_user!, :filter_access_filter, :only => :templates
|
24
24
|
before_filter :build_resource, :only => :templates
|
25
|
+
before_filter :set_edit_title, :only => :edit
|
25
26
|
|
26
27
|
def templates
|
27
28
|
render RecordAttribute::TEMPLATES
|
@@ -71,6 +72,10 @@ class E9Crm::ContactsController < E9Crm::ResourcesController
|
|
71
72
|
end
|
72
73
|
end
|
73
74
|
|
75
|
+
def set_edit_title
|
76
|
+
@edit_title ||= e9_t(:edit_title_with_name, :contact => resource.name)
|
77
|
+
end
|
78
|
+
|
74
79
|
# we don't need @index_title in the breadcrumb here (too long)
|
75
80
|
def add_index_breadcrumb
|
76
81
|
add_breadcrumb! e9_t(:index_title), collection_path
|
@@ -5,34 +5,33 @@ class E9Crm::DealsController < E9Crm::ResourcesController
|
|
5
5
|
# for campaign select options
|
6
6
|
helper :"e9_crm/campaigns"
|
7
7
|
|
8
|
-
filter_access_to :leads, :
|
8
|
+
filter_access_to :leads, :require => :read, :context => :admin
|
9
9
|
|
10
10
|
skip_after_filter :flash_to_headers, :except => :destroy
|
11
11
|
|
12
12
|
prepend_before_filter :set_leads_index_title, :only => :leads
|
13
|
-
prepend_before_filter :set_reports_index_title, :only => :reports
|
14
13
|
|
15
14
|
before_filter :prepop_deal_owner_contact, :only => [:new, :edit]
|
16
15
|
|
17
|
-
before_filter :redirect_for_default_from_time, :only =>
|
16
|
+
before_filter :redirect_for_default_from_time, :only => :leads
|
18
17
|
|
19
18
|
##
|
20
19
|
# All Scopes
|
21
20
|
#
|
22
21
|
|
23
22
|
has_scope :until_time, :as => :until, :unless => 'params[:from].present?'
|
24
|
-
|
25
23
|
has_scope :from_time, :as => :from do |controller, scope, value|
|
26
|
-
#is_reports = controller.params[:action] == 'reports'
|
27
|
-
is_reports = false
|
28
|
-
|
29
24
|
if controller.params[:until]
|
30
|
-
scope.for_time_range(value, controller.params[:until]
|
25
|
+
scope.for_time_range(value, controller.params[:until])
|
31
26
|
else
|
32
|
-
scope.from_time(value
|
27
|
+
scope.from_time(value)
|
33
28
|
end
|
34
29
|
end
|
35
30
|
|
31
|
+
has_scope :closed_in_month, :as => :closed, :only => :index do |controller, scope, value|
|
32
|
+
scope.for_time_range(value, :column => :closed_at, :in => :month)
|
33
|
+
end
|
34
|
+
|
36
35
|
##
|
37
36
|
# Leads Scopes
|
38
37
|
#
|
@@ -57,20 +56,6 @@ class E9Crm::DealsController < E9Crm::ResourcesController
|
|
57
56
|
has_scope :status, :only => :index
|
58
57
|
has_scope :owner, :only => :index
|
59
58
|
|
60
|
-
##
|
61
|
-
# Reports scopes
|
62
|
-
#
|
63
|
-
|
64
|
-
has_scope :reports, :only => :reports, :type => :boolean, :default => true
|
65
|
-
|
66
|
-
has_scope :group, :only => :reports do |c, scope, value|
|
67
|
-
scope & Campaign.of_group(value)
|
68
|
-
end
|
69
|
-
|
70
|
-
has_scope :type, :only => :reports do |_, scope, value|
|
71
|
-
scope & Campaign.of_type("#{value}_campaign".classify)
|
72
|
-
end
|
73
|
-
|
74
59
|
##
|
75
60
|
# Actions
|
76
61
|
#
|
@@ -79,10 +64,6 @@ class E9Crm::DealsController < E9Crm::ResourcesController
|
|
79
64
|
index!
|
80
65
|
end
|
81
66
|
|
82
|
-
def reports
|
83
|
-
index!
|
84
|
-
end
|
85
|
-
|
86
67
|
protected
|
87
68
|
|
88
69
|
def add_edit_breadcrumb(opts = {})
|
@@ -94,18 +75,15 @@ class E9Crm::DealsController < E9Crm::ResourcesController
|
|
94
75
|
def collection
|
95
76
|
get_collection_ivar || begin
|
96
77
|
set_collection_ivar(
|
97
|
-
if params[:action] == '
|
98
|
-
end_of_association_chain.
|
78
|
+
if params[:action] == 'leads'
|
79
|
+
end_of_association_chain.includes(:contacts).paginate(pagination_parameters)
|
99
80
|
|
100
|
-
|
81
|
+
else
|
101
82
|
# NOTE this is a pretty ugly join just to be able to sort on owner
|
102
83
|
end_of_association_chain
|
103
84
|
.joins("left outer join contacts on contacts.id = deals.contact_id")
|
104
85
|
.select("deals.*, contacts.first_name owner_name")
|
105
86
|
.all
|
106
|
-
|
107
|
-
else
|
108
|
-
end_of_association_chain.includes(:contacts).paginate(pagination_parameters)
|
109
87
|
end
|
110
88
|
)
|
111
89
|
end
|
@@ -123,12 +101,12 @@ class E9Crm::DealsController < E9Crm::ResourcesController
|
|
123
101
|
@index_title = I18n.t(:index_title, :scope => 'e9.e9_crm.leads')
|
124
102
|
end
|
125
103
|
|
126
|
-
def
|
127
|
-
|
104
|
+
def should_paginate_index
|
105
|
+
params[:action] == 'leads'
|
128
106
|
end
|
129
107
|
|
130
108
|
def ordered_if
|
131
|
-
%w(index leads
|
109
|
+
%w(index leads).member? params[:action]
|
132
110
|
end
|
133
111
|
|
134
112
|
def default_ordered_on
|
@@ -17,9 +17,20 @@ module E9Crm::CampaignsHelper
|
|
17
17
|
val && "?#{E9Crm.query_param}=#{val}" || 'n/a'
|
18
18
|
end
|
19
19
|
|
20
|
-
def display_campaign_type(val
|
21
|
-
|
22
|
-
|
20
|
+
def display_campaign_type(val)
|
21
|
+
@_campaign_types ||= {}
|
22
|
+
@_campaign_types[val] ||= begin
|
23
|
+
retv = val[/(.*)Campaign/, 1]
|
24
|
+
retv == 'No' ? 'No Campaign' : retv
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def display_campaign_group_by_id(val)
|
29
|
+
@_campaign_groups ||= CampaignGroup.all
|
30
|
+
@_campaign_group_by_id ||= {}
|
31
|
+
@_campaign_group_by_id[val] ||= begin
|
32
|
+
@_campaign_groups.find {|c| c.id == val }.try(:name) || 'No Group'
|
33
|
+
end
|
23
34
|
end
|
24
35
|
|
25
36
|
def campaign_type_select_options(with_all_option = true)
|
@@ -46,40 +46,34 @@ module E9Crm::DealsHelper
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
def deal_date_select_options(
|
49
|
+
def deal_date_select_options(options = {})
|
50
50
|
@_first_deal_date ||= Deal.order(:created_at).first.try(:created_at) || Date.today
|
51
51
|
|
52
52
|
date, cdate = @_first_deal_date, Date.today
|
53
53
|
|
54
|
-
|
54
|
+
sel_options = []
|
55
55
|
|
56
|
-
if
|
57
|
-
prefix = '
|
56
|
+
if options[:type] == :until
|
57
|
+
prefix = 'Up to '
|
58
58
|
label = prefix + ' Now'
|
59
|
+
elsif options[:type] == :in_month
|
60
|
+
prefix = 'Closed in '
|
61
|
+
label = 'Since Inception'
|
59
62
|
else
|
60
|
-
prefix = '
|
63
|
+
prefix = 'Since '
|
61
64
|
label = prefix + ' Inception'
|
62
65
|
end
|
63
66
|
|
64
67
|
begin
|
65
|
-
|
68
|
+
sel_options << [date.strftime("#{prefix}%B %Y"), date.strftime('%Y/%m')]
|
66
69
|
date += 1.month
|
67
70
|
end while date.year <= cdate.year && date.month <= cdate.month
|
68
71
|
|
69
|
-
|
72
|
+
sel_options.reverse!
|
70
73
|
|
71
|
-
|
72
|
-
end
|
74
|
+
sel_options.unshift([label, nil])
|
73
75
|
|
74
|
-
|
75
|
-
campaign = deal.campaign
|
76
|
-
|
77
|
-
if campaign.blank?
|
78
|
-
0
|
79
|
-
else
|
80
|
-
c = campaign.non_leads.count
|
81
|
-
c > 0 ? campaign.cost.to_f / c : campaign.cost
|
82
|
-
end.to_money
|
76
|
+
options_for_select(sel_options)
|
83
77
|
end
|
84
78
|
|
85
79
|
def deal_contacts
|
@@ -92,4 +86,9 @@ module E9Crm::DealsHelper
|
|
92
86
|
def deal_contacts_array
|
93
87
|
deal_contacts.map {|c| [c.name, c.id] }
|
94
88
|
end
|
89
|
+
|
90
|
+
def title_and_or_company(contact)
|
91
|
+
[contact.title, contact.company_name].
|
92
|
+
map(&:presence).compact.join(' at ').html_safe
|
93
|
+
end
|
95
94
|
end
|
data/app/models/campaign.rb
CHANGED
@@ -22,10 +22,6 @@ class Campaign < ActiveRecord::Base
|
|
22
22
|
# NOTE tracking cookie code changes with new visits
|
23
23
|
has_many :tracking_cookies, :foreign_key => :code, :primary_key => :code, :class_name => 'TrackingCookie'
|
24
24
|
|
25
|
-
def self.default
|
26
|
-
NoCampaign.first || NoCampaign.create
|
27
|
-
end
|
28
|
-
|
29
25
|
validates :name, :presence => true,
|
30
26
|
:uniqueness => { :allow_blank => true, :case_sensitive => false }
|
31
27
|
|
@@ -39,9 +35,81 @@ class Campaign < ActiveRecord::Base
|
|
39
35
|
|
40
36
|
scope :active, lambda {|val=true| where(:active => val) }
|
41
37
|
scope :inactive, lambda { active(false) }
|
42
|
-
scope :of_group, lambda {|val| where(:campaign_group_id => val.to_param) }
|
38
|
+
scope :of_group, lambda {|val| joins(:campaign_group).where(:campaign_group_id => val.to_param) }
|
43
39
|
scope :typed, lambda { where(arel_table[:type].not_eq('NoCampaign')) }
|
44
40
|
scope :ordered, lambda { order(arel_table[:name].asc) }
|
41
|
+
|
42
|
+
scope :reports, lambda {|*args|
|
43
|
+
options = args.extract_options!
|
44
|
+
|
45
|
+
selects = <<-SQL.gsub(/\s+/, ' ')
|
46
|
+
campaigns.*,
|
47
|
+
|
48
|
+
SUM(IF(deals.status != 'lead',1,0)) deal_count,
|
49
|
+
|
50
|
+
COUNT(deals.id) lead_count,
|
51
|
+
|
52
|
+
SUM(IF(deals.status='won',1,0)) won_deal_count,
|
53
|
+
SUM(IF(deals.status='won',deals.value,0)) total_value,
|
54
|
+
AVG(IF(deals.status='won',deals.value,NULL)) average_value,
|
55
|
+
|
56
|
+
SUM(costs.total) total_cost,
|
57
|
+
SUM(costs.total) /
|
58
|
+
SUM(IF(deals.status='won',1,0)) average_cost,
|
59
|
+
|
60
|
+
rv.count repeat_visits,
|
61
|
+
nv.count new_visits,
|
62
|
+
|
63
|
+
FLOOR(AVG(
|
64
|
+
DATEDIFF(
|
65
|
+
deals.closed_at,
|
66
|
+
deals.created_at))) average_elapsed
|
67
|
+
SQL
|
68
|
+
|
69
|
+
select(selects)
|
70
|
+
.joins(
|
71
|
+
'LEFT OUTER JOIN deals ' +
|
72
|
+
'ON deals.campaign_id = campaigns.id ' +
|
73
|
+
"#{ 'AND ' + Deal.for_time_range_conditions(*args, options).to_sql if args.present?}")
|
74
|
+
.joins(
|
75
|
+
'LEFT OUTER JOIN ( ' +
|
76
|
+
'SELECT SUM(cost) total, costable_id dc_cid ' +
|
77
|
+
'FROM dated_costs ' +
|
78
|
+
'WHERE costable_type="Campaign" ' +
|
79
|
+
"#{ 'AND ' + DatedCost.for_time_range_conditions(*args, options).to_sql if args.present?}" +
|
80
|
+
' GROUP BY dc_cid) costs ' +
|
81
|
+
'ON costs.dc_cid = campaigns.id')
|
82
|
+
.joins(
|
83
|
+
'LEFT OUTER JOIN ( ' +
|
84
|
+
'SELECT COUNT(DISTINCT session) count, campaign_id nv_cid ' +
|
85
|
+
'FROM page_views ' +
|
86
|
+
'WHERE page_views.new_visit = 1 ' +
|
87
|
+
"#{ 'AND ' + PageView.for_time_range_conditions(*args, options).to_sql if args.present?}" +
|
88
|
+
' GROUP BY nv_cid ) nv ' +
|
89
|
+
'ON nv.nv_cid = campaigns.id')
|
90
|
+
.joins(
|
91
|
+
'LEFT OUTER JOIN ( ' +
|
92
|
+
'SELECT COUNT(DISTINCT session) count, campaign_id rv_cid ' +
|
93
|
+
'FROM page_views ' +
|
94
|
+
'WHERE page_views.new_visit = 0 ' +
|
95
|
+
"#{ 'AND ' + PageView.for_time_range_conditions(*args, options).to_sql if args.present?}" +
|
96
|
+
' GROUP BY rv_cid ) rv ' +
|
97
|
+
'ON rv.rv_cid = campaigns.id')
|
98
|
+
.group('campaigns.id')
|
99
|
+
}
|
100
|
+
|
101
|
+
def self.default
|
102
|
+
NoCampaign.first || NoCampaign.create
|
103
|
+
end
|
104
|
+
|
105
|
+
# money column definitions for pseudo attributes (added on the reports scope)
|
106
|
+
%w(total_value average_value total_cost average_cost).each do |money_column|
|
107
|
+
class_eval("def #{money_column}; (r = read_attribute(:#{money_column})) && Money.new(r) end")
|
108
|
+
end
|
109
|
+
|
110
|
+
%w(new_visits repeat_visits).each do |count_column|
|
111
|
+
class_eval("def #{count_column}; (r = read_attribute(:#{count_column})) || 0 end")
|
112
|
+
end
|
45
113
|
|
46
114
|
def new_visit_session_count
|
47
115
|
page_views.new_visits.group(:session).count.keys.length
|
@@ -55,7 +123,7 @@ class Campaign < ActiveRecord::Base
|
|
55
123
|
page_views.repeat_visits.group(:session).count.keys.length
|
56
124
|
end
|
57
125
|
|
58
|
-
def
|
126
|
+
def repeat_visit_page_view_count
|
59
127
|
page_views.repeat_visits.group(:session).count.values.sum
|
60
128
|
end
|
61
129
|
|
data/app/models/contact.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
class Contact < ActiveRecord::Base
|
2
2
|
include E9Tags::Model
|
3
|
-
include
|
3
|
+
include E9::ActiveRecord::AttributeSearchable
|
4
4
|
include E9Rails::ActiveRecord::Initialization
|
5
5
|
|
6
6
|
# necessary so contact knows its merge path
|
@@ -215,6 +215,8 @@ class Contact < ActiveRecord::Base
|
|
215
215
|
def merge_and_destroy!(other)
|
216
216
|
merge_tags(other)
|
217
217
|
|
218
|
+
self.info = "#{self.info}\n\n#{other.info}"
|
219
|
+
|
218
220
|
if success = save
|
219
221
|
merge_destructive_and_destroy!(other)
|
220
222
|
end
|
@@ -236,6 +238,8 @@ class Contact < ActiveRecord::Base
|
|
236
238
|
self.address_attributes |= other.address_attributes
|
237
239
|
self.phone_number_attributes |= other.phone_number_attributes
|
238
240
|
self.instant_messaging_handle_attributes |= other.instant_messaging_handle_attributes
|
241
|
+
|
242
|
+
other.associated_deals.clear
|
239
243
|
other.destroy
|
240
244
|
end
|
241
245
|
|
@@ -327,6 +331,17 @@ class Contact < ActiveRecord::Base
|
|
327
331
|
reject_record_attribute?(hash) || super
|
328
332
|
end
|
329
333
|
|
334
|
+
def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
|
335
|
+
record.attributes = attributes.except(*UNASSIGNABLE_KEYS)
|
336
|
+
if has_destroy_flag?(attributes) && allow_destroy
|
337
|
+
if record.prospect?
|
338
|
+
record.mark_for_destruction
|
339
|
+
else
|
340
|
+
self.users.delete(record)
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
330
345
|
#
|
331
346
|
# #TODO figure out exactly how to handle rejection of Users
|
332
347
|
#
|
data/app/models/dated_cost.rb
CHANGED
data/app/models/deal.rb
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
class Deal < ActiveRecord::Base
|
5
5
|
include E9Rails::ActiveRecord::Initialization
|
6
6
|
include E9Rails::ActiveRecord::InheritableOptions
|
7
|
-
|
8
7
|
include E9::ActiveRecord::TimeScopes
|
9
8
|
|
10
9
|
self.options_column = false
|
@@ -24,7 +23,7 @@ class Deal < ActiveRecord::Base
|
|
24
23
|
validates :campaign, :presence => true
|
25
24
|
validate do |record|
|
26
25
|
if !Status::OPTIONS.include?(record.status)
|
27
|
-
record.errors.add(:status, :inclusion, :options => Status::
|
26
|
+
record.errors.add(:status, :inclusion, :options => Status::HUMAN_OPTIONS.join(', '))
|
28
27
|
elsif record.status_changed? && record.won? || record.lost? and record.status_was == Status::Lead
|
29
28
|
record.errors.add(:status, :illegal_conversion)
|
30
29
|
end
|
@@ -54,63 +53,12 @@ class Deal < ActiveRecord::Base
|
|
54
53
|
before_save :handle_status_conversion, :if => lambda {|r| r.status_changed? }
|
55
54
|
before_save :handle_dated_cost, :if => lambda {|r| r.status_changed? || r.campaign_id_changed? }
|
56
55
|
|
57
|
-
# money column definitions for pseudo attributes (added on the reports scope)
|
58
|
-
%w(total_value average_value total_cost average_cost).each do |money_column|
|
59
|
-
class_eval("def #{money_column}; (r = read_attribute(:#{money_column})) && Money.new(r) end")
|
60
|
-
end
|
61
|
-
|
62
56
|
delegate :name, :to => :owner, :prefix => true, :allow_nil => true
|
63
57
|
|
64
58
|
# mailing_list_ids may be set on Deals when they are being created as leads, this is
|
65
59
|
# done via opt-in checkboxes on the form
|
66
60
|
attr_accessor :mailing_list_ids
|
67
61
|
|
68
|
-
#
|
69
|
-
# reports is technically a deal scope, but actually returns campaigns
|
70
|
-
# and a selection of relevant pseudo columns.
|
71
|
-
#
|
72
|
-
# NOTE reports probably should be a campaign scope? It doesn't really seem
|
73
|
-
# to matter. The resultset is neither Deals nor Campaigns, anyway, but
|
74
|
-
# a selection of calculated columns aggregated from data from both tables.
|
75
|
-
#
|
76
|
-
scope :reports, lambda {
|
77
|
-
selects = <<-SQL.gsub(/\s+/, ' ')
|
78
|
-
campaigns.type campaign_type,
|
79
|
-
campaigns.name campaign_name,
|
80
|
-
campaigns.new_visits new_visits,
|
81
|
-
campaigns.repeat_visits repeat_visits,
|
82
|
-
deals.closed_at closed_at,
|
83
|
-
deals.created_at created_at,
|
84
|
-
campaign_groups.name campaign_group,
|
85
|
-
SUM(IF(deals.status != 'lead',1,0)) deal_count,
|
86
|
-
COUNT(deals.id) lead_count,
|
87
|
-
SUM(IF(deals.status='won',1,0)) won_deal_count,
|
88
|
-
SUM(IF(deals.status='won',deals.value,0)) total_value,
|
89
|
-
AVG(IF(deals.status='won',deals.value,NULL)) average_value,
|
90
|
-
SUM(dated_costs.cost) total_cost,
|
91
|
-
SUM(dated_costs.cost) /
|
92
|
-
SUM(IF(deals.status='won',1,0)) average_cost,
|
93
|
-
FLOOR(AVG(
|
94
|
-
DATEDIFF(
|
95
|
-
deals.closed_at,
|
96
|
-
deals.created_at))) average_elapsed
|
97
|
-
SQL
|
98
|
-
|
99
|
-
joins = <<-SQL.gsub(/\s+/, ' ')
|
100
|
-
RIGHT JOIN campaigns
|
101
|
-
ON deals.campaign_id = campaigns.id
|
102
|
-
|
103
|
-
LEFT JOIN dated_costs
|
104
|
-
ON campaigns.id = dated_costs.costable_id
|
105
|
-
AND dated_costs.costable_type = "Campaign"
|
106
|
-
|
107
|
-
LEFT JOIN campaign_groups
|
108
|
-
ON campaign_groups.id = campaigns.campaign_group_id
|
109
|
-
SQL
|
110
|
-
|
111
|
-
select(selects).joins(joins).group('campaigns.id')
|
112
|
-
}
|
113
|
-
|
114
62
|
scope :column_op, lambda {|op, column, value, reverse=false|
|
115
63
|
conditions = arel_table[column].send(op, value)
|
116
64
|
conditions = conditions.not if reverse
|
@@ -262,10 +210,12 @@ class Deal < ActiveRecord::Base
|
|
262
210
|
end
|
263
211
|
|
264
212
|
module Status
|
265
|
-
OPTIONS
|
266
|
-
Lead
|
267
|
-
Pending
|
268
|
-
Won
|
269
|
-
Lost
|
213
|
+
OPTIONS = %w(lead pending won lost)
|
214
|
+
Lead = OPTIONS[0]
|
215
|
+
Pending = OPTIONS[1]
|
216
|
+
Won = OPTIONS[2]
|
217
|
+
Lost = OPTIONS[3]
|
218
|
+
|
219
|
+
HUMAN_OPTIONS = [Pending, Won, Lost].map(&:titleize)
|
270
220
|
end
|
271
221
|
end
|
data/app/models/no_campaign.rb
CHANGED
data/app/models/page_view.rb
CHANGED
@@ -14,15 +14,16 @@
|
|
14
14
|
#
|
15
15
|
class PageView < ActiveRecord::Base
|
16
16
|
include E9Rails::ActiveRecord::Scopes::Times
|
17
|
+
include E9::ActiveRecord::TimeScopes
|
17
18
|
|
18
19
|
belongs_to :tracking_cookie
|
19
20
|
|
20
21
|
belongs_to :campaign, :inverse_of => :page_views
|
21
22
|
has_one :user, :through => :tracking_cookie
|
22
23
|
|
23
|
-
attr_accessor :should_cache
|
24
|
+
#attr_accessor :should_cache
|
24
25
|
|
25
|
-
after_create :increment_campaign_visit_cache, :if => '!!should_cache'
|
26
|
+
#after_create :increment_campaign_visit_cache, :if => '!!should_cache'
|
26
27
|
|
27
28
|
scope :by_user, lambda {|*users|
|
28
29
|
users.flatten!
|
@@ -43,7 +44,7 @@ class PageView < ActiveRecord::Base
|
|
43
44
|
|
44
45
|
protected
|
45
46
|
|
46
|
-
def increment_campaign_visit_cache
|
47
|
-
Campaign.increment_counter(new_visit ? :new_visits : :repeat_visits, campaign_id)
|
48
|
-
end
|
47
|
+
#def increment_campaign_visit_cache
|
48
|
+
#Campaign.increment_counter(new_visit ? :new_visits : :repeat_visits, campaign_id)
|
49
|
+
#end
|
49
50
|
end
|
@@ -4,7 +4,13 @@
|
|
4
4
|
%legend= e9_t(:affiliate_information_legend, :scope => 'e9_crm.campaigns')
|
5
5
|
.field
|
6
6
|
= f.label :affiliate, :class => :req, :for => 'campaign_affiliate_id'
|
7
|
-
|
7
|
+
%input.contact-autocomplete{:type => 'text'}
|
8
|
+
- contact = f.object.affiliate
|
9
|
+
.contact-select{:style => contact.blank? && 'display:none' || ''}
|
10
|
+
%span.content= contact.try(:name)
|
11
|
+
%input{:type => :hidden, :name => "#{resource_instance_name}[affiliate_id]", :value => contact.try(:id)}
|
12
|
+
%a{:class => :remove, :title => "Remove", :alt => "Remove"} Remove
|
13
|
+
|
8
14
|
.field
|
9
15
|
= help_label f, :affiliate_fee
|
10
16
|
= f.text_field :affiliate_fee, :value => f.object.affiliate_fee.to_s
|
@@ -3,7 +3,7 @@
|
|
3
3
|
%tr
|
4
4
|
%th= orderable_column_link(:campaign_type)
|
5
5
|
%th= orderable_column_link(:campaign_group)
|
6
|
-
%th= orderable_column_link(:
|
6
|
+
%th= orderable_column_link(:name)
|
7
7
|
%th= orderable_column_link(:new_visits)
|
8
8
|
%th= orderable_column_link(:repeat_visits)
|
9
9
|
%th= orderable_column_link(:lead_count)
|
@@ -14,6 +14,7 @@
|
|
14
14
|
%th= orderable_column_link(:total_cost)
|
15
15
|
%th= orderable_column_link(:average_cost)
|
16
16
|
%th= orderable_column_link(:average_elapsed)
|
17
|
+
|
17
18
|
%tbody
|
18
19
|
-# prepare a hash to store the accumulated row data for calculations
|
19
20
|
- dat = Hash.new {|k,v| k[v] = [] }
|
@@ -25,11 +26,11 @@
|
|
25
26
|
- collection.each do |record|
|
26
27
|
%tr{:id => "ids_#{record.id}", :class => cycle('odd', 'even')}
|
27
28
|
%td.record-campaign-type
|
28
|
-
= display_campaign_type(record.
|
29
|
+
= display_campaign_type(record.type)
|
29
30
|
%td.record-campaign-group
|
30
|
-
= record.campaign_group
|
31
|
-
%td.record-
|
32
|
-
= record.
|
31
|
+
= display_campaign_group_by_id(record.campaign_group)
|
32
|
+
%td.record-name
|
33
|
+
= record.name || 'n/a'
|
33
34
|
%td.record-new-visits.num
|
34
35
|
- dat[:new_visits] << record.new_visits
|
35
36
|
= record.new_visits
|
@@ -60,6 +61,7 @@
|
|
60
61
|
%td.record-average-elapsed.num
|
61
62
|
- dat[:average_elapsed] << record.average_elapsed
|
62
63
|
= record.average_elapsed && "%s days" % record.average_elapsed || 'n/a'
|
64
|
+
|
63
65
|
%tfooter
|
64
66
|
%tr{:class => 'record-totals'}
|
65
67
|
%td.record-totals-label{:colspan => 3}
|
@@ -1,11 +1,19 @@
|
|
1
|
-
= title (@index_title || e9_t(:
|
1
|
+
= title (@index_title || e9_t(:index_title, :scope => 'e9_crm.reports'))
|
2
2
|
|
3
|
-
|
3
|
+
.toolbar
|
4
|
+
.toolbar-left
|
5
|
+
= form_tag(resource_class, :method => :get, :class => 'scope-selects') do
|
6
|
+
%select{:name => 'type'}
|
7
|
+
= campaign_type_select_options
|
8
|
+
%select{:name => 'group'}
|
9
|
+
= campaign_group_select_options
|
10
|
+
%select{:name => 'from'}
|
11
|
+
= deal_date_select_options
|
12
|
+
%select{:name => 'until'}
|
13
|
+
= deal_date_select_options(:type => :until)
|
4
14
|
|
5
|
-
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
%div#records_table
|
11
|
-
= render 'table', :resources => collection
|
15
|
+
.toolbar-right
|
16
|
+
= link_to_collection(Campaign)
|
17
|
+
|
18
|
+
%div#records_table
|
19
|
+
= render 'reports_table'
|
File without changes
|
@@ -55,7 +55,7 @@
|
|
55
55
|
%td.contact-deal-status
|
56
56
|
= deal.status
|
57
57
|
%td.contact-deal-value
|
58
|
-
= deal.value
|
58
|
+
= deal.value.format
|
59
59
|
|
60
60
|
.contact.leads
|
61
61
|
%h2
|
@@ -74,10 +74,10 @@
|
|
74
74
|
- else
|
75
75
|
- leads.each do |lead|
|
76
76
|
%tr
|
77
|
-
%td.contact-lead-name
|
78
|
-
= link_to lead.offer_name, edit_deal_path(lead)
|
79
77
|
%td.contact-lead-created-at
|
80
78
|
= l(lead.created_at)
|
79
|
+
%td.contact-lead-name
|
80
|
+
= link_to lead.offer_name, edit_deal_path(lead)
|
81
81
|
|
82
82
|
.contact-sidebar
|
83
83
|
= render 'sidebar'
|
@@ -19,7 +19,7 @@
|
|
19
19
|
= f.collection_select :campaign_id, Campaign.ordered.all, :id, :to_s, {:prompt => true}, :id => 'deal_campaign_select'
|
20
20
|
.field.select
|
21
21
|
= f.label :status
|
22
|
-
= f.select :status, Deal::Status::
|
22
|
+
= f.select :status, Deal::Status::HUMAN_OPTIONS.map {|d| [d, d.downcase] }
|
23
23
|
.field.select
|
24
24
|
= f.label :contacts
|
25
25
|
%input#contact_autocomplete.list{:type => 'text', 'data-values' => resource.contact_ids.join(','), 'data-iname' => resource_instance_name, 'data-field' => '[contact_ids]'}
|
@@ -40,19 +40,3 @@
|
|
40
40
|
.field.date-picker
|
41
41
|
= f.label :closed_at
|
42
42
|
= f.text_field :closed_at, :class => 'date-picker', :value => l(resource.closed_at.try(:to_date))
|
43
|
-
|
44
|
-
.field
|
45
|
-
Created At:
|
46
|
-
= l(resource.created_at)
|
47
|
-
.field
|
48
|
-
Closed At:
|
49
|
-
= resource.closed_at ? l(resource.closed_at) : 'n/a'
|
50
|
-
.field
|
51
|
-
Campaign Cost:
|
52
|
-
= resource.campaign.try(:cost) || 0
|
53
|
-
.field
|
54
|
-
Campaign Deal Count:
|
55
|
-
= resource.campaign ? resource.campaign.non_leads.count : 0
|
56
|
-
.field
|
57
|
-
Cost:
|
58
|
-
= deal_cost(resource)
|
@@ -7,6 +7,8 @@
|
|
7
7
|
= deal_category_select_options
|
8
8
|
%select{:name => 'owner'}
|
9
9
|
= deal_owner_select_options
|
10
|
+
%select{:name => 'closed'}
|
11
|
+
= deal_date_select_options(:type => :in_month)
|
10
12
|
|
11
13
|
.toolbar-right
|
12
14
|
= link_to I18n.t('activerecord.links.index', :models => 'Leads'), leads_path
|
@@ -1,4 +1,7 @@
|
|
1
|
-
= title
|
1
|
+
= title resource.name
|
2
|
+
|
3
|
+
.actions
|
4
|
+
= link_to_edit_resource(resource)
|
2
5
|
|
3
6
|
.deal-header
|
4
7
|
.deal-category
|
@@ -8,6 +11,22 @@
|
|
8
11
|
.deal-status
|
9
12
|
= resource.status
|
10
13
|
|
11
|
-
.deal-
|
12
|
-
.
|
13
|
-
|
14
|
+
.deal-info
|
15
|
+
= k(resource.info)
|
16
|
+
|
17
|
+
%fieldset.deal-owner
|
18
|
+
%legend= t(:deal_owner_legend)
|
19
|
+
.deal-contact
|
20
|
+
- if resource.owner
|
21
|
+
.contact-link= link_to(resource.owner.name, resource.owner)
|
22
|
+
.contact-title= title_and_or_company(resource.owner)
|
23
|
+
- else
|
24
|
+
= t(:no_deal_owner)
|
25
|
+
|
26
|
+
- if resource.contacts.any?
|
27
|
+
%fieldset.deal-contacts
|
28
|
+
%legend= t(:deal_contact_legend)
|
29
|
+
- resource.contacts.each do |contact|
|
30
|
+
.deal-contact
|
31
|
+
.contact-link= link_to(contact.name, contact)
|
32
|
+
.contact-title= title_and_or_company(contact)
|
@@ -1,14 +1,14 @@
|
|
1
|
-
= form_for resource, :url => public_offer_deals_path(@offer)
|
1
|
+
= form_for resource, :url => public_offer_deals_path(@offer) do |f|
|
2
2
|
.errors= resource_error_messages!
|
3
3
|
.notice
|
4
4
|
|
5
5
|
- if f.object.user.blank?
|
6
6
|
.field
|
7
|
-
= f.label :lead_email
|
8
|
-
= f.text_field :lead_email
|
7
|
+
= f.label :lead_email, nil, :class => :req
|
8
|
+
= f.text_field :lead_email, :class => 'email required'
|
9
9
|
.field
|
10
|
-
= f.label :lead_name
|
11
|
-
= f.text_field :lead_name
|
10
|
+
= f.label :lead_name, nil, :class => :req
|
11
|
+
= f.text_field :lead_name, :class => 'required'
|
12
12
|
|
13
13
|
= @offer.custom_form_html.html_safe
|
14
14
|
|
@@ -3,8 +3,14 @@
|
|
3
3
|
%fieldset
|
4
4
|
%legend= e9_t(:salesperson_information_legend, :scope => 'e9_crm.campaigns')
|
5
5
|
.field
|
6
|
-
= f.label :
|
7
|
-
|
6
|
+
= f.label :sales_person, :class => :req, :for => 'campaign_sales_person_id'
|
7
|
+
%input.contact-autocomplete{:type => 'text'}
|
8
|
+
- contact = f.object.sales_person
|
9
|
+
.contact-select{:style => contact.blank? && 'display:none' || ''}
|
10
|
+
%span.content= contact.try(:name)
|
11
|
+
%input{:type => :hidden, :name => "#{resource_instance_name}[sales_person_id]", :value => contact.try(:id)}
|
12
|
+
%a{:class => :remove, :title => "Remove", :alt => "Remove"} Remove
|
13
|
+
|
8
14
|
.field
|
9
15
|
= help_label f, :sales_fee
|
10
16
|
= f.text_field :sales_fee, :value => f.object.sales_fee.to_s
|
data/config/locales/e9.en.yml
CHANGED
@@ -19,6 +19,7 @@ en:
|
|
19
19
|
no_contacts_notification: "Either you have selected no contacts, or all of those selected are opted out of bulk email. Please correct this and try again."
|
20
20
|
email_actions_legend: Email Actions
|
21
21
|
search_options_legend: Search Filters
|
22
|
+
edit_title_with_name: "Edit %{contact}"
|
22
23
|
contact_merges:
|
23
24
|
new_title: Merge Contacts
|
24
25
|
no_value: (No Value)
|
@@ -31,6 +32,7 @@ en:
|
|
31
32
|
select_submit: Send Email Now
|
32
33
|
leads:
|
33
34
|
index_title: Leads
|
35
|
+
no_records_text: No leads exist.
|
34
36
|
reports:
|
35
37
|
index_title: Marketing Report
|
36
38
|
no_records_text: No data exists for those query parameters.
|
data/config/locales/en.yml
CHANGED
@@ -5,6 +5,9 @@ en:
|
|
5
5
|
merge_conflict: "<p>This email is already attached to another contact and cannot be used.</p><p>Do you want to edit that contact instead?</p>"
|
6
6
|
duplicate_email_warning: "<p>You cannot enter the same email twice.</p>"
|
7
7
|
no_offer_value: No Offer
|
8
|
+
deal_owner_legend: Responsible
|
9
|
+
no_deal_owner: No one is responsible
|
10
|
+
deal_contact_legend: Associated Contacts
|
8
11
|
|
9
12
|
actions: Actions
|
10
13
|
clear: Clear
|
@@ -45,6 +48,8 @@ en:
|
|
45
48
|
confirm_destroy: Are you sure? This cannot be undone.
|
46
49
|
campaign_group:
|
47
50
|
confirm_destroy: Are you sure? This cannot be undone. Any campaigns which are associated with this group will become groupless.
|
51
|
+
with_model:
|
52
|
+
show: "View %{name}"
|
48
53
|
|
49
54
|
errors:
|
50
55
|
messages:
|
data/config/routes.rb
CHANGED
@@ -66,7 +66,7 @@ Rails.application.routes.draw do
|
|
66
66
|
|
67
67
|
# leads are simply a scoped view of deals (only index)
|
68
68
|
get :leads, :as => :leads, :to => 'deals#leads'
|
69
|
-
get :marketing_report, :to => '
|
69
|
+
get :marketing_report, :to => 'campaigns#reports'
|
70
70
|
|
71
71
|
get '/merge_contacts/:contact_a_id/and/:contact_b_id', :as => :new_contact_merge, :to => 'contact_merges#new'
|
72
72
|
post '/merge_contacts', :as => :contact_merges, :to => 'contact_merges#create'
|
data/lib/e9_crm/controller.rb
CHANGED
@@ -66,8 +66,8 @@ module E9Crm
|
|
66
66
|
else
|
67
67
|
attrs = {}
|
68
68
|
|
69
|
-
if current_user &&
|
70
|
-
E9Crm.log("Cookie user
|
69
|
+
if current_user && @_tracking_cookie.user_id.nil?
|
70
|
+
E9Crm.log("Cookie has no user, setting as current_user (#{current_user.id})")
|
71
71
|
attrs[:user] = current_user
|
72
72
|
end
|
73
73
|
|
@@ -11,9 +11,9 @@ module E9Crm::Rack
|
|
11
11
|
if env["PATH_INFO"] =~ /^\/autocomplete\/contacts/
|
12
12
|
params = Rack::Request.new(env).params
|
13
13
|
|
14
|
-
if query = params['query']
|
14
|
+
if query = params['query'] || params['term']
|
15
15
|
relation =
|
16
|
-
Contact.any_attrs_like('first_name', 'last_name', query).
|
16
|
+
Contact.any_attrs_like('first_name', 'last_name', query, :matcher => '%s%%').
|
17
17
|
limit(params['limit'] || DEFAULT_LIMIT).
|
18
18
|
joins("LEFT JOIN users on users.contact_id = contacts.id").
|
19
19
|
where(%{users.options REGEXP "primary: [\\"']?true" OR users.options IS NULL}).
|
@@ -3,8 +3,7 @@ module E9Crm
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
-
|
7
|
-
after_filter :track_page_view
|
6
|
+
before_filter :track_page_view
|
8
7
|
|
9
8
|
prepend_before_filter do
|
10
9
|
E9Crm.log("E9Crm tracking controller request")
|
@@ -13,24 +12,6 @@ module E9Crm
|
|
13
12
|
|
14
13
|
protected
|
15
14
|
|
16
|
-
#
|
17
|
-
# Checks to see if our session is new (a first request). During such a call
|
18
|
-
# the session id has not been set yet.
|
19
|
-
#
|
20
|
-
# This is useful when determining whether or not the page view (created afterwards)
|
21
|
-
# should increment the counter cache for its associated Campaign, as this should
|
22
|
-
# only happen once per session.
|
23
|
-
#
|
24
|
-
def check_for_new_session
|
25
|
-
if request.session_options[:id].blank? && request.get?
|
26
|
-
E9Crm.log("No session found, page view will increment campaign counter cache")
|
27
|
-
@_should_cache = true
|
28
|
-
end
|
29
|
-
|
30
|
-
E9Crm.log("session id: #{request.session_options[:id]}")
|
31
|
-
E9Crm.log("session get?: #{request.get?}")
|
32
|
-
end
|
33
|
-
|
34
15
|
#
|
35
16
|
# Track a page view and associate it with the loaded cookie.
|
36
17
|
#
|
@@ -43,13 +24,6 @@ module E9Crm
|
|
43
24
|
# [:campaign] The campaign associated with the tracking cookie or the default
|
44
25
|
# campaign (typically the NoCampaign record)
|
45
26
|
#
|
46
|
-
# [:should_cache] Whether or not this page view should increment the page view
|
47
|
-
# count for it's associated page. This should only happen once
|
48
|
-
# per session and for this reason, is only true on sessionless
|
49
|
-
# (new) requests. This is determined by looking for a session id
|
50
|
-
# during the before filter, as in the after filter the session id
|
51
|
-
# has been assigned.
|
52
|
-
#
|
53
27
|
def track_page_view
|
54
28
|
if request.get?
|
55
29
|
@_page_view ||= tracking_cookie.page_views.create({
|
@@ -59,8 +33,7 @@ module E9Crm
|
|
59
33
|
:remote_ip => request.remote_ip,
|
60
34
|
:session => request.session_options[:id],
|
61
35
|
:campaign => tracking_campaign,
|
62
|
-
:new_visit => session[:new_visit].present
|
63
|
-
:should_cache => !!@_should_cache
|
36
|
+
:new_visit => session[:new_visit].present?
|
64
37
|
})
|
65
38
|
end
|
66
39
|
end
|
data/lib/e9_crm/version.rb
CHANGED
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: e9_crm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.1.
|
5
|
+
version: 0.1.22
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Travis Cox
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-09-
|
13
|
+
date: 2011-09-21 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rails
|
@@ -223,8 +223,10 @@ files:
|
|
223
223
|
- app/views/e9_crm/campaign_groups/_header.html.haml
|
224
224
|
- app/views/e9_crm/campaigns/_form_inner.html.haml
|
225
225
|
- app/views/e9_crm/campaigns/_header.html.haml
|
226
|
+
- app/views/e9_crm/campaigns/_reports_table.html.haml
|
226
227
|
- app/views/e9_crm/campaigns/_table.html.haml
|
227
228
|
- app/views/e9_crm/campaigns/reports.html.haml
|
229
|
+
- app/views/e9_crm/campaigns/reports.js.erb
|
228
230
|
- app/views/e9_crm/companies/_form_inner.html.haml
|
229
231
|
- app/views/e9_crm/companies/_header.html.haml
|
230
232
|
- app/views/e9_crm/contact_emails/_form.html.haml
|
@@ -245,6 +247,7 @@ files:
|
|
245
247
|
- app/views/e9_crm/contacts/_table.html.haml
|
246
248
|
- app/views/e9_crm/contacts/_tag_table.html.haml
|
247
249
|
- app/views/e9_crm/contacts/_who.html.haml
|
250
|
+
- app/views/e9_crm/contacts/edit.html.haml
|
248
251
|
- app/views/e9_crm/contacts/index.html.haml
|
249
252
|
- app/views/e9_crm/contacts/index.js.erb
|
250
253
|
- app/views/e9_crm/contacts/show.html.haml
|
@@ -265,13 +268,10 @@ files:
|
|
265
268
|
- app/views/e9_crm/deals/_header.html.haml
|
266
269
|
- app/views/e9_crm/deals/_leads_header.html.haml
|
267
270
|
- app/views/e9_crm/deals/_leads_table.html.haml
|
268
|
-
- app/views/e9_crm/deals/_reports_table.html.haml
|
269
271
|
- app/views/e9_crm/deals/_table.html.haml
|
270
272
|
- app/views/e9_crm/deals/leads.html.haml
|
271
273
|
- app/views/e9_crm/deals/leads.js.erb
|
272
274
|
- app/views/e9_crm/deals/offer_form.html.haml
|
273
|
-
- app/views/e9_crm/deals/reports.html.haml
|
274
|
-
- app/views/e9_crm/deals/reports.js.erb
|
275
275
|
- app/views/e9_crm/deals/show.html.haml
|
276
276
|
- app/views/e9_crm/email_campaigns/_form_inner.html.haml
|
277
277
|
- app/views/e9_crm/email_templates/_form_inner.html.haml
|
@@ -1,19 +0,0 @@
|
|
1
|
-
= title (@index_title || e9_t(:index_title, :scope => 'e9_crm.reports'))
|
2
|
-
|
3
|
-
.toolbar
|
4
|
-
.toolbar-left
|
5
|
-
= form_tag(resource_class, :method => :get, :class => 'scope-selects') do
|
6
|
-
%select{:name => 'type'}
|
7
|
-
= campaign_type_select_options
|
8
|
-
%select{:name => 'group'}
|
9
|
-
= campaign_group_select_options
|
10
|
-
%select{:name => 'from'}
|
11
|
-
= deal_date_select_options
|
12
|
-
%select{:name => 'until'}
|
13
|
-
= deal_date_select_options(true)
|
14
|
-
|
15
|
-
.toolbar-right
|
16
|
-
= link_to_collection(Campaign)
|
17
|
-
|
18
|
-
%div#records_table
|
19
|
-
= render 'reports_table'
|