effective_memberships 0.4.17 → 0.5.1

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/admin/registrar_actions_controller.rb +8 -0
  3. data/app/controllers/admin/statuses_controller.rb +19 -0
  4. data/app/datatables/admin/effective_membership_histories_datatable.rb +10 -0
  5. data/app/datatables/admin/effective_memberships_datatable.rb +2 -1
  6. data/app/datatables/admin/effective_statuses_datatable.rb +21 -0
  7. data/app/models/concerns/effective_memberships_applicant.rb +84 -41
  8. data/app/models/concerns/effective_memberships_category.rb +10 -3
  9. data/app/models/concerns/effective_memberships_fee_payment.rb +16 -5
  10. data/app/models/concerns/effective_memberships_owner.rb +37 -7
  11. data/app/models/concerns/effective_memberships_registrar.rb +70 -4
  12. data/app/models/concerns/effective_memberships_status.rb +71 -0
  13. data/app/models/effective/fee.rb +11 -3
  14. data/app/models/effective/membership.rb +41 -3
  15. data/app/models/effective/membership_history.rb +17 -4
  16. data/app/models/effective/membership_status.rb +13 -0
  17. data/app/models/effective/registrar_action.rb +38 -4
  18. data/app/models/effective/status.rb +7 -0
  19. data/app/views/admin/membership_histories/_form.html.haml +2 -0
  20. data/app/views/admin/registrar_actions/_form.html.haml +7 -0
  21. data/app/views/admin/registrar_actions/_form_assign.html.haml +29 -9
  22. data/app/views/admin/registrar_actions/_form_register.html.haml +7 -2
  23. data/app/views/admin/registrar_actions/_form_status_change.html.haml +41 -0
  24. data/app/views/admin/statuses/_form.html.haml +8 -0
  25. data/app/views/admin/statuses/_form_status.html.haml +9 -0
  26. data/app/views/effective/applicants/_reinstatement.html.haml +9 -0
  27. data/app/views/effective/applicants/reinstatement.html.haml +15 -0
  28. data/app/views/effective/applicants/select/_apply_for_reinstatement.html.haml +3 -0
  29. data/app/views/effective/applicants/select/_apply_to_join.html.haml +1 -0
  30. data/app/views/effective/applicants/select/_apply_to_reclassify.html.haml +5 -0
  31. data/app/views/effective/applicants/select/_categories.html.haml +25 -0
  32. data/app/views/effective/applicants/select.html.haml +34 -26
  33. data/config/effective_memberships.rb +4 -0
  34. data/config/routes.rb +2 -1
  35. data/db/migrate/01_create_effective_memberships.rb.erb +38 -0
  36. data/db/seeds.rb +6 -0
  37. data/lib/effective_memberships/engine.rb +1 -0
  38. data/lib/effective_memberships/version.rb +1 -1
  39. data/lib/effective_memberships.rb +10 -7
  40. metadata +16 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1394fcdd25d9322f467e1392a9efeec60cc028e424ee85d3baa464d4d185a46a
4
- data.tar.gz: 6c4660193bb2e98b113a4ba1e7372b469dcda714f2d78d712b635a497b488d3c
3
+ metadata.gz: 0c5813efd8fd5455db9964bbb466b8ae8d12f44ee3acce7a945c5ff67550c5e8
4
+ data.tar.gz: a91d84a98511574b854ae98724d5fbdd37a66aef5a9d87a29956bd6c9cf95062
5
5
  SHA512:
