effective_cpd 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/admin/cpd_audit_levels_controller.rb +2 -2
  3. data/app/controllers/admin/cpd_audit_reviews_controller.rb +2 -2
  4. data/app/controllers/admin/cpd_audits_controller.rb +15 -1
  5. data/app/controllers/admin/cpd_statements_controller.rb +1 -1
  6. data/app/controllers/effective/cpd_audit_reviews_controller.rb +3 -39
  7. data/app/controllers/effective/cpd_audits_controller.rb +3 -41
  8. data/app/datatables/admin/effective_cpd_audit_reviews_datatable.rb +3 -1
  9. data/app/datatables/admin/effective_cpd_audits_datatable.rb +11 -9
  10. data/app/datatables/effective_cpd_available_audit_reviews_datatable.rb +4 -4
  11. data/app/datatables/effective_cpd_available_audits_datatable.rb +1 -1
  12. data/app/datatables/effective_cpd_completed_audit_reviews_datatable.rb +3 -3
  13. data/app/datatables/effective_cpd_completed_audits_datatable.rb +2 -2
  14. data/app/helpers/effective_cpd_audits_helper.rb +0 -32
  15. data/app/mailers/effective/cpd_mailer.rb +37 -24
  16. data/app/models/concerns/effective_cpd_audit.rb +235 -66
  17. data/app/models/concerns/effective_cpd_audit_level.rb +7 -4
  18. data/app/models/concerns/effective_cpd_audit_review.rb +121 -65
  19. data/app/models/concerns/effective_cpd_user.rb +10 -5
  20. data/app/models/effective/cpd_audit_level_question.rb +1 -1
  21. data/app/models/effective/cpd_audit_level_section.rb +4 -3
  22. data/app/models/effective/cpd_audit_response.rb +3 -2
  23. data/app/models/effective/cpd_audit_review_item.rb +1 -1
  24. data/app/views/admin/cpd_audit_levels/_form_content_audit.html.haml +1 -1
  25. data/app/views/admin/cpd_audit_levels/_form_content_audit_review.html.haml +1 -1
  26. data/app/views/admin/cpd_audit_levels/_form_cpd_audit_level.html.haml +7 -1
  27. data/app/views/admin/cpd_audit_levels/_form_cpd_audit_level_section.html.haml +2 -0
  28. data/app/views/admin/cpd_audit_reviews/_form.html.haml +4 -1
  29. data/app/views/admin/cpd_audits/_form.html.haml +1 -49
  30. data/app/views/admin/cpd_audits/{_form_determination.html.haml → _form_close.html.haml} +1 -1
  31. data/app/views/admin/cpd_audits/_form_complete.html.haml +17 -0
  32. data/app/views/admin/cpd_audits/_form_cpd_audit.html.haml +56 -0
  33. data/app/views/admin/cpd_audits/_form_deadlines.html.haml +8 -9
  34. data/app/views/admin/cpd_audits/_form_exemption.html.haml +1 -1
  35. data/app/views/admin/cpd_audits/_form_extension.html.haml +1 -1
  36. data/app/views/admin/cpd_audits/_form_files.html.haml +6 -0
  37. data/app/views/admin/cpd_audits/_form_missing_info.html.haml +11 -0
  38. data/app/views/admin/cpd_audits/_form_new.html.haml +3 -1
  39. data/app/views/admin/cpd_audits/_form_process.html.haml +18 -0
  40. data/app/views/admin/cpd_audits/_status.html.haml +48 -6
  41. data/app/views/effective/cpd/_dashboard.html.haml +15 -12
  42. data/app/views/effective/cpd_audit_level_questions/_cpd_audit_level_question.html.haml +2 -1
  43. data/app/views/effective/cpd_audit_reviews/_conflict.html.haml +1 -1
  44. data/app/views/effective/cpd_audit_reviews/_cpd_audit_level_section.html.haml +3 -3
  45. data/app/views/effective/cpd_audit_reviews/_cpd_audit_review.html.haml +3 -1
  46. data/app/views/effective/cpd_audit_reviews/_cpd_statement.html.haml +3 -3
  47. data/app/views/effective/cpd_audit_reviews/_feedback.html.haml +9 -0
  48. data/app/views/effective/cpd_audit_reviews/_files.html.haml +12 -0
  49. data/app/views/effective/cpd_audit_reviews/_layout.html.haml +3 -0
  50. data/app/views/effective/cpd_audit_reviews/_recommendation.html.haml +6 -2
  51. data/app/views/effective/cpd_audit_reviews/_summary.html.haml +41 -14
  52. data/app/views/effective/cpd_audit_reviews/conflict.html.haml +5 -1
  53. data/app/views/effective/cpd_audit_reviews/cpd_audit_level_section.html.haml +2 -2
  54. data/app/views/effective/cpd_audit_reviews/feedback.html.haml +16 -0
  55. data/app/views/effective/cpd_audit_reviews/files.html.haml +17 -0
  56. data/app/views/effective/cpd_audit_reviews/recommendation.html.haml +5 -3
  57. data/app/views/effective/cpd_audit_reviews/start.html.haml +10 -3
  58. data/app/views/effective/cpd_audit_reviews/statements.html.haml +4 -5
  59. data/app/views/effective/cpd_audit_reviews/submit.html.haml +1 -1
  60. data/app/views/effective/cpd_audit_reviews/submitted.html.haml +20 -0
  61. data/app/views/effective/cpd_audit_reviews/waiting.html.haml +1 -1
  62. data/app/views/effective/cpd_audits/_conflict.html.haml +1 -1
  63. data/app/views/effective/cpd_audits/_cpd.html.haml +1 -1
  64. data/app/views/effective/cpd_audits/_cpd_audit.html.haml +3 -1
  65. data/app/views/effective/cpd_audits/_cpd_audit_level_section.html.haml +1 -1
  66. data/app/views/effective/cpd_audits/_exemption.html.haml +1 -1
  67. data/app/views/effective/cpd_audits/_extension.html.haml +1 -1
  68. data/app/views/effective/cpd_audits/_files.html.haml +1 -1
  69. data/app/views/effective/cpd_audits/_missing_info.html.haml +19 -0
  70. data/app/views/effective/cpd_audits/_summary.html.haml +30 -12
  71. data/app/views/effective/cpd_audits/_waiting.html.haml +1 -1
  72. data/app/views/effective/cpd_audits/conflict.html.haml +5 -1
  73. data/app/views/effective/cpd_audits/cpd_audit_level_section.html.haml +1 -0
  74. data/app/views/effective/cpd_audits/start.html.haml +10 -4
  75. data/app/views/effective/cpd_audits/submit.html.haml +5 -2
  76. data/app/views/effective/cpd_audits/{complete.html.haml → submitted.haml} +13 -8
  77. data/app/views/effective/cpd_audits/waiting.html.haml +1 -1
  78. data/app/views/effective/cpd_mailer/cpd_audit_missing_info.liquid +15 -0
  79. data/app/views/effective/cpd_mailer/cpd_audit_review_ready.liquid +1 -1
  80. data/app/views/effective/cpd_mailer/cpd_audit_submitted.liquid +3 -1
  81. data/config/effective_cpd.rb +2 -2
  82. data/db/migrate/01_create_effective_cpd.rb.erb +25 -7
  83. data/lib/effective_cpd/version.rb +1 -1
  84. metadata +16 -5
  85. data/app/views/effective/cpd_audit_reviews/complete.html.haml +0 -20
