e9_crm 0.1.8 → 0.1.10

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.
Files changed (42) hide show
  1. data/README.md +2 -3
  2. data/app/controllers/e9_crm/advertising_campaigns_controller.rb +2 -0
  3. data/app/controllers/e9_crm/affiliate_campaigns_controller.rb +2 -0
  4. data/app/controllers/e9_crm/campaign_groups_controller.rb +8 -4
  5. data/app/controllers/e9_crm/campaign_subclass_controller.rb +15 -0
  6. data/app/controllers/e9_crm/campaigns_controller.rb +15 -23
  7. data/app/controllers/e9_crm/companies_controller.rb +11 -0
  8. data/app/controllers/e9_crm/email_campaigns_controller.rb +2 -0
  9. data/app/controllers/e9_crm/menu_options_controller.rb +2 -0
  10. data/app/controllers/e9_crm/resources_controller.rb +11 -1
  11. data/app/controllers/e9_crm/sales_campaigns_controller.rb +2 -0
  12. data/app/helpers/e9_crm/base_helper.rb +29 -0
  13. data/app/helpers/e9_crm/campaign_groups_helper.rb +1 -1
  14. data/app/helpers/e9_crm/campaigns_helper.rb +3 -4
  15. data/app/models/affiliate_campaign.rb +3 -1
  16. data/app/models/campaign.rb +7 -2
  17. data/app/models/campaign_group.rb +3 -1
  18. data/app/models/company.rb +5 -0
  19. data/app/models/contact.rb +19 -1
  20. data/app/models/menu_option.rb +4 -3
  21. data/app/models/sales_campaign.rb +3 -1
  22. data/app/views/e9_crm/affiliate_campaigns/_form_inner.html.haml +3 -3
  23. data/app/views/e9_crm/campaign_groups/_form_inner.html.haml +3 -0
  24. data/app/views/e9_crm/campaigns/_form_inner.html.haml +5 -8
  25. data/app/views/e9_crm/campaigns/_table.html.haml +2 -2
  26. data/app/views/e9_crm/companies/_header.html.haml +4 -0
  27. data/app/views/e9_crm/contacts/_header.html.haml +0 -21
  28. data/app/views/e9_crm/contacts/_index_sidebar.html.haml +1 -1
  29. data/app/views/e9_crm/contacts/show.html.haml +3 -1
  30. data/app/views/e9_crm/menu_options/_footer.html.haml +0 -0
  31. data/app/views/e9_crm/menu_options/_form_inner.html.haml +1 -1
  32. data/app/views/e9_crm/resources/_table.html.haml +5 -2
  33. data/app/views/e9_crm/sales_campaigns/_form_inner.html.haml +2 -2
  34. data/config/locales/e9.en.yml +0 -1
  35. data/config/locales/en.yml +11 -1
  36. data/e9_crm.gemspec +1 -0
  37. data/lib/e9_crm/e9_extensions.rb +20 -0
  38. data/lib/e9_crm/email.rb +16 -0
  39. data/lib/e9_crm/rails_extensions.rb +32 -1
  40. data/lib/e9_crm/version.rb +1 -1
  41. data/lib/e9_crm.rb +5 -0
  42. metadata +20 -5
data/README.md CHANGED
@@ -1,9 +1,8 @@
1
+ *NOTE his plugin requires the private e9_base CMS gem and WILL NOT WORK without it.*
2
+
1
3
  CRM Plugin for the e9 CMS
2
4
  =========================
3
5
 
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
6
  To use, add as a gem and install by running:
8
7
 
9
8
  rails g e9_crm:install
@@ -1,4 +1,6 @@
1
1
  class E9Crm::AdvertisingCampaignsController < E9Crm::CampaignSubclassController
2
2
  defaults :resource_class => AdvertisingCampaign
3
3
  include E9Rails::Controllers::Orderable
4
+
5
+ helper 'e9_crm/campaigns'
4
6
  end
@@ -1,4 +1,6 @@
1
1
  class E9Crm::AffiliateCampaignsController < E9Crm::CampaignSubclassController
2
2
  defaults :resource_class => AffiliateCampaign
3
3
  include E9Rails::Controllers::Orderable
4
+
5
+ helper 'e9_crm/campaigns'
4
6
  end
@@ -1,11 +1,15 @@
1
1
  class E9Crm::CampaignGroupsController < E9Crm::ResourcesController
2
2
  defaults :resource_class => CampaignGroup
3
3
  include E9Rails::Controllers::Orderable
4
+ self.should_paginate_index = false
4
5
 
5
6
  protected
