erp_tech_svcs 4.0.0 → 4.2.0

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -24
  3. data/app/controllers/api/v1/audit_log_items_controller.rb +33 -0
  4. data/app/controllers/api/v1/audit_logs_controller.rb +32 -0
  5. data/app/controllers/api/v1/capabilities_controller.rb +160 -0
  6. data/app/controllers/api/v1/file_assets_controller.rb +40 -0
  7. data/app/controllers/api/v1/groups_controller.rb +236 -0
  8. data/app/controllers/api/v1/security_roles_controller.rb +276 -0
  9. data/app/controllers/api/v1/users_controller.rb +262 -0
  10. data/app/controllers/erp_tech_svcs/session_controller.rb +8 -5
  11. data/app/controllers/erp_tech_svcs/user_controller.rb +14 -15
  12. data/app/mailers/user_mailer.rb +8 -5
  13. data/app/models/audit_log.rb +111 -36
  14. data/app/models/audit_log_item.rb +30 -0
  15. data/app/models/audit_log_item_type.rb +1 -0
  16. data/app/models/audit_log_type.rb +19 -0
  17. data/app/models/capability.rb +22 -6
  18. data/app/models/extensions/tracked_status_type.rb +3 -0
  19. data/app/models/file_asset.rb +245 -20
  20. data/app/models/file_asset_holder.rb +20 -0
  21. data/app/models/group.rb +38 -25
  22. data/app/models/notification.rb +32 -13
  23. data/app/models/notification_type.rb +13 -0
  24. data/app/models/security_role.rb +17 -4
  25. data/app/models/user.rb +116 -29
  26. data/app/validators/password_strength_validator.rb +1 -1
  27. data/app/views/user_mailer/activation_needed_email.html.erb +293 -15
  28. data/app/views/user_mailer/reset_password_email.html.erb +268 -13
  29. data/config/initializers/logger.rb +19 -0
  30. data/config/initializers/sorcery.rb +2 -0
  31. data/config/initializers/wickedpdf.rb +4 -0
  32. data/config/routes.rb +64 -0
  33. data/db/data_migrations/20110802200222_schedule_delete_expired_sessions_job.rb +1 -5
  34. data/db/data_migrations/20150819140550_create_job_tracker_for_notification.rb +14 -0
  35. data/db/migrate/20080805000010_base_tech_services.rb +99 -39
  36. data/db/migrate/20150414151421_add_nested_set_columns_to_security_role.rb +13 -0
  37. data/db/migrate/20150609003216_update_user_for_sorcery.rb +11 -0
  38. data/db/migrate/20150819135108_add_custom_fields_to_notifications.rb +5 -0
  39. data/db/migrate/20160122155402_add_description_to_file_asset.rb +13 -0
  40. data/db/migrate/20160310163060_add_created_by_updated_by_to_erp_tech_svcs.rb +35 -0
  41. data/db/migrate/20160313161611_add_tenant_id_to_audit_log.rb +16 -0
  42. data/lib/erp_tech_svcs.rb +6 -10
  43. data/lib/erp_tech_svcs/config.rb +7 -2
  44. data/lib/erp_tech_svcs/delayed_jobs/delete_expired_sessions_job.rb +49 -0
  45. data/lib/erp_tech_svcs/delayed_jobs/notification_job.rb +50 -0
  46. data/lib/erp_tech_svcs/engine.rb +0 -1
  47. data/lib/erp_tech_svcs/erp_tech_svcs_audit_log.rb +12 -6
  48. data/lib/erp_tech_svcs/extensions.rb +0 -1
  49. data/lib/erp_tech_svcs/extensions/active_record/has_capability_accessors.rb +57 -29
  50. data/lib/erp_tech_svcs/extensions/active_record/has_file_assets.rb +57 -31
  51. data/lib/erp_tech_svcs/extensions/active_record/has_security_roles.rb +12 -4
  52. data/lib/erp_tech_svcs/extensions/active_record/is_json.rb +22 -15
  53. data/lib/erp_tech_svcs/extensions/active_record/scoped_by.rb +16 -13
  54. data/lib/erp_tech_svcs/extensions/compass_ae/erp_base_erp_svcs/controllers/api/parties_controller.rb +15 -0
  55. data/lib/erp_tech_svcs/file_support.rb +1 -0
  56. data/lib/erp_tech_svcs/file_support/file_system_manager.rb +77 -44
  57. data/lib/erp_tech_svcs/file_support/manager.rb +12 -3
  58. data/lib/erp_tech_svcs/file_support/railties/compass_ae_resolver.rb +49 -0
  59. data/lib/erp_tech_svcs/file_support/s3_manager.rb +73 -51
  60. data/lib/erp_tech_svcs/utils/compass_access_negotiator.rb +11 -2
  61. data/lib/erp_tech_svcs/utils/default_nested_set_methods.rb +238 -46
  62. data/lib/erp_tech_svcs/version.rb +1 -1
  63. data/lib/tasks/erp_tech_svcs_tasks.rake +43 -5
  64. metadata +73 -42
  65. data/app/models/user_defined_data.rb +0 -6
  66. data/app/models/user_defined_field.rb +0 -8
  67. data/config/initializers/pdfkit.rb +0 -18
  68. data/db/data_migrations/20121130212146_note_capabilities.rb +0 -23
  69. data/db/migrate/20121116151510_create_groups.rb +0 -18
  70. data/db/migrate/20121126171612_upgrade_security.rb +0 -53
  71. data/db/migrate/20121126173506_upgrade_security2.rb +0 -274
  72. data/db/migrate/20130410135419_add_queue_to_delayed_jobs.rb +0 -13
  73. data/db/migrate/20130610163240_create_notifications.rb +0 -37
  74. data/db/migrate/20130725212647_add_party_id_idx_to_users.rb +0 -9
  75. data/db/migrate/20131113213843_add_audit_log_item_old_value.rb +0 -13
  76. data/db/migrate/20131113213844_add_erp_tech_svcs_missing_indexes.rb +0 -31
  77. data/db/migrate/20131129203603_add_user_defined_fields.rb +0 -43
  78. data/db/migrate/20141013060204_add_custom_fields_to_notifications.rb +0 -12
  79. data/db/migrate/20141108182427_add_scoped_by_to_file_assets.rb +0 -14
  80. data/lib/erp_tech_svcs/extensions/active_record/has_user_defined_data.rb +0 -147
  81. data/lib/erp_tech_svcs/sessions/delete_expired_sessions_job.rb +0 -47
  82. data/lib/erp_tech_svcs/sessions/delete_expired_sessions_service.rb +0 -15
  83. data/lib/erp_tech_svcs/utils/compass_logger.rb +0 -87