6
- metadata.gz: 4f4bd2c3f2f544a65d9bbace898fc5538ec115175a635d2895e399597d1687850b8cbf4824bf28517c9b8cd96bee3c96fdd03fcf6224efe39db03650a85881c8
7
- data.tar.gz: 9dd8b11979d28592083378f98ab4d69a3c452f156c0698e3eb717273321d4aefe20069accfe00272afc945e0791fffc202fcf3cec526b008922339a5b334f9fb
6
+ metadata.gz: 83c2dba1cfde3ace28c1037a633e50a4f975f5b8be24877ee2d96e029197c5d43ea8b4e66a2e3a992c9f9298c6c81d84fdaa2045a6e7dc12e3ef97ad154a4a90
7
+ data.tar.gz: 18b9f5b9377923a1a17703f104ab51710a9c6dacfb6315c322c6a71c3c6204e4b80d64ec13121c4d95b20178e807087bf8ab383288486e6f3abba9b09ca72bf2
@@ -14,6 +14,14 @@ 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 :status_change, 'Change Status',
18
+ success: -> { "#{resource.owner} has been status changed to #{resource.owner.membership.status || 'None'}" },
19
+ redirect: -> { admin_owners_path(resource) }
20
+
21
+ submit :status_remove, 'Remove Status',
22
+ success: -> { "#{resource.owner} has had their status removed" },
23
+ redirect: -> { admin_owners_path(resource) }
24
+
17
25
  submit :assign, 'Assign',
18
26
  success: -> { "#{resource.owner} has been assigned to #{resource.owner.membership.categories.to_sentence}" },
19
27
  redirect: -> { admin_owners_path(resource) }
@@ -0,0 +1,19 @@
1
+ module Admin
2
+ class StatusesController < 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
+ resource_scope -> { EffectiveMemberships.Status.deep.all }
9
+ datatable -> { Admin::EffectiveStatusesDatatable.new }
10
+
11
+ private
12
+
13
+ def permitted_params
14
+ model = (params.key?(:effective_status) ? :effective_status : :status)
15
+ params.require(model).permit!
16
+ end
17
+
18
+ end
19
+ end
@@ -21,9 +21,19 @@ module Admin
21
21
  end.join.html_safe
22
22
  end
23
23
 
24
+ col :statuses, label: 'Status' do |history|
25
+ history.statuses.map.with_index do |status, index|
26
+ status_id = history.status_ids[index]
27
+ link = link_to(status, effective_memberships.edit_admin_status_path(status_id))
28
+
29
+ content_tag(:div, link, class: 'col-resource_item')
30
+ end.join.html_safe
31
+ end
32
+
24
33
  col :number
25
34
 
26
35
  col :category_ids, visible: false
36
+ col :status_ids, visible: false
27
37
 
28
38
  col :bad_standing
29
39
  col :removed
@@ -9,7 +9,8 @@ module Admin
9
9
  col :owner_type, visible: false
10
10
 
11
11
  val :owner
12
- col :categories
12
+ col :categories, search: EffectiveMemberships.Category.sorted.map(&:to_s)
13
+ col :statuses, search: EffectiveMemberships.Status.sorted.map(&:to_s)
13
14
 
14
15
  col :number
15
16
  col :number_as_integer, visible: false
@@ -0,0 +1,21 @@
1
+ module Admin
2
+ class EffectiveStatusesDatatable < Effective::Datatable
3
+ datatable do
4
+ reorder :position
5
+
6
+ col :updated_at, visible: false
7
+ col :created_at, visible: false
8
+ col :id, visible: false
9
+
10
+ col :title
11
+ col :status_type, search: EffectiveMemberships.Status.status_types
12
+
13
+ actions_col
14
+ end
15
+
16
+ collection do
17
+ EffectiveMemberships.Status.deep.all
18
+ end
19
+
20
+ end
21
+ end
@@ -22,7 +22,7 @@ module EffectiveMembershipsApplicant
22
22
  end
23
23
 
24
24
  def categories
25
- ['Apply to Join', 'Apply to Reclassify']
25
+ ['Apply to Join', 'Apply to Reclassify', 'Apply for Reinstatement']
26
26
  end
27
27
 
28
28
  def transcripts_statuses
@@ -51,6 +51,7 @@ module EffectiveMembershipsApplicant
51
51
  select: 'Select Application Type',
52
52
  demographics: 'Demographics', # Individual only. Users fields.
53
53
  organization: 'Organization', # Organization only. Organization fields.
54
+ reinstatement: 'Reinstatement', # Apply for Reinstatement only
54
55
  education: 'Education',
55
56
  course_amounts: 'Courses',
56
57
  equivalences: 'Equivalent Memberships',
