effective_memberships 0.4.8 → 0.4.11

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.
Files changed (51) 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/concerns/effective_memberships_organization.rb +5 -0
  16. data/app/models/concerns/effective_memberships_owner.rb +16 -0
  17. data/app/models/concerns/effective_memberships_user.rb +5 -0
  18. data/app/models/effective/applicant_endorsement.rb +100 -0
  19. data/app/models/effective/applicant_equivalence.rb +32 -0
  20. data/app/models/effective/membership_history.rb +2 -0
  21. data/app/views/admin/applicant_endorsements/_applicant_endorsement.html.haml +1 -0
  22. data/app/views/admin/applicants/_form.html.haml +4 -0
  23. data/app/views/admin/categories/_form_applicant_steps.html.haml +12 -0
  24. data/app/views/admin/fee_payments/_fee_payment.html.haml +4 -0
  25. data/app/views/admin/fees/_form.html.haml +2 -3
  26. data/app/views/admin/users/_col.html.haml +12 -0
  27. data/app/views/effective/applicant_endorsements/_applicant_endorsement.html.haml +46 -0
  28. data/app/views/effective/applicant_endorsements/_datatable_actions.html.haml +4 -0
  29. data/app/views/effective/applicant_endorsements/_form.html.haml +9 -0
  30. data/app/views/effective/applicant_endorsements/_form_declaration.html.haml +22 -0
  31. data/app/views/effective/applicant_endorsements/complete.html.haml +3 -0
  32. data/app/views/effective/applicant_endorsements/edit.html.haml +8 -0
  33. data/app/views/effective/applicants/_endorsements.html.haml +15 -0
  34. data/app/views/effective/applicants/_equivalences.html.haml +10 -0
  35. data/app/views/effective/applicants/course_amounts.html.haml +2 -0
  36. data/app/views/effective/applicants/declarations.html.haml +2 -0
  37. data/app/views/effective/applicants/education.html.haml +1 -1
  38. data/app/views/effective/applicants/endorsements.html.haml +34 -0
  39. data/app/views/effective/applicants/equivalences.html.haml +25 -0
  40. data/app/views/effective/applicants/experience.html.haml +3 -3
  41. data/app/views/effective/applicants/files.html.haml +2 -0
  42. data/app/views/effective/applicants/references.html.haml +2 -0
  43. data/app/views/effective/applicants/select.html.haml +6 -1
  44. data/app/views/effective/fee_payments/_orders.html.haml +1 -1
  45. data/app/views/effective/memberships_mailer/applicant_endorsement_notification.liquid +15 -0
  46. data/config/effective_memberships.rb +3 -0
  47. data/config/routes.rb +9 -0
  48. data/db/migrate/01_create_effective_memberships.rb.erb +49 -0
  49. data/lib/effective_memberships/version.rb +1 -1
  50. data/lib/effective_memberships.rb +1 -1
  51. metadata +22 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52711c835dc479a8bc52e2c523d295999270b98eac479cfa8af855d12452466e
4
- data.tar.gz: d5368da347ce46759fc6858b4c231788f91a0baf4ba70a8a8c9104c10e991c1d
3
+ metadata.gz: a08504cb78fd8324ad3a72904249556ba0761b490a4574e1be3a4f6ac22ef51f
4
+ data.tar.gz: 409b51e03245bd8e331aeaf2acd038c111eb060e2852ffdfdbd282e4a20d7e9a
5
5
  SHA512:
