effective_memberships 0.4.0 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 223d0bdf9ce08beacd79d4fdeb3e184878bb9e9cdb8b5964aa1c4ace9f1d2735
4
- data.tar.gz: 158b570a8dc933b247245e450c74a78c63877ece8f35a59b1e400b0fdb017b43
3
+ metadata.gz: 1de9f1b37bb81729fcb68686af4113263a847742ef6c763679c74ad75e0633ca
4
+ data.tar.gz: 930ceb5ab018318e39331a02d9fbf725129b997c09d5019fc8a3bb03e7e916e4
5
5
  SHA512:
6
- metadata.gz: b8e3fe9641f2826a4df557f1817163d719bb53112a835b3a5456ea7020283f360e4bcdc99d75c1a79256f6515b2fa05f5a2c0001b6eb1ecfa988dd09eba8eb5d
7
- data.tar.gz: 013d39152828dce9d873e1eb1f1bb9f3a42aa83fc31105e020adbd47734f42902eec0659ff661b1fd4e8bd1983a389374848847221b6aec35dcbb8bfb5552661
6
+ metadata.gz: 3872b4f4e30ec2e6c2f5a3068cfcb2dc5fbcadd557cf1146942f95dfd6663b04d586cf6137990b2742e410e8eac6b36a0d6f05b15ed537ef0a7aec1328b68dcf
7
+ data.tar.gz: 8806de111fc454554070e6d93213ce3a6c0f2bb6db1dd0bf4b5d07f4f9d352014be9f1da1ae078d1a0f99653be5613834449fccadebe02e67b55bf1512162848
@@ -0,0 +1,24 @@
1
+ module Admin
2
+ class MembershipHistoriesController < 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
+ submit :save, 'Update History',
9
+ success: -> { "Membership history successfully updated. Please double check the history is correct." },
10
+ redirect: -> { admin_owners_path(resource) }
11
+
12
+ private
13
+
14
+ def permitted_params
15
+ model = (params.key?(:effective_membership_history) ? :effective_membership_history : :membership_history)
16
+ params.require(model).permit!
17
+ end
18
+
19
+ def admin_owners_path(resource)
20
+ Effective::Resource.new(resource.owner, namespace: :admin).action_path(:edit) + '?tab=membership'
21
+ end
22
+
23
+ end
24
+ end
@@ -5,5 +5,20 @@ module Admin
5
5
 
6
6
  include Effective::CrudController
7
7
 
8
+ submit :save, 'Update Membership',
9
+ success: -> { "#{resource.owner} has been successfully updated. Please double check the membership history is correct" },
10
+ redirect: -> { admin_owners_path(resource) }
11
+
12
+ private
13
+
14
+ def permitted_params
15
+ model = (params.key?(:effective_membership) ? :effective_membership : :membership)
16
+ params.require(model).permit!
17
+ end
18
+
19
+ def admin_owners_path(resource)
20
+ Effective::Resource.new(resource.owner, namespace: :admin).action_path(:edit) + '?tab=membership'
21
+ end
22
+
8
23
  end
9
24
  end
@@ -6,7 +6,7 @@ module Admin
6
6
  include Effective::CrudController
7
7
 
8
8
  resource_scope -> { EffectiveMemberships.Organization.deep.all }
9
- datatable -> { EffectiveResources.best('Admin::EffectiveMembershipsDatatable').new }
9
+ datatable -> { EffectiveResources.best('Admin::EffectiveOrganizationsDatatable').new }
10
10
 
11
11
  private
12
12
 
@@ -25,7 +25,7 @@ module Effective
25
25
  def permitted_params
26
26
  permitted = params.require(:effective_applicant_reference).permit!.except(:token, :last_notified_at, :status, :status_steps)
27
27
 
28
- if current_user && current_user.effective_memberships_owners.include?(resource.applicant&.owner)
28
+ if current_user && current_user.memberships_owners.include?(resource.applicant&.owner)
29
29
  permitted.except(:reservations, :reservations_reason, :work_history, :accept_declaration)
30
30
  else
31
31
  permitted
@@ -1,31 +1,33 @@
1
- class Admin::EffectiveApplicantReferencesDatatable < Effective::Datatable
1
+ module Admin
2
+ class EffectiveApplicantReferencesDatatable < Effective::Datatable
2
3
 
3
- datatable do
4
- order :name
4
+ datatable do
5
+ order :name
5
6
 
6
- col :applicant
7
+ col :applicant
7
8
 