@@ -15,6 +15,27 @@ module EffectiveCpdAudit
15
15
 
16
16
  module ClassMethods
17
17
  def effective_cpd_audit?; true; end
18
+
19
+ def admin_process_request_options
20
+ ['Granted', 'Denied']
21
+ end
22
+
23
+ def done_states
24
+ [:exemption_granted, :closed]
25
+ end
26
+
27
+ def waiting_on_admin_states
28
+ [:conflicted, :exemption_requested, :extension_requested, :submitted, :reviewed]
29
+ end
30
+
31
+ def waiting_on_reviewers_states
32
+ [:completed]
33
+ end
34
+
35
+ def waiting_on_auditee_states
36
+ [:opened, :started, :conflicted_resolved, :exemption_denied, :extension_granted, :extension_denied]
37
+ end
38
+
18
39
  end
19
40
 
20
41
  included do
@@ -35,6 +56,8 @@ module EffectiveCpdAudit
35
56
  :extension_granted, # Extension granted
36
57
  :extension_denied, # Extension denied
37
58
  :submitted, # Audittee has completed questionnaire submitted. Audittee is done.
59
+ :missing_info, # Admin has indicated information is missing. The audittee can edit applicant and add info
60
+ :completed, # Admin has received all deliverables. The audit is complete and ready for review.
38
61
  :reviewed, # All audit reviews completed. Ready for a determination.
