e9_crm 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/Gemfile.lock +38 -45
  2. data/app/controllers/e9_crm/advertising_campaigns_controller.rb +1 -1
  3. data/app/controllers/e9_crm/affiliate_campaigns_controller.rb +1 -1
  4. data/app/controllers/e9_crm/campaign_subclass_controller.rb +8 -0
  5. data/app/controllers/e9_crm/campaigns_controller.rb +16 -16
  6. data/app/controllers/e9_crm/contact_emails_controller.rb +3 -1
  7. data/app/controllers/e9_crm/contacts_controller.rb +29 -0
  8. data/app/controllers/e9_crm/dated_costs_controller.rb +60 -2
  9. data/app/controllers/e9_crm/email_campaigns_controller.rb +1 -1
  10. data/app/controllers/e9_crm/email_templates.controller.rb +1 -0
  11. data/app/controllers/e9_crm/resources_controller.rb +14 -1
  12. data/app/controllers/e9_crm/sales_campaigns_controller.rb +1 -1
  13. data/app/helpers/e9_crm/base_helper.rb +10 -3
  14. data/app/helpers/e9_crm/companies_helper.rb +7 -0
  15. data/app/helpers/e9_crm/contacts_helper.rb +11 -3
  16. data/app/helpers/e9_crm/email_templates_helper.rb +7 -0
  17. data/app/models/advertising_campaign.rb +2 -0
  18. data/app/models/contact.rb +24 -9
  19. data/app/models/contact_email.rb +5 -4
  20. data/app/models/dated_cost.rb +24 -0
  21. data/app/models/deal.rb +9 -9
  22. data/app/views/e9_crm/advertising_campaigns/costs.html.haml +0 -0
  23. data/app/views/e9_crm/campaigns/_table.html.haml +2 -0
  24. data/app/views/e9_crm/companies/_form_inner.html.haml +6 -0
  25. data/app/views/e9_crm/contacts/_contact.html.haml +1 -0
  26. data/app/views/e9_crm/contacts/_details.html.haml +12 -8
  27. data/app/views/e9_crm/contacts/_form_inner.html.haml +18 -32
  28. data/app/views/e9_crm/contacts/_who.html.haml +14 -0
  29. data/app/views/e9_crm/contacts/index.html.haml +4 -5
  30. data/app/views/e9_crm/dated_costs/_dated_cost.html.haml +8 -0
  31. data/app/views/e9_crm/dated_costs/_form.html.haml +3 -0
  32. data/app/views/e9_crm/dated_costs/_form_inner.html.haml +15 -0
  33. data/app/views/e9_crm/dated_costs/bulk_form.html.haml +20 -0
  34. data/app/views/e9_crm/dated_costs/create.js.erb +5 -0
  35. data/app/views/e9_crm/dated_costs/destroy.js.erb +3 -0
  36. data/app/views/e9_crm/dated_costs/edit.html.haml +3 -0
  37. data/app/views/e9_crm/dated_costs/edit.js.erb +1 -0
  38. data/app/views/e9_crm/dated_costs/index.html.haml +15 -0
  39. data/app/views/e9_crm/dated_costs/new.html.haml +2 -0
  40. data/app/views/e9_crm/dated_costs/show.html.haml +1 -0
  41. data/app/views/e9_crm/dated_costs/update.js.erb +5 -0
  42. data/app/views/e9_crm/email_templates/_footer.html.haml +0 -0
  43. data/app/views/e9_crm/email_templates/_form_inner.html.haml +3 -0
  44. data/app/views/e9_crm/email_templates/_header.html.haml +3 -0
  45. data/app/views/e9_crm/record_attributes/_form_partial.html.haml +4 -0
  46. data/app/views/e9_crm/resources/show.html.haml +1 -1
  47. data/config/locales/en.yml +17 -0
  48. data/config/routes.rb +14 -2
  49. data/e9_crm.gemspec +1 -1
  50. data/lib/e9_crm/model.rb +1 -1
  51. data/lib/e9_crm/version.rb +1 -1
  52. data/lib/generators/e9_crm/templates/migration.rb +2 -0
  53. metadata +25 -3
data/Gemfile.lock CHANGED
@@ -1,9 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- e9_crm (0.1.0)
5
- e9_rails (~> 0.0.4)
6
- e9_tags (~> 0.0.2)
4
+ e9_crm (0.1.4)
5
+ e9_rails (~> 0.0.13)
6
+ e9_tags (~> 0.0.11)
7
7
  has_scope
8
8
  inherited_resources (~> 1.1.2)
9
9
  money
@@ -14,39 +14,39 @@ GEM
14
14
  remote: http://rubygems.org/
15
15
  specs:
16
16
  abstract (1.0.0)
17
- actionmailer (3.0.5)
18
- actionpack (= 3.0.5)
17
+ actionmailer (3.0.6)
18
+ actionpack (= 3.0.6)
19
19
  mail (~> 2.2.15)
20
- actionpack (3.0.5)
21
- activemodel (= 3.0.5)
22
- activesupport (= 3.0.5)
20
+ actionpack (3.0.6)
21
+ activemodel (= 3.0.6)
22
+ activesupport (= 3.0.6)
23
23
  builder (~> 2.1.2)
24
24
  erubis (~> 2.6.6)
25
- i18n (~> 0.4)
25
+ i18n (~> 0.5.0)
26
26
  rack (~> 1.2.1)
27
- rack-mount (~> 0.6.13)
27
+ rack-mount (~> 0.6.14)
28
28
  rack-test (~> 0.5.7)
29
29
  tzinfo (~> 0.3.23)
30
- activemodel (3.0.5)
31
- activesupport (= 3.0.5)
30
+ activemodel (3.0.6)
31
+ activesupport (= 3.0.6)
32
32
  builder (~> 2.1.2)
33
- i18n (~> 0.4)
34
- activerecord (3.0.5)
35
- activemodel (= 3.0.5)
36
- activesupport (= 3.0.5)
33
+ i18n (~> 0.5.0)
34
+ activerecord (3.0.6)
35
+ activemodel (= 3.0.6)
36
+ activesupport (= 3.0.6)
37
37
  arel (~> 2.0.2)
