effective_cpd 0.0.1 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +89 -10
  3. data/app/controllers/admin/cpd_audit_level_questions_controller.rb +13 -0
  4. data/app/controllers/admin/cpd_audit_levels_controller.rb +13 -0
  5. data/app/controllers/admin/cpd_audit_reviews_controller.rb +13 -0
  6. data/app/controllers/admin/cpd_audits_controller.rb +41 -0
  7. data/app/controllers/effective/cpd_audit_reviews_controller.rb +60 -0
  8. data/app/controllers/effective/cpd_audits_controller.rb +55 -0
  9. data/app/controllers/effective/cpd_cycles_controller.rb +1 -0
  10. data/app/controllers/effective/cpd_statements_controller.rb +1 -3
  11. data/app/datatables/admin/effective_cpd_audit_level_questions_datatable.rb +30 -0
  12. data/app/datatables/admin/effective_cpd_audit_levels_datatable.rb +34 -0
  13. data/app/datatables/admin/effective_cpd_audit_reviews_datatable.rb +29 -0
  14. data/app/datatables/admin/effective_cpd_audits_datatable.rb +67 -0
  15. data/app/datatables/admin/effective_cpd_statements_datatable.rb +3 -3
  16. data/app/datatables/effective_cpd_available_audit_reviews_datatable.rb +37 -0
  17. data/app/datatables/effective_cpd_available_audits_datatable.rb +30 -0
  18. data/app/datatables/{effective_cpd_datatable.rb → effective_cpd_available_cycles_datatable.rb} +3 -4
  19. data/app/datatables/effective_cpd_completed_audit_reviews_datatable.rb +32 -0
  20. data/app/datatables/effective_cpd_completed_audits_datatable.rb +24 -0
  21. data/app/datatables/effective_cpd_completed_statements_datatable.rb +28 -0
  22. data/app/helpers/effective_cpd_audits_helper.rb +48 -0
  23. data/app/mailers/effective/cpd_mailer.rb +155 -3
  24. data/app/models/effective/cpd_activity.rb +16 -3
  25. data/app/models/effective/cpd_audit.rb +380 -0
  26. data/app/models/effective/cpd_audit_level.rb +87 -0
  27. data/app/models/effective/cpd_audit_level_question.rb +91 -0
  28. data/app/models/effective/cpd_audit_level_question_option.rb +34 -0
  29. data/app/models/effective/cpd_audit_level_section.rb +50 -0
  30. data/app/models/effective/cpd_audit_response.rb +86 -0
  31. data/app/models/effective/cpd_audit_response_option.rb +6 -0
  32. data/app/models/effective/cpd_audit_review.rb +222 -0
  33. data/app/models/effective/cpd_audit_review_item.rb +34 -0
  34. data/app/models/effective/cpd_category.rb +12 -3
  35. data/app/models/effective/cpd_cycle.rb +7 -0
  36. data/app/models/effective/cpd_rule.rb +3 -1
  37. data/app/models/effective/cpd_statement.rb +15 -8
  38. data/app/views/admin/cpd_activities/_form.html.haml +1 -1
  39. data/app/views/admin/cpd_audit_level_questions/_form.html.haml +100 -0
  40. data/app/views/admin/cpd_audit_levels/_form.html.haml +24 -0
  41. data/app/views/admin/cpd_audit_levels/_form_content_audit.html.haml +15 -0
  42. data/app/views/admin/cpd_audit_levels/_form_content_audit_review.html.haml +15 -0
  43. data/app/views/admin/cpd_audit_levels/_form_cpd_audit_level.html.haml +52 -0
  44. data/app/views/admin/cpd_audit_levels/_form_cpd_audit_level_section.html.haml +10 -0
  45. data/app/views/admin/cpd_audit_reviews/_cpd_audit_review.html.haml +1 -0
  46. data/app/views/admin/cpd_audit_reviews/_form.html.haml +13 -0
  47. data/app/views/admin/cpd_audits/_audit_reviewer_fields.html.haml +2 -0
  48. data/app/views/admin/cpd_audits/_auditee_fields.html.haml +2 -0
  49. data/app/views/admin/cpd_audits/_form.html.haml +46 -0
  50. data/app/views/admin/cpd_audits/_form_conflict.html.haml +24 -0
  51. data/app/views/admin/cpd_audits/_form_determination.html.haml +10 -0
  52. data/app/views/admin/cpd_audits/_form_exemption.html.haml +24 -0
  53. data/app/views/admin/cpd_audits/_form_extension.html.haml +24 -0
  54. data/app/views/admin/cpd_audits/_form_new.html.haml +28 -0
  55. data/app/views/admin/cpd_audits/_status.html.haml +121 -0
  56. data/app/views/admin/cpd_categories/_form.html.haml +1 -1
  57. data/app/views/admin/cpd_cycles/_form.html.haml +4 -4
  58. data/app/views/admin/cpd_cycles/_form_cpd_cycle.html.haml +3 -0
  59. data/app/views/admin/cpd_statements/_cpd_statement.html.haml +7 -0
  60. data/app/views/effective/cpd_audit_level_questions/_cpd_audit_level_question.html.haml +5 -0
  61. data/app/views/effective/cpd_audit_responses/_cpd_audit_response.html.haml +4 -0
  62. data/app/views/effective/cpd_audit_responses/_fields.html.haml +13 -0
  63. data/app/views/effective/cpd_audit_responses/fields/_choose_one.html.haml +1 -0
  64. data/app/views/effective/cpd_audit_responses/fields/_date.html.haml +2 -0
  65. data/app/views/effective/cpd_audit_responses/fields/_email.html.haml +2 -0
  66. data/app/views/effective/cpd_audit_responses/fields/_long_answer.html.haml +1 -0
  67. data/app/views/effective/cpd_audit_responses/fields/_number.html.haml +2 -0
  68. data/app/views/effective/cpd_audit_responses/fields/_select_all_that_apply.html.haml +1 -0
  69. data/app/views/effective/cpd_audit_responses/fields/_select_up_to_1.html.haml +1 -0
  70. data/app/views/effective/cpd_audit_responses/fields/_select_up_to_2.html.haml +1 -0
  71. data/app/views/effective/cpd_audit_responses/fields/_select_up_to_3.html.haml +1 -0
  72. data/app/views/effective/cpd_audit_responses/fields/_select_up_to_4.html.haml +1 -0
  73. data/app/views/effective/cpd_audit_responses/fields/_select_up_to_5.html.haml +1 -0
  74. data/app/views/effective/cpd_audit_responses/fields/_short_answer.html.haml +1 -0
  75. data/app/views/effective/cpd_audit_responses/fields/_upload_file.html.haml +1 -0
  76. data/app/views/effective/cpd_audit_responses/responses/_choose_one.html.haml +1 -0
  77. data/app/views/effective/cpd_audit_responses/responses/_date.html.haml +1 -0
  78. data/app/views/effective/cpd_audit_responses/responses/_email.html.haml +1 -0
  79. data/app/views/effective/cpd_audit_responses/responses/_long_answer.html.haml +1 -0
  80. data/app/views/effective/cpd_audit_responses/responses/_number.html.haml +1 -0
  81. data/app/views/effective/cpd_audit_responses/responses/_select_all_that_apply.html.haml +5 -0
  82. data/app/views/effective/cpd_audit_responses/responses/_select_up_to_1.html.haml +1 -0
  83. data/app/views/effective/cpd_audit_responses/responses/_select_up_to_2.html.haml +5 -0
  84. data/app/views/effective/cpd_audit_responses/responses/_select_up_to_3.html.haml +5 -0
  85. data/app/views/effective/cpd_audit_responses/responses/_select_up_to_4.html.haml +5 -0
  86. data/app/views/effective/cpd_audit_responses/responses/_select_up_to_5.html.haml +5 -0
  87. data/app/views/effective/cpd_audit_responses/responses/_short_answer.html.haml +1 -0
  88. data/app/views/effective/cpd_audit_responses/responses/_upload_file.html.haml +4 -0
  89. data/app/views/effective/cpd_audit_review_items/_cpd_audit_review_item.html.haml +6 -0
  90. data/app/views/effective/cpd_audit_review_items/_fields.html.haml +11 -0
  91. data/app/views/effective/cpd_audit_reviews/_conflict.html.haml +15 -0
  92. data/app/views/effective/cpd_audit_reviews/_cpd_audit_level_section.html.haml +23 -0
  93. data/app/views/effective/cpd_audit_reviews/_cpd_audit_review.html.haml +17 -0
  94. data/app/views/effective/cpd_audit_reviews/_cpd_statement.html.haml +15 -0
  95. data/app/views/effective/cpd_audit_reviews/_layout.html.haml +24 -0
  96. data/app/views/effective/cpd_audit_reviews/_recommendation.html.haml +9 -0
  97. data/app/views/effective/cpd_audit_reviews/_summary.html.haml +47 -0
  98. data/app/views/effective/cpd_audit_reviews/complete.html.haml +20 -0
  99. data/app/views/effective/cpd_audit_reviews/conflict.html.haml +21 -0
  100. data/app/views/effective/cpd_audit_reviews/cpd_audit_level_section.html.haml +43 -0
  101. data/app/views/effective/cpd_audit_reviews/cpd_statement.html.haml +35 -0
  102. data/app/views/effective/cpd_audit_reviews/information.html.haml +5 -0
  103. data/app/views/effective/cpd_audit_reviews/instructions.html.haml +10 -0
  104. data/app/views/effective/cpd_audit_reviews/questionnaire.html.haml +8 -0
  105. data/app/views/effective/cpd_audit_reviews/recommendation.html.haml +21 -0
  106. data/app/views/effective/cpd_audit_reviews/start.html.haml +10 -0
  107. data/app/views/effective/cpd_audit_reviews/statements.html.haml +29 -0
  108. data/app/views/effective/cpd_audit_reviews/submit.html.haml +14 -0
  109. data/app/views/effective/cpd_audit_reviews/waiting.html.haml +9 -0
  110. data/app/views/effective/cpd_audits/_conflict.html.haml +15 -0
  111. data/app/views/effective/cpd_audits/_cpd_audit.html.haml +12 -0
  112. data/app/views/effective/cpd_audits/_cpd_audit_level_section.html.haml +14 -0
  113. data/app/views/effective/cpd_audits/_exemption.html.haml +26 -0
  114. data/app/views/effective/cpd_audits/_extension.html.haml +30 -0
  115. data/app/views/effective/cpd_audits/_files.html.haml +12 -0
  116. data/app/views/effective/cpd_audits/_layout.html.haml +33 -0
  117. data/app/views/effective/cpd_audits/_summary.html.haml +54 -0
  118. data/app/views/effective/cpd_audits/_waiting.html.haml +6 -0
  119. data/app/views/effective/cpd_audits/complete.html.haml +24 -0
  120. data/app/views/effective/cpd_audits/conflict.html.haml +22 -0
  121. data/app/views/effective/cpd_audits/cpd_audit_level_section.html.haml +20 -0
  122. data/app/views/effective/cpd_audits/exemption.html.haml +19 -0
  123. data/app/views/effective/cpd_audits/extension.html.haml +21 -0
  124. data/app/views/effective/cpd_audits/files.html.haml +7 -0
  125. data/app/views/effective/cpd_audits/information.html.haml +5 -0
  126. data/app/views/effective/cpd_audits/instructions.html.haml +5 -0
  127. data/app/views/effective/cpd_audits/questionnaire.html.haml +5 -0
  128. data/app/views/effective/cpd_audits/start.html.haml +11 -0
  129. data/app/views/effective/cpd_audits/submit.html.haml +15 -0
  130. data/app/views/effective/cpd_audits/waiting.html.haml +24 -0
  131. data/app/views/effective/cpd_mailer/README.md +1 -0
  132. data/app/views/effective/cpd_mailer/cpd_audit_closed.liquid +15 -0
  133. data/app/views/effective/cpd_mailer/cpd_audit_conflict_resolved.liquid +15 -0
  134. data/app/views/effective/cpd_mailer/cpd_audit_conflicted.liquid +13 -0
  135. data/app/views/effective/cpd_mailer/cpd_audit_exemption_denied.liquid +13 -0
  136. data/app/views/effective/cpd_mailer/cpd_audit_exemption_granted.liquid +13 -0
  137. data/app/views/effective/cpd_mailer/cpd_audit_exemption_request.liquid +13 -0
  138. data/app/views/effective/cpd_mailer/cpd_audit_extension_denied.liquid +13 -0
  139. data/app/views/effective/cpd_mailer/cpd_audit_extension_granted.liquid +13 -0
  140. data/app/views/effective/cpd_mailer/cpd_audit_extension_request.liquid +13 -0
  141. data/app/views/effective/cpd_mailer/cpd_audit_opened.liquid +13 -0
  142. data/app/views/effective/cpd_mailer/cpd_audit_review_opened.liquid +15 -0
  143. data/app/views/effective/cpd_mailer/cpd_audit_review_ready.liquid +13 -0
  144. data/app/views/effective/cpd_mailer/cpd_audit_review_submitted.liquid +11 -0
  145. data/app/views/effective/cpd_mailer/cpd_audit_reviewed.liquid +13 -0
  146. data/app/views/effective/cpd_mailer/cpd_audit_submitted.liquid +13 -0
  147. data/app/views/effective/cpd_statement_activities/_cpd_statement_activity.html.haml +65 -0
  148. data/app/views/effective/cpd_statement_activities/_form.html.haml +9 -10
  149. data/app/views/effective/cpd_statements/_activities.html.haml +3 -64
  150. data/app/views/effective/cpd_statements/_activities_edit.html.haml +2 -0
  151. data/app/views/effective/cpd_statements/_activities_table.html.haml +64 -0
  152. data/app/views/effective/cpd_statements/_agreements.html.haml +19 -5
  153. data/app/views/effective/cpd_statements/_cpd_statement.html.haml +6 -3
  154. data/app/views/effective/cpd_statements/_summary.html.haml +26 -31
  155. data/app/views/effective/cpd_statements/activities.html.haml +1 -1
  156. data/app/views/effective/cpd_statements/agreements.html.haml +7 -1
  157. data/app/views/effective/cpd_statements/submit.html.haml +1 -1
  158. data/config/effective_cpd.rb +47 -10
  159. data/config/routes.rb +18 -1
  160. data/db/migrate/01_create_effective_cpd.rb.erb +157 -1
  161. data/db/seeds.rb +2 -1
  162. data/lib/effective_cpd.rb +42 -3
  163. data/lib/effective_cpd/version.rb +1 -1
  164. data/lib/generators/effective_cpd/install_generator.rb +16 -3
  165. metadata +168 -9
  166. data/app/datatables/effective_cpd_statements_datatable.rb +0 -23
  167. data/app/views/admin/cpd_statements/_form.html.haml +0 -6
