effective_memberships 0.4.9 → 0.4.12
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 +38 -0
- data/app/datatables/effective_applicant_equivalences_datatable.rb +23 -0
- data/app/datatables/effective_applicant_experiences_datatable.rb +1 -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 +52 -6
- data/app/models/concerns/effective_memberships_category.rb +2 -0
- data/app/models/concerns/effective_memberships_fee_payment.rb +0 -1
- data/app/models/effective/applicant_endorsement.rb +100 -0
- data/app/models/effective/applicant_equivalence.rb +32 -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 +12 -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 +2 -0
- data/app/views/effective/applicant_endorsements/edit.html.haml +7 -0
- data/app/views/effective/applicant_references/complete.html.haml +2 -3
- data/app/views/effective/applicant_references/edit.html.haml +6 -7
- data/app/views/effective/applicants/_applicant.html.haml +2 -1
- data/app/views/effective/applicants/_course_amounts.html.haml +9 -17
- data/app/views/effective/applicants/_declarations.html.haml +9 -16
- data/app/views/effective/applicants/_demographics.html.haml +2 -9
- data/app/views/effective/applicants/_education.html.haml +6 -13
- data/app/views/effective/applicants/_endorsements.html.haml +8 -0
- data/app/views/effective/applicants/_equivalences.html.haml +3 -0
- data/app/views/effective/applicants/_experience.html.haml +18 -25
- data/app/views/effective/applicants/_files.html.haml +16 -23
- data/app/views/effective/applicants/_organization.html.haml +2 -9
- data/app/views/effective/applicants/_references.html.haml +6 -13
- data/app/views/effective/applicants/_stamp.html.haml +23 -30
- data/app/views/effective/applicants/_summary.html.haml +5 -0
- data/app/views/effective/applicants/billing.html.haml +6 -7
- data/app/views/effective/applicants/checkout.html.haml +2 -3
- data/app/views/effective/applicants/course_amounts.html.haml +48 -47
- data/app/views/effective/applicants/declarations.html.haml +10 -9
- data/app/views/effective/applicants/demographics.html.haml +6 -7
- data/app/views/effective/applicants/education.html.haml +16 -17
- data/app/views/effective/applicants/endorsements.html.haml +33 -0
- data/app/views/effective/applicants/equivalences.html.haml +24 -0
- data/app/views/effective/applicants/experience.html.haml +35 -36
- data/app/views/effective/applicants/files.html.haml +9 -8
- data/app/views/effective/applicants/organization.html.haml +10 -11
- data/app/views/effective/applicants/references.html.haml +17 -16
- data/app/views/effective/applicants/select.html.haml +27 -23
- data/app/views/effective/applicants/stamp.html.haml +16 -17
- data/app/views/effective/applicants/start.html.haml +4 -5
- data/app/views/effective/applicants/submitted.html.haml +9 -10
- data/app/views/effective/fee_payments/_declarations.html.haml +9 -16
- data/app/views/effective/fee_payments/_demographics.html.haml +2 -9
- data/app/views/effective/fee_payments/_fee_payment.html.haml +2 -1
- data/app/views/effective/fee_payments/_organization.html.haml +2 -9
- data/app/views/effective/fee_payments/billing.html.haml +6 -7
- data/app/views/effective/fee_payments/checkout.html.haml +2 -3
- data/app/views/effective/fee_payments/declarations.html.haml +8 -9
- data/app/views/effective/fee_payments/demographics.html.haml +6 -7
- data/app/views/effective/fee_payments/organization.html.haml +10 -11
- data/app/views/effective/fee_payments/start.html.haml +23 -24
- data/app/views/effective/fee_payments/submitted.html.haml +2 -2
- 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 +49 -0
- data/lib/effective_memberships/version.rb +1 -1
- data/lib/effective_memberships.rb +1 -1
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e598cee90bcbc1349b12ff9d56babaf0686ddcc91f6ed162c8a8edc6c4b5c67
|
4
|
+
data.tar.gz: 8ace8c8625d529b305e832b9f24c5377be98e3da711a7d146fdf67d613bf7a93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2dadd826408201278a2cfcd8638399c140c755ceda775accdb015b8613e7f036d68dbd0776e39d77ff76bc41d0bec765972c33c1f859dc3dfcfc5ce3f57d965f
|
7
|
+
data.tar.gz: 7d915acf40e2b27b05773674e536d70c45555ea217f3f7abaa34c330ee51c00d75c914681c91f8ec43d39d33c0fe2127a6614f1f5afc7fb8664b8af937b054cd
|
@@ -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,38 @@
|
|
1
|
+
# For the applicant complete step to add additional endorsements
|
2
|
+
class EffectiveApplicantEndorsementsDatatable < Effective::Datatable
|
3
|
+
|
4
|
+
datatable do
|
5
|
+
length :all
|
6
|
+
order :name
|
7
|
+
|
8
|
+
col :name do |endorsement|
|
9
|
+
endorser&.to_s || name
|
10
|
+
end
|
11
|
+
|
12
|
+
col :email
|
13
|
+
col :phone
|
14
|
+
|
15
|
+
col :status do |reference|
|
16
|
+
if reference.submitted?
|
17
|
+
'Waiting on response'
|
18
|
+
elsif reference.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 partial: 'effective/applicant_endorsements/datatable_actions', partial_as: :applicant_endorsement
|
28
|
+
end
|
29
|
+
|
30
|
+
collection do
|
31
|
+
Effective::ApplicantEndorsement.deep.where(applicant: applicant)
|
32
|
+
end
|
33
|
+
|
34
|
+
def applicant
|
35
|
+
@applicant ||= EffectiveMemberships.Applicant.where(id: attributes[:applicant_id]).first!
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class EffectiveApplicantEquivalencesDatatable < Effective::Datatable
|
2
|
+
datatable do
|
3
|
+
length :all
|
4
|
+
order :start_on
|
5
|
+
|
6
|
+
col :start_on
|
7
|
+
|
8
|
+
col :end_on do |applicant_equivalence|
|
9
|
+
applicant_equivalence.end_on&.strftime('%F') || '-'
|
10
|
+
end
|
11
|
+
|
12
|
+
col :name
|
13
|
+
col :notes
|
14
|
+
end
|
15
|
+
|
16
|
+
collection do
|
17
|
+
Effective::ApplicantEquivalence.deep.where(applicant: applicant)
|
18
|
+
end
|
19
|
+
|
20
|
+
def applicant
|
21
|
+
@applicant ||= EffectiveMemberships.Applicant.where(id: attributes[:applicant_id]).first!
|
22
|
+
end
|
23
|
+
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,12 @@ 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
|
+
|
99
|
+
has_many :applicant_equivalences, -> { order(:id) }, class_name: 'Effective::ApplicantEquivalence', as: :applicant, inverse_of: :applicant, dependent: :destroy
|
100
|
+
accepts_nested_attributes_for :applicant_equivalences, reject_if: :all_blank, allow_destroy: true
|
101
|
+
|
96
102
|
has_many :applicant_experiences, -> { order(:id) }, class_name: 'Effective::ApplicantExperience', as: :applicant, inverse_of: :applicant, dependent: :destroy
|
97
103
|
accepts_nested_attributes_for :applicant_experiences, reject_if: :all_blank, allow_destroy: true
|
98
104
|
|
@@ -238,6 +244,26 @@ module EffectiveMembershipsApplicant
|
|
238
244
|
end
|
239
245
|
end
|
240
246
|
|
247
|
+
# Applicant Endorsements Step
|
248
|
+
with_options(if: -> { current_step == :endorsements }) do
|
249
|
+
validate do
|
250
|
+
required = min_applicant_endorsements()
|
251
|
+
existing = applicant_endorsements().reject(&:marked_for_destruction?).length
|
252
|
+
|
253
|
+
self.errors.add(:applicant_endorsements, "please include #{required} or more endorsements") if existing < required
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
# Applicant Equivalences Step
|
258
|
+
with_options(if: -> { current_step == :equivalences }) do
|
259
|
+
validate do
|
260
|
+
required = min_applicant_equivalences()
|
261
|
+
existing = applicant_equivalences().reject(&:marked_for_destruction?).length
|
262
|
+
|
263
|
+
self.errors.add(:applicant_equivalences, "please include #{required} or more equivalences") if existing < required
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
241
267
|
# Applicant References Step
|
242
268
|
with_options(if: -> { current_step == :references }) do
|
243
269
|
validate do
|
@@ -361,6 +387,7 @@ module EffectiveMembershipsApplicant
|
|
361
387
|
stamps.each { |stamp| stamp.submit! }
|
362
388
|
|
363
389
|
after_commit do
|
390
|
+
applicant_endorsements.each { |endorsement| endorsement.notify! if endorsement.submitted? }
|
364
391
|
applicant_references.each { |reference| reference.notify! if reference.submitted? }
|
365
392
|
end
|
366
393
|
|
@@ -521,6 +548,16 @@ module EffectiveMembershipsApplicant
|
|
521
548
|
category&.min_applicant_references.to_i
|
522
549
|
end
|
523
550
|
|
551
|
+
# Endorsements Step
|
552
|
+
def min_applicant_endorsements
|
553
|
+
category&.min_applicant_endorsements.to_i
|
554
|
+
end
|
555
|
+
|
556
|
+
# Equivalences Step
|
557
|
+
def min_applicant_equivalences
|
558
|
+
category&.min_applicant_equivalences.to_i
|
559
|
+
end
|
560
|
+
|
524
561
|
# Files Step
|
525
562
|
def min_applicant_files
|
526
563
|
category&.min_applicant_files.to_i
|
@@ -536,6 +573,10 @@ module EffectiveMembershipsApplicant
|
|
536
573
|
)
|
537
574
|
end
|
538
575
|
|
576
|
+
def applicant_endorsements_required?
|
577
|
+
min_applicant_endorsements > 0
|
578
|
+
end
|
579
|
+
|
539
580
|
def applicant_references_required?
|
540
581
|
min_applicant_references > 0
|
541
582
|
end
|
@@ -543,13 +584,18 @@ module EffectiveMembershipsApplicant
|
|
543
584
|
# When an application is submitted, these must be done to go to completed.
|
544
585
|
# An Admin can override this and just set them to completed.
|
545
586
|
def completed_requirements
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
{}
|
587
|
+
requirements = {}
|
588
|
+
return requirements unless category.present?
|
589
|
+
|
590
|
+
if category.applicant_wizard_steps.include?(:references) || applicant_references_required?
|
591
|
+
requirements['Applicant References'] = (!applicant_references_required? || applicant_references.count(&:completed?) >= min_applicant_references)
|
552
592
|
end
|
593
|
+
|
594
|
+
if category.applicant_wizard_steps.include?(:endorsements) || applicant_endorsements_required?
|
595
|
+
requirements['Applicant Endorsements'] = (!applicant_endorsements_required? || applicant_endorsements.count(&:completed?) >= min_applicant_endorsements)
|
596
|
+
end
|
597
|
+
|
598
|
+
requirements
|
553
599
|
end
|
554
600
|
|
555
601
|
def complete!
|
@@ -55,6 +55,8 @@ module EffectiveMembershipsCategory
|
|
55
55
|
applicant_wizard_steps :text
|
56
56
|
|
57
57
|
min_applicant_educations :integer
|
58
|
+
min_applicant_endorsements :integer
|
59
|
+
min_applicant_equivalences :integer
|
58
60
|
min_applicant_experiences_months :integer
|
59
61
|
min_applicant_references :integer
|
60
62
|
min_applicant_courses :integer
|
@@ -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_id, 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,32 @@
|
|
1
|
+
module Effective
|
2
|
+
class ApplicantEquivalence < ActiveRecord::Base
|
3
|
+
belongs_to :applicant, polymorphic: true
|
4
|
+
|
5
|
+
log_changes(to: :applicant) if respond_to?(:log_changes)
|
6
|
+
|
7
|
+
effective_resource do
|
8
|
+
name :string
|
9
|
+
|
10
|
+
start_on :date
|
11
|
+
end_on :date
|
12
|
+
|
13
|
+
notes :text
|
14
|
+
|
15
|
+
timestamps
|
16
|
+
end
|
17
|
+
|
18
|
+
scope :deep, -> { all }
|
19
|
+
|
20
|
+
validates :name, presence: true
|
21
|
+
validates :start_on, presence: true
|
22
|
+
|
23
|
+
validate(if: -> { start_on.present? && end_on.present? }) do
|
24
|
+
errors.add(:end_on, 'must be after start date') unless start_on < end_on
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
name || 'equivalence'
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
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,18 @@
|
|
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
|
+
|
41
|
+
- if applicant.wizard_step_keys.include?(:equivalences)
|
42
|
+
%tr
|
43
|
+
%td= applicant.wizard_step_title(:equivalences)
|
44
|
+
%td= f.number_field :min_applicant_equivalences, label: false
|
45
|
+
%td minimimum number of equivalent memberships
|
46
|
+
|
35
47
|
- if applicant.wizard_step_keys.include?(:references)
|
36
48
|
%tr
|
37
49
|
%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'
|
@@ -1,3 +1,2 @@
|
|
1
|
-
|
2
|
-
.
|
3
|
-
%p Thank you! The confidential reference has been completed.
|
1
|
+
= card do
|
2
|
+
%p Thank you! The confidential reference has been completed.
|
@@ -1,8 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
=
|
6
|
-
%strong= @applicant_reference.applicant.owner
|
1
|
+
= card do
|
2
|
+
%p
|
3
|
+
Please provide a reference for the applicant
|
4
|
+
= succeed('.') do
|
5
|
+
%strong= @applicant_reference.applicant.owner
|
7
6
|
|
8
|
-
|
7
|
+
= render('form_declaration', applicant_reference: @applicant_reference)
|