effective_cpd 0.6.9 → 1.0.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.
- checksums.yaml +4 -4
- data/app/controllers/admin/cpd_audit_levels_controller.rb +7 -1
- data/app/controllers/admin/cpd_audit_reviews_controller.rb +7 -1
- data/app/controllers/admin/cpd_audits_controller.rb +7 -1
- data/app/controllers/effective/cpd_statements_controller.rb +9 -0
- data/app/datatables/admin/effective_cpd_audit_levels_datatable.rb +1 -1
- data/app/datatables/admin/effective_cpd_audit_reviews_datatable.rb +1 -1
- data/app/datatables/admin/effective_cpd_audits_datatable.rb +1 -1
- data/app/models/concerns/effective_cpd_audit.rb +425 -0
- data/app/models/concerns/effective_cpd_audit_level.rb +108 -0
- data/app/models/concerns/effective_cpd_audit_review.rb +241 -0
- data/app/models/concerns/effective_cpd_user.rb +6 -6
- data/app/models/effective/cpd_audit.rb +1 -397
- data/app/models/effective/cpd_audit_level.rb +1 -83
- data/app/models/effective/cpd_audit_response.rb +1 -0
- data/app/models/effective/cpd_audit_review.rb +1 -224
- data/config/effective_cpd.rb +3 -0
- data/db/migrate/01_create_effective_cpd.rb.erb +14 -3
- data/lib/effective_cpd/engine.rb +4 -0
- data/lib/effective_cpd/version.rb +1 -1
- data/lib/effective_cpd.rb +13 -1
- metadata +5 -2
@@ -1,87 +1,5 @@
|
|
1
1
|
module Effective
|
2
2
|
class CpdAuditLevel < ActiveRecord::Base
|
3
|
-
|
4
|
-
|
5
|
-
# For each cpd audit and cpd audit review wizard step
|
6
|
-
# rich_text_all_steps_audit_content
|
7
|
-
# rich_text_step_audit_content
|
8
|
-
|
9
|
-
# rich_text_all_steps_audit_review_content
|
10
|
-
# rich_text_step_audit_review_content
|
11
|
-
|
12
|
-
has_many :cpd_audit_level_sections, -> { CpdAuditLevelSection.sorted }, inverse_of: :cpd_audit_level, dependent: :destroy
|
13
|
-
accepts_nested_attributes_for :cpd_audit_level_sections, allow_destroy: true
|
14
|
-
|
15
|
-
has_many :cpd_audit_level_questions, -> { CpdAuditLevelQuestion.sorted }, through: :cpd_audit_level_sections
|
16
|
-
|
17
|
-
has_many :cpd_audit_reviews, -> { CpdAuditReview.sorted }, inverse_of: :cpd_audit_level, dependent: :destroy
|
18
|
-
accepts_nested_attributes_for :cpd_audit_reviews, allow_destroy: true
|
19
|
-
|
20
|
-
has_many :cpd_audits
|
21
|
-
|
22
|
-
if respond_to?(:log_changes)
|
23
|
-
log_changes(except: [:cpd_audits, :cpd_audit_reviews, :cpd_audit_level_sections, :cpd_audit_level_questions])
|
24
|
-
end
|
25
|
-
|
26
|
-
effective_resource do
|
27
|
-
title :string
|
28
|
-
|
29
|
-
determinations :text # Final determination by auditor
|
30
|
-
recommendations :text # Recommendations by audit reviewers
|
31
|
-
|
32
|
-
days_to_submit :integer # For auditee to submit statement
|
33
|
-
days_to_review :integer # For auditor/audit_review to be completed
|
34
|
-
|
35
|
-
conflict_of_interest :boolean # Feature flags
|
36
|
-
can_request_exemption :boolean
|
37
|
-
can_request_extension :boolean
|
38
|
-
|
39
|
-
days_to_declare_conflict :integer
|
40
|
-
days_to_request_exemption :integer
|
41
|
-
days_to_request_extension :integer
|
42
|
-
|
43
|
-
timestamps
|
44
|
-
end
|
45
|
-
|
46
|
-
serialize :determinations, Array
|
47
|
-
serialize :recommendations, Array
|
48
|
-
|
49
|
-
scope :deep, -> { all }
|
50
|
-
scope :sorted, -> { order(:title) }
|
51
|
-
|
52
|
-
validates :title, presence: true
|
53
|
-
validates :determinations, presence: true
|
54
|
-
validates :recommendations, presence: true
|
55
|
-
|
56
|
-
validates :days_to_submit, numericality: { greater_than: 0, allow_nil: true }
|
57
|
-
validates :days_to_review, numericality: { greater_than: 0, allow_nil: true }
|
58
|
-
|
59
|
-
validates :days_to_declare_conflict, presence: true, if: -> { conflict_of_interest? }
|
60
|
-
validates :days_to_declare_conflict, numericality: { greater_than: 0, allow_nil: true }
|
61
|
-
|
62
|
-
validates :days_to_request_exemption, presence: true, if: -> { can_request_exemption? }
|
63
|
-
validates :days_to_request_exemption, numericality: { greater_than: 0, allow_nil: true }
|
64
|
-
|
65
|
-
validates :days_to_request_extension, presence: true, if: -> { can_request_extension? }
|
66
|
-
validates :days_to_request_extension, numericality: { greater_than: 0, allow_nil: true }
|
67
|
-
|
68
|
-
before_destroy do
|
69
|
-
if (count = cpd_audits.length) > 0
|
70
|
-
raise("#{count} audits belong to this audit level")
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def to_s
|
75
|
-
title.presence || 'audit level'
|
76
|
-
end
|
77
|
-
|
78
|
-
def determinations
|
79
|
-
Array(self[:determinations]) - [nil, '']
|
80
|
-
end
|
81
|
-
|
82
|
-
def recommendations
|
83
|
-
Array(self[:recommendations]) - [nil, '']
|
84
|
-
end
|
85
|
-
|
3
|
+
effective_cpd_audit_level
|
86
4
|
end
|
87
5
|
end
|
@@ -25,6 +25,7 @@ module Effective
|
|
25
25
|
end
|
26
26
|
|
27
27
|
scope :submitted, -> { where(cpd_audit: Effective::CpdAudit.where.not(submitted_at: nil)) }
|
28
|
+
scope :sorted, -> { order(:id) }
|
28
29
|
scope :deep, -> { includes(:cpd_audit, :cpd_audit_level_question) }
|
29
30
|
|
30
31
|
before_validation(if: -> { cpd_audit_level_question.present? }) do
|
@@ -1,228 +1,5 @@
|
|
1
1
|
module Effective
|
2
2
|
class CpdAuditReview < ActiveRecord::Base
|
3
|
-
|
4
|
-
attr_accessor :current_step
|
5
|
-
|
6
|
-
belongs_to :cpd_audit
|
7
|
-
belongs_to :cpd_audit_level
|
8
|
-
belongs_to :user, polymorphic: true # Auditor
|
9
|
-
|
10
|
-
has_many :cpd_audit_review_items, -> { CpdAuditReviewItem.sorted }, inverse_of: :cpd_audit_review, dependent: :destroy
|
11
|
-
accepts_nested_attributes_for :cpd_audit_review_items, reject_if: :all_blank, allow_destroy: true
|
12
|
-
|
13
|
-
if respond_to?(:log_changes)
|
14
|
-
log_changes(to: :cpd_audit, except: :wizard_steps)
|
15
|
-
end
|
16
|
-
|
17
|
-
acts_as_email_form
|
18
|
-
acts_as_tokened
|
19
|
-
acts_as_reportable if respond_to?(:acts_as_reportable)
|
20
|
-
|
21
|
-
acts_as_wizard(
|
22
|
-
start: 'Start',
|
23
|
-
information: 'Information',
|
24
|
-
instructions: 'Instructions',
|
25
|
-
|
26
|
-
# Optional based on cpd_audit_level options
|
27
|
-
conflict: 'Conflict of Interest',
|
28
|
-
|
29
|
-
waiting: 'Waiting on Auditee Submission',
|
30
|
-
|
31
|
-
statements: 'Review CPD Statements',
|
32
|
-
# ... There will be one step per cpd_statement here. "statement1"
|
33
|
-
|
34
|
-
questionnaire: 'Review Questionnaire Responses',
|
35
|
-
# ... There will be one step per cpd_audit_level_sections here
|
36
|
-
|
37
|
-
recommendation: 'Recommendation',
|
38
|
-
submit: 'Confirm & Submit',
|
39
|
-
complete: 'Complete'
|
40
|
-
)
|
41
|
-
|
42
|
-
effective_resource do
|
43
|
-
token :string
|
44
|
-
due_date :date
|
45
|
-
|
46
|
-
# Auditor response
|
47
|
-
conflict_of_interest :boolean
|
48
|
-
conflict_of_interest_reason :text
|
49
|
-
|
50
|
-
# Rolling comments
|
51
|
-
comments :text
|
52
|
-
|
53
|
-
# Recommendation Step
|
54
|
-
recommendation :string
|
55
|
-
|
56
|
-
# Status Dates
|
57
|
-
submitted_at :datetime
|
58
|
-
|
59
|
-
# acts_as_statused
|
60
|
-
# I'm not using acts_as_statused yet, but I probably will later
|
61
|
-
status :string
|
62
|
-
status_steps :text
|
63
|
-
|
64
|
-
# Wizard Progress
|
65
|
-
wizard_steps :text
|
66
|
-
|
67
|
-
timestamps
|
68
|
-
end
|
69
|
-
|
70
|
-
scope :deep, -> { includes(:cpd_audit, :cpd_audit_level, :user) }
|
71
|
-
scope :sorted, -> { order(:id) }
|
72
|
-
|
73
|
-
scope :available, -> { where(submitted_at: nil) }
|
74
|
-
scope :completed, -> { where.not(submitted_at: nil) }
|
75
|
-
|
76
|
-
# effective_reports
|
77
|
-
def reportable_scopes
|
78
|
-
{ available: nil, completed: nil }
|
79
|
-
end
|
80
|
-
|
81
|
-
before_validation(if: -> { new_record? }) do
|
82
|
-
self.cpd_audit_level ||= cpd_audit&.cpd_audit_level
|
83
|
-
self.due_date ||= deadline_to_review()
|
84
|
-
end
|
85
|
-
|
86
|
-
validate(if: -> { recommendation.present? }) do
|
87
|
-
unless cpd_audit_level.recommendations.include?(recommendation)
|
88
|
-
self.errors.add(:recommendation, 'must exist in this audit level')
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
after_commit(on: :create) { send_email(:cpd_audit_review_opened) }
|
93
|
-
after_commit(on: :destroy) { cpd_audit.review! }
|
94
|
-
|
95
|
-
def to_s
|
96
|
-
'audit review'
|
97
|
-
end
|
98
|
-
|
99
|
-
def to_s
|
100
|
-
persisted? ? "#{cpd_audit_level} Audit Review by #{user}" : 'audit review'
|
101
|
-
end
|
102
|
-
|
103
|
-
# Find or build
|
104
|
-
def cpd_audit_review_item(item)
|
105
|
-
unless item.kind_of?(CpdAuditResponse) || item.kind_of?(CpdStatementActivity)
|
106
|
-
raise("expected a cpd_audit_response or cpd_statement_activity")
|
107
|
-
end
|
108
|
-
|
109
|
-
cpd_audit_review_item = cpd_audit_review_items.find { |cari| cari.item == item }
|
110
|
-
cpd_audit_review_item ||= cpd_audit_review_items.build(item: item)
|
111
|
-
end
|
112
|
-
|
113
|
-
# The dynamic CPD Statement review steps
|
114
|
-
def auditee_cpd_statements
|
115
|
-
cpd_audit.user.cpd_statements.select do |cpd_statement|
|
116
|
-
cpd_statement.completed? && (submitted_at.blank? || cpd_statement.submitted_at < submitted_at)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def cpd_statement(wizard_step)
|
121
|
-
cpd_cycle_id = (wizard_step.to_s.split('statement').last.to_i rescue false)
|
122
|
-
auditee_cpd_statements.find { |cpd_statement| cpd_statement.cpd_cycle_id == cpd_cycle_id }
|
123
|
-
end
|
124
|
-
|
125
|
-
def dynamic_wizard_statement_steps
|
126
|
-
@statement_steps ||= auditee_cpd_statements.each_with_object({}) do |cpd_statement, h|
|
127
|
-
h["statement#{cpd_statement.cpd_cycle_id}".to_sym] = "#{cpd_statement.cpd_cycle.to_s} Activities"
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
# The dynamic CPD Audit Level Sections steps
|
132
|
-
def cpd_audit_level_section(wizard_step)
|
133
|
-
position = (wizard_step.to_s.split('section').last.to_i rescue false)
|
134
|
-
cpd_audit_level.cpd_audit_level_sections.find { |section| (section.position + 1) == position }
|
135
|
-
end
|
136
|
-
|
137
|
-
def dynamic_wizard_questionnaire_steps
|
138
|
-
@questionnaire_steps ||= cpd_audit_level.cpd_audit_level_sections.each_with_object({}) do |section, h|
|
139
|
-
h["section#{section.position+1}".to_sym] = section.title
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
def dynamic_wizard_steps
|
144
|
-
dynamic_wizard_statement_steps.merge(dynamic_wizard_questionnaire_steps)
|
145
|
-
end
|
146
|
-
|
147
|
-
def can_visit_step?(step)
|
148
|
-
return (step == :complete) if completed? # Can only view complete step once submitted
|
149
|
-
can_revisit_completed_steps(step)
|
150
|
-
end
|
151
|
-
|
152
|
-
def required_steps
|
153
|
-
steps = [:start, :information, :instructions]
|
154
|
-
|
155
|
-
steps << :conflict if cpd_audit_level.conflict_of_interest?
|
156
|
-
|
157
|
-
if conflict_of_interest?
|
158
|
-
return steps + [:submit, :complete]
|
159
|
-
end
|
160
|
-
|
161
|
-
steps += [:waiting] unless ready?
|
162
|
-
|
163
|
-
steps += [:statements] + dynamic_wizard_statement_steps.keys
|
164
|
-
steps += [:questionnaire] + dynamic_wizard_questionnaire_steps.keys
|
165
|
-
steps += [:recommendation, :submit, :complete]
|
166
|
-
|
167
|
-
steps
|
168
|
-
end
|
169
|
-
|
170
|
-
def wizard_step_title(step)
|
171
|
-
WIZARD_STEPS[step] || dynamic_wizard_steps.fetch(step)
|
172
|
-
end
|
173
|
-
|
174
|
-
# Called by CpdAudit.extension_granted
|
175
|
-
def extension_granted!
|
176
|
-
self.due_date = deadline_to_review()
|
177
|
-
end
|
178
|
-
|
179
|
-
# Called by CpdAudit.submit!
|
180
|
-
def ready!
|
181
|
-
send_email(:cpd_audit_review_ready)
|
182
|
-
end
|
183
|
-
|
184
|
-
# Called by review wizard submit step
|
185
|
-
def submit!
|
186
|
-
update!(submitted_at: Time.zone.now)
|
187
|
-
cpd_audit.review! # maybe go from submitted->removed
|
188
|
-
|
189
|
-
send_email(:cpd_audit_review_submitted)
|
190
|
-
end
|
191
|
-
|
192
|
-
# When ready, the applicant review wizard hides the waiting step
|
193
|
-
def ready?
|
194
|
-
cpd_audit&.was_submitted?
|
195
|
-
end
|
196
|
-
|
197
|
-
def in_progress?
|
198
|
-
submitted_at.blank?
|
199
|
-
end
|
200
|
-
|
201
|
-
def completed?
|
202
|
-
submitted_at.present?
|
203
|
-
end
|
204
|
-
|
205
|
-
def email_form_defaults(action)
|
206
|
-
{ from: EffectiveCpd.mailer_sender }
|
207
|
-
end
|
208
|
-
|
209
|
-
def send_email(email)
|
210
|
-
EffectiveCpd.send_email(email, self, email_form_params) unless email_form_skip?
|
211
|
-
true
|
212
|
-
end
|
213
|
-
|
214
|
-
def deadline_to_conflict_of_interest
|
215
|
-
cpd_audit.deadline_to_conflict_of_interest
|
216
|
-
end
|
217
|
-
|
218
|
-
def deadline_to_review
|
219
|
-
return nil unless cpd_audit_level&.days_to_review.present?
|
220
|
-
|
221
|
-
date = cpd_audit.deadline_to_submit
|
222
|
-
return nil unless date.present?
|
223
|
-
|
224
|
-
EffectiveResources.advance_date(date, business_days: cpd_audit_level.days_to_review)
|
225
|
-
end
|
226
|
-
|
3
|
+
effective_cpd_audit_review
|
227
4
|
end
|
228
5
|
end
|
data/config/effective_cpd.rb
CHANGED
@@ -32,6 +32,9 @@ EffectiveCpd.setup do |config|
|
|
32
32
|
# CPD Class Settings
|
33
33
|
# Configure the class responsible for the cpd statements.
|
34
34
|
# config.cpd_statement_class_name = 'Effective::CpdStatement'
|
35
|
+
# config.cpd_audit_class_name = 'Effective::CpdAudit'
|
36
|
+
# config.cpd_audit_level_class_name = 'Effective::CpdAuditLevel'
|
37
|
+
# config.cpd_audit_review_class_name = 'Effective::CpdAuditReview'
|
35
38
|
|
36
39
|
# Program label settings
|
37
40
|
config.program_label = 'CPD' # CPD or MCE
|
@@ -120,6 +120,8 @@ class CreateEffectiveCpd < ActiveRecord::Migration[6.0]
|
|
120
120
|
|
121
121
|
create_table <%= @cpd_audit_levels_table_name %> do |t|
|
122
122
|
t.string :title
|
123
|
+
t.boolean :anonymous, default: false
|
124
|
+
|
123
125
|
t.text :determinations
|
124
126
|
t.text :recommendations
|
125
127
|
|
@@ -172,13 +174,19 @@ class CreateEffectiveCpd < ActiveRecord::Migration[6.0]
|
|
172
174
|
end
|
173
175
|
|
174
176
|
create_table <%= @cpd_audits_table_name %> do |t|
|
175
|
-
t.
|
177
|
+
t.intger :cpd_audit_level_id
|
178
|
+
t.string :cpd_audit_level_type
|
176
179
|
|
177
180
|
t.integer :user_id
|
178
181
|
t.string :user_type
|
179
182
|
|
180
183
|
t.date :due_date
|
181
184
|
|
185
|
+
t.string :anonymous_name
|
186
|
+
t.string :selection
|
187
|
+
t.string :region
|
188
|
+
t.text :notes
|
189
|
+
|
182
190
|
t.date :notification_date
|
183
191
|
t.date :extension_date
|
184
192
|
|
@@ -213,8 +221,11 @@ class CreateEffectiveCpd < ActiveRecord::Migration[6.0]
|
|
213
221
|
end
|
214
222
|
|
215
223
|
create_table <%= @cpd_audit_reviews_table_name %> do |t|
|
216
|
-
t.
|
217
|
-
t.
|
224
|
+
t.integer :cpd_audit_level_id
|
225
|
+
t.string :cpd_audit_level_type
|
226
|
+
|
227
|
+
t.integer :cpd_audit_id
|
228
|
+
t.string :cpd_audit_type
|
218
229
|
|
219
230
|
t.integer :user_id
|
220
231
|
t.string :user_type
|
data/lib/effective_cpd/engine.rb
CHANGED
@@ -11,7 +11,11 @@ module EffectiveCpd
|
|
11
11
|
initializer 'effective_cpd.active_record' do |app|
|
12
12
|
ActiveSupport.on_load :active_record do
|
13
13
|
ActiveRecord::Base.extend(EffectiveCpdUser::Base)
|
14
|
+
#ActiveRecord::Base.extend(EffectiveCpdCycle::Base)
|
14
15
|
ActiveRecord::Base.extend(EffectiveCpdStatement::Base)
|
16
|
+
ActiveRecord::Base.extend(EffectiveCpdAudit::Base)
|
17
|
+
ActiveRecord::Base.extend(EffectiveCpdAuditLevel::Base)
|
18
|
+
ActiveRecord::Base.extend(EffectiveCpdAuditReview::Base)
|
15
19
|
end
|
16
20
|
end
|
17
21
|
|
data/lib/effective_cpd.rb
CHANGED
@@ -16,7 +16,7 @@ module EffectiveCpd
|
|
16
16
|
:cpd_audit_reviews_table_name, :cpd_audit_review_items_table_name,
|
17
17
|
:program_label, :cycle_label, :credit_label, :layout, :auditee_user_scope, :audit_reviewer_user_scope,
|
18
18
|
:mailer, :parent_mailer, :deliver_method, :mailer_layout, :mailer_sender, :mailer_admin, :mailer_subject, :use_effective_email_templates,
|
19
|
-
:cpd_statement_class_name
|
19
|
+
:cpd_statement_class_name, :cpd_audit_class_name, :cpd_audit_level_class_name, :cpd_audit_review_class_name
|
20
20
|
]
|
21
21
|
end
|
22
22
|
|
@@ -30,4 +30,16 @@ module EffectiveCpd
|
|
30
30
|
cpd_statement_class_name&.constantize || Effective::CpdStatement
|
31
31
|
end
|
32
32
|
|
33
|
+
def self.CpdAudit
|
34
|
+
cpd_audit_class_name&.constantize || Effective::CpdAudit
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.CpdAuditLevel
|
38
|
+
cpd_audit_level_class_name&.constantize || Effective::CpdAuditLevel
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.CpdAuditReview
|
42
|
+
cpd_audit_review_class_name&.constantize || Effective::CpdAuditReview
|
43
|
+
end
|
44
|
+
|
33
45
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: effective_cpd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Code and Effect
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-01-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -216,6 +216,9 @@ files:
|
|
216
216
|
- app/helpers/effective_cpd_audits_helper.rb
|
217
217
|
- app/helpers/effective_cpd_helper.rb
|
218
218
|
- app/mailers/effective/cpd_mailer.rb
|
219
|
+
- app/models/concerns/effective_cpd_audit.rb
|
220
|
+
- app/models/concerns/effective_cpd_audit_level.rb
|
221
|
+
- app/models/concerns/effective_cpd_audit_review.rb
|
219
222
|
- app/models/concerns/effective_cpd_statement.rb
|
220
223
|
- app/models/concerns/effective_cpd_user.rb
|
221
224
|
- app/models/effective/cpd_activity.rb
|