6
- metadata.gz: 7a62bf9547cd039f6f9035c0e075328db83455173709a0dbd672db63be2a6c92cf10551d43962deddcafca76c828c53ff0df2cf374ebf26a8e960d443b71586b
7
- data.tar.gz: 7d57abebdaffccdf19af9a1220e20cd244054f04e8807da1e2dc3a45bb40a6010758e4e8676ded2e6321f14d59e4ca488dd05ac391514403c0315464b21c5c86
6
+ metadata.gz: ca160549bff37e5194ca4eaea87f1479426b9f019631497892d564ec4e4be828dc41513085418c89a5b08aa3fa5b2190ee7eb30a3b84acec7f6b5f03a15ff82e
7
+ data.tar.gz: 184c786911efb0309e0d988e288f3d61f7ce9cb440e850e45a2c6fb13903e9a2f5eb79ef6b414a4b9f1365c3af9dfdd5536e050f780d1f50f60b19fd47008586
@@ -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
@@ -62,6 +62,11 @@ module EffectiveMembershipsOrganization
62
62
  scope :deep, -> { includes(:representatives) }
63
63
  scope :sorted, -> { order(:title) }
64
64
 
65
+ scope :membership_applying, -> {
66
+ applicants = EffectiveMemberships.Applicant.all
67
+ without_role(:member).where(id: applicants.select(:organization_id))
68
+ }
69
+
65
70
  validates :title, presence: true, uniqueness: true
66
71
  validates :email, presence: true
67
72
  end
@@ -37,6 +37,22 @@ module EffectiveMembershipsOwner
37
37
  accepts_nested_attributes_for :membership_histories
38
38
 
39
39
  scope :members, -> { joins(:membership) }
40
+
41
+ scope :membership_removed, -> {
42
+ removed = Effective::MembershipHistory.removed.where(owner_type: name)
43
+ without_role(:member).where(id: removed.select(:owner_id))
44
+ }
45
+
46
+ scope :membership_bad_standing, -> {
47
+ bad_standing = Effective::Membership.where(bad_standing: true).where(owner_type: name)
48
+ where(id: bad_standing.select(:owner_id))
49
+ }
50
+
51
+ scope :membership_renewed_this_period, -> {
52
+ with_paid_fees_through = Effective::Membership.with_paid_fees_through.where(owner_type: name)
53
+ where(id: with_paid_fees_through.select(:owner_id))
54
+ }
55
+
40
56
  end
41
57
 
42
58
  def assign_member_role
@@ -27,6 +27,11 @@ module EffectiveMembershipsUser
27
27
  class_name: 'Effective::Representative', inverse_of: :user, dependent: :delete_all
28
28
 
29
29
  accepts_nested_attributes_for :representatives, allow_destroy: true
30
+
31
+ scope :membership_applying, -> {
32
+ applicants = EffectiveMemberships.Applicant.all
33
+ without_role(:member).where(id: applicants.select(:user_id))
34
+ }
30
35
  end
31
36
 
32
37
  # Instance Methods
@@ -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
@@ -25,6 +25,8 @@ module Effective
25
25
  scope :deep, -> { includes(:owner) }
26
26
  scope :sorted, -> { order(:start_on) }
27
27
 
28
+ scope :removed, -> { where(removed: true) }
29
+
28
30
  validates :owner, presence: true
29
31
 
30
32
  # validates :categories, presence: true, unless: -> { removed? }
@@ -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)
@@ -1 +1,5 @@
1
+ .card
2
+ .card-body= render 'effective/fee_payments/summary', fee_payment: fee_payment
3
+
1
4
  = render 'effective/fee_payments/fee_payment', fee_payment: fee_payment
5
+ = render 'effective/fee_payments/orders', fee_payment: fee_payment
@@ -3,9 +3,8 @@
3
3
  = f.hidden_field :owner_id
4
4
  = f.hidden_field :owner_type
5
5
  - else
6
- - raise('todo')
7
- - collection = EffectiveMembershipsOwner.descendants.map { |d| [d.name.to_s, d.members.sorted] }.to_h
8
- = f.select :owner_id, collection, polymorphic: true
6
+ - collection = { 'Users' => current_user.class.sorted, 'Organizations' => EffectiveMemberships.Organization.sorted }
7
+ = f.select :owner, collection, polymorphic: true
9
8
 
10
9
  - if f.object.new_record?
11
10
  = f.select :fee_type, EffectiveMemberships.custom_fee_types
