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.
Files changed (32) 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 +37 -0
  8. data/app/datatables/effective_applicant_references_datatable.rb +1 -1
  9. data/app/mailers/effective/memberships_mailer.rb +25 -0
  10. data/app/models/concerns/effective_memberships_applicant.rb +34 -6
  11. data/app/models/concerns/effective_memberships_fee_payment.rb +0 -1
  12. data/app/models/effective/applicant_endorsement.rb +100 -0
  13. data/app/views/admin/applicant_endorsements/_applicant_endorsement.html.haml +1 -0
  14. data/app/views/admin/applicants/_form.html.haml +4 -0
  15. data/app/views/admin/categories/_form_applicant_steps.html.haml +6 -0
  16. data/app/views/effective/applicant_endorsements/_applicant_endorsement.html.haml +46 -0
  17. data/app/views/effective/applicant_endorsements/_datatable_actions.html.haml +4 -0
  18. data/app/views/effective/applicant_endorsements/_form.html.haml +9 -0
  19. data/app/views/effective/applicant_endorsements/_form_declaration.html.haml +22 -0
  20. data/app/views/effective/applicant_endorsements/complete.html.haml +3 -0
  21. data/app/views/effective/applicant_endorsements/edit.html.haml +8 -0
  22. data/app/views/effective/applicants/_endorsements.html.haml +15 -0
  23. data/app/views/effective/applicants/endorsements.html.haml +25 -0
  24. data/app/views/effective/applicants/experience.html.haml +1 -1
  25. data/app/views/effective/applicants/select.html.haml +6 -1
  26. data/app/views/effective/memberships_mailer/applicant_endorsement_notification.liquid +15 -0
  27. data/config/effective_memberships.rb +3 -0
  28. data/config/routes.rb +9 -0
  29. data/db/migrate/01_create_effective_memberships.rb.erb +30 -0
  30. data/lib/effective_memberships/version.rb +1 -1
  31. data/lib/effective_memberships.rb +1 -1
  32. metadata +17 -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: b7d09ef68f61058a8ee29567caa3a008d887482edab0fa3e8d251ea69f9ea3cb
4
+ data.tar.gz: 10a88e200464a6d2d7dbb8fbdcaa3aed77337b067e12fc3577b31a8032cbd2ce
5
5
  SHA512:
6
- metadata.gz: bffdf6c0614f8f36ee84b38426b1a8ea9aae37a7b1385fe9ce746584196bb043d2c835f1414942f8dc504b53090e6725649657fe348feab736a9b00453aa13c7
7
- data.tar.gz: 845130d67ea3500c634f2f9c96684af9ca1785dc50133ac94efda8087cb80e2e50bf05ea071540106d7d0250c557a0267ead9ed5b3ea2f98ca59cb99858b197a
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
@@ -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,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
- 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
- {}
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!
@@ -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, 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,3 @@
1
+ .card
2
+ .card-body
3
+ %p Thank you! The confidential endorsement has been completed.
@@ -0,0 +1,8 @@
1
+ .card
2
+ .card-body
3
+ %p
4
+ Please provide a endorsement for the applicant
5
+ = succeed('.') do
6
+ %strong= @applicant_endorsement.applicant.owner
7
+
8
+ = render('form_declaration', applicant_endorsement: @applicant_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'
@@ -12,7 +12,7 @@
12
12
  = f.error :applicant_experiences_months
13
13
 
14
14
  = f.has_many(:applicant_experiences, cards: true) do |aef|
15
- %h4 Work Experience
15
+ %h4.mb-4 Work Experience
16
16
 
17
17
  .row
18
18
  .col= aef.text_field :employer
@@ -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
- %small.text-muted #{mc.category} #{mc.category_type} Membership
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
@@ -1,3 +1,3 @@
1
1
  module EffectiveMemberships
2
- VERSION = '0.4.9'
2
+ VERSION = '0.4.10'
3
3
  end
@@ -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.9
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-04-13 00:00:00.000000000 Z
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