6
-
7
- # no pagination
8
- def collection
9
- get_collection_ivar || set_collection_ivar(end_of_association_chain.all)
7
+
8
+ def default_ordered_on
9
+ 'name'
10
+ end
11
+
12
+ def default_ordered_dir
13
+ 'ASC'
10
14
  end
11
15
  end
@@ -1,6 +1,21 @@
1
1
  class E9Crm::CampaignSubclassController < E9Crm::ResourcesController
2
2
 
3
+ def update
4
+ update! { parent_redirect_path }
5
+ end
6
+
7
+ def create
8
+ create! { parent_redirect_path }
9
+ end
10
+
3
11
  protected
12
+ def parent_redirect_path
13
+ campaigns_path(:type => type_param)
14
+ end
15
+
16
+ def type_param
17
+ resource_class.name[/(.*)Campaign/, 1].underscore
18
+ end
4
19
 
5
20
  def add_index_breadcrumb
6
21
  add_breadcrumb! Campaign.model_name.collection.titleize, campaigns_path
@@ -2,11 +2,11 @@ class E9Crm::CampaignsController < E9Crm::ResourcesController
2
2
  defaults :resource_class => Campaign
3
3
  include E9Rails::Controllers::Orderable
4
4
 
5
- filter_access_to :reports, :require => :read, :context => :admin
5
+ self.should_paginate_index = false
6
6
 
7
7
  has_scope :of_group, :as => :group, :only => :index
8
8
 
9
- has_scope :active, :only => :index do |_, scope, value|
9
+ has_scope :active, :only => :index, :default => 'true' do |_, scope, value|
10
10
  scope.active(E9.true_value?(value))
11
11
  end
12
12
 
@@ -14,31 +14,23 @@ class E9Crm::CampaignsController < E9Crm::ResourcesController
14
14
  scope.of_type("#{value}_campaign".classify)
15
15
  end
16
16
 
17
- def reports
18
- index!
19
- end
20
-
21
17
  protected
22
18
 
23
- def set_reports_index_title
24
- @index_title = I18n.t(:reports_title, :scope => 'e9.e9_crm.campaigns')
19
+ def collection_scope
20
+ end_of_association_chain.typed.includes(:campaign_group)
21
+
22
+ # NOTE this is a pretty ugly join just to be able to sort on campaign group name
23
+ end_of_association_chain.typed
24
+ .joins("left outer join campaign_groups on campaign_groups.id = campaigns.campaign_group_id")
25
+ .select("campaigns.*, campaign_groups.name campaign_group_name")
25
26
  end
26
27
 
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
- else
34
- # don't include NoCampaign normally
35
- scope = scope.typed
36
- end
37
- scope
28
+ def default_ordered_on
29
+ 'campaign_group_name,name'
38
30
  end
39
-
40
- # no pagination
41
- def collection
42
- get_collection_ivar || set_collection_ivar(collection_scope.all)
31
+
32
+ def default_ordered_dir
33
+ 'ASC'
43
34
  end
35
+
44
36
  end
@@ -1,4 +1,15 @@
1
1
  class E9Crm::CompaniesController < E9Crm::ResourcesController
2
2
  defaults :resource_class => Company
3
3
  include E9Rails::Controllers::Orderable
4
+ include E9::DestroyRestricted::Controller
5
+
6
+ protected
7
+
8
+ def default_ordered_on
9
+ 'name'
10
+ end
11
+
12
+ def default_ordered_dir
13
+ 'ASC'
14
+ end
4
15
  end
@@ -1,4 +1,6 @@
1
1
  class E9Crm::EmailCampaignsController < E9Crm::CampaignSubclassController
2
2
  defaults :resource_class => EmailCampaign
3
3
  include E9Rails::Controllers::Orderable
4
+
5
+ helper 'e9_crm/campaigns'
4
6
  end
@@ -2,6 +2,8 @@ class E9Crm::MenuOptionsController < E9Crm::ResourcesController
2
2
  defaults :resource_class => MenuOption
3
3
  include E9Rails::Controllers::Sortable
4
4
 
5
+ self.should_paginate_index = false
6
+
5
7
  has_scope :options_for, :as => :key, :only => :index
6
8
 
7
9
  # NOTE The reason this is set in a filter instead of just a default scope value
@@ -1,6 +1,12 @@
1
1
  class E9Crm::ResourcesController < E9Crm::BaseController
2
2
  include E9Rails::Helpers::ResourceErrorMessages