38
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)
39
+ activeresource (3.0.6)
40
+ activemodel (= 3.0.6)
41
+ activesupport (= 3.0.6)
42
+ activesupport (3.0.6)
43
43
  acts-as-taggable-on (2.0.6)
44
44
  arel (2.0.9)
45
45
  builder (2.1.2)
46
- e9_rails (0.0.4)
46
+ e9_rails (0.0.13)
47
47
  inherited_resources
48
48
  rails (~> 3.0.0)
49
- e9_tags (0.0.2)
49
+ e9_tags (0.0.11)
50
50
  acts-as-taggable-on (~> 2.0.6)
51
51
  rails (~> 3.0.0)
52
52
  erubis (2.6.6)
@@ -56,7 +56,7 @@ GEM
56
56
  inherited_resources (1.1.2)
57
57
  has_scope (~> 0.5.0)
58
58
  responders (~> 0.6.0)
59
- mail (2.2.15)
59
+ mail (2.2.19)
60
60
  activesupport (>= 2.3.6)
61
61
  i18n (>= 0.4.0)
62
62
  mime-types (~> 1.16)
@@ -65,41 +65,34 @@ GEM
65
65
  money (3.6.1)
66
66
  i18n (~> 0.4)
67
67
  polyglot (0.3.1)
68
- rack (1.2.1)
69
- rack-mount (0.6.13)
68
+ rack (1.2.2)
69
+ rack-mount (0.6.14)
70
70
  rack (>= 1.0.0)
71
71
  rack-test (0.5.7)
72
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)
73
+ rails (3.0.6)
74
+ actionmailer (= 3.0.6)
75
+ actionpack (= 3.0.6)
76
+ activerecord (= 3.0.6)
77
+ activeresource (= 3.0.6)
78
+ activesupport (= 3.0.6)
79
79
  bundler (~> 1.0)
80
- railties (= 3.0.5)
81
- railties (3.0.5)
82
- actionpack (= 3.0.5)
83
- activesupport (= 3.0.5)
80
+ railties (= 3.0.6)
81
+ railties (3.0.6)
82
+ actionpack (= 3.0.6)
83
+ activesupport (= 3.0.6)
84
84
  rake (>= 0.8.7)
85
85
  thor (~> 0.14.4)
86
86
  rake (0.8.7)
87
- responders (0.6.2)
87
+ responders (0.6.4)
88
88
  thor (0.14.6)
89
89
  treetop (1.4.9)
90
90
  polyglot (>= 0.3.1)
91
- tzinfo (0.3.24)
92
- will_paginate (2.3.15)
91
+ tzinfo (0.3.27)
92
+ will_paginate (3.0.pre2)
93
93
 
94
94
  PLATFORMS
95
95
  ruby
96
96
 
97
97
  DEPENDENCIES
98
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
@@ -1,4 +1,4 @@
1
- class E9Crm::AdvertisingCampaignsController < E9Crm::ResourcesController
1
+ class E9Crm::AdvertisingCampaignsController < E9Crm::CampaignSubclassController
2
2
  defaults :resource_class => AdvertisingCampaign
3
3
  include E9Rails::Controllers::Orderable
4
4
  end
@@ -1,4 +1,4 @@
1
- class E9Crm::AffiliateCampaignsController < E9Crm::ResourcesController
1
+ class E9Crm::AffiliateCampaignsController < E9Crm::CampaignSubclassController
2
2
  defaults :resource_class => AffiliateCampaign
3
3
  include E9Rails::Controllers::Orderable
4
4
  end
@@ -0,0 +1,8 @@
1
+ class E9Crm::CampaignSubclassController < E9Crm::ResourcesController
2
+
3
+ protected
4
+
5
+ def add_index_breadcrumb
6
+ add_breadcrumb! Campaign.model_name.collection.titleize, campaigns_path
7
+ end
8
+ end
@@ -20,22 +20,22 @@ class E9Crm::CampaignsController < E9Crm::ResourcesController
20
20
 
21
21
  protected
22
22
 
23
- def set_reports_index_title
24
- @index_title = I18n.t(:reports_title, :scope => 'e9.e9_crm.campaigns')
25
- end
23
+ def set_reports_index_title
24
+ @index_title = I18n.t(:reports_title, :scope => 'e9.e9_crm.campaigns')
25
+ end
26
26
 
27
- def collection_scope
28
- scope = end_of_association_chain
29
- if params[:action] == 'reports'
30
- scope = scope.select(
31
- 'campaigns.*, count(deals.id) won_deals_count, count(deals_campaigns.id) deals_count'
32
- ).joins([:deals, :won_deals])
27
+ def collection_scope
28
+ scope = end_of_association_chain
29
+ if params[:action] == 'reports'
30
+ scope = scope.select(
31
+ 'campaigns.*, count(deals.id) won_deals_count, count(deals_campaigns.id) deals_count'
32
+ ).joins([:deals, :won_deals])
33
+ end
34
+ scope
35
+ end
36
+
37
+ # no pagination
38
+ def collection
39
+ get_collection_ivar || set_collection_ivar(collection_scope.all)
33
40
  end
34
- scope
35
- end
36
-
37
- # no pagination
38
- def collection
39
- get_collection_ivar || set_collection_ivar(collection_scope.all)
40
- end
41
41
  end
@@ -17,9 +17,11 @@ class E9Crm::ContactEmailsController < E9Crm::ResourcesController
17
17
  object = if params[resource_instance_name]
18
18
  ContactEmail.new(params[resource_instance_name] || {})
19
19
  else
20
- ContactEmail.new_from_template(template, :from_email => current_user.email, :contact_ids => params[:uids])
20
+ ContactEmail.new_from_template(template, :contact_ids => params[:uids])
21
21
  end
22
22
 
23
+ object.from_email ||= current_user.email
24
+
23
25
  object.valid?
24
26
 
25
27
  set_resource_ivar(object)
@@ -6,8 +6,11 @@ class E9Crm::ContactsController < E9Crm::ResourcesController
6
6
 
7
7
  respond_to :js, :html
8
8
 
9
+ carrierwave_column_methods :avatar, :context => :admin
10
+
9
11
  before_filter :determine_title, :only => :index
10
12
  before_filter :load_contact_ids, :only => :index
13
+ before_filter :build_nested_associations, :only => [:new, :edit]
11
14
 