@@ -80,6 +81,9 @@ module EffectiveMembershipsApplicant
80
81
  attr_accessor :declare_code_of_ethics
81
82
  attr_accessor :declare_truth
82
83
 
84
+ # Reinstatements Step
85
+ attr_accessor :declare_reinstatement
86
+
83
87
  # Admin Approve Step
84
88
  attr_accessor :approved_membership_number
85
89
  attr_accessor :approved_membership_date
@@ -92,8 +96,13 @@ module EffectiveMembershipsApplicant
92
96
  accepts_nested_attributes_for :organization
93
97
 
94
98
  belongs_to :category, polymorphic: true, optional: true
99
+
100
+ # Required for Apply to Reclassify
95
101
  belongs_to :from_category, polymorphic: true, optional: true
96
102
 
103
+ # Required for Apply for Reinstatement
104
+ belongs_to :from_status, polymorphic: true, optional: true
105
+
97
106
  has_many :applicant_reviews, -> { order(:id) }, as: :applicant, inverse_of: :applicant, dependent: :destroy
98
107
  accepts_nested_attributes_for :applicant_reviews, reject_if: :all_blank, allow_destroy: true
99
108
 
@@ -169,7 +178,7 @@ module EffectiveMembershipsApplicant
169
178
  timestamps
170
179
  end
171
180
 
172
- scope :deep, -> { includes(:user, :organization, :category, :from_category, :orders) }
181
+ scope :deep, -> { includes(:user, :organization, :category, :from_category, :from_status, :orders) }
173
182
  scope :sorted, -> { order(:id) }
174
183
 
175
184
  scope :in_progress, -> { where.not(status: [:approved, :declined]) }
@@ -184,8 +193,9 @@ module EffectiveMembershipsApplicant
184
193
 
185
194
  # Set Apply to Join or Reclassification
186
195
  before_validation(if: -> { (new_record? || current_step == :select) && owner.present? }) do
187
- self.applicant_type = (owner.membership.blank? ? 'Apply to Join' : 'Apply to Reclassify')
196
+ self.applicant_type ||= can_apply_applicant_types_collection().first
188
197
  self.from_category = owner.membership&.category
198
+ self.from_status = owner.membership&.status
189
199
  end
190
200
 
191
201
  before_validation(if: -> { current_step == :experience }) do
@@ -194,12 +204,18 @@ module EffectiveMembershipsApplicant
194
204
 
195
205
  # All Steps validations
196
206
  validates :user, presence: true
207
+
197
208
  validates :from_category, presence: true, if: -> { reclassification? }
209
+ validates :from_status, presence: true, if: -> { reinstatement? }
198
210
 
199
- validate(if: -> { reclassification? }) do
211
+ validate(if: -> { reclassification? && category_id.present? }) do
200
212
  errors.add(:category_id, "can't reclassify to existing category") if category_id == from_category_id
201
213
  end
202
214
 
215
+ validate(if: -> { reinstatement? && category_id.present? }) do
216
+ errors.add(:category_id, "must match existing category when reinstatement") unless category_id == from_category_id
217
+ end
218
+
203
219
  validate(if: -> { category.present? }) do
204
220
  self.errors.add(:organization_id, "can't be blank when organization category") if category.organization? && organization.blank?
205
221
  self.errors.add(:organization_id, "must be blank when individual category") if category.individual? && organization.present?
@@ -312,6 +328,12 @@ module EffectiveMembershipsApplicant
312
328
  validates :declare_truth, acceptance: true
313
329
  end
314
330
 
331
+ # Reinstatements step
332
+ with_options(if: -> { current_step == :reinstatement }) do
333
+ validates :declare_reinstatement, acceptance: true
334
+ end
335
+
336
+
315
337
  # Admin Approve
316
338
  validate(if: -> { approved_membership_date.present? }) do
317
339
  if approved_membership_date.to_date > Time.zone.now.to_date
@@ -337,16 +359,19 @@ module EffectiveMembershipsApplicant
337
359
  # This required_steps is defined inside the included do .. end block so it overrides the acts_as_wizard one.