3
- include E9Rails::Helpers::Pagination
3
+
4
+ # NOTE depending on e9_base pagination (which should eventually use this module)
5
+ #include E9Rails::Helpers::Pagination
6
+
7
+ # TODO implement role on e9_crm models?
8
+ #include E9::Roles::Controller
9
+ #filter_access_to :update, :edit, :attribute_check => true, :load_method => :filter_target, :context => :admin
4
10
 
5
11
  class_inheritable_accessor :should_paginate_index
6
12
  self.should_paginate_index = true
@@ -25,6 +31,10 @@ class E9Crm::ResourcesController < E9Crm::BaseController
25
31
 
26
32
  protected
27
33
 
34
+ def filter_target
35
+ resource
36
+ end
37
+
28
38
  # NOTE parent is defined so it's always available, it will be overridden on controllers which have belongs_to routes
29
39
  def parent; end
30
40
  helper_method :parent
@@ -1,4 +1,6 @@
1
1
  class E9Crm::SalesCampaignsController < E9Crm::CampaignSubclassController
2
2
  defaults :resource_class => SalesCampaign
3
3
  include E9Rails::Controllers::Orderable
4
+
5
+ helper 'e9_crm/campaigns'
4
6
  end
@@ -1,5 +1,34 @@
1
1
  module E9Crm::BaseHelper
2
2
 
3
+ def kramdown(string)
4
+ Kramdown::Document.new(string).to_html.html_safe
5
+ end
6
+
7
+ alias :k :kramdown
8
+
9
+ def help_tooltip(string)
10
+ return <<-HTML.strip.html_safe
11
+ <span class="help" rel="tooltip" title="#{CGI.escape_html(string)}">#{t(:inline_help_link)}</span>
12
+ HTML
13
+ end
14
+
15
+ def help_label(form_or_id, key, options = {})
16
+ options[:key] ||= :"#{key}_help"
17
+
18
+ help_title = options.delete(:title) || resource_humanize(options.delete(:key))
19
+
20
+ str = ''.html_safe
21
+ str.safe_concat resource_humanize(key)
22
+ str.safe_concat ' '
23
+ str.safe_concat help_tooltip(help_title)
24
+
25
+ if form_or_id.respond_to?(:label)
26
+ form_or_id.label(key, str, options)
27
+ else
28
+ label_tag(form_or_id, str, options)
29
+ end
30
+ end
31
+
3
32
  ##
4
33
  # Field maps
5
34
  #
@@ -1,7 +1,7 @@
1
1
  module E9Crm::CampaignGroupsHelper
2
2
  def records_table_field_map_for_campaign_group
3
3
  {
4
- :fields => { :name => nil },
4
+ :fields => { 'name' => nil },
5
5
  :links => proc {|r| [link_to_edit_resource(r), link_to_destroy_resource(r)] }
6
6
  }
7
7
  end
@@ -4,7 +4,7 @@ module E9Crm::CampaignsHelper
4
4
  end
5
5
 
6
6
  def display_campaign_code(val)
7
- val || 'n/a'
7
+ val && "?#{E9Crm.query_param}=#{val}" || 'n/a'
8
8
  end
9
9
 
10
10
  def display_campaign_type(val)
@@ -18,7 +18,7 @@ module E9Crm::CampaignsHelper
18
18
  end
19
19
 
20
20
  def campaign_group_select_options
21
- options = CampaignGroup.select('name, id').all.map {|c| [c.name, c.id] }
21
+ options = CampaignGroup.select('name, id').ordered.all.map {|c| [c.name, c.id] }
22
22
  options.unshift(['All Groups', nil])
23
23
  options_for_select(options)
24
24
  end
@@ -29,8 +29,7 @@ module E9Crm::CampaignsHelper
29
29
 
30
30
  def campaign_active_select_options
31
31
  options = [
32
- ['All Statuses', nil],
33
- ['Active', true],
32
+ ['Active', true],
34
33
  ['Inactive', false]
35
34
  ]
36
35
  options_for_select(options)
@@ -4,7 +4,9 @@
4
4
  #
5
5
  class AffiliateCampaign < SalesCampaign
6
6
  money_columns :affiliate_fee
7
- belongs_to :affiliate
7
+
8
+ belongs_to :affiliate, :class_name => 'Contact'
9
+ validates :affiliate, :presence => true
8
10
 
9
11
  ##
10
12
  # The sum cost of this campaign
@@ -26,9 +26,14 @@ class Campaign < ActiveRecord::Base
26
26
  NoCampaign.first || NoCampaign.create
27
27
  end
28
28
 
29
+ validates :name, :presence => true,
30
+ :uniqueness => { :allow_blank => true, :case_sensitive => false }
31
+
29
32
  validates :code, :presence => { :unless => lambda {|r| r.is_a?(NoCampaign) } },
