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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b9ef412f128415399ade394ba5ee59d6799d9f13a600904bccfc6c88aa80fd6
|
4
|
+
data.tar.gz: 3c193fe0263c126908e9e2029ea303cfc9842b41d80de7e0bd29761afd97d870
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8f785d86dedc5821fb7118a7e8ea2e7c5baedc29abcd470e0c420c8e1352e51aaa4bcd364a0822e75d013778fcfdd21ad20ab5337ee55223f12cda03bf7bcee
|
7
|
+
data.tar.gz: 8a817518db9ab7b7a7104f3d6eb4991e9422bce880a4d5817bf9dd6e8917a6fd4070f96fdae059bd978bb6b6f6888faf781e0ee8c84ea959fb39ed9be6d0c129
|
@@ -5,8 +5,14 @@ module Admin
|
|
5
5
|
|
6
6
|
include Effective::CrudController
|
7
7
|
|
8
|
+
resource_scope -> { EffectiveCpd.AuditLevel.deep.all }
|
9
|
+
datatable -> { Admin::EffectiveCpdAuditLevelsDatatable.new }
|
10
|
+
|
11
|
+
private
|
12
|
+
|
8
13
|
def permitted_params
|
9
|
-
params.
|
14
|
+
model = (params.key?(:effective_cpd_audit_level) ? :effective_cpd_audit_level : :cpd_audit_level)
|
15
|
+
params.require(model).permit!
|
10
16
|
end
|
11
17
|
|
12
18
|
end
|
@@ -5,8 +5,14 @@ module Admin
|
|
5
5
|
|
6
6
|
include Effective::CrudController
|
7
7
|
|
8
|
+
resource_scope -> { EffectiveCpd.AuditReview.deep.all }
|
9
|
+
datatable -> { Admin::EffectiveCpdAuditReviewsDatatable.new }
|
10
|
+
|
11
|
+
private
|
12
|
+
|
8
13
|
def permitted_params
|
9
|
-
params.
|
14
|
+
model = (params.key?(:effective_cpd_audit_review) ? :effective_cpd_audit_review : :cpd_audit_review)
|
15
|
+
params.require(model).permit!
|
10
16
|
end
|
11
17
|
|
12
18
|
end
|
@@ -5,6 +5,9 @@ module Admin
|
|
5
5
|
|
6
6
|
include Effective::CrudController
|
7
7
|
|
8
|
+
resource_scope -> { EffectiveCpd.CpdAudit.deep.all }
|
9
|
+
datatable -> { Admin::EffectiveCpdAuditsDatatable.new }
|
10
|
+
|
8
11
|
submit :resolve_conflict, 'Resolve Conflict of Interest', success: -> {
|
9
12
|
[
|
10
13
|
"Successfully resolved #{resource}",
|
@@ -33,8 +36,11 @@ module Admin
|
|
33
36
|
].compact.join(' ')
|
34
37
|
}
|
35
38
|
|
39
|
+
private
|
40
|
+
|
36
41
|
def permitted_params
|
37
|
-
params.
|
42
|
+
model = (params.key?(:effective_cpd_audit) ? :effective_cpd_audit : :cpd_audit)
|
43
|
+
params.require(model).permit!
|
38
44
|
end
|
39
45
|
|
40
46
|
end
|
@@ -14,6 +14,15 @@ module Effective
|
|
14
14
|
CpdScorer.new(user: resource.user).score!
|
15
15
|
end
|
16
16
|
|
17
|
+
# Enforce one statement per user per cycle. Redirect them to an existing statement for this cycle.
|
18
|
+
before_action(only: [:new, :show]) do
|
19
|
+
existing = resource_scope.where.not(id: resource).first
|
20
|
+
|
21
|
+
if existing.present?
|
22
|
+
redirect_to effective_cpd.cpd_cycle_cpd_statement_build_path(existing.cpd_cycle, existing, existing.next_step)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
17
26
|
# Enforce cycle availability
|
18
27
|
before_action(only: [:show, :update]) do
|
19
28
|
cycle = resource.cpd_cycle
|
@@ -0,0 +1,425 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# EffectiveCpdAudit
|
4
|
+
#
|
5
|
+
# Mark your owner model with effective_cpd_audit to get all the includes
|
6
|
+
|
7
|
+
module EffectiveCpdAudit
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
module Base
|
11
|
+
def effective_cpd_audit
|
12
|
+
include ::EffectiveCpdAudit
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def effective_cpd_audit?; true; end
|
18
|
+
end
|
19
|
+
|
20
|
+
included do
|
21
|
+
acts_as_email_form
|
22
|
+
acts_as_tokened
|
23
|
+
acts_as_reportable if respond_to?(:acts_as_reportable)
|
24
|
+
log_changes(except: [:wizard_steps, :cpd_audit_reviews]) if respond_to?(:log_changes)
|
25
|
+
|
26
|
+
acts_as_statused(
|
27
|
+
:opened, # Just Opened
|
28
|
+
:started, # First screen clicked
|
29
|
+
:conflicted, # Auditee has declared a conflict of interest
|
30
|
+
:conflicted_resolved, # The conflict of interest has been resolved
|
31
|
+
:exemption_requested, # Auditee has requested an exemption
|
32
|
+
:exemption_granted, # Exemption granted -> Audit is cancelled. Exit state.
|
33
|
+
:exemption_denied, # Exemption denied
|
34
|
+
:extension_requested, # Audittee has requested an extension
|
35
|
+
:extension_granted, # Extension granted
|
36
|
+
:extension_denied, # Extension denied
|
37
|
+
:submitted, # Audittee has completed questionnaire submitted. Audittee is done.
|
38
|
+
:reviewed, # All audit reviews completed. Ready for a determination.
|
39
|
+
:closed # Determination made by admin and/or audit committee. Exit state. All done.
|
40
|
+
)
|
41
|
+
|
42
|
+
acts_as_wizard(
|
43
|
+
start: 'Start',
|
44
|
+
information: 'Information',
|
45
|
+
instructions: 'Instructions',
|
46
|
+
|
47
|
+
# These 4 steps are determined by audit_level settings
|
48
|
+
conflict: 'Conflict of Interest',
|
49
|
+
exemption: 'Request Exemption',
|
50
|
+
extension: 'Request Extension',
|
51
|
+
waiting: 'Waiting',
|
52
|
+
cpd: 'CPD',
|
53
|
+
|
54
|
+
questionnaire: 'Questionnaire',
|
55
|
+
# ... There will be one step per cpd_audit_level_sections here
|
56
|
+
files: 'Upload Resume',
|
57
|
+
|
58
|
+
submit: 'Confirm & Submit',
|
59
|
+
complete: 'Complete'
|
60
|
+
)
|
61
|
+
|
62
|
+
attr_accessor :current_user
|
63
|
+
attr_accessor :current_step
|
64
|
+
attr_accessor :admin_process_request
|
65
|
+
|
66
|
+
# App scoped
|
67
|
+
belongs_to :cpd_audit_level, polymorphic: true
|
68
|
+
belongs_to :user, polymorphic: true # The user being audited
|
69
|
+
|
70
|
+
has_many :cpd_audit_reviews, -> { order(:id) }, inverse_of: :cpd_audit, dependent: :destroy
|
71
|
+
accepts_nested_attributes_for :cpd_audit_reviews, allow_destroy: true
|
72
|
+
|
73
|
+
# Effective Scoped
|
74
|
+
has_many :cpd_audit_responses, -> { Effective::CpdAuditResponse.sorted }, class_name: 'Effective::CpdAuditResponse', inverse_of: :cpd_audit, dependent: :destroy
|
75
|
+
accepts_nested_attributes_for :cpd_audit_responses
|
76
|
+
|
77
|
+
has_many_attached :files
|
78
|
+
|
79
|
+
ADMIN_PROCESS_REQUEST_OPTIONS = ['Granted', 'Denied']
|
80
|
+
COMPLETED_STATES = [:exemption_granted, :closed]
|
81
|
+
WAITING_ON_ADMIN_STATES = [:conflicted, :exemption_requested, :extension_requested, :reviewed]
|
82
|
+
WAITING_ON_REVIEWERS_STATES = [:submitted]
|
83
|
+
WAITING_ON_AUDITEE_STATES = [:opened, :started, :conflicted_resolved, :exemption_denied, :extension_granted, :extension_denied]
|
84
|
+
|
85
|
+
effective_resource do
|
86
|
+
due_date :date # Computed due date based on notification and extension date
|
87
|
+
|
88
|
+
selection :string
|
89
|
+
region :string
|
90
|
+
notes :text
|
91
|
+
|
92
|
+
# Anonymous Name
|
93
|
+
anonymous_name :string # Required when cpd_audit_level.anonymous?
|
94
|
+
|
95
|
+
# Important dates
|
96
|
+
notification_date :date # Can be set on CpdAudits#new, but basically created_at
|
97
|
+
extension_date :date # set by admin if extension if granted
|
98
|
+
|
99
|
+
# Final determination
|
100
|
+
determination :string
|
101
|
+
|
102
|
+
# Override Deadlines
|
103
|
+
ignore_deadlines :boolean
|
104
|
+
|
105
|
+
# Auditee response
|
106
|
+
conflict_of_interest :boolean
|
107
|
+
conflict_of_interest_reason :text
|
108
|
+
|
109
|
+
exemption_request :boolean
|
110
|
+
exemption_request_reason :text
|
111
|
+
|
112
|
+
extension_request :boolean
|
113
|
+
extension_request_date :date
|
114
|
+
extension_request_reason :text
|
115
|
+
|
116
|
+
# acts_as_statused
|
117
|
+
status :string
|
118
|
+
status_steps :text
|
119
|
+
|
120
|
+
# Status dates
|
121
|
+
started_at :datetime
|
122
|
+
submitted_at :datetime
|
123
|
+
reviewed_at :datetime
|
124
|
+
closed_at :datetime
|
125
|
+
|
126
|
+
# Acts as tokened
|
127
|
+
token :string
|
128
|
+
|
129
|
+
# Acts as Wizard
|
130
|
+
wizard_steps :text
|
131
|
+
|
132
|
+
timestamps
|
133
|
+
end
|
134
|
+
|
135
|
+
scope :deep, -> { includes(:cpd_audit_level, user: [:cpd_statements], cpd_audit_reviews: [:cpd_audit_level, :user, :cpd_audit_review_items]) }
|
136
|
+
scope :sorted, -> { order(:id) }
|
137
|
+
|
138
|
+
scope :draft, -> { where(submitted_at: nil) }
|
139
|
+
scope :available, -> { where.not(status: COMPLETED_STATES) }
|
140
|
+
scope :completed, -> { where(status: COMPLETED_STATES) }
|
141
|
+
|
142
|
+
scope :waiting_on_admin, -> { where(status: WAITING_ON_ADMIN_STATES) }
|
143
|
+
scope :waiting_on_auditee, -> { where(status: WAITING_ON_AUDITEE_STATES) }
|
144
|
+
scope :waiting_on_reviewers, -> { where(status: WAITING_ON_REVIEWERS_STATES) }
|
145
|
+
|
146
|
+
# effective_reports
|
147
|
+
def reportable_scopes
|
148
|
+
{ draft: nil, available: nil, completed: nil, waiting_on_admin: nil, waiting_on_auditee: nil, waiting_on_reviewers: nil }
|
149
|
+
end
|
150
|
+
|
151
|
+
before_validation(if: -> { new_record? }) do
|
152
|
+
self.notification_date ||= Time.zone.now
|
153
|
+
self.due_date ||= deadline_to_submit()
|
154
|
+
end
|
155
|
+
|
156
|
+
validates :anonymous_name, presence: true, if: -> { cpd_audit_level&.anonymous? }
|
157
|
+
|
158
|
+
validates :notification_date, presence: true
|
159
|
+
validates :determination, presence: true, if: -> { closed? }
|
160
|
+
|
161
|
+
validates :conflict_of_interest_reason, presence: true, if: -> { conflict_of_interest? }
|
162
|
+
validates :exemption_request_reason, presence: true, if: -> { exemption_request? }
|
163
|
+
validates :extension_request_date, presence: true, if: -> { extension_request? }
|
164
|
+
validates :extension_request_reason, presence: true, if: -> { extension_request? }
|
165
|
+
|
166
|
+
validate(if: -> { current_step == :conflict && conflict_of_interest? && !ignore_deadlines? }) do
|
167
|
+
deadline = deadline_to_conflict_of_interest()
|
168
|
+
self.errors.add(:base, 'deadline to declare conflict of interest has already passed') if deadline && deadline < Time.zone.now
|
169
|
+
end
|
170
|
+
|
171
|
+
validate(if: -> { current_step == :exemption && exemption_request? && !ignore_deadlines? }) do
|
172
|
+
deadline = deadline_to_exemption()
|
173
|
+
self.errors.add(:base, 'deadline to request exemption has already passed') if deadline && deadline < Time.zone.now
|
174
|
+
end
|
175
|
+
|
176
|
+
validate(if: -> { current_step == :extension && extension_request? && !ignore_deadlines? }) do
|
177
|
+
deadline = deadline_to_extension()
|
178
|
+
self.errors.add(:base, 'deadline to request extension has already passed') if deadline && deadline < Time.zone.now
|
179
|
+
end
|
180
|
+
|
181
|
+
validate(if: -> { determination.present? }) do
|
182
|
+
unless cpd_audit_level.determinations.include?(determination)
|
183
|
+
self.errors.add(:determination, 'must exist in this audit level')
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# If we're submitted. Check if we can go into reviewed?
|
188
|
+
before_save(if: -> { submitted? }) { review! }
|
189
|
+
|
190
|
+
after_commit(on: :create) do
|
191
|
+
send_email(:cpd_audit_opened)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def to_s
|
196
|
+
persisted? ? "#{cpd_audit_level} Audit of #{user}" : 'audit'
|
197
|
+
end
|
198
|
+
|
199
|
+
def dynamic_wizard_steps
|
200
|
+
cpd_audit_level.cpd_audit_level_sections.each_with_object({}) do |section, h|
|
201
|
+
h["section#{section.position+1}".to_sym] = section.title
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def can_visit_step?(step)
|
206
|
+
return (step == :complete) if was_submitted? # Can only view complete step once submitted
|
207
|
+
can_revisit_completed_steps(step)
|
208
|
+
end
|
209
|
+
|
210
|
+
def required_steps
|
211
|
+
steps = [:start, :information, :instructions]
|
212
|
+
|
213
|
+
steps << :conflict if cpd_audit_level.conflict_of_interest?
|
214
|
+
|
215
|
+
steps << :exemption if cpd_audit_level.can_request_exemption?
|
216
|
+
|
217
|
+
unless exemption_requested?
|
218
|
+
steps << :extension if cpd_audit_level.can_request_extension?
|
219
|
+
end
|
220
|
+
|
221
|
+
if exemption_requested? || extension_requested?
|
222
|
+
steps += [:waiting]
|
223
|
+
end
|
224
|
+
|
225
|
+
steps += [:cpd, :questionnaire] + dynamic_wizard_steps.keys + [:files, :submit, :complete]
|
226
|
+
|
227
|
+
steps
|
228
|
+
end
|
229
|
+
|
230
|
+
def wizard_step_title(step)
|
231
|
+
WIZARD_STEPS[step] || dynamic_wizard_steps.fetch(step)
|
232
|
+
end
|
233
|
+
|
234
|
+
def deadline_date
|
235
|
+
(extension_date || notification_date)
|
236
|
+
end
|
237
|
+
|
238
|
+
def completed?
|
239
|
+
COMPLETED_STATES.include?(status.to_sym)
|
240
|
+
end
|
241
|
+
|
242
|
+
def in_progress?
|
243
|
+
COMPLETED_STATES.include?(status.to_sym) == false
|
244
|
+
end
|
245
|
+
|
246
|
+
def cpd_audit_level_section(wizard_step)
|
247
|
+
position = (wizard_step.to_s.split('section').last.to_i rescue false)
|
248
|
+
cpd_audit_level.cpd_audit_level_sections.find { |section| (section.position + 1) == position }
|
249
|
+
end
|
250
|
+
|
251
|
+
# Find or build
|
252
|
+
def cpd_audit_response(cpd_audit_level_question)
|
253
|
+
cpd_audit_response = cpd_audit_responses.find { |r| r.cpd_audit_level_question_id == cpd_audit_level_question.id }
|
254
|
+
cpd_audit_response ||= cpd_audit_responses.build(cpd_audit: self, cpd_audit_level_question: cpd_audit_level_question)
|
255
|
+
end
|
256
|
+
|
257
|
+
# Auditee wizard action
|
258
|
+
def start!
|
259
|
+
started!
|
260
|
+
end
|
261
|
+
|
262
|
+
# Admin action
|
263
|
+
def resolve_conflict!
|
264
|
+
wizard_steps[:conflict] = nil # Have them complete the conflict step again.
|
265
|
+
|
266
|
+
assign_attributes(conflict_of_interest: false, conflict_of_interest_reason: nil)
|
267
|
+
conflicted_resolved!
|
268
|
+
submitted!
|
269
|
+
|
270
|
+
send_email(:cpd_audit_conflict_resolved)
|
271
|
+
true
|
272
|
+
end
|
273
|
+
|
274
|
+
# Auditee wizard action
|
275
|
+
def exemption!
|
276
|
+
return started! unless exemption_request?
|
277
|
+
|
278
|
+
update!(status: :exemption_requested)
|
279
|
+
send_email(:cpd_audit_exemption_request)
|
280
|
+
end
|
281
|
+
|
282
|
+
# Admin action
|
283
|
+
def process_exemption!
|
284
|
+
case admin_process_request
|
285
|
+
when 'Granted' then grant_exemption!
|
286
|
+
when 'Denied' then deny_exemption!
|
287
|
+
else
|
288
|
+
self.errors.add(:admin_process_request, "can't be blank"); save!
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def grant_exemption!
|
293
|
+
wizard_steps[:submit] ||= Time.zone.now
|
294
|
+
submitted! && exemption_granted!
|
295
|
+
send_email(:cpd_audit_exemption_granted)
|
296
|
+
end
|
297
|
+
|
298
|
+
def deny_exemption!
|
299
|
+
assign_attributes(exemption_request: false)
|
300
|
+
exemption_denied!
|
301
|
+
send_email(:cpd_audit_exemption_denied)
|
302
|
+
end
|
303
|
+
|
304
|
+
# Auditee wizard action
|
305
|
+
def extension!
|
306
|
+
return started! unless extension_request?
|
307
|
+
|
308
|
+
update!(status: :extension_requested)
|
309
|
+
send_email(:cpd_audit_extension_request)
|
310
|
+
end
|
311
|
+
|
312
|
+
# Admin action
|
313
|
+
def process_extension!
|
314
|
+
case admin_process_request
|
315
|
+
when 'Granted' then grant_extension!
|
316
|
+
when 'Denied' then deny_extension!
|
317
|
+
else
|
318
|
+
self.errors.add(:admin_process_request, "can't be blank"); save!
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
def grant_extension!
|
323
|
+
self.extension_date = extension_request_date
|
324
|
+
self.due_date = deadline_to_submit()
|
325
|
+
|
326
|
+
cpd_audit_reviews.each { |cpd_audit_review| cpd_audit_review.extension_granted! }
|
327
|
+
extension_granted!
|
328
|
+
send_email(:cpd_audit_extension_granted)
|
329
|
+
end
|
330
|
+
|
331
|
+
def deny_extension!
|
332
|
+
assign_attributes(extension_request: false)
|
333
|
+
extension_denied!
|
334
|
+
send_email(:cpd_audit_extension_denied)
|
335
|
+
end
|
336
|
+
|
337
|
+
# Require CPD step
|
338
|
+
def user_cpd_required?
|
339
|
+
return false unless user.cpd_audit_cpd_required?
|
340
|
+
required_cpd_cycle.present?
|
341
|
+
end
|
342
|
+
|
343
|
+
def user_cpd_completed?
|
344
|
+
return true if required_cpd_cycle.blank?
|
345
|
+
user.cpd_statements.any? { |s| s.completed? && s.cpd_cycle_id == required_cpd_cycle.id }
|
346
|
+
end
|
347
|
+
|
348
|
+
def required_cpd_cycle
|
349
|
+
@required_cpd_cycle ||= begin
|
350
|
+
last_year = ((notification_date || created_at || Time.zone.now) - 1.year).all_year
|
351
|
+
EffectiveCpd.CpdCycle.available.where(start_at: last_year).first
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
# Auditee wizard action
|
356
|
+
def submit!
|
357
|
+
if conflict_of_interest?
|
358
|
+
conflicted!
|
359
|
+
send_email(:cpd_audit_conflicted)
|
360
|
+
else
|
361
|
+
submitted!
|
362
|
+
|
363
|
+
cpd_audit_reviews.each { |cpd_audit_review| cpd_audit_review.ready! }
|
364
|
+
send_email(:cpd_audit_submitted)
|
365
|
+
end
|
366
|
+
|
367
|
+
true
|
368
|
+
end
|
369
|
+
|
370
|
+
# Called in a before_save. Intended for applicant_review to call in its submit! method
|
371
|
+
def review!
|
372
|
+
return false unless submitted?
|
373
|
+
return false unless cpd_audit_reviews.present? && cpd_audit_reviews.all?(&:completed?)
|
374
|
+
|
375
|
+
reviewed!
|
376
|
+
send_email(:cpd_audit_reviewed)
|
377
|
+
end
|
378
|
+
|
379
|
+
# Admin action
|
380
|
+
def close!
|
381
|
+
closed!
|
382
|
+
send_email(:cpd_audit_closed)
|
383
|
+
end
|
384
|
+
|
385
|
+
def email_form_defaults(action)
|
386
|
+
{ from: EffectiveCpd.mailer_sender }
|
387
|
+
end
|
388
|
+
|
389
|
+
def send_email(email)
|
390
|
+
EffectiveCpd.send_email(email, self, email_form_params) unless email_form_skip?
|
391
|
+
true
|
392
|
+
end
|
393
|
+
|
394
|
+
def deadline_to_conflict_of_interest
|
395
|
+
return nil unless cpd_audit_level&.conflict_of_interest?
|
396
|
+
return nil unless cpd_audit_level.days_to_declare_conflict.present?
|
397
|
+
|
398
|
+
date = (notification_date || created_at || Time.zone.now)
|
399
|
+
EffectiveResources.advance_date(date, business_days: cpd_audit_level.days_to_declare_conflict)
|
400
|
+
end
|
401
|
+
|
402
|
+
def deadline_to_exemption
|
403
|
+
return nil unless cpd_audit_level&.can_request_exemption?
|
404
|
+
return nil unless cpd_audit_level.days_to_request_exemption.present?
|
405
|
+
|
406
|
+
date = (notification_date || created_at || Time.zone.now)
|
407
|
+
EffectiveResources.advance_date(date, business_days: cpd_audit_level.days_to_request_exemption)
|
408
|
+
end
|
409
|
+
|
410
|
+
def deadline_to_extension
|
411
|
+
return nil unless cpd_audit_level&.can_request_extension?
|
412
|
+
return nil unless cpd_audit_level.days_to_request_extension.present?
|
413
|
+
|
414
|
+
date = (notification_date || created_at || Time.zone.now)
|
415
|
+
EffectiveResources.advance_date(date, business_days: cpd_audit_level.days_to_request_extension)
|
416
|
+
end
|
417
|
+
|
418
|
+
def deadline_to_submit
|
419
|
+
return nil unless cpd_audit_level&.days_to_submit.present?
|
420
|
+
|
421
|
+
date = (extension_date || notification_date || created_at || Time.zone.now)
|
422
|
+
EffectiveResources.advance_date(date, business_days: cpd_audit_level.days_to_submit)
|
423
|
+
end
|
424
|
+
|
425
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# EffectiveCpdUser
|
2
|
+
#
|
3
|
+
# Mark your user model with effective_cpd_user to get a few helpers
|
4
|
+
# And user specific point required scores
|
5
|
+
|
6
|
+
module EffectiveCpdAuditLevel
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
module Base
|
10
|
+
def effective_cpd_audit_level
|
11
|
+
include ::EffectiveCpdAuditLevel
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def effective_cpd_audit_level?; true; end
|
17
|
+
end
|
18
|
+
|
19
|
+
included do
|
20
|
+
has_many_rich_texts
|
21
|
+
|
22
|
+
# For each cpd audit and cpd audit review wizard step
|
23
|
+
# rich_text_all_steps_audit_content
|
24
|
+
# rich_text_step_audit_content
|
25
|
+
|
26
|
+
# rich_text_all_steps_audit_review_content
|
27
|
+
# rich_text_step_audit_review_content
|
28
|
+
|
29
|
+
# App scoped
|
30
|
+
has_many :cpd_audits
|
31
|
+
|
32
|
+
has_many :cpd_audit_reviews, -> { EffectiveCpd.CpdAuditReview.sorted }, inverse_of: :cpd_audit_level, dependent: :destroy
|
33
|
+
accepts_nested_attributes_for :cpd_audit_reviews, allow_destroy: true
|
34
|
+
|
35
|
+
# Effective Scoped
|
36
|
+
has_many :cpd_audit_level_sections, -> { Effective::CpdAuditLevelSection.sorted }, class_name: 'Effective::CpdAuditLevelSection', inverse_of: :cpd_audit_level, dependent: :destroy
|
37
|
+
accepts_nested_attributes_for :cpd_audit_level_sections, allow_destroy: true
|
38
|
+
|
39
|
+
has_many :cpd_audit_level_questions, -> { Effective::CpdAuditLevelQuestion.sorted }, class_name: 'Effective::CpdAuditLevelQuestion', through: :cpd_audit_level_sections
|
40
|
+
|
41
|
+
if respond_to?(:log_changes)
|
42
|
+
log_changes(except: [:cpd_audits, :cpd_audit_reviews, :cpd_audit_level_sections, :cpd_audit_level_questions])
|
43
|
+
end
|
44
|
+
|
45
|
+
effective_resource do
|
46
|
+
title :string
|
47
|
+
|
48
|
+
anonymous :boolean
|
49
|
+
|
50
|
+
determinations :text # Final determination by auditor
|
51
|
+
recommendations :text # Recommendations by audit reviewers
|
52
|
+
|
53
|
+
days_to_submit :integer # For auditee to submit statement
|
54
|
+
days_to_review :integer # For auditor/audit_review to be completed
|
55
|
+
|
56
|
+
conflict_of_interest :boolean # Feature flags
|
57
|
+
can_request_exemption :boolean
|
58
|
+
can_request_extension :boolean
|
59
|
+
|
60
|
+
days_to_declare_conflict :integer
|
61
|
+
days_to_request_exemption :integer
|
62
|
+
days_to_request_extension :integer
|
63
|
+
|
64
|
+
timestamps
|
65
|
+
end
|
66
|
+
|
67
|
+
serialize :determinations, Array
|
68
|
+
serialize :recommendations, Array
|
69
|
+
|
70
|
+
scope :deep, -> { all }
|
71
|
+
scope :sorted, -> { order(:title) }
|
72
|
+
|
73
|
+
validates :title, presence: true
|
74
|
+
validates :determinations, presence: true
|
75
|
+
validates :recommendations, presence: true
|
76
|
+
|
77
|
+
validates :days_to_submit, numericality: { greater_than: 0, allow_nil: true }
|
78
|
+
validates :days_to_review, numericality: { greater_than: 0, allow_nil: true }
|
79
|
+
|
80
|
+
validates :days_to_declare_conflict, presence: true, if: -> { conflict_of_interest? }
|
81
|
+
validates :days_to_declare_conflict, numericality: { greater_than: 0, allow_nil: true }
|
82
|
+
|
83
|
+
validates :days_to_request_exemption, presence: true, if: -> { can_request_exemption? }
|
84
|
+
validates :days_to_request_exemption, numericality: { greater_than: 0, allow_nil: true }
|
85
|
+
|
86
|
+
validates :days_to_request_extension, presence: true, if: -> { can_request_extension? }
|
87
|
+
validates :days_to_request_extension, numericality: { greater_than: 0, allow_nil: true }
|
88
|
+
|
89
|
+
before_destroy do
|
90
|
+
if (count = cpd_audits.length) > 0
|
91
|
+
raise("#{count} audits belong to this audit level")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_s
|
97
|
+
title.presence || 'audit level'
|
98
|
+
end
|
99
|
+
|
100
|
+
def determinations
|
101
|
+
Array(self[:determinations]) - [nil, '']
|
102
|
+
end
|
103
|
+
|
104
|
+
def recommendations
|
105
|
+
Array(self[:recommendations]) - [nil, '']
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|