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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/effective_memberships/applicant_experiences.js +0 -5
  3. data/app/controllers/admin/applicant_endorsements_controller.rb +9 -0
  4. data/app/controllers/effective/applicant_endorsements_controller.rb +48 -0
  5. data/app/controllers/effective/applicant_references_controller.rb +1 -1
  6. data/app/datatables/admin/effective_applicant_endorsements_datatable.rb +35 -0
  7. data/app/datatables/effective_applicant_endorsements_datatable.rb +38 -0
  8. data/app/datatables/effective_applicant_equivalences_datatable.rb +23 -0
  9. data/app/datatables/effective_applicant_experiences_datatable.rb +1 -0
  10. data/app/datatables/effective_applicant_references_datatable.rb +1 -1
  11. data/app/mailers/effective/memberships_mailer.rb +25 -0
  12. data/app/models/concerns/effective_memberships_applicant.rb +52 -6
  13. data/app/models/concerns/effective_memberships_category.rb +2 -0
  14. data/app/models/concerns/effective_memberships_fee_payment.rb +0 -1
  15. data/app/models/effective/applicant_endorsement.rb +100 -0
  16. data/app/models/effective/applicant_equivalence.rb +32 -0
  17. data/app/views/admin/applicant_endorsements/_applicant_endorsement.html.haml +1 -0
  18. data/app/views/admin/applicants/_form.html.haml +4 -0
  19. data/app/views/admin/categories/_form_applicant_steps.html.haml +12 -0
  20. data/app/views/effective/applicant_endorsements/_applicant_endorsement.html.haml +46 -0
  21. data/app/views/effective/applicant_endorsements/_datatable_actions.html.haml +4 -0
  22. data/app/views/effective/applicant_endorsements/_form.html.haml +9 -0
  23. data/app/views/effective/applicant_endorsements/_form_declaration.html.haml +22 -0
  24. data/app/views/effective/applicant_endorsements/complete.html.haml +2 -0
  25. data/app/views/effective/applicant_endorsements/edit.html.haml +7 -0
  26. data/app/views/effective/applicant_references/complete.html.haml +2 -3
  27. data/app/views/effective/applicant_references/edit.html.haml +6 -7
  28. data/app/views/effective/applicants/_applicant.html.haml +2 -1
  29. data/app/views/effective/applicants/_course_amounts.html.haml +9 -17
  30. data/app/views/effective/applicants/_declarations.html.haml +9 -16
  31. data/app/views/effective/applicants/_demographics.html.haml +2 -9
  32. data/app/views/effective/applicants/_education.html.haml +6 -13
  33. data/app/views/effective/applicants/_endorsements.html.haml +8 -0
  34. data/app/views/effective/applicants/_equivalences.html.haml +3 -0
  35. data/app/views/effective/applicants/_experience.html.haml +18 -25
  36. data/app/views/effective/applicants/_files.html.haml +16 -23
  37. data/app/views/effective/applicants/_organization.html.haml +2 -9
  38. data/app/views/effective/applicants/_references.html.haml +6 -13
  39. data/app/views/effective/applicants/_stamp.html.haml +23 -30
  40. data/app/views/effective/applicants/_summary.html.haml +5 -0
  41. data/app/views/effective/applicants/billing.html.haml +6 -7
  42. data/app/views/effective/applicants/checkout.html.haml +2 -3
  43. data/app/views/effective/applicants/course_amounts.html.haml +48 -47
  44. data/app/views/effective/applicants/declarations.html.haml +10 -9
  45. data/app/views/effective/applicants/demographics.html.haml +6 -7
  46. data/app/views/effective/applicants/education.html.haml +16 -17
  47. data/app/views/effective/applicants/endorsements.html.haml +33 -0
  48. data/app/views/effective/applicants/equivalences.html.haml +24 -0
  49. data/app/views/effective/applicants/experience.html.haml +35 -36
  50. data/app/views/effective/applicants/files.html.haml +9 -8
  51. data/app/views/effective/applicants/organization.html.haml +10 -11
  52. data/app/views/effective/applicants/references.html.haml +17 -16
  53. data/app/views/effective/applicants/select.html.haml +27 -23
  54. data/app/views/effective/applicants/stamp.html.haml +16 -17
  55. data/app/views/effective/applicants/start.html.haml +4 -5
  56. data/app/views/effective/applicants/submitted.html.haml +9 -10
  57. data/app/views/effective/fee_payments/_declarations.html.haml +9 -16
  58. data/app/views/effective/fee_payments/_demographics.html.haml +2 -9
  59. data/app/views/effective/fee_payments/_fee_payment.html.haml +2 -1
  60. data/app/views/effective/fee_payments/_organization.html.haml +2 -9
  61. data/app/views/effective/fee_payments/billing.html.haml +6 -7
  62. data/app/views/effective/fee_payments/checkout.html.haml +2 -3
  63. data/app/views/effective/fee_payments/declarations.html.haml +8 -9
  64. data/app/views/effective/fee_payments/demographics.html.haml +6 -7
  65. data/app/views/effective/fee_payments/organization.html.haml +10 -11
  66. data/app/views/effective/fee_payments/start.html.haml +23 -24
  67. data/app/views/effective/fee_payments/submitted.html.haml +2 -2
  68. data/app/views/effective/memberships_mailer/applicant_endorsement_notification.liquid +15 -0
  69. data/config/effective_memberships.rb +3 -0
  70. data/config/routes.rb +9 -0
  71. data/db/migrate/01_create_effective_memberships.rb.erb +49 -0
  72. data/lib/effective_memberships/version.rb +1 -1
  73. data/lib/effective_memberships.rb +1 -1
  74. metadata +21 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60e4351c09c9d070ea622ed6e7770df2f14d53e177a4ea74e04294d00b91af6f