30
- :length => { :maximum => 32 },
31
- :uniqueness => { :ignore_case => true, :allow_blank => true }
33
+ :length => { :maximum => 32 },
34
+ :format => { :allow_blank => true, :with => /^[\w\d]+$/ },
35
+ :uniqueness => { :case_sensitive => false, :allow_blank => true }
36
+
32
37
  validates :affiliate_fee, :numericality => true
33
38
  validates :sales_fee, :numericality => true
34
39
 
@@ -3,7 +3,9 @@
3
3
  class CampaignGroup < ActiveRecord::Base
4
4
  has_many :campaigns
5
5
 
6
- validates :name, :uniqueness => { :ignore_case => true }
6
+ validates :name, :presence => true, :uniqueness => { :allow_nil => true, :case_sensitive => false }
7
+
8
+ scope :ordered, lambda { order(arel_table[:name].asc) }
7
9
 
8
10
  def to_s
9
11
  name
@@ -2,4 +2,9 @@
2
2
  #
3
3
  class Company < ActiveRecord::Base
4
4
  include E9Rails::ActiveRecord::AttributeSearchable
5
+
6
+ has_many :contacts, :dependent => :restrict
7
+
8
+ validates :name, :presence => true, :uniqueness => { :allow_blank => true, :case_sensitive => false }
9
+
5
10
  end
@@ -8,12 +8,13 @@ class Contact < ActiveRecord::Base
8
8
  include Rails.application.routes.url_helpers
9
9
 
10
10
  before_validation :ensure_user_references
11
+ before_destroy :ensure_no_associated_deals
11
12
 
12
13
  ##
13
14
  # Associations
14
15
  #
15
16
  belongs_to :company
16
- has_many :owned_deals, :class_name => 'Deal'
17
+ has_many :owned_deals, :class_name => 'Deal', :dependent => :restrict
17
18
  has_and_belongs_to_many :associated_deals, :class_name => 'Deal'
18
19
 
19
20
  has_many :users, :inverse_of => :contact, :dependent => :nullify do
@@ -83,6 +84,8 @@ class Contact < ActiveRecord::Base
83
84
  end
84
85
  accepts_nested_attributes_for :users, :allow_destroy => true
85
86
 
87
+ delegate :email, :to => 'users.primary.first', :allow_nil => true
88
+
86
89
  def page_views
87
90
  PageView.by_user(users)
88
91
  end
@@ -149,6 +152,7 @@ class Contact < ActiveRecord::Base
149
152
  scope :sales_persons, lambda { where(:status => Status::SalesPerson) }
150
153
  scope :affiliates, lambda { where(:status => Status::Affiliate) }
151
154
  scope :contacts, lambda { where(:status => Status::Contact) }
155
+ scope :ordered, lambda { order(arel_table[:first_name].asc) }
152
156
 
153
157
  scope :by_title, lambda {|val| where(:title => val) }
154
158
  scope :by_company, lambda {|val| where(:company_id => val) }
@@ -284,6 +288,10 @@ class Contact < ActiveRecord::Base
284
288
  end
285
289
  end
286
290
 
291
+ def to_liquid
292
+ Drop.new(self)
293
+ end
294
+
287
295
  protected
288
296
 
289
297
  def _assign_initialization_defaults
@@ -307,6 +315,16 @@ class Contact < ActiveRecord::Base
307
315
  attributes.keys.member?('email') && attributes['email'].blank?
308
316
  end
309
317
 
318
+ def ensure_no_associated_deals
319
+ unless self.associated_deals.empty?
320
+ object.errors.add(:associated_deals, :delete_restricted)
321
+ end
322
+ end
323
+
324
+ class Drop < ::E9::Liquid::Drops::Base
325
+ source_methods :first_name, :last_name, :name, :email, :title, :company_name
326
+ end
327
+
310
328
  module Status
311
329
  VALUES = %w(contact sales_person affiliate)
312
330
  Contact = VALUES[0]
@@ -2,12 +2,13 @@
2
2
  #
3
3
  class MenuOption < ActiveRecord::Base
4
4
  KEYS = [
5
+ #'Task Category',
6
+ #'Task Status',
7
+ 'Address',
5
8
  'Deal Category',
6
9
  'Email',
7
10
  'Instant Messaging Handle',
8
11
  'Phone Number',
9
- #'Task Category',
10
- #'Task Status',
11
12
  'Website'
12
13
  ].freeze
13
14
 
@@ -16,7 +17,7 @@ class MenuOption < ActiveRecord::Base
16
17
 