12
15
  has_scope :search, :by_title, :by_company, :only => :index
13
16
  has_scope :tagged, :only => :index, :type => :array
@@ -17,8 +20,20 @@ class E9Crm::ContactsController < E9Crm::ResourcesController
17
20
  before_filter :build_resource, :only => :templates
18
21
  caches_action :templates
19
22
 
23
+ def create
24
+ create! { resource_path }
25
+ end
26
+
27
+ def update
28
+ update! { resource_path }
29
+ end
30
+
20
31
  protected
21
32
 
33
+ #
34
+ # Load all contact ids for the request (no pagination) using a direct sql query
35
+ # for the contact_id rather than loading all contacts.
36
+ #
22
37
  def load_contact_ids
23
38
  @contact_ids ||= begin
24
39
  contact_id_sql = end_of_association_chain.scoped.select('contacts.id').to_sql
@@ -26,6 +41,9 @@ class E9Crm::ContactsController < E9Crm::ResourcesController
26
41
  end
27
42
  end
28
43
 
44
+ #
45
+ # Change title depending on search params (tags & search)
46
+ #
29
47
  def determine_title
30
48
  params.delete(:search) if params[:search].blank?
31
49
 
@@ -38,6 +56,17 @@ class E9Crm::ContactsController < E9Crm::ResourcesController
38
56
  end
39
57
  end
40
58
 
59
+ def collection_scope
60
+ #TODO fix eager loading, which totally breaks because of the left outer joins in search
61
+ #super.includes(:users => :subscriptions)
62
+ scope = super
63
+ end
64
+
65
+ def build_nested_associations
66
+ object = params[:id] ? resource : build_resource
67
+ object.build_all_record_attributes
68
+ end
69
+
41
70
  def default_ordered_on
42
71
  'first_name'
43
72
  end
@@ -1,5 +1,63 @@
1
- class E9Crm::DatedCostsController < E9Crm::ResourcesController
2
- belongs_to :advertising_campaign
1
+ class E9Crm::DatedCostsController < E9Crm::CampaignSubclassController
2
+ belongs_to :advertising_campaign, :optional => true
3
3
  defaults :resource_class => DatedCost
4
4
  include E9Rails::Controllers::Orderable
5
+
6
+ filter_access_to :bulk_create, :require => :create, :context => :admin
7
+
8
+ before_filter :add_breadcrumbs
9
+ before_filter :generate_temp_id, :only => :new
10
+
11
+ def index
12
+ if params[:advertising_campaign_id]
13
+ index!
14
+ else
15
+ @advertising_campaigns = AdvertisingCampaign.all
16
+ render 'bulk_form'
17
+ end
18
+ end
19
+
20
+ def bulk_create
21
+ params[:id].zip(params[:cost]) do |id, cost|
22
+ DatedCost.create(
23
+ :costable_id => id,
24
+ :costable_type => AdvertisingCampaign.base_class.name,
25
+ :date => params[:date],
26
+ :cost => cost
27
+ )
28
+ end
29
+
30
+ flash[:notice] = "Advertising Costs Added"
31
+ redirect_to :marketing_report
32
+ end
33
+
34
+ protected
35
+
36
+ def collection
37
+ get_collection_ivar || set_collection_ivar(end_of_association_chain.all)
38
+ end
39
+
40
+ def default_ordered_on
41
+ 'date'
42
+ end
43
+
44
+ def default_ordered_dir
45
+ 'ASC'
46
+ end
47
+
48
+ def add_breadcrumbs
49
+ if parent?
50
+ add_breadcrumb! parent.name, edit_advertising_campaign_path(parent)
51
+ end
52
+
53
+ add_breadcrumb! e9_t(:index_title)
54
+ end
55
+
56
+ def determine_layout
57
+ request.xhr? ? false : super
58
+ end
59
+
60
+ def generate_temp_id
61
+ @temp_id ||= DateTime.now.to_i
62
+ end
5
63
  end
@@ -1,4 +1,4 @@
1
- class E9Crm::EmailCampaignsController < E9Crm::ResourcesController
1
+ class E9Crm::EmailCampaignsController < E9Crm::CampaignSubclassController
2
2
  defaults :resource_class => EmailCampaign
3
3
  include E9Rails::Controllers::Orderable
4
4
  end
@@ -1,4 +1,5 @@
1
1
  class E9Crm::EmailTemplatesController < E9Crm::ResourcesController
2
2
  defaults
3
3
  include E9Rails::Controllers::Orderable
4
+ self.should_paginate_index = false
4
5
  end
@@ -2,6 +2,9 @@ class E9Crm::ResourcesController < E9Crm::BaseController
2
2
  include E9Rails::Helpers::ResourceErrorMessages
3
3
  include E9Rails::Helpers::Pagination
4
4
 
5
+ class_inheritable_accessor :should_paginate_index
6
+ self.should_paginate_index = true
7
+
5
8
  inherit_resources
6
9
 
7
10
  respond_to :js
@@ -27,11 +30,21 @@ class E9Crm::ResourcesController < E9Crm::BaseController
27
30
  helper_method :parent
28
31
 
29
32
  def add_index_breadcrumb
33
+ yield if block_given?
30
34
  add_breadcrumb! @index_title || e9_t(:index_title), collection_path
31
35
  end
32
36
 
37
+ # expose collection scope to be overridden
38
+ def collection_scope
39
+ end_of_association_chain
40
+ end
41
+
33
42
  def collection
34
- get_collection_ivar || set_collection_ivar(end_of_association_chain.paginate(pagination_parameters))
43
+ get_collection_ivar || begin
44
+ set_collection_ivar(
45
+ collection_scope.send *(should_paginate_index ? [:paginate, pagination_parameters] : [:all])
46
+ )
47
+ end
35
48
  end
36
49
 
37
50
  def default_ordered_on
@@ -1,4 +1,4 @@
1
- class E9Crm::SalesCampaignsController < E9Crm::ResourcesController
1
+ class E9Crm::SalesCampaignsController < E9Crm::CampaignSubclassController
2
2
  defaults :resource_class => SalesCampaign
3
3
  include E9Rails::Controllers::Orderable
4
4
  end
@@ -6,7 +6,7 @@ module E9Crm::BaseHelper
6
6
 