39
62
  :closed # Determination made by admin and/or audit committee. Exit state. All done.
40
63
  )
@@ -55,8 +78,8 @@ module EffectiveCpdAudit
55
78
  # ... There will be one step per cpd_audit_level_sections here
56
79
  files: 'Upload Resume',
57
80
 
58
- submit: 'Confirm & Submit',
59
- complete: 'Complete'
81
+ submit: 'Submit',
82
+ submitted: 'Submitted'
60
83
  )
61
84
 
62
85
  attr_accessor :current_user
@@ -76,31 +99,29 @@ module EffectiveCpdAudit
76
99
 
77
100
  has_many_attached :files
78
101
 
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
102
  effective_resource do
86
- due_date :date # Computed due date based on notification and extension date
103
+ due_date :date, permitted: false # Computed due date based on notification and extension date
104
+
105
+ # Additional Info. Not really used.
106
+ notes :text, permitted: false
87
107
 
88
- selection :string
89
- region :string
90
- notes :text
108
+ # Anonymous Name Mode
109
+ anonymous_name :string, permitted: false # Required when cpd_audit_level.anonymous?
110
+ anonymous_number :integer, permitted: false # A unique value
91
111
 
92
- # Anonymous Name
93
- anonymous_name :string # Required when cpd_audit_level.anonymous?
112
+ # Missing Info
113
+ missing_info_at :datetime
114
+ missing_info_reason :text
94
115
 
95
116
  # 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
117
+ notification_date :date, permitted: false # Can be set on CpdAudits#new, but basically created_at
118
+ extension_date :date, permitted: false # set by admin if extension if granted
98
119
 
99
120
  # Final determination
100
- determination :string
121
+ determination :string, permitted: false
101
122
 
102
123
  # Override Deadlines
103
- ignore_deadlines :boolean
124
+ ignore_deadlines :boolean, permitted: false
104
125
 
105
126
  # Auditee response
106
127
  conflict_of_interest :boolean
@@ -114,20 +135,21 @@ module EffectiveCpdAudit
114
135
  extension_request_reason :text
115
136
 
116
137
  # acts_as_statused
117
- status :string
138
+ status :string, permitted: false
118
139
  status_steps :text
119
140
 
120
141
  # Status dates
121
- started_at :datetime
122
- submitted_at :datetime
123
- reviewed_at :datetime
124
- closed_at :datetime
142
+ started_at :datetime, permitted: false
143
+ submitted_at :datetime, permitted: false
144
+ completed_at :datetime, permitted: false
145
+ reviewed_at :datetime, permitted: false
146
+ closed_at :datetime, permitted: false
125
147
 
126
- # Acts as tokened
127
- token :string
148
+ # Acts as tokyyened
149
+ token :string, permitted: false
128
150
 