338
360
  def required_steps
339
361
  return self.class.test_required_steps if Rails.env.test? && self.class.test_required_steps.present?
362
+ return self.class.all_wizard_steps if category.blank?
340
363
 
341
364
  @_required_steps ||= begin
342
- wizard_steps = self.class.all_wizard_steps
365
+ # All required
343
366
  required_steps = self.class.required_wizard_steps
344
367
 
345
- applicant_steps = Array(category&.applicant_wizard_steps)
368
+ # Based on current applicant
369
+ applicant_steps = (reinstatement? ? category.applicant_reinstatement_wizard_steps : category.applicant_wizard_steps)
346
370
 
347
- # Special logic for stamp step
371
+ # Sanity check
348
372
  applicant_steps.delete(:stamp) unless apply_to_join?
349
373
  applicant_steps.delete(:organization) unless category&.organization?
374
+ applicant_steps.delete(:reinstatement) unless reinstatement?
350
375
 
351
376
  # change_wizard_steps is defined in effective_resources acts_as_wizard
352
377
  applicant_steps = change_wizard_steps(applicant_steps)
@@ -355,40 +380,45 @@ module EffectiveMembershipsApplicant
355
380
  raise('expected change_wizard_steps to return an Array of steps with no nils')
356
381
  end
357
382
 
358
- wizard_steps.select do |step|
359
- required_steps.include?(step) || category.blank? || applicant_steps.include?(step)
383
+ self.class.all_wizard_steps.select do |step|
384
+ required_steps.include?(step) || applicant_steps.include?(step)
360
385
  end
361
386
  end
362
387
  end
363
388
 
364
389
  def sidebar_steps
365
- return self.class.required_wizard_steps unless category.present?
366
- required_steps
390
+ has_completed_step?(:select) ? required_steps : self.class.required_wizard_steps
367
391
  end
368
392
 
369
393
  def can_visit_step?(step)
370
- if missing_info?
371
- return [:start, :select, :billing, :checkout].exclude?(step)
372
- end
373
-
394
+ return [:start, :select, :billing, :checkout].exclude?(step) if missing_info?
374
395
  can_revisit_completed_steps(step)
375
396
  end
376
397
 
398
+ def applicant_fee_type
399
+ return 'Reinstatement' if reinstatement?
400
+ 'Applicant'
401
+ end
402
+
377
403
  def applicant_fee_category
378
404
  category
379
405
  end
380
406
 
381
407
  def applicant_fee_price
408
+ return category.applicant_reinstatement_fee if reinstatement?
382
409
  category.applicant_fee
383
410
  end
384
411
 
385
412
  # All Fees and Orders
386
413
  def submit_fees
387
414
  # Find or build submit fee
388
- fee = fees.first || fees.build(owner: owner, fee_type: 'Applicant')
415
+ fee = fees.first || fees.build(owner: owner)
416
+
417
+ raise('expected a present applicant fee price') if applicant_fee_price.blank?
389
418
 
390
419
  unless fee.purchased?