@@ -3,10 +3,13 @@ module Effective
3
3
  belongs_to :cpd_category
4
4
 
5
5
  has_rich_text :body
6
- log_changes(to: :cpd_category) if respond_to?(:log_changes)
7
6
 
8
- # has_many :rules, -> { order(cycle_id: :desc) }, as: :ruleable, dependent: :delete_all
9
- # accepts_nested_attributes_for :rules, allow_destroy: true
7
+ if respond_to?(:log_changes)
8
+ log_changes(to: :cpd_category, except: [:cpd_statement_activities])
9
+ end
10
+
11
+ #has_many :rules, class_name: 'Effective::CpdRule', as: :ruleable
12
+ has_many :cpd_statement_activities
10
13
 
11
14
  effective_resource do
12
15
  title :string
@@ -33,9 +36,19 @@ module Effective
33
36
  validates :title, presence: true
34
37
  validates :position, presence: true
35
38
 
39
+ before_destroy do
40
+ if (count = cpd_statement_activities.length) > 0
41
+ raise("#{count} statement activities belong to this activity")
42
+ end
43
+ end
44
+
36
45
  def to_s
37
46
  title.presence || 'activity'
38
47
  end
39
48
 
49
+ def amount_static?
50
+ amount_label.blank? && amount2_label.blank?
51
+ end
52
+
40
53
  end