17
18
  acts_as_list :scope => 'menu_options.key = \"#{key}\"'
18
19
 
19
- scope :options_for, lambda {|key| where(:key => key) }
20
+ scope :options_for, lambda {|key| where(:key => key).order('menu_options.position ASC') }
20
21
 
21
22
  ##
22
23
  # A direct SQL selection of values for a given key
@@ -4,7 +4,9 @@
4
4
  #
5
5
  class SalesCampaign < Campaign
6
6
  money_columns :sales_fee
7
- belongs_to :sales_person
7
+
8
+ belongs_to :sales_person, :class_name => 'Contact'
9
+ validates :sales_person, :presence => true
8
10
 
9
11
  ##
10
12
  # The sum cost of this campaign
@@ -3,8 +3,8 @@
3
3
  %fieldset
4
4
  %legend= e9_t(:affiliate_information_legend, :scope => 'e9_crm.campaigns')
5
5
  .field
6
- = f.label :affilate_id
7
- = f.collection_select :affiliate_id, Contact.affiliates.all, :id, :name, :prompt => true
6
+ = f.label :affilate, nil, :class => :req, :for => 'campaign_affiliate_id'
7
+ = f.collection_select :affiliate_id, Contact.affiliates.ordered.all, :id, :name, {:prompt => true}, :id => 'campaign_affiliate_id'
8
8
  .field
9
- = f.label :affiliate_fee
9
+ = help_label f, :affiliate_fee
10
10
  = f.text_field :affiliate_fee
@@ -0,0 +1,3 @@
1
+ .field
2
+ = f.label :name, nil, :class => :req
3
+ = f.text_field :name
@@ -2,20 +2,17 @@
2
2
  %legend= e9_t(:campaign_information_legend, :scope => 'e9_crm.campaigns')
3
3
  .field
4
4
  = f.label :campaign_group_id
5
- = f.collection_select :campaign_group_id, CampaignGroup.all, :id, :name, :include_blank => 'No Group'
5
+ = f.collection_select :campaign_group_id, CampaignGroup.ordered.all, :id, :name, :include_blank => 'No Group'
6
6
  .field
7
- = f.label :name
7
+ = f.label :name, nil, :class => :req
8
8
  = f.text_field :name
9
9
  - if f.object.new_record?
10
10
  .field#campaign_code_field
11
- = f.label :code
11
+ = f.label :code, nil, :class => :req
12
12
  = f.text_field :code
13
13
  .field
14
- %label{:for => 'campaign_code_hint'}
15
- = resource_class.human_attribute_name(:code_hint)
16
- %span.help{:title => e9_t(:code_help, :scope => 'e9_crm.campaigns', :code => E9Crm.query_param)}= t(:inline_help_link)
17
- #campaign_code_hint= "?#{E9Crm.query_param}=#{f.object.code}"
14
+ = help_label :campaign_code_hint, :code_hint, :title => t('activerecord.attributes.campaign.code_hint_help', :code => E9Crm.query_param)
15
+ #campaign_code_hint= display_campaign_code(f.object.code || '')
18
16
  .field
19
17
  = f.label :active
20
18
  = f.check_box :active
21
-
@@ -3,7 +3,7 @@
3
3
  %tr
4
4
  %th= orderable_column_link(:type)
5
5
  %th= orderable_column_link(:name)
6
- %th= orderable_column_link('campaign_group.name', :campaign_group)
6
+ %th= orderable_column_link('campaign_group_name', :campaign_group)
7
7
  %th= orderable_column_link(:code)
8
8
  %th= orderable_column_link(:affiliate_fee)
9
9
  %th= orderable_column_link(:sales_fee)
@@ -20,7 +20,7 @@
20
20
  %td.record-name
21
21
  = record.name
22
22
  %td.record-campaign-group
23
- = record.campaign_group || e9_t(:no_group, :scope => 'e9_crm.campaigns')
23
+ = record.campaign_group_name || e9_t(:no_group, :scope => 'e9_crm.campaigns')
24
24
  %td.record-code
25
25
  = display_campaign_code(record.code)
26
26
  %td.record-affiliate-fee
