effective_memberships 0.4.9 → 0.4.10
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/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
|