4
- data.tar.gz: 65436fdfda6a93aa08e1846d44b13131eedcc9d92e5831742dd6892d5ed87a32
3
+ metadata.gz: 9e598cee90bcbc1349b12ff9d56babaf0686ddcc91f6ed162c8a8edc6c4b5c67
4
+ data.tar.gz: 8ace8c8625d529b305e832b9f24c5377be98e3da711a7d146fdf67d613bf7a93
5
5
  SHA512:
6
- metadata.gz: bffdf6c0614f8f36ee84b38426b1a8ea9aae37a7b1385fe9ce746584196bb043d2c835f1414942f8dc504b53090e6725649657fe348feab736a9b00453aa13c7
7
- data.tar.gz: 845130d67ea3500c634f2f9c96684af9ca1785dc50133ac94efda8087cb80e2e50bf05ea071540106d7d0250c557a0267ead9ed5b3ea2f98ca59cb99858b197a
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
@@ -1,6 +1,7 @@
1
1
  class EffectiveApplicantExperiencesDatatable < Effective::Datatable
2
2
  datatable do
3
3
  length :all
4
+
4
5
  order :start_on
5
6
 
6
7
  col :start_on
@@ -1,4 +1,4 @@
1
- # For the applicant complete step to add additional applicants
1
+ # For the applicant complete step to add additional references
2
2
  class EffectiveApplicantReferencesDatatable < Effective::Datatable
3
3
 
4
4
  datatable do
@@ -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
- if category&.applicant_wizard_steps&.include?(:references) || applicant_references_required?
547
- {
548
- 'Applicant References' => (!applicant_references_required? || applicant_references.count(&:completed?) >= min_applicant_references)
549
- }
550
- else
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
@@ -129,7 +129,6 @@ module EffectiveMembershipsFeePayment
129
129
 
130
130
  fee_payment_steps.delete(:organization) unless organization?
131
131
 
132
-
133
132
  wizard_steps.select do |step|
134
133
  required_steps.include?(step) || category.blank? || fee_payment_steps.include?(step)
135
134
  end
@@ -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'
@@ -0,0 +1,2 @@
1
+ = card do
2
+ %p Thank you! The confidential endorsement has been completed.
@@ -0,0 +1,7 @@
1
+ = card do
2
+ %p
3
+ Please provide a endorsement for the applicant
4
+ = succeed('.') do
5
+ %strong= @applicant_endorsement.applicant.owner
6
+
7
+ = render('form_declaration', applicant_endorsement: @applicant_endorsement)
@@ -1,3 +1,2 @@
1
- .card
2
- .card-body
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
- .card
2
- .card-body
3
- %p
4
- Please provide a reference for the applicant
5
- = succeed('.') do
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
- = render('form_declaration', applicant_reference: @applicant_reference)
7
+ = render('form_declaration', applicant_reference: @applicant_reference)
@@ -1,3 +1,4 @@
1
1
  .effective-applicant
2
2
  - applicant.render_steps.each do |partial|
3
- = render "effective/applicants/#{partial}", applicant: applicant, step: partial
3
+ - applicant.render_step = partial
4
+ = render "effective/applicants/#{partial}", applicant: applicant