7
7
  def records_table_field_map(options = {})
8
8
  options.symbolize_keys!
9
- options.reverse_merge!(:class_name => resource_class.base_class.name.underscore)
9
+ options.reverse_merge!(:class_name => resource_class.name.underscore)
10
10
 
11
11
  base_map = {
12
12
  :fields => { :id => nil },
@@ -44,6 +44,13 @@ module E9Crm::BaseHelper
44
44
  )
45
45
  end
46
46
 
47
+ def render_record_attribute_form(association_name, form)
48
+ render('e9_crm/record_attributes/form_partial', {
49
+ :form => form,
50
+ :association_name => association_name
51
+ })
52
+ end
53
+
47
54
  def render_record_attribute_association(association_name, form, options = {})
48
55
  options.symbolize_keys!
49
56
 
@@ -59,10 +66,10 @@ module E9Crm::BaseHelper
59
66
  def record_attribute_template(association_name, builder, options = {})
60
67
  options.symbolize_keys!
61
68
 
62
- render({
69
+ render(
63
70
  :partial => options[:partial] || "e9_crm/record_attributes/#{association_name.to_s.singularize}",
64
71
  :locals => { :f => builder }
65
- })
72
+ )
66
73
  end
67
74
 
68
75
  def build_associated_resource(association_name)
@@ -0,0 +1,7 @@
1
+ module E9Crm::CompaniesHelper
2
+ def records_table_field_map_for_company
3
+ {
4
+ :fields => { :name => nil }
5
+ }
6
+ end
7
+ end
@@ -12,6 +12,12 @@ module E9Crm::ContactsHelper
12
12
  options_for_select( UserEmail.pending.order('name').map {|e| [e.name, e.id] })
13
13
  end
14
14
 
15
+ def contact_user_subscribed_to_newsletter?(user)
16
+ @_newsletter ||= MailingList.newsletter
17
+
18
+ user.subscription_ids.include? @_newsletter.id
19
+ end
20
+
15
21
  def records_table_field_map_for_contact
16
22
  {
17
23
  :fields => {
@@ -19,9 +25,11 @@ module E9Crm::ContactsHelper
19
25
  :details => proc {|r| render('details', :record => r) }
20
26
  },
21
27
 
22
- :links => proc {|r|
23
- [link_to_show_resource(r), link_to_edit_resource(r), link_to_destroy_resource(r)]
24
- }
28
+ :links => proc {|r| [
29
+ link_to_show_resource(r),
30
+ link_to_edit_resource(r),
31
+ link_to_destroy_resource(r)
32
+ ]}
25
33
  }
26
34
  end
27
35
  end
@@ -0,0 +1,7 @@
1
+ module E9Crm::EmailTemplatesHelper
2
+ def records_table_field_map_for_email_template
3
+ {
4
+ :fields => { :name => nil }
5
+ }
6
+ end
7
+ end
@@ -5,6 +5,8 @@
5
5
  #
6
6
  class AdvertisingCampaign < Campaign
7
7
 
8
+ accepts_nested_attributes_for :dated_costs
9
+
8
10
  ##
9
11
  # The sum cost of this campaign
10
12
  #
@@ -94,6 +94,14 @@ class Contact < ActiveRecord::Base
94
94
  accepts_nested_attributes_for association_name.to_sym, :allow_destroy => true, :reject_if => :reject_record_attribute?
95
95
  end
96
96
 
97
+ def build_all_record_attributes
98
+ RECORD_ATTRIBUTES.each do |attr|
99
+ params_method = "#{attr}_build_parameters"
100
+ build_params = self.class.send(params_method) if self.class.respond_to?(params_method)
101
+ send(attr).send(:build, build_params || {})
102
+ end
103
+ end
104
+
97
105
  ##
98
106
  # Validations
99
107
  #
@@ -119,15 +127,18 @@ class Contact < ActiveRecord::Base
119
127
  LEFT OUTER JOIN record_attributes
120
128
  ON record_attributes.record_id = contacts.id
121
129
  AND record_attributes.record_type = 'Contact'
122
- LEFT OUTER JOIN users
123
- ON users.contact_id = contacts.id
130
+ LEFT OUTER JOIN users AS contacts_users
131
+ ON contacts_users.contact_id = contacts.id
124
132
  }
125
133
 
126
- joins(join_sql).group('contacts.id').where(
127
- any_attrs_like_scope_conditions(:first_name, :last_name, :title, query)
128
- .or(RecordAttribute.attr_like_scope_condition(:value, query))
129
- .or(User.attr_like_scope_condition(:email, query))
130
- )
134
+ where_sql = any_attrs_like_scope_conditions(:first_name, :last_name, :title, query)
135
+ .or(RecordAttribute.attr_like_scope_condition(:value, query))
136
+ .to_sql
137
+
138
+ ucond = sanitize_sql_array(['contacts_users.email like ?', "%#{query}%"])
139
+ where_sql << " OR (#{ucond})"
140
+
141
+ select("distinct contacts.*").joins(join_sql).where(where_sql)
131
142
  }
132
143
 
133
144
  scope :sales_persons, lambda { where(:status => Status::SalesPerson) }
@@ -152,7 +163,7 @@ class Contact < ActiveRecord::Base
152
163
 
153
164
  # The parameters for building the JS template for associated users
154
165
  def self.users_build_parameters # :nodoc:
155
- { :status => User::Status::PROSPECT }
166
+ { :status => User::Status::Prospect }
156
167
  end
157
168
 
158
169
  ##
@@ -267,8 +278,12 @@ class Contact < ActiveRecord::Base
267
278
  reject_record_attribute?(hash) || super
268
279
  end
269
280
 
281
+ #
282
+ # #TODO figure out exactly how to handle rejection of Users
283
+ #
270
284
  def reject_record_attribute?(attributes)
271
- attributes.keys.member?('value') && attributes['value'].blank?
285
+ attributes.keys.member?('value') && attributes['value'].blank? ||
286
+ attributes.keys.member?('email') && attributes['email'].blank?
272
287
  end
273
288
 
274
289
  module Status
@@ -13,10 +13,11 @@ class ContactEmail < Email
13
13
  class << self
14
14
  def new_from_template(template, attrs = {})
