effective_memberships 0.6.13 → 0.7.1
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/applicant_reviews_controller.rb +19 -0
- data/app/controllers/effective/applicant_reviews_controller.rb +13 -0
- data/app/datatables/admin/effective_applicant_reviews_datatable.rb +24 -0
- data/app/datatables/effective_applicant_reviews_datatable.rb +25 -0
- data/app/datatables/effective_available_applicant_reviews_datatable.rb +40 -0
- data/app/mailers/effective/memberships_mailer.rb +57 -4
- data/app/models/concerns/effective_memberships_applicant.rb +26 -3
- data/app/models/concerns/effective_memberships_applicant_review.rb +34 -68
- data/app/models/concerns/effective_memberships_owner.rb +19 -0
- data/app/models/concerns/effective_memberships_user.rb +6 -0
- data/app/models/effective/membership.rb +3 -0
- data/app/views/admin/applicant_reviews/_applicant_review.html.haml +1 -0
- data/app/views/admin/applicants/_form_applicant.html.haml +4 -0
- data/app/views/admin/applicants/_status.html.haml +2 -2
- data/app/views/effective/applicant_endorsements/complete.html.haml +1 -1
- data/app/views/effective/applicant_references/complete.html.haml +1 -1
- data/app/views/effective/applicant_reviews/_applicant_review.html.haml +4 -0
- data/app/views/effective/applicant_reviews/_content.html.haml +8 -0
- data/app/views/effective/applicant_reviews/_dashboard.html.haml +36 -0
- data/app/views/effective/applicant_reviews/_layout.html.haml +3 -0
- data/app/views/effective/applicant_reviews/_recommendation.html.haml +23 -0
- data/app/views/effective/applicant_reviews/_review.html.haml +1 -0
- data/app/views/effective/applicant_reviews/recommendation.html.haml +20 -0
- data/app/views/effective/applicant_reviews/review.html.haml +14 -0
- data/app/views/effective/applicant_reviews/start.html.haml +18 -0
- data/app/views/effective/applicant_reviews/submitted.html.haml +15 -0
- data/app/views/effective/applicants/_summary.html.haml +4 -3
- data/app/views/effective/fees/_dashboard.html.haml +1 -1
- data/app/views/effective/memberships_mailer/applicant_approved.liquid +2 -2
- data/app/views/effective/memberships_mailer/applicant_completed.liquid +1 -1
- data/app/views/effective/memberships_mailer/applicant_review_submitted.liquid +19 -0
- data/app/views/effective/memberships_mailer/applicant_submitted.liquid +15 -0
- data/config/routes.rb +6 -0
- data/lib/effective_memberships/version.rb +1 -1
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a205c16844097a8874a4f40bda0465c96bd75cbda0ca7505cd74222180891323
|
4
|
+
data.tar.gz: f0edcff046181395c697a0c1ce386188dd28d419c7f3c5b9c466f6f9c51b972c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2abc588db28def44fbebc89138443c1de6e4cfba9654b2e0b93d2d399eca177b3ce0da51a0a62ac50dea6585518459609bc3a7749061719f9ea5f2c3572dfd2
|
7
|
+
data.tar.gz: 37e0ca136d376fba18e6d84eba56289a81c17c7da9268aaab10c415b78aecc5f83c256dd1a9eb43a4d4c362491b8deaf0223a711fcdf957007ccca463728c1d0
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Admin
|
2
|
+
class ApplicantReviewsController < ApplicationController
|
3
|
+
before_action(:authenticate_user!) if defined?(Devise)
|
4
|
+
before_action { EffectiveResources.authorize!(self, :admin, :effective_memberships) }
|
5
|
+
|
6
|
+
include Effective::CrudController
|
7
|
+
|
8
|
+
resource_scope -> { EffectiveMemberships.ApplicantReview.deep.all }
|
9
|
+
datatable -> { Admin::EffectiveApplicantReviewsDatatable.new }
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def permitted_params
|
14
|
+
model = (params.key?(:effective_applicant_review) ? :effective_applicant_review: :applicant_review)
|
15
|
+
params.require(model).permit!
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Effective
|
2
|
+
class ApplicantReviewsController < ApplicationController
|
3
|
+
before_action(:authenticate_user!) if defined?(Devise)
|
4
|
+
|
5
|
+
include Effective::WizardController
|
6
|
+
|
7
|
+
resource_scope -> {
|
8
|
+
applicant = EffectiveMemberships.Applicant.find(params[:applicant_id])
|
9
|
+
EffectiveMemberships.ApplicantReview.deep.where(reviewer: current_user, applicant: applicant)
|
10
|
+
}
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Admin
|
2
|
+
class EffectiveApplicantReviewsDatatable < Effective::Datatable
|
3
|
+
datatable do
|
4
|
+
order :submitted_at
|
5
|
+
|
6
|
+
col :created_at, visible: false
|
7
|
+
col :id, visible: false
|
8
|
+
|
9
|
+
col :submitted_at, label: 'Reviewed', as: :date
|
10
|
+
col :reviewer
|
11
|
+
col :applicant
|
12
|
+
|
13
|
+
col :recommendation
|
14
|
+
col :comments
|
15
|
+
|
16
|
+
actions_col
|
17
|
+
end
|
18
|
+
|
19
|
+
collection do
|
20
|
+
EffectiveMemberships.ApplicantReview.deep.submitted
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Dashboard ApplicantReviews
|
2
|
+
class EffectiveApplicantReviewsDatatable < Effective::Datatable
|
3
|
+
datatable do
|
4
|
+
order :id, :desc
|
5
|
+
|
6
|
+
col :created_at, visible: false
|
7
|
+
col :id, visible: false
|
8
|
+
|
9
|
+
col :applicant
|
10
|
+
col :reviewer, visible: false
|
11
|
+
|
12
|
+
col :submitted_at, label: 'Reviewed', as: :date
|
13
|
+
col :recommendation
|
14
|
+
col :comments
|
15
|
+
|
16
|
+
actions_col(show: false) do |applicant_review|
|
17
|
+
dropdown_link_to('Show', effective_memberships.applicant_applicant_review_path(applicant_review.applicant, applicant_review))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
collection do
|
22
|
+
EffectiveMemberships.ApplicantReview.deep.submitted.where(reviewer: current_user)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Dashboard Available Applicant Reviews
|
2
|
+
class EffectiveAvailableApplicantReviewsDatatable < Effective::Datatable
|
3
|
+
datatable do
|
4
|
+
order :created_at
|
5
|
+
|
6
|
+
col :id, visible: false
|
7
|
+
|
8
|
+
col :created_at, label: 'Created', as: :date, visible: false
|
9
|
+
col :updated_at, label: 'Updated', as: :date, visible: false
|
10
|
+
col :submitted_at, label: 'Submitted', as: :date, visible: false
|
11
|
+
|
12
|
+
# This is when they become available for review
|
13
|
+
col :completed_at, label: 'Date', as: :date
|
14
|
+
|
15
|
+
col :user
|
16
|
+
col :category, label: 'Category'
|
17
|
+
col :to_status, label: 'Status'
|
18
|
+
|
19
|
+
actions_col(show: false) do |applicant|
|
20
|
+
applicant_review = applicant.applicant_review(reviewer: current_user)
|
21
|
+
|
22
|
+
if applicant_review.blank?
|
23
|
+
dropdown_link_to('Start Review', effective_memberships.new_applicant_applicant_review_path(applicant), 'data-turbolinks' => false)
|
24
|
+
elsif applicant_review.in_progress?
|
25
|
+
dropdown_link_to('Continue', effective_memberships.applicant_applicant_review_build_path(applicant, applicant_review, applicant_review.next_step), 'data-turbolinks' => false)
|
26
|
+
dropdown_link_to('Delete', effective_memberships.applicant_applicant_review_path(applicant, applicant_review), 'data-confirm': "Really delete #{applicant_review}?", 'data-method': :delete)
|
27
|
+
else
|
28
|
+
dropdown_link_to('Show', effective_memberships.applicant_applicant_review_path(applicant, applicant_review), 'data-turbolinks' => false)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
collection do
|
34
|
+
# These ones have already been submitted by this reviewer
|
35
|
+
submitted = EffectiveMemberships.ApplicantReview.deep.submitted.where(reviewer: current_user).select('applicant_id')
|
36
|
+
|
37
|
+
EffectiveMemberships.Applicant.deep.reviewable.where.not(id: submitted)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -4,11 +4,21 @@ module Effective
|
|
4
4
|
include EffectiveMailer
|
5
5
|
include EffectiveEmailTemplatesMailer if EffectiveMemberships.use_effective_email_templates
|
6
6
|
|
7
|
+
def applicant_submitted(resource, opts = {})
|
8
|
+
@assigns = assigns_for(resource)
|
9
|
+
@applicant = resource
|
10
|
+
|
11
|
+
subject = subject_for(__method__, "Applicant Submitted - #{resource}", resource, opts)
|
12
|
+
headers = headers_for(resource, opts)
|
13
|
+
|
14
|
+
mail(to: resource.user.email, subject: subject, **headers)
|
15
|
+
end
|
16
|
+
|
7
17
|
def applicant_completed(resource, opts = {})
|
8
18
|
@assigns = assigns_for(resource)
|
9
19
|
@applicant = resource
|
10
20
|
|
11
|
-
subject = subject_for(__method__,
|
21
|
+
subject = subject_for(__method__, "Applicant Completed - #{resource}", resource, opts)
|
12
22
|
headers = headers_for(resource, opts)
|
13
23
|
|
14
24
|
mail(to: resource.user.email, subject: subject, **headers)
|
@@ -18,7 +28,7 @@ module Effective
|
|
18
28
|
@assigns = assigns_for(resource)
|
19
29
|
@applicant = resource
|
20
30
|
|
21
|
-
subject = subject_for(__method__,
|
31
|
+
subject = subject_for(__method__, "Applicant Missing Info - #{resource}", resource, opts)
|
22
32
|
headers = headers_for(resource, opts)
|
23
33
|
|
24
34
|
mail(to: resource.user.email, subject: subject, **headers)
|
@@ -28,7 +38,7 @@ module Effective
|
|
28
38
|
@assigns = assigns_for(resource)
|
29
39
|
@applicant = resource
|
30
40
|
|
31
|
-
subject = subject_for(__method__,
|
41
|
+
subject = subject_for(__method__, "Applicant Approved - #{resource}", resource, opts)
|
32
42
|
headers = headers_for(resource, opts)
|
33
43
|
|
34
44
|
mail(to: resource.user.email, subject: subject, **headers)
|
@@ -38,7 +48,7 @@ module Effective
|
|
38
48
|
@assigns = assigns_for(resource)
|
39
49
|
@applicant = resource
|
40
50
|
|
41
|
-
subject = subject_for(__method__,
|
51
|
+
subject = subject_for(__method__, "Applicant Declined - #{resource}", resource, opts)
|
42
52
|
headers = headers_for(resource, opts)
|
43
53
|
|
44
54
|
mail(to: resource.user.email, subject: subject, **headers)
|
@@ -64,6 +74,17 @@ module Effective
|
|
64
74
|
mail(to: resource.email, subject: subject, **headers)
|
65
75
|
end
|
66
76
|
|
77
|
+
def applicant_review_submitted(resource, opts = {})
|
78
|
+
@assigns = assigns_for(resource)
|
79
|
+
@applicant_review = resource
|
80
|
+
@applicant = resource.applicant
|
81
|
+
|
82
|
+
subject = subject_for(__method__, "Applicant Review Submitted - #{resource.applicant}", resource, opts)
|
83
|
+
headers = headers_for(resource, opts)
|
84
|
+
|
85
|
+
mail(to: mailer_admin, subject: subject, **headers)
|
86
|
+
end
|
87
|
+
|
67
88
|
protected
|
68
89
|
|
69
90
|
def assigns_for(resource)
|
@@ -71,6 +92,10 @@ module Effective
|
|
71
92
|
return applicant_assigns(resource).merge(owner_assigns(resource.owner)).merge(membership_assigns(resource.owner.membership))
|
72
93
|
end
|
73
94
|
|
95
|
+
if resource.class.respond_to?(:effective_memberships_applicant_review?)
|
96
|
+
return applicant_review_assigns(resource).merge(applicant_assigns(resource.applicant)).merge(owner_assigns(resource.applicant.owner)).merge(membership_assigns(resource.applicant.owner.membership))
|
97
|
+
end
|
98
|
+
|
74
99
|
if resource.kind_of?(Effective::ApplicantEndorsement)
|
75
100
|
return endorsement_assigns(resource).merge(owner_assigns(resource.applicant.owner))
|
76
101
|
end
|
@@ -103,6 +128,23 @@ module Effective
|
|
103
128
|
{ applicant: values }
|
104
129
|
end
|
105
130
|
|
131
|
+
def applicant_review_assigns(applicant_review)
|
132
|
+
raise('expected an applicant review') unless applicant_review.class.respond_to?(:effective_memberships_applicant_review?)
|
133
|
+
|
134
|
+
values = {
|
135
|
+
date: (applicant_review.submitted_at || Time.zone.now).strftime('%F'),
|
136
|
+
submitted_at: (applicant_review.submitted_at&.strftime('%F') || 'not submitted'),
|
137
|
+
|
138
|
+
url: effective_memberships.applicant_applicant_review_url(applicant_review.applicant, applicant_review),
|
139
|
+
admin_url: effective_memberships.admin_applicant_review_url(applicant_review),
|
140
|
+
|
141
|
+
recommendation: applicant_review.recommendation.presence,
|
142
|
+
comments: applicant_review.comments.presence
|
143
|
+
}.compact
|
144
|
+
|
145
|
+
{ applicant_review: values }
|
146
|
+
end
|
147
|
+
|
106
148
|
def membership_assigns(membership)
|
107
149
|
return {} if membership.blank?
|
108
150
|
raise('expected a membership') unless membership.kind_of?(Effective::Membership)
|
@@ -149,5 +191,16 @@ module Effective
|
|
149
191
|
{ user: values }
|
150
192
|
end
|
151
193
|
|
194
|
+
def reviewer_assigns(owner)
|
195
|
+
raise('expected a owner') unless owner.class.respond_to?(:effective_memberships_owner?)
|
196
|
+
|
197
|
+
values = {
|
198
|
+
name: owner.to_s,
|
199
|
+
email: owner.email
|
200
|
+
}
|
201
|
+
|
202
|
+
{ reviewer: values }
|
203
|
+
end
|
204
|
+
|
152
205
|
end
|
153
206
|
end
|
@@ -187,6 +187,8 @@ module EffectiveMembershipsApplicant
|
|
187
187
|
|
188
188
|
scope :not_draft, -> { where.not(status: :draft) }
|
189
189
|
|
190
|
+
scope :reviewable, -> { where(status: :completed) }
|
191
|
+
|
190
192
|
scope :for, -> (user) {
|
191
193
|
raise('expected a effective memberships user') unless user.class.try(:effective_memberships_user?)
|
192
194
|
where(user: user).or(where(organization: user.organizations))
|
@@ -488,6 +490,8 @@ module EffectiveMembershipsApplicant
|
|
488
490
|
stamps.each { |stamp| stamp.submit! }
|
489
491
|
|
490
492
|
after_commit do
|
493
|
+
send_email(:applicant_submitted)
|
494
|
+
|
491
495
|
applicant_endorsements.each { |endorsement| endorsement.notify! if endorsement.submitted? }
|
492
496
|
applicant_references.each { |reference| reference.notify! if reference.submitted? }
|
493
497
|
end
|
@@ -569,7 +573,7 @@ module EffectiveMembershipsApplicant
|
|
569
573
|
when 'reviewed'
|
570
574
|
"This application has been reviewed and is now ready for an admin to approve or decline it. If approved, prorated fees will be generated."
|
571
575
|
when 'approved'
|
572
|
-
"The application has been approved
|
576
|
+
"The application has been approved. All done."
|
573
577
|
when 'declined'
|
574
578
|
"This application has been declined."
|
575
579
|
else
|
@@ -750,6 +754,10 @@ module EffectiveMembershipsApplicant
|
|
750
754
|
end
|
751
755
|
|
752
756
|
# Completed -> Reviewed requirements
|
757
|
+
def reviewable?
|
758
|
+
completed?
|
759
|
+
end
|
760
|
+
|
753
761
|
def applicant_reviews_required?
|
754
762
|
(min_applicant_reviews > 0 || applicant_reviews.present?)
|
755
763
|
end
|
@@ -758,6 +766,16 @@ module EffectiveMembershipsApplicant
|
|
758
766
|
category&.min_applicant_reviews.to_i
|
759
767
|
end
|
760
768
|
|
769
|
+
# Find
|
770
|
+
def applicant_review(reviewer:)
|
771
|
+
applicant_reviews.find { |ar| ar.reviewer_id == reviewer.id && ar.reviewer_type == reviewer.class.name }
|
772
|
+
end
|
773
|
+
|
774
|
+
# Find or build
|
775
|
+
def build_applicant_review(reviewer:)
|
776
|
+
applicant_review(reviewer: reviewer) || applicant_reviews.build(reviewer: reviewer)
|
777
|
+
end
|
778
|
+
|
761
779
|
# When an application is completed, these must be done to go to reviewed
|
762
780
|
# An Admin can override this and just set them to reviewed.
|
763
781
|
def reviewed_requirements
|
@@ -765,17 +783,22 @@ module EffectiveMembershipsApplicant
|
|
765
783
|
return requirements unless category.present?
|
766
784
|
|
767
785
|
if applicant_reviews_required?
|
768
|
-
requirements['Applicant Reviews'] = (applicant_reviews.count(&:
|
786
|
+
requirements['Applicant Reviews'] = (applicant_reviews.count(&:done?) >= min_applicant_reviews)
|
769
787
|
end
|
770
788
|
|
771
789
|
requirements
|
772
790
|
end
|
773
791
|
|
792
|
+
# Called when an applicant_review was submitted
|
793
|
+
def try_reviewed!
|
794
|
+
return false unless completed? && reviewed_requirements.values.all?
|
795
|
+
reviewed!
|
796
|
+
end
|
797
|
+
|
774
798
|
def review!
|
775
799
|
raise('applicant must have been submitted to review!') unless was_submitted?
|
776
800
|
|
777
801
|
# Let an admin ignore these requirements if need be
|
778
|
-
# return false unless completed? && reviewed_requirements.values.all?
|
779
802
|
reviewed!
|
780
803
|
end
|
781
804
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# EffectiveMembershipsApplicantReview
|
4
4
|
#
|
5
|
-
# Mark your
|
5
|
+
# Mark your owner model with effective_memberships_applicant_review to get all the includes
|
6
6
|
|
7
7
|
module EffectiveMembershipsApplicantReview
|
8
8
|
extend ActiveSupport::Concern
|
@@ -22,82 +22,60 @@ module EffectiveMembershipsApplicantReview
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def recommendations
|
25
|
-
['
|
25
|
+
['Recommend Approve', 'Recommend Decline', 'Missing Information']
|
26
26
|
end
|
27
27
|
|
28
28
|
end
|
29
29
|
|
30
30
|
included do
|
31
|
-
log_changes if respond_to?(:log_changes)
|
31
|
+
log_changes(to: :applicant) if respond_to?(:log_changes)
|
32
32
|
|
33
33
|
acts_as_tokened
|
34
34
|
|
35
35
|
acts_as_statused(
|
36
|
-
:draft,
|
37
|
-
:
|
38
|
-
:accepted, # Accepted
|
39
|
-
:rejected # Rejected
|
36
|
+
:draft, # Just Started
|
37
|
+
:submitted # All Done
|
40
38
|
)
|
41
39
|
|
42
40
|
acts_as_wizard(
|
43
41
|
start: 'Start',
|
44
|
-
|
45
|
-
education: 'Education',
|
46
|
-
course_amounts: 'Courses',
|
47
|
-
experience: 'Work Experience',
|
48
|
-
references: 'References',
|
49
|
-
files: 'Attach Files',
|
50
|
-
declarations: 'Declarations',
|
42
|
+
review: 'Review Applicant',
|
51
43
|
recommendation: 'Recommendation',
|
52
44
|
submitted: 'Submitted'
|
53
45
|
)
|
54
46
|
|
47
|
+
scope :in_progress, -> { where(status: :draft) }
|
48
|
+
scope :done, -> { where(status: :submitted) }
|
49
|
+
|
55
50
|
belongs_to :applicant
|
56
51
|
belongs_to :reviewer, polymorphic: true
|
57
52
|
|
58
53
|
effective_resource do
|
59
54
|
submitted_at :datetime
|
60
|
-
recommendation :string
|
61
55
|
|
56
|
+
recommendation :string
|
62
57
|
comments :text # Rolling comments
|
63
58
|
|
64
|
-
# Conflict Step
|
65
|
-
conflict_of_interest :boolean
|
66
|
-
|
67
|
-
# Education Step
|
68
|
-
education_accepted :boolean
|
69
|
-
|
70
|
-
# Course Amounts
|
71
|
-
course_amounts_accepted :boolean
|
72
|
-
|
73
|
-
# Courses
|
74
|
-
courses_accepted :boolean
|
75
|
-
|
76
|
-
# Experience Step
|
77
|
-
experience_accepted :boolean
|
78
|
-
|
79
|
-
# References Step
|
80
|
-
references_accepted :boolean
|
81
|
-
|
82
|
-
# References Step
|
83
|
-
files_accepted :boolean
|
84
|
-
|
85
59
|
timestamps
|
86
60
|
end
|
87
61
|
|
88
|
-
scope :deep, -> { includes(:reviewer, applicant: :
|
62
|
+
scope :deep, -> { includes(:reviewer, applicant: :user) }
|
89
63
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
64
|
+
scope :for, -> (user) {
|
65
|
+
raise('expected a effective memberships user') unless user.class.try(:effective_memberships_user?)
|
66
|
+
where(reviewer: user)
|
67
|
+
}
|
94
68
|
|
95
|
-
|
69
|
+
with_options(if: -> { submitted? }) do
|
70
|
+
validates :recommendation, presence: true
|
96
71
|
|
97
|
-
|
72
|
+
validate do
|
73
|
+
self.errors.add(:recommendation, 'is invalid') unless EffectiveMemberships.ApplicantReview.recommendations.include?(recommendation)
|
74
|
+
end
|
75
|
+
end
|
98
76
|
|
99
77
|
def to_s
|
100
|
-
|
78
|
+
"Review of #{applicant}"
|
101
79
|
end
|
102
80
|
|
103
81
|
def in_progress?
|
@@ -105,43 +83,31 @@ module EffectiveMembershipsApplicantReview
|
|
105
83
|
end
|
106
84
|
|
107
85
|
def done?
|
108
|
-
|
86
|
+
submitted?
|
109
87
|
end
|
110
88
|
|
111
|
-
def
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
applicant.save!
|
116
|
-
|
117
|
-
after_commit { send_email(:applicant_review_conflict_of_interest) }
|
118
|
-
true
|
89
|
+
def can_visit_step?(step)
|
90
|
+
return (step == :submitted) if was_submitted?
|
91
|
+
can_revisit_completed_steps(step)
|
119
92
|
end
|
120
93
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
applicant.save!
|
126
|
-
|
127
|
-
after_commit { send_email(:applicant_review_completed) }
|
128
|
-
true
|
94
|
+
# Called by the recommendation wizard step
|
95
|
+
def recommendation!
|
96
|
+
submit!
|
129
97
|
end
|
130
98
|
|
131
|
-
def
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
applicant.save!
|
99
|
+
def submit!
|
100
|
+
submitted!
|
101
|
+
applicant.try_reviewed!
|
136
102
|
|
137
|
-
after_commit { send_email(:
|
103
|
+
after_commit { send_email(:applicant_review_submitted) }
|
138
104
|
true
|
139
105
|
end
|
140
106
|
|
141
107
|
private
|
142
108
|
|
143
109
|
def send_email(email)
|
144
|
-
EffectiveMemberships.send_email(email, self
|
110
|
+
EffectiveMemberships.send_email(email, self)
|
145
111
|
end
|
146
112
|
|
147
113
|
end
|
@@ -43,6 +43,11 @@ module EffectiveMembershipsOwner
|
|
43
43
|
without_role(:member).where(id: removed.select(:owner_id))
|
44
44
|
}
|
45
45
|
|
46
|
+
scope :membership_good_standing, -> {
|
47
|
+
bad_standing = Effective::Membership.where(bad_standing: false).where(owner_type: name)
|
48
|
+
where(id: bad_standing.select(:owner_id))
|
49
|
+
}
|
50
|
+
|
46
51
|
scope :membership_bad_standing, -> {
|
47
52
|
bad_standing = Effective::Membership.where(bad_standing: true).where(owner_type: name)
|
48
53
|
where(id: bad_standing.select(:owner_id))
|
@@ -71,6 +76,20 @@ module EffectiveMembershipsOwner
|
|
71
76
|
where(id: memberships.where(owner_type: name).select(:owner_id))
|
72
77
|
}
|
73
78
|
|
79
|
+
scope :membership_joined_before, -> (date) {
|
80
|
+
raise('expected a date') unless date.respond_to?(:strftime)
|
81
|
+
|
82
|
+
joined_before = Effective::Membership.joined_before(date).where(owner_type: name)
|
83
|
+
where(id: joined_before.select(:owner_id))
|
84
|
+
}
|
85
|
+
|
86
|
+
scope :membership_joined_after, -> (date) {
|
87
|
+
raise('expected a date') unless date.respond_to?(:strftime)
|
88
|
+
|
89
|
+
joined_after = Effective::Membership.joined_after(date).where(owner_type: name)
|
90
|
+
where(id: joined_after.select(:owner_id))
|
91
|
+
}
|
92
|
+
|
74
93
|
end
|
75
94
|
|
76
95
|
def assign_member_role
|
@@ -21,6 +21,7 @@ module EffectiveMembershipsUser
|
|
21
21
|
# App scoped
|
22
22
|
has_many :applicants, -> { order(:id) }, inverse_of: :user, as: :user
|
23
23
|
has_many :fee_payments, -> { order(:id) }, inverse_of: :user, as: :user
|
24
|
+
has_many :applicant_reviews, -> { order(:id) }, inverse_of: :reviewer, as: :reviewer
|
24
25
|
|
25
26
|
# Effective Scoped
|
26
27
|
has_many :representatives, -> { Effective::Representative.sorted },
|
@@ -85,4 +86,9 @@ module EffectiveMembershipsUser
|
|
85
86
|
membership_history_on(date).try(:membership_categories)
|
86
87
|
end
|
87
88
|
|
89
|
+
# Roles
|
90
|
+
def applicant_reviewer?
|
91
|
+
false
|
92
|
+
end
|
93
|
+
|
88
94
|
end
|
@@ -36,6 +36,9 @@ module Effective
|
|
36
36
|
|
37
37
|
scope :good_standing, -> { where(bad_standing: [nil, false]) }
|
38
38
|
|
39
|
+
scope :joined_before, -> (date) { where(arel_table[:joined_on].lt(date)) }
|
40
|
+
scope :joined_after, -> (date) { where(arel_table[:joined_on].gteq(date)) }
|
41
|
+
|
39
42
|
scope :with_paid_fees_through, -> (period = nil) {
|
40
43
|
where(arel_table[:fees_paid_period].gteq(period || EffectiveMemberships.Registrar.current_period))
|
41
44
|
}
|
@@ -0,0 +1 @@
|
|
1
|
+
= render 'effective/applicant_reviews/applicant_review', applicant_review: applicant_review
|
@@ -17,6 +17,10 @@
|
|
17
17
|
= render 'effective/applicants/applicant', applicant: applicant, namespace: :admin
|
18
18
|
|
19
19
|
- # Just normal tabs now
|
20
|
+
- if applicant.applicant_reviews.present?
|
21
|
+
= tab 'Reviews' do
|
22
|
+
.mb-4= render_inline_datatable(Admin::EffectiveApplicantReviewsDatatable.new(applicant: applicant))
|
23
|
+
|
20
24
|
- if applicant.was_submitted? && !applicant.was_approved?
|
21
25
|
= tab 'Process' do
|
22
26
|
= render 'admin/applicants/form_process', applicant: applicant
|
@@ -107,10 +107,10 @@
|
|
107
107
|
- if applicant.reviewed_requirements['Applicant Reviews']
|
108
108
|
= icon('check', class: 'small-1')
|
109
109
|
|
110
|
-
= applicant.applicant_reviews.count(&:
|
110
|
+
= applicant.applicant_reviews.count(&:submitted?)
|
111
111
|
= '/'
|
112
112
|
= applicant.min_applicant_reviews
|
113
|
-
Reviews
|
113
|
+
Reviews Submitted
|
114
114
|
- else
|
115
115
|
%p
|
116
116
|
= icon('check', class: 'small-1')
|
@@ -1,2 +1,2 @@
|
|
1
1
|
= card do
|
2
|
-
%p Thank you
|
2
|
+
%p Thank you. The confidential endorsement has been completed.
|
@@ -1,2 +1,2 @@
|
|
1
1
|
= card do
|
2
|
-
%p Thank you
|
2
|
+
%p Thank you. The confidential reference has been completed.
|
@@ -0,0 +1,8 @@
|
|
1
|
+
- all_steps_content = resource.applicant&.category&.rich_text_applicant_review_all_steps_content
|
2
|
+
- step_content = resource.applicant&.category&.send("rich_text_applicant_review_#{step}_content")
|
3
|
+
|
4
|
+
- if all_steps_content.present?
|
5
|
+
.mb-2= all_steps_content
|
6
|
+
|
7
|
+
- if step_content.present?
|
8
|
+
.mb-2= step_content
|
@@ -0,0 +1,36 @@
|
|
1
|
+
- authorized = EffectiveResources.authorized?(self, :new, EffectiveMemberships.ApplicantReview)
|
2
|
+
|
3
|
+
- available = EffectiveResources.best('EffectiveAvailableApplicantReviewsDatatable').new(self)
|
4
|
+
- datatable = EffectiveResources.best('EffectiveApplicantReviewsDatatable').new(self)
|
5
|
+
|
6
|
+
- # In progress
|
7
|
+
- applicant_review = current_user.applicant_reviews.in_progress.first
|
8
|
+
|
9
|
+
- if applicant_review.present? && applicant_review.draft?
|
10
|
+
%h2 In-Progress Applicant Review
|
11
|
+
|
12
|
+
%p
|
13
|
+
Your review of #{applicant_review} is incomplete.
|
14
|
+
|
15
|
+
%p
|
16
|
+
Please
|
17
|
+
= link_to("Continue review", effective_memberships.applicant_applicant_review_build_path(applicant_review.applicant, applicant_review, applicant_review.next_step), 'data-turbolinks' => false, class: 'btn btn-primary')
|
18
|
+
or you can
|
19
|
+
= link_to('Abandon review', effective_memberships.applicant_applicant_review_path(applicant_review.applicant, applicant_review), 'data-confirm': "Really delete #{applicant_review}?", 'data-method': :delete, class: 'btn btn-danger')
|
20
|
+
to review again.
|
21
|
+
|
22
|
+
%hr
|
23
|
+
|
24
|
+
%h2 Applicant Reviews
|
25
|
+
|
26
|
+
- if available.present?
|
27
|
+
%p The following applicants are available for your review:
|
28
|
+
|
29
|
+
= render_simple_datatable(available)
|
30
|
+
- else
|
31
|
+
%p There are no applicants available for your review. When there are, we'll show them here.
|
32
|
+
|
33
|
+
- if datatable.present?
|
34
|
+
.mt-4
|
35
|
+
%h2 Review History
|
36
|
+
= render_datatable(datatable, simple: true)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
= wizard_card(applicant_review) do
|
2
|
+
%table.table
|
3
|
+
- if request.path.start_with?('/admin')
|
4
|
+
%tr
|
5
|
+
%th Reviewer
|
6
|
+
%td
|
7
|
+
- url = (polymorphic_admin_path(applicant_review.reviewer) rescue "/admin/users/#{applicant_review.reviewer.to_param}/edit")
|
8
|
+
= link_to(applicant_review.reviewer, url)
|
9
|
+
- else
|
10
|
+
%tr
|
11
|
+
%th Reviewer
|
12
|
+
%td
|
13
|
+
= applicant_review.reviewer.to_s
|
14
|
+
%br
|
15
|
+
= mail_to(applicant_review.reviewer.email)
|
16
|
+
|
17
|
+
%tr
|
18
|
+
%th Recommendation
|
19
|
+
%td= applicant_review.recommendation
|
20
|
+
|
21
|
+
%tr
|
22
|
+
%th Comments
|
23
|
+
%td= simple_format(applicant_review.comments.presence || 'none')
|
@@ -0,0 +1 @@
|
|
1
|
+
- # Intentionally blank
|
@@ -0,0 +1,20 @@
|
|
1
|
+
= render 'layout' do
|
2
|
+
= render 'effective/applicant_reviews/content', resource: resource
|
3
|
+
|
4
|
+
= card('Summary') do
|
5
|
+
= render 'effective/applicants/summary', applicant: resource.applicant
|
6
|
+
|
7
|
+
.mb-2
|
8
|
+
= collapse('Show application...', card_class: 'my-2') do
|
9
|
+
= render 'effective/applicants/applicant', applicant: resource.applicant
|
10
|
+
|
11
|
+
= card('Recommendation') do
|
12
|
+
|
13
|
+
= effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
|
14
|
+
= f.hidden_field :id
|
15
|
+
|
16
|
+
%p My recommendation is
|
17
|
+
= f.radios :recommendation, EffectiveMemberships.ApplicantReview.recommendations, label: false
|
18
|
+
= f.text_area :comments, label: 'Reviewer comments'
|
19
|
+
|
20
|
+
= f.save 'Submit Recommendation'
|
@@ -0,0 +1,14 @@
|
|
1
|
+
= render 'layout' do
|
2
|
+
= render 'effective/applicant_reviews/content', resource: resource
|
3
|
+
|
4
|
+
= card do
|
5
|
+
= render 'effective/applicants/summary', applicant: resource.applicant
|
6
|
+
= render 'effective/applicants/applicant', applicant: resource.applicant
|
7
|
+
|
8
|
+
= card do
|
9
|
+
= effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
|
10
|
+
= f.hidden_field :id
|
11
|
+
|
12
|
+
= f.text_area :comments, label: 'Reviewer comments'
|
13
|
+
|
14
|
+
= f.save 'Save and Continue'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
= render 'layout' do
|
2
|
+
= render 'effective/applicant_reviews/content', resource: resource
|
3
|
+
|
4
|
+
= card do
|
5
|
+
%p Welcome #{current_user}
|
6
|
+
|
7
|
+
%p You are starting an applicant review for #{resource.applicant}.
|
8
|
+
|
9
|
+
= effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
|
10
|
+
= f.hidden_field :id
|
11
|
+
|
12
|
+
= f.hidden_field :applicant_type, value: f.object.applicant.class.name
|
13
|
+
= f.hidden_field :applicant_id
|
14
|
+
|
15
|
+
= f.hidden_field :reviewer_type
|
16
|
+
= f.hidden_field :reviewer_id
|
17
|
+
|
18
|
+
= f.save 'Save and Continue'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
= render 'layout' do
|
2
|
+
= render 'effective/applicant_reviews/content', resource: resource
|
3
|
+
|
4
|
+
- raise('expected a submitted applicant review') unless resource.was_submitted?
|
5
|
+
|
6
|
+
.alert.alert-success.mb-4
|
7
|
+
This review was submitted on #{resource.submitted_at.strftime('%F')}
|
8
|
+
|
9
|
+
.mb-4
|
10
|
+
= collapse('Show application...', card_class: 'my-2') do
|
11
|
+
= render 'effective/applicants/applicant', applicant: resource.applicant
|
12
|
+
|
13
|
+
= render 'effective/applicant_reviews/applicant_review', applicant_review: resource
|
14
|
+
|
15
|
+
= link_to "Return to Dashboard", root_path, class: 'btn btn-lg btn-primary btn-block'
|
@@ -54,11 +54,12 @@
|
|
54
54
|
%th Stream
|
55
55
|
%td= applicant.stream
|
56
56
|
|
57
|
-
-
|
57
|
+
- orders = applicant.orders.select { |order| EffectiveResources.authorized?(self, :show, order) }
|
58
|
+
- if orders.present?
|
58
59
|
%tr
|
59
|
-
%th Order#{'s' if
|
60
|
+
%th Order#{'s' if orders.length > 1}
|
60
61
|
%td
|
61
|
-
-
|
62
|
+
- orders.each do |order|
|
62
63
|
- if request.path.start_with?('/admin')
|
63
64
|
= link_to(order, effective_orders.edit_admin_order_path(order))
|
64
65
|
- else
|
@@ -1,10 +1,10 @@
|
|
1
1
|
---
|
2
|
-
subject: 'Your application was approved
|
2
|
+
subject: 'Your application was approved'
|
3
3
|
from: 'admin@example.com'
|
4
4
|
---
|
5
5
|
Hello {{ user.name }},
|
6
6
|
|
7
|
-
Your application for {{ applicant.to_category }} was approved
|
7
|
+
Your application for {{ applicant.to_category }} was approved.
|
8
8
|
|
9
9
|
Please log in and purchase any outstanding fees.
|
10
10
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
---
|
2
|
+
subject: 'An applicant review has been submitted'
|
3
|
+
from: 'admin@example.com'
|
4
|
+
---
|
5
|
+
Hello admin,
|
6
|
+
|
7
|
+
The application for {{ user.name }} for {{ applicant.to_category }} has been reviewed by #{{ reviewer.name }}
|
8
|
+
|
9
|
+
They recommended {{ applicant_review.recommendation }} with the following comments:
|
10
|
+
|
11
|
+
{{ applicant_review.comments }}
|
12
|
+
|
13
|
+
For more details please visit
|
14
|
+
|
15
|
+
{{ applicant.admin_url }}
|
16
|
+
|
17
|
+
Thanks,
|
18
|
+
|
19
|
+
Have a great day
|
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
subject: 'Your application was successfully submitted'
|
3
|
+
from: 'admin@example.com'
|
4
|
+
---
|
5
|
+
Hello {{ user.name }},
|
6
|
+
|
7
|
+
We have received your application for {{ applicant.to_category }} #{{ applicant.to_status }}
|
8
|
+
|
9
|
+
For more details please visit
|
10
|
+
|
11
|
+
{{ applicant.url }}
|
12
|
+
|
13
|
+
Thank you.
|
14
|
+
|
15
|
+
Please contact us for assistance.
|
data/config/routes.rb
CHANGED
@@ -7,6 +7,10 @@ EffectiveMemberships::Engine.routes.draw do
|
|
7
7
|
scope module: 'effective' do
|
8
8
|
resources :applicants, only: [:new, :show, :destroy] do
|
9
9
|
resources :build, controller: :applicants, only: [:show, :update]
|
10
|
+
|
11
|
+
resources :applicant_reviews, only: [:new, :show, :destroy] do
|
12
|
+
resources :build, controller: :applicant_reviews, only: [:show, :update]
|
13
|
+
end
|
10
14
|
end
|
11
15
|
|
12
16
|
resources :applicant_endorsements, only: [:new, :create, :show, :update] do
|
@@ -45,6 +49,8 @@ EffectiveMemberships::Engine.routes.draw do
|
|
45
49
|
post :notify, on: :member
|
46
50
|
end
|
47
51
|
|
52
|
+
resources :applicant_reviews, only: [:index, :show]
|
53
|
+
|
48
54
|
resources :fees
|
49
55
|
resources :categories, only: [:index, :edit, :update]
|
50
56
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: effective_memberships
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Code and Effect
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-09-
|
11
|
+
date: 2022-09-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -258,6 +258,7 @@ files:
|
|
258
258
|
- app/controllers/admin/applicant_course_names_controller.rb
|
259
259
|
- app/controllers/admin/applicant_endorsements_controller.rb
|
260
260
|
- app/controllers/admin/applicant_references_controller.rb
|
261
|
+
- app/controllers/admin/applicant_reviews_controller.rb
|
261
262
|
- app/controllers/admin/applicants_controller.rb
|
262
263
|
- app/controllers/admin/categories_controller.rb
|
263
264
|
- app/controllers/admin/fee_payments_controller.rb
|
@@ -270,6 +271,7 @@ files:
|
|
270
271
|
- app/controllers/admin/statuses_controller.rb
|
271
272
|
- app/controllers/effective/applicant_endorsements_controller.rb
|
272
273
|
- app/controllers/effective/applicant_references_controller.rb
|
274
|
+
- app/controllers/effective/applicant_reviews_controller.rb
|
273
275
|
- app/controllers/effective/applicants_controller.rb
|
274
276
|
- app/controllers/effective/fee_payments_controller.rb
|
275
277
|
- app/controllers/effective/membership_cards_controller.rb
|
@@ -280,6 +282,7 @@ files:
|
|
280
282
|
- app/datatables/admin/effective_applicant_course_names_datatable.rb
|
281
283
|
- app/datatables/admin/effective_applicant_endorsements_datatable.rb
|
282
284
|
- app/datatables/admin/effective_applicant_references_datatable.rb
|
285
|
+
- app/datatables/admin/effective_applicant_reviews_datatable.rb
|
283
286
|
- app/datatables/admin/effective_applicants_datatable.rb
|
284
287
|
- app/datatables/admin/effective_categories_datatable.rb
|
285
288
|
- app/datatables/admin/effective_fee_payments_datatable.rb
|
@@ -296,7 +299,9 @@ files:
|
|
296
299
|
- app/datatables/effective_applicant_equivalences_datatable.rb
|
297
300
|
- app/datatables/effective_applicant_experiences_datatable.rb
|
298
301
|
- app/datatables/effective_applicant_references_datatable.rb
|
302
|
+
- app/datatables/effective_applicant_reviews_datatable.rb
|
299
303
|
- app/datatables/effective_applicants_datatable.rb
|
304
|
+
- app/datatables/effective_available_applicant_reviews_datatable.rb
|
300
305
|
- app/datatables/effective_fee_payments_datatable.rb
|
301
306
|
- app/datatables/effective_memberships_directory_datatable.rb
|
302
307
|
- app/datatables/effective_organizations_datatable.rb
|
@@ -342,6 +347,7 @@ files:
|
|
342
347
|
- app/views/admin/applicant_course_name/_form.html.haml
|
343
348
|
- app/views/admin/applicant_endorsements/_applicant_endorsement.html.haml
|
344
349
|
- app/views/admin/applicant_references/_applicant_reference.html.haml
|
350
|
+
- app/views/admin/applicant_reviews/_applicant_review.html.haml
|
345
351
|
- app/views/admin/applicants/_form.html.haml
|
346
352
|
- app/views/admin/applicants/_form_applicant.html.haml
|
347
353
|
- app/views/admin/applicants/_form_approve.html.haml
|
@@ -395,6 +401,16 @@ files:
|
|
395
401
|
- app/views/effective/applicant_references/_form_declaration.html.haml
|
396
402
|
- app/views/effective/applicant_references/complete.html.haml
|
397
403
|
- app/views/effective/applicant_references/edit.html.haml
|
404
|
+
- app/views/effective/applicant_reviews/_applicant_review.html.haml
|
405
|
+
- app/views/effective/applicant_reviews/_content.html.haml
|
406
|
+
- app/views/effective/applicant_reviews/_dashboard.html.haml
|
407
|
+
- app/views/effective/applicant_reviews/_layout.html.haml
|
408
|
+
- app/views/effective/applicant_reviews/_recommendation.html.haml
|
409
|
+
- app/views/effective/applicant_reviews/_review.html.haml
|
410
|
+
- app/views/effective/applicant_reviews/recommendation.html.haml
|
411
|
+
- app/views/effective/applicant_reviews/review.html.haml
|
412
|
+
- app/views/effective/applicant_reviews/start.html.haml
|
413
|
+
- app/views/effective/applicant_reviews/submitted.html.haml
|
398
414
|
- app/views/effective/applicants/_applicant.html.haml
|
399
415
|
- app/views/effective/applicants/_content.html.haml
|
400
416
|
- app/views/effective/applicants/_course_amounts.html.haml
|
@@ -470,6 +486,8 @@ files:
|
|
470
486
|
- app/views/effective/memberships_mailer/applicant_endorsement_notification.liquid
|
471
487
|
- app/views/effective/memberships_mailer/applicant_missing_info.liquid
|
472
488
|
- app/views/effective/memberships_mailer/applicant_reference_notification.liquid
|
489
|
+
- app/views/effective/memberships_mailer/applicant_review_submitted.liquid
|
490
|
+
- app/views/effective/memberships_mailer/applicant_submitted.liquid
|
473
491
|
- app/views/effective/organizations/_dashboard.html.haml
|
474
492
|
- app/views/effective/organizations/_form.html.haml
|
475
493
|
- app/views/effective/organizations/_form_organization.html.haml
|