8
- col :name
9
- col :email
10
- col :phone
9
+ col :name
10
+ col :email
11
+ col :phone
11
12
 
12
- col :status do |reference|
13
- if reference.submitted?
14
- 'Waiting on response'
15
- elsif reference.completed?
16
- 'Completed'
13
+ col :status do |reference|
14
+ if reference.submitted?
15
+ 'Waiting on response'
16
+ elsif reference.completed?
17
+ 'Completed'
18
+ end
19
+ end
20
+
21
+ col :last_notified_at do |reference|
22
+ reference.last_notified_at&.strftime('%F') unless reference.completed?
17
23
  end
18
- end
19
24
 
20
- col :last_notified_at do |reference|
21
- reference.last_notified_at&.strftime('%F') unless reference.completed?
25
+ actions_col
22
26
  end
23
27
 
24
- actions_col
25
- end
28
+ collection do
29
+ Effective::ApplicantReference.deep.all
30
+ end
26
31
 
27
- collection do
28
- Effective::ApplicantReference.deep.all
29
32
  end
30
-
31
33
  end
@@ -49,8 +49,8 @@ module Admin
49
49
 
50
50
  raise('expected an owner_id, not user_id') if attributes[:user_id].present?
51
51
 
52
- if attributes[:owner_id]
53
- scope = scope.where(owner_id: attributes[:owner_id])
52
+ if attributes[:owner_id] && attributes[:owner_type]
53
+ scope = scope.where(owner_id: attributes[:owner_id], owner_type: attributes[:owner_type])
54
54
  end
55
55
 
56
56
  if attributes[:applicant_id]
@@ -12,8 +12,6 @@ module Admin
12
12
 
13
13
  col :owner
14
14
 
15
- col :number
16
-
17
15
  col :categories, label: 'Category' do |history|
18
16
  history.categories.map.with_index do |category, index|
19
17
  category_id = history.category_ids[index]
@@ -23,6 +21,8 @@ module Admin
23
21
  end.join.html_safe
24
22
  end
25
23
 
24
+ col :number
25
+
26
26
  col :category_ids, visible: false
27
27
 
28
28
  col :bad_standing
@@ -35,7 +35,11 @@ module Admin
35
35
  raise('expected an owner_id, not user_id') if attributes[:user_id].present?
36
36
 
37
37
  scope = Effective::MembershipHistory.deep.all
38
- scope = scope.where(owner_id: attributes[:owner_id]) if attributes[:owner_id]
38
+
39
+ if attributes[:owner_id] && attributes[:owner_type]
40
+ scope = scope.where(owner_id: attributes[:owner_id], owner_type: attributes[:owner_type])
41
+ end
42
+
39
43
  scope
40
44
  end
41
45
 
@@ -69,8 +69,8 @@ module Admin
69
69
 
70
70
  raise('expected an owner_id, not user_id') if attributes[:user_id].present?
71
71
 
72
- if attributes[:owner_id].present?
73
- memberships = memberships.where(owner_id: attributes[:owner_id])
72
+ if attributes[:owner_id].present? && attributes[:owner_type].present?
73
+ memberships = memberships.where(owner_id: attributes[:owner_id], owner_type: attributes[:owner_type])
74
74
  end
75
75
 
76
76
  memberships
@@ -265,4 +265,48 @@ module EffectiveMembershipsOwner
265
265
  membership_histories.find { |history| (history.start_on..history.end_on).cover?(date) } # Ruby 2.6 supports endless ranges
266
266
  end
267
267
 