@@ -0,0 +1,12 @@
1
+ - user = (resource.respond_to?(:email) ? resource : resource.user)
2
+
3
+ = user.to_s
4
+
5
+ - if user.email.present?
6
+ %br
7
+ = mail_to user.email
8
+
9
+ - if user.membership.present?
10
+ %br
11
+ = '#' + user.membership.number
12
+ #{user.membership.categories.to_sentence}
@@ -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,10 @@
1
+ .card
2
+ .card-body
3
+ .row
4
+ .col-sm
5
+ %h5.card-title= applicant.wizard_step_title(:equivalences)
6
+ .col-sm-auto.text-right
7
+ = link_to('Edit', wizard_path(:equivalences)) if edit_effective_wizard?
8
+
9
+ - datatable = EffectiveApplicantEquivalencesDatatable.new(applicant: applicant)
10
+ .mb-4= render_simple_datatable(datatable)
@@ -47,4 +47,6 @@
47
47
  .col
48
48
  = f.static_field :min_applicant_courses, label: 'Required Courses'
49
49
 
50
+ = render_if_exists("effective/applicants/course_amounts_fields", f: f)
51
+
50
52
  = f.save 'Save and Continue'
@@ -16,4 +16,6 @@
16
16
  = f.check_box :declare_truth,
17
17
  label: 'Yes, I hereby certify that the statements and information contained herein are correct'
18
18
 
19
+ = render_if_exists("effective/applicants/declarations_fields", f: f)
20
+
19
21
  = f.save 'Save and Continue'
@@ -22,6 +22,6 @@
22
22
 
23
23
  = aef.text_field :degree_obtained, label: 'Degree, diploma or program'
24
24
 
25
- /= f.text_area :applicant_educations_details, label: 'Additional Education Details'
25
+ = render_if_exists("effective/applicants/education_fields", f: f)
26
26
 
27
27
  = f.save 'Save and Continue'
@@ -0,0 +1,34 @@
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
+ - endorders_collection = Effective::ApplicantEndorsement.endorser_collection(resource)
13
+
14
+ = f.has_many(:applicant_endorsements, cards: true) do |aef|
15
+ - aef.object.endorser_type ||= endorders_collection.klass.name
16
+
17
+ %h4 Endorsement
18
+
19
+ = aef.hidden_field :endorser_id
20
+ = aef.hidden_field :endorser_type
21
+
22
+ = aef.select :endorser_id, endorders_collection, required: false,
23
+ ajax_url: effective_memberships.select2_ajax_endorser_applicant_endorsement_path(applicant_id: resource)
24
+
25
+ = aef.check_box :unknown_member, label: 'I cant find my endorser in the above list'
26
+
27
+ = aef.show_if(:unknown_member, true) do
28
+ = aef.text_field :name, label: 'Endorser name', required: true
29
+ = aef.email_field :endorser_email, label: 'Email', required: true
30
+ = aef.phone_field :phone, required: true
31
+
32
+ = render_if_exists("effective/applicants/endorsements_fields", f: f)
33
+
34
+ = f.save 'Save and Continue'
@@ -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_equivalences > 0
7
+ .alert.alert-danger You must include #{resource.min_applicant_equivalences} or more equivalent memberships.
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_equivalences, cards: true) do |aef|
13
+ %h4.mb-4 Equivalent Membership
14
+
15
+ = aef.text_field :name
16
+
17
+ .row
18
+ .col= aef.date_field :start_on, label: 'Start Date'
19
+ .col= aef.date_field :end_on, label: 'End Date'
20
+
21
+ = aef.text_area :notes
22
+
23
+ = render_if_exists("effective/applicants/equivalences_fields", f: f)
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
@@ -21,7 +21,7 @@
21
21
  .row
22
22
  .col= aef.date_field :start_on, label: 'Start Date', 'data-applicant-experiences-month': true
23
23
  .col
