effective_memberships 0.6.13 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|