268
+ # Point out busted data
269
+ def membership_history_errors
270
+ return unless membership.present?
271
+ return unless membership_histories.present?
272
+
273
+ errors = []
274
+ history = membership_histories.first
275
+ last_history = membership_histories.last
276
+
277
+ # Check membership joined on date matches first history start date
278
+ if membership.joined_on != history.start_on
279
+ 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."
280
+ end
281
+
282
+ # Check that there is a membership history row if the registered date is unique
283
+ if membership.joined_on != membership.registration_on && membership_histories.none? { |mh| mh.start_on == membership.registration_on }
284
+ errors << "The registered date #{membership.registration_on.strftime('%F')} is missing a history with this date. Please create a history with a start date of #{membership.registration_on.strftime('%F')} or update the registered date above."
285
+ end
286
+
287
+ # Check numbers
288
+ if membership.number.present? && membership_histories.none? { |history| history.number == membership.number }
289
+ errors << "The membership number ##{membership.number} is missing a history with this number. Please create a history with the #{membership.number} number or update the membership number above."
290
+ end
291
+
292
+ # Check that the last history does not have an end_on date
293
+
294
+ if last_history.end_on.present? && !last_history.removed?
295
+ errors << "The most recent history must have a blank end date. Please remove the end date of the most recent history entry or create another history."
296
+ elsif membership_histories.any? { |history| history.end_on.blank? && history != last_history }
297
+ errors << "The end date must be present for all past histories. Please add the end date to all histories, except the most recent history."
298
+ elsif !membership_history_continuous?
299
+ errors << "The start and end dates are overlapping or non-continuous. Please make sure each history start date has a matching history end date"
300
+ end
301
+
302
+ errors
303
+ end
304
+
305
+ def membership_history_continuous?
306
+ membership_histories.all? do |history|
307
+ history.end_on.blank? || (history.end_on.present? && history.removed?) || membership_histories.find { |h| h.start_on == history.end_on }.present?
308
+ end
309
+ end
310
+
311
+
268
312
  end
@@ -207,9 +207,10 @@ module EffectiveMembershipsRegistrar
207
207
  save!(owner, date: date)
208
208
  end
209
209
 
210
- def fees_paid!(owner, date: nil)
210
+ def fees_paid!(owner, date: nil, order_attributes: nil)
211
211
  raise('expecting a memberships owner') unless owner.class.respond_to?(:effective_memberships_owner?)
212
212
  raise('expected a member') unless owner.membership.present?
213
+ raise('expected a Hash of attributes') if order_attributes.present? && !order_attributes.kind_of?(Hash)
213
214
 
214
215
  # Date
215
216
  date ||= Time.zone.now
@@ -219,6 +220,7 @@ module EffectiveMembershipsRegistrar
219
220
 
220
221
  if owner.outstanding_fee_payment_fees.present?
221
222
  order = Effective::Order.new(items: owner.outstanding_fee_payment_fees, user: owner)
223
+ order.assign_attributes(order_attributes) if order_attributes.present?
222
224
  order.mark_as_purchased!
223
225
  end
224
226
 
@@ -2,6 +2,8 @@ module Effective
2
2
  class Membership < ActiveRecord::Base
3
3
  belongs_to :owner, polymorphic: true
4
4
 
5
+ attr_accessor :current_action
6
+
5
7
  has_many :membership_categories, -> { order(:id) }, inverse_of: :membership, dependent: :delete_all
6
8
  accepts_nested_attributes_for :membership_categories
7
9
 
@@ -54,7 +56,10 @@ module Effective
54
56
 
55
57
  before_validation do
56
58
  self.registration_on ||= joined_on
57
- self.number_as_integer ||= (Integer(number) rescue nil)
59
+ end
60
+
61
+ before_validation(if: -> { number_changed? }) do
62
+ self.number_as_integer = (Integer(number) rescue nil)
58
63
  end
59
64
 
60
65
  validates :number, presence: true, uniqueness: true
@@ -66,12 +71,29 @@ module Effective
66
71
  self.errors.add(:owner_id, 'must be a memberships owner') unless owner.class.effective_memberships_owner?
67
72
  end
68
73
 
74
+ validate(if: -> { registration_on.present? && joined_on.present? }) do
75
+ self.errors.add(:registration_on, 'must match or be greater than the joined date') if registration_on < joined_on
76
+ end
77
+
69
78
  def self.max_number
70
79
  maximum('number_as_integer') || 0
71
80
  end
72
81
 
73
82
  def to_s
74
- 'membership'
83
+ return 'membership' if owner.blank?
84
+
85
+ summary = [
86
+ owner.to_s,
87
+ 'is',
88
+ (categories.to_sentence),
89
+ 'member',
90
+ "##{number_was}",
91
+ "who joined #{joined_on&.strftime('%F') || '-'}",
92
+ ("and last registered #{registration_on.strftime('%F')}" if registration_on > joined_on),
93
+ (". Membership is Not In Good Standing because #{bad_standing_reason}" if bad_standing?)
94
+ ].compact.join(' ')
95
+
96
+ (summary + '.').html_safe
75
97
  end
76
98
 
77
99
  # We can't use the polymorphic has_many. So this is a helper.
@@ -36,5 +36,20 @@ module Effective
36
36
  'membership history'
37
37
  end
38
38
 