391
420
  fee.assign_attributes(
421
+ fee_type: applicant_fee_type(),
392
422
  category: applicant_fee_category(),
393
423
  price: applicant_fee_price(),
394
424
  tax_exempt: applicant_fee_category().applicant_fee_tax_exempt,
@@ -433,18 +463,9 @@ module EffectiveMembershipsApplicant
433
463
 
434
464
  # Instance Methods
435
465
  def to_s
436
- if category.present? && category.present?
437
- [
438
- owner.to_s,
439
- '-',
440
- category,
441
- 'for',
442
- category,
443
- ("from #{from_category}" if reclassification?)
444
- ].compact.join(' ')
445
- else
446
- 'New Applicant'
447
- end
466
+ return 'New Applicant' if applicant_type.blank? || category.blank? || owner.blank?
467
+
468
+ "#{owner} - #{applicant_type} to #{category}"
448
469
  end
449
470
 
450
471
  def owner
@@ -467,6 +488,10 @@ module EffectiveMembershipsApplicant
467
488
  applicant_type == 'Apply to Reclassify'
468
489
  end
469
490
 
491
+ def reinstatement?
492
+ applicant_type == 'Apply for Reinstatement'
493
+ end
494
+
470
495
  def individual?
471
496
  !(owner.kind_of?(EffectiveMemberships.Organization) && category&.organization?)
472
497
  end
@@ -516,6 +541,17 @@ module EffectiveMembershipsApplicant
516
541
  end.html_safe
517
542
  end
518
543
 
544
+ # Used by the select step
545
+ def can_apply_applicant_types_collection
546
+ if owner.blank? || owner.membership.blank? || owner.membership.categories.blank?
547
+ ['Apply to Join']
548
+ elsif owner.membership.statuses.present?
549
+ ['Apply for Reinstatement', 'Apply to Join']
550
+ else
551
+ ['Apply to Reclassify']
552
+ end
553
+ end
554
+
519
555
  # Used by the select step
520
556
  def can_apply_categories_collection
521
557
  categories = EffectiveMemberships.Category.sorted.can_apply
@@ -524,7 +560,7 @@ module EffectiveMembershipsApplicant
524
560
  return categories.where(can_apply_new: true)
525
561
  end
526
562
 
527
- category_ids = user.memberships.map(&:category_ids).flatten
563
+ category_ids = user.membership.category_ids
528
564
 
529
565
  categories.select do |cat|
530
566
  cat.can_apply_existing? ||
@@ -584,7 +620,7 @@ module EffectiveMembershipsApplicant
584
620
  end
585
621
 
586
622
  def applicant_references_required?
587
- min_applicant_references > 0
623
+ required_steps.include?(:references) && min_applicant_references > 0
588
624
  end
589
625
 
590
626
  # Endorsements Step
@@ -593,7 +629,7 @@ module EffectiveMembershipsApplicant
593
629
  end
594
630
 
595
631
  def applicant_endorsements_required?
596
- min_applicant_endorsements > 0
632
+ required_steps.include?(:endorsements) && min_applicant_endorsements > 0
597
633
  end
598
634
 
599
635
  # Equivalences Step
@@ -607,7 +643,7 @@ module EffectiveMembershipsApplicant
607
643
  end
608
644
 
609
645
  def transcripts_required?
610
- category.applicant_wizard_steps.include?(:transcripts)
646
+ required_steps.include?(:transcripts)
611
647
  end
612
648
 
613
649
  # Files Step
@@ -631,16 +667,16 @@ module EffectiveMembershipsApplicant
631
667
  requirements = {}
632
668
  return requirements unless category.present?
633
669
 
634
- if category.applicant_wizard_steps.include?(:endorsements) || applicant_endorsements_required?
635
- requirements['Applicant Endorsements'] = (!applicant_endorsements_required? || applicant_endorsements.count(&:completed?) >= min_applicant_endorsements)
670
+ if applicant_endorsements_required?
671
+ requirements['Applicant Endorsements'] = (applicant_endorsements.count(&:completed?) >= min_applicant_endorsements)
636
672
  end
637
673
 
638
- if category.applicant_wizard_steps.include?(:references) || applicant_references_required?
639
- requirements['Applicant References'] = (!applicant_references_required? || applicant_references.count(&:completed?) >= min_applicant_references)
674
+ if applicant_references_required?
675
+ requirements['Applicant References'] = (applicant_references.count(&:completed?) >= min_applicant_references)
640
676
  end
641
677
 
642
- if category.applicant_wizard_steps.include?(:transcripts) || transcripts_required?
643
- requirements['Applicant Transcripts'] = (!transcripts_required? || transcripts_received?)
678
+ if transcripts_required?
679
+ requirements['Applicant Transcripts'] = transcripts_received?
644
680
  end
645
681
 
646
682
  requirements
@@ -689,9 +725,14 @@ module EffectiveMembershipsApplicant
689
725
  # When an application is completed, these must be done to go to reviewed
690
726
  # An Admin can override this and just set them to reviewed.
691
727
  def reviewed_requirements
692
- {
693
- 'Applicant Reviews' => (!applicant_reviews_required? || applicant_reviews.count(&:completed?) >= min_applicant_reviews)
694
- }
728
+ requirements = {}
729
+ return requirements unless category.present?
730
+
731
+ if applicant_reviews_required?
732
+ requirements['Applicant Reviews'] = (applicant_reviews.count(&:completed?) >= min_applicant_reviews)
733
+ end
734
+
735
+ requirements
695
736
  end
696
737
 
697
738
  def review!
@@ -723,6 +764,8 @@ module EffectiveMembershipsApplicant
723
764
  )
