e9_crm 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. data/.gitignore +3 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +105 -0
  4. data/README.md +31 -0
  5. data/Rakefile +2 -0
  6. data/app/controllers/e9_crm/advertising_campaigns_controller.rb +3 -0
  7. data/app/controllers/e9_crm/affiliate_campaigns_controller.rb +3 -0
  8. data/app/controllers/e9_crm/base_controller.rb +5 -0
  9. data/app/controllers/e9_crm/campaign_groups_controller.rb +3 -0
  10. data/app/controllers/e9_crm/campaigns_controller.rb +4 -0
  11. data/app/controllers/e9_crm/companies_controller.rb +3 -0
  12. data/app/controllers/e9_crm/contact_emails_controller.rb +38 -0
  13. data/app/controllers/e9_crm/contact_merges_controller.rb +26 -0
  14. data/app/controllers/e9_crm/contacts_controller.rb +46 -0
  15. data/app/controllers/e9_crm/dated_costs_controller.rb +4 -0
  16. data/app/controllers/e9_crm/deals_controller.rb +19 -0
  17. data/app/controllers/e9_crm/email_campaigns_controller.rb +3 -0
  18. data/app/controllers/e9_crm/email_templates.controller.rb +3 -0
  19. data/app/controllers/e9_crm/menu_options_controller.rb +26 -0
  20. data/app/controllers/e9_crm/offers_controller.rb +3 -0
  21. data/app/controllers/e9_crm/page_views_controller.rb +20 -0
  22. data/app/controllers/e9_crm/record_attributes_controller.rb +3 -0
  23. data/app/controllers/e9_crm/reports_controller.rb +2 -0
  24. data/app/controllers/e9_crm/resources_controller.rb +43 -0
  25. data/app/controllers/e9_crm/sales_campaigns_controller.rb +3 -0
  26. data/app/helpers/e9_crm/base_helper.rb +77 -0
  27. data/app/helpers/e9_crm/campaigns_helper.rb +14 -0
  28. data/app/helpers/e9_crm/contact_merges_helper.rb +17 -0
  29. data/app/helpers/e9_crm/contacts_helper.rb +27 -0
  30. data/app/helpers/e9_crm/deals_helper.rb +15 -0
  31. data/app/helpers/e9_crm/menu_options_helper.rb +11 -0
  32. data/app/helpers/e9_crm/page_views_helper.rb +2 -0
  33. data/app/models/address_attribute.rb +4 -0
  34. data/app/models/advertising_campaign.rb +15 -0
  35. data/app/models/affiliate.rb +4 -0
  36. data/app/models/affiliate_campaign.rb +15 -0
  37. data/app/models/campaign.rb +34 -0
  38. data/app/models/campaign_group.rb +5 -0
  39. data/app/models/company.rb +4 -0
  40. data/app/models/contact.rb +258 -0
  41. data/app/models/contact_email.rb +49 -0
  42. data/app/models/dated_cost.rb +9 -0
  43. data/app/models/deal.rb +83 -0
  44. data/app/models/email_campaign.rb +13 -0
  45. data/app/models/email_template.rb +7 -0
  46. data/app/models/instant_messaging_handle_attribute.rb +4 -0
  47. data/app/models/menu_option.rb +33 -0
  48. data/app/models/offer.rb +50 -0
  49. data/app/models/page_view.rb +80 -0
  50. data/app/models/phone_number_attribute.rb +4 -0
  51. data/app/models/record_attribute.rb +41 -0
  52. data/app/models/sales_campaign.rb +15 -0
  53. data/app/models/sales_person.rb +4 -0
  54. data/app/models/tracking_cookie.rb +61 -0
  55. data/app/models/website_attribute.rb +4 -0
  56. data/app/observers/deal_observer.rb +11 -0
  57. data/app/uploaders/file_uploader.rb +34 -0
  58. data/app/views/e9_crm/campaigns/_form_inner.html.haml +5 -0
  59. data/app/views/e9_crm/contact_emails/_form.html.haml +7 -0
  60. data/app/views/e9_crm/contact_emails/_form_inner.html.haml +11 -0
  61. data/app/views/e9_crm/contact_emails/destroy.js.erb +3 -0
  62. data/app/views/e9_crm/contact_emails/send_email.js.erb +1 -0
  63. data/app/views/e9_crm/contact_merges/_field.html.haml +10 -0
  64. data/app/views/e9_crm/contact_merges/_form.html.haml +10 -0
  65. data/app/views/e9_crm/contact_merges/new.html.haml +2 -0
  66. data/app/views/e9_crm/contacts/_details.html.haml +22 -0
  67. data/app/views/e9_crm/contacts/_form_inner.html.haml +51 -0
  68. data/app/views/e9_crm/contacts/_header.html.haml +19 -0
  69. data/app/views/e9_crm/contacts/_tag_table.html.haml +15 -0
  70. data/app/views/e9_crm/contacts/index.html.haml +13 -0
  71. data/app/views/e9_crm/contacts/index.js.erb +5 -0
  72. data/app/views/e9_crm/contacts/merge.html.haml +1 -0
  73. data/app/views/e9_crm/contacts/templates.js.erb +1 -0
  74. data/app/views/e9_crm/deals/_form_inner.html.haml +5 -0
  75. data/app/views/e9_crm/deals/_header.html.haml +0 -0
  76. data/app/views/e9_crm/deals/leads.html.haml +13 -0
  77. data/app/views/e9_crm/email_templates/_form_inner.html.haml +9 -0
  78. data/app/views/e9_crm/menu_options/_form_inner.html.haml +6 -0
  79. data/app/views/e9_crm/menu_options/_header.html.haml +8 -0
  80. data/app/views/e9_crm/offers/_form.html.haml +7 -0
  81. data/app/views/e9_crm/offers/_form_inner.html.haml +42 -0
  82. data/app/views/e9_crm/offers/_form_inner.html.haml.bak +43 -0
  83. data/app/views/e9_crm/page_views/_table.html.haml +25 -0
  84. data/app/views/e9_crm/record_attributes/_address_attribute.html.haml +5 -0
  85. data/app/views/e9_crm/record_attributes/_instant_messaging_handle_attribute.html.haml +5 -0
  86. data/app/views/e9_crm/record_attributes/_phone_number_attribute.html.haml +5 -0
  87. data/app/views/e9_crm/record_attributes/_record_attribute.html.haml +10 -0
  88. data/app/views/e9_crm/record_attributes/_templates.js.erb +12 -0
  89. data/app/views/e9_crm/record_attributes/_user.html.haml +23 -0
  90. data/app/views/e9_crm/record_attributes/_website_attribute.html.haml +5 -0
  91. data/app/views/e9_crm/resources/_footer.html.haml +1 -0
  92. data/app/views/e9_crm/resources/_form.html.haml +7 -0
  93. data/app/views/e9_crm/resources/_form_inner.html.haml +5 -0
  94. data/app/views/e9_crm/resources/_header.html.haml +0 -0
  95. data/app/views/e9_crm/resources/_table.html.haml +21 -0
  96. data/app/views/e9_crm/resources/create.js.erb +6 -0
  97. data/app/views/e9_crm/resources/destroy.js.erb +3 -0
  98. data/app/views/e9_crm/resources/edit.html.haml +2 -0
  99. data/app/views/e9_crm/resources/index.html.haml +13 -0
  100. data/app/views/e9_crm/resources/index.js.erb +1 -0
  101. data/app/views/e9_crm/resources/new.html.haml +2 -0
  102. data/app/views/e9_crm/resources/show.html.haml +2 -0
  103. data/app/views/e9_crm/resources/update.js.erb +5 -0
  104. data/config/initializers/inflections.rb +3 -0
  105. data/config/locales/e9.en.yml +28 -0
  106. data/config/locales/en.yml +63 -0
  107. data/config/routes.rb +61 -0
  108. data/e9_crm.gemspec +29 -0
  109. data/lib/e9_crm/model.rb +63 -0
  110. data/lib/e9_crm/rails_extensions.rb +98 -0
  111. data/lib/e9_crm/tracking_controller.rb +78 -0
  112. data/lib/e9_crm/version.rb +3 -0
  113. data/lib/e9_crm.rb +59 -0
  114. data/lib/generators/e9_crm/install_generator.rb +32 -0
  115. data/lib/generators/e9_crm/templates/create_e9_crm_tables.rb +107 -0
  116. data/lib/generators/e9_crm/templates/initializer.rb +4 -0
  117. data/lib/generators/e9_crm/templates/javascript.js +187 -0
  118. data/test/functional/e9_crm/campaign_types_controller_test.rb +49 -0
  119. data/test/functional/home_controller_test.rb +8 -0
  120. metadata +283 -0
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .bundle
2
+ *.swp
3
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,105 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ e9_crm (0.1.0)
5
+ e9_rails (~> 0.0.4)
6
+ e9_tags (~> 0.0.2)
7
+ has_scope
8
+ inherited_resources (~> 1.1.2)
9
+ money
10
+ rails (~> 3.0.0)
11
+ will_paginate
12
+
13
+ GEM
14
+ remote: http://rubygems.org/
15
+ specs:
16
+ abstract (1.0.0)
17
+ actionmailer (3.0.5)
18
+ actionpack (= 3.0.5)
19
+ mail (~> 2.2.15)
20
+ actionpack (3.0.5)
21
+ activemodel (= 3.0.5)
22
+ activesupport (= 3.0.5)
23
+ builder (~> 2.1.2)
24
+ erubis (~> 2.6.6)
25
+ i18n (~> 0.4)
26
+ rack (~> 1.2.1)
27
+ rack-mount (~> 0.6.13)
28
+ rack-test (~> 0.5.7)
29
+ tzinfo (~> 0.3.23)
30
+ activemodel (3.0.5)
31
+ activesupport (= 3.0.5)
32
+ builder (~> 2.1.2)
33
+ i18n (~> 0.4)
34
+ activerecord (3.0.5)
35
+ activemodel (= 3.0.5)
36
+ activesupport (= 3.0.5)
37
+ arel (~> 2.0.2)
38
+ tzinfo (~> 0.3.23)
39
+ activeresource (3.0.5)
40
+ activemodel (= 3.0.5)
41
+ activesupport (= 3.0.5)
42
+ activesupport (3.0.5)
43
+ acts-as-taggable-on (2.0.6)
44
+ arel (2.0.9)
45
+ builder (2.1.2)
46
+ e9_rails (0.0.4)
47
+ inherited_resources
48
+ rails (~> 3.0.0)
49
+ e9_tags (0.0.2)
50
+ acts-as-taggable-on (~> 2.0.6)
51
+ rails (~> 3.0.0)
52
+ erubis (2.6.6)
53
+ abstract (>= 1.0.0)
54
+ has_scope (0.5.0)
55
+ i18n (0.5.0)
56
+ inherited_resources (1.1.2)
57
+ has_scope (~> 0.5.0)
58
+ responders (~> 0.6.0)
59
+ mail (2.2.15)
60
+ activesupport (>= 2.3.6)
61
+ i18n (>= 0.4.0)
62
+ mime-types (~> 1.16)
63
+ treetop (~> 1.4.8)
64
+ mime-types (1.16)
65
+ money (3.6.1)
66
+ i18n (~> 0.4)
67
+ polyglot (0.3.1)
68
+ rack (1.2.1)
69
+ rack-mount (0.6.13)
70
+ rack (>= 1.0.0)
71
+ rack-test (0.5.7)
72
+ rack (>= 1.0)
73
+ rails (3.0.5)
74
+ actionmailer (= 3.0.5)
75
+ actionpack (= 3.0.5)
76
+ activerecord (= 3.0.5)
77
+ activeresource (= 3.0.5)
78
+ activesupport (= 3.0.5)
79
+ bundler (~> 1.0)
80
+ railties (= 3.0.5)
81
+ railties (3.0.5)
82
+ actionpack (= 3.0.5)
83
+ activesupport (= 3.0.5)
84
+ rake (>= 0.8.7)
85
+ thor (~> 0.14.4)
86
+ rake (0.8.7)
87
+ responders (0.6.2)
88
+ thor (0.14.6)
89
+ treetop (1.4.9)
90
+ polyglot (>= 0.3.1)
91
+ tzinfo (0.3.24)
92
+ will_paginate (2.3.15)
93
+
94
+ PLATFORMS
95
+ ruby
96
+
97
+ DEPENDENCIES
98
+ e9_crm!
99
+ e9_rails (~> 0.0.4)
100
+ e9_tags (~> 0.0.2)
101
+ has_scope
102
+ inherited_resources (~> 1.1.2)
103
+ money
104
+ rails (~> 3.0.0)
105
+ will_paginate
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ CRM Plugin for the e9 CMS
2
+ =========================
3
+
4
+ It should be noted that while this plugin is intended for use in the
5
+ e9 CMS, it does neither includes nor requires it.
6
+
7
+ To use, add as a gem and install by running:
8
+
9
+ rails g e9_crm:install
10
+
11
+ Then modify the installed initializer as per your app, including
12
+ the controller module in your desired controllers, with the final
13
+ result looking something like this:
14
+
15
+ require 'e9_crm'
16
+
17
+ User.send :include, E9Crm::Backend::ActiveRecord
18
+
19
+ Rails.configuration.after_initialize do
20
+ [
21
+ MyFirstTrackedController,
22
+ MySecondTrackedController
23
+ ].each {|c| c.send(:include, E9Crm::TrackingController) }
24
+ end
25
+
26
+ NOTE: A few assumptions are made:
27
+ ---------------------------------
28
+
29
+ 1. Your app has a "User" model
30
+ 2. Your app has a controller method #current_user to return the
31
+ currently logged in user.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler"
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,3 @@
1
+ class E9Crm::AdvertisingCampaignsController < E9Crm::ResourcesController
2
+ defaults :resource_class => AdvertisingCampaign
3
+ end
@@ -0,0 +1,3 @@
1
+ class E9Crm::AffiliateCampaignsController < E9Crm::ResourcesController
2
+ defaults :resource_class => AffiliateCampaign
3
+ end
@@ -0,0 +1,5 @@
1
+ class E9Crm::BaseController < AdminController
2
+ include E9Rails::Helpers::ResourceLinks
3
+ include E9Rails::Helpers::Title
4
+ include E9Rails::Helpers::Translation
5
+ end
@@ -0,0 +1,3 @@
1
+ class E9Crm::CampaignGroupsController < E9Crm::ResourcesController
2
+ defaults :resource_class => CampaignGroup
3
+ end
@@ -0,0 +1,4 @@
1
+ class E9Crm::CampaignsController < E9Crm::ResourcesController
2
+ defaults :resource_class => Campaign
3
+ has_scope :of_type, :as => :t, :only => :index
4
+ end
@@ -0,0 +1,3 @@
1
+ class E9Crm::CompaniesController < E9Crm::ResourcesController
2
+ defaults :resource_class => Company
3
+ end
@@ -0,0 +1,38 @@
1
+ class E9Crm::ContactEmailsController < E9Crm::ResourcesController
2
+ defaults
3
+
4
+ respond_to :js, :html
5
+
6
+ def create
7
+ create! do |success, failure|
8
+ success.html { redirect_to :admin_sent_email }
9
+ success.js { head :ok }
10
+ end
11
+ end
12
+
13
+ protected
14
+
15
+ def build_resource
16
+ get_resource_ivar || begin
17
+ object = if params[resource_instance_name]
18
+ ContactEmail.new(params[resource_instance_name] || {})
19
+ else
20
+ ContactEmail.new_from_template(template, :from_email => current_user.email, :user_ids => params[:uids])
21
+ end
22
+
23
+ # we set the user_ids.blank? error right away signifiying a problem,
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
28
+
29
+ set_resource_ivar(object)
30
+ end
31
+ end
32
+
33
+ # throw record_not_found if there's no template. #new requires email_template_id
34
+ # be passed in params (and also user_ids)
35
+ def template
36
+ @_template ||= EmailTemplate.find(params[:etid])
37
+ end
38
+ end
@@ -0,0 +1,26 @@
1
+ class E9Crm::ContactMergesController < E9Crm::BaseController
2
+ before_filter :build_resources
3
+
4
+ def create
5
+ if @contact_a.update_attributes(params[:contact])
6
+ @contact_a.merge_and_destroy!(@contact_b)
7
+ redirect_to @contact_a
8
+ else
9
+ render :new
10
+ end
11
+ end
12
+
13
+ protected
14
+
15
+ def build_resources
16
+ @contact ||= Contact.new
17
+ @contact_a ||= Contact.find(params[:contact_a_id])
18
+
19
+ # NOTE lets see if this works
20
+ @contact_b ||= if params[:contact]
21
+ Contact.new(params[:contact])
22
+ else
23
+ Contact.find(params[:contact_b_id])
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,46 @@
1
+ class E9Crm::ContactsController < E9Crm::ResourcesController
2
+ defaults :resource_class => Contact
3
+
4
+ include E9Tags::Controller
5
+
6
+ respond_to :js, :html
7
+
8
+ before_filter :determine_title, :only => :index
9
+ before_filter :load_user_ids, :only => :index
10
+
11
+ has_scope :search, :by_title, :by_company, :only => :index
12
+ has_scope :tagged, :only => :index, :type => :array
13
+
14
+ # record attributes templates js
15
+ skip_before_filter :authenticate_user!, :filter_access_filter, :only => :templates
16
+ before_filter :build_resource, :only => :templates
17
+ caches_action :templates
18
+
19
+ protected
20
+
21
+ def load_user_ids
22
+ @user_ids ||= begin
23
+ (User.primary.joins(:contact) & end_of_association_chain.scoped).all.map(&:id)
24
+ end
25
+ end
26
+
27
+ def determine_title
28
+ params.delete(:search) if params[:search].blank?
29
+
30
+ @index_title ||= if params[:tagged] && params[:search]
31
+ e9_t(:index_title_with_search_and_tags, :tagged => params[:tagged].join(' or '), :search => params[:search])
32
+ elsif params[:tagged]
33
+ e9_t(:index_title_with_tags, :tagged => params[:tagged].join(' or '))
34
+ elsif params[:search]
35
+ e9_t(:index_title_with_search, :search => params[:search])
36
+ end
37
+ end
38
+
39
+ def default_ordered_on
40
+ 'first_name'
41
+ end
42
+
43
+ def default_ordered_at
44
+ 'ASC'
45
+ end
46
+ end
@@ -0,0 +1,4 @@
1
+ class E9Crm::DatedCostsController < E9Crm::ResourcesController
2
+ belongs_to :advertising_campaign
3
+ defaults :resource_class => DatedCost
4
+ end
@@ -0,0 +1,19 @@
1
+ class E9Crm::DealsController < E9Crm::ResourcesController
2
+ defaults :resource_class => Deal
3
+
4
+ filter_access_to :leads, :require => :read, :context => :admin
5
+ prepend_before_filter :set_leads_index_title, :only => :leads
6
+
7
+ has_scope :leads, :only => :leads, :default => true
8
+ has_scope :leads, :except => :leads, :default => false
9
+
10
+ def leads
11
+ index!
12
+ end
13
+
14
+ protected
15
+
16
+ def set_leads_index_title
17
+ @index_title = I18n.t(:index_title, :scope => 'e9.e9_crm.leads')
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ class E9Crm::EmailCampaignsController < E9Crm::ResourcesController
2
+ defaults :resource_class => EmailCampaign
3
+ end
@@ -0,0 +1,3 @@
1
+ class E9Crm::EmailTemplatesController < E9Crm::ResourcesController
2
+ defaults
3
+ end
@@ -0,0 +1,26 @@
1
+ class E9Crm::MenuOptionsController < E9Crm::ResourcesController
2
+ defaults :resource_class => MenuOption
3
+ include E9Rails::Controllers::Sortable
4
+
5
+ has_scope :options_for, :as => :key, :only => :index
6
+
7
+ # NOTE The reason this is set in a filter instead of just a default scope value
8
+ # is that it is used in the index view to add the key param to the new
9
+ # resource link
10
+ #
11
+ before_filter :ensure_default_fetch_key, :only => :index
12
+
13
+ def create
14
+ create! { collection_path(:key => resource.key) }
15
+ end
16
+
17
+ def update
18
+ update! { collection_path(:key => resource.key) }
19
+ end
20
+
21
+ protected
22
+
23
+ def ensure_default_fetch_key
24
+ params[:key] ||= MenuOption::KEYS.sort.first
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ class E9Crm::OffersController < E9Crm::ResourcesController
2
+ defaults :resource_class => Offer
3
+ end
@@ -0,0 +1,20 @@
1
+ class E9Crm::PageViewsController < E9Crm::ResourcesController
2
+ defaults :resource_class => PageView
3
+ belongs_to :campaign, :contact, :polymorphic => true
4
+
5
+ # NOTE association chain is prepended to ensure parent is loaded so other
6
+ # before filters can use collection_path, etc. Is there a better solution
7
+ # for this?
8
+ #
9
+ prepend_before_filter :association_chain
10
+
11
+ has_scope :until_time, :as => :until, :unless => 'params[:from].present?'
12
+
13
+ has_scope :from_time, :as => :from do |controller, scope, value|
14
+ if controller.params[:until]
15
+ scope.for_time_range(value, controller.params[:until])
16
+ else
17
+ scope.from_time(value)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ class E9Crm::RecordAttributesController < ApplicationController
2
+ helper 'e9_crm/base'
3
+ end
@@ -0,0 +1,2 @@
1
+ class E9Crm::ReportsController < E9Crm::BaseController
2
+ end
@@ -0,0 +1,43 @@
1
+ class E9Crm::ResourcesController < E9Crm::BaseController
2
+ include E9Rails::Controllers::Orderable
3
+ include E9Rails::Helpers::ResourceErrorMessages
4
+ include E9Rails::Helpers::Pagination
5
+
6
+ inherit_resources
7
+
8
+ add_resource_breadcrumbs
9
+
10
+ def self.defaults(hash = {})
11
+ super(hash.reverse_merge(:route_prefix => nil))
12
+ end
13
+
14
+ def create
15
+ create! { collection_path }
16
+ end
17
+
18
+ def update
19
+ update! { collection_path }
20
+ end
21
+
22
+ protected
23
+
24
+ # NOTE parent is defined so it's always available, it will be overridden on controllers which have belongs_to routes
25
+ def parent; end
26
+ helper_method :parent
27
+
28
+ def add_index_breadcrumb
29
+ add_breadcrumb! @index_title || e9_t(:index_title), collection_path
30
+ end
31
+
32
+ def collection
33
+ get_collection_ivar || set_collection_ivar(end_of_association_chain.paginate(pagination_parameters))
34
+ end
35
+
36
+ def default_ordered_on
37
+ 'created_at'
38
+ end
39
+
40
+ def default_ordered_dir
41
+ 'DESC'
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ class E9Crm::SalesCampaignsController < E9Crm::ResourcesController
2
+ defaults :resource_class => SalesCampaign
3
+ end
@@ -0,0 +1,77 @@
1
+ module E9Crm::BaseHelper
2
+
3
+ ##
4
+ # Field maps
5
+ #
6
+
7
+ def records_table_field_map(options = {})
8
+ options.symbolize_keys!
9
+ options.reverse_merge!(:class_name => resource_class.base_class.name.underscore)
10
+
11
+ base_map = {
12
+ :fields => { :id => nil },
13
+ :links => lambda {|r| [link_to_edit_resource(r), link_to_destroy_resource(r)] }
14
+ }
15
+
16
+ method_name = "records_table_field_map_for_#{options[:class_name]}"
17
+
18
+ if respond_to?(method_name)
19
+ base_map.merge! send(method_name)
20
+ end
21
+
22
+ base_map
23
+ end
24
+
25
+
26
+ ##
27
+ # Misc
28
+ #
29
+
30
+ def link_to_add_record_attribute(association_name)
31
+ link_to(
32
+ t(:add_record_attribute, :scope => :e9_crm),
33
+ 'javascript:;',
34
+ :class => 'add-nested-association',
35
+ 'data-association' => association_name
36
+ )
37
+ end
38
+
39
+ def link_to_destroy_record_attribute
40
+ link_to(
41
+ t(:destroy_record_attribute, :scope => :e9_crm),
42
+ 'javascript:;',
43
+ :class => 'destroy-nested-association'
44
+ )
45
+ end
46
+
47
+ def render_record_attribute_association(association_name, form, options = {})
48
+ options.symbolize_keys!
49
+
50
+ association = resource.send(association_name)
51
+
52
+ unless association.empty?
53
+ form.fields_for(association_name) do |f|
54
+ concat record_attribute_template(association_name, f, options)
55
+ end
56
+ end
57
+ end
58
+
59
+ def record_attribute_template(association_name, builder, options = {})
60
+ options.symbolize_keys!
61
+
62
+ render({
63
+ :partial => options[:partial] || "e9_crm/record_attributes/#{association_name.to_s.singularize}",
64
+ :locals => { :f => builder }
65
+ })
66
+ end
67
+
68
+ def build_associated_resource(association_name)
69
+ params_method = "#{association_name}_build_parameters"
70
+ build_params = resource_class.send(params_method) if resource_class.respond_to?(params_method)
71
+ resource.send(association_name).build(build_params || {})
72
+ end
73
+
74
+ def sortable_controller?
75
+ @_sortable_controller ||= controller.class.ancestors.member?(E9Rails::Controllers::Sortable)
76
+ end
77
+ end
@@ -0,0 +1,14 @@
1
+ module E9Crm::CampaignsHelper
2
+ def records_table_map_for_campaign
3
+ {
4
+ :fields => {
5
+ :type => nil,
6
+ :name => nil,
7
+ :code => nil,
8
+ :affiliate_fee => proc {|r| v = r.affiliate_fee; Money === v ? v : 'n/a' },
9
+ :sales_fee => nil,
10
+ :status => proc {|r| resource_class.human_attribute_name(Campaign::Status::VALUES[r.status]) }
11
+ }
12
+ }
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ module E9Crm::ContactMergesHelper
2
+ def contact_a(field_name)
3
+ @_contact_a_vals ||= {}
4
+ @_contact_a_vals[field_name] ||= merge_contact_val(@contact_a, field_name)
5
+ end
6
+
7
+ def contact_b(field_name)
8
+ @_contact_b_vals ||= {}
9
+ @_contact_b_vals[field_name] ||= merge_contact_val(@contact_b, field_name)
10
+ end
11
+
12
+ def merge_contact_val(obj, field_name)
13
+ # return '' here rather than nil so inputs will have a value, otherwise checked
14
+ # with no value ends up being interpreted as "on"
15
+ obj.send(field_name) || ''
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ module E9Crm::ContactsHelper
2
+
3
+ def link_to_contact_search(attribute, query, text = nil)
4
+ link_to(text || query, contacts_path(attribute => query), :class => "contact-search contact-#{attribute.to_s.dasherize}-search")
5
+ end
6
+
7
+ def contact_email_template_select_options
8
+ options_for_select( EmailTemplate.order('name').map {|e| [e.name, e.id] })
9
+ end
10
+
11
+ def contact_newsletter_select_options
12
+ options_for_select( UserEmail.pending.order('name').map {|e| [e.name, e.id] })
13
+ end
14
+
15
+ def records_table_field_map_for_contact
16
+ {
17
+ :fields => {
18
+ :avatar => proc {|r| },
19
+ :details => proc {|r| render('details', :record => r) }
20
+ },
21
+
22
+ :links => proc {|r|
23
+ [link_to_show_resource(r), link_to_edit_resource(r), link_to_destroy_resource(r)]
24
+ }
25
+ }
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ module E9Crm::DealsHelper
2
+ def records_table_field_map_for_deal
3
+ {
4
+ :fields => {
5
+ :created_at => nil,
6
+ :offer => nil,
7
+ :campaign => nil
8
+ },
9
+
10
+ :links => proc {|r|
11
+ []
12
+ }
13
+ }
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module E9Crm::MenuOptionsHelper
2
+ def records_table_field_map_for_menu_option
3
+ {
4
+ :fields => {
5
+ :position => proc { '<div class="handle">+++</div>'.html_safe },
6
+ :value => nil,
7
+ :key => nil
8
+ }
9
+ }
10
+ end
11
+ end
@@ -0,0 +1,2 @@
1
+ module E9Crm::PageViewsHelper
2
+ end
@@ -0,0 +1,4 @@
1
+ # Attribute for address info
2
+ #
3
+ class AddressAttribute < RecordAttribute
4
+ end
@@ -0,0 +1,15 @@
1
+ # An ad driven campaign.
2
+ #
3
+ # Unique from other campaigns in that their cost is derived from associated
4
+ # DatedCost records.
5
+ #
6
+ class AdvertisingCampaign < Campaign
7
+ has_many :dated_costs, :as => :costable
8
+
9
+ ##
10
+ # The sum cost of this campaign
11
+ #
12
+ def cost
13
+ Money.new(dated_costs.sum(:cost))
14
+ end
15
+ end
@@ -0,0 +1,4 @@
1
+ # A contact subtype for affiliates, who drive affiliate campaigns
2
+ #
3
+ class Affiliate < Contact
4
+ end
@@ -0,0 +1,15 @@
1
+ # An affiliate campaign
2
+ #
3
+ # Carries an affiliate fee (and sales fee)
4
+ #
5
+ class AffiliateCampaign < SalesCampaign
6
+ money_columns :affiliate_fee
7
+ belongs_to :affiliate
8
+
9
+ ##
10
+ # The sum cost of this campaign
11
+ #
12
+ def cost
13
+ super + affiliate_fee
14
+ end
15
+ end