@@ -0,0 +1,4 @@
1
+ .toolbar
2
+ .toolbar-right
3
+ = link_to_new_resource(Company)
4
+ = link_to_collection(Contact)
@@ -1,21 +0,0 @@
1
- .toolbar
2
- .toolbar-left
3
- = form_tag(resource_class, :method => :get, :id => 'contact_search_form') do
4
- = label_tag 'contact_search_field', t(:search)
5
- = text_field_tag 'search', params[:search], :id => 'contact_search_field'
6
- = submit_tag t(:go), :name => nil
7
- = submit_tag t(:clear), :name => nil, :id => 'contact_search_clear'
8
- .toolbar-middle
9
- - if (tag = contact_email_template_select_tag).present?
10
- = form_tag new_contact_email_path, :method => :get, :id => 'contact_email_form', 'data-empty' => e9_t(:no_contacts_notification), 'data-count' => @contact_ids.length do
11
- = tag
12
- = hidden_field_tag 'uids', @contact_ids.join(','), :id => 'contact_email_uids'
13
- = submit_tag e9_t(:send_email_template), :name => nil
14
- - if (tag = contact_newsletter_select_tag).present?
15
- = form_tag send_email_admin_user_email_path('__ID__'), :method => :put, :id => 'contact_newsletter_form', 'data-confirm' => e9_t(:contact_newsletter_confirmation, :count => collection.length), 'data-empty' => e9_t(:no_contacts_notification), 'data-count' => @contact_ids.length do
16
- = tag
17
- = hidden_field_tag 'uids', @contact_ids.join(','), :id => 'contact_newsletter_uids'
18
- = submit_tag e9_t(:send_email_newsletter), :name => nil
19
- .toolbar-right
20
- = link_to_new_resource(Contact)
21
- = link_to_new_resource(Company)
@@ -1,6 +1,6 @@
1
1
  .actions
2
2
  = link_to_new_resource(Contact)
3
- = link_to_new_resource(Company)
3
+ = link_to_collection(Company)
4
4
 
5
5
  -# Email actions (template, newsletter)
6
6
  - etag, ntag = contact_email_template_select_tag, contact_newsletter_select_tag
@@ -15,10 +15,12 @@
15
15
  - if company = resource.company
16
16
  .contact-company
17
17
  %label= company.name
18
+
18
19
  .contact-company-links.actions
19
20
  = google_search_link(company.name)
20
21
  = google_news_link(company.name)
22
+
21
23
  - if company.info.present?
22
- = contact_simple_format(company.info)
24
+ = k(company.info)
23
25
  .contact-sidebar
24
26
  = render 'sidebar'
File without changes
@@ -2,5 +2,5 @@
2
2
  = f.label :key
3
3
  = f.select :key, MenuOption::KEYS.sort
4
4
  .field
5
- = f.label :value
5
+ = f.label :value, nil, :class => :req
6
6
  = f.text_field :value
@@ -3,8 +3,11 @@
3
3
  %table.records{:class => sortable_controller? ? 'sortable' : nil }
4
4
  %thead
5
5
  %tr
6
- - field_map[:fields].keys.each do |field|
7
- %th= resource_class.human_attribute_name(field)
6
+ - field_map[:fields].keys.each do |key|
7
+ - if key.is_a?(Symbol)
8
+ %th= resource_class.human_attribute_name(key)
9
+ - else
10
+ %th= orderable_column_link(key)
8
11
  %th= e9_t(:actions)
9
12
  %tbody
10
13
  - if collection.empty?
@@ -4,8 +4,8 @@
4
4
  %legend= e9_t(:salesperson_information_legend, :scope => 'e9_crm.campaigns')
5
5
  .field
6
6
  = f.label :sales_person_id
7
- = f.collection_select :sales_person_id, Contact.sales_persons.all, :id, :name, :prompt => true
7
+ = f.collection_select :sales_person_id, Contact.sales_persons.ordered.all, :id, :name, :prompt => true
8
8
  .field
9
- = f.label :sales_fee
9
+ = help_label f, :sales_fee
10
10
  = f.text_field :sales_fee
11
11
 
@@ -38,4 +38,3 @@ en:
38
38
  campaign_information_legend: Campaign Information
39
39
  salesperson_information_legend: Salesperson Information
40
40
  affiliate_information_legend: Affiliate Information
41
- code_help: "Be careful when creating tracking codes. They are NOT EDITABLE after you save them.&#10;&#10;To track the success of this campaign, you can append ?%{code}={Your Code} to the end of any and all URLs on your website.&#10;&#10;It might look something like this: http://www.example.com/about-us?%{code}=alpha, where \"alpha\" represents the code you created.&#10;&#10;You can provide this tracking URL to a salesperson or an affiliate or it can be placed at the end of a link in any of your advertisements."
@@ -36,6 +36,11 @@ en:
36
36
  merge_required: 'Merge Required! <a href="%{merge_path}">MERGE ME</a>.'
37
37
 
38
38
  models:
