effective_memberships 0.9.6 → 0.9.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/admin/registrar_actions_controller.rb +4 -0
- data/app/controllers/effective/membership_directory_controller.rb +9 -0
- data/app/datatables/admin/effective_organizations_datatable.rb +1 -1
- data/app/datatables/effective_memberships_directory_datatable.rb +12 -12
- data/app/models/concerns/effective_memberships_applicant.rb +11 -7
- data/app/models/concerns/effective_memberships_category.rb +3 -0
- data/app/models/concerns/effective_memberships_directory.rb +44 -18
- data/app/models/concerns/effective_memberships_organization.rb +4 -0
- data/app/models/concerns/effective_memberships_owner.rb +23 -7
- data/app/models/concerns/effective_memberships_registrar.rb +68 -16
- data/app/models/effective/membership.rb +32 -9
- data/app/models/effective/membership_history.rb +6 -1
- data/app/models/effective/registrar_action.rb +9 -1
- data/app/views/admin/applicants/_form_approve.html.haml +10 -0
- data/app/views/admin/memberships/_form.html.haml +4 -0
- data/app/views/admin/registrar_actions/_form.html.haml +3 -0
- data/app/views/admin/registrar_actions/_form_reinstatement.html.haml +44 -0
- data/app/views/admin/registrar_actions/_form_remove.html.haml +5 -0
- data/app/views/effective/applicants/select/_apply_for_reinstatement.html.haml +1 -3
- data/app/views/effective/applicants/select.html.haml +7 -11
- data/app/views/effective/membership_directory/_form.html.haml +8 -5
- data/app/views/effective/membership_directory/_membership.html.haml +20 -0
- data/app/views/effective/membership_directory/_membership_directory.html.haml +4 -4
- data/app/views/effective/membership_directory/show.html.haml +25 -0
- data/app/views/effective/memberships/_dashboard.html.haml +1 -0
- data/config/routes.rb +1 -1
- data/db/migrate/01_create_effective_memberships.rb.erb +3 -0
- data/lib/effective_memberships/version.rb +1 -1
- metadata +5 -3
- data/app/views/effective/membership_directory/_membership_owner.html.haml +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 612909b87d53748d9b3488893e419cf6f8bdcb852fdb07d3bd0f015bda89771c
|
4
|
+
data.tar.gz: 7c48d16e502036542d4f80e785986134e19466593b44ecce9cef151e76387acc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 835021e63b547f68e17ae9bd9ca6bbc523217e1f4f7c3eaf100696ef4456fb63b4f384765cad26db941aca67203ddb87e157ac64620e41220f98571a23d9074b
|
7
|
+
data.tar.gz: 706ec5a09d03a23f5495ccae6ec1f28fb930a4a1533a41b15f6f9a859f6e1bbb1d19dd9df0f83e0e6a56fb0bd6abe6d378576e5d1d439a2fa9ebaa3adfec2d69
|
@@ -14,6 +14,10 @@ module Admin
|
|
14
14
|
success: -> { "#{resource.owner} has been reclassified to #{resource.owner.membership.category}" },
|
15
15
|
redirect: -> { admin_owners_path(resource) }
|
16
16
|
|
17
|
+
submit :reinstate, 'Reinstate',
|
18
|
+
success: -> { "#{resource.owner} has been reinstated to #{resource.owner.membership.category}" },
|
19
|
+
redirect: -> { admin_owners_path(resource) }
|
20
|
+
|
17
21
|
submit :status_assign, 'Assign Status',
|
18
22
|
success: -> { "#{resource.owner} has been assigned #{resource.owner.membership.statuses_sentence}" },
|
19
23
|
redirect: -> { admin_owners_path(resource) }
|
@@ -16,6 +16,15 @@ module Effective
|
|
16
16
|
@memberships = @membership_directory.results(page: params[:page])
|
17
17
|
end
|
18
18
|
|
19
|
+
def show
|
20
|
+
@page_title = 'Directory'
|
21
|
+
|
22
|
+
@membership = Effective::Membership.find_by_token(params[:id])
|
23
|
+
|
24
|
+
EffectiveResources.authorize!(self, :show, @membership)
|
25
|
+
EffectiveResources.authorize!(self, :show, @membership.owner)
|
26
|
+
end
|
27
|
+
|
19
28
|
def build_membership_directory
|
20
29
|
directory = EffectiveMemberships.MembershipDirectory.new(search_params)
|
21
30
|
directory.current_user = current_user
|
@@ -63,7 +63,7 @@ module Admin
|
|
63
63
|
end
|
64
64
|
|
65
65
|
collection do
|
66
|
-
EffectiveMemberships.Organization.deep.left_joins(:membership).includes(:addresses, membership: :
|
66
|
+
EffectiveMemberships.Organization.deep.left_joins(:membership).includes(:addresses, membership: [membership_categories: :category], representatives: :user)
|
67
67
|
end
|
68
68
|
|
69
69
|
def categories
|
@@ -3,25 +3,25 @@ class EffectiveMembershipsDirectoryDatatable < Effective::Datatable
|
|
3
3
|
datatable do
|
4
4
|
length 100
|
5
5
|
|
6
|
-
col
|
7
|
-
|
6
|
+
col :owner_name, label: 'Name'
|
8
7
|
col :joined_on
|
9
8
|
col :number
|
10
|
-
col :categories, search: :string, label: 'Category'
|
11
|
-
end
|
12
|
-
|
13
|
-
collection do
|
14
|
-
scope = Effective::Membership.directory.all
|
15
9
|
|
16
|
-
|
17
|
-
|
10
|
+
col(:categories, search: categories, label: 'Category', sql_column: true).search do |collection, term|
|
11
|
+
collection.with_category(EffectiveMemberships.Category.find(term))
|
18
12
|
end
|
19
13
|
|
20
|
-
scope
|
21
14
|
end
|
22
15
|
|
23
|
-
|
24
|
-
|
16
|
+
collection do
|
17
|
+
Effective::Membership.deep.sorted
|
18
|
+
.in_good_standing
|
19
|
+
.without_archived_owners
|
20
|
+
.with_category(categories)
|
21
|
+
end
|
22
|
+
|
23
|
+
def categories
|
24
|
+
EffectiveMemberships.Category.membership_directory.all
|
25
25
|
end
|
26
26
|
|
27
27
|
end
|
@@ -21,6 +21,10 @@ module EffectiveMembershipsApplicant
|
|
21
21
|
[:start, :select, :summary, :billing, :checkout, :submitted]
|
22
22
|
end
|
23
23
|
|
24
|
+
# Apply to Join - Anyone can do this
|
25
|
+
# Apply to Reclassify - Must have an existing membership and category
|
26
|
+
# Apply for Reinstatement - Must have a removed membership. Membership history.
|
27
|
+
|
24
28
|
def categories
|
25
29
|
['Apply to Join', 'Apply to Reclassify', 'Apply for Reinstatement']
|
26
30
|
end
|
@@ -101,7 +105,6 @@ module EffectiveMembershipsApplicant
|
|
101
105
|
# Required for Apply to Reclassify
|
102
106
|
belongs_to :from_category, polymorphic: true, optional: true
|
103
107
|
|
104
|
-
# Required for Apply for Reinstatement
|
105
108
|
belongs_to :from_status, polymorphic: true, optional: true
|
106
109
|
|
107
110
|
has_many :applicant_reviews, -> { order(:id) }, as: :applicant, inverse_of: :applicant, dependent: :destroy
|
@@ -202,8 +205,8 @@ module EffectiveMembershipsApplicant
|
|
202
205
|
self.from_category = owner.membership.categories.first
|
203
206
|
end
|
204
207
|
|
205
|
-
if
|
206
|
-
self.
|
208
|
+
if reinstatement?
|
209
|
+
self.from_category = owner.reinstatement_membership_category
|
207
210
|
end
|
208
211
|
end
|
209
212
|
|
@@ -215,7 +218,6 @@ module EffectiveMembershipsApplicant
|
|
215
218
|
validates :user, presence: true
|
216
219
|
|
217
220
|
validates :from_category, presence: true, if: -> { reclassification? }
|
218
|
-
validates :from_status, presence: true, if: -> { reinstatement? }
|
219
221
|
|
220
222
|
validate(if: -> { reclassification? && category_id.present? }) do
|
221
223
|
errors.add(:category_id, "can't reclassify to existing category") if category_id == from_category_id
|
@@ -603,10 +605,12 @@ module EffectiveMembershipsApplicant
|
|
603
605
|
|
604
606
|
# Used by the select step
|
605
607
|
def can_apply_applicant_types_collection
|
606
|
-
if owner.blank?
|
608
|
+
if owner.blank?
|
607
609
|
['Apply to Join']
|
608
|
-
elsif owner.
|
610
|
+
elsif owner.membership_removed?
|
609
611
|
['Apply for Reinstatement', 'Apply to Join']
|
612
|
+
elsif owner.membership.blank? || owner.membership.categories.blank?
|
613
|
+
['Apply to Join']
|
610
614
|
else
|
611
615
|
['Apply to Reclassify']
|
612
616
|
end
|
@@ -861,7 +865,7 @@ module EffectiveMembershipsApplicant
|
|
861
865
|
elsif reclassification?
|
862
866
|
EffectiveMemberships.Registrar.reclassify!(owner, to: to_category, status: to_status)
|
863
867
|
elsif reinstatement?
|
864
|
-
EffectiveMemberships.Registrar.reinstate!(owner,
|
868
|
+
EffectiveMemberships.Registrar.reinstate!(owner, to: to_category)
|
865
869
|
else
|
866
870
|
raise('unsupported approval applicant_type')
|
867
871
|
end
|
@@ -114,6 +114,9 @@ module EffectiveMembershipsCategory
|
|
114
114
|
.or(where(can_apply_restricted: true))
|
115
115
|
}
|
116
116
|
|
117
|
+
# Can be used to limit which memberships are displayed in the directory
|
118
|
+
scope :membership_directory, -> { sorted }
|
119
|
+
|
117
120
|
validates :title, presence: true, uniqueness: true
|
118
121
|
validates :category_type, presence: true
|
119
122
|
validates :position, presence: true
|
@@ -19,18 +19,29 @@ module EffectiveMembershipsDirectory
|
|
19
19
|
attr_accessor :term
|
20
20
|
attr_accessor :category
|
21
21
|
|
22
|
+
# Users
|
22
23
|
attr_accessor :first_name
|
23
24
|
attr_accessor :last_name
|
24
25
|
|
26
|
+
# Organizations
|
27
|
+
attr_accessor :title
|
28
|
+
|
25
29
|
validates :term, length: { minimum: 3, allow_blank: true }
|
26
30
|
validates :first_name, length: { minimum: 2, allow_blank: true }
|
27
31
|
validates :last_name, length: { minimum: 2, allow_blank: true }
|
32
|
+
validates :title, length: { minimum: 2, allow_blank: true }
|
33
|
+
end
|
34
|
+
|
35
|
+
def categories
|
36
|
+
EffectiveMemberships.Category.membership_directory.all
|
28
37
|
end
|
29
38
|
|
30
39
|
# Base collection to search. Can be configured per tenant.
|
31
40
|
def collection
|
32
|
-
|
33
|
-
|
41
|
+
Effective::Membership.deep.sorted
|
42
|
+
.in_good_standing
|
43
|
+
.without_archived_owners
|
44
|
+
.with_category(categories)
|
34
45
|
end
|
35
46
|
|
36
47
|
# Search Users and Organizations for only these fields. Passed into search_any. Return nil for all.
|
@@ -44,21 +55,21 @@ module EffectiveMembershipsDirectory
|
|
44
55
|
end
|
45
56
|
|
46
57
|
def present?
|
47
|
-
term.present? || first_name.present? || last_name.present? || category.present?
|
58
|
+
term.present? || first_name.present? || last_name.present? || title.present? || category.present?
|
48
59
|
end
|
49
60
|
|
50
61
|
# Search and assigns the collection
|
51
62
|
# Assigns the entire collection() if there are no search terms
|
52
63
|
# Otherwise validate the search terms
|
53
64
|
def search!
|
54
|
-
@
|
55
|
-
@
|
56
|
-
@
|
65
|
+
@memberships = build_collection()
|
66
|
+
@memberships = @memberships.none if present? && !valid?
|
67
|
+
@memberships
|
57
68
|
end
|
58
69
|
|
59
70
|
# The unpaginated results of the search
|
60
|
-
def
|
61
|
-
@
|
71
|
+
def memberships
|
72
|
+
@memberships || collection
|
62
73
|
end
|
63
74
|
|
64
75
|
# The paginated results
|
@@ -66,38 +77,53 @@ module EffectiveMembershipsDirectory
|
|
66
77
|
page = (page || 1).to_i
|
67
78
|
offset = [(page - 1), 0].max * per_page
|
68
79
|
|
69
|
-
|
80
|
+
memberships.limit(per_page).offset(offset)
|
70
81
|
end
|
71
82
|
|
72
83
|
protected
|
73
84
|
|
74
85
|
def build_collection
|
75
|
-
|
76
|
-
raise('expected an ActiveRecord collection') unless
|
77
|
-
raise('expected
|
86
|
+
memberships = collection()
|
87
|
+
raise('expected an ActiveRecord collection') unless memberships.kind_of?(ActiveRecord::Relation)
|
88
|
+
raise('expected an ActiveRecord collection of Effective::Memberships') unless memberships.klass.respond_to?(:effective_membership?)
|
78
89
|
|
79
90
|
# Filter by term
|
80
91
|
if term.present?
|
81
|
-
owners = Effective::Resource.new(
|
92
|
+
owners = owner_klasses.map { |klass| Effective::Resource.new(klass).search_any(term, columns: search_any_columns) }
|
93
|
+
memberships = memberships.where(owner: owners)
|
82
94
|
end
|
83
95
|
|
84
96
|
# Filter by first name
|
85
97
|
if first_name.present?
|
86
|
-
owners = Effective::Resource.new(
|
98
|
+
owners = owner_klasses.map { |klass| Effective::Resource.new(klass).search_any(first_name, columns: :first_name) }
|
99
|
+
memberships = memberships.where(owner: owners)
|
87
100
|
end
|
88
101
|
|
89
102
|
# Filter by last name
|
90
103
|
if last_name.present?
|
91
|
-
owners = Effective::Resource.new(
|
104
|
+
owners = owner_klasses.map { |klass| Effective::Resource.new(klass).search_any(last_name, columns: :last_name) }
|
105
|
+
memberships = memberships.where(owner: owners)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Filter by title
|
109
|
+
if title.present?
|
110
|
+
owners = owner_klasses.map { |klass| Effective::Resource.new(klass).search_any(last_name, columns: :title) }
|
111
|
+
memberships = memberships.where(owner: owners)
|
92
112
|
end
|
93
113
|
|
94
114
|
# Filter by category
|
95
115
|
if category.present?
|
96
|
-
|
97
|
-
owners = owners.members_with_category(cat) if cat.present?
|
116
|
+
memberships = memberships.with_category(EffectiveMemberships.Category.where(id: category))
|
98
117
|
end
|
99
118
|
|
100
|
-
|
119
|
+
# Return an ActiveRecord::Relation of Effective::Memberships
|
120
|
+
memberships
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def owner_klasses
|
126
|
+
Effective::Membership.owner_klasses
|
101
127
|
end
|
102
128
|
|
103
129
|
end
|
@@ -130,8 +130,25 @@ module EffectiveMembershipsOwner
|
|
130
130
|
end
|
131
131
|
|
132
132
|
def membership_removed_on
|
133
|
-
|
134
|
-
|
133
|
+
removed_membership_history.start_on if membership_removed?
|
134
|
+
end
|
135
|
+
|
136
|
+
# The membership history that is removed. Has exit statuses that may affect reinstatement fees.
|
137
|
+
def removed_membership_history
|
138
|
+
membership_histories.reverse.find { |history| history.removed? } if membership_removed?
|
139
|
+
end
|
140
|
+
|
141
|
+
# The membership history we would reinstate to. Should be the one just before removed.
|
142
|
+
def reinstatement_membership_history
|
143
|
+
membership_histories.reverse.find { |history| !history.removed? } if membership_removed?
|
144
|
+
end
|
145
|
+
|
146
|
+
def reinstatement_membership_category
|
147
|
+
reinstatement_membership_history.membership_categories.first if membership_removed?
|
148
|
+
end
|
149
|
+
|
150
|
+
def reinstatement_membership_statuses
|
151
|
+
reinstatement_membership_history.membership_statuses if membership_removed?
|
135
152
|
end
|
136
153
|
|
137
154
|
def registrar_action_categories(action)
|
@@ -139,7 +156,7 @@ module EffectiveMembershipsOwner
|
|
139
156
|
end
|
140
157
|
|
141
158
|
def registrar_action_statuses(action)
|
142
|
-
EffectiveMemberships.Status.sorted.all
|
159
|
+
EffectiveMemberships.Status.sorted.all
|
143
160
|
end
|
144
161
|
|
145
162
|
def after_build_fee(fee)
|
@@ -311,9 +328,7 @@ module EffectiveMembershipsOwner
|
|
311
328
|
EffectiveMemberships.Registrar.in_good_standing!(self)
|
312
329
|
end
|
313
330
|
|
314
|
-
if membership_histories.blank?
|
315
|
-
build_membership_history()
|
316
|
-
end
|
331
|
+
build_membership_history() if membership_histories.blank?
|
317
332
|
|
318
333
|
save!
|
319
334
|
end
|
@@ -361,9 +376,10 @@ module EffectiveMembershipsOwner
|
|
361
376
|
errors = []
|
362
377
|
history = membership_histories.first
|
363
378
|
last_history = membership_histories.last
|
379
|
+
was_removed = membership_histories.any? { |history| history.removed? }
|
364
380
|
|
365
381
|
# Check membership joined on date matches first history start date
|
366
|
-
if membership.joined_on != history.start_on
|
382
|
+
if membership.joined_on != history.start_on && !was_removed
|
367
383
|
errors << "The joined date #{membership.joined_on.strftime('%F')} does not match the first history start date of #{history.start_on.strftime('%F')}. Please change the first history start date to #{membership.joined_on.strftime('%F')} or update the joined date above."
|
368
384
|
end
|
369
385
|
|
@@ -131,9 +131,12 @@ module EffectiveMembershipsRegistrar
|
|
131
131
|
membership.registration_on = date # Always new registration_on
|
132
132
|
|
133
133
|
# Assign Number (this could be nil)
|
134
|
-
number = next_membership_number(owner, to: to) if number.
|
135
|
-
|
136
|
-
|
134
|
+
number = next_membership_number(owner, to: to) if number.nil?
|
135
|
+
|
136
|
+
if number.present?
|
137
|
+
membership.number = number
|
138
|
+
membership.number_as_integer = (Integer(number) rescue nil)
|
139
|
+
end
|
137
140
|
|
138
141
|
# Assign Category
|
139
142
|
membership.build_membership_category(category: to)
|
@@ -173,11 +176,13 @@ module EffectiveMembershipsRegistrar
|
|
173
176
|
|
174
177
|
date ||= Time.zone.now
|
175
178
|
|
179
|
+
# Existing Membership
|
176
180
|
membership = owner.membership
|
177
181
|
|
178
|
-
#
|
182
|
+
# Last Changed Date
|
179
183
|
membership.registration_on = date
|
180
184
|
|
185
|
+
# Assign Category
|
181
186
|
membership.build_membership_category(category: to)
|
182
187
|
membership.membership_category(category: from).mark_for_destruction
|
183
188
|
|
@@ -195,24 +200,60 @@ module EffectiveMembershipsRegistrar
|
|
195
200
|
save!(owner, date: date)
|
196
201
|
end
|
197
202
|
|
198
|
-
def reinstate!(owner,
|
203
|
+
def reinstate!(owner, to: nil, date: nil, skip_fees: false)
|
199
204
|
raise('expecting a memberships owner') unless owner.class.respond_to?(:effective_memberships_owner?)
|
200
|
-
raise('owner must have
|
205
|
+
raise('owner must have a removed membership.') unless owner.membership_removed?
|
206
|
+
|
207
|
+
history = owner.reinstatement_membership_history
|
208
|
+
raise('owner must have a reinstatement membership history.') if history.blank?
|
201
209
|
|
202
|
-
|
203
|
-
raise('
|
210
|
+
to ||= owner.reinstatement_membership_category
|
211
|
+
raise('expecting a to memberships category') unless to.class.respond_to?(:effective_memberships_category?)
|
204
212
|
|
213
|
+
statuses = owner.reinstatement_membership_statuses
|
214
|
+
|
215
|
+
# Default Date
|
205
216
|
date ||= Time.zone.now
|
217
|
+
period = period(date: date)
|
218
|
+
period_end_on = period_end_on(date: date)
|
206
219
|
|
207
|
-
|
208
|
-
membership
|
209
|
-
|
220
|
+
# Build a membership
|
221
|
+
membership = owner.build_membership
|
222
|
+
|
223
|
+
# Assign Dates
|
224
|
+
membership.joined_on = date
|
225
|
+
membership.registration_on = date
|
226
|
+
|
227
|
+
# Assign Number (this could be nil)
|
228
|
+
if history.number.present?
|
229
|
+
membership.number = history.number
|
230
|
+
membership.number_as_integer = (Integer(history.number) rescue nil)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Assign Category
|
234
|
+
membership.build_membership_category(category: to)
|
235
|
+
|
236
|
+
# Assign Statuses
|
237
|
+
statuses.each do |status|
|
238
|
+
membership.build_membership_status(status: status)
|
239
|
+
end
|
210
240
|
|
241
|
+
# Assign fees paid through period
|
242
|
+
if skip_fees
|
243
|
+
membership.fees_paid_period = period
|
244
|
+
membership.fees_paid_through_period = period_end_on
|
245
|
+
end
|
246
|
+
|
247
|
+
# Or, Build Fees
|
211
248
|
unless skip_fees
|
212
249
|
fee = owner.build_prorated_fee(date: date)
|
213
250
|
# This might already be present and purchased if joined and reinstated in the same period
|
214
251
|
end
|
215
252
|
|
253
|
+
# Assign member role
|
254
|
+
add_member_role(owner)
|
255
|
+
|
256
|
+
# Save owner
|
216
257
|
save!(owner, date: date)
|
217
258
|
end
|
218
259
|
|
@@ -267,16 +308,17 @@ module EffectiveMembershipsRegistrar
|
|
267
308
|
true
|
268
309
|
end
|
269
310
|
|
270
|
-
|
311
|
+
# When you remove, you can set a status(es) which may affect reinstatement applications
|
312
|
+
def remove!(owner, statuses: nil, date: nil, notes: nil)
|
313
|
+
statuses = Array(statuses)
|
314
|
+
|
271
315
|
raise('expecting a memberships owner') unless owner.class.respond_to?(:effective_memberships_owner?)
|
272
316
|
raise('expected a member with a membership') unless owner.membership.present?
|
317
|
+
raise('expecting an membership status (optional)') if statuses.present? && statuses.any? { |status| !status.class.respond_to?(:effective_memberships_status?) }
|
273
318
|
|
274
319
|
# Date
|
275
320
|
date ||= Time.zone.now
|
276
321
|
|
277
|
-
# Remove Membership
|
278
|
-
owner.membership.mark_for_destruction
|
279
|
-
|
280
322
|
# Delete unpurchased fees and orders
|
281
323
|
owner.outstanding_fee_payment_fees.each { |fee| fee.mark_for_destruction }
|
282
324
|
owner.outstanding_fee_payment_orders.each { |order| order.mark_for_destruction }
|
@@ -284,7 +326,17 @@ module EffectiveMembershipsRegistrar
|
|
284
326
|
# Remove member role
|
285
327
|
remove_member_role(owner)
|
286
328
|
|
287
|
-
|
329
|
+
# Remove Membership
|
330
|
+
owner.membership.mark_for_destruction
|
331
|
+
|
332
|
+
# We do the save a bit differently here because we want to set the removed statuses
|
333
|
+
history = owner.build_membership_history(start_on: date, notes: notes)
|
334
|
+
|
335
|
+
if statuses.present?
|
336
|
+
history.assign_attributes(statuses: statuses.map(&:to_s), status_ids: statuses.map(&:id))
|
337
|
+
end
|
338
|
+
|
339
|
+
owner.save!
|
288
340
|
end
|
289
341
|
|
290
342
|
def not_in_good_standing!(owner, date: nil, notes: nil)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module Effective
|
2
2
|
class Membership < ActiveRecord::Base
|
3
3
|
belongs_to :owner, polymorphic: true
|
4
|
+
has_secure_token
|
4
5
|
|
5
6
|
attr_accessor :current_action
|
6
7
|
|
@@ -24,30 +25,34 @@ module Effective
|
|
24
25
|
fees_paid_period :date # The most recent period they have paid in. Start date of period.
|
25
26
|
fees_paid_through_period :date # The most recent period they have paid in. End date of period. Kind of an expires.
|
26
27
|
|
28
|
+
# Membership Directory
|
29
|
+
owner_name :string
|
30
|
+
token :string
|
31
|
+
|
27
32
|
timestamps
|
28
33
|
end
|
29
34
|
|
30
35
|
scope :deep, -> { includes(:owner, membership_categories: :category, membership_statuses: :status) }
|
31
|
-
scope :sorted, -> { order(
|
36
|
+
scope :sorted, -> { order('lower(owner_name)') }
|
32
37
|
|
33
38
|
scope :with_status, -> (statuses) {
|
34
|
-
raise('expected an EffectiveMemberships.Status') unless Array(statuses).all? { |status| status.kind_of?(EffectiveMemberships.Status) }
|
35
|
-
where(id: MembershipStatus.where(
|
39
|
+
raise('expected an EffectiveMemberships.Status') unless statuses.class.respond_to?(:effective_memberships_status?) || Array(statuses).all? { |status| status.kind_of?(EffectiveMemberships.Status) }
|
40
|
+
where(id: MembershipStatus.where(status_id: statuses).select(:membership_id))
|
36
41
|
}
|
37
42
|
|
38
43
|
scope :without_status, -> (statuses) {
|
39
|
-
raise('expected an EffectiveMemberships.Status') unless Array(statuses).all? { |status| status.kind_of?(EffectiveMemberships.Status) }
|
40
|
-
where.not(id: MembershipStatus.where(
|
44
|
+
raise('expected an EffectiveMemberships.Status') unless statuses.class.respond_to?(:effective_memberships_status?) || Array(statuses).all? { |status| status.kind_of?(EffectiveMemberships.Status) }
|
45
|
+
where.not(id: MembershipStatus.where(status_id: statuses).select(:membership_id))
|
41
46
|
}
|
42
47
|
|
43
48
|
scope :with_category, -> (categories) {
|
44
|
-
raise('expected an EffectiveMemberships.Category') unless Array(categories).all? { |cat| cat.kind_of?(EffectiveMemberships.Category) }
|
45
|
-
where(id: MembershipCategory.where(
|
49
|
+
raise('expected an EffectiveMemberships.Category') unless categories.class.respond_to?(:effective_memberships_category?) || Array(categories).all? { |cat| cat.kind_of?(EffectiveMemberships.Category) }
|
50
|
+
where(id: MembershipCategory.where(category_id: categories).select(:membership_id))
|
46
51
|
}
|
47
52
|
|
48
53
|
scope :without_category, -> (categories) {
|
49
|
-
raise('expected an EffectiveMemberships.Category') unless Array(categories).all? { |cat| cat.kind_of?(EffectiveMemberships.Category) }
|
50
|
-
where.not(id: MembershipCategory.where(
|
54
|
+
raise('expected an EffectiveMemberships.Category') unless categories.class.respond_to?(:effective_memberships_category?) || Array(categories).all? { |cat| cat.kind_of?(EffectiveMemberships.Category) }
|
55
|
+
where.not(id: MembershipCategory.where(category_id: categories).select(:membership_id))
|
51
56
|
}
|
52
57
|
|
53
58
|
scope :joined_before, -> (date) {
|
@@ -73,14 +78,24 @@ module Effective
|
|
73
78
|
scope :not_in_good_standing, -> { with_status(EffectiveMemberships.Registrar.not_in_good_standing_status) }
|
74
79
|
scope :in_good_standing, -> { without_status(EffectiveMemberships.Registrar.not_in_good_standing_status) }
|
75
80
|
|
81
|
+
scope :without_archived_owners, -> {
|
82
|
+
where.not(owner: klass.acts_as_archived_owner_klasses.map { |klass| klass.archived })
|
83
|
+
}
|
84
|
+
|
76
85
|
before_validation do
|
77
86
|
self.registration_on ||= joined_on
|
78
87
|
end
|
79
88
|
|
89
|
+
before_validation(if: -> { owner.present? }) do
|
90
|
+
self.owner_name = owner.to_s
|
91
|
+
end
|
92
|
+
|
80
93
|
before_validation(if: -> { number_changed? }) do
|
81
94
|
self.number_as_integer = (number.present? ? (Integer(number) rescue nil) : nil)
|
82
95
|
end
|
83
96
|
|
97
|
+
validates :owner_name, presence: true
|
98
|
+
|
84
99
|
validates :number, uniqueness: { allow_blank: true }
|
85
100
|
validates :number, presence: true, if: -> { categories.any?(:membership_number_required?) }
|
86
101
|
|
@@ -100,11 +115,19 @@ module Effective
|
|
100
115
|
maximum('number_as_integer') || 0
|
101
116
|
end
|
102
117
|
|
118
|
+
def self.effective_membership?
|
119
|
+
true
|
120
|
+
end
|
121
|
+
|
103
122
|
def self.owner_klasses
|
104
123
|
klasses = Effective::Membership.distinct(:owner_type).pluck(:owner_type)
|
105
124
|
klasses.select { |klass| klass.safe_constantize }.map { |klass| klass.constantize }
|
106
125
|
end
|
107
126
|
|
127
|
+
def self.acts_as_archived_owner_klasses
|
128
|
+
owner_klasses.select { |klass| klass.try(:acts_as_archived?) }
|
129
|
+
end
|
130
|
+
|
108
131
|
def to_s
|
109
132
|
return 'membership' if owner.blank?
|
110
133
|
|
@@ -25,13 +25,18 @@ module Effective
|
|
25
25
|
serialize :status_ids, Array
|
26
26
|
|
27
27
|
scope :deep, -> { includes(:owner) }
|
28
|
-
scope :sorted, -> { order(:start_on) }
|
28
|
+
scope :sorted, -> { order(:start_on).order(:id) }
|
29
29
|
|
30
30
|
scope :removed, -> { where(removed: true) }
|
31
31
|
|
32
32
|
validates :owner, presence: true
|
33
33
|
validates :start_on, presence: true
|
34
34
|
|
35
|
+
with_options(if: -> { removed? }) do
|
36
|
+
validates :number, absence: { message: 'must be blank when removed' }
|
37
|
+
validates :category_ids, absence: { message: 'must be blank when removed' }
|
38
|
+
end
|
39
|
+
|
35
40
|
def to_s
|
36
41
|
'membership history'
|
37
42
|
end
|
@@ -17,6 +17,9 @@ module Effective
|
|
17
17
|
attr_accessor :membership_number
|
18
18
|
attr_accessor :skip_fees
|
19
19
|
|
20
|
+
# Reinstate
|
21
|
+
attr_accessor :skip_fees
|
22
|
+
|
20
23
|
# Status Change
|
21
24
|
attr_accessor :status_ids
|
22
25
|
attr_accessor :status_id
|
@@ -64,6 +67,11 @@ module Effective
|
|
64
67
|
EffectiveMemberships.Registrar.reclassify!(owner, to: category, skip_fees: skip_fees?)
|
65
68
|
end
|
66
69
|
|
70
|
+
def reinstate!
|
71
|
+
update!(current_action: :reinstate)
|
72
|
+
EffectiveMemberships.Registrar.reinstate!(owner, skip_fees: skip_fees?)
|
73
|
+
end
|
74
|
+
|
67
75
|
def status_assign!
|
68
76
|
update!(current_action: :status_assign)
|
69
77
|
EffectiveMemberships.Registrar.status_assign!(owner, status: statuses)
|
@@ -86,7 +94,7 @@ module Effective
|
|
86
94
|
|
87
95
|
def remove!
|
88
96
|
update!(current_action: :remove)
|
89
|
-
EffectiveMemberships.Registrar.remove!(owner)
|
97
|
+
EffectiveMemberships.Registrar.remove!(owner, statuses: statuses)
|
90
98
|
end
|
91
99
|
|
92
100
|
def assign_attributes(atts)
|
@@ -4,9 +4,13 @@
|
|
4
4
|
- categories = EffectiveMemberships.Category.all
|
5
5
|
= f.select :category_id, categories, label: 'Approve to'
|
6
6
|
|
7
|
+
- reinstatement = applicant.owner.reinstatement_membership_history if applicant.reinstatement?
|
8
|
+
|
7
9
|
%p
|
8
10
|
- if applicant.owner.membership&.number_was.present?
|
9
11
|
The member will keep their existing membership number: #{applicant.owner.membership.number}.
|
12
|
+
- elsif applicant.reinstatement? && reinstatement.number.present?
|
13
|
+
The member will keep their previous membership number: #{reinstatement.number}.
|
10
14
|
- else
|
11
15
|
- number = EffectiveMemberships.Registrar.next_membership_number(applicant.owner, to: applicant.category)
|
12
16
|
|
@@ -15,6 +19,9 @@
|
|
15
19
|
- else
|
16
20
|
No membership number will be assigned.
|
17
21
|
|
22
|
+
- if applicant.reinstatement?
|
23
|
+
%p The member will be reinstated with their previous status: #{badges(reinstatement.membership_statuses) || 'None'}.
|
24
|
+
|
18
25
|
%h3 Fees
|
19
26
|
%p The following fees will be created:
|
20
27
|
- month = Time.zone.now.strftime('%B')
|
@@ -27,6 +34,9 @@
|
|
27
34
|
%li A #{month} prorated fee to the new category
|
28
35
|
%li A #{month} discount fee from their old category
|
29
36
|
|
37
|
+
- if applicant.reinstatement?
|
38
|
+
%li A #{month} prorated fee to their reinstatement category
|
39
|
+
|
30
40
|
%p The following unpurchased fees will be deleted:
|
31
41
|
- outstanding_fee_payment_fees = applicant.owner.outstanding_fee_payment_fees
|
32
42
|
|
@@ -15,3 +15,7 @@
|
|
15
15
|
- if membership.blank? && owner.membership_histories.present?
|
16
16
|
= card('History') do
|
17
17
|
= render_datatable(Admin::EffectiveMembershipHistoriesDatatable.new(owner: owner), inline: true, simple: true)
|
18
|
+
|
19
|
+
- if owner.membership_removed?
|
20
|
+
- registrar_action = Effective::RegistrarAction.new(current_user: current_user, owner: owner)
|
21
|
+
= render 'admin/registrar_actions/form_reinstatement', registrar_action: registrar_action
|
@@ -15,3 +15,6 @@
|
|
15
15
|
.mb-4= render 'admin/registrar_actions/form_not_in_good_standing', registrar_action: registrar_action
|
16
16
|
.mb-4= render 'admin/registrar_actions/form_fees_paid', registrar_action: registrar_action
|
17
17
|
.mb-4= render 'admin/registrar_actions/form_remove', registrar_action: registrar_action
|
18
|
+
|
19
|
+
|
20
|
+
- # The reinstatement form is added by memberships/form and not here
|
@@ -0,0 +1,44 @@
|
|
1
|
+
.card
|
2
|
+
.card-body
|
3
|
+
%h5.card-title Reinstate
|
4
|
+
|
5
|
+
= effective_form_with(model: [:admin, registrar_action], url: effective_memberships.admin_registrar_actions_path) do |f|
|
6
|
+
= f.hidden_field :owner_id
|
7
|
+
= f.hidden_field :owner_type
|
8
|
+
|
9
|
+
- removed = f.object.owner.removed_membership_history
|
10
|
+
- reinstatement = f.object.owner.reinstatement_membership_history
|
11
|
+
|
12
|
+
- raise('expected a removed membership history') unless removed.present?
|
13
|
+
- raise('expected a reinstatement membership history') unless reinstatement.present?
|
14
|
+
|
15
|
+
- period = EffectiveMemberships.Registrar.current_period
|
16
|
+
|
17
|
+
%p.text-muted
|
18
|
+
Reinstate to previous membership and optionally create fees.
|
19
|
+
|
20
|
+
%p
|
21
|
+
This member was removed on #{removed.start_on.strftime('%F')} from
|
22
|
+
= reinstatement.membership_categories.first
|
23
|
+
= badges(reinstatement.membership_statuses)
|
24
|
+
- if reinstatement.number.present?
|
25
|
+
with number #{reinstatement.number}.
|
26
|
+
|
27
|
+
= f.check_box :current_action, label: 'Yes, reinstate this member to their previous category, status and number'
|
28
|
+
|
29
|
+
= f.show_if :current_action, true do
|
30
|
+
= f.check_box :skip_fees, label: 'Yes, skip creating fees and just set the category'
|
31
|
+
|
32
|
+
= f.hide_if :skip_fees, true do
|
33
|
+
%p The following fee(s) will be created:
|
34
|
+
- month = Time.zone.now.strftime('%B')
|
35
|
+
|
36
|
+
%ul
|
37
|
+
%li A #{month} prorated fee for the #{reinstatement.membership_categories.first} category
|
38
|
+
|
39
|
+
%p The member will be required to return to the website and make a fee payment
|
40
|
+
|
41
|
+
= f.show_if :skip_fees, true do
|
42
|
+
%p No fees will be created
|
43
|
+
|
44
|
+
= f.submit 'Reinstate', border: false, center: true, 'data-confirm': "Really reinstate #{f.object.owner}?"
|
@@ -13,5 +13,10 @@
|
|
13
13
|
= f.check_box :current_action, label: 'Yes, remove this member'
|
14
14
|
|
15
15
|
= f.show_if :current_action, true do
|
16
|
+
- statuses = f.object.owner.registrar_action_statuses(:remove)
|
17
|
+
|
18
|
+
- if statuses.present?
|
19
|
+
= f.select :status_ids, statuses, label: 'Remove with status', multiple: true, hint: 'Will be assigned to the membership history and may affect fees generated for reinstatement applicants'
|
20
|
+
|
16
21
|
= f.submit 'Remove', border: false, center: true,
|
17
22
|
'data-confirm': "Really remove #{f.object.owner}?"
|
@@ -3,7 +3,8 @@
|
|
3
3
|
|
4
4
|
- categories = resource.can_apply_categories_collection()
|
5
5
|
- applicant_types = resource.can_apply_applicant_types_collection()
|
6
|
-
- existing_category = resource.owner
|
6
|
+
- existing_category = resource.owner.membership&.category
|
7
|
+
- reinstatement_category = resource.owner.reinstatement_membership_category
|
7
8
|
|
8
9
|
= card do
|
9
10
|
= effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
|
@@ -20,11 +21,6 @@
|
|
20
21
|
= render('effective/applicants/select/apply_to_join', f: f)
|
21
22
|
= render('effective/applicants/select/categories', f: f, categories: categories)
|
22
23
|
|
23
|
-
- elsif applicant_types == ['Apply for Reinstatement']
|
24
|
-
= f.hidden_field :applicant_type, value: applicant_types.first
|
25
|
-
= f.hidden_field :category_id, value: existing_category.id
|
26
|
-
= render('effective/applicants/select/apply_for_reinstatement', f: f)
|
27
|
-
|
28
24
|
- elsif applicant_types == ['Apply to Reclassify']
|
29
25
|
= render('effective/applicants/select/apply_to_reclassify', f: f)
|
30
26
|
= render('effective/applicants/select/categories', f: f, categories: categories - [existing_category])
|
@@ -32,6 +28,11 @@
|
|
32
28
|
- else
|
33
29
|
= f.select :applicant_type, applicant_types, label: 'Apply to...'
|
34
30
|
|
31
|
+
- if applicant_types.include?('Apply for Reinstatement')
|
32
|
+
= f.show_if :applicant_type, 'Apply for Reinstatement' do
|
33
|
+
= f.hidden_field :category_id, value: reinstatement_category.id
|
34
|
+
= render('effective/applicants/select/apply_for_reinstatement', f: f)
|
35
|
+
|
35
36
|
- if applicant_types.include?('Apply to Join')
|
36
37
|
= f.show_if :applicant_type, 'Apply to Join' do
|
37
38
|
= render('effective/applicants/select/apply_to_join', f: f)
|
@@ -42,9 +43,4 @@
|
|
42
43
|
= render('effective/applicants/select/apply_to_reclassify', f: f)
|
43
44
|
= render('effective/applicants/select/categories', f: f, categories: categories - [existing_category])
|
44
45
|
|
45
|
-
- if applicant_types.include?('Apply for Reinstatement')
|
46
|
-
= f.show_if :applicant_type, 'Apply for Reinstatement' do
|
47
|
-
= f.hidden_field :category_id, value: existing_category.id
|
48
|
-
= render('effective/applicants/select/apply_for_reinstatement', f: f)
|
49
|
-
|
50
46
|
= f.save 'Save and Continue'
|
@@ -1,11 +1,14 @@
|
|
1
1
|
= effective_form_with(scope: :q, model: membership_directory, method: :get, url: request.path) do |f|
|
2
|
-
|
3
|
-
= f.search_field :term, label: 'Name'
|
2
|
+
= f.search_field :term, label: 'Name'
|
4
3
|
|
5
|
-
|
6
|
-
|
4
|
+
-# Users only
|
5
|
+
/= f.search_field :first_name
|
6
|
+
/= f.search_field :last_name
|
7
7
|
|
8
|
-
|
8
|
+
- # Organizations only
|
9
|
+
/= f.search_field :title
|
10
|
+
|
11
|
+
= f.select :category, membership_directory.categories
|
9
12
|
|
10
13
|
= f.save('Search', class: 'btn btn-primary btn-search mr-3', name: nil)
|
11
14
|
= link_to 'Reset filters', request.path
|
@@ -0,0 +1,20 @@
|
|
1
|
+
= card do
|
2
|
+
%h6= membership.owner
|
3
|
+
%p= membership.categories.map(&:membership_directory_title).to_sentence
|
4
|
+
%p= membership.statuses.map(&:membership_directory_title).to_sentence
|
5
|
+
|
6
|
+
- if membership.owner.try(:email).present?
|
7
|
+
%p= reveal_mail_to(membership.owner.email)
|
8
|
+
|
9
|
+
- if (organizations = membership.owner.try(:membership_organizations)).present?
|
10
|
+
.mb-4.membership-directory-organizations
|
11
|
+
- organizations.each do |organization|
|
12
|
+
%div= organization
|
13
|
+
|
14
|
+
- if (users = membership.owner.try(:membership_users)).present?
|
15
|
+
.mb-4.membership-directory-users
|
16
|
+
- users.each do |user|
|
17
|
+
%div= user
|
18
|
+
|
19
|
+
- if EffectiveResources.authorized?(self, :show, membership) && EffectiveResources.authorized?(self, :show, membership.owner)
|
20
|
+
= link_to 'Show more', effective_memberships.membership_directory_path(membership.token)
|
@@ -4,14 +4,14 @@
|
|
4
4
|
- results = membership_directory.results(page: params[:page])
|
5
5
|
|
6
6
|
- if membership_directory.present? && results.length == 0
|
7
|
-
.alert.alert-info There are no results for your search. Please try again.
|
7
|
+
.mt-4.alert.alert-info There are no results for your search. Please try again.
|
8
8
|
|
9
9
|
- results.in_groups_of(3).each do |group|
|
10
10
|
.row.mt-4
|
11
|
-
- group.each do |
|
12
|
-
- next unless owner
|
11
|
+
- group.each do |membership|
|
13
12
|
.col-md
|
14
|
-
|
13
|
+
- next unless membership
|
14
|
+
= render('effective/membership_directory/membership', membership: membership)
|
15
15
|
|
16
16
|
%nav.d-flex.justify-content-center
|
17
17
|
= bootstrap_paginate(results, per_page: membership_directory.per_page)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
= render 'layout' do
|
2
|
+
.effective-membership
|
3
|
+
|
4
|
+
%h6= @membership.owner
|
5
|
+
%p= @membership.categories.map(&:membership_directory_title).to_sentence
|
6
|
+
%p= @membership.statuses.map(&:membership_directory_title).to_sentence
|
7
|
+
|
8
|
+
- if @membership.owner.respond_to?(:rich_text_body)
|
9
|
+
.mb-4= @membership.owner.rich_text_body.to_s
|
10
|
+
|
11
|
+
- if @membership.owner.try(:email).present?
|
12
|
+
%p= reveal_mail_to(@membership.owner.email)
|
13
|
+
|
14
|
+
- if (organizations = @membership.owner.try(:membership_organizations)).present?
|
15
|
+
.mb-4.membership-directory-organizations
|
16
|
+
- organizations.each do |organization|
|
17
|
+
%div= organization
|
18
|
+
|
19
|
+
- if (users = @membership.owner.try(:membership_users)).present?
|
20
|
+
.mb-4.membership-directory-users
|
21
|
+
- users.each do |user|
|
22
|
+
%div= user
|
23
|
+
|
24
|
+
%hr
|
25
|
+
= link_to 'Return to Directory', effective_memberships.membership_directory_index_path, class: 'btn btn-primary'
|
@@ -35,6 +35,7 @@
|
|
35
35
|
|
36
36
|
- if current_user.membership_removed?
|
37
37
|
%p Your membership was removed on #{current_user.membership_removed_on.strftime('%F')}.
|
38
|
+
%p You may Apply for Reinstatement to #{current_user.reinstatement_membership_category} by clicking the Apply to Join button below and then complete the wizard.
|
38
39
|
|
39
40
|
- if membership_organizations.present?
|
40
41
|
%p You are a representative for #{pluralize(membership_organizations.length, 'member organization')}.
|
data/config/routes.rb
CHANGED
@@ -26,7 +26,7 @@ EffectiveMemberships::Engine.routes.draw do
|
|
26
26
|
resources :build, controller: :fee_payments, only: [:show, :update]
|
27
27
|
end
|
28
28
|
|
29
|
-
resources :membership_directory, only: :index
|
29
|
+
resources :membership_directory, only: [:index, :show]
|
30
30
|
get '/directory', to: 'membership_directory#index'
|
31
31
|
|
32
32
|
resources :membership_cards, only: :index
|
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.9.
|
4
|
+
version: 0.9.8
|
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-
|
11
|
+
date: 2022-12-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -383,6 +383,7 @@ files:
|
|
383
383
|
- app/views/admin/registrar_actions/_form_not_in_good_standing.html.haml
|
384
384
|
- app/views/admin/registrar_actions/_form_reclassify.html.haml
|
385
385
|
- app/views/admin/registrar_actions/_form_register.html.haml
|
386
|
+
- app/views/admin/registrar_actions/_form_reinstatement.html.haml
|
386
387
|
- app/views/admin/registrar_actions/_form_remove.html.haml
|
387
388
|
- app/views/admin/registrar_actions/_form_status_change.html.haml
|
388
389
|
- app/views/admin/representatives/_form.html.haml
|
@@ -482,9 +483,10 @@ files:
|
|
482
483
|
- app/views/effective/membership_cards/index.html.haml
|
483
484
|
- app/views/effective/membership_directory/_form.html.haml
|
484
485
|
- app/views/effective/membership_directory/_layout.html.haml
|
486
|
+
- app/views/effective/membership_directory/_membership.html.haml
|
485
487
|
- app/views/effective/membership_directory/_membership_directory.html.haml
|
486
|
-
- app/views/effective/membership_directory/_membership_owner.html.haml
|
487
488
|
- app/views/effective/membership_directory/index.html.haml
|
489
|
+
- app/views/effective/membership_directory/show.html.haml
|
488
490
|
- app/views/effective/memberships/_dashboard.html.haml
|
489
491
|
- app/views/effective/memberships/_membership.html.haml
|
490
492
|
- app/views/effective/memberships_mailer/applicant_approved.liquid
|