erp_tech_svcs 4.0.0 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
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