129
151
  # Acts as Wizard
130
- wizard_steps :text
152
+ wizard_steps :text, permitted: false
131
153
 
132
154
  timestamps
133
155
  end
@@ -136,16 +158,19 @@ module EffectiveCpdAudit
136
158
  scope :sorted, -> { order(:id) }
137
159
 
138
160
  scope :draft, -> { where(submitted_at: nil) }
139
- scope :available, -> { where.not(status: COMPLETED_STATES) }
140
- scope :completed, -> { where(status: COMPLETED_STATES) }
141
161
 
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) }
162
+ scope :available, -> { where.not(status: done_states) }
163
+ scope :in_progress, -> { where.not(status: done_states) }
164
+
165
+ scope :done, -> { where(status: done_states) }
166
+
167
+ scope :waiting_on_admin, -> { where(status: waiting_on_admin_states) }
168
+ scope :waiting_on_auditee, -> { where(status: waiting_on_auditee_states) }
169
+ scope :waiting_on_reviewers, -> { where(status: waiting_on_reviewers_states) }
145
170
 
146
171
  # effective_reports
147
172
  def reportable_scopes
148
- { draft: nil, available: nil, completed: nil, waiting_on_admin: nil, waiting_on_auditee: nil, waiting_on_reviewers: nil }
173
+ { draft: nil, available: nil, done: nil, waiting_on_admin: nil, waiting_on_auditee: nil, waiting_on_reviewers: nil }
149
174
  end
150
175
 
151
176
  before_validation(if: -> { new_record? }) do
@@ -153,7 +178,12 @@ module EffectiveCpdAudit
153
178
  self.due_date ||= deadline_to_submit()
154
179
  end
155
180
 
156
- validates :anonymous_name, presence: true, if: -> { cpd_audit_level&.anonymous? }
181
+ with_options(if: -> { cpd_audit_level&.anonymous? }) do
182
+ before_validation { assign_anonymous_name_and_number }
183
+
184
+ validates :anonymous_name, presence: true
185
+ validates :anonymous_number, presence: true
186
+ end
157
187
 
158
188
  validates :notification_date, presence: true
159
189
  validates :determination, presence: true, if: -> { closed? }
@@ -163,6 +193,9 @@ module EffectiveCpdAudit
163
193
  validates :extension_request_date, presence: true, if: -> { extension_request? }
164
194
  validates :extension_request_reason, presence: true, if: -> { extension_request? }
165
195
 
196
+ # Admin Missing Info
197
+ validates :missing_info_reason, presence: true, if: -> { missing_info? }
198
+
166
199
  validate(if: -> { current_step == :conflict && conflict_of_interest? && !ignore_deadlines? }) do
167
200
  deadline = deadline_to_conflict_of_interest()
168
201
  self.errors.add(:base, 'deadline to declare conflict of interest has already passed') if deadline && deadline < Time.zone.now
@@ -185,62 +218,130 @@ module EffectiveCpdAudit
185
218
  end
186
219
 
187
220
  # If we're submitted. Check if we can go into reviewed?
188
- before_save(if: -> { submitted? }) { review! }
221
+ before_save(if: -> { submitted? }) { try_complete! }
222
+ before_save(if: -> { completed? }) { try_review! }
189
223
 
190
224
  after_commit(on: :create) do
191
225
  send_email(:cpd_audit_opened)
192
226
  end
193
- end
194
227
 
195
- def to_s
196
- persisted? ? "#{cpd_audit_level} Audit of #{user}" : 'audit'
197
- end
228
+ def dynamic_wizard_steps
229
+ cpd_audit_level.cpd_audit_level_sections.each_with_object({}) do |section, h|
230
+ h["section#{section.position+1}".to_sym] = section.title
231
+ end
232
+ end
198
233
 
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
234
+ def wizard_step_title(step)
235
+ self.class::WIZARD_STEPS[step] || dynamic_wizard_steps.fetch(step)
202
236
  end