15
15
  new({
16
- :name => "#{template.name} - #{DateTime.now.to_i}",
17
- :subject => template.subject,
18
- :html_body => template.html_body,
19
- :text_body => template.text_body
16
+ :name => "#{template.name} - #{DateTime.now.to_i}",
17
+ :subject => template.subject,
18
+ :from_email => template.from_email,
19
+ :html_body => template.html_body,
20
+ :text_body => template.text_body
20
21
  }.merge(attrs))
21
22
  end
22
23
  end
@@ -3,7 +3,31 @@
3
3
  # date so sums for date ranges can be generated.
4
4
  #
5
5
  class DatedCost < ActiveRecord::Base
6
+ include E9Rails::ActiveRecord::Initialization
7
+
6
8
  money_columns :cost
7
9
  belongs_to :costable, :polymorphic => true
8
10
  validates :date, :date => true
11
+ validates :cost, :numericality => { :greater_than => 0 }
12
+
13
+ attr_accessor :temp_id
14
+
15
+ def as_json(options={})
16
+ {}.tap do |hash|
17
+ hash[:id] = self.id
18
+ hash[:cost] = self.cost
19
+ hash[:date] = self.date
20
+ hash[:costable_type] = self.costable_type.try(:underscore)
21
+ hash[:costable_id] = self.costable_id
22
+ hash[:errors] = self.errors
23
+
24
+ hash.merge!(options)
25
+ end
26
+ end
27
+
28
+ protected
29
+
30
+ def _assign_initialization_defaults
31
+ self.date ||= Date.today
32
+ end
9
33
  end
data/app/models/deal.rb CHANGED
@@ -31,29 +31,29 @@ class Deal < ActiveRecord::Base
31
31
  SUM(IF(deals.status='won',value,0)) total_value,
32
32
  AVG(IF(deals.status='won',value,NULL)) average_value,
33
33
 