24
- = aef.date_field :end_on, label: 'End Date', 'data-applicant-experiences-month': true
24
+ = aef.date_field :end_on, label: 'End Date', required: false, 'data-applicant-experiences-month': true
25
25
  = aef.check_box :still_work_here, label: 'I still work here'
26
26
 
27
27
  .row
@@ -46,6 +46,6 @@
46
46
 
47
47
  = f.error :applicant_experiences_months
48
48
 
49
- /= f.text_area :applicant_experiences_details, label: 'Additional Experiences Details'
49
+ = render_if_exists("effective/applicants/experience_fields", f: f)
50
50
 
51
51
  = f.save 'Save and Continue'
@@ -11,4 +11,6 @@
11
11
 
12
12
  = f.file_field :applicant_files, attachment_style: :table
13
13
 
14
+ = render_if_exists("effective/applicants/files_fields", f: f)
15
+
14
16
  = f.save 'Save and Continue'
@@ -21,4 +21,6 @@
21
21
  .col= arf.select :relationship, Effective::ApplicantReference::RELATIONSHIPS
22
22
  .col= arf.select :known, Effective::ApplicantReference::KNOWNS, label: 'Known for'
23
23
 
24
+ = render_if_exists("effective/applicants/references_fields", f: f)
25
+
24
26
  = 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
- %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)
@@ -1,4 +1,4 @@
1
1
  - if fee_payment.submit_order&.purchased?
2
2
  .card.mb-4
3
3
  .card-body
4
- = render(fee_payment.submit_order)
4
+ = render('effective/orders/order', order: fee_payment.submit_order)
@@ -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,8 @@ 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
28
+ t.integer :min_applicant_equivalences
27
29
  t.integer :min_applicant_courses
28
30
  t.integer :min_applicant_files
29
31
 
@@ -295,6 +297,53 @@ class CreateEffectiveMemberships < ActiveRecord::Migration[6.0]
295
297
  add_index :applicant_references, :applicant_id
296
298
  add_index :applicant_references, :token
297
299
 
300
+ # Applicant Endorsements
301
+ create_table :applicant_endorsements do |t|
302
+ t.integer :applicant_id
303
+ t.string :applicant_type
304
+
305
+ t.integer :endorser_id
306
+ t.string :endorser_type
307
+
308
+ t.boolean :unknown_member, default: false
309
+ t.string :endorser_email
310
+ t.string :name
311
+ t.string :phone
312
+
313
+ t.string :status
314
+ t.text :status_steps
315
+
316
+ t.text :notes
317
+ t.boolean :accept_declaration
318
+
319
+ t.string :token
320
+ t.datetime :last_notified_at
321
+
322
+ t.datetime :created_at
323
+ t.datetime :updated_at
324
+ end
325
+
326
+ add_index :applicant_endorsements, :applicant_id
327
+ add_index :applicant_endorsements, :token
328
+
329
+ # Applicant Equivalences
330
+ create_table :applicant_equivalences do |t|
331
+ t.integer :applicant_id
332
+ t.string :applicant_type
333
+
334
+ t.string :name
335
+
336
+ t.date :start_on
337
+ t.date :end_on
338
+
339
+ t.text :notes
340
+
341
+ t.datetime :created_at
342
+ t.datetime :updated_at
343
+ end
344
+
345
+ add_index :applicant_equivalences, :applicant_id
346
+
298
347
  # Applicant Courses
299
348
  create_table :applicant_course_areas do |t|
300
349
  t.string :title
@@ -1,3 +1,3 @@
1
1
  module EffectiveMemberships
2
- VERSION = '0.4.8'
2
+ VERSION = '0.4.11'
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.8
4
+ version: 0.4.11
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-29 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,8 @@ 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
293
+ - app/datatables/effective_applicant_equivalences_datatable.rb
289
294
  - app/datatables/effective_applicant_experiences_datatable.rb
290
295
  - app/datatables/effective_applicant_references_datatable.rb