@@ -3,10 +3,10 @@ module ErpTechSvcs
3
3
  def create
4
4
  login = params[:login].strip
5
5
  if login(login, params[:password])
6
- #log when someone logs in
6
+ # log when someone logs in
7
7
  ErpTechSvcs::ErpTechSvcsAuditLog.successful_login(current_user)
8
8
 
9
- #set logout
9
+ # set logout
10
10
  session[:logout_to] = params[:logout_to]
11
11
 
12
12
  login_to = session[:return_to_url].blank? ? params[:login_to] : session[:return_to_url]
@@ -20,13 +20,16 @@ module ErpTechSvcs
20
20
 
21
21
  def destroy
22
22
  message = "You have logged out."
23
- logged_out_user_id = current_user.id unless current_user === false
23
+ user = current_user
24
24
  logout_to = session[:logout_to]
25
25
 
26
+ # clear return_to_url
27
+ session[:return_to_url] = nil
28
+
26
29
  logout
27
30
 
28
- #log when someone logs out
29
- ErpTechSvcs::ErpTechSvcsAuditLog.successful_logout(logged_out_user_id) if logged_out_user_id
31
+ # log when someone logs out
32
+ ErpTechSvcs::ErpTechSvcsAuditLog.successful_logout(user) unless user.nil?
30
33
 
31
34
  if logout_to
32
35
  redirect_to logout_to, :notice => message
@@ -30,25 +30,21 @@ module ErpTechSvcs
30
30
 
31
31
  def reset_password
32
32
  begin
33
- login_url = params[:login_url].blank? ? ErpTechSvcs::Config.login_url : params[:login_url]
34
33
  login = params[:login].strip
35
34
  if user = (User.where('username = ? or email = ?', login, login)).first
36
35
 
