e9_crm 0.1.1 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/app/controllers/e9_crm/advertising_campaigns_controller.rb +1 -0
- data/app/controllers/e9_crm/affiliate_campaigns_controller.rb +1 -0
- data/app/controllers/e9_crm/campaign_groups_controller.rb +8 -0
- data/app/controllers/e9_crm/campaigns_controller.rb +38 -1
- data/app/controllers/e9_crm/companies_controller.rb +1 -0
- data/app/controllers/e9_crm/contact_emails_controller.rb +3 -7
- data/app/controllers/e9_crm/contacts_controller.rb +6 -4
- data/app/controllers/e9_crm/dated_costs_controller.rb +1 -0
- data/app/controllers/e9_crm/deals_controller.rb +66 -2
- data/app/controllers/e9_crm/email_campaigns_controller.rb +1 -0
- data/app/controllers/e9_crm/email_templates.controller.rb +1 -0
- data/app/controllers/e9_crm/offers_controller.rb +1 -0
- data/app/controllers/e9_crm/page_views_controller.rb +1 -0
- data/app/controllers/e9_crm/resources_controller.rb +2 -1
- data/app/controllers/e9_crm/sales_campaigns_controller.rb +1 -0
- data/app/helpers/e9_crm/campaign_groups_helper.rb +8 -0
- data/app/helpers/e9_crm/campaigns_helper.rb +42 -11
- data/app/helpers/e9_crm/contacts_helper.rb +1 -1
- data/app/helpers/e9_crm/deals_helper.rb +25 -0
- data/app/models/advertising_campaign.rb +0 -1
- data/app/models/campaign.rb +40 -8
- data/app/models/campaign_group.rb +6 -0
- data/app/models/contact.rb +22 -0
- data/app/models/contact_email.rb +29 -18
- data/app/models/deal.rb +93 -14
- data/app/models/no_campaign.rb +5 -0
- data/app/models/page_view.rb +13 -44
- data/app/models/tracking_cookie.rb +0 -6
- data/app/observers/deal_observer.rb +3 -0
- data/app/views/e9_crm/advertising_campaigns/_form_inner.html.haml +1 -0
- data/app/views/e9_crm/affiliate_campaigns/_form_inner.html.haml +10 -0
- data/app/views/e9_crm/campaign_groups/_footer.html.haml +0 -0
- data/app/views/e9_crm/campaign_groups/_header.html.haml +3 -0
- data/app/views/e9_crm/campaigns/_footer.html.haml +0 -0
- data/app/views/e9_crm/campaigns/_form_inner.html.haml +20 -4
- data/app/views/e9_crm/campaigns/_header.html.haml +16 -0
- data/app/views/e9_crm/campaigns/_reports_table.html.haml +31 -0
- data/app/views/e9_crm/campaigns/_table.html.haml +31 -0
- data/app/views/e9_crm/campaigns/reports.html.haml +13 -0
- data/app/views/e9_crm/contact_emails/_form_inner.html.haml +1 -1
- data/app/views/e9_crm/contacts/_header.html.haml +5 -4
- data/app/views/e9_crm/deals/_reports_table.html.haml +86 -0
- data/app/views/e9_crm/deals/reports.html.haml +19 -0
- data/app/views/e9_crm/deals/reports.js.erb +1 -0
- data/app/views/e9_crm/email_campaigns/_form_inner.html.haml +1 -0
- data/app/views/e9_crm/page_views/_table.html.haml +7 -7
- data/app/views/e9_crm/resources/_table.html.haml +1 -1
- data/app/views/e9_crm/sales_campaigns/_form_inner.html.haml +11 -0
- data/config/locales/e9.en.yml +11 -1
- data/config/locales/en.yml +16 -5
- data/config/routes.rb +15 -12
- data/e9_crm.gemspec +1 -1
- data/lib/e9_crm/rails_extensions.rb +7 -0
- data/lib/e9_crm/tracking_controller.rb +69 -52
- data/lib/e9_crm/version.rb +1 -1
- data/lib/generators/e9_crm/install_generator.rb +1 -1
- data/lib/generators/e9_crm/templates/{create_e9_crm_tables.rb → migration.rb} +6 -7
- metadata +20 -6
- data/app/controllers/e9_crm/record_attributes_controller.rb +0 -3
- data/app/controllers/e9_crm/reports_controller.rb +0 -2
@@ -1,3 +1,11 @@
|
|
1
1
|
class E9Crm::CampaignGroupsController < E9Crm::ResourcesController
|
2
2
|
defaults :resource_class => CampaignGroup
|
3
|
+
include E9Rails::Controllers::Orderable
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
# no pagination
|
8
|
+
def collection
|
9
|
+
get_collection_ivar || set_collection_ivar(end_of_association_chain.all)
|
10
|
+
end
|
3
11
|
end
|
@@ -1,4 +1,41 @@
|
|
1
1
|
class E9Crm::CampaignsController < E9Crm::ResourcesController
|
2
2
|
defaults :resource_class => Campaign
|
3
|
-
|
3
|
+
include E9Rails::Controllers::Orderable
|
4
|
+
|
5
|
+
filter_access_to :reports, :require => :read, :context => :admin
|
6
|
+
|
7
|
+
has_scope :of_group, :as => :group, :only => :index
|
8
|
+
|
9
|
+
has_scope :active, :only => :index do |_, scope, value|
|
10
|
+
scope.active(E9.true_value?(value))
|
11
|
+
end
|
12
|
+
|
13
|
+
has_scope :of_type, :as => :type, :only => :index do |_, scope, value|
|
14
|
+
scope.of_type("#{value}_campaign".classify)
|
15
|
+
end
|
16
|
+
|
17
|
+
def reports
|
18
|
+
index!
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def set_reports_index_title
|
24
|
+
@index_title = I18n.t(:reports_title, :scope => 'e9.e9_crm.campaigns')
|
25
|
+
end
|
26
|
+
|
27
|
+
def collection_scope
|
28
|
+
scope = end_of_association_chain
|
29
|
+
if params[:action] == 'reports'
|
30
|
+
scope = scope.select(
|
31
|
+
'campaigns.*, count(deals.id) won_deals_count, count(deals_campaigns.id) deals_count'
|
32
|
+
).joins([:deals, :won_deals])
|
33
|
+
end
|
34
|
+
scope
|
35
|
+
end
|
36
|
+
|
37
|
+
# no pagination
|
38
|
+
def collection
|
39
|
+
get_collection_ivar || set_collection_ivar(collection_scope.all)
|
40
|
+
end
|
4
41
|
end
|
@@ -17,21 +17,17 @@ class E9Crm::ContactEmailsController < E9Crm::ResourcesController
|
|
17
17
|
object = if params[resource_instance_name]
|
18
18
|
ContactEmail.new(params[resource_instance_name] || {})
|
19
19
|
else
|
20
|
-
ContactEmail.new_from_template(template, :from_email => current_user.email, :
|
20
|
+
ContactEmail.new_from_template(template, :from_email => current_user.email, :contact_ids => params[:uids])
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
# as the record won't be valid if the user_ids weren't passed in params
|
25
|
-
if object.user_ids.blank?
|
26
|
-
object.errors.add(:user_ids, :blank)
|
27
|
-
end
|
23
|
+
object.valid?
|
28
24
|
|
29
25
|
set_resource_ivar(object)
|
30
26
|
end
|
31
27
|
end
|
32
28
|
|
33
29
|
# throw record_not_found if there's no template. #new requires email_template_id
|
34
|
-
# be passed in params (and also
|
30
|
+
# be passed in params (and also contact_ids)
|
35
31
|
def template
|
36
32
|
@_template ||= EmailTemplate.find(params[:etid])
|
37
33
|
end
|
@@ -1,12 +1,13 @@
|
|
1
1
|
class E9Crm::ContactsController < E9Crm::ResourcesController
|
2
2
|
defaults :resource_class => Contact
|
3
3
|
|
4
|
+
include E9Rails::Controllers::Orderable
|
4
5
|
include E9Tags::Controller
|
5
6
|
|
6
7
|
respond_to :js, :html
|
7
8
|
|
8
9
|
before_filter :determine_title, :only => :index
|
9
|
-
before_filter :
|
10
|
+
before_filter :load_contact_ids, :only => :index
|
10
11
|
|
11
12
|
has_scope :search, :by_title, :by_company, :only => :index
|
12
13
|
has_scope :tagged, :only => :index, :type => :array
|
@@ -18,9 +19,10 @@ class E9Crm::ContactsController < E9Crm::ResourcesController
|
|
18
19
|
|
19
20
|
protected
|
20
21
|
|
21
|
-
def
|
22
|
-
@
|
23
|
-
|
22
|
+
def load_contact_ids
|
23
|
+
@contact_ids ||= begin
|
24
|
+
contact_id_sql = end_of_association_chain.scoped.select('contacts.id').to_sql
|
25
|
+
Contact.connection.send(:select_values, contact_id_sql, 'Contact ID Load')
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
@@ -1,19 +1,83 @@
|
|
1
1
|
class E9Crm::DealsController < E9Crm::ResourcesController
|
2
2
|
defaults :resource_class => Deal
|
3
|
+
include E9Rails::Controllers::Orderable
|
4
|
+
|
5
|
+
# for campaign select options
|
6
|
+
helper :"e9_crm/campaigns"
|
7
|
+
|
8
|
+
filter_access_to :leads, :reports, :require => :read, :context => :admin
|
3
9
|
|
4
|
-
filter_access_to :leads, :require => :read, :context => :admin
|
5
10
|
prepend_before_filter :set_leads_index_title, :only => :leads
|
11
|
+
prepend_before_filter :set_reports_index_title, :only => :reports
|
12
|
+
|
13
|
+
##
|
14
|
+
# Index/Reports Scopes
|
15
|
+
#
|
6
16
|
|
7
17
|
has_scope :leads, :only => :leads, :default => true
|
8
|
-
has_scope :leads, :except => :leads, :default => false
|
18
|
+
has_scope :leads, :except => [:leads, :reports], :default => false
|
19
|
+
|
20
|
+
|
21
|
+
##
|
22
|
+
# Reports scopes
|
23
|
+
#
|
24
|
+
|
25
|
+
has_scope :reports, :only => :reports, :type => :boolean, :default => true
|
26
|
+
|
27
|
+
has_scope :group, :only => :reports do |c, scope, value|
|
28
|
+
scope & Campaign.of_group(value)
|
29
|
+
end
|
30
|
+
|
31
|
+
has_scope :type, :only => :reports do |_, scope, value|
|
32
|
+
scope & Campaign.of_type("#{value}_campaign".classify)
|
33
|
+
end
|
34
|
+
|
35
|
+
has_scope :until_time, :as => :until, :unless => 'params[:from].present?'
|
36
|
+
|
37
|
+
has_scope :from_time, :as => :from do |controller, scope, value|
|
38
|
+
if controller.params[:until]
|
39
|
+
scope.for_time_range(value, controller.params[:until])
|
40
|
+
else
|
41
|
+
scope.from_time(value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
##
|
47
|
+
# Actions
|
48
|
+
#
|
9
49
|
|
10
50
|
def leads
|
11
51
|
index!
|
12
52
|
end
|
13
53
|
|
54
|
+
def reports
|
55
|
+
index!
|
56
|
+
end
|
57
|
+
|
14
58
|
protected
|
15
59
|
|
60
|
+
def collection
|
61
|
+
get_collection_ivar || begin
|
62
|
+
set_collection_ivar(
|
63
|
+
if params[:action] == 'reports'
|
64
|
+
end_of_association_chain.all
|
65
|
+
else
|
66
|
+
end_of_association_chain.paginate(pagination_parameters)
|
67
|
+
end
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
16
72
|
def set_leads_index_title
|
17
73
|
@index_title = I18n.t(:index_title, :scope => 'e9.e9_crm.leads')
|
18
74
|
end
|
75
|
+
|
76
|
+
def set_reports_index_title
|
77
|
+
@index_title = I18n.t(:index_title, :scope => 'e9.e9_crm.reports')
|
78
|
+
end
|
79
|
+
|
80
|
+
def ordered_if
|
81
|
+
%w(index reports).member? params[:action]
|
82
|
+
end
|
19
83
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
class E9Crm::PageViewsController < E9Crm::ResourcesController
|
2
2
|
defaults :resource_class => PageView
|
3
3
|
belongs_to :campaign, :contact, :polymorphic => true
|
4
|
+
include E9Rails::Controllers::Orderable
|
4
5
|
|
5
6
|
# NOTE association chain is prepended to ensure parent is loaded so other
|
6
7
|
# before filters can use collection_path, etc. Is there a better solution
|
@@ -1,10 +1,11 @@
|
|
1
1
|
class E9Crm::ResourcesController < E9Crm::BaseController
|
2
|
-
include E9Rails::Controllers::Orderable
|
3
2
|
include E9Rails::Helpers::ResourceErrorMessages
|
4
3
|
include E9Rails::Helpers::Pagination
|
5
4
|
|
6
5
|
inherit_resources
|
7
6
|
|
7
|
+
respond_to :js
|
8
|
+
|
8
9
|
add_resource_breadcrumbs
|
9
10
|
|
10
11
|
def self.defaults(hash = {})
|
@@ -1,14 +1,45 @@
|
|
1
1
|
module E9Crm::CampaignsHelper
|
2
|
-
def
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
2
|
+
def display_campaign_fee(val)
|
3
|
+
Money === val && val || 'n/a'
|
4
|
+
end
|
5
|
+
|
6
|
+
def display_campaign_code(val)
|
7
|
+
val || 'n/a'
|
8
|
+
end
|
9
|
+
|
10
|
+
def display_campaign_type(val)
|
11
|
+
val[/(.*)Campaign/, 1]
|
12
|
+
end
|
13
|
+
|
14
|
+
def campaign_type_select_options(with_all_option = true)
|
15
|
+
options = %w( advertising affiliate email sales ).map {|t| [t.titleize, t] }
|
16
|
+
options.unshift(['All Types', nil]) if with_all_option
|
17
|
+
options_for_select(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def campaign_group_select_options
|
21
|
+
options = CampaignGroup.select('name, id').all.map {|c| [c.name, c.id] }
|
22
|
+
options.unshift(['All Groups', nil])
|
23
|
+
options_for_select(options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def campaign_date_select_options
|
27
|
+
''
|
28
|
+
end
|
29
|
+
|
30
|
+
def campaign_active_select_options
|
31
|
+
options = [
|
32
|
+
['All Statuses', nil],
|
33
|
+
['Active', true],
|
34
|
+
['Inactive', false]
|
35
|
+
]
|
36
|
+
options_for_select(options)
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Accommodate for "NoCampaign" campaign in link
|
41
|
+
#
|
42
|
+
def link_to_edit_campaign(record)
|
43
|
+
link_to_edit_resource(record) unless record.is_a?(NoCampaign)
|
13
44
|
end
|
14
45
|
end
|
@@ -15,7 +15,7 @@ module E9Crm::ContactsHelper
|
|
15
15
|
def records_table_field_map_for_contact
|
16
16
|
{
|
17
17
|
:fields => {
|
18
|
-
:avatar => proc {|r| },
|
18
|
+
:avatar => proc {|r| "<img src=\"#{r.avatar_url}\" alt=\"Avatar for #{r.name}\" />".html_safe },
|
19
19
|
:details => proc {|r| render('details', :record => r) }
|
20
20
|
},
|
21
21
|
|
@@ -12,4 +12,29 @@ module E9Crm::DealsHelper
|
|
12
12
|
}
|
13
13
|
}
|
14
14
|
end
|
15
|
+
|
16
|
+
def deal_date_select_options(ending_month = false)
|
17
|
+
@_first_deal_date ||= Deal.order(:created_at).first.try(:created_at) || Date.today
|
18
|
+
|
19
|
+
date, cdate = @_first_deal_date, Date.today
|
20
|
+
|
21
|
+
options = []
|
22
|
+
|
23
|
+
if ending_month
|
24
|
+
prefix = 'Until'
|
25
|
+
label = prefix + ' Now'
|
26
|
+
else
|
27
|
+
prefix = 'From'
|
28
|
+
label = prefix + ' Inception'
|
29
|
+
end
|
30
|
+
|
31
|
+
begin
|
32
|
+
options << [date.strftime("#{prefix} %B %Y"), date.strftime('%Y/%m')]
|
33
|
+
date += 1.month
|
34
|
+
end while date.year <= cdate.year && date.month <= cdate.month
|
35
|
+
|
36
|
+
options.unshift([label, nil])
|
37
|
+
|
38
|
+
options_for_select(options)
|
39
|
+
end
|
15
40
|
end
|
data/app/models/campaign.rb
CHANGED
@@ -7,18 +7,50 @@ class Campaign < ActiveRecord::Base
|
|
7
7
|
include E9Rails::ActiveRecord::STI
|
8
8
|
|
9
9
|
belongs_to :campaign_group
|
10
|
-
|
11
|
-
has_many :
|
10
|
+
|
11
|
+
has_many :deals, :inverse_of => :campaign, :dependent => :nullify
|
12
|
+
has_many :won_deals, :class_name => 'Deal', :conditions => ['deals.status = ?', Deal::Status::Won]
|
13
|
+
has_many :lost_deals, :class_name => 'Deal', :conditions => ['deals.status = ?', Deal::Status::Lost]
|
14
|
+
has_many :pending_deals, :class_name => 'Deal', :conditions => ['deals.status = ?', Deal::Status::Pending]
|
15
|
+
has_many :leads, :class_name => 'Deal', :conditions => ['deals.status = ?', Deal::Status::Lead]
|
16
|
+
|
17
|
+
has_many :page_views, :inverse_of => :campaign, :dependent => :nullify
|
18
|
+
|
19
|
+
# only advertising campaigns use this association
|
20
|
+
has_many :dated_costs, :as => :costable
|
12
21
|
|
13
22
|
# NOTE tracking cookie code changes with new visits
|
14
|
-
has_many
|
23
|
+
has_many :tracking_cookies, :foreign_key => :code, :primary_key => :code, :class_name => 'TrackingCookie'
|
15
24
|
|
16
|
-
|
17
|
-
|
18
|
-
|
25
|
+
def self.default
|
26
|
+
NoCampaign.first || NoCampaign.create
|
27
|
+
end
|
19
28
|
|
20
|
-
|
21
|
-
|
29
|
+
validates :code, :presence => { :unless => lambda {|r| r.is_a?(NoCampaign) } },
|
30
|
+
:length => { :maximum => 32 },
|
31
|
+
:uniqueness => { :ignore_case => true, :allow_blank => true }
|
32
|
+
validates :affiliate_fee, :numericality => true
|
33
|
+
validates :sales_fee, :numericality => true
|
34
|
+
|
35
|
+
scope :active, lambda {|val=true| where(:active => val) }
|
36
|
+
scope :inactive, lambda { active(false) }
|
37
|
+
scope :of_group, lambda {|val| where(:campaign_group_id => val.to_param) }
|
38
|
+
|
39
|
+
def new_visit_session_count
|
40
|
+
page_views.new_visits.group(:session).count.keys.length
|
41
|
+
end
|
42
|
+
|
43
|
+
def new_visit_page_view_count
|
44
|
+
page_views.new_visits.group(:session).count.values.sum
|
45
|
+
end
|
46
|
+
|
47
|
+
def repeat_visit_session_count
|
48
|
+
page_views.repeat_visits.group(:session).count.keys.length
|
49
|
+
end
|
50
|
+
|
51
|
+
def repeat_visit_session_count
|
52
|
+
page_views.repeat_visits.group(:session).count.values.sum
|
53
|
+
end
|
22
54
|
|
23
55
|
##
|
24
56
|
# The sum cost of this campaign
|