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