37
- # generate new password with only letters
38
- charset = %w{ A C D E F G H J K M N P Q R T V W X Y Z }
39
- new_password = (0...8).map{ charset.to_a[rand(charset.size)] }.join
40
-
41
- user.password_confirmation = new_password
42
- if user.change_password!(new_password)
43
- user.add_instance_attribute(:login_url, login_url)
44
- user.add_instance_attribute(:domain, params[:domain])
45
- user.deliver_reset_password_instructions!
46
- message = "Password has been reset. An email has been sent with further instructions to #{user.email}."
47
- success = true
48
- else
49
- message = "Error re-setting password."
50
- success = false
36
+ website = Website.find_by_host(request.host_with_port)
37
+ if website
38
+ user.add_instance_attribute(:website_id, website.id)
51
39
  end
40
+
41
+ user.add_instance_attribute(:reset_password_url, (params[:reset_password_url] || '/erp_app/reset_password'))
42
+ user.add_instance_attribute(:domain, params[:domain])
43
+ user.deliver_reset_password_instructions!
44
+
45
+ message = "Password has been reset. An email has been sent with further instructions to #{user.email}."
46
+ success = true
47
+
52
48
  else
53
49
  message = "Invalid user name or email address."
54
50
  success = false
@@ -57,6 +53,9 @@ module ErpTechSvcs
57
53
  rescue => ex
58
54
  Rails.logger.error ex.message
59
55
  Rails.logger.error ex.backtrace.join("\n")
56
+
57
+ ExceptionNotifier.notify_exception(ex) if defined? ExceptionNotifier
58
+
60
59
  render :json => {:success => false, :message => 'Error sending email.'}
61
60
  end
62
61
  end
@@ -1,7 +1,7 @@
1
1
  class UserMailer < ActionMailer::Base
2
2
  default :from => ErpTechSvcs::Config.email_notifications_from
3
3
 
4
- def activation_needed_email(user)
4
+ def activation_needed_email(user, dba_organization=nil)
5
5
  @user = user
6
6
  @url = "#{get_domain(user.instance_attributes[:domain])}/users/activate/#{user.activation_token}"
7
7
  @url << "?login_url=#{@user.instance_attributes[:login_url]}" unless @user.instance_attributes[:login_url].nil?
@@ -11,15 +11,18 @@ class UserMailer < ActionMailer::Base
11
11
  mail(:to => user.email, :subject => "An account has been created and needs activation")
12
12
  end
13
13
 
14
- def reset_password_email(user)
14
+ def reset_password_email(user, dba_organization=nil)
15
15
  @user = user
16
- @url = "#{get_domain(user.instance_attributes[:domain])}#{@user.instance_attributes[:login_url]}"
16
+ @reset_password_token = @user.reset_password_token
17
+
18
+ @url = "#{get_domain(user.instance_attributes[:domain])}#{@user.instance_attributes[:reset_password_url]}?token=#{@reset_password_token}"
19
+
17
20
  mail(:to => user.email, :subject => "Your password has been reset")
18
21
  end
19
22
 
20
23
  def get_domain(domain)
21
24
  domain = domain || ErpTechSvcs::Config.installation_domain
22
- domain = "http://#{domain}" if domain.index('http').nil?
23
- domain
25
+
26
+ "#{ErpTechSvcs::Config.file_protocol}://#{domain}"
24
27
  end
25
28
  end
@@ -1,6 +1,24 @@
1
+ # create_table :audit_logs do |t|
2
+ # t.string :application
3
+ # t.string :description
4
+ # t.integer :party_id
5
+ # t.text :additional_info
6
+ # t.references :audit_log_type
7
+ #
8
+ # #polymorphic columns
9
+ # t.references :event_record, :polymorphic => true
10
+ #
11
+ # t.timestamps
12
+ # end
13
+ # add_index :audit_logs, :party_id
14
+ # add_index :audit_logs, [:event_record_id, :event_record_type], :name => 'event_record_index'
15
+ # add_index :audit_logs, :audit_log_type_id, :name => 'audit_logs_audit_log_type_id_idx'
16
+
1
17
  class AuditLog < ActiveRecord::Base
2
18
  attr_protected :created_at, :updated_at
3
19
 
20
+ is_tenantable
21
+
4
22
  validates :party_id, :presence => {:message => 'cannot be blank'}
5
23
  validates :description, :presence => {:message => 'cannot be blank'}
6
24
  validates :audit_log_type, :presence => {:message => 'cannot be blank'}
@@ -8,78 +26,135 @@ class AuditLog < ActiveRecord::Base
8
26
  belongs_to :audit_log_type