203
- end
204
237
 
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
238
+ def required_steps
239
+ steps = [:start, :information, :instructions]
209
240
 
210
- def required_steps
211
- steps = [:start, :information, :instructions]
241
+ steps << :conflict if cpd_audit_level.conflict_of_interest?
212
242
 
213
- steps << :conflict if cpd_audit_level.conflict_of_interest?
243
+ steps << :exemption if cpd_audit_level.can_request_exemption?
214
244
 
215
- steps << :exemption if cpd_audit_level.can_request_exemption?
245
+ unless exemption_requested?
246
+ steps << :extension if cpd_audit_level.can_request_extension?
247
+ end
216
248
 
217
- unless exemption_requested?
218
- steps << :extension if cpd_audit_level.can_request_extension?
249
+ if exemption_requested? || extension_requested?
250
+ steps += [:waiting]
251
+ end
252
+
253
+ if user_cpd_required?
254
+ steps += [:cpd]
255
+ end
256
+
257
+ steps += [:questionnaire] + dynamic_wizard_steps.keys + [:files, :submit, :submitted]
258
+
259
+ steps
219
260
  end
220
261
 
221
- if exemption_requested? || extension_requested?
222
- steps += [:waiting]
262
+ def can_visit_step?(step)
263
+ return [:start].exclude?(step) if missing_info? # Can revisit whole audit if missing info
264
+ return (step == :submitted) if was_submitted? # Can only view complete step once submitted
265
+
266
+ can_revisit_completed_steps(step)
223
267
  end
268
+ end
224
269
 
225
- steps += [:cpd, :questionnaire] + dynamic_wizard_steps.keys + [:files, :submit, :complete]
270
+ def to_s
271
+ persisted? ? "#{cpd_audit_level} Audit of #{name}" : 'audit'
272
+ end
226
273
 
227
- steps
274
+ def name
275
+ anonymous_name.presence || user.to_s
228
276
  end
229
277
 
230
- def wizard_step_title(step)
231
- WIZARD_STEPS[step] || dynamic_wizard_steps.fetch(step)
278
+ def anonymous?
279
+ cpd_audit_level&.anonymous?
232
280
  end
233
281
 
234
282
  def deadline_date
235
283
  (extension_date || notification_date)
236
284
  end
237
285
 
238
- def completed?
239
- COMPLETED_STATES.include?(status.to_sym)
286
+ def draft?
287
+ !was_submitted?
240
288
  end
241
289
 
242
290
  def in_progress?
