e9_crm 0.1.26 → 0.1.27
Sign up to get free protection for your applications and to get access to all the features.
- data/app/controllers/e9_crm/base_controller.rb +1 -1
- data/app/controllers/e9_crm/campaigns_controller.rb +1 -1
- data/app/controllers/e9_crm/leads_controller.rb +0 -1
- data/app/controllers/e9_crm/page_views_controller.rb +57 -8
- data/app/controllers/e9_crm/resources_controller.rb +1 -0
- data/app/controllers/e9_crm/visits_controller.rb +60 -0
- data/app/helpers/e9_crm/contacts_helper.rb +2 -9
- data/app/helpers/e9_crm/page_views_helper.rb +45 -0
- data/app/models/campaign.rb +23 -23
- data/app/models/contact.rb +17 -4
- data/app/models/page_view.rb +3 -20
- data/app/models/tracking_cookie.rb +16 -0
- data/app/views/e9_crm/affiliate_campaigns/_form_inner.html.haml +2 -1
- data/app/views/e9_crm/campaigns/_reports_table.html.haml +3 -3
- data/app/views/e9_crm/campaigns/_table.html.haml +1 -1
- data/app/views/e9_crm/campaigns/reports.js.erb +1 -1
- data/app/views/e9_crm/contacts/_sidebar_email_form.html.haml +7 -5
- data/app/views/e9_crm/contacts/index.html.haml +1 -1
- data/app/views/e9_crm/contacts/show.html.haml +1 -0
- data/app/views/e9_crm/dated_costs/index.html.haml +1 -3
- data/app/views/e9_crm/deals/_leads_table.html.haml +3 -0
- data/app/views/e9_crm/deals/_table.html.haml +3 -0
- data/app/views/e9_crm/deals/leads.js.erb +1 -1
- data/app/views/e9_crm/deals/show.html.haml +14 -5
- data/app/views/e9_crm/page_views/_header.html.haml +15 -0
- data/app/views/e9_crm/page_views/_table.html.haml +6 -6
- data/app/views/e9_crm/resources/index.html.haml +0 -3
- data/app/views/e9_crm/resources/index.js.erb +1 -1
- data/app/views/e9_crm/sales_campaigns/_form_inner.html.haml +2 -1
- data/app/views/e9_crm/visits/_header.html.haml +3 -0
- data/app/views/e9_crm/visits/_table.html.haml +27 -0
- data/config/locales/e9.en.yml +19 -12
- data/config/routes.rb +8 -16
- data/e9_crm.gemspec +0 -8
- data/lib/e9_crm/controller.rb +27 -12
- data/lib/e9_crm/model.rb +6 -5
- data/lib/e9_crm/rack/email_campaign_auto_completer.rb +29 -0
- data/lib/e9_crm/rails_extensions.rb +0 -33
- data/lib/e9_crm/tracking_controller.rb +3 -5
- data/lib/e9_crm/version.rb +1 -1
- data/lib/e9_crm.rb +7 -5
- metadata +48 -165
- data/app/controllers/e9_crm/email_templates.controller.rb +0 -46
- data/app/models/email_template.rb +0 -27
- data/app/views/e9_crm/email_templates/_form_inner.html.haml +0 -12
- data/app/views/e9_crm/email_templates/_header.html.haml +0 -7
- data/app/views/e9_crm/email_templates/select.html.haml +0 -5
- data/lib/e9_crm/email.rb +0 -18
@@ -32,7 +32,7 @@ class E9Crm::CampaignsController < E9Crm::ResourcesController
|
|
32
32
|
|
33
33
|
has_scope :of_group, :as => :group, :only => :reports
|
34
34
|
|
35
|
-
has_scope :type, :only => :reports do |_, scope, value|
|
35
|
+
has_scope :type, :only => [:reports, :index] do |_, scope, value|
|
36
36
|
scope.of_type("#{value}_campaign".classify)
|
37
37
|
end
|
38
38
|
|
@@ -3,7 +3,6 @@ class E9Crm::LeadsController < ApplicationController
|
|
3
3
|
# rather it handles only public side lead creation
|
4
4
|
|
5
5
|
# TODO these should all be included in e9_base
|
6
|
-
include E9Rails::Helpers::ResourceLinks
|
7
6
|
include E9Rails::Helpers::Title
|
8
7
|
include E9Rails::Helpers::Translation
|
9
8
|
include E9Rails::Helpers::ResourceErrorMessages
|
@@ -1,7 +1,8 @@
|
|
1
1
|
class E9Crm::PageViewsController < E9Crm::ResourcesController
|
2
2
|
defaults :resource_class => PageView
|
3
|
-
|
4
|
-
|
3
|
+
include E9::Controllers::Orderable
|
4
|
+
|
5
|
+
actions :index
|
5
6
|
|
6
7
|
# NOTE association chain is prepended to ensure parent is loaded so other
|
7
8
|
# before filters can use collection_path, etc. Is there a better solution
|
@@ -9,13 +10,61 @@ class E9Crm::PageViewsController < E9Crm::ResourcesController
|
|
9
10
|
#
|
10
11
|
prepend_before_filter :association_chain
|
11
12
|
|
12
|
-
|
13
|
+
before_filter :find_contact
|
14
|
+
|
15
|
+
has_scope :campaign, :only => :index do |_, scope, value|
|
16
|
+
scope.where(:campaign_id => value)
|
17
|
+
end
|
18
|
+
|
19
|
+
has_scope :contact, :only => :index do |_, scope, value|
|
20
|
+
scope.joins(:user => :contact).merge(Contact.where(:id => value))
|
21
|
+
end
|
22
|
+
|
23
|
+
has_scope :new_visits, :only => :index, do |_, scope, value|
|
24
|
+
scope.new_visits(E9.true_value?(value))
|
25
|
+
end
|
26
|
+
|
27
|
+
has_scope :month, :only => :index do |controller, scope, value|
|
28
|
+
scope.for_time_range(value, :in => :month)
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
13
32
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
33
|
+
def collection_scope
|
34
|
+
scope = end_of_association_chain
|
35
|
+
|
36
|
+
unless params[:contact]
|
37
|
+
join_sql = <<-SQL
|
38
|
+
LEFT OUTER JOIN tracking_cookies
|
39
|
+
ON tracking_cookies.id = page_views.tracking_cookie_id
|
40
|
+
LEFT OUTER JOIN users
|
41
|
+
ON users.id = tracking_cookies.user_id
|
42
|
+
LEFT OUTER JOIN contacts
|
43
|
+
ON contacts.id = users.contact_id
|
44
|
+
SQL
|
45
|
+
|
46
|
+
scope = scope.joins(join_sql)
|
47
|
+
end
|
48
|
+
|
49
|
+
sel_sql = <<-SQL
|
50
|
+
page_views.*,
|
51
|
+
IF(contacts.id,CONCAT_WS(' ', contacts.first_name, contacts.last_name),'(Unknown)') contact_name,
|
52
|
+
contacts.id as contact_id
|
53
|
+
SQL
|
54
|
+
|
55
|
+
scope.select(sel_sql)
|
56
|
+
end
|
57
|
+
|
58
|
+
def collection
|
59
|
+
get_collection_ivar || begin
|
60
|
+
total_entries = collection_scope.except(:order).count
|
61
|
+
objects = collection_scope.includes(:campaign).paginate(pagination_parameters.merge(:total_entries => total_entries))
|
62
|
+
|
63
|
+
set_collection_ivar objects
|
19
64
|
end
|
20
65
|
end
|
66
|
+
|
67
|
+
def find_contact
|
68
|
+
@contact ||= params[:contact] && Contact.find_by_id(params[:contact])
|
69
|
+
end
|
21
70
|
end
|
@@ -2,6 +2,7 @@ class E9Crm::ResourcesController < E9Crm::BaseController
|
|
2
2
|
inherit_resources
|
3
3
|
|
4
4
|
include E9Rails::Helpers::ResourceErrorMessages
|
5
|
+
include E9::Controllers::InheritableViews
|
5
6
|
|
6
7
|
# NOTE depending on e9_base pagination (which should eventually use this module)
|
7
8
|
#include E9Rails::Helpers::Pagination
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class E9Crm::VisitsController < E9Crm::ResourcesController
|
2
|
+
defaults :resource_class => PageView, :collection_name => :page_views
|
3
|
+
belongs_to :campaign
|
4
|
+
include E9::Controllers::Orderable
|
5
|
+
|
6
|
+
actions :index
|
7
|
+
|
8
|
+
# NOTE association chain is prepended to ensure parent is loaded so other
|
9
|
+
# before filters can use collection_path, etc. Is there a better solution
|
10
|
+
# for this?
|
11
|
+
#
|
12
|
+
prepend_before_filter :association_chain
|
13
|
+
|
14
|
+
before_filter :determine_title, :only => :index
|
15
|
+
|
16
|
+
has_scope :visits, :default => 'true' do |_, scope, _|
|
17
|
+
sel_sql = <<-SQL
|
18
|
+
page_views.*,
|
19
|
+
IF(contacts.id,CONCAT_WS(' ', contacts.first_name, contacts.last_name),'(Unknown)') contact_name,
|
20
|
+
contacts.id as contact_id,
|
21
|
+
count(distinct(if(page_views.new_visit=1,IFNULL(page_views.session,1),null))) as new_visits,
|
22
|
+
count(distinct(if(page_views.new_visit=1,null,IFNULL(page_views.session,1)))) as repeat_visits
|
23
|
+
SQL
|
24
|
+
|
25
|
+
join_sql = <<-SQL
|
26
|
+
LEFT OUTER JOIN tracking_cookies
|
27
|
+
ON tracking_cookies.id = page_views.tracking_cookie_id
|
28
|
+
LEFT OUTER JOIN users
|
29
|
+
ON users.id = tracking_cookies.user_id
|
30
|
+
LEFT OUTER JOIN contacts
|
31
|
+
ON contacts.id = users.contact_id
|
32
|
+
SQL
|
33
|
+
|
34
|
+
scope.select(sel_sql).joins(join_sql).group('contact_id')
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def collection
|
40
|
+
get_collection_ivar || begin
|
41
|
+
set_collection_ivar end_of_association_chain.paginate(pagination_parameters)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def default_ordered_dir
|
46
|
+
'ASC'
|
47
|
+
end
|
48
|
+
|
49
|
+
def default_ordered_on
|
50
|
+
'contact_name'
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_index_breadcrumb
|
54
|
+
add_breadcrumb! e9_t(:breadcrumb_title)
|
55
|
+
end
|
56
|
+
|
57
|
+
def determine_title
|
58
|
+
@index_title = e9_t(:index_title, :parent => parent.name)
|
59
|
+
end
|
60
|
+
end
|
@@ -62,16 +62,9 @@ module E9Crm::ContactsHelper
|
|
62
62
|
tags.map {|tag| link_to_contact_search(:tagged, [tag], tag) }.join(', ').html_safe
|
63
63
|
end
|
64
64
|
|
65
|
-
def contact_newsletter_select_tag
|
66
|
-
options = UserEmail.pending.order('name').map {|e| [e.name, e.id] }
|
67
|
-
#select_tag 'eid', options_for_select( options.presence || [['n/a', nil]] )
|
68
|
-
select_tag 'eid', options_for_select(options) if options.present?
|
69
|
-
end
|
70
|
-
|
71
65
|
def contact_email_template_select_tag
|
72
|
-
options =
|
73
|
-
|
74
|
-
select_tag 'etid', options_for_select(options) if options.present?
|
66
|
+
options = UserEmail.bulk_sendable.order('name').map {|e| [e.name, e.id] }
|
67
|
+
select_tag 'email_id', options_for_select(options) if options.present?
|
75
68
|
end
|
76
69
|
|
77
70
|
def contact_user_subscribed_to_newsletter?(user)
|
@@ -1,2 +1,47 @@
|
|
1
1
|
module E9Crm::PageViewsHelper
|
2
|
+
def page_view_campaign_select_options
|
3
|
+
@_page_view_campaign_select_options ||= begin
|
4
|
+
opts = Campaign.all.map {|campaign| [campaign.name, campaign.id] }
|
5
|
+
opts.unshift ['Any', nil]
|
6
|
+
options_for_select(opts)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def page_view_new_visit_select_options
|
11
|
+
options_for_select([
|
12
|
+
['Any', nil],
|
13
|
+
['New Visits', true],
|
14
|
+
['Repeat Visits', false]
|
15
|
+
])
|
16
|
+
end
|
17
|
+
|
18
|
+
def page_view_date_select_options(options = {})
|
19
|
+
@_first_date ||= PageView.order(:created_at).first.try(:created_at) || Date.today
|
20
|
+
|
21
|
+
date, cdate = @_first_date, Date.today
|
22
|
+
|
23
|
+
sel_options = []
|
24
|
+
|
25
|
+
if options[:type] == :until
|
26
|
+
prefix = 'Up to '
|
27
|
+
label = prefix + ' Now'
|
28
|
+
elsif options[:type] == :in_month
|
29
|
+
prefix = 'Closed in '
|
30
|
+
label = 'Since Inception'
|
31
|
+
else
|
32
|
+
prefix = 'Since '
|
33
|
+
label = prefix + ' Inception'
|
34
|
+
end
|
35
|
+
|
36
|
+
begin
|
37
|
+
sel_options << [date.strftime("#{prefix}%B %Y"), date.strftime('%Y/%m')]
|
38
|
+
date += 1.month
|
39
|
+
end while date.year <= cdate.year && date.month <= cdate.month
|
40
|
+
|
41
|
+
sel_options.reverse!
|
42
|
+
|
43
|
+
sel_options.unshift([label, nil])
|
44
|
+
|
45
|
+
options_for_select(sel_options)
|
46
|
+
end
|
2
47
|
end
|
data/app/models/campaign.rb
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
#
|
6
6
|
class Campaign < ActiveRecord::Base
|
7
7
|
include E9Rails::ActiveRecord::STI
|
8
|
+
include E9::ActiveRecord::AttributeSearchable
|
8
9
|
|
9
10
|
belongs_to :campaign_group
|
10
11
|
|
@@ -14,7 +15,10 @@ class Campaign < ActiveRecord::Base
|
|
14
15
|
has_many :pending_deals, :class_name => 'Deal', :conditions => ['deals.status = ?', Deal::Status::Pending]
|
15
16
|
has_many :leads, :class_name => 'Deal', :conditions => ['deals.status = ?', Deal::Status::Lead]
|
16
17
|
has_many :non_leads, :class_name => 'Deal', :conditions => ['deals.status != ?', Deal::Status::Lead]
|
18
|
+
|
17
19
|
has_many :page_views, :inverse_of => :campaign, :dependent => :nullify
|
20
|
+
has_many :new_page_views, :class_name => 'PageView', :conditions => ['page_views.new_visit = ?', true]
|
21
|
+
has_many :repeat_page_views, :class_name => 'PageView', :conditions => ['page_views.new_visit = ?', false]
|
18
22
|
|
19
23
|
# only advertising campaigns use this association
|
20
24
|
has_many :dated_costs, :as => :costable
|
@@ -58,8 +62,8 @@ class Campaign < ActiveRecord::Base
|
|
58
62
|
SUM(costs.total) /
|
59
63
|
SUM(IF(deals.status='won',1,0)) average_cost,
|
60
64
|
|
61
|
-
rv.count
|
62
|
-
nv.count
|
65
|
+
IFNULL(rv.count, 0) repeat_visits,
|
66
|
+
IFNULL(nv.count, 0) new_visits,
|
63
67
|
|
64
68
|
SUM(DATEDIFF(
|
65
69
|
deals.closed_at,
|
@@ -70,36 +74,36 @@ class Campaign < ActiveRecord::Base
|
|
70
74
|
deals.created_at)) average_elapsed
|
71
75
|
SQL
|
72
76
|
|
73
|
-
select(selects)
|
74
|
-
|
77
|
+
select(selects).
|
78
|
+
joins(
|
75
79
|
'LEFT OUTER JOIN deals ' +
|
76
80
|
'ON deals.campaign_id = campaigns.id ' +
|
77
|
-
"#{ 'AND ' + Deal.for_time_range_conditions(*args, options).to_sql if args.present?}")
|
78
|
-
|
81
|
+
"#{ 'AND ' + Deal.for_time_range_conditions(*args, options).to_sql if args.present?}").
|
82
|
+
joins(
|
79
83
|
'LEFT OUTER JOIN ( ' +
|
80
84
|
'SELECT SUM(cost) total, costable_id dc_cid ' +
|
81
85
|
'FROM dated_costs ' +
|
82
86
|
'WHERE costable_type="Campaign" ' +
|
83
87
|
"#{ 'AND ' + DatedCost.for_time_range_conditions(*args, options).to_sql if args.present?}" +
|
84
88
|
' GROUP BY dc_cid) costs ' +
|
85
|
-
'ON costs.dc_cid = campaigns.id')
|
86
|
-
|
89
|
+
'ON costs.dc_cid = campaigns.id').
|
90
|
+
joins(
|
87
91
|
'LEFT OUTER JOIN ( ' +
|
88
92
|
'SELECT COUNT(DISTINCT session) count, campaign_id nv_cid ' +
|
89
93
|
'FROM page_views ' +
|
90
94
|
'WHERE page_views.new_visit = 1 ' +
|
91
95
|
"#{ 'AND ' + PageView.for_time_range_conditions(*args, options).to_sql if args.present?}" +
|
92
96
|
' GROUP BY nv_cid ) nv ' +
|
93
|
-
'ON nv.nv_cid = campaigns.id')
|
94
|
-
|
97
|
+
'ON nv.nv_cid = campaigns.id').
|
98
|
+
joins(
|
95
99
|
'LEFT OUTER JOIN ( ' +
|
96
100
|
'SELECT COUNT(DISTINCT session) count, campaign_id rv_cid ' +
|
97
101
|
'FROM page_views ' +
|
98
102
|
'WHERE page_views.new_visit = 0 ' +
|
99
103
|
"#{ 'AND ' + PageView.for_time_range_conditions(*args, options).to_sql if args.present?}" +
|
100
104
|
' GROUP BY rv_cid ) rv ' +
|
101
|
-
'ON rv.rv_cid = campaigns.id')
|
102
|
-
|
105
|
+
'ON rv.rv_cid = campaigns.id').
|
106
|
+
group('campaigns.id')
|
103
107
|
}
|
104
108
|
|
105
109
|
def self.default
|
@@ -111,24 +115,20 @@ class Campaign < ActiveRecord::Base
|
|
111
115
|
class_eval("def #{money_column}; (r = read_attribute(:#{money_column})) && Money.new(r) end")
|
112
116
|
end
|
113
117
|
|
114
|
-
|
115
|
-
|
116
|
-
end
|
117
|
-
|
118
|
-
def new_visit_session_count
|
119
|
-
page_views.new_visits.group(:session).count.keys.length
|
118
|
+
def new_visit_count
|
119
|
+
new_visits.group(:session).count.keys.length
|
120
120
|
end
|
121
121
|
|
122
122
|
def new_visit_page_view_count
|
123
|
-
|
123
|
+
new_visits.group(:session).count.values.sum
|
124
124
|
end
|
125
125
|
|
126
|
-
def
|
127
|
-
|
126
|
+
def repeat_visit_count
|
127
|
+
repeat_visits.group(:session).count.keys.length
|
128
128
|
end
|
129
129
|
|
130
130
|
def repeat_visit_page_view_count
|
131
|
-
|
131
|
+
repeat_visits.group(:session).count.values.sum
|
132
132
|
end
|
133
133
|
|
134
134
|
##
|
@@ -140,7 +140,7 @@ class Campaign < ActiveRecord::Base
|
|
140
140
|
end
|
141
141
|
|
142
142
|
def to_s
|
143
|
-
name.tap {|n| n << " (#{code})" if code.present? }
|
143
|
+
name.dup.tap {|n| n << " (#{code})" if code.present? }
|
144
144
|
end
|
145
145
|
|
146
146
|
def to_liquid
|
data/app/models/contact.rb
CHANGED
@@ -18,6 +18,13 @@ class Contact < ActiveRecord::Base
|
|
18
18
|
has_many :owned_deals, :class_name => 'Deal', :dependent => :restrict
|
19
19
|
has_and_belongs_to_many :associated_deals, :class_name => 'Deal'
|
20
20
|
|
21
|
+
has_many :campaigns_as_salesperson, :class_name => "SalesCampaign", :inverse_of => :sales_person, :foreign_key => 'sales_person_id'
|
22
|
+
has_many :campaigns_as_affiliate, :class_name => "AffiliateCampaign", :inverse_of => :affiliate, :foreign_key => 'affiliate_id'
|
23
|
+
|
24
|
+
def page_views
|
25
|
+
PageView.by_users(user_ids)
|
26
|
+
end
|
27
|
+
|
21
28
|
has_many :users, :inverse_of => :contact do
|
22
29
|
|
23
30
|
##
|
@@ -91,10 +98,6 @@ class Contact < ActiveRecord::Base
|
|
91
98
|
|
92
99
|
delegate :email, :to => :primary_user, :allow_nil => true
|
93
100
|
|
94
|
-
def page_views
|
95
|
-
PageView.by_user(users)
|
96
|
-
end
|
97
|
-
|
98
101
|
has_record_attributes :users,
|
99
102
|
:phone_number_attributes,
|
100
103
|
:instant_messaging_handle_attributes,
|
@@ -204,6 +207,12 @@ class Contact < ActiveRecord::Base
|
|
204
207
|
end
|
205
208
|
delegate :name, :to => :company, :prefix => true, :allow_nil => true
|
206
209
|
|
210
|
+
def name_with_email
|
211
|
+
retv = name.dup
|
212
|
+
retv << " (#{email})" if email.present?
|
213
|
+
retv
|
214
|
+
end
|
215
|
+
|
207
216
|
##
|
208
217
|
# Helper to concatenate a Contact's full name
|
209
218
|
#
|
@@ -232,7 +241,10 @@ class Contact < ActiveRecord::Base
|
|
232
241
|
|
233
242
|
def merge_destructive_and_destroy!(other)
|
234
243
|
other.users.clear_primary!
|
244
|
+
self.owned_deals |= other.owned_deals
|
235
245
|
self.associated_deals |= other.associated_deals
|
246
|
+
self.campaigns_as_salesperson |= other.campaigns_as_salesperson
|
247
|
+
self.campaigns_as_affiliate |= other.campaigns_as_affiliate
|
236
248
|
self.users |= other.users
|
237
249
|
self.website_attributes |= other.website_attributes
|
238
250
|
self.address_attributes |= other.address_attributes
|
@@ -240,6 +252,7 @@ class Contact < ActiveRecord::Base
|
|
240
252
|
self.instant_messaging_handle_attributes |= other.instant_messaging_handle_attributes
|
241
253
|
|
242
254
|
other.associated_deals.clear
|
255
|
+
other.reload
|
243
256
|
other.destroy
|
244
257
|
end
|
245
258
|
|
data/app/models/page_view.rb
CHANGED
@@ -21,30 +21,13 @@ class PageView < ActiveRecord::Base
|
|
21
21
|
belongs_to :campaign, :inverse_of => :page_views
|
22
22
|
has_one :user, :through => :tracking_cookie
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
#after_create :increment_campaign_visit_cache, :if => '!!should_cache'
|
27
|
-
|
28
|
-
scope :by_user, lambda {|*users|
|
29
|
-
users.flatten!
|
30
|
-
users.map! &:to_param
|
31
|
-
joins(:tracking_cookie).where(TrackingCookie.arel_table[:user_id].send *(users.length == 1 ? [:eq, users.pop] : [:in, users]))
|
32
|
-
}
|
33
|
-
|
34
|
-
scope :by_campaign, lambda {|*campaigns|
|
35
|
-
campaigns.flatten!
|
36
|
-
campaigns.map! &:to_param
|
37
|
-
where(arel_table[:campaign_id].send *(campaigns.length == 1 ? [:eq, campaigns.pop] : [:in, campaigns]))
|
24
|
+
scope :by_users, lambda {|*users|
|
25
|
+
joins(:tracking_cookie) & TrackingCookie.for_users(users)
|
38
26
|
}
|
39
27
|
|
40
28
|
scope :new_visits, lambda {|v=true| where(:new_visit => v) }
|
41
29
|
scope :repeat_visits, lambda { new_visits(false) }
|
42
30
|
|
43
31
|
delegate :name, :code, :to => :campaign, :prefix => true, :allow_nil => true
|
44
|
-
|
45
|
-
protected
|
46
|
-
|
47
|
-
#def increment_campaign_visit_cache
|
48
|
-
#Campaign.increment_counter(new_visit ? :new_visits : :repeat_visits, campaign_id)
|
49
|
-
#end
|
32
|
+
delegate :contact, :to => :user, :allow_nil => true
|
50
33
|
end
|
@@ -45,6 +45,22 @@ class TrackingCookie < ActiveRecord::Base
|
|
45
45
|
has_many :page_views
|
46
46
|
after_save :generate_hid, :on => :create
|
47
47
|
|
48
|
+
scope :for_users, lambda {|*users|
|
49
|
+
users.flatten!
|
50
|
+
users.map!(&:to_param)
|
51
|
+
users.compact!
|
52
|
+
|
53
|
+
if users.length == 1
|
54
|
+
where arel_table[:user_id].eq(users.first)
|
55
|
+
else
|
56
|
+
where arel_table[:user_id].in(users)
|
57
|
+
end
|
58
|
+
}
|
59
|
+
|
60
|
+
scope :for_user, lambda {|user|
|
61
|
+
where arel_table[:user_id].in(users.flatten.map(&:to_param))
|
62
|
+
}
|
63
|
+
|
48
64
|
protected
|
49
65
|
|
50
66
|
def generate_hid
|
@@ -4,8 +4,9 @@
|
|
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
|
+
|
8
8
|
- contact = f.object.affiliate
|
9
|
+
%input.contact-autocomplete{:type => 'text', :style => contact.blank? && '' || 'display:none'}
|
9
10
|
.contact-select{:style => contact.blank? && 'display:none' || ''}
|
10
11
|
%span.content= contact.try(:name)
|
11
12
|
%input{:type => :hidden, :name => "#{resource_instance_name}[affiliate_id]", :value => contact.try(:id)}
|
@@ -83,13 +83,13 @@
|
|
83
83
|
- total_value = dat[:total_value].compact.sum
|
84
84
|
= m(total_value)
|
85
85
|
%td.record-average-value.num
|
86
|
-
= m(total_value.to_f / won_deal_count)
|
86
|
+
= won_deal_count.zero? ? 'n/a' : m(total_value.to_f / won_deal_count)
|
87
87
|
%td.record-total-cost.num
|
88
88
|
- total_cost = dat[:total_cost].compact.sum
|
89
89
|
= m(total_cost)
|
90
90
|
%td.record-average-cost.num
|
91
|
-
= m(total_cost.to_f / won_deal_count)
|
91
|
+
= won_deal_count.zero? ? 'n/a' : m(total_cost.to_f / won_deal_count)
|
92
92
|
%td.record-average-elapsed.num
|
93
93
|
- total_elapsed = dat[:total_elapsed].compact.sum.to_f
|
94
94
|
- closed_deal_count = dat[:closed_deal_count].sum.to_i
|
95
|
-
= "%i days" % (total_elapsed / closed_deal_count)
|
95
|
+
= closed_deal_count.zero? ? 'n/a' : "%i days" % (total_elapsed / closed_deal_count)
|
@@ -20,7 +20,7 @@
|
|
20
20
|
%td.record-campaign-group
|
21
21
|
= record.campaign_group_name || e9_t(:no_group, :scope => 'e9_crm.campaigns')
|
22
22
|
%td.record-name
|
23
|
-
= record.name
|
23
|
+
= link_to record.name, campaign_visits_path(record), :title => 'View Campaign Activity'
|
24
24
|
%td.record-code
|
25
25
|
= display_campaign_code(record.code)
|
26
26
|
%td.record-affiliate-fee.num
|
@@ -1 +1 @@
|
|
1
|
-
$("
|
1
|
+
$("#records_table").html("<%= escape_javascript(render('reports_table', :collection => collection)) %>");
|
@@ -1,7 +1,9 @@
|
|
1
|
-
|
2
|
-
%
|
3
|
-
|
4
|
-
=
|
1
|
+
%fieldset
|
2
|
+
%legend= e9_t(:email_actions_legend)
|
3
|
+
- if (etag = contact_email_template_select_tag).present?
|
4
|
+
= form_for EmailDelivery.new(:contact_ids => @contact_ids), :url => new_admin_email_delivery_path, :html => { :method => :get, :id => 'contact_email_form', 'data-empty' => e9_t(:no_contacts_notification), 'data-count' => @contact_ids.length } do |f|
|
5
5
|
= etag
|
6
|
-
=
|
6
|
+
= f.hidden_field :contact_ids, :id => 'contact_email_uids'
|
7
7
|
= submit_tag e9_t(:send_email_template), :name => nil
|
8
|
+
- else
|
9
|
+
= I18n.t(:no_email_templates).html_safe
|
@@ -1,7 +1,6 @@
|
|
1
1
|
= title @index_title || e9_t(:index_title)
|
2
|
+
|
2
3
|
.toolbar
|
3
|
-
.toolbar-left
|
4
|
-
.toolbar-middle
|
5
4
|
.toolbar-right
|
6
5
|
= link_to_new_resource(resource_class, :class => 'new-resource')
|
7
6
|
|
@@ -19,4 +18,3 @@
|
|
19
18
|
%td{:colspan => 7}= e9_t(:no_records_text)
|
20
19
|
- else
|
21
20
|
= render collection
|
22
|
-
|
@@ -32,3 +32,6 @@
|
|
32
32
|
= link_to 'View Contact', record.contacts.first
|
33
33
|
= link_to 'Create Deal', edit_deal_path(record)
|
34
34
|
= link_to 'Delete Lead', record, :method => :delete, :remote => true, :confirm => I18n.t("#{resource_class.i18n_scope}.links.confirm_destroy")
|
35
|
+
|
36
|
+
- if controller.send(:should_paginate_index)
|
37
|
+
= will_paginate collection
|
@@ -1 +1 @@
|
|
1
|
-
$("
|
1
|
+
$("#records_table").html("<%= escape_javascript(render('leads_table', :collection => collection)) %>");
|
@@ -18,15 +18,24 @@
|
|
18
18
|
%legend= t(:deal_owner_legend)
|
19
19
|
.deal-contact
|
20
20
|
- if resource.owner
|
21
|
-
|
22
|
-
|
21
|
+
= link_to resource.owner do
|
22
|
+
.contact-photo
|
23
|
+
%img{:src => resource.owner.avatar_url, :alt => "Photo for #{resource.owner.name}"}
|
24
|
+
.contact-name= resource.owner.name
|
25
|
+
.contact-title= title_and_or_company(resource.owner)
|
23
26
|
- else
|
24
|
-
|
27
|
+
.contact-photo
|
28
|
+
%img{:src => User.new.avatar_url, :alt => "Default Contact Photo"}
|
29
|
+
.contact-name
|
30
|
+
= t(:no_deal_owner)
|
25
31
|
|
26
32
|
- if resource.contacts.any?
|
27
33
|
%fieldset.deal-contacts
|
28
34
|
%legend= t(:deal_contact_legend)
|
29
35
|
- resource.contacts.each do |contact|
|
30
36
|
.deal-contact
|
31
|
-
|
32
|
-
|
37
|
+
= link_to contact do
|
38
|
+
.contact-photo
|
39
|
+
%img{:src => contact.avatar_url, :alt => "Photo for #{contact.name}"}
|
40
|
+
.contact-name= contact.name
|
41
|
+
.contact-title= title_and_or_company(contact)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
.toolbar
|
2
|
+
.toolbar-left
|
3
|
+
= form_tag(resource_class, :method => :get, :class => 'scope-selects') do
|
4
|
+
%select{:name => 'campaign'}
|
5
|
+
= page_view_campaign_select_options
|
6
|
+
%select{:name => 'new_visits'}
|
7
|
+
= page_view_new_visit_select_options
|
8
|
+
%select{:name => 'month'}
|
9
|
+
= page_view_date_select_options(:type => :in_month)
|
10
|
+
|
11
|
+
%input.contact-autocomplete{:type => :text, :style => @contact.blank? && '' || 'display:none', :placeholder => 'Enter Contact...'}
|
12
|
+
.contact-select{:style => @contact.blank? && 'display:none' || ''}
|
13
|
+
%span.content= @contact.try(:name_with_email)
|
14
|
+
%input{:type => :hidden, :name => "contact", :value => @contact.try(:id)}
|
15
|
+
%a{:class => :remove, :title => "Remove", :alt => "Remove"} Remove
|
@@ -3,9 +3,9 @@
|
|
3
3
|
%tr
|
4
4
|
%th= orderable_column_link(:created_at)
|
5
5
|
%th= orderable_column_link('campaign.name', :campaign)
|
6
|
+
%th= orderable_column_link(:new_visit)
|
6
7
|
%th= orderable_column_link(:request_path)
|
7
|
-
%th= orderable_column_link(:
|
8
|
-
%th= orderable_column_link(:remote_ip)
|
8
|
+
%th= orderable_column_link('contact_name', :contact)
|
9
9
|
%tbody
|
10
10
|
- if collection.empty?
|
11
11
|
%tr
|
@@ -17,12 +17,12 @@
|
|
17
17
|
= I18n.l(record.created_at)
|
18
18
|
%td.record-campaign-name
|
19
19
|
= record.campaign_name || e9_t(:no_campaign)
|
20
|
+
%td.record-request-new-visit
|
21
|
+
= record.new_visit ? 'New Visit' : 'Repeat Visit'
|
20
22
|
%td.record-request-path
|
21
23
|
= record.request_path
|
22
|
-
%td.record-
|
23
|
-
= record.
|
24
|
-
%td.record-remote-ip
|
25
|
-
= record.remote_ip
|
24
|
+
%td.record-contact-name
|
25
|
+
= record.contact_name
|
26
26
|
|
27
27
|
- if controller.send(:should_paginate_index)
|
28
28
|
= will_paginate collection
|