9
27
  belongs_to :party
10
28
  belongs_to :event_record, :polymorphic => true
11
- has_many :audit_log_items, :dependent => :destroy
29
+ has_many :audit_log_items, :dependent => :destroy
12
30
 
13
31
  alias :items :audit_log_items
14
32
  alias :type :audit_log_type
15
33
 
16
- def get_item_by_item_type_internal_identifier(item_type_internal_identifier)
17
- self.items.includes(:audit_log_item_type)
18
- .where(:audit_log_item_types => {:internal_identifier => item_type_internal_identifier}).first
19
- end
34
+ class << self
20
35
 
21
- #allow items to be looked up by method calls
22
- def respond_to?(m, include_private_methods = false)
23
- (super ? true : get_item_by_item_type_internal_identifier(m.to_s)) rescue super
24
- end
36
+ # Filter records
37
+ #
38
+ # @param filters [Hash] a hash of filters to be applied,
39
+ # @param statement [ActiveRecord::Relation] the query being built
40
+ # @return [ActiveRecord::Relation] the query being built
41
+ def apply_filters(filters, statement=nil)
42
+ audit_log_tbl = self.arel_table
25
43
 
26
- #allow items to be looked up by method calls
27
- def method_missing(m, *args, &block)
28
- if self.respond_to?(m)
29
- item = get_item_by_item_type_internal_identifier(m.to_s)
30
- (item.nil?) ? super : (return item.audit_log_item_value)
31
- else
32
- super
44
+ unless statement
45
+ statement = self
46
+ end
47
+
48
+ # filter by tenant
49
+ unless filters[:tenant].blank?
50
+ statement = statement.by_tenant(filters[:tenant])
51
+ end
52
+
53
+ # filter by logged by
54
+ unless filters[:logged_by].blank?
55
+ statement = statement.where(party_id: filters[:logged_by].split(','))
56
+ end
57
+
58
+ # filter by start_at
59
+ unless filters[:start_date].blank?
60
+ statement = statement.where(audit_log_tbl[:created_at].gteq(filters[:start_date]))
61
+ end
62
+
63
+ # filter by end_at
64
+ unless filters[:end_date].blank?
65
+ statement = statement.where(audit_log_tbl[:created_at].lteq(filters[:end_date]))
66
+ end
67
+
68
+ statement
33
69
  end
34
- end
35
70
 
36
- class << self
37
71
  def custom_application_log_message(party, msg)
38
72
  self.create(
39
- :party_id => party.id,
40
- :audit_log_type => AuditLogType.find_by_type_and_subtype_iid('application','custom_message'),
41
- :description => "#{party.description}: #{msg}"
73
+ :party_id => party.id,
74
+ :audit_log_type => AuditLogType.find_by_type_and_subtype_iid('application', 'custom_message'),
75
+ :description => "#{party.description}: #{msg}"
42
76
  )
43
77
  end
44
78
 
45
79
  def party_logout(party)
46
80
  self.create(
47
- :party_id => party.id,
48
- :audit_log_type => AuditLogType.find_by_type_and_subtype_iid('application','successful_logout'),
49
- :description => "#{party.description} has logged out"
81
+ :party_id => party.id,
82
+ :audit_log_type => AuditLogType.find_by_type_and_subtype_iid('application', 'successful_logout'),
83
+ :description => "#{party.description} has logged out"
50
84
  )
51
85
  end
52
86
 
53
87
  def party_login(party)
54
88
  self.create(
55
- :party_id => party.id,
56
- :audit_log_type => AuditLogType.find_by_type_and_subtype_iid('application','successful_login'),
57
- :description => "#{party.description} has logged in"
89
+ :party_id => party.id,
90
+ :audit_log_type => AuditLogType.find_by_type_and_subtype_iid('application', 'successful_login'),
91
+ :description => "#{party.description} has logged in"
58
92
  )
59
93
  end
60
94
 
61
95
  def party_access(party, url)
62
96
  self.create(
63
- :party_id => party.id,
64
- :audit_log_type => AuditLogType.find_by_type_and_subtype_iid('application','accessed_area'),
65
- :description => "#{party.description} has accessed area #{url}"
97
+ :party_id => party.id,
98
+ :audit_log_type => AuditLogType.find_by_type_and_subtype_iid('application', 'accessed_area'),
99
+ :description => "#{party.description} has accessed area #{url}"
66
100
  )