41
54
  end
@@ -0,0 +1,380 @@
1
+ module Effective
2
+ class CpdAudit < ActiveRecord::Base
3
+ attr_accessor :current_user
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
+
27
+ COMPLETED_STATES = [:exemption_granted, :closed]
28
+
29
+ WAITING_ON_ADMIN_STATES = [:conflicted, :exemption_requested, :extension_requested, :reviewed]
30
+ WAITING_ON_REVIEWERS_STATES = [:submitted]
31
+ WAITING_ON_AUDITEE_STATES = [:opened, :started, :conflicted_resolved, :exemption_denied, :extension_granted, :extension_denied]
32
+
33
+ acts_as_statused(
34
+ :opened, # Just Opened
35
+ :started, # First screen clicked
36
+ :conflicted, # Auditee has declared a conflict of interest
37
+ :conflicted_resolved, # The conflict of interest has been resolved
38
+ :exemption_requested, # Auditee has requested an exemption
39
+ :exemption_granted, # Exemption granted -> Audit is cancelled. Exit state.
40
+ :exemption_denied, # Exemption denied
41
+ :extension_requested, # Audittee has requested an extension
42
+ :extension_granted, # Extension granted
43
+ :extension_denied, # Extension denied
44
+ :submitted, # Audittee has completed questionnaire submitted. Audittee is done.
45
+ :reviewed, # All audit reviews completed. Ready for a determination.
46
+ :closed # Determination made by admin and/or audit committee. Exit state. All done.
47
+ )
48
+
49
+ acts_as_wizard(
50
+ start: 'Start',
51
+ information: 'Information',
52
+ instructions: 'Instructions',
53
+
54
+ # These 4 steps are determined by audit_level settings
55
+ conflict: 'Conflict of Interest',
56
+ exemption: 'Request Exemption',
57
+ extension: 'Request Extension',
58
+ waiting: 'Waiting',
59
+
60
+ questionnaire: 'Questionnaire',
61
+ # ... There will be one step per cpd_audit_level_sections here
62
+ files: 'Upload Resume',
63
+
64
+ submit: 'Confirm & Submit',
65
+ complete: 'Complete'
66
+ )
67
+
68
+ effective_resource do
69
+ due_date :date # Computed due date based on notification and extension date
70
+
71
+ # Important dates
72
+ notification_date :date # Can be set on CpdAudits#new, but basically created_at
73
+ extension_date :date # set by admin if extension if granted
74
+
75
+ # Final determination
76
+ determination :string
77
+
78
+ # Auditee response
79
+ conflict_of_interest :boolean
80
+ conflict_of_interest_reason :text
81
+
82
+ exemption_request :boolean
83
+ exemption_request_reason :text
84
+
85
+ extension_request :boolean
86
+ extension_request_date :date
87
+ extension_request_reason :text
88
+
89
+ # acts_as_statused
90
+ status :string
91
+ status_steps :text
92
+
93
+ # Status dates
94
+ started_at :datetime
95
+ submitted_at :datetime
96
+ reviewed_at :datetime
97
+ audited_at :datetime # TODO: CHange to closed_at
98
+
99
+ # Acts as tokened
100
+ token :string
101
+
102
+ # Acts as Wizard
103
+ wizard_steps :text
104
+
105
+ timestamps
106
+ end
107
+
108
+ scope :deep, -> { includes(:cpd_audit_level, user: [:cpd_statements], cpd_audit_reviews: [:cpd_audit_level, :user, :cpd_audit_review_items]) }
109
+ scope :sorted, -> { order(:id) }
110
+
111
+ scope :draft, -> { where(submitted_at: nil) }
112
+ scope :available, -> { where.not(status: COMPLETED_STATES) }
113
+ scope :completed, -> { where(status: COMPLETED_STATES) }
114
+
115
+ scope :waiting_on_admin, -> { where(status: WAITING_ON_ADMIN_STATES) }
116
+ scope :waiting_on_auditee, -> { where(status: WAITING_ON_AUDITEE_STATES) }
117
+ scope :waiting_on_reviewers, -> { where(status: WAITING_ON_REVIEWERS_STATES) }
118
+
119
+ before_validation(if: -> { new_record? }) do
120
+ self.notification_date ||= Time.zone.now
121
+ self.due_date ||= deadline_to_submit()
122
+ end
123
+
124
+ validates :notification_date, presence: true
125
+ validates :determination, presence: true, if: -> { closed? }
126
+
127
+ validates :conflict_of_interest_reason, presence: true, if: -> { conflict_of_interest? }
128
+ validates :exemption_request_reason, presence: true, if: -> { exemption_request? }
129
+ validates :extension_request_date, presence: true, if: -> { extension_request? }
130
+ validates :extension_request_reason, presence: true, if: -> { extension_request? }
131
+
132
+ validate(if: -> { determination.present? }) do
133
+ unless cpd_audit_level.determinations.include?(determination)
134
+ self.errors.add(:determination, 'must exist in this audit level')
135
+ end
136
+ end
137
+
138
+ # If we're submitted. Check if we can go into reviewed?
139
+ before_save(if: -> { submitted? }) { review! }
140
+
141
+ after_commit(on: :create) do
142
+ send_email(:cpd_audit_opened)
143
+ end
144
+
145
+ def to_s
146
+ persisted? ? "#{cpd_audit_level} Audit of #{user}" : 'audit'
147
+ end
148
+
149
+ acts_as_wizard(
150
+ start: 'Start',
151
+ information: 'Information',
152
+ instructions: 'Instructions',
153
+
154
+ # These 4 steps are determined by audit_level settings
155
+ conflict: 'Conflict of Interest',
156
+ exemption: 'Request Exemption',
157
+ extension: 'Request Extension',
158
+ waiting: 'Waiting on Request',
159
+
160
+ questionaire: 'Questionaire',
161
+ # ... There will be one step per cpd_audit_level_sections here
162
+ files: 'Upload Resume',
163
+
164
+ submit: 'Confirm & Submit',
165
+ complete: 'Complete'
166
+ )
167
+
168
+ def dynamic_wizard_steps
169
+ cpd_audit_level.cpd_audit_level_sections.each_with_object({}) do |section, h|
170
+ h["section#{section.position+1}".to_sym] = section.title
171
+ end
172
+ end
173
+
174
+ def can_visit_step?(step)
175
+ return (step == :complete) if was_submitted? # Can only view complete step once submitted
176
+ can_revisit_completed_steps(step)
177
+ end
178
+
179
+ def required_steps
180
+ steps = [:start, :information, :instructions]
181
+
182
+ steps << :conflict if cpd_audit_level.conflict_of_interest?
183
+
184
+ if conflicted?
185
+ return steps + [:waiting, :submit, :complete]
186
+ end
187
+
188
+ steps << :exemption if cpd_audit_level.can_request_exemption?
189
+
190
+ unless exemption_requested?
191
+ steps << :extension if cpd_audit_level.can_request_extension?
192
+ end
193
+
194
+ if exemption_requested? || extension_requested?
195
+ steps += [:waiting]
196
+ end
197
+
198
+ steps += [:questionnaire] + dynamic_wizard_steps.keys + [:files, :submit, :complete]
199
+
200
+ steps
201
+ end
202
+
203
+ def wizard_step_title(step)
204
+ WIZARD_STEPS[step] || dynamic_wizard_steps.fetch(step)
205
+ end
206
+
207
+ def deadline_date
208
+ (extension_date || notification_date)
209
+ end
210
+
211
+ def completed?
212
+ COMPLETED_STATES.include?(status.to_sym)
213
+ end
214
+
215
+ def in_progress?
216
+ COMPLETED_STATES.include?(status.to_sym) == false
217
+ end
218
+
219
+ def cpd_audit_level_section(wizard_step)
220
+ position = (wizard_step.to_s.split('section').last.to_i rescue false)
221
+ cpd_audit_level.cpd_audit_level_sections.find { |section| (section.position + 1) == position }
222
+ end
223
+
224
+ # Find or build
225
+ def cpd_audit_response(cpd_audit_level_question)
226
+ cpd_audit_response = cpd_audit_responses.find { |r| r.cpd_audit_level_question_id == cpd_audit_level_question.id }
227
+ cpd_audit_response ||= cpd_audit_responses.build(cpd_audit: self, cpd_audit_level_question: cpd_audit_level_question)
228
+ end
229
+
230
+ # Auditee wizard action
231
+ def start!
232
+ started!
233
+ end
234
+
235
+ # Auditee wizard action
236
+ def conflict!
237
+ return started! unless conflict_of_interest?
238
+
239
+ update!(status: :conflicted)
240
+ send_email(:cpd_audit_conflicted)
241
+ end
242
+
243
+ # Admin action
244
+ def resolve_conflict!
245
+ wizard_steps[:conflict] = nil # Have them complete the conflict step again.
246
+
247
+ assign_attributes(conflict_of_interest: false, conflict_of_interest_reason: nil)
248
+ conflicted_resolved!
249
+
250
+ send_email(:cpd_audit_conflict_resolved)
251
+ true
252
+ end
253
+
254
+ # Auditee wizard action
255
+ def exemption!
256
+ return started! unless exemption_request?
257
+
258
+ update!(status: :exemption_requested)
259
+ send_email(:cpd_audit_exemption_request)
260
+ end
261
+
262
+ # Admin action
263
+ def process_exemption!
264
+ case admin_process_request
265
+ when 'Granted' then grant_exemption!
266
+ when 'Denied' then deny_exemption!
267
+ else
268
+ self.errors.add(:admin_process_request, "can't be blank"); save!
269
+ end
270
+ end
271
+
272
+ def grant_exemption!
273
+ wizard_steps[:submit] ||= Time.zone.now
274
+ submitted! && exemption_granted!
275
+ send_email(:cpd_audit_exemption_granted)
276
+ end
277
+
278
+ def deny_exemption!
279
+ assign_attributes(exemption_request: false)
280
+ exemption_denied!
281
+ send_email(:cpd_audit_exemption_denied)
282
+ end
283
+
284
+ # Auditee wizard action
285
+ def extension!
286
+ return started! unless extension_request?
287
+
288
+ update!(status: :extension_requested)
289
+ send_email(:cpd_audit_extension_request)
290
+ end
291
+
292
+ # Admin action
293
+ def process_extension!
294
+ case admin_process_request
295
+ when 'Granted' then grant_extension!
296
+ when 'Denied' then deny_extension!
297
+ else
298
+ self.errors.add(:admin_process_request, "can't be blank"); save!
299
+ end
300
+ end
301
+
302
+ def grant_extension!
303
+ self.extension_date = extension_request_date
304
+ self.due_date = deadline_to_submit()
305
+
306
+ cpd_audit_reviews.each { |cpd_audit_review| cpd_audit_review.extension_granted! }
307
+ extension_granted!
308
+ send_email(:cpd_audit_extension_granted)
309
+ end
310
+
311
+ def deny_extension!
312
+ assign_attributes(extension_request: false)
313
+ extension_denied!
314
+ send_email(:cpd_audit_extension_denied)
315
+ end
316
+
317
+ # Auditee wizard action
318
+ def submit!
319
+ submitted!
320
+ cpd_audit_reviews.each { |cpd_audit_review| cpd_audit_review.ready! }
321
+ send_email(:cpd_audit_submitted)
322
+ end
323
+
324
+ # Called in a before_save. Intended for applicant_review to call in its submit! method
325
+ def review!
326
+ return false unless submitted?
327
+ return false unless cpd_audit_reviews.present? && cpd_audit_reviews.all?(&:completed?)
328
+
329
+ reviewed!
330
+ send_email(:cpd_audit_reviewed)
331
+ end
332
+
333
+ # Admin action
334
+ def close!
335
+ closed!
336
+ send_email(:cpd_audit_closed)
337
+ end
338
+
339
+ def email_form_defaults(action)
340
+ { from: EffectiveCpd.mailer_sender }
341
+ end
342
+
343
+ def send_email(email)
344
+ EffectiveCpd.send_email(email, self, email_form_params) unless email_form_skip?
345
+ true
346
+ end
347
+
348
+ def deadline_to_conflict_of_interest
349
+ return nil unless cpd_audit_level&.conflict_of_interest?
350
+ return nil unless cpd_audit_level.days_to_declare_conflict.present?
351
+
352
+ date = (notification_date || created_at || Time.zone.now)
353
+ EffectiveResources.advance_date(date, business_days: cpd_audit_level.days_to_declare_conflict)
354
+ end
355
+
356
+ def deadline_to_exemption
357
+ return nil unless cpd_audit_level&.can_request_exemption?
358
+ return nil unless cpd_audit_level.days_to_request_exemption.present?
359
+
360
+ date = (notification_date || created_at || Time.zone.now)
361
+ EffectiveResources.advance_date(date, business_days: cpd_audit_level.days_to_request_exemption)
362
+ end
363
+
364
+ def deadline_to_extension
365
+ return nil unless cpd_audit_level&.can_request_extension?
366
+ return nil unless cpd_audit_level.days_to_request_extension.present?
367
+
368
+ date = (notification_date || created_at || Time.zone.now)
369
+ EffectiveResources.advance_date(date, business_days: cpd_audit_level.days_to_request_extension)
370
+ end
371
+
372
+ def deadline_to_submit
373
+ return nil unless cpd_audit_level&.days_to_submit.present?
374
+
375
+ date = (extension_date || notification_date || created_at || Time.zone.now)
376
+ EffectiveResources.advance_date(date, business_days: cpd_audit_level.days_to_submit)
377
+ end
378
+
379
+ end
380
+ end
@@ -0,0 +1,87 @@
1
+ module Effective
2
+ class CpdAuditLevel < ActiveRecord::Base
3
+ has_many_rich_texts
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
+
86
+ end
87
+ end