291
296
  - app/datatables/effective_applicants_datatable.rb
@@ -309,6 +314,8 @@ files:
309
314
  - app/models/effective/applicant_course_area.rb
310
315
  - app/models/effective/applicant_course_name.rb
311
316
  - app/models/effective/applicant_education.rb
317
+ - app/models/effective/applicant_endorsement.rb
318
+ - app/models/effective/applicant_equivalence.rb
312
319
  - app/models/effective/applicant_experience.rb
313
320
  - app/models/effective/applicant_reference.rb
314
321
  - app/models/effective/applicant_review.rb
@@ -327,6 +334,7 @@ files:
327
334
  - app/views/admin/applicant_course_areas/_form_applicant_course_area.html.haml
328
335
  - app/views/admin/applicant_course_areas/index.html.haml
329
336
  - app/views/admin/applicant_course_name/_form.html.haml
337
+ - app/views/admin/applicant_endorsements/_applicant_endorsement.html.haml
330
338
  - app/views/admin/applicant_references/_applicant_reference.html.haml
331
339
  - app/views/admin/applicants/_form.html.haml
332
340
  - app/views/admin/applicants/_form_approve.html.haml
@@ -366,6 +374,13 @@ files:
366
374
  - app/views/admin/registrar_actions/_form_remove.html.haml
367
375
  - app/views/admin/representatives/_form.html.haml
368
376
  - app/views/admin/representatives/_user_fields.html.haml
377
+ - app/views/admin/users/_col.html.haml
378
+ - app/views/effective/applicant_endorsements/_applicant_endorsement.html.haml
379
+ - app/views/effective/applicant_endorsements/_datatable_actions.html.haml
380
+ - app/views/effective/applicant_endorsements/_form.html.haml
381
+ - app/views/effective/applicant_endorsements/_form_declaration.html.haml
382
+ - app/views/effective/applicant_endorsements/complete.html.haml
383
+ - app/views/effective/applicant_endorsements/edit.html.haml
369
384
  - app/views/effective/applicant_references/_applicant_reference.html.haml
370
385
  - app/views/effective/applicant_references/_datatable_actions.html.haml
371
386
  - app/views/effective/applicant_references/_form.html.haml
@@ -379,6 +394,8 @@ files:
379
394
  - app/views/effective/applicants/_declarations.html.haml
380
395
  - app/views/effective/applicants/_demographics.html.haml
381
396
  - app/views/effective/applicants/_education.html.haml
397
+ - app/views/effective/applicants/_endorsements.html.haml
398
+ - app/views/effective/applicants/_equivalences.html.haml
382
399
  - app/views/effective/applicants/_experience.html.haml
383
400
  - app/views/effective/applicants/_files.html.haml
384
401
  - app/views/effective/applicants/_layout.html.haml
@@ -396,6 +413,8 @@ files:
396
413
  - app/views/effective/applicants/declarations.html.haml
397
414
  - app/views/effective/applicants/demographics.html.haml
398
415
  - app/views/effective/applicants/education.html.haml
416
+ - app/views/effective/applicants/endorsements.html.haml
417
+ - app/views/effective/applicants/equivalences.html.haml
399
418
  - app/views/effective/applicants/experience.html.haml
400
419
  - app/views/effective/applicants/files.html.haml
401
420
  - app/views/effective/applicants/organization.html.haml
@@ -431,6 +450,7 @@ files:
431
450
  - app/views/effective/memberships_mailer/applicant_approved.liquid
432
451
  - app/views/effective/memberships_mailer/applicant_completed.liquid
433
452
  - app/views/effective/memberships_mailer/applicant_declined.liquid
453
+ - app/views/effective/memberships_mailer/applicant_endorsement_notification.liquid
434
454
  - app/views/effective/memberships_mailer/applicant_missing_info.liquid
435
455
  - app/views/effective/memberships_mailer/applicant_reference_notification.liquid
436
456
  - app/views/effective/organizations/_dashboard.html.haml