e9_crm 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/app/controllers/e9_crm/campaigns_controller.rb +3 -0
- data/app/controllers/e9_crm/contact_emails_controller.rb +5 -1
- data/app/controllers/e9_crm/contact_offers_controller.rb +3 -0
- data/app/controllers/e9_crm/deals_controller.rb +41 -13
- data/app/controllers/e9_crm/file_download_offers_controller.rb +3 -0
- data/app/controllers/e9_crm/leads_controller.rb +74 -0
- data/app/controllers/e9_crm/new_content_subscription_offers_controller.rb +3 -0
- data/app/controllers/e9_crm/newsletter_subscription_offers_controller.rb +3 -0
- data/app/controllers/e9_crm/offer_subclass_controller.rb +11 -0
- data/app/controllers/e9_crm/offers_controller.rb +37 -0
- data/app/controllers/e9_crm/resources_controller.rb +8 -0
- data/app/controllers/e9_crm/video_offers_controller.rb +3 -0
- data/app/helpers/e9_crm/deals_helper.rb +32 -0
- data/app/helpers/e9_crm/leads_helper.rb +63 -0
- data/app/helpers/e9_crm/offers_helper.rb +16 -0
- data/app/models/campaign.rb +1 -0
- data/app/models/contact.rb +2 -0
- data/app/models/contact_offer.rb +2 -0
- data/app/models/deal.rb +110 -8
- data/app/models/file_download_offer.rb +2 -0
- data/app/models/new_content_subscription_offer.rb +5 -0
- data/app/models/newsletter_subscription_offer.rb +5 -0
- data/app/models/no_campaign.rb +5 -1
- data/app/models/offer.rb +30 -19
- data/app/models/subscription_offer.rb +3 -0
- data/app/models/video_offer.rb +2 -0
- data/app/views/e9_crm/campaigns/_header.html.haml +2 -2
- data/app/views/e9_crm/contact_offers/_form_inner.html.haml +5 -0
- data/app/views/e9_crm/contacts/_form_inner.html.haml +2 -1
- data/app/views/e9_crm/contacts/_index_sidebar.html.haml +30 -0
- data/app/views/e9_crm/contacts/index.html.haml +1 -30
- data/app/views/e9_crm/contacts/index.js.erb +1 -1
- data/app/views/e9_crm/deals/_form_inner.html.haml +16 -8
- data/app/views/e9_crm/deals/_header.html.haml +13 -0
- data/app/views/e9_crm/deals/_leads_header.html.haml +12 -0
- data/app/views/e9_crm/deals/_leads_table.html.haml +34 -0
- data/app/views/e9_crm/deals/leads.html.haml +4 -9
- data/app/views/e9_crm/deals/leads.js.erb +1 -0
- data/app/views/e9_crm/deals/offer_form.html.haml +6 -0
- data/app/views/e9_crm/deals/reports.html.haml +1 -1
- data/app/views/e9_crm/{offers → file_download_offers}/_form.html.haml +0 -0
- data/app/views/e9_crm/file_download_offers/_form_inner.html.haml +11 -0
- data/app/views/e9_crm/leads/_form.html.haml +26 -0
- data/app/views/e9_crm/leads/create.js.erb +11 -0
- data/app/views/e9_crm/leads/new.html.haml +6 -0
- data/app/views/e9_crm/menu_options/_header.html.haml +3 -4
- data/app/views/e9_crm/new_content_subscription_offers/_form_inner.html.haml +1 -0
- data/app/views/e9_crm/newsletter_subscription_offers/_form_inner.html.haml +1 -0
- data/app/views/e9_crm/offers/_footer.html.haml +0 -0
- data/app/views/e9_crm/offers/_form_inner.html.haml +8 -22
- data/app/views/e9_crm/offers/_header.html.haml +12 -0
- data/app/views/e9_crm/offers/_offer.html.haml +4 -0
- data/app/views/e9_crm/offers/_public_offer.html.haml +6 -0
- data/app/views/e9_crm/offers/show.html.haml +2 -0
- data/app/views/e9_crm/resources/_form_inner.html.haml +0 -5
- data/app/views/e9_crm/resources/create.js.erb +3 -2
- data/app/views/e9_crm/resources/edit.html.haml +1 -1
- data/app/views/e9_crm/resources/new.html.haml +1 -1
- data/app/views/e9_crm/resources/show.html.haml +1 -1
- data/app/views/e9_crm/resources/update.js.erb +3 -1
- data/app/views/e9_crm/subscription_offers/_form_inner.html.haml +5 -0
- data/app/views/e9_crm/video_offers/_form_inner.html.haml +5 -0
- data/config/locales/e9.en.yml +3 -2
- data/config/locales/en.yml +30 -13
- data/config/routes.rb +31 -8
- data/e9_crm.gemspec +1 -1
- data/lib/e9_crm/controller.rb +94 -0
- data/lib/e9_crm/model.rb +9 -9
- data/lib/e9_crm/rails_extensions.rb +9 -0
- data/lib/e9_crm/tracking_controller.rb +48 -80
- data/lib/e9_crm/version.rb +1 -1
- data/lib/e9_crm.rb +8 -1
- data/lib/generators/e9_crm/templates/migration.rb +3 -9
- metadata +39 -9
- data/Gemfile.lock +0 -98
- data/app/models/affiliate.rb +0 -4
- data/app/models/sales_person.rb +0 -4
- data/app/views/e9_crm/contact_emails/send_email.js.erb +0 -1
- data/app/views/e9_crm/offers/_form_inner.html.haml.bak +0 -43
data/.gitignore
CHANGED
@@ -30,6 +30,9 @@ class E9Crm::CampaignsController < E9Crm::ResourcesController
|
|
30
30
|
scope = scope.select(
|
31
31
|
'campaigns.*, count(deals.id) won_deals_count, count(deals_campaigns.id) deals_count'
|
32
32
|
).joins([:deals, :won_deals])
|
33
|
+
else
|
34
|
+
# don't include NoCampaign normally
|
35
|
+
scope = scope.typed
|
33
36
|
end
|
34
37
|
scope
|
35
38
|
end
|
@@ -6,7 +6,7 @@ class E9Crm::ContactEmailsController < E9Crm::ResourcesController
|
|
6
6
|
def create
|
7
7
|
create! do |success, failure|
|
8
8
|
success.html { redirect_to :admin_sent_email }
|
9
|
-
success.js
|
9
|
+
success.js
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
@@ -33,4 +33,8 @@ class E9Crm::ContactEmailsController < E9Crm::ResourcesController
|
|
33
33
|
def template
|
34
34
|
@_template ||= EmailTemplate.find(params[:etid])
|
35
35
|
end
|
36
|
+
|
37
|
+
def determine_layout
|
38
|
+
request.xhr? ? false : super
|
39
|
+
end
|
36
40
|
end
|
@@ -7,23 +7,49 @@ class E9Crm::DealsController < E9Crm::ResourcesController
|
|
7
7
|
|
8
8
|
filter_access_to :leads, :reports, :require => :read, :context => :admin
|
9
9
|
|
10
|
+
skip_after_filter :flash_to_headers
|
11
|
+
|
10
12
|
prepend_before_filter :set_leads_index_title, :only => :leads
|
11
13
|
prepend_before_filter :set_reports_index_title, :only => :reports
|
12
14
|
|
13
15
|
##
|
14
|
-
#
|
16
|
+
# All Scopes
|
15
17
|
#
|
16
18
|
|
19
|
+
has_scope :until_time, :as => :until, :unless => 'params[:from].present?'
|
20
|
+
|
21
|
+
has_scope :from_time, :as => :from do |controller, scope, value|
|
22
|
+
if controller.params[:until]
|
23
|
+
scope.for_time_range(value, controller.params[:until])
|
24
|
+
else
|
25
|
+
scope.from_time(value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
##
|
30
|
+
# Leads Scopes
|
31
|
+
#
|
32
|
+
|
17
33
|
# NOTE default => 'true' only exists to ensure this scope is called
|
18
34
|
has_scope :only_leads, :only => :leads, :default => 'true' do |controller, scope|
|
19
35
|
scope.leads(true)
|
20
36
|
end
|
21
37
|
|
38
|
+
has_scope :offer, :only => :leads
|
39
|
+
|
40
|
+
##
|
41
|
+
# Index Scopes
|
42
|
+
#
|
43
|
+
|
22
44
|
# NOTE default => 'false' only exists to ensure this scope is called
|
23
|
-
has_scope :no_leads, :
|
45
|
+
has_scope :no_leads, :only => :index, :default => 'false' do |controller, scope|
|
24
46
|
scope.leads(false)
|
25
47
|
end
|
26
48
|
|
49
|
+
has_scope :category, :only => :index
|
50
|
+
has_scope :status, :only => :index
|
51
|
+
has_scope :owner, :only => :index
|
52
|
+
|
27
53
|
##
|
28
54
|
# Reports scopes
|
29
55
|
#
|
@@ -38,15 +64,6 @@ class E9Crm::DealsController < E9Crm::ResourcesController
|
|
38
64
|
scope & Campaign.of_type("#{value}_campaign".classify)
|
39
65
|
end
|
40
66
|
|
41
|
-
has_scope :until_time, :as => :until, :unless => 'params[:from].present?'
|
42
|
-
|
43
|
-
has_scope :from_time, :as => :from do |controller, scope, value|
|
44
|
-
if controller.params[:until]
|
45
|
-
scope.for_time_range(value, controller.params[:until])
|
46
|
-
else
|
47
|
-
scope.from_time(value)
|
48
|
-
end
|
49
|
-
end
|
50
67
|
|
51
68
|
##
|
52
69
|
# Actions
|
@@ -62,15 +79,26 @@ class E9Crm::DealsController < E9Crm::ResourcesController
|
|
62
79
|
|
63
80
|
protected
|
64
81
|
|
82
|
+
def add_edit_breadcrumb(opts = {})
|
83
|
+
@edit_title = e9_t(resource.lead? ? :new_title : :edit_title)
|
84
|
+
add_breadcrumb!(@edit_title)
|
85
|
+
end
|
86
|
+
|
65
87
|
def collection
|
66
88
|
get_collection_ivar || begin
|
67
89
|
set_collection_ivar(
|
68
90
|
if params[:action] == 'reports'
|
69
91
|
end_of_association_chain.all
|
70
|
-
|
92
|
+
|
93
|
+
elsif params[:action] == 'deals'
|
94
|
+
# NOTE this is a pretty ugly join just to be able to sort on owner
|
71
95
|
end_of_association_chain
|
72
96
|
.joins("left outer join contacts on contacts.id = deals.contact_id")
|
73
97
|
.select("deals.*, contacts.first_name owner_name")
|
98
|
+
.all
|
99
|
+
|
100
|
+
else
|
101
|
+
end_of_association_chain.includes(:contacts).paginate(pagination_parameters)
|
74
102
|
end
|
75
103
|
)
|
76
104
|
end
|
@@ -85,6 +113,6 @@ class E9Crm::DealsController < E9Crm::ResourcesController
|
|
85
113
|
end
|
86
114
|
|
87
115
|
def ordered_if
|
88
|
-
%w(index reports).member? params[:action]
|
116
|
+
%w(index leads reports).member? params[:action]
|
89
117
|
end
|
90
118
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
class E9Crm::LeadsController < ApplicationController
|
2
|
+
# TODO these should all be included in e9_base
|
3
|
+
include E9Rails::Helpers::ResourceLinks
|
4
|
+
include E9Rails::Helpers::Title
|
5
|
+
include E9Rails::Helpers::Translation
|
6
|
+
include E9Rails::Helpers::ResourceErrorMessages
|
7
|
+
|
8
|
+
inherit_resources
|
9
|
+
belongs_to :offer, :param => :public_offer_id
|
10
|
+
defaults :resource_class => Deal, :instance_name => 'deal'
|
11
|
+
|
12
|
+
respond_to :js
|
13
|
+
respond_to :html, :only => []
|
14
|
+
|
15
|
+
has_scope :leads, :type => :boolean, :default => true
|
16
|
+
|
17
|
+
# we want to control (or not use) our own flash messages
|
18
|
+
skip_after_filter :flash_to_headers
|
19
|
+
|
20
|
+
before_filter :association_chain
|
21
|
+
after_filter :install_offers_cookie, :only => :create
|
22
|
+
|
23
|
+
#
|
24
|
+
# In the case that this is actually an HTML request, redirect to
|
25
|
+
# the offer on success (regardless of what type of offer?)
|
26
|
+
#
|
27
|
+
def create
|
28
|
+
create!(:flash => false) do |success, failure|
|
29
|
+
success.html { redirect_to public_offer_path(@offer) }
|
30
|
+
success.js
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def create_resource(object)
|
37
|
+
@lead_was_created = object.save
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_resource
|
41
|
+
get_resource_ivar || set_resource_ivar(
|
42
|
+
Deal.leads.new((params[resource_instance_name] || {}).reverse_merge(
|
43
|
+
:user => current_user,
|
44
|
+
:offer => @offer,
|
45
|
+
:campaign => tracking_campaign
|
46
|
+
))
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# this cookie is installed after successfully creating a lead and once installed,
|
52
|
+
# allows the cookied user to view the public offer page for the parent @offer
|
53
|
+
#
|
54
|
+
def install_offers_cookie
|
55
|
+
if @lead_was_created
|
56
|
+
cookied_offer_array = Marshal.load(cookies['_e9_offers']) rescue []
|
57
|
+
|
58
|
+
cookied_offer_array |= [@offer.id]
|
59
|
+
|
60
|
+
cookies['_e9_offers'] = {
|
61
|
+
:value => Marshal.dump(cookied_offer_array),
|
62
|
+
:expires => 1.year.from_now
|
63
|
+
}
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def determine_layout
|
68
|
+
request.xhr? ? false : super
|
69
|
+
end
|
70
|
+
|
71
|
+
def find_current_page
|
72
|
+
@current_page ||= Offer.page || super
|
73
|
+
end
|
74
|
+
end
|
@@ -1,4 +1,41 @@
|
|
1
1
|
class E9Crm::OffersController < E9Crm::ResourcesController
|
2
2
|
defaults :resource_class => Offer
|
3
3
|
include E9Rails::Controllers::Orderable
|
4
|
+
self.should_paginate_index = false
|
5
|
+
|
6
|
+
# record attributes templates js
|
7
|
+
skip_before_filter :authenticate_user!, :filter_access_filter, :only => :show
|
8
|
+
|
9
|
+
before_filter :throw_forbidden_unless_offer_cookied, :only => :show
|
10
|
+
|
11
|
+
has_scope :of_type, :as => :type, :only => :index do |_, scope, value|
|
12
|
+
scope.of_type("#{value}_offer".classify)
|
13
|
+
end
|
14
|
+
|
15
|
+
def show
|
16
|
+
clear_breadcrumbs
|
17
|
+
@show_title = resource.name
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def throw_forbidden_unless_offer_cookied
|
23
|
+
cookied_offer_array = Marshal.load(cookies['_e9_offers']) rescue []
|
24
|
+
|
25
|
+
unless cookied_offer_array.member?(params[:id].to_i)
|
26
|
+
permission_denied and return false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_current_page
|
31
|
+
if params[:action] != 'show'
|
32
|
+
super
|
33
|
+
else
|
34
|
+
@current_page ||= Offer.page || super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def determine_layout
|
39
|
+
request.xhr? ? false : super
|
40
|
+
end
|
4
41
|
end
|
@@ -47,6 +47,14 @@ class E9Crm::ResourcesController < E9Crm::BaseController
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
+
def build_params
|
51
|
+
params[resource_instance_name] || {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_resource
|
55
|
+
get_resource_ivar || set_resource_ivar(end_of_association_chain.send(method_for_build, build_params))
|
56
|
+
end
|
57
|
+
|
50
58
|
def default_ordered_on
|
51
59
|
'created_at'
|
52
60
|
end
|
@@ -13,6 +13,38 @@ module E9Crm::DealsHelper
|
|
13
13
|
select_tag 'contacts_ids', deal_contact_select_options
|
14
14
|
end
|
15
15
|
|
16
|
+
def deal_status_select_options
|
17
|
+
@_deal_status_select_options ||= begin
|
18
|
+
options = Deal::Status::OPTIONS - %w(lead)
|
19
|
+
options.unshift ['All Statuses', nil]
|
20
|
+
options_for_select(options)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def deal_category_select_options
|
25
|
+
@_deal_category_select_options ||= begin
|
26
|
+
options = MenuOption.options_for('Deal Category')
|
27
|
+
options.unshift ['All Categories', nil]
|
28
|
+
options_for_select(options)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def deal_owner_select_options
|
33
|
+
@_deal_owner_select_options ||= begin
|
34
|
+
options = Contact.deal_owners.all.map {|c| [c.name, c.id] }
|
35
|
+
options.unshift ['Any Owner', nil]
|
36
|
+
options_for_select(options)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def deal_offer_select_options
|
41
|
+
@_deal_offer_select_options ||= begin
|
42
|
+
options = Offer.all.map {|c| [c.name, c.id] }
|
43
|
+
options.unshift ['Any/No Offer', nil]
|
44
|
+
options_for_select(options)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
16
48
|
def deal_date_select_options(ending_month = false)
|
17
49
|
@_first_deal_date ||= Deal.order(:created_at).first.try(:created_at) || Date.today
|
18
50
|
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module E9Crm::LeadsHelper
|
2
|
+
def custom_offer_form_fields(offer, form)
|
3
|
+
@offer.parsed_custom_form_data.each do |field|
|
4
|
+
field.symbolize_keys!
|
5
|
+
|
6
|
+
case field[:type]
|
7
|
+
when 'select' then render_custom_offer_select(field, form)
|
8
|
+
when 'checkbox' then render_custom_offer_checkbox(field, form)
|
9
|
+
when 'radio' then render_custom_offer_radio(field, form)
|
10
|
+
when 'textfield' then render_custom_offer_textfield(field, form)
|
11
|
+
when 'textarea' then render_custom_offer_textarea(field, form)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def render_custom_offer_select(field, form)
|
17
|
+
label_html = field[:required] ? form.label(field[:name], nil, :class => :req) : form.label(field[:name])
|
18
|
+
select_html = form.select field[:name], options_for_select(field[:options])
|
19
|
+
safe_concat <<-HTML
|
20
|
+
<div class="field">
|
21
|
+
#{label_html}
|
22
|
+
#{select_html}
|
23
|
+
</div>
|
24
|
+
HTML
|
25
|
+
end
|
26
|
+
|
27
|
+
def render_custom_offer_checkbox(field, form)
|
28
|
+
label_html = field[:required] ? form.label(field[:name], nil, :class => :req) : form.label(field[:name])
|
29
|
+
safe_concat <<-HTML
|
30
|
+
<div class="field checkbox">
|
31
|
+
#{label_html}
|
32
|
+
</div>
|
33
|
+
HTML
|
34
|
+
end
|
35
|
+
|
36
|
+
def render_custom_offer_textfield(field, form)
|
37
|
+
label_html = field[:required] ? form.label(field[:name], nil, :class => :req) : form.label(field[:name])
|
38
|
+
|
39
|
+
safe_concat <<-HTML
|
40
|
+
<div class="field">
|
41
|
+
#{label_html}
|
42
|
+
</div>
|
43
|
+
HTML
|
44
|
+
end
|
45
|
+
|
46
|
+
def render_custom_offer_textarea(field, form)
|
47
|
+
label_html = field[:required] ? form.label(field[:name], nil, :class => :req) : form.label(field[:name])
|
48
|
+
|
49
|
+
safe_concat <<-HTML
|
50
|
+
<div class="field">
|
51
|
+
#{label_html}
|
52
|
+
</div>
|
53
|
+
HTML
|
54
|
+
end
|
55
|
+
|
56
|
+
def render_custom_offer_radio(field, form)
|
57
|
+
safe_concat <<-HTML
|
58
|
+
<div class="field radio">
|
59
|
+
|
60
|
+
</div>
|
61
|
+
HTML
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module E9Crm::OffersHelper
|
2
|
+
def records_table_field_map_for_offer
|
3
|
+
{
|
4
|
+
:fields => {
|
5
|
+
:name => nil,
|
6
|
+
:type => lambda {|r| r.class.model_name.human }
|
7
|
+
}
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def offer_select_options(with_all_option = true)
|
12
|
+
options = %w(contact file_download new_content_subscription newsletter_subscription video).map {|t| [t.titleize, t] }
|
13
|
+
options.unshift(['All Types', nil]) if with_all_option
|
14
|
+
options_for_select(options)
|
15
|
+
end
|
16
|
+
end
|
data/app/models/campaign.rb
CHANGED
@@ -35,6 +35,7 @@ class Campaign < ActiveRecord::Base
|
|
35
35
|
scope :active, lambda {|val=true| where(:active => val) }
|
36
36
|
scope :inactive, lambda { active(false) }
|
37
37
|
scope :of_group, lambda {|val| where(:campaign_group_id => val.to_param) }
|
38
|
+
scope :typed, lambda { where(arel_table[:type].not_eq('NoCampaign')) }
|
38
39
|
|
39
40
|
def new_visit_session_count
|
40
41
|
page_views.new_visits.group(:session).count.keys.length
|
data/app/models/contact.rb
CHANGED
data/app/models/deal.rb
CHANGED
@@ -4,25 +4,54 @@
|
|
4
4
|
class Deal < ActiveRecord::Base
|
5
5
|
include E9Rails::ActiveRecord::Initialization
|
6
6
|
include E9Rails::ActiveRecord::Scopes::Times
|
7
|
+
include E9Rails::ActiveRecord::InheritableOptions
|
8
|
+
|
9
|
+
self.options_column = false
|
7
10
|
|
8
11
|
belongs_to :campaign, :inverse_of => :deals
|
9
|
-
belongs_to :tracking_cookie, :inverse_of => :deals
|
10
12
|
belongs_to :offer, :inverse_of => :deals
|
13
|
+
belongs_to :user
|
11
14
|
|
12
15
|
belongs_to :owner, :class_name => 'Contact', :foreign_key => :contact_id
|
13
16
|
has_and_belongs_to_many :contacts
|
14
17
|
|
15
18
|
money_columns :value
|
16
19
|
|
17
|
-
validates :
|
18
|
-
|
20
|
+
validates :value, :numericality => true
|
21
|
+
|
22
|
+
# non-lead validations (deals in the admin)
|
23
|
+
# require a deal name
|
24
|
+
validates :name, :presence => { :unless => lambda {|r| r.lead? } }
|
25
|
+
|
26
|
+
# lead only validations
|
27
|
+
# require lead_name and lead_email; gotten from current_user if it exists
|
28
|
+
validates :lead_name, :presence => { :if => lambda {|r| r.lead? } }
|
29
|
+
validates :lead_email, :presence => { :if => lambda {|r| r.lead? } },
|
30
|
+
:email => { :if => lambda {|r| r.lead? }, :allow_blank => true }
|
31
|
+
|
32
|
+
# If a lead with a user, get the lead_name and lead_email from the user before validation
|
33
|
+
before_validation :get_name_and_email_from_user, :only => :create
|
34
|
+
before_validation :update_to_pending_status, :only => :update
|
35
|
+
|
36
|
+
# copy temp options over into custom_info column
|
37
|
+
before_create :transform_options_column
|
19
38
|
|
39
|
+
# denormalize campaign code and offer name columns
|
40
|
+
before_save :ensure_denormalized_columns
|
41
|
+
|
42
|
+
# If a lead with no user, find the user by email or create it, then if mailing_lists
|
43
|
+
# were passed, assign the user those mailing lists
|
44
|
+
after_create :find_or_create_user, :assign_user_mailing_lists
|
45
|
+
|
46
|
+
# money column definitions for pseudo attributes (added on the reports scope)
|
20
47
|
%w(total_value average_value total_cost average_cost).each do |money_column|
|
21
48
|
class_eval("def #{money_column}; (r = read_attribute(:#{money_column})) && Money.new(r) end")
|
22
49
|
end
|
23
50
|
|
24
51
|
delegate :name, :to => :owner, :prefix => true, :allow_nil => true
|
25
52
|
|
53
|
+
attr_accessor :mailing_list_ids
|
54
|
+
|
26
55
|
scope :reports, lambda {
|
27
56
|
select_sql = <<-SELECT.gsub(/\s+/, ' ')
|
28
57
|
campaigns.id campaign_id,
|
@@ -122,13 +151,34 @@ class Deal < ActiveRecord::Base
|
|
122
151
|
column_op(:eq, column, value, reverse)
|
123
152
|
}
|
124
153
|
|
125
|
-
scope :leads,
|
126
|
-
scope :pending,
|
127
|
-
scope :won,
|
128
|
-
scope :lost,
|
154
|
+
scope :leads, lambda {|reverse=true| column_eq(:status, Status::Lead, !reverse) }
|
155
|
+
scope :pending, lambda {|reverse=true| column_eq(:status, Status::Pending, !reverse) }
|
156
|
+
scope :won, lambda {|reverse=true| column_eq(:status, Status::Won, !reverse) }
|
157
|
+
scope :lost, lambda {|reverse=true| column_eq(:status, Status::Lost, !reverse) }
|
158
|
+
|
159
|
+
scope :category, lambda {|category| where(:category => category) }
|
160
|
+
scope :offer, lambda {|offer| where(:offer_id => offer.to_param) }
|
161
|
+
scope :owner, lambda {|owner| where(:contact_id => owner.to_param) }
|
162
|
+
scope :status, lambda {|status| where(:status => status) }
|
163
|
+
|
164
|
+
def custom_info
|
165
|
+
read_attribute(:options)
|
166
|
+
end
|
167
|
+
|
168
|
+
def custom_info=(v)
|
169
|
+
write_attribute(:options, v)
|
170
|
+
end
|
129
171
|
|
130
172
|
protected
|
131
173
|
|
174
|
+
def write_options(obj={})
|
175
|
+
@custom_options = obj.to_hash
|
176
|
+
end
|
177
|
+
|
178
|
+
def read_options
|
179
|
+
@custom_options ||= {}
|
180
|
+
end
|
181
|
+
|
132
182
|
def method_missing(method_name, *args)
|
133
183
|
if method_name =~ /(.*)\?$/ && Status::OPTIONS.member?($1)
|
134
184
|
self.status == $1
|
@@ -148,7 +198,7 @@ class Deal < ActiveRecord::Base
|
|
148
198
|
end
|
149
199
|
|
150
200
|
def _do_close
|
151
|
-
self.closed_at =
|
201
|
+
self.closed_at = Time.now.utc
|
152
202
|
notify_observers :before_close
|
153
203
|
end
|
154
204
|
|
@@ -158,6 +208,58 @@ class Deal < ActiveRecord::Base
|
|
158
208
|
self.status ||= Status::Pending
|
159
209
|
end
|
160
210
|
|
211
|
+
def transform_options_column
|
212
|
+
# NOTE this column can't be passed, it is only generated from
|
213
|
+
# the custom options pseudo column
|
214
|
+
self.custom_info = begin
|
215
|
+
options.to_hash.map do |k, v|
|
216
|
+
"%s:\n%s\n\n" % [k.to_s.titleize, v]
|
217
|
+
end.join
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def ensure_denormalized_columns
|
222
|
+
self.campaign_code ||= campaign.code if campaign.present?
|
223
|
+
self.offer_name ||= offer.name if offer.present?
|
224
|
+
end
|
225
|
+
|
226
|
+
def get_name_and_email_from_user
|
227
|
+
if lead? && user.present?
|
228
|
+
self.lead_email = user.email
|
229
|
+
self.lead_name = user.first_name
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def find_or_create_user
|
234
|
+
if lead? && user.blank? && lead_email
|
235
|
+
u = User.find_by_email(lead_email) || create_prospect
|
236
|
+
update_attribute(:user_id, u.id)
|
237
|
+
|
238
|
+
u.create_contact_if_missing!
|
239
|
+
self.contacts << u.contact
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def assign_user_mailing_lists
|
244
|
+
if @mailing_list_ids
|
245
|
+
user.mailing_list_ids |= @mailing_list_ids
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def update_to_pending_status
|
250
|
+
if self.status == Status::Lead
|
251
|
+
self.status = Status::Pending
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def create_prospect
|
256
|
+
create_user(
|
257
|
+
:email => lead_email,
|
258
|
+
:first_name => lead_name,
|
259
|
+
:status => User::Status::Prospect
|
260
|
+
)
|
261
|
+
end
|
262
|
+
|
161
263
|
module Status
|
162
264
|
OPTIONS = %w(lead pending won lost)
|
163
265
|
Lead = OPTIONS[0]
|