67
101
  end
68
102
 
69
103
  def party_failed_access(party, url)
70
104
  self.create(
71
- :party_id => party.id,
72
- :audit_log_type => AuditLogType.find_by_type_and_subtype_iid('application','accessed_area'),
73
- :description => "#{party.description} has tried to access a restricted area #{url}"
105
+ :party_id => party.id,
106
+ :audit_log_type => AuditLogType.find_by_type_and_subtype_iid('application', 'accessed_area'),
107
+ :description => "#{party.description} has tried to access a restricted area #{url}"
74
108
  )
75
109
  end
76
110
 
77
111
  def party_session_timeout(party)
78
112
  self.create(
79
- :party_id => party.id,
80
- :audit_log_type => AuditLogType.find_by_type_and_subtype_iid('application','session_timeout'),
81
- :description => "#{party.description} session has expired"
113
+ :party_id => party.id,
114
+ :audit_log_type => AuditLogType.find_by_type_and_subtype_iid('application', 'session_timeout'),
115
+ :description => "#{party.description} session has expired"
82
116
  )
83
117
  end
84
118
  end
119
+
120
+ def get_item_by_item_type_internal_identifier(item_type_internal_identifier)
121
+ self.items.includes(:audit_log_item_type)
122
+ .where(:audit_log_item_types => {:internal_identifier => item_type_internal_identifier}).first
123
+ end
124
+
125
+ # convert to hash of data
126
+ #
127
+ def to_data_hash
128
+ data = to_hash(only: [:id, :application, :additional_info, :description, :created_at])
129
+
130
+ data[:party] = party.to_data_hash
131
+
132
+ if event_record.respond_to?(:to_data_hash)
133
+ data[:event_record] = event_record.to_data_hash
134
+ else
135
+ data[:event_record] = {id: event_record.id, type: event_record.class.name}
136
+ end
137
+
138
+ if type
139
+ data[:audit_log_type] = audit_log_type.to_data_hash
140
+ end
141
+
142
+ data
143
+ end
144
+
145
+ # allow items to be looked up by method calls
146
+ def respond_to?(m, include_private_methods = false)
147
+ (super ? true : get_item_by_item_type_internal_identifier(m.to_s)) rescue super
148
+ end
149
+
150
+ # allow items to be looked up by method calls
151
+ def method_missing(m, *args, &block)
152
+ if self.respond_to?(m)
153
+ item = get_item_by_item_type_internal_identifier(m.to_s)
154
+ (item.nil?) ? super : (return item.audit_log_item_value)
155
+ else
156
+ super
157
+ end
158
+ end
159
+
85
160
  end
@@ -1,3 +1,16 @@
1
+ # create_table :audit_log_items do |t|
2
+ # t.references :audit_log
3
+ # t.references :audit_log_item_type
4
+ # t.string :audit_log_item_value
5
+ # t.string :audit_log_item_old_value
6
+ # t.string :description
7
+ #
8
+ # t.timestamps
9
+ # end
10
+ #
11
+ # add_index :audit_log_items, :audit_log_id, :name => 'audit_log_items_audit_log_id_idx'
12
+ # add_index :audit_log_items, :audit_log_item_type_id, :name => 'audit_log_items_audit_log_item_type_id_idx'
13
+
1
14
  class AuditLogItem < ActiveRecord::Base
2
15
  attr_protected :created_at, :updated_at
3
16
 
@@ -5,4 +18,21 @@ class AuditLogItem < ActiveRecord::Base
5
18
  belongs_to :audit_log
6
19
 
7
20
  alias :type :audit_log_item_type
21
+
22
+ # convert to hash of data
23
+ #
24
+ def to_data_hash
25
+ data = to_hash(only: [:id,
26
+ {audit_log_item_value: :new_value},
27
+ {audit_log_item_old_value: :old_value},
28
+ :description,
29
+ :created_at,
30
+ :updated_at])
31
+
32
+ if audit_log_item_type
33
+ data[:audit_log_item_type] = audit_log_item_type.to_data_hash
34
+ end
35
+
36
+ data
37
+ end
8
38
  end
@@ -5,4 +5,5 @@ class AuditLogItemType < ActiveRecord::Base
5
5
  acts_as_erp_type
6
6
 
7
7
  has_many :audit_log_items
8
+
8
9
  end