39
+ campaign:
40
+ not_a_number: "%{attribute} must be a number (maximum 2 decimal places)."
41
+ attributes:
42
+ code:
43
+ invalid: "%{attribute} must be comprised of letters and numbers only."
39
44
  contact:
40
45
  attributes:
41
46
  users:
@@ -44,7 +49,7 @@ en:
44
49
  contact_email:
45
50
  attributes:
46
51
  contact_ids:
47
- blank: "You cannot send an emailwith no recipients!"
52
+ blank: "You cannot send an email with no recipients!"
48
53
  user_ids:
49
54
  blank: "None of the contacts selected are configured with primary emails. Please correct this and try again."
50
55
  deal:
@@ -86,6 +91,11 @@ en:
86
91
  campaign:
87
92
  name: Name
88
93
  code_hint: Append to URL
94
+ code_hint_help: "Be careful when creating tracking codes. They are NOT EDITABLE after you save them.\n\nTo track the success of this campaign, you can append ?%{code}={Your Code} to the end of any and all URLs on your website.\n\nIt might look something like this: http://www.example.com/about-us?%{code}=alpha, where \"alpha\" represents the code you created.\n\nYou can provide this tracking URL to a salesperson or an affiliate or it can be placed at the end of a link in any of your advertisements."
95
+ affiliate_fee: Affiliate Fee
96
+ affiliate_fee_help: This is the fee payable for each lead this affiliate sends you where it converts to a sale.
97
+ sales_fee: Sales Fee
98
+ sales_fee_help: This is the fee payable for each lead this salesperson sends you where it converts to a sale.
89
99
  contact:
90
100
  avatar: Photo
91
101
  users: Email Addresses
data/e9_crm.gemspec CHANGED
@@ -26,4 +26,5 @@ Gem::Specification.new do |s|
26
26
  s.add_dependency("e9_rails", "~> 0.0.15")
27
27
  s.add_dependency("e9_tags", "~> 0.0.11")
28
28
  s.add_dependency("will_paginate")
29
+ s.add_dependency("kramdown", "~> 0.13")
29
30
  end
@@ -0,0 +1,20 @@
1
+ module E9Crm
2
+ #
3
+ # Extends E9Base's Email to attempt to embed the Contact
4
+ # for the recipient in the liquid template arguments.
5
+ #
6
+ module Email
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ def locals_with_contact
11
+ default_locals.merge({
12
+ :contact => recipient.try(:contact)
13
+ })
14
+ end
15
+
16
+ alias :default_locals :this_locals
17
+ alias :this_locals :locals_with_contact
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ module E9Crm
2
+ module Email
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ def locals_with_contact
7
+ default_locals.merge({
8
+ :contact => recipient.try(:contact)
9
+ })
10
+ end
11
+
12
+ alias :default_locals :this_locals
13
+ alias :this_locals :locals_with_contact
14
+ end
15
+ end
16
+ end
@@ -16,6 +16,37 @@ class Array
16
16
  end
17
17
  end
18
18
 
19
+
20
+ #
21
+ # Money is too clever by half and attempts to deal with parsing Strings into
22
+ # currency e.g.
23
+ #
24
+ # "asdf 0.23 asdf".to_money #=> #<Money cents:23 currency:USD>
25
+ #
26
+ # This is problematic if you want to actually validate money as it parses most
27
+ # bogus strings to zero, so unless you want to validate > 0, most any input
28
+ # will pass validation.
29
+ #
30
+ # This silly hack prevents "asdf".to_money from parsing to 0 cents, instead
31
+ # returning itself. The #cents method allows for our composed_of column to
32
+ # still work, simply returning self, which would fail a numericality validation.
33
+ #
34
+ require 'money'
35
+
36
+ class String
37
+ def to_money(currency = nil)
38
+ if match /^$|[^\$\d\.\,]/
39
+ self
40
+ else
41
+ Money.parse(self, currency)
42
+ end
43
+ end
44
+
45
+ def cents
46
+ self
47
+ end
48
+ end
49
+
19
50
  class ActiveRecord::Base
20
51
  #
21
52
  # Basic conversion for "money" columns using the Money class and Rails composed_of
@@ -26,7 +57,7 @@ class ActiveRecord::Base
26
57
  composed_of :#{column_name},
27
58
  :class_name => 'Money',