243
- COMPLETED_STATES.include?(status.to_sym) == false
291
+ self.class.done_states.include?(status.to_sym) == false
292
+ end
293
+
294
+ def done?
295
+ self.class.done_states.include?(status.to_sym)
296
+ end
297
+
298
+ def ready_to_review?
299
+ was_completed?
300
+ end
301
+
302
+ def status_label
303
+ (status_was || status).to_s.gsub('_', ' ')
304
+ end
305
+
306
+ def summary
307
+ case status_was
308
+ when 'opened'
309
+ "The audit has been opened. The auditee and audit reviewers have been notified. Waiting for the auditee to submit their audit questionnaire."
310
+ when 'started'
311
+ "The auditee has begun their audit questionnaire. Waiting for the auditee to submit their audit questionnaire."
312
+ when 'conflicted'
313
+ "The auditee has declared a conflict of interest. A new reviewer will be assigned."
314
+ when 'conflicted_resolved'
315
+ "The auditee had declared a conflict of interest. This has been resolved. Waiting for the auditee to submit their audit questionnaire."
316
+ when 'exemption_requested'
317
+ "The auditee has requested an exemption. Waiting for request to be granted or denied."
318
+ when 'exemption_granted'
319
+ "The exemption request has been granted. This audit may now be closed."
320
+ when 'exemption_denied'
321
+ "The exemption request has been denied. The audit will continue. Waiting for the auditee to submit their audit questionnaire."
322
+ when 'extension_requested'
323
+ "The auditee has requested an extension. Waiting for request to be granted or denied."
324
+ when 'extension_granted'
325
+ "The extension request has been granted. There is a new deadline. Waiting for the auditee to submit their audit questionnaire."
326
+ when 'extension_denied'
327
+ "The extension request has been denied. The deadline remains. Waiting for the auditee to submit their audit questionnaire."
328
+ when 'submitted'
329
+ summary = "Auditee has submitted their audit wizard."
330
+ tasks = "The following tasks remain before it can be completed:"
331
+ approval = "Waiting on complete, review and determination."
332
+ items = completed_requirements.map { |item, done| "<li>#{item}: #{done ? 'Complete' : 'Incomplete'}</li>" }.join
333
+ completed_requirements.present? ? "<p>#{summary} #{tasks}</p><ul>#{items}</ul>" : "#{summary} #{approval}"
334
+ when 'completed'
335
+ "All required materials have been provided. This audit will transition to 'reviewed' after all reviewers have finished."
336
+ when 'missing_info'
337
+ "Missing the following information: <ul><li>#{missing_info_reason}</li></ul>"
338
+ when 'reviewed'
339
+ "The audit has been reviewed and is ready for the final determination to be made."
340
+ when 'closed'
341
+ "This audit has been closed with a final determination #{determination}. All done."
342
+ else
343
+ raise("unexpected status #{status}")
344
+ end.html_safe
244
345
  end
245
346
 
246
347
  def cpd_audit_level_section(wizard_step)
@@ -348,29 +449,79 @@ module EffectiveCpdAudit
348
449
  def required_cpd_cycle
349
450
  @required_cpd_cycle ||= begin
350
451
  last_year = ((notification_date || created_at || Time.zone.now) - 1.year).all_year
351
- EffectiveCpd.CpdCycle.available.where(start_at: last_year).first
452
+ Effective::CpdCycle.available.where(start_at: last_year).first
352
453
  end
353
454
  end
354
455
 
355
456
  # Auditee wizard action
356
457
  def submit!
458
+ # Complete the very last step too
459
+ wizard_steps[:submitted] = Time.zone.now
460
+
357
461
  if conflict_of_interest?
358
462
  conflicted!
359
463
  send_email(:cpd_audit_conflicted)
360
464
  else
361
465
  submitted!
362
-
363
- cpd_audit_reviews.each { |cpd_audit_review| cpd_audit_review.ready! }
364
466
  send_email(:cpd_audit_submitted)
365
467
  end
366
468
 
367
469
  true
368
470
  end
369
471
 
472
+ # When an audit is submitted, these must be done to go to completed.
473
+ # An Admin can override this and just set them to completed.
474
+ def completed_requirements
475
+ {}
476
+ end
477
+
478
+ # called by a before_save when submitted
479
+ def try_complete!
480
+ false # Nothing to do. Admin completes audits.
481
+ end
482
+
483
+ def complete!
484
+ raise('audit must have been submitted to complete!') unless was_submitted?
485
+
486
+ assign_attributes(missing_info_reason: nil)
487
+ completed!
488
+
489
+ # Each of these sends a cpd_audit_review_ready email
490
+ cpd_audit_reviews.each { |cpd_audit_review| cpd_audit_review.ready! }
491
+
492
+ true
493
+ end
494
+
495
+ def missing!
496
+ raise('audit must have been submitted to missing!') unless was_submitted?
497
+
498
+ missing_info!
499
+ send_email(:cpd_audit_missing_info)
500
+ end
501
+
502
+ def resubmit!
503
+ raise('audit must have been submitted and missing info to resubmit!') unless was_submitted? && was_missing_info?
504
+ raise('already submitted') if submitted?
505
+
506
+ assign_attributes(skip_to_step: :submitted, submitted_at: Time.zone.now)
507
+
508
+ submitted!
509
+ send_email(:cpd_audit_submitted)
510
+ end
511
+
512
+ def try_review!
513
+ return false unless was_submitted?
514
+ return false unless completed?
515
+ return false unless cpd_audit_reviews.present? && cpd_audit_reviews.all?(&:completed?)
516
+
517
+ review!
518
+ end
519
+
370
520
  # Called in a before_save. Intended for applicant_review to call in its submit! method
