effective_memberships 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +114 -0
- data/Rakefile +18 -0
- data/app/assets/config/effective_memberships_manifest.js +3 -0
- data/app/assets/javascripts/effective_memberships/applicant_courses.js +29 -0
- data/app/assets/javascripts/effective_memberships/applicant_experiences.js +38 -0
- data/app/assets/javascripts/effective_memberships/base.js +0 -0
- data/app/assets/javascripts/effective_memberships.js +1 -0
- data/app/assets/stylesheets/effective_memberships/base.scss +5 -0
- data/app/assets/stylesheets/effective_memberships.scss +1 -0
- data/app/controllers/admin/applicant_course_areas_controller.rb +15 -0
- data/app/controllers/admin/applicant_course_names_controller.rb +15 -0
- data/app/controllers/admin/applicants_controller.rb +33 -0
- data/app/controllers/admin/categories_controller.rb +19 -0
- data/app/controllers/admin/fees_controller.rb +18 -0
- data/app/controllers/admin/registrar_actions_controller.rb +49 -0
- data/app/controllers/effective/applicant_references_controller.rb +37 -0
- data/app/controllers/effective/applicants_controller.rb +34 -0
- data/app/controllers/effective/fee_payments_controller.rb +29 -0
- data/app/datatables/admin/effective_applicant_course_areas_datatable.rb +17 -0
- data/app/datatables/admin/effective_applicant_course_names_datatable.rb +17 -0
- data/app/datatables/admin/effective_applicants_datatable.rb +55 -0
- data/app/datatables/admin/effective_categories_datatable.rb +23 -0
- data/app/datatables/admin/effective_fees_datatable.rb +69 -0
- data/app/datatables/admin/effective_membership_histories_datatable.rb +35 -0
- data/app/datatables/effective_applicant_courses_datatable.rb +30 -0
- data/app/datatables/effective_applicant_educations_datatable.rb +22 -0
- data/app/datatables/effective_applicant_experiences_datatable.rb +42 -0
- data/app/datatables/effective_applicant_references_datatable.rb +34 -0
- data/app/datatables/effective_applicants_datatable.rb +34 -0
- data/app/helpers/effective_memberships_helper.rb +15 -0
- data/app/mailers/effective/memberships_mailer.rb +84 -0
- data/app/models/concerns/effective_memberships_applicant.rb +612 -0
- data/app/models/concerns/effective_memberships_applicant_review.rb +149 -0
- data/app/models/concerns/effective_memberships_category.rb +196 -0
- data/app/models/concerns/effective_memberships_fee_payment.rb +229 -0
- data/app/models/concerns/effective_memberships_owner.rb +263 -0
- data/app/models/concerns/effective_memberships_registrar.rb +300 -0
- data/app/models/effective/applicant.rb +7 -0
- data/app/models/effective/applicant_course.rb +40 -0
- data/app/models/effective/applicant_course_area.rb +31 -0
- data/app/models/effective/applicant_course_name.rb +29 -0
- data/app/models/effective/applicant_education.rb +36 -0
- data/app/models/effective/applicant_experience.rb +79 -0
- data/app/models/effective/applicant_reference.rb +81 -0
- data/app/models/effective/applicant_review.rb +7 -0
- data/app/models/effective/category.rb +7 -0
- data/app/models/effective/fee.rb +142 -0
- data/app/models/effective/fee_payment.rb +7 -0
- data/app/models/effective/membership.rb +119 -0
- data/app/models/effective/membership_category.rb +11 -0
- data/app/models/effective/membership_history.rb +40 -0
- data/app/models/effective/registrar.rb +19 -0
- data/app/models/effective/registrar_action.rb +97 -0
- data/app/views/admin/applicant_course_areas/_form.html.haml +8 -0
- data/app/views/admin/applicant_course_areas/_form_applicant_course_area.html.haml +8 -0
- data/app/views/admin/applicant_course_areas/index.html.haml +8 -0
- data/app/views/admin/applicant_course_name/_form.html.haml +9 -0
- data/app/views/admin/applicants/_form.html.haml +38 -0
- data/app/views/admin/applicants/_form_approve.html.haml +34 -0
- data/app/views/admin/applicants/_form_decline.html.haml +11 -0
- data/app/views/admin/applicants/_form_process.html.haml +18 -0
- data/app/views/admin/applicants/_status.html.haml +133 -0
- data/app/views/admin/categories/_form.html.haml +14 -0
- data/app/views/admin/categories/_form_applicant.html.haml +12 -0
- data/app/views/admin/categories/_form_applicant_content.html.haml +19 -0
- data/app/views/admin/categories/_form_applicant_eligibility.html.haml +13 -0
- data/app/views/admin/categories/_form_applicant_fees.html.haml +29 -0
- data/app/views/admin/categories/_form_applicant_steps.html.haml +42 -0
- data/app/views/admin/categories/_form_category.html.haml +10 -0
- data/app/views/admin/categories/_form_fee_payment.html.haml +9 -0
- data/app/views/admin/categories/_form_fee_payment_content.html.haml +19 -0
- data/app/views/admin/categories/_form_fee_payment_steps.html.haml +9 -0
- data/app/views/admin/categories/_form_renewals.html.haml +32 -0
- data/app/views/admin/fees/_fee.html.haml +1 -0
- data/app/views/admin/fees/_form.html.haml +14 -0
- data/app/views/admin/memberships/_status.html.haml +6 -0
- data/app/views/admin/registrar_actions/_form.html.haml +10 -0
- data/app/views/admin/registrar_actions/_form_bad_standing.html.haml +43 -0
- data/app/views/admin/registrar_actions/_form_fees_paid.html.haml +30 -0
- data/app/views/admin/registrar_actions/_form_reclassify.html.haml +43 -0
- data/app/views/admin/registrar_actions/_form_register.html.haml +44 -0
- data/app/views/admin/registrar_actions/_form_remove.html.haml +17 -0
- data/app/views/effective/applicant_references/_applicant_reference.html.haml +51 -0
- data/app/views/effective/applicant_references/_datatable_actions.html.haml +4 -0
- data/app/views/effective/applicant_references/_form.html.haml +18 -0
- data/app/views/effective/applicant_references/_form_declaration.html.haml +37 -0
- data/app/views/effective/applicant_references/complete.html.haml +3 -0
- data/app/views/effective/applicant_references/edit.html.haml +8 -0
- data/app/views/effective/applicants/_applicant.html.haml +6 -0
- data/app/views/effective/applicants/_content.html.haml +10 -0
- data/app/views/effective/applicants/_course_amounts.html.haml +19 -0
- data/app/views/effective/applicants/_dashboard.html.haml +19 -0
- data/app/views/effective/applicants/_declarations.html.haml +16 -0
- data/app/views/effective/applicants/_demographics.html.haml +9 -0
- data/app/views/effective/applicants/_demographics_fields.html.haml +11 -0
- data/app/views/effective/applicants/_demographics_owner.html.haml +20 -0
- data/app/views/effective/applicants/_education.html.haml +14 -0
- data/app/views/effective/applicants/_experience.html.haml +28 -0
- data/app/views/effective/applicants/_files.html.haml +27 -0
- data/app/views/effective/applicants/_layout.html.haml +3 -0
- data/app/views/effective/applicants/_orders.html.haml +4 -0
- data/app/views/effective/applicants/_references.html.haml +15 -0
- data/app/views/effective/applicants/_summary.html.haml +40 -0
- data/app/views/effective/applicants/billing.html.haml +14 -0
- data/app/views/effective/applicants/checkout.html.haml +6 -0
- data/app/views/effective/applicants/course_amounts.html.haml +50 -0
- data/app/views/effective/applicants/declarations.html.haml +19 -0
- data/app/views/effective/applicants/demographics.html.haml +11 -0
- data/app/views/effective/applicants/education.html.haml +27 -0
- data/app/views/effective/applicants/experience.html.haml +51 -0
- data/app/views/effective/applicants/files.html.haml +14 -0
- data/app/views/effective/applicants/references.html.haml +24 -0
- data/app/views/effective/applicants/select.html.haml +27 -0
- data/app/views/effective/applicants/start.html.haml +18 -0
- data/app/views/effective/applicants/submitted.html.haml +44 -0
- data/app/views/effective/applicants/summary.html.haml +8 -0
- data/app/views/effective/fee_payments/_content.html.haml +8 -0
- data/app/views/effective/fee_payments/_declarations.html.haml +16 -0
- data/app/views/effective/fee_payments/_demographics.html.haml +9 -0
- data/app/views/effective/fee_payments/_demographics_fields.html.haml +11 -0
- data/app/views/effective/fee_payments/_demographics_owner.html.haml +20 -0
- data/app/views/effective/fee_payments/_fee_payment.html.haml +6 -0
- data/app/views/effective/fee_payments/_layout.html.haml +3 -0
- data/app/views/effective/fee_payments/_orders.html.haml +4 -0
- data/app/views/effective/fee_payments/billing.html.haml +14 -0
- data/app/views/effective/fee_payments/checkout.html.haml +6 -0
- data/app/views/effective/fee_payments/declarations.html.haml +20 -0
- data/app/views/effective/fee_payments/demographics.html.haml +12 -0
- data/app/views/effective/fee_payments/start.html.haml +20 -0
- data/app/views/effective/fee_payments/submitted.html.haml +14 -0
- data/app/views/effective/fee_payments/summary.html.haml +8 -0
- data/app/views/effective/fees/_dashboard.html.haml +22 -0
- data/app/views/effective/fees/_fee.html.haml +37 -0
- data/app/views/effective/memberships/_dashboard.html.haml +29 -0
- data/app/views/effective/memberships/_membership.html.haml +40 -0
- data/app/views/effective/memberships_mailer/applicant_approved.liquid +15 -0
- data/app/views/effective/memberships_mailer/applicant_declined.liquid +13 -0
- data/app/views/effective/memberships_mailer/applicant_reference_notification.liquid +15 -0
- data/app/views/layouts/effective_memberships_mailer_layout.html.haml +7 -0
- data/config/effective_memberships.rb +47 -0
- data/config/routes.rb +32 -0
- data/db/migrate/01_create_effective_memberships.rb.erb +380 -0
- data/db/seeds.rb +59 -0
- data/lib/effective_memberships/engine.rb +23 -0
- data/lib/effective_memberships/version.rb +3 -0
- data/lib/effective_memberships.rb +86 -0
- data/lib/generators/effective_memberships/install_generator.rb +40 -0
- data/lib/generators/templates/effective_memberships_mailer_preview.rb +4 -0
- data/lib/tasks/effective_memberships_tasks.rake +17 -0
- metadata +377 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module Effective
|
|
2
|
+
class ApplicantExperience < ActiveRecord::Base
|
|
3
|
+
belongs_to :applicant
|
|
4
|
+
|
|
5
|
+
log_changes(to: :applicant) if respond_to?(:log_changes)
|
|
6
|
+
|
|
7
|
+
LEVELS = ['Full Time', 'Part Time', 'Volunteer']
|
|
8
|
+
ONE_HUNDRED_PERCENT = 100_000
|
|
9
|
+
|
|
10
|
+
effective_resource do
|
|
11
|
+
position :string
|
|
12
|
+
employer :string
|
|
13
|
+
|
|
14
|
+
start_on :date
|
|
15
|
+
end_on :date
|
|
16
|
+
|
|
17
|
+
percent_worked :integer # 3 digits of precision. 50_000 == 50%
|
|
18
|
+
still_work_here :boolean
|
|
19
|
+
|
|
20
|
+
level :string
|
|
21
|
+
tasks_performed :text
|
|
22
|
+
|
|
23
|
+
months :integer
|
|
24
|
+
|
|
25
|
+
timestamps
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
scope :deep, -> { includes(:applicant) }
|
|
29
|
+
|
|
30
|
+
before_validation(if: -> { start_on.present? && end_on.present? }) do
|
|
31
|
+
self.months ||= ((end_on - start_on) / 1.month).to_i
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
validates :position, presence: true
|
|
35
|
+
validates :employer, presence: true
|
|
36
|
+
validates :start_on, presence: true
|
|
37
|
+
validates :end_on, presence: true
|
|
38
|
+
validates :level, presence: true, inclusion: { in: LEVELS }
|
|
39
|
+
validates :months, presence: true
|
|
40
|
+
|
|
41
|
+
validates :percent_worked, presence: true,
|
|
42
|
+
inclusion: { in: 0..ONE_HUNDRED_PERCENT, message: 'must be between 0 and 100%' }
|
|
43
|
+
|
|
44
|
+
validate(if: -> { start_on.present? && end_on.present? }) do
|
|
45
|
+
errors.add(:end_on, 'must be after start date') unless start_on < end_on
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def to_s
|
|
49
|
+
position.presence || 'work experience'
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def assign_months!
|
|
53
|
+
return 0 unless end_on.present? && start_on.present?
|
|
54
|
+
|
|
55
|
+
self.percent_worked ||= (part_time? ? 0 : ONE_HUNDRED_PERCENT)
|
|
56
|
+
|
|
57
|
+
months = ((end_on.end_of_day.to_time - start_on.to_time) / 1.month.second).ceil
|
|
58
|
+
months *= (percent_worked / ONE_HUNDRED_PERCENT.to_f)
|
|
59
|
+
|
|
60
|
+
self.months = months
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def percent_worked_to_s
|
|
64
|
+
(percent_worked.to_i / 1000.to_f).to_i.to_s + '%'
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def full_time?
|
|
68
|
+
level == 'Full Time'
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def part_time?
|
|
72
|
+
level == 'Part Time'
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def volunteer?
|
|
76
|
+
level == 'Volunteer'
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module Effective
|
|
2
|
+
class ApplicantReference < ActiveRecord::Base
|
|
3
|
+
acts_as_tokened
|
|
4
|
+
acts_as_addressable :reference
|
|
5
|
+
acts_as_statused :submitted, :completed
|
|
6
|
+
log_changes(to: :applicant) if respond_to?(:log_changes)
|
|
7
|
+
|
|
8
|
+
belongs_to :applicant
|
|
9
|
+
|
|
10
|
+
KNOWNS = ['6 months - 1 year', '1 year - 2 years', '2 - 5 years', '5 - 10 years', 'More than 10 years']
|
|
11
|
+
RELATIONSHIPS = ['Supervisor', 'Employer', 'Colleague', 'Other']
|
|
12
|
+
|
|
13
|
+
effective_resource do
|
|
14
|
+
# These two fields are submitted on the Applicant wizard step
|
|
15
|
+
name :string
|
|
16
|
+
email :string
|
|
17
|
+
phone :string
|
|
18
|
+
|
|
19
|
+
# As per acts_as_statused. For tracking the state machine.
|
|
20
|
+
status :string
|
|
21
|
+
status_steps :text
|
|
22
|
+
|
|
23
|
+
# Reference Declaration
|
|
24
|
+
known :string
|
|
25
|
+
relationship :string
|
|
26
|
+
|
|
27
|
+
reservations :boolean
|
|
28
|
+
reservations_reason :text
|
|
29
|
+
|
|
30
|
+
work_history :text
|
|
31
|
+
|
|
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, -> { includes(:applicant) }
|
|
42
|
+
|
|
43
|
+
# All step validations
|
|
44
|
+
validates :applicant, presence: true
|
|
45
|
+
|
|
46
|
+
validates :name, presence: true
|
|
47
|
+
validates :email, presence: true, email: true
|
|
48
|
+
validates :phone, presence: true
|
|
49
|
+
|
|
50
|
+
validates :relationship, presence: true
|
|
51
|
+
validates :known, presence: true
|
|
52
|
+
|
|
53
|
+
# When being submit by the reference
|
|
54
|
+
with_options(if: -> { completed? }) do
|
|
55
|
+
validates :reference_address, presence: true
|
|
56
|
+
validates :reservations_reason, presence: true, if: -> { completed? && reservations? }
|
|
57
|
+
validates :work_history, presence: true
|
|
58
|
+
validates :accept_declaration, acceptance: true
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
after_commit(on: :create, if: -> { applicant.submitted? }) { notify! }
|
|
62
|
+
|
|
63
|
+
def to_s
|
|
64
|
+
'reference'
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def notify!
|
|
68
|
+
raise('expected reference email') unless email.present?
|
|
69
|
+
|
|
70
|
+
EffectiveMemberships.send_email(:applicant_reference_notification, self)
|
|
71
|
+
update!(last_notified_at: Time.zone.now)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def complete!
|
|
75
|
+
completed!
|
|
76
|
+
applicant.save!
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
module Effective
|
|
2
|
+
class Fee < ActiveRecord::Base
|
|
3
|
+
acts_as_purchasable
|
|
4
|
+
|
|
5
|
+
log_changes(to: :owner) if respond_to?(:log_changes)
|
|
6
|
+
|
|
7
|
+
# Every fee is charged to a owner
|
|
8
|
+
belongs_to :owner, polymorphic: true
|
|
9
|
+
|
|
10
|
+
# This fee may belong to an application, fee payment, or other parent model
|
|
11
|
+
belongs_to :parent, polymorphic: true, optional: true
|
|
12
|
+
|
|
13
|
+
# The membership category for this fee, if there's only 1 membership.categories
|
|
14
|
+
belongs_to :category, polymorphic: true, optional: true
|
|
15
|
+
|
|
16
|
+
effective_resource do
|
|
17
|
+
fee_type :string
|
|
18
|
+
|
|
19
|
+
title :string
|
|
20
|
+
|
|
21
|
+
period :date
|
|
22
|
+
|
|
23
|
+
late_on :date
|
|
24
|
+
bad_standing_on :date
|
|
25
|
+
|
|
26
|
+
price :integer
|
|
27
|
+
qb_item_name :string
|
|
28
|
+
tax_exempt :boolean
|
|
29
|
+
|
|
30
|
+
timestamps
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
scope :sorted, -> { order(:id) }
|
|
34
|
+
scope :deep, -> { includes(:owner, :parent, :category) }
|
|
35
|
+
|
|
36
|
+
before_validation(if: -> { owner.present? }) do
|
|
37
|
+
additional = owner.additional_fee_attributes(self)
|
|
38
|
+
raise('expected a Hash of attributes') unless additional.kind_of?(Hash)
|
|
39
|
+
assign_attributes(additional)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
before_validation(if: -> { owner && owner.membership }) do
|
|
43
|
+
self.category ||= owner.membership.categories.first if owner.membership.categories.length == 1
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
before_validation do
|
|
47
|
+
self.period ||= default_period()
|
|
48
|
+
self.late_on ||= default_late_on()
|
|
49
|
+
self.bad_standing_on ||= default_bad_standing_on()
|
|
50
|
+
|
|
51
|
+
self.qb_item_name ||= default_qb_item_name()
|
|
52
|
+
self.tax_exempt = default_tax_exempt() if tax_exempt.nil?
|
|
53
|
+
|
|
54
|
+
self.title ||= default_title()
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
validates :fee_type, presence: true
|
|
58
|
+
validates :price, presence: true
|
|
59
|
+
|
|
60
|
+
validates :title, presence: true
|
|
61
|
+
validates :period, presence: true
|
|
62
|
+
validates :qb_item_name, presence: true
|
|
63
|
+
|
|
64
|
+
validate(if: -> { fee_type.present? }) do
|
|
65
|
+
self.errors.add(:fee_type, 'is not included') unless EffectiveMemberships.fee_types.include?(fee_type)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
validates :late_on, presence: true,
|
|
69
|
+
if: -> { fee_type == 'Renewal' && category&.create_late_fees? }
|
|
70
|
+
|
|
71
|
+
validates :bad_standing_on, presence: true,
|
|
72
|
+
if: -> { fee_type == 'Renewal' && category&.create_bad_standing? }
|
|
73
|
+
|
|
74
|
+
def to_s
|
|
75
|
+
title.presence || default_title()
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def late?
|
|
79
|
+
return false if late_on.blank?
|
|
80
|
+
return false if purchased?
|
|
81
|
+
|
|
82
|
+
late_on <= Time.zone.now.to_date
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def bad_standing?
|
|
86
|
+
return false if bad_standing_on.blank?
|
|
87
|
+
return false if purchased?
|
|
88
|
+
|
|
89
|
+
bad_standing_on <= Time.zone.now.to_date
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Used by applicant.applicant_submit_fees
|
|
93
|
+
def applicant_submit_fee?
|
|
94
|
+
fee_type == 'Applicant'
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def fee_payment_fee?
|
|
98
|
+
fee_type != 'Applicant'
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Will advance a membership.fees_paid_through_year value when purchased
|
|
102
|
+
def membership_period_fee?
|
|
103
|
+
fee_type == 'Prorated' || fee_type == 'Renewal'
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def custom_fee?
|
|
107
|
+
EffectiveMemberships.custom_fee_types.include?(fee_type)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
def default_period
|
|
113
|
+
EffectiveMemberships.Registrar.current_period
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def default_late_on
|
|
117
|
+
nil
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def default_bad_standing_on
|
|
121
|
+
nil
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def default_title
|
|
125
|
+
[
|
|
126
|
+
period&.strftime('%Y').presence,
|
|
127
|
+
category.to_s.presence,
|
|
128
|
+
fee_type.presence,
|
|
129
|
+
'Fee'
|
|
130
|
+
].join(' ')
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def default_qb_item_name
|
|
134
|
+
"#{fee_type} Fee"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def default_tax_exempt
|
|
138
|
+
false
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
module Effective
|
|
2
|
+
class Membership < ActiveRecord::Base
|
|
3
|
+
belongs_to :owner, polymorphic: true
|
|
4
|
+
|
|
5
|
+
has_many :membership_categories, -> { order(:id) }, inverse_of: :membership, dependent: :delete_all
|
|
6
|
+
accepts_nested_attributes_for :membership_categories
|
|
7
|
+
|
|
8
|
+
log_changes(to: :owner) if respond_to?(:log_changes)
|
|
9
|
+
|
|
10
|
+
effective_resource do
|
|
11
|
+
# Membership Info
|
|
12
|
+
number :string # A unique value
|
|
13
|
+
number_as_integer :integer # A unique integer
|
|
14
|
+
|
|
15
|
+
joined_on :date # When they first receive a membership category
|
|
16
|
+
registration_on :date # When the membership category last changed. Applied or reclassified.
|
|
17
|
+
|
|
18
|
+
# Membership Status
|
|
19
|
+
fees_paid_period :date # The most recent period they have paid in. Start date of period.
|
|
20
|
+
fees_paid_through_period :date # The most recent period they have paid in. End date of period. Kind of an expires.
|
|
21
|
+
|
|
22
|
+
bad_standing :boolean # Calculated value. Is this owner in bad standing? (fees due)
|
|
23
|
+
bad_standing_admin :boolean # Admin set this
|
|
24
|
+
bad_standing_reason :text # Reason for bad standing
|
|
25
|
+
|
|
26
|
+
timestamps
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
scope :deep, -> { includes(owner: [:fees, :membership]) }
|
|
30
|
+
scope :sorted, -> { order(:id) }
|
|
31
|
+
|
|
32
|
+
scope :with_paid_fees_through, -> (period = nil) {
|
|
33
|
+
where(arel_table[:fees_paid_period].gteq(period || EffectiveMemberships.Registrar.current_period))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
scope :with_unpaid_fees_through, -> (period = nil) {
|
|
37
|
+
where(arel_table[:fees_paid_period].lt(period || EffectiveMemberships.Registrar.current_period))
|
|
38
|
+
.or(where(fees_paid_period: nil))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
scope :create_renewal_fees, -> (period = nil) {
|
|
42
|
+
deep.with_unpaid_fees_through(period)
|
|
43
|
+
.where.not(fees_paid_period: nil) # Must have purchased a Prorated or Renewal Fee before
|
|
44
|
+
#.where(category_id: EffectiveMemberships.Category.create_renewal_fees)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
scope :create_late_fees, -> (period = nil) {
|
|
48
|
+
deep.with_unpaid_fees_through(period)
|
|
49
|
+
.where.not(fees_paid_period: nil) # Must have purchased a Prorated or Renewal Fee before
|
|
50
|
+
#.where(category_id: EffectiveMemberships.Category.create_late_fees)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
scope :create_bad_standing, -> (period = nil) {
|
|
54
|
+
deep.with_unpaid_fees_through(period)
|
|
55
|
+
.where.not(fees_paid_period: nil) # Must have purchased a Prorated or Renewal Fee before
|
|
56
|
+
#.where(category_id: EffectiveMemberships.Category.create_bad_standing)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
before_validation do
|
|
60
|
+
self.registration_on ||= joined_on
|
|
61
|
+
self.number_as_integer ||= (Integer(number) rescue nil)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
validates :number, presence: true, uniqueness: true
|
|
65
|
+
validates :joined_on, presence: true
|
|
66
|
+
validates :registration_on, presence: true
|
|
67
|
+
|
|
68
|
+
validate(if: -> { owner.present? }) do
|
|
69
|
+
self.errors.add(:owner_id, 'must be a memberships owner') unless owner.class.effective_memberships_owner?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.max_number
|
|
73
|
+
maximum('number_as_integer') || 0
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def to_s
|
|
77
|
+
'membership'
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# We can't use the polymorphic has_many. So this is a helper.
|
|
81
|
+
def categories
|
|
82
|
+
membership_categories.reject(&:marked_for_destruction?).map(&:category)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def category_ids
|
|
86
|
+
membership_categories.reject(&:marked_for_destruction?).map(&:category_id)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# We might want to use singular memberships.
|
|
90
|
+
def category
|
|
91
|
+
raise('expected singular usage but there are more than one membership') if categories.length > 1
|
|
92
|
+
categories.first
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def category_id
|
|
96
|
+
raise('expected singular usage but there are more than one membership') if categories.length > 1
|
|
97
|
+
categories.first.id
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def membership_category(category:)
|
|
101
|
+
raise('expected a category') unless category.class.respond_to?(:effective_memberships_category?)
|
|
102
|
+
membership_categories.find { |mc| mc.category_id == category.id && mc.category_type == category.class.name }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# find or build
|
|
106
|
+
def build_membership_category(category:)
|
|
107
|
+
membership_category(category: category) || membership_categories.build(category: category)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def good_standing?
|
|
111
|
+
!bad_standing?
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def fees_paid?
|
|
115
|
+
fees_paid_period == EffectiveMemberships.Registrar.current_period
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Effective
|
|
2
|
+
class MembershipHistory < ActiveRecord::Base
|
|
3
|
+
belongs_to :owner, polymorphic: true
|
|
4
|
+
|
|
5
|
+
effective_resource do
|
|
6
|
+
start_on :date
|
|
7
|
+
end_on :date
|
|
8
|
+
|
|
9
|
+
number :string
|
|
10
|
+
|
|
11
|
+
bad_standing :boolean
|
|
12
|
+
removed :boolean
|
|
13
|
+
|
|
14
|
+
categories :text
|
|
15
|
+
category_ids :text
|
|
16
|
+
|
|
17
|
+
notes :text
|
|
18
|
+
|
|
19
|
+
timestamps
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
serialize :categories, Array
|
|
23
|
+
serialize :category_ids, Array
|
|
24
|
+
|
|
25
|
+
scope :deep, -> { includes(:owner) }
|
|
26
|
+
scope :sorted, -> { order(:start_on) }
|
|
27
|
+
|
|
28
|
+
validates :owner, presence: true
|
|
29
|
+
|
|
30
|
+
validates :categories, presence: true, unless: -> { removed? }
|
|
31
|
+
validates :category_ids, presence: true, unless: -> { removed? }
|
|
32
|
+
|
|
33
|
+
validates :start_on, presence: true
|
|
34
|
+
|
|
35
|
+
def to_s
|
|
36
|
+
'membership history'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Effective
|
|
2
|
+
class Registrar
|
|
3
|
+
|
|
4
|
+
include EffectiveMembershipsRegistrar
|
|
5
|
+
|
|
6
|
+
def renewal_fee_date(date:)
|
|
7
|
+
Date.new(date.year, 12, 1) # Fees roll over every December 1st
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def late_fee_date(period:)
|
|
11
|
+
Date.new(period.year, 2, 1) # Fees are late after February 1st
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def bad_standing_date(period:)
|
|
15
|
+
Date.new(period.year, 3, 1) # Membership in bad standing after March 1st
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Form Object for the Admin Change Status screens
|
|
2
|
+
|
|
3
|
+
module Effective
|
|
4
|
+
class RegistrarAction
|
|
5
|
+
include ActiveModel::Model
|
|
6
|
+
|
|
7
|
+
# All Actions
|
|
8
|
+
attr_accessor :current_user
|
|
9
|
+
attr_accessor :current_action
|
|
10
|
+
attr_accessor :owner, :owner_id, :owner_type
|
|
11
|
+
|
|
12
|
+
# Bad Standing
|
|
13
|
+
attr_accessor :bad_standing_reason
|
|
14
|
+
|
|
15
|
+
# Reclassify & Register
|
|
16
|
+
attr_accessor :category_id
|
|
17
|
+
attr_accessor :membership_number
|
|
18
|
+
attr_accessor :skip_fees
|
|
19
|
+
|
|
20
|
+
# All Action Validations
|
|
21
|
+
validates :current_action, presence: true
|
|
22
|
+
validates :current_user, presence: true
|
|
23
|
+
validates :owner, presence: true
|
|
24
|
+
|
|
25
|
+
# Bad Standing
|
|
26
|
+
validates :bad_standing_reason, presence: true, if: -> { current_action == :bad_standing }
|
|
27
|
+
|
|
28
|
+
# Reclassification & Register
|
|
29
|
+
validates :category_id, presence: true,
|
|
30
|
+
if: -> { current_action == :reclassify || current_action == :register }
|
|
31
|
+
|
|
32
|
+
def to_s
|
|
33
|
+
'action'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def good_standing!
|
|
37
|
+
update!(current_action: :good_standing)
|
|
38
|
+
EffectiveMemberships.Registrar.good_standing!(owner)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def bad_standing!
|
|
42
|
+
update!(current_action: :bad_standing)
|
|
43
|
+
EffectiveMemberships.Registrar.bad_standing!(owner, reason: bad_standing_reason)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def reclassify!
|
|
47
|
+
update!(current_action: :reclassify)
|
|
48
|
+
EffectiveMemberships.Registrar.reclassify!(owner, to: category, skip_fees: skip_fees?)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def fees_paid!
|
|
52
|
+
update!(current_action: :fees_paid)
|
|
53
|
+
EffectiveMemberships.Registrar.fees_paid!(owner)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def remove!
|
|
57
|
+
update!(current_action: :remove)
|
|
58
|
+
EffectiveMemberships.Registrar.remove!(owner)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def register!
|
|
62
|
+
update!(current_action: :register)
|
|
63
|
+
EffectiveMemberships.Registrar.register!(owner, to: category, number: membership_number.presence, skip_fees: skip_fees?)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def update!(atts)
|
|
67
|
+
assign_attributes(atts); save!
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def save!
|
|
71
|
+
valid? ? true : raise('invalid')
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def owner
|
|
75
|
+
@owner ||= (@owner_type.constantize.find(@owner_id) if @owner_type && @owner_id)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def owner_type
|
|
79
|
+
@owner_type || (@owner.class.name if @owner)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def owner_id
|
|
83
|
+
@owner_id || (@owner.id if @owner)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def category
|
|
89
|
+
EffectiveMemberships.Category.find(@category_id) if @category_id
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def skip_fees?
|
|
93
|
+
EffectiveResources.truthy?(@skip_fees)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
= tabs do
|
|
2
|
+
= tab 'Course Area' do
|
|
3
|
+
= render 'admin/applicant_course_areas/form_applicant_course_area', applicant_course_area: applicant_course_area
|
|
4
|
+
|
|
5
|
+
- if applicant_course_area.persisted?
|
|
6
|
+
- if applicant_course_area.respond_to?(:log_changes_datatable)
|
|
7
|
+
= tab 'Logs' do
|
|
8
|
+
= render_inline_datatable(applicant_course_area.log_changes_datatable)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
= effective_form_with(model: [:admin, applicant_course_area], engine: true) do |f|
|
|
2
|
+
= f.text_field :title
|
|
3
|
+
= effective_submit(f)
|
|
4
|
+
|
|
5
|
+
- if applicant_course_area.persisted?
|
|
6
|
+
%h2 Course Names
|
|
7
|
+
- datatable = Admin::EffectiveApplicantCourseNamesDatatable.new(applicant_course_area_id: applicant_course_area.id)
|
|
8
|
+
= render_datatable(datatable, inline: true)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
= effective_form_with(model: [:admin, applicant_course_name], engine: true) do |f|
|
|
2
|
+
- if inline_datatable?
|
|
3
|
+
= f.hidden_field :applicant_course_area_id
|
|
4
|
+
- else
|
|
5
|
+
= f.select :applicant_course_area_id, Effective::ApplicantCourseArea.all
|
|
6
|
+
|
|
7
|
+
= f.text_field :title
|
|
8
|
+
|
|
9
|
+
= f.submit
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
= tabs do
|
|
2
|
+
- # Done applicants have the Applicant tab first. In progress ones the Status tab
|
|
3
|
+
- if applicant.done?
|
|
4
|
+
= tab 'Applicant' do
|
|
5
|
+
.mb-4= render 'effective/applicants/summary', applicant: applicant, namespace: :admin
|
|
6
|
+
= render 'effective/applicants/applicant', applicant: applicant, namespace: :admin
|
|
7
|
+
|
|
8
|
+
= tab 'Status' do
|
|
9
|
+
= render 'admin/applicants/status', applicant: applicant, namespace: :admin
|
|
10
|
+
|
|
11
|
+
- if applicant.in_progress?
|
|
12
|
+
= tab 'Status' do
|
|
13
|
+
= render 'admin/applicants/status', applicant: applicant, namespace: :admin
|
|
14
|
+
|
|
15
|
+
= tab 'Applicant' do
|
|
16
|
+
.mb-4= render 'effective/applicants/summary', applicant: applicant, namespace: :admin
|
|
17
|
+
= render 'effective/applicants/applicant', applicant: applicant, namespace: :admin
|
|
18
|
+
|
|
19
|
+
- # Just normal tabs now
|
|
20
|
+
- if applicant.was_submitted? && !applicant.was_approved?
|
|
21
|
+
= tab 'Process' do
|
|
22
|
+
= render 'admin/applicants/form_process', applicant: applicant
|
|
23
|
+
|
|
24
|
+
- if applicant.fees.present?
|
|
25
|
+
= tab 'Fees' do
|
|
26
|
+
.mb-4= render_inline_datatable(Admin::EffectiveFeesDatatable.new(applicant_id: applicant.to_param))
|
|
27
|
+
|
|
28
|
+
- if applicant.orders.present?
|
|
29
|
+
= tab 'Orders' do
|
|
30
|
+
.mb-4= render_datatable(Admin::EffectiveOrdersDatatable.new(parent_id: applicant.id, parent_type: EffectiveMemberships.Applicant.name, owner_id: applicant.owner_id, user_type: applicant.owner_type))
|
|
31
|
+
|
|
32
|
+
- if applicant.owner.applicants.any? { |other| other.was_submitted? && other.id != applicant.id }
|
|
33
|
+
= tab 'Other Applications' do
|
|
34
|
+
= render_datatable(Admin::EffectiveApplicantsDatatable.new(owner_id: applicant.owner_id, except_id: applicant.id))
|
|
35
|
+
|
|
36
|
+
- if applicant.persisted? && applicant.respond_to?(:log_changes_datatable)
|
|
37
|
+
= tab 'Logs' do
|
|
38
|
+
= render_inline_datatable(applicant.log_changes_datatable)
|