@@ -1,3 +1,22 @@
1
+ # create_table :audit_log_types do |t|
2
+ # t.string :description
3
+ # t.string :error_code
4
+ # t.string :comments
5
+ # t.string :internal_identifier
6
+ # t.string :external_identifier
7
+ # t.string :external_id_source
8
+ #
9
+ # # awesome nested set columns
10
+ # t.integer :parent_id
11
+ # t.integer :lft
12
+ # t.integer :rgt
13
+ #
14
+ # t.timestamps
15
+ # end
16
+ #
17
+ # add_index :audit_log_types, :internal_identifier, :name => 'audit_log_types_internal_identifier_idx'
18
+ # add_index :audit_log_types, :parent_id, :name => 'audit_log_types_parent_id_idx'
19
+
1
20
  class AuditLogType < ActiveRecord::Base
2
21
  attr_protected :created_at, :updated_at
3
22
 
@@ -1,6 +1,19 @@
1
+ # create_table :capabilities do |t|
2
+ # t.string :description
3
+ # t.references :capability_type
4
+ # t.references :capability_resource, :polymorphic => true
5
+ # t.integer :scope_type_id
6
+ # t.text :scope_query
7
+ # t.timestamps
8
+ # end
9
+ #
10
+ # add_index :capabilities, :capability_type_id
11
+ # add_index :capabilities, :scope_type_id
12
+ # add_index :capabilities, [:capability_resource_id, :capability_resource_type], :name => 'capability_resource_index'
13
+
1
14
  class Capability < ActiveRecord::Base
2
15
  attr_protected :created_at, :updated_at
3
-
16
+
4
17
  belongs_to :scope_type
5
18
  belongs_to :capability_type
6
19
  belongs_to :capability_resource, :polymorphic => true
@@ -13,11 +26,11 @@ class Capability < ActiveRecord::Base
13
26
  def update_description
14
27
  if description.blank?
15
28
  desc = "#{capability_type.description} #{capability_resource_type}"
16
- case scope_type
29
+ case scope_type
17
30
  when ScopeType.find_by_internal_identifier('class')
18
31
  self.description = "#{desc}"
19
32
  when ScopeType.find_by_internal_identifier('instance')
20
- self.description = "#{desc} Instance"
33
+ self.description = "#{desc} #{capability_resource.to_s} Instance"
21
34
  when ScopeType.find_by_internal_identifier('query')
22
35
  self.description = "#{desc} Scope"
23
36
  end
@@ -29,7 +42,7 @@ class Capability < ActiveRecord::Base
29
42
  SecurityRole.joins("LEFT JOIN capability_accessors ON capability_accessors.capability_id = #{self.id}
30
43
  AND capability_accessors.capability_accessor_record_type = 'SecurityRole'
31
44
  AND capability_accessors.capability_accessor_record_id = security_roles.id").
32
- where("capability_accessors.id IS NULL")
45
+ where("capability_accessors.id IS NULL")
33
46
  end
34
47
 
35
48
  def remove_all_roles
@@ -44,7 +57,7 @@ class Capability < ActiveRecord::Base
44
57
  User.joins("LEFT JOIN capability_accessors ON capability_accessors.capability_id = #{self.id}
45
58
  AND capability_accessors.capability_accessor_record_type = 'User'
46
59
  AND capability_accessors.capability_accessor_record_id = users.id").
47
- where("capability_accessors.id IS NULL")
60
+ where("capability_accessors.id IS NULL")
48
61
  end
49
62
 
50
63
  def users
@@ -55,11 +68,14 @@ class Capability < ActiveRecord::Base
55
68
  Group.joins("LEFT JOIN capability_accessors ON capability_accessors.capability_id = #{self.id}
56
69
  AND capability_accessors.capability_accessor_record_type = 'Group'
57
70
  AND capability_accessors.capability_accessor_record_id = groups.id").
58
- where("capability_accessors.id IS NULL")
71
+ where("capability_accessors.id IS NULL")
59
72
  end
60
73
 
61
74
  def groups
62
75
  Group.joins(:capability_accessors).where(:capability_accessors => {:capability_id => self.id })
63
76
  end
64
77
 
78
+ def to_data_hash
79
+ to_hash(only: [:id, :description])
80
+ end
65
81
  end
@@ -0,0 +1,3 @@
1
+ TrackedStatusType.class_eval do
2
+ include ErpTechSvcs::Utils::DefaultNestedSetMethods
3
+ end