39
+ # These two accessors are for the memberships history form.
40
+ # But we just assign categories and category_ids directly in registrar.
41
+ def membership_categories
42
+ category_ids.present? ? EffectiveMemberships.Category.where(id: category_ids) : []
43
+ end
44
+
45
+ def membership_category_ids
46
+ membership_categories.map(&:id)
47
+ end
48
+
49
+ def membership_category_ids=(ids)
50
+ categories = EffectiveMemberships.Category.where(id: ids)
51
+ assign_attributes(categories: categories.map(&:to_s), category_ids: categories.map(&:id))
52
+ end
53
+
39
54
  end
40
55
  end
@@ -20,6 +20,12 @@ module Effective
20
20
  # Assign
21
21
  attr_accessor :category_ids
22
22
 
23
+ # Mark Fees Paid - Order Attributes
24
+ attr_accessor :payment_provider
25
+ attr_accessor :payment_card
26
+ attr_accessor :note_to_buyer
27
+ attr_accessor :note_internal
28
+
23
29
  # All Action Validations
24
30
  validates :current_action, presence: true
25
31
  validates :current_user, presence: true
@@ -65,7 +71,7 @@ module Effective
65
71
 
66
72
  def fees_paid!
67
73
  update!(current_action: :fees_paid)
68
- EffectiveMemberships.Registrar.fees_paid!(owner)
74
+ EffectiveMemberships.Registrar.fees_paid!(owner, order_attributes: order_attributes)
69
75
  end
70
76
 
71
77
  def remove!
@@ -103,6 +109,15 @@ module Effective
103
109
  EffectiveMemberships.Category.where(id: @category_ids) if @category_ids
104
110
  end
105
111
 
112
+ def order_attributes
113
+ {
114
+ payment_provider: @payment_provider.presence,
115
+ payment_card: @payment_card.presence,
116
+ note_to_buyer: @note_to_buyer.presence,
117
+ note_internal: @note_internal.presence
118
+ }.compact
119
+ end
120
+
106
121
  def skip_fees?
107
122
  EffectiveResources.truthy?(@skip_fees)
108
123
  end
@@ -0,0 +1,19 @@
1
+ = effective_form_with(model: [:admin, membership_history], engine: true) do |f|
2
+ - if inline_datatable?
3
+ = f.hidden_field :owner_id
4
+ = f.hidden_field :owner_type
5
+ - else
6
+ - raise('todo')
7
+ - collection = EffectiveMembershipsOwner.descendants.map { |d| [d.name.to_s, d.members.sorted] }.to_h
8
+ = f.select :owner_id, collection, polymorphic: true
9
+
10
+ = f.date_field :start_on, hint: 'The start date of this period in history. Must be present.'
11
+
12
+ = f.date_field :end_on, hint: 'The end date of this period in history. Must be present for all past histories. Must be blank in the most recent history, unless membership removed.'
13
+ = f.text_field :number, hint: 'The membership number'
14
+ = f.select :membership_category_ids, EffectiveMemberships.Category.all.sorted, label: 'Membership Categories', hint: 'The membership category or categories held during this period in history.'
15
+
16
+ = f.check_box :bad_standing, hint: 'Membership in bad standing'
17
+ = f.check_box :removed, hint: 'Membership removed'
18
+
19
+ = f.submit 'Update History', border: false, center: true, 'data-confirm': "Really update #{f.object.owner}?"
@@ -0,0 +1,7 @@
1
+ - raise('expected a memberships owner with a membership') unless owner.present? && owner.try(:membership).present?
2
+
3
+ - errors = owner.membership_history_errors
4
+
5
+ - if errors.present?
6
+ - errors.each do |error|
7
+ .mb-2.alert.alert-warning= error
@@ -0,0 +1,17 @@
1
+ - membership ||= owner.membership
2
+ - owner ||= membership.owner
3
+
4
+ - if membership.present?
5
+ = card('Member Information') do
6
+ = render 'admin/memberships/form_membership', membership: membership
7
+
8
+ = card('History') do
9
+ = render 'admin/membership_histories/validation', owner: owner
10
+ = render_datatable(Admin::EffectiveMembershipHistoriesDatatable.new(owner: owner), inline: true, simple: true)
11
+
12
+ - # Always render this one
13
+ = render 'admin/registrar_actions/form', owner: owner
14
+
15
+ - if membership.blank? && owner.membership_histories.present?
16
+ = card('History') do
17
+ = render_datatable(Admin::EffectiveMembershipHistoriesDatatable.new(owner: owner), inline: true, simple: true)
@@ -0,0 +1,22 @@
1
+ = effective_form_with(model: [:admin, membership], engine: true) do |f|
2
+ = f.hidden_field :owner_id
3
+ = f.hidden_field :owner_type
4
+
5
+ %p.text-muted
6
+ Change a member's information.
7
+
8
+ - f.object.current_action = true if f.errors.present?
9
+
10
+ = f.static_field :current_action, label: 'Current Membership' do
11
+ = membership.to_s
12
+
13
+ = f.check_box :current_action, label: 'Yes, update membership information'
14
+
15
+ = f.show_if :current_action, true do
16
+ = f.date_field :joined_on, label: 'Joined', hint: 'When the member first received any membership category'
17
+ = f.date_field :registration_on, label: 'Registered', hint: 'When the membership category last changed'
18
+ = f.text_field :number, hint: 'The membership number. Must be unique.'
19
+
20
+ %p.text-muted To update the current membership categories, use the 'Assign' or 'Reclassify' actions below
21
+
22
+ = f.submit 'Update Membership', border: false, center: true, 'data-confirm': "Really update #{f.object.owner}?"
@@ -25,6 +25,22 @@
25
25
  = f.check_box :current_action, label: 'Yes, mark this members fees paid in full'
