e9_crm 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.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]
|