28
59
  :mapping => %w(#{column_name} cents),
29
- :converter => Proc.new { |value| value.respond_to?(:to_money) ? value.to_money : Money.empty }
60
+ :converter => Proc.new {|v| v.respond_to?(:to_money) ? v.to_money : v }
30
61
  EVAL
31
62
  end
32
63
  end
@@ -1,3 +1,3 @@
1
1
  module E9Crm
2
- VERSION = '0.1.8'
2
+ VERSION = '0.1.10'
3
3
  end
data/lib/e9_crm.rb CHANGED
@@ -5,6 +5,8 @@ require 'money'
5
5
  require 'inherited_resources'
6
6
  require 'will_paginate'
7
7
  require 'has_scope'
8
+ require 'kramdown'
9
+ require 'e9_base'
8
10
 
9
11
  require 'e9_crm/rails_extensions'
10
12
 
@@ -12,6 +14,7 @@ module E9Crm
12
14
  autoload :VERSION, 'e9_crm/version'
13
15
  autoload :Controller, 'e9_crm/controller'
14
16
  autoload :Model, 'e9_crm/model'
17
+ autoload :Email, 'e9_crm/email'
15
18
  autoload :TrackingController, 'e9_crm/tracking_controller'
16
19
 
17
20
  mattr_accessor :cookie_name
@@ -52,6 +55,8 @@ module E9Crm
52
55
 
53
56
  ActionController::Base.send(:include, E9Crm::Controller)
54
57
 
58
+ ::Email.send(:include, E9Crm::Email)
59
+
55
60
  E9Crm.tracking_controllers.each do |controller|
56
61
  controller.send(:include, E9Crm::TrackingController)
57
62
  end
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.8
5
+ version: 0.1.10
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-05-25 00:00:00 -04:00
13
+ date: 2011-05-26 00:00:00 -04:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -90,13 +90,23 @@ dependencies:
90
90
  version: "0"
91
91
  type: :runtime
92
92
  version_requirements: *id007
93
+ - !ruby/object:Gem::Dependency
94
+ name: kramdown
95
+ prerelease: false
96
+ requirement: &id008 !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: "0.13"
102
+ type: :runtime
103
+ version_requirements: *id008
93
104
  description: |
105
+ *NOTE his plugin requires the private e9_base CMS gem and WILL NOT WORK without it.*
106
+
94
107
  CRM Plugin for the e9 CMS
95
108
  =========================
96
109
 
97
- It should be noted that while this plugin is intended for use in the
98
- e9 CMS, it does neither includes nor requires it.
99
-
100
110
  To use, add as a gem and install by running:
101
111
 
102
112
  rails g e9_crm:install
@@ -207,6 +217,7 @@ files:
207
217
  - app/views/e9_crm/advertising_campaigns/costs.html.haml
208
218
  - app/views/e9_crm/affiliate_campaigns/_form_inner.html.haml
209
219
  - app/views/e9_crm/campaign_groups/_footer.html.haml
220
+ - app/views/e9_crm/campaign_groups/_form_inner.html.haml
210
221
  - app/views/e9_crm/campaign_groups/_header.html.haml
211
222
  - app/views/e9_crm/campaigns/_footer.html.haml
212
223
  - app/views/e9_crm/campaigns/_form_inner.html.haml
@@ -215,6 +226,7 @@ files:
215
226
  - app/views/e9_crm/campaigns/_table.html.haml
216
227
  - app/views/e9_crm/campaigns/reports.html.haml
217
228
  - app/views/e9_crm/companies/_form_inner.html.haml
229
+ - app/views/e9_crm/companies/_header.html.haml
218
230
  - app/views/e9_crm/contact_emails/_form.html.haml
219
231
  - app/views/e9_crm/contact_emails/_form_inner.html.haml
220
232
  - app/views/e9_crm/contact_emails/destroy.js.erb
@@ -268,6 +280,7 @@ files:
268
280
  - app/views/e9_crm/leads/_form.html.haml
269
281
  - app/views/e9_crm/leads/create.js.erb
270
282
  - app/views/e9_crm/leads/new.html.haml
283
+ - app/views/e9_crm/menu_options/_footer.html.haml
271
284
  - app/views/e9_crm/menu_options/_form_inner.html.haml
272
285
  - app/views/e9_crm/menu_options/_header.html.haml
273
286
  - app/views/e9_crm/new_content_subscription_offers/_form_inner.html.haml
@@ -310,6 +323,8 @@ files:
310
323
  - e9_crm.gemspec
311
324
  - lib/e9_crm.rb
312
325
  - lib/e9_crm/controller.rb
326
+ - lib/e9_crm/e9_extensions.rb
327
+ - lib/e9_crm/email.rb
313
328
  - lib/e9_crm/model.rb
314
329
  - lib/e9_crm/rails_extensions.rb
315
330
  - lib/e9_crm/tracking_controller.rb