26
26
 
27
27
  = f.show_if :current_action, true do
28
+ - if fees.blank?
29
+ %p There are no outstanding fee payment fees, so no order will be created.
30
+ - else
31
+ %p An order will be created and marked as paid with the following information:
32
+
33
+ = f.select :payment_provider, EffectiveOrders.admin_payment_providers, required: true
34
+
35
+ = f.text_field :payment_card,
36
+ label: 'Payment card type, cheque or transaction number',
37
+ hint: 'Do not enter credit card numbers here, or anywhere.'
38
+
39
+ .row
40
+ .col
41
+ = f.text_area :note_to_buyer, hint: 'This message will be displayed to the buyer on the receipt.'
42
+ .col
43
+ = f.text_area :note_internal, hint: 'For or internal admin use only. This note will never be displayed to the buyer.'
28
44
 
29
45
  = f.submit 'Mark Fees Paid', border: false, center: true,
30
46
  'data-confirm': "Really mark #{f.object.owner} fees paid?"
@@ -1,4 +1,4 @@
1
- - owners = current_user.effective_memberships_owners
1
+ - owners = current_user.memberships_owners
2
2
 
3
3
  - owners.each do |owner|
4
4
  - membership = owner.membership
data/config/routes.rb CHANGED
@@ -43,7 +43,8 @@ EffectiveMemberships::Engine.routes.draw do
43
43
  resources :applicant_course_names, except: [:show]
44
44
 
45
45
  resources :fee_payments, only: [:index, :show]
46
- resources :memberships, only: [:index]
46
+ resources :memberships, only: [:index, :update]
47
+ resources :membership_histories, except: [:show]
47
48
  resources :registrar_actions, only: [:create]
48
49
 
49
50
  resources :organizations, except: [:show] do
@@ -1,3 +1,3 @@
1
1
  module EffectiveMemberships
2
- VERSION = '0.4.0'
2
+ VERSION = '0.4.3'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_memberships
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.3
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-03-17 00:00:00.000000000 Z
11
+ date: 2022-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -247,6 +247,7 @@ files:
247
247
  - app/controllers/admin/categories_controller.rb
248
248
  - app/controllers/admin/fee_payments_controller.rb
249
249
  - app/controllers/admin/fees_controller.rb
250
+ - app/controllers/admin/membership_histories_controller.rb
250
251
  - app/controllers/admin/memberships_controller.rb
251
252
  - app/controllers/admin/organizations_controller.rb
252
253
  - app/controllers/admin/registrar_actions_controller.rb
@@ -334,6 +335,10 @@ files:
334
335
  - app/views/admin/fee_payments/_fee_payment.html.haml
335
336
  - app/views/admin/fees/_fee.html.haml
336
337
  - app/views/admin/fees/_form.html.haml
338
+ - app/views/admin/membership_histories/_form.html.haml
339
+ - app/views/admin/membership_histories/_validation.html.haml
340
+ - app/views/admin/memberships/_form.html.haml
341
+ - app/views/admin/memberships/_form_membership.html.haml
337
342
  - app/views/admin/memberships/_status.html.haml
338
343
  - app/views/admin/organizations/_fields.html.haml
339
344
  - app/views/admin/organizations/_form.html.haml