34
- (CASE campaigns.type
35
- WHEN "SalesCampaign"
36
- THEN SUM(campaigns.sales_fee)
34
+ SUM(CASE campaigns.type
37
35
  WHEN "AdvertisingCampaign"
38
36
  THEN dated_costs.cost
37
+ WHEN "SalesCampaign"
38
+ THEN campaigns.sales_fee
39
39
  WHEN "AffiliateCampaign"
40
- THEN SUM(campaigns.sales_fee +
41
- campaigns.affiliate_fee)
40
+ THEN campaigns.sales_fee +
41
+ campaigns.affiliate_fee
42
42
  ELSE
43
43
  0
44
44
  END) total_cost,
45
45
 
46
- (CASE campaigns.type
47
- WHEN "SalesCampaign"
48
- THEN campaigns.sales_fee
46
+ SUM(CASE campaigns.type
49
47
  WHEN "AdvertisingCampaign"
50
48
  THEN dated_costs.cost
49
+ WHEN "SalesCampaign"
50
+ THEN campaigns.sales_fee
51
51
  WHEN "AffiliateCampaign"
52
52
  THEN campaigns.sales_fee +
53
53
  campaigns.affiliate_fee
54
54
  ELSE
55
55
  0
56
- END / COUNT(deals.id)) average_cost,
56
+ END) / SUM(IF(deals.status='won',1,0)) average_cost,
57
57
 
58
58
  FLOOR(AVG(
59
59
  DATEDIFF(
@@ -28,4 +28,6 @@
28
28
  %td.record-sales-fee
29
29
  = display_campaign_fee(record.sales_fee)
30
30
  %td.links
31
+ - if record.is_a?(AdvertisingCampaign)
32
+ = link_to t(:edit_dated_costs), advertising_campaign_dated_costs_path(record), :class => 'dated-costs'
31
33
  = link_to_edit_campaign(record)
@@ -0,0 +1,6 @@
1
+ .field
2
+ = f.label :name, nil, :class => :req
3
+ = f.text_field :name
4
+ .field.tinymce
5
+ = f.label :info
6
+ = f.text_area :info
@@ -0,0 +1 @@
1
+ = resource.name
@@ -1,10 +1,13 @@
1
- .contact-who
2
- %span.contact-name= record.name
3
- - if record.company_id? || record.title?
4
- = " - "
5
- = link_to_contact_search(:by_title, record.title) if record.title?
6
- = " at " if record.company_id? && record.title?
7
- = link_to_contact_search(:by_company, record.company_id, record.company_name) if record.company_id?
1
+ = render 'who', :record => record
2
+
3
+ - if record.users.any?
4
+ .contact-phone-numbers
5
+ - record.users.each do |user|
6
+ .contact-email
7
+ = '* ' if contact_user_subscribed_to_newsletter?(user)
8
+ = link_to(user.email, "mailto:#{user.email}")
9
+ = "(#{user.options.type})" if user.options.type
10
+ = "(primary)" if user.primary?
8
11
 
9
12
  - if record.phone_number_attributes.any?
10
13
  .contact-phone-numbers
@@ -19,4 +22,5 @@
19
22
  - if record.website_attributes.any?
20
23
  .contact-websites
21
24
  - record.website_attributes.each do |website_attribute|
22
- .contact-website= website_attribute
25
+ .contact-website
26
+ = auto_link(website_attribute.to_s, :rel => "external nofollow")
@@ -12,40 +12,26 @@
12
12
  .field
13
13
  = f.label :title
14
14
  = f.text_field :title
15
+ .field
16
+ = f.label :status
17
+ = f.select :status, Contact::Status::VALUES.map {|v| [v.titleize, v] }
18
+
19
+ = upload_mount_field(resource.avatar, :reset_path => true)
15
20
 
16
21
  = render 'e9_tags/form', :f => f, :context => [:users, true]
17
22
 
18
23
  %fieldset.contact-contact-fields
19
24
  %legend= e9_t(:form_legend_contact)
20
-
21
- %fieldset.nested-associations
22
- %legend
23
- %span= f.label(:users)
24
- = link_to_add_record_attribute(:users)
25
- = render_record_attribute_association(:users, f)
26
-
27
- %fieldset.nested-associations
28
- %legend
29
- %span= f.label(:phone_number_attributes)
30
- = link_to_add_record_attribute(:phone_number_attributes)
31
- = render_record_attribute_association(:phone_number_attributes, f)
32
-
33
- %fieldset.nested-associations
34
- %legend
35
- %span= f.label(:instant_messaging_handle_attributes)
36
- = link_to_add_record_attribute(:instant_messaging_handle_attributes)
37
- = render_record_attribute_association(:instant_messaging_handle_attributes, f)
38
-
39
- %fieldset.nested-associations
40
- %legend
41
- %span= f.label(:website_attributes)
42
- = link_to_add_record_attribute(:website_attributes)
43
- = render_record_attribute_association(:website_attributes, f)
44
-
45
- %fieldset.nested-associations
46
- %legend
47
- %span= f.label(:address_attributes)
48
- = link_to_add_record_attribute(:address_attributes)
49
- = render_record_attribute_association(:address_attributes, f)
50
-
51
- = javascript_include_tag templates_contacts_path
25
+ = render_record_attribute_form(:users , f)
26
+ = render_record_attribute_form(:phone_number_attributes , f)
27
+ = render_record_attribute_form(:instant_messaging_handle_attributes , f)
28
+ = render_record_attribute_form(:website_attributes , f)
29
+ = render_record_attribute_form(:address_attributes , f)
30
+
31
+ %fieldset.contact-background-info
32
+ %legend= f.label(:info)
33
+ .field.tinymce
34
+ = f.text_area :info
35
+
36
+ - content_for :bottom_javascripts do
37
+ = javascript_include_tag templates_contacts_path
@@ -0,0 +1,14 @@
1
+ .contact-who
2
+ %span.contact-who-name= record.name
3
+
4
+ = " - " if record.company_id? || record.title?
5
+
6
+ - if record.title?
7
+ %span.contact-who-title
8
+ = link_to_contact_search(:by_title, record.title)
9
+
10
+ = " at " if record.company_id? && record.title?
11
+
12
+ - if record.company_id?
13
+ %span.contact-who-company
14
+ = link_to_contact_search(:by_company, record.company_id, record.company_name)
@@ -1,13 +1,12 @@
1
1
  = title (@index_title || e9_t(:index_title))
2
2
 
3
-
4
- #index_header
3
+ #index-header
5
4
  = render 'header'
6
5
 
7
- #records_table
8
- = render 'table', :resources => collection
9
-
10
6
  #contact-tag-list
11
7
  = render 'tag_table'
12
8
 
9
+ #records_table
10
+ = render 'table', :resources => collection
11
+
13
12
  = render 'footer'
@@ -0,0 +1,8 @@
1
+ .dated-cost{:id => "dated_cost_#{dated_cost.id}"}
2
+ .field
3
+ = l(dated_cost.date)
4
+ .field.cost{':data-cost' => dated_cost.cost.to_f}
5
+ = dated_cost.cost
6
+ .actions
7
+ = link_to_edit_resource(dated_cost, :remote => true)
8
+ = link_to_destroy_resource(dated_cost, :remote => true)
@@ -0,0 +1,3 @@
1
+ = form_for resource, :url => polymorphic_path([parent, resource]), :remote => request.xhr? do |f|
2
+ .errors= resource_error_messages!
3
+ = render 'form_inner', :f => f
@@ -0,0 +1,15 @@
1
+ - id = f.object.persisted? ? f.object.id : @temp_id
2
+ .field.date-picker
3
+ - if temp_id = f.object.temp_id || @temp_id
4
+ = f.hidden_field :temp_id, :value => temp_id
5
+ - fid = "dated_cost_date_#{id}"
6
+ - unless request.xhr?
7
+ = f.label :date, nil, :class => :req, :for => id
8
+ = f.text_field :date, :class => 'date-picker', :value => l(f.object.date), :id => id
9
+ .field
10
+ - fid = "dated_cost_cost_#{id}"
11
+ - unless request.xhr?
12
+ = f.label :cost, nil, :class => :req, :for => id
13
+ = f.text_field :cost, :id => id
14
+ .actions
15
+ = f.submit
@@ -0,0 +1,20 @@
1
+ = form_tag bulk_create_dated_costs_path do
2
+ .field.date-picker
3
+ = label_tag 'dated_cost_date', resource_class.human_attribute_name(:date)
4
+ %input{:id => 'dated_cost_date', :type => 'text', :class => 'date-picker', :name => 'date', :value => l(params[:date] || Date.today)}
5
+
6
+ %table
7
+ %thead
8
+ %th= AdvertisingCampaign.model_name.human
9
+ %th= resource_class.human_attribute_name(:cost)
10
+ %tbody
11
+ - @advertising_campaigns.each do |ac|
12
+ %tr
13
+ %td
14
+ = ac.name
15
+ %td
16
+ %input{:type => 'hidden', :name => 'id[]', :value => ac.id}
17
+ %input{:type => 'text', :name => 'cost[]'}
18
+
19
+ .actions
20
+ = submit_tag
@@ -0,0 +1,5 @@
1
+ <% if resource.errors.any? %>
2
+ $("#dated_cost_<%= resource.temp_id %>").html("<%= escape_javascript(render('form')) %>");
3
+ <% else %>
4
+ $("#dated_cost_<%= resource.temp_id %>").replaceWith("<%= escape_javascript(render(resource)) %>");
5
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <% if !resource.errors.any? %>
2
+ $("#dated_cost_<%= resource.id %>").fadeOut();
3
+ <% end %>
@@ -0,0 +1,3 @@
1
+ - id = resource.persisted? ? "dated_cost_#{resource.id}" : nil
2
+ .dated-cost{:id => id }
3
+ = render 'form'
@@ -0,0 +1 @@
1
+ $("#dated_cost_<%= resource.id %>").html("<%= escape_javascript(render('form')) %>");
@@ -0,0 +1,15 @@
1
+ = title @index_title || e9_t(:index_title)
2
+
3
+ %fieldset
4
+ .dated-costs
5
+ .dated-cost-labels
6
+ .field
7
+ = resource_class.human_attribute_name(:date)
8
+ .field
9
+ = resource_class.human_attribute_name(:cost)
10
+
11
+ = render collection
12
+
13
+ .actions
14
+ = link_to_new_resource(resource_class, :remote => true)
15
+
@@ -0,0 +1,2 @@
1
+ .dated-cost{:id => "dated_cost_#{@temp_id}"}
2
+ = render 'form'
@@ -0,0 +1 @@
1
+ = render resource
@@ -0,0 +1,5 @@
1
+ <% if resource.errors.any? %>
2
+ $("#dated_cost_<%= resource.id %>").html("<%= escape_javascript(render('form')) %>");
3
+ <% else %>
4
+ $("#dated_cost_<%= resource.id %>").replaceWith("<%= escape_javascript(render(resource)) %>");
5
+ <% end %>
@@ -1,6 +1,9 @@
1
1
  .field
2
2
  = f.label :name, nil, :class => :req
3
3
  = f.text_field :name
4
+ .field
5
+ = f.label :from_email
6
+ = f.text_field :from_email
4
7
  .field
5
8
  = f.label :subject, nil, :class => :req
6
9
  = f.text_field :subject
@@ -0,0 +1,3 @@
1
+ .toolbar
2
+ .toolbar-right
3
+ = link_to_new_resource(EmailTemplate)
@@ -0,0 +1,4 @@
1
+ %fieldset.nested-associations
2
+ %legend= form.label(association_name)
3
+ = render_record_attribute_association(association_name, form)
4
+ = link_to_add_record_attribute(association_name)
@@ -1,2 +1,2 @@
1
1
  = title e9_t(:show_title)
2
- = render 'form'
2
+ = render resource
@@ -6,12 +6,18 @@ en:
6
6
  none: None
7
7
  totals: Totals
8
8
  inline_help_link: '[?]'
9
+ edit_dated_costs: Edit Costs
9
10
 
10
11
  e9_crm:
11
12
  add_record_attribute: Add
12
13
  destroy_record_attribute: Remove
13
14
 
14
15
  activerecord:
16
+ titles:
17
+ show: 'Show %{model}'
18
+ campaign:
19
+ show: '%{model}'
20
+
15
21
  links:
16
22
  edit: "Edit"
17
23
  destroy: "Delete"
@@ -54,6 +60,7 @@ en:
54
60
  campaign_type: Campaign Type
55
61
  company: Company
56
62
  contact: Contact
63
+ dated_cost: Advertising Cost
57
64
  deal: Deal
58
65
  page_view: Page View
59
66
  tracking_cookie: Tracking Cookie
@@ -62,6 +69,16 @@ en:
62
69
  campaign:
63
70
  name: Name
64
71
  code_hint: Append to URL
72
+ contact:
73
+ avatar: Photo
74
+ users: Email Addresses
75
+ address_attributes: Addresses
76
+ instant_messaging_handle_attributes: Instant Messaging Handles
77
+ phone_number_attrubutes: Phone Numbers
78
+ website_attributes: Websites
79
+ info: Background Information
80
+ company:
81
+ info: Background Information
65
82
  no_campaign:
66
83
  name: No Campaign
67
84
  offer:
data/config/routes.rb CHANGED
@@ -3,9 +3,15 @@ Rails.application.routes.draw do
3
3
 
4
4
  scope :path => crm_path, :module => :e9_crm do
5
5
  resources :companies, :except => :show
6
- resources :contacts, :except => :show do
6
+ resources :contacts do
7
+ # page_views currently not routed, but near working
7
8
  #resources :page_views, :path => 'activity', :only => :index
9
+
8
10
  collection { get :templates }
11
+ member do
12
+ post :upload_avatar
13
+ delete :reset_avatar
14
+ end
9
15
  end
10
16
  resources :deals, :except => :show
11
17
  resources :offers, :except => :show
@@ -18,7 +24,14 @@ Rails.application.routes.draw do
18
24
  collection { post :update_order }
19
25
  end
20
26
 
27
+ resources :dated_costs, :path => 'advertising_costs', :only => [:index] do
28
+ collection do
29
+ post :bulk_create
30
+ end
31
+ end
32
+
21
33
  resources :campaigns, :only => [:index, :destroy] do
34
+ # page_views currently not routed, but near working
22
35
  #resources :page_views, :path => 'activity', :only => :index
23
36
  end
24
37
  scope :path => :campaigns do
@@ -51,7 +64,6 @@ Rails.application.routes.draw do
51
64
  campaigns/email
52
65
  campaigns/sales
53
66
  campaigns/groups
54
- contacts
55
67
  deals
56
68
  email_templates
57
69
  menu_options
data/e9_crm.gemspec CHANGED
@@ -23,7 +23,7 @@ Gem::Specification.new do |s|
23
23
  s.add_dependency("inherited_resources", "~> 1.1.2")
24
24
  s.add_dependency("has_scope")
25
25
  s.add_dependency("money")
26
- s.add_dependency("e9_rails", "~> 0.0.13")
26
+ s.add_dependency("e9_rails", "~> 0.0.14")
27
27
  s.add_dependency("e9_tags", "~> 0.0.11")
28
28
  s.add_dependency("will_paginate")
29
29
  end
data/lib/e9_crm/model.rb CHANGED
@@ -21,7 +21,7 @@ module E9Crm
21
21
 
22
22
  self.options_parameters = [:type, :primary]
23
23
 
24
- scope :primary, lambda { where(arel_table[:options].matches("%primary: true%")) }
24
+ scope :primary, lambda { where(%Q{#{table_name}.options REGEXP "primary: [\\"']?true"}) }
25
25
 
26
26
  before_create :create_contact_if_missing
27
27
  after_destroy :cleanup_contact
@@ -1,3 +1,3 @@
1
1
  module E9Crm
2
- VERSION = '0.1.4'
2
+ VERSION = '0.1.5'
3
3
  end
@@ -26,6 +26,7 @@ class CreateE9CrmStructure < ActiveRecord::Migration
26
26
  t.string :title
27
27
  t.string :avatar
28
28
  t.string :status
29
+ t.text :info
29
30
  t.references :company
30
31
  t.timestamps
31
32
  end
@@ -63,6 +64,7 @@ class CreateE9CrmStructure < ActiveRecord::Migration
63
64
 
64
65
  create_table :companies, :force => true do |t|
65
66
  t.string :name
67
+ t.text :info
66
68
  t.timestamps
67
69
  end
68
70
 
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.4
5
+ version: 0.1.5
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-16 00:00:00 -04:00
13
+ date: 2011-05-18 00:00:00 -04:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -65,7 +65,7 @@ dependencies:
65
65
  requirements:
66
66
  - - ~>
67
67
  - !ruby/object:Gem::Version
68
- version: 0.0.13
68
+ version: 0.0.14
69
69
  type: :runtime
70
70
  version_requirements: *id005
71
71
  - !ruby/object:Gem::Dependency
@@ -140,6 +140,7 @@ files:
140
140
  - app/controllers/e9_crm/affiliate_campaigns_controller.rb
141
141
  - app/controllers/e9_crm/base_controller.rb
142
142
  - app/controllers/e9_crm/campaign_groups_controller.rb
143
+ - app/controllers/e9_crm/campaign_subclass_controller.rb
143
144
  - app/controllers/e9_crm/campaigns_controller.rb
144
145
  - app/controllers/e9_crm/companies_controller.rb
145
146
  - app/controllers/e9_crm/contact_emails_controller.rb
@@ -157,9 +158,11 @@ files:
157
158
  - app/helpers/e9_crm/base_helper.rb
158
159
  - app/helpers/e9_crm/campaign_groups_helper.rb
159
160
  - app/helpers/e9_crm/campaigns_helper.rb
161
+ - app/helpers/e9_crm/companies_helper.rb
160
162
  - app/helpers/e9_crm/contact_merges_helper.rb
161
163
  - app/helpers/e9_crm/contacts_helper.rb
162
164
  - app/helpers/e9_crm/deals_helper.rb
165
+ - app/helpers/e9_crm/email_templates_helper.rb
163
166
  - app/helpers/e9_crm/menu_options_helper.rb
164
167
  - app/helpers/e9_crm/page_views_helper.rb
165
168
  - app/models/address_attribute.rb
@@ -189,6 +192,7 @@ files:
189
192
  - app/observers/deal_observer.rb
190
193
  - app/uploaders/file_uploader.rb
191
194
  - app/views/e9_crm/advertising_campaigns/_form_inner.html.haml
195
+ - app/views/e9_crm/advertising_campaigns/costs.html.haml
192
196
  - app/views/e9_crm/affiliate_campaigns/_form_inner.html.haml
193
197
  - app/views/e9_crm/campaign_groups/_footer.html.haml
194
198
  - app/views/e9_crm/campaign_groups/_header.html.haml
@@ -198,6 +202,7 @@ files:
198
202
  - app/views/e9_crm/campaigns/_reports_table.html.haml
199
203
  - app/views/e9_crm/campaigns/_table.html.haml
200
204
  - app/views/e9_crm/campaigns/reports.html.haml
205
+ - app/views/e9_crm/companies/_form_inner.html.haml
201
206
  - app/views/e9_crm/contact_emails/_form.html.haml
202
207
  - app/views/e9_crm/contact_emails/_form_inner.html.haml
203
208
  - app/views/e9_crm/contact_emails/destroy.js.erb
@@ -205,14 +210,28 @@ files:
205
210
  - app/views/e9_crm/contact_merges/_field.html.haml
206
211
  - app/views/e9_crm/contact_merges/_form.html.haml
207
212
  - app/views/e9_crm/contact_merges/new.html.haml
213
+ - app/views/e9_crm/contacts/_contact.html.haml
208
214
  - app/views/e9_crm/contacts/_details.html.haml
209
215
  - app/views/e9_crm/contacts/_form_inner.html.haml
210
216
  - app/views/e9_crm/contacts/_header.html.haml
211
217
  - app/views/e9_crm/contacts/_tag_table.html.haml
218
+ - app/views/e9_crm/contacts/_who.html.haml
212
219
  - app/views/e9_crm/contacts/index.html.haml
213
220
  - app/views/e9_crm/contacts/index.js.erb
214
221
  - app/views/e9_crm/contacts/merge.html.haml
215
222
  - app/views/e9_crm/contacts/templates.js.erb
223
+ - app/views/e9_crm/dated_costs/_dated_cost.html.haml
224
+ - app/views/e9_crm/dated_costs/_form.html.haml
225
+ - app/views/e9_crm/dated_costs/_form_inner.html.haml
226
+ - app/views/e9_crm/dated_costs/bulk_form.html.haml
227
+ - app/views/e9_crm/dated_costs/create.js.erb
228
+ - app/views/e9_crm/dated_costs/destroy.js.erb
229
+ - app/views/e9_crm/dated_costs/edit.html.haml
230
+ - app/views/e9_crm/dated_costs/edit.js.erb
231
+ - app/views/e9_crm/dated_costs/index.html.haml
232
+ - app/views/e9_crm/dated_costs/new.html.haml
233
+ - app/views/e9_crm/dated_costs/show.html.haml
234
+ - app/views/e9_crm/dated_costs/update.js.erb
216
235
  - app/views/e9_crm/deals/_form_inner.html.haml
217
236
  - app/views/e9_crm/deals/_header.html.haml
218
237
  - app/views/e9_crm/deals/_reports_table.html.haml
@@ -220,7 +239,9 @@ files:
220
239
  - app/views/e9_crm/deals/reports.html.haml
221
240
  - app/views/e9_crm/deals/reports.js.erb
222
241
  - app/views/e9_crm/email_campaigns/_form_inner.html.haml
242
+ - app/views/e9_crm/email_templates/_footer.html.haml
223
243
  - app/views/e9_crm/email_templates/_form_inner.html.haml
244
+ - app/views/e9_crm/email_templates/_header.html.haml
224
245
  - app/views/e9_crm/menu_options/_form_inner.html.haml
225
246
  - app/views/e9_crm/menu_options/_header.html.haml
226
247
  - app/views/e9_crm/offers/_form.html.haml
@@ -228,6 +249,7 @@ files:
228
249
  - app/views/e9_crm/offers/_form_inner.html.haml.bak
229
250
  - app/views/e9_crm/page_views/_table.html.haml
230
251
  - app/views/e9_crm/record_attributes/_address_attribute.html.haml
252
+ - app/views/e9_crm/record_attributes/_form_partial.html.haml
231
253
  - app/views/e9_crm/record_attributes/_instant_messaging_handle_attribute.html.haml
232
254
  - app/views/e9_crm/record_attributes/_phone_number_attribute.html.haml
233
255
  - app/views/e9_crm/record_attributes/_record_attribute.html.haml