effective_memberships 0.4.9 → 0.4.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/effective_memberships/applicant_experiences.js +0 -5
- data/app/controllers/admin/applicant_endorsements_controller.rb +9 -0
- data/app/controllers/effective/applicant_endorsements_controller.rb +48 -0
- data/app/controllers/effective/applicant_references_controller.rb +1 -1
- data/app/datatables/admin/effective_applicant_endorsements_datatable.rb +35 -0
- data/app/datatables/effective_applicant_endorsements_datatable.rb +37 -0
- data/app/datatables/effective_applicant_references_datatable.rb +1 -1
- data/app/mailers/effective/memberships_mailer.rb +25 -0
- data/app/models/concerns/effective_memberships_applicant.rb +34 -6
- data/app/models/concerns/effective_memberships_fee_payment.rb +0 -1
- data/app/models/effective/applicant_endorsement.rb +100 -0
- data/app/views/admin/applicant_endorsements/_applicant_endorsement.html.haml +1 -0
- data/app/views/admin/applicants/_form.html.haml +4 -0
- data/app/views/admin/categories/_form_applicant_steps.html.haml +6 -0
- data/app/views/effective/applicant_endorsements/_applicant_endorsement.html.haml +46 -0
- data/app/views/effective/applicant_endorsements/_datatable_actions.html.haml +4 -0
- data/app/views/effective/applicant_endorsements/_form.html.haml +9 -0
- data/app/views/effective/applicant_endorsements/_form_declaration.html.haml +22 -0
- data/app/views/effective/applicant_endorsements/complete.html.haml +3 -0
- data/app/views/effective/applicant_endorsements/edit.html.haml +8 -0
- data/app/views/effective/applicants/_endorsements.html.haml +15 -0
- data/app/views/effective/applicants/endorsements.html.haml +25 -0
- data/app/views/effective/applicants/experience.html.haml +1 -1
- data/app/views/effective/applicants/select.html.haml +6 -1
- data/app/views/effective/memberships_mailer/applicant_endorsement_notification.liquid +15 -0
- data/config/effective_memberships.rb +3 -0
- data/config/routes.rb +9 -0
- data/db/migrate/01_create_effective_memberships.rb.erb +30 -0
- data/lib/effective_memberships/version.rb +1 -1
- data/lib/effective_memberships.rb +1 -1
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7d09ef68f61058a8ee29567caa3a008d887482edab0fa3e8d251ea69f9ea3cb
|
4
|
+
data.tar.gz: 10a88e200464a6d2d7dbb8fbdcaa3aed77337b067e12fc3577b31a8032cbd2ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3adbdb1ab5f18df509ebc6631f39a142f2857213ce9ee0a23b83eec2fa44dda3d8b77c42afe7e4e4b494bb938dac7aee672cc26dacbd398fd9f34cc3c55ed222
|
7
|
+
data.tar.gz: ab80c7ff8b01736429ecabc774b81c45702d21ae521e2e7db276191802fa212171f8e81e448a06e452fb97745c5aec3723fe37537669f3a3ed87b9cd30d03746
|
@@ -31,8 +31,3 @@ $(document).on('change dp.change keyup', "[data-applicant-experiences-month]", f
|
|
31
31
|
const $form = $(event.currentTarget).closest('form');
|
32
32
|
sumApplicantExperiences($form);
|
33
33
|
});
|
34
|
-
|
35
|
-
|
36
|
-
// $(document).on 'cocoon:after-remove', (event) ->
|
37
|
-
// $form = $(event.target).closest('form')
|
38
|
-
// sumMonths($form) if $form.find('[data-experience-month]').length > 0
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Admin
|
2
|
+
class ApplicantEndorsementsController < 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
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Effective
|
2
|
+
class ApplicantEndorsementsController < ApplicationController
|
3
|
+
include Effective::CrudController
|
4
|
+
include Effective::Select2AjaxController
|
5
|
+
|
6
|
+
page_title 'Confidential Endorsement Form'
|
7
|
+
|
8
|
+
# The show and update actions are public routes but can only be reached by the token.
|
9
|
+
# The endorser must declare and submit the form
|
10
|
+
# To move an applicant endorsement from submitted to completed
|
11
|
+
|
12
|
+
submit :notify, 'Resend email notification',
|
13
|
+
success: -> { "Sent email notification to #{resource.email}" }
|
14
|
+
|
15
|
+
submit :complete, 'Complete Endorsement'
|
16
|
+
|
17
|
+
def show
|
18
|
+
@applicant_endorsement = ApplicantEndorsement.submitted.find(params[:id])
|
19
|
+
EffectiveResources.authorize!(self, :show, @applicant_endorsement)
|
20
|
+
|
21
|
+
render 'edit'
|
22
|
+
end
|
23
|
+
|
24
|
+
# Must be signed in
|
25
|
+
def select2_ajax_endorser
|
26
|
+
authenticate_user! if defined?(Devise)
|
27
|
+
|
28
|
+
applicant = EffectiveMemberships.Applicant.find(params[:applicant_id])
|
29
|
+
collection = Effective::ApplicantEndorsement.endorser_collection(applicant)
|
30
|
+
|
31
|
+
respond_with_select2_ajax(collection)
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def permitted_params
|
37
|
+
permitted = params.require(:effective_applicant_endorsement).permit!.except(:token, :last_notified_at, :status, :status_steps, :applicant_id, :endorser_id)
|
38
|
+
|
39
|
+
if current_user && current_user.memberships_owners.include?(resource.applicant&.owner)
|
40
|
+
permitted.except(:notes, :accept_declaration)
|
41
|
+
else
|
42
|
+
permitted
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -23,7 +23,7 @@ module Effective
|
|
23
23
|
protected
|
24
24
|
|
25
25
|
def permitted_params
|
26
|
-
permitted = params.require(:effective_applicant_reference).permit!.except(:token, :last_notified_at, :status, :status_steps)
|
26
|
+
permitted = params.require(:effective_applicant_reference).permit!.except(:token, :last_notified_at, :status, :status_steps, :applicant_id)
|
27
27
|
|
28
28
|
if current_user && current_user.memberships_owners.include?(resource.applicant&.owner)
|
29
29
|
permitted.except(:reservations, :reservations_reason, :work_history, :accept_declaration)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Admin
|
2
|
+
class EffectiveApplicantEndorsementsDatatable < Effective::Datatable
|
3
|
+
|
4
|
+
datatable do
|
5
|
+
order :name
|
6
|
+
|
7
|
+
col :applicant
|
8
|
+
|
9
|
+
col :endorser
|
10
|
+
col :email
|
11
|
+
col :name
|
12
|
+
col :phone
|
13
|
+
col :unknown_member, visible: false
|
14
|
+
|
15
|
+
col :status do |endorsement|
|
16
|
+
if endorsement.submitted?
|
17
|
+
'Waiting on response'
|
18
|
+
elsif endorsement.completed?
|
19
|
+
'Completed'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
col :last_notified_at do |endorsement|
|
24
|
+
endorsement.last_notified_at&.strftime('%F') unless endorsement.completed?
|
25
|
+
end
|
26
|
+
|
27
|
+
actions_col
|
28
|
+
end
|
29
|
+
|
30
|
+
collection do
|
31
|
+
Effective::ApplicantEndorsement.deep.all
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# For the applicant complete step to add additional endorsements
|
2
|
+
class EffectiveApplicantEndorsementsDatatable < Effective::Datatable
|
3
|
+
|
4
|
+
datatable do
|
5
|
+
order :name
|
6
|
+
|
7
|
+
col :name do |endorsement|
|
8
|
+
endorser&.to_s || name
|
9
|
+
end
|
10
|
+
|
11
|
+
col :email
|
12
|
+
col :phone
|
13
|
+
|
14
|
+
col :status do |reference|
|
15
|
+
if reference.submitted?
|
16
|
+
'Waiting on response'
|
17
|
+
elsif reference.completed?
|
18
|
+
'Completed'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
col :last_notified_at do |endorsement|
|
23
|
+
endorsement.last_notified_at&.strftime('%F') unless endorsement.completed?
|
24
|
+
end
|
25
|
+
|
26
|
+
actions_col partial: 'effective/applicant_endorsements/datatable_actions', partial_as: :applicant_endorsement
|
27
|
+
end
|
28
|
+
|
29
|
+
collection do
|
30
|
+
Effective::ApplicantEndorsement.deep.where(applicant: applicant)
|
31
|
+
end
|
32
|
+
|
33
|
+
def applicant
|
34
|
+
@applicant ||= EffectiveMemberships.Applicant.where(id: attributes[:applicant_id]).first!
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -44,6 +44,16 @@ module Effective
|
|
44
44
|
mail(to: resource.user.email, subject: subject, **headers)
|
45
45
|
end
|
46
46
|
|
47
|
+
def applicant_endorsement_notification(resource, opts = {})
|
48
|
+
@assigns = assigns_for(resource)
|
49
|
+
@applicant_endorsement = resource
|
50
|
+
|
51
|
+
subject = subject_for(__method__, 'Endorsement Requested', resource, opts)
|
52
|
+
headers = headers_for(resource, opts)
|
53
|
+
|
54
|
+
mail(to: resource.email, subject: subject, **headers)
|
55
|
+
end
|
56
|
+
|
47
57
|
def applicant_reference_notification(resource, opts = {})
|
48
58
|
@assigns = assigns_for(resource)
|
49
59
|
@applicant_reference = resource
|
@@ -61,6 +71,10 @@ module Effective
|
|
61
71
|
return applicant_assigns(resource).merge(owner_assigns(resource.owner))
|
62
72
|
end
|
63
73
|
|
74
|
+
if resource.kind_of?(Effective::ApplicantEndorsement)
|
75
|
+
return endorsement_assigns(resource).merge(owner_assigns(resource.applicant.owner))
|
76
|
+
end
|
77
|
+
|
64
78
|
if resource.kind_of?(Effective::ApplicantReference)
|
65
79
|
return reference_assigns(resource).merge(owner_assigns(resource.applicant.owner))
|
66
80
|
end
|
@@ -88,6 +102,17 @@ module Effective
|
|
88
102
|
{ applicant: values }
|
89
103
|
end
|
90
104
|
|
105
|
+
def endorsement_assigns(applicant_endorsement)
|
106
|
+
raise('expected a endorsement') unless applicant_endorsement.kind_of?(Effective::ApplicantEndorsement)
|
107
|
+
|
108
|
+
values = {
|
109
|
+
name: (applicant_endorsement.endorser&.to_s || applicant_endorsement.name),
|
110
|
+
url: effective_memberships.applicant_endorsement_url(applicant_endorsement)
|
111
|
+
}
|
112
|
+
|
113
|
+
{ endorsement: values }
|
114
|
+
end
|
115
|
+
|
91
116
|
def reference_assigns(applicant_reference)
|
92
117
|
raise('expected a reference') unless applicant_reference.kind_of?(Effective::ApplicantReference)
|
93
118
|
|
@@ -93,6 +93,9 @@ module EffectiveMembershipsApplicant
|
|
93
93
|
has_many :applicant_educations, -> { order(:id) }, class_name: 'Effective::ApplicantEducation', as: :applicant, inverse_of: :applicant, dependent: :destroy
|
94
94
|
accepts_nested_attributes_for :applicant_educations, reject_if: :all_blank, allow_destroy: true
|
95
95
|
|
96
|
+
has_many :applicant_endorsements, -> { order(:id) }, class_name: 'Effective::ApplicantEndorsement', as: :applicant, inverse_of: :applicant, dependent: :destroy
|
97
|
+
accepts_nested_attributes_for :applicant_endorsements, reject_if: :all_blank, allow_destroy: true
|
98
|
+
|
96
99
|
has_many :applicant_experiences, -> { order(:id) }, class_name: 'Effective::ApplicantExperience', as: :applicant, inverse_of: :applicant, dependent: :destroy
|
97
100
|
accepts_nested_attributes_for :applicant_experiences, reject_if: :all_blank, allow_destroy: true
|
98
101
|
|
@@ -238,6 +241,16 @@ module EffectiveMembershipsApplicant
|
|
238
241
|
end
|
239
242
|
end
|
240
243
|
|
244
|
+
# Applicant Endorsements Step
|
245
|
+
with_options(if: -> { current_step == :endorsements }) do
|
246
|
+
validate do
|
247
|
+
required = min_applicant_endorsements()
|
248
|
+
existing = applicant_endorsements().reject(&:marked_for_destruction?).length
|
249
|
+
|
250
|
+
self.errors.add(:applicant_endorsements, "please include #{required} or more endorsements") if existing < required
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
241
254
|
# Applicant References Step
|
242
255
|
with_options(if: -> { current_step == :references }) do
|
243
256
|
validate do
|
@@ -361,6 +374,7 @@ module EffectiveMembershipsApplicant
|
|
361
374
|
stamps.each { |stamp| stamp.submit! }
|
362
375
|
|
363
376
|
after_commit do
|
377
|
+
applicant_endorsements.each { |endorsement| endorsement.notify! if endorsement.submitted? }
|
364
378
|
applicant_references.each { |reference| reference.notify! if reference.submitted? }
|
365
379
|
end
|
366
380
|
|
@@ -521,6 +535,11 @@ module EffectiveMembershipsApplicant
|
|
521
535
|
category&.min_applicant_references.to_i
|
522
536
|
end
|
523
537
|
|
538
|
+
# Endorsements Step
|
539
|
+
def min_applicant_endorsements
|
540
|
+
category&.min_applicant_endorsements.to_i
|
541
|
+
end
|
542
|
+
|
524
543
|
# Files Step
|
525
544
|
def min_applicant_files
|
526
545
|
category&.min_applicant_files.to_i
|
@@ -536,6 +555,10 @@ module EffectiveMembershipsApplicant
|
|
536
555
|
)
|
537
556
|
end
|
538
557
|
|
558
|
+
def applicant_endorsements_required?
|
559
|
+
min_applicant_endorsements > 0
|
560
|
+
end
|
561
|
+
|
539
562
|
def applicant_references_required?
|
540
563
|
min_applicant_references > 0
|
541
564
|
end
|
@@ -543,13 +566,18 @@ module EffectiveMembershipsApplicant
|
|
543
566
|
# When an application is submitted, these must be done to go to completed.
|
544
567
|
# An Admin can override this and just set them to completed.
|
545
568
|
def completed_requirements
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
{}
|
569
|
+
requirements = {}
|
570
|
+
return requirements unless category.present?
|
571
|
+
|
572
|
+
if category.applicant_wizard_steps.include?(:references) || applicant_references_required?
|
573
|
+
requirements['Applicant References'] = (!applicant_references_required? || applicant_references.count(&:completed?) >= min_applicant_references)
|
552
574
|
end
|
575
|
+
|
576
|
+
if category.applicant_wizard_steps.include?(:endorsements) || applicant_endorsements_required?
|
577
|
+
requirements['Applicant Endorsements'] = (!applicant_endorsements_required? || applicant_endorsements.count(&:completed?) >= min_applicant_endorsements)
|
578
|
+
end
|
579
|
+
|
580
|
+
requirements
|
553
581
|
end
|
554
582
|
|
555
583
|
def complete!
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# An ApplicantEndorsement is similar to a reference, except it must be an existing member
|
2
|
+
|
3
|
+
module Effective
|
4
|
+
class ApplicantEndorsement < ActiveRecord::Base
|
5
|
+
acts_as_tokened
|
6
|
+
|
7
|
+
acts_as_statused(
|
8
|
+
:submitted, # Was submitted by the applicant
|
9
|
+
:completed # Was completed by the endorser.
|
10
|
+
)
|
11
|
+
|
12
|
+
log_changes(to: :applicant) if respond_to?(:log_changes)
|
13
|
+
|
14
|
+
belongs_to :applicant, polymorphic: true
|
15
|
+
belongs_to :endorser, polymorphic: true, optional: true
|
16
|
+
|
17
|
+
effective_resource do
|
18
|
+
# These fields are submitted by the applicant
|
19
|
+
# They should try to select an endorser from the dropdown
|
20
|
+
# But we also allow a fallback
|
21
|
+
unknown_member :boolean
|
22
|
+
endorser_email :string
|
23
|
+
name :string
|
24
|
+
phone :string
|
25
|
+
|
26
|
+
# As per acts_as_statused. For tracking the state machine.
|
27
|
+
status :string
|
28
|
+
status_steps :text
|
29
|
+
|
30
|
+
# Endorser Declaration
|
31
|
+
notes :text
|
32
|
+
accept_declaration :boolean
|
33
|
+
|
34
|
+
# Tracking the submission
|
35
|
+
token :string
|
36
|
+
last_notified_at :datetime
|
37
|
+
|
38
|
+
timestamps
|
39
|
+
end
|
40
|
+
|
41
|
+
scope :deep, -> { all }
|
42
|
+
|
43
|
+
# All step validations
|
44
|
+
validates :applicant, presence: true
|
45
|
+
validates :endorser, presence: true, unless: -> { unknown_member? }
|
46
|
+
|
47
|
+
with_options(if: -> { unknown_member? }) do
|
48
|
+
validates :endorser_email, presence: true, email: true
|
49
|
+
validates :name, presence: true
|
50
|
+
validates :phone, presence: true
|
51
|
+
end
|
52
|
+
|
53
|
+
# When being submit by the reference
|
54
|
+
with_options(if: -> { completed? }) do
|
55
|
+
validates :accept_declaration, acceptance: true
|
56
|
+
end
|
57
|
+
|
58
|
+
after_commit(on: :create, if: -> { applicant.was_submitted? }) { notify! }
|
59
|
+
|
60
|
+
def self.endorser_collection(applicant)
|
61
|
+
raise('expected an effective memberships applicant') unless applicant.class.try(:effective_memberships_applicant?)
|
62
|
+
|
63
|
+
collection_method = EffectiveMemberships.applicant_endorsements_endorser_collection()
|
64
|
+
|
65
|
+
if collection_method.blank?
|
66
|
+
return (applicant.user.class.members)
|
67
|
+
end
|
68
|
+
|
69
|
+
collection = instance_exec(applicant, &collection_method)
|
70
|
+
|
71
|
+
unless collection.kind_of?(ActiveRecord::Relation)
|
72
|
+
raise("expected EffectiveMemberships.applicant_endorsements_endorsers_collection to return an ActiveRecord::Relation.")
|
73
|
+
end
|
74
|
+
|
75
|
+
collection
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_s
|
79
|
+
'endorsement'
|
80
|
+
end
|
81
|
+
|
82
|
+
def email
|
83
|
+
endorser&.email || endorser_email
|
84
|
+
end
|
85
|
+
|
86
|
+
def notify!
|
87
|
+
raise('expected endorsement email') unless email.present?
|
88
|
+
|
89
|
+
EffectiveMemberships.send_email(:applicant_endorsement_notification, self)
|
90
|
+
update!(last_notified_at: Time.zone.now)
|
91
|
+
end
|
92
|
+
|
93
|
+
def complete!
|
94
|
+
completed!
|
95
|
+
applicant.save!
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
= render '/effective/applicant_endorsements/applicant_endorsement', applicant_endorsement: applicant_endorsement, skip_actions: true
|
@@ -25,6 +25,10 @@
|
|
25
25
|
= tab 'References' do
|
26
26
|
.mb-4= render_inline_datatable(Admin::EffectiveApplicantReferencesDatatable.new(applicant: applicant))
|
27
27
|
|
28
|
+
- if applicant.applicant_endorsements.present?
|
29
|
+
= tab 'Endorsements' do
|
30
|
+
.mb-4= render_inline_datatable(Admin::EffectiveApplicantEndorsementsDatatable.new(applicant: applicant))
|
31
|
+
|
28
32
|
- if applicant.fees.present? || applicant.orders.present?
|
29
33
|
= tab 'Fees' do
|
30
34
|
.mb-4
|
@@ -32,6 +32,12 @@
|
|
32
32
|
%td= f.number_field :min_applicant_experiences_months, label: false
|
33
33
|
%td minimimum months of work experience
|
34
34
|
|
35
|
+
- if applicant.wizard_step_keys.include?(:endorsements)
|
36
|
+
%tr
|
37
|
+
%td= applicant.wizard_step_title(:endorsements)
|
38
|
+
%td= f.number_field :min_applicant_endorsements, label: false
|
39
|
+
%td minimimum number of endorsements
|
40
|
+
|
35
41
|
- if applicant.wizard_step_keys.include?(:references)
|
36
42
|
%tr
|
37
43
|
%td= applicant.wizard_step_title(:references)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
- endorsement = applicant_endorsement
|
2
|
+
|
3
|
+
%table.table.table-sm
|
4
|
+
%tbody
|
5
|
+
%tr
|
6
|
+
%th Endorser
|
7
|
+
%td
|
8
|
+
- if endorsement.endorser.present?
|
9
|
+
- endorser = endorsement.endorser
|
10
|
+
= link_to("#{endorser.to_s} <#{endorser.email}>", "mailto:#{endorser.email}")
|
11
|
+
- else
|
12
|
+
= link_to("#{endorsement.name} <#{endorsement.email}>", "mailto:#{endorsement.email}")
|
13
|
+
%td
|
14
|
+
|
15
|
+
- if endorsement.phone.present?
|
16
|
+
%tr
|
17
|
+
%th Phone
|
18
|
+
%td= endorsement.phone
|
19
|
+
|
20
|
+
- if endorsement.applicant.was_submitted?
|
21
|
+
%tr
|
22
|
+
%th Status
|
23
|
+
%td
|
24
|
+
- if endorsement.submitted?
|
25
|
+
Waiting on response
|
26
|
+
- elsif endorsement.completed?
|
27
|
+
Response completed
|
28
|
+
|
29
|
+
- unless endorsement.applicant.was_approved?
|
30
|
+
%tr
|
31
|
+
%th Last Notified at
|
32
|
+
%td= endorsement.last_notified_at&.strftime('%F') || 'Never'
|
33
|
+
|
34
|
+
- if can?(:notify, endorsement) && !local_assigns[:skip_actions]
|
35
|
+
%tr
|
36
|
+
%th Notification
|
37
|
+
%td= link_to('Resend email notification', effective_memberships.notify_applicant_endorsement_path(endorsement), 'data-method': :post, 'data-confirm': "Really resend email notification to #{endorsement.email}?")
|
38
|
+
|
39
|
+
- if endorsement.completed? && can?(:review, endorsement)
|
40
|
+
%tr
|
41
|
+
%th Notes
|
42
|
+
%td= endorsement.notes
|
43
|
+
|
44
|
+
%tr
|
45
|
+
%th Endorsed On
|
46
|
+
%td= endorsement.completed_at.strftime('%F')
|
@@ -0,0 +1,4 @@
|
|
1
|
+
= dropdown(variation: :dropleft) do
|
2
|
+
- unless applicant_endorsement.completed?
|
3
|
+
= dropdown_link_to 'Notify', effective_memberships.notify_applicant_endorsement_path(applicant_endorsement),
|
4
|
+
data: { method: :post, confirm: "Really notify #{applicant_endorsement.email}?" }
|
@@ -0,0 +1,9 @@
|
|
1
|
+
= effective_form_with(model: applicant_endorsement, engine: true) do |f|
|
2
|
+
= f.hidden_field :applicant_id
|
3
|
+
= f.hidden_field :applicant_type
|
4
|
+
|
5
|
+
- if f.object.new_record?
|
6
|
+
%p An email will be sent asking them to complete a endorsement declaration form.
|
7
|
+
= f.submit('Create and Notify endorser')
|
8
|
+
- else
|
9
|
+
= f.submit
|
@@ -0,0 +1,22 @@
|
|
1
|
+
= effective_form_with(model: applicant_endorsement, engine: true) do |f|
|
2
|
+
%h2 Applicant endorsement
|
3
|
+
|
4
|
+
%p
|
5
|
+
I endorse and sponser
|
6
|
+
= applicant_endorsement.applicant.owner
|
7
|
+
as a candidate for
|
8
|
+
= applicant_endorsement.applicant.category
|
9
|
+
membership and certify that I have personal knowledge of the professional ability
|
10
|
+
and character, methods of practice, and experience of the applicant.
|
11
|
+
|
12
|
+
%p
|
13
|
+
The candidate has satisfied the requirements for membership in this category,
|
14
|
+
and to the best of my personal knowledge,
|
15
|
+
the applicant observes and upholds the Code of Ethics and Professional Practice.
|
16
|
+
|
17
|
+
.mt-4
|
18
|
+
|
19
|
+
= f.text_area :notes, label: "Additional comments"
|
20
|
+
= f.check_box :accept_declaration, label: 'Yes, I declare that the above information is true and accurate'
|
21
|
+
|
22
|
+
= f.submit 'Complete Endorsement'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
.card
|
2
|
+
.card-body
|
3
|
+
.row
|
4
|
+
.col-sm
|
5
|
+
%h5.card-title= applicant.wizard_step_title(:endorsements)
|
6
|
+
.col-sm-auto.text-right
|
7
|
+
= link_to('Edit', wizard_path(:endorsements)) if edit_effective_wizard?
|
8
|
+
|
9
|
+
- applicant.applicant_endorsements.each_with_index do |applicant_endorsement, index|
|
10
|
+
- if index > 0
|
11
|
+
.mb-4
|
12
|
+
|
13
|
+
%h6 Endorsement ##{index + 1}
|
14
|
+
|
15
|
+
= render('effective/applicant_endorsements/applicant_endorsement', applicant_endorsement: applicant_endorsement)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
= render 'layout' do
|
2
|
+
= render 'effective/applicants/content', resource: resource
|
3
|
+
|
4
|
+
.card
|
5
|
+
.card-body
|
6
|
+
- if resource.min_applicant_endorsements > 0
|
7
|
+
%p You must include #{resource.min_applicant_endorsements} or more endorsements.
|
8
|
+
|
9
|
+
= effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
|
10
|
+
= f.hidden_field :id
|
11
|
+
|
12
|
+
= f.has_many(:applicant_endorsements, cards: true) do |aef|
|
13
|
+
%h4 Endorsement
|
14
|
+
|
15
|
+
= aef.select :endorser, Effective::ApplicantEndorsement.endorser_collection(resource), required: false,
|
16
|
+
ajax_url: effective_memberships.select2_ajax_endorser_applicant_endorsement_path(applicant_id: resource)
|
17
|
+
|
18
|
+
= aef.check_box :unknown_member, label: 'I cant find my endorser in the above list'
|
19
|
+
|
20
|
+
= aef.show_if(:unknown_member, true) do
|
21
|
+
= aef.email_field :endorser_email, required: true
|
22
|
+
= aef.text_field :name, required: true
|
23
|
+
= aef.phone_field :phone, required: true
|
24
|
+
|
25
|
+
= f.save 'Save and Continue'
|
@@ -27,9 +27,14 @@
|
|
27
27
|
= f.show_if(:category_id, mc.id) do
|
28
28
|
.mb-4
|
29
29
|
%h3= mc.to_s
|
30
|
-
|
30
|
+
|
31
|
+
- if organization_categories.present?
|
32
|
+
%small.text-muted #{mc.category} #{mc.category_type} Membership
|
33
|
+
|
31
34
|
= mc.rich_text_body
|
32
35
|
|
36
|
+
= render_if_exists("effective/applicants/select/#{mc.to_s.parameterize.underscore}", f: f, category: mc)
|
37
|
+
|
33
38
|
- if organization_categories.present?
|
34
39
|
= f.show_if_any(:category_id, organization_categories.map(&:id)) do
|
35
40
|
= render('effective/applicants/select_organization', f: f)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
subject: 'Your endorsement has been requested'
|
3
|
+
from: 'admin@example.com'
|
4
|
+
---
|
5
|
+
Hello {{ endorsement.name }},
|
6
|
+
|
7
|
+
An applicant, {{ user.name }}, has requested your endorsement.
|
8
|
+
|
9
|
+
Would you please complete the endorsement declaration form at this url:
|
10
|
+
|
11
|
+
{{ endorsement.url }}
|
12
|
+
|
13
|
+
Thank you.
|
14
|
+
|
15
|
+
Please contact us for assistance.
|
@@ -24,6 +24,9 @@ EffectiveMemberships.setup do |config|
|
|
24
24
|
# The defaults include: Applicant, Prorated, Renewal, Late, Admin
|
25
25
|
# config.additional_fee_types = []
|
26
26
|
|
27
|
+
# Applicant Endorsements
|
28
|
+
# config.applicant_endorsements_endorser_collection = Proc.new { |applicant| applicant.user.class.members }
|
29
|
+
|
27
30
|
# Applicant Reviews
|
28
31
|
# When true, display the reviewed state and require Category.min_applicant_reviews
|
29
32
|
# When false, hide the reviewed state entirely
|
data/config/routes.rb
CHANGED
@@ -9,6 +9,11 @@ EffectiveMemberships::Engine.routes.draw do
|
|
9
9
|
resources :build, controller: :applicants, only: [:show, :update]
|
10
10
|
end
|
11
11
|
|
12
|
+
resources :applicant_endorsements, only: [:new, :create, :show, :update] do
|
13
|
+
get :select2_ajax_endorser, on: :member
|
14
|
+
post :notify, on: :member
|
15
|
+
end
|
16
|
+
|
12
17
|
resources :applicant_references, only: [:new, :create, :show, :update] do
|
13
18
|
post :notify, on: :member
|
14
19
|
end
|
@@ -32,6 +37,10 @@ EffectiveMemberships::Engine.routes.draw do
|
|
32
37
|
namespace :admin do
|
33
38
|
resources :applicants, except: [:new, :create, :show]
|
34
39
|
|
40
|
+
resources :applicant_endorsements do
|
41
|
+
post :notify, on: :member
|
42
|
+
end
|
43
|
+
|
35
44
|
resources :applicant_references do
|
36
45
|
post :notify, on: :member
|
37
46
|
end
|
@@ -24,6 +24,7 @@ class CreateEffectiveMemberships < ActiveRecord::Migration[6.0]
|
|
24
24
|
t.integer :min_applicant_educations
|
25
25
|
t.integer :min_applicant_experiences_months
|
26
26
|
t.integer :min_applicant_references
|
27
|
+
t.integer :min_applicant_endorsements
|
27
28
|
t.integer :min_applicant_courses
|
28
29
|
t.integer :min_applicant_files
|
29
30
|
|
@@ -295,6 +296,35 @@ class CreateEffectiveMemberships < ActiveRecord::Migration[6.0]
|
|
295
296
|
add_index :applicant_references, :applicant_id
|
296
297
|
add_index :applicant_references, :token
|
297
298
|
|
299
|
+
# Applicant Endorsements
|
300
|
+
create_table :applicant_endorsements do |t|
|
301
|
+
t.integer :applicant_id
|
302
|
+
t.string :applicant_type
|
303
|
+
|
304
|
+
t.integer :endorser_id
|
305
|
+
t.string :endorser_type
|
306
|
+
|
307
|
+
t.boolean :unknown_member, default: false
|
308
|
+
t.string :endorser_email
|
309
|
+
t.string :name
|
310
|
+
t.string :phone
|
311
|
+
|
312
|
+
t.string :status
|
313
|
+
t.text :status_steps
|
314
|
+
|
315
|
+
t.text :notes
|
316
|
+
t.boolean :accept_declaration
|
317
|
+
|
318
|
+
t.string :token
|
319
|
+
t.datetime :last_notified_at
|
320
|
+
|
321
|
+
t.datetime :created_at
|
322
|
+
t.datetime :updated_at
|
323
|
+
end
|
324
|
+
|
325
|
+
add_index :applicant_endorsements, :applicant_id
|
326
|
+
add_index :applicant_endorsements, :token
|
327
|
+
|
298
328
|
# Applicant Courses
|
299
329
|
create_table :applicant_course_areas do |t|
|
300
330
|
t.string :title
|
@@ -8,7 +8,7 @@ module EffectiveMemberships
|
|
8
8
|
[
|
9
9
|
:categories_table_name, :applicants_table_name, :applicant_reviews_table_name, :fee_payments_table_name, :organizations_table_name, :representatives_table_name,
|
10
10
|
:category_class_name, :organization_class_name, :applicant_class_name, :applicant_review_class_name, :fee_payment_class_name, :registrar_class_name, :membership_card_class_name,
|
11
|
-
:additional_fee_types, :applicant_reviews,
|
11
|
+
:additional_fee_types, :applicant_reviews, :applicant_endorsements_endorser_collection,
|
12
12
|
:layout,
|
13
13
|
:mailer, :parent_mailer, :deliver_method, :mailer_layout, :mailer_sender, :mailer_admin, :mailer_subject, :use_effective_email_templates
|
14
14
|
]
|
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.
|
4
|
+
version: 0.4.10
|
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-
|
11
|
+
date: 2022-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -256,6 +256,7 @@ files:
|
|
256
256
|
- app/assets/stylesheets/effective_memberships/base.scss
|
257
257
|
- app/controllers/admin/applicant_course_areas_controller.rb
|
258
258
|
- app/controllers/admin/applicant_course_names_controller.rb
|
259
|
+
- app/controllers/admin/applicant_endorsements_controller.rb
|
259
260
|
- app/controllers/admin/applicant_references_controller.rb
|
260
261
|
- app/controllers/admin/applicants_controller.rb
|
261
262
|
- app/controllers/admin/categories_controller.rb
|
@@ -266,6 +267,7 @@ files:
|
|
266
267
|
- app/controllers/admin/organizations_controller.rb
|
267
268
|
- app/controllers/admin/registrar_actions_controller.rb
|
268
269
|
- app/controllers/admin/representatives_controller.rb
|
270
|
+
- app/controllers/effective/applicant_endorsements_controller.rb
|
269
271
|
- app/controllers/effective/applicant_references_controller.rb
|
270
272
|
- app/controllers/effective/applicants_controller.rb
|
271
273
|
- app/controllers/effective/fee_payments_controller.rb
|
@@ -275,6 +277,7 @@ files:
|
|
275
277
|
- app/controllers/effective/representatives_controller.rb
|
276
278
|
- app/datatables/admin/effective_applicant_course_areas_datatable.rb
|
277
279
|
- app/datatables/admin/effective_applicant_course_names_datatable.rb
|
280
|
+
- app/datatables/admin/effective_applicant_endorsements_datatable.rb
|
278
281
|
- app/datatables/admin/effective_applicant_references_datatable.rb
|
279
282
|
- app/datatables/admin/effective_applicants_datatable.rb
|
280
283
|
- app/datatables/admin/effective_categories_datatable.rb
|
@@ -286,6 +289,7 @@ files:
|
|
286
289
|
- app/datatables/admin/effective_representatives_datatable.rb
|
287
290
|
- app/datatables/effective_applicant_courses_datatable.rb
|
288
291
|
- app/datatables/effective_applicant_educations_datatable.rb
|
292
|
+
- app/datatables/effective_applicant_endorsements_datatable.rb
|
289
293
|
- app/datatables/effective_applicant_experiences_datatable.rb
|
290
294
|
- app/datatables/effective_applicant_references_datatable.rb
|
291
295
|
- app/datatables/effective_applicants_datatable.rb
|
@@ -309,6 +313,7 @@ files:
|
|
309
313
|
- app/models/effective/applicant_course_area.rb
|
310
314
|
- app/models/effective/applicant_course_name.rb
|
311
315
|
- app/models/effective/applicant_education.rb
|
316
|
+
- app/models/effective/applicant_endorsement.rb
|
312
317
|
- app/models/effective/applicant_experience.rb
|
313
318
|
- app/models/effective/applicant_reference.rb
|
314
319
|
- app/models/effective/applicant_review.rb
|
@@ -327,6 +332,7 @@ files:
|
|
327
332
|
- app/views/admin/applicant_course_areas/_form_applicant_course_area.html.haml
|
328
333
|
- app/views/admin/applicant_course_areas/index.html.haml
|
329
334
|
- app/views/admin/applicant_course_name/_form.html.haml
|
335
|
+
- app/views/admin/applicant_endorsements/_applicant_endorsement.html.haml
|
330
336
|
- app/views/admin/applicant_references/_applicant_reference.html.haml
|
331
337
|
- app/views/admin/applicants/_form.html.haml
|
332
338
|
- app/views/admin/applicants/_form_approve.html.haml
|
@@ -367,6 +373,12 @@ files:
|
|
367
373
|
- app/views/admin/representatives/_form.html.haml
|
368
374
|
- app/views/admin/representatives/_user_fields.html.haml
|
369
375
|
- app/views/admin/users/_col.html.haml
|
376
|
+
- app/views/effective/applicant_endorsements/_applicant_endorsement.html.haml
|
377
|
+
- app/views/effective/applicant_endorsements/_datatable_actions.html.haml
|
378
|
+
- app/views/effective/applicant_endorsements/_form.html.haml
|
379
|
+
- app/views/effective/applicant_endorsements/_form_declaration.html.haml
|
380
|
+
- app/views/effective/applicant_endorsements/complete.html.haml
|
381
|
+
- app/views/effective/applicant_endorsements/edit.html.haml
|
370
382
|
- app/views/effective/applicant_references/_applicant_reference.html.haml
|
371
383
|
- app/views/effective/applicant_references/_datatable_actions.html.haml
|
372
384
|
- app/views/effective/applicant_references/_form.html.haml
|
@@ -380,6 +392,7 @@ files:
|
|
380
392
|
- app/views/effective/applicants/_declarations.html.haml
|
381
393
|
- app/views/effective/applicants/_demographics.html.haml
|
382
394
|
- app/views/effective/applicants/_education.html.haml
|
395
|
+
- app/views/effective/applicants/_endorsements.html.haml
|
383
396
|
- app/views/effective/applicants/_experience.html.haml
|
384
397
|
- app/views/effective/applicants/_files.html.haml
|
385
398
|
- app/views/effective/applicants/_layout.html.haml
|
@@ -397,6 +410,7 @@ files:
|
|
397
410
|
- app/views/effective/applicants/declarations.html.haml
|
398
411
|
- app/views/effective/applicants/demographics.html.haml
|
399
412
|
- app/views/effective/applicants/education.html.haml
|
413
|
+
- app/views/effective/applicants/endorsements.html.haml
|
400
414
|
- app/views/effective/applicants/experience.html.haml
|
401
415
|
- app/views/effective/applicants/files.html.haml
|
402
416
|
- app/views/effective/applicants/organization.html.haml
|
@@ -432,6 +446,7 @@ files:
|
|
432
446
|
- app/views/effective/memberships_mailer/applicant_approved.liquid
|
433
447
|
- app/views/effective/memberships_mailer/applicant_completed.liquid
|
434
448
|
- app/views/effective/memberships_mailer/applicant_declined.liquid
|
449
|
+
- app/views/effective/memberships_mailer/applicant_endorsement_notification.liquid
|
435
450
|
- app/views/effective/memberships_mailer/applicant_missing_info.liquid
|
436
451
|
- app/views/effective/memberships_mailer/applicant_reference_notification.liquid
|
437
452
|
- app/views/effective/organizations/_dashboard.html.haml
|