e9_crm 0.1.21 → 0.1.22
Sign up to get free protection for your applications and to get access to all the features.
- 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'
|