371
521
  def review!
372
- return false unless submitted?
373
- return false unless cpd_audit_reviews.present? && cpd_audit_reviews.all?(&:completed?)
522
+ raise('already reviewed') if was_reviewed?
523
+ raise('audit must have been submitted to review!') unless was_submitted?
524
+ raise('audit must have been completed to review!') unless was_completed?
374
525
 
375
526
  reviewed!
376
527
  send_email(:cpd_audit_reviewed)
@@ -422,4 +573,22 @@ module EffectiveCpdAudit
422
573
  EffectiveResources.advance_date(date, business_days: cpd_audit_level.days_to_submit)
423
574
  end
424
575
 
576
+ # The name pattern is A23XXX where XXX is an autoincrement
577
+ # The name pattern is A23XXX where XXX is an autoincrement
578
+ def assign_anonymous_name_and_number
579
+ return if anonymous_name.present? || anonymous_number.present?
580
+ return if cpd_audit_level.blank?
581
+
582
+ prefix = cpd_audit_level.anonymous_audits_prefix
583
+ raise('expected cpd audit level to have an anonymous prefix') unless prefix.present?
584
+
585
+ # Where starts with prefix
586
+ number = (self.class.all.where('anonymous_name LIKE ?', "#{prefix}%").maximum('anonymous_number') || 0) + 1 # The next number
587
+
588
+ # Apply prefix and pad number to 3 digits
589
+ name = prefix + number.to_s.rjust(3, '0')
590
+
591
+ assign_attributes(anonymous_number: number, anonymous_name: name)
592
+ end
593
+
425
594
  end
@@ -29,7 +29,7 @@ module EffectiveCpdAuditLevel
29
29
  # App scoped
30
30
  has_many :cpd_audits
31
31
 
32
- has_many :cpd_audit_reviews, -> { EffectiveCpd.CpdAuditReview.sorted }, inverse_of: :cpd_audit_level, dependent: :destroy
32
+ has_many :cpd_audit_reviews, -> { order(:id) }, inverse_of: :cpd_audit_level, dependent: :destroy
33
33
  accepts_nested_attributes_for :cpd_audit_reviews, allow_destroy: true
34
34
 
35
35
  # Effective Scoped
@@ -46,6 +46,8 @@ module EffectiveCpdAuditLevel
46
46
  title :string
47
47
 
48
48
  anonymous :boolean
49
+ anonymous_audits_prefix :string
50
+ anonymous_audit_reviews_prefix :string
49
51
 
50
52
  determinations :text # Final determination by auditor
51
53
  recommendations :text # Recommendations by audit reviewers
@@ -86,10 +88,11 @@ module EffectiveCpdAuditLevel
86
88
  validates :days_to_request_extension, presence: true, if: -> { can_request_extension? }
87
89
  validates :days_to_request_extension, numericality: { greater_than: 0, allow_nil: true }
88
90
 
91
+ validates :anonymous_audits_prefix, presence: true, if: -> { anonymous? }
92
+ validates :anonymous_audit_reviews_prefix, presence: true, if: -> { anonymous? }
93
+
89
94
  before_destroy do
90
- if (count = cpd_audits.length) > 0
91
- raise("#{count} audits belong to this audit level")
92
- end
95
+ raise("can't destroy audit level with existing audits") if cpd_audits.length > 0
93
96
  end
94
97
  end
95
98