effective_memberships 0.4.17 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
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)