724
765
  elsif reclassification?
725
766
  EffectiveMemberships.Registrar.reclassify!(owner, to: category)
767
+ elsif reinstatement?
768
+ EffectiveMemberships.Registrar.reinstate!(owner)
726
769
  else
727
770
  raise('unsupported approval applicant_type')
728
771
  end
@@ -42,7 +42,7 @@ module EffectiveMembershipsCategory
42
42
  category_type :string
43
43
 
44
44
  title :string
45
- category :string
45
+ category :string # Unused but must belong to self.class.categories
46
46
  position :integer
47
47
 
48
48
  # Applicants
@@ -51,8 +51,9 @@ module EffectiveMembershipsCategory
51
51
  can_apply_restricted :boolean
52
52
  can_apply_restricted_ids :text
53
53
 
54
- applicant_fee :integer
55
- applicant_wizard_steps :text
54
+ applicant_fee :integer
55
+ applicant_reinstatement_fee :integer
56
+ applicant_wizard_steps :text
56
57
 
57
58
  min_applicant_educations :integer
58
59
  min_applicant_endorsements :integer
@@ -192,10 +193,16 @@ module EffectiveMembershipsCategory
192
193
  applicant_review_wizard_steps - EffectiveMemberships.ApplicantReview.required_wizard_steps
193
194
  end
194
195
 
196
+ # Apply to Join or Apply to Reclassify
195
197
  def applicant_wizard_steps
196
198
  (Array(self[:applicant_wizard_steps]) - [nil, '']).map(&:to_sym)
197
199
  end
198
200
 
201
+ # Apply for Reinstatement
202
+ def applicant_reinstatement_wizard_steps
203
+ [:reinstatement]
204
+ end
205
+
199
206
  def fee_payment_wizard_steps
200
207
  (Array(self[:fee_payment_wizard_steps]) - [nil, '']).map(&:to_sym)
201
208
  end
@@ -56,8 +56,8 @@ module EffectiveMembershipsFeePayment
56
56
  belongs_to :organization, polymorphic: true, optional: true
57
57
  accepts_nested_attributes_for :organization
58
58
 
59
- # Like maybe optionally it makes sense.
60
59
  belongs_to :category, polymorphic: true, optional: true
60
+ belongs_to :with_status, polymorphic: true, optional: true
61
61
 
62
62
  # Effective Namespace
63
63
  has_many :fees, -> { order(:id) }, as: :parent, class_name: 'Effective::Fee', dependent: :nullify
@@ -102,6 +102,7 @@ module EffectiveMembershipsFeePayment
102
102
 
103
103
  before_validation(if: -> { current_step == :start && user && user.membership }) do
104
104
  self.category ||= user.membership.categories.first if user.membership.categories.length == 1
105
+ self.with_status ||= user.membership.statuses.first if user.membership.statuses.length == 1
105
106
  end
106
107
 
107
108
  # All Steps validations
@@ -120,17 +121,27 @@ module EffectiveMembershipsFeePayment
120
121
  # This required_steps is defined inside the included do .. end block so it overrides the acts_as_wizard one.
121
122
  def required_steps
122
123
  return self.class.test_required_steps if Rails.env.test? && self.class.test_required_steps.present?
124
+ return self.class.all_wizard_steps if category.blank?
123
125
 
124
126
  @_required_steps ||= begin
125
- wizard_steps = self.class.all_wizard_steps
127
+ # All required
126
128
  required_steps = self.class.required_wizard_steps
127
129
 
128
- fee_payment_steps = Array(category&.fee_payment_wizard_steps)
130
+ # Based on current fee payment
131
+ fee_payment_steps = Array(category.fee_payment_wizard_steps)
129
132
 
133
+ # Sanity Check
130
134
  fee_payment_steps.delete(:organization) unless organization?
131
135
 
132
- wizard_steps.select do |step|
133
- required_steps.include?(step) || category.blank? || fee_payment_steps.include?(step)
136
+ # change_wizard_steps is defined in effective_resources acts_as_wizard
137
+ fee_payment_steps = change_wizard_steps(fee_payment_steps)
138
+
139
+ unless fee_payment_steps.kind_of?(Array) && fee_payment_steps.all? { |step| step.kind_of?(Symbol) }
140
+ raise('expected change_wizard_steps to return an Array of steps with no nils')
141
+ end
142
+
143
+ self.class.all_wizard_steps.select do |step|
144
+ required_steps.include?(step) || fee_payment_steps.include?(step)
134
145
  end
135
146
  end
136
147
  end
@@ -53,6 +53,24 @@ module EffectiveMembershipsOwner
53
53
  where(id: with_paid_fees_through.select(:owner_id))
54
54
  }
55
55
 
56
+ scope :members_with_category, -> (categories) {
57
+ raise('expected an EffectiveMemberships.Category') unless Array(categories).all? { |cat| cat.kind_of?(EffectiveMemberships.Category) }
58
+
59
+ membership_categories = Effective::MembershipCategory.where(category: categories)
60
+ memberships = Effective::Membership.where(id: membership_categories.select(:membership_id))
61
+
62
+ where(id: memberships.where(owner_type: name).select(:owner_id))
63
+ }
64
+
65
+ scope :members_with_status, -> (statuses) {
66
+ raise('expected an EffectiveMemberships.Status') unless Array(statuses).all? { |status| status.kind_of?(EffectiveMemberships.Status) }
67
+
68
+ membership_statuses = Effective::MembershipStatus.where(status: statuses)
69
+ memberships = Effective::Membership.where(id: membership_statuses.select(:membership_id))
70
+
71
+ where(id: memberships.where(owner_type: name).select(:owner_id))
72
+ }
73
+
56
74
  end
57
75
 
58
76
  def assign_member_role
@@ -103,6 +121,10 @@ module EffectiveMembershipsOwner
103
121
  EffectiveMemberships.Category.sorted.all
104
122
  end
105
123
 
124
+ def registrar_action_statuses(action)
125
+ EffectiveMemberships.Status.sorted.all
126
+ end
127
+
106
128
  # Instance Methods
107
129
  def build_prorated_fee(date: nil)
108
130
  raise('must have an existing membership') unless membership.present?
@@ -271,21 +293,29 @@ module EffectiveMembershipsOwner
271
293
 
272
294
  # The date of change
273
295
  start_on ||= Time.zone.now
274
- removed = membership.marked_for_destruction?
275
296
 
276
297
  # End the other membership histories
277
298
  membership_histories.each { |history| history.end_on ||= start_on }
278
299
 
279
300
  # Snapshot of the current membership at this time
280
- membership_histories.build(
301
+ history = membership_histories.build(
281
302
  start_on: start_on,
282
303
  end_on: nil,
283
- removed: removed,
284
- bad_standing: membership.bad_standing?,
285
- categories: (membership.categories.map(&:to_s) unless removed),
286
- category_ids: (membership.categories.map(&:id) unless removed),
287
- number: (membership.number unless removed)
304
+ removed: membership.marked_for_destruction?,
305
+ bad_standing: membership.bad_standing?
288
306
  )
307
+
308
+ unless history.removed?
309
+ history.assign_attributes(
310
+ number: membership.number,
311
+ categories: membership.categories.map(&:to_s),
312
+ category_ids: membership.categories.map(&:id),
313
+ statuses: membership.statuses.map(&:to_s),
314
+ status_ids: membership.statuses.map(&:id)
315
+ )
316
+ end
317
+
318
+ history
289
319
  end
290
320
 
291
321
  def membership_history_on(date)