effective_events 0.13.1 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/admin/event_addons_controller.rb +1 -1
  3. data/app/controllers/admin/event_registrants_controller.rb +1 -1
  4. data/app/datatables/admin/effective_event_addons_datatable.rb +2 -1
  5. data/app/datatables/admin/effective_event_registrants_datatable.rb +34 -5
  6. data/app/datatables/admin/effective_event_registrations_datatable.rb +3 -2
  7. data/app/datatables/admin/effective_event_tickets_datatable.rb +1 -0
  8. data/app/datatables/effective_event_registrants_datatable.rb +6 -2
  9. data/app/helpers/effective_events_helper.rb +2 -3
  10. data/app/models/concerns/effective_events_event_registration.rb +46 -3
  11. data/app/models/effective/event.rb +9 -4
  12. data/app/models/effective/event_addon.rb +7 -3
  13. data/app/models/effective/event_registrant.rb +70 -13
  14. data/app/models/effective/event_ticket.rb +31 -2
  15. data/app/views/admin/event_addons/_form.html.haml +1 -1
  16. data/app/views/admin/event_registrants/_form.html.haml +3 -1
  17. data/app/views/admin/event_tickets/_form.html.haml +28 -21
  18. data/app/views/effective/event_registrations/_event_tickets.html.haml +43 -0
  19. data/app/views/effective/event_registrations/_form_blank_registrants.html.haml +1 -1
  20. data/app/views/effective/event_registrations/addons.html.haml +8 -10
  21. data/app/views/effective/event_registrations/checkout.html.haml +3 -3
  22. data/app/views/effective/event_registrations/submitted.html.haml +5 -1
  23. data/app/views/effective/event_registrations/summary.html.haml +1 -1
  24. data/app/views/effective/event_registrations/tickets.html.haml +13 -10
  25. data/config/locales/effective_events.en.yml +13 -0
  26. data/config/routes.rb +3 -0
  27. data/db/migrate/101_create_effective_events.rb +6 -0
  28. data/lib/effective_events/version.rb +1 -1
  29. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3436a306cb79a123bec79007b2730897d0bb3566dc1c0f2dbd6a31c5eb608a74
4
- data.tar.gz: 91c60cfc2122eae660a4fc5151f395bf9709bf00c0ccec0f50915e3a2c84f3a9
3
+ metadata.gz: 5d25eaee6089c0b76752550e6458c502124947d0e244a1d0e7b65584935caa43
4
+ data.tar.gz: 6b06663b84f26a0ba07324fe601c41bc7070117c42e019527ecffbcbcc0fcd6c
5
5
  SHA512:
6
- metadata.gz: 972bffdad5a0d13913ec5ab267c82308e1808d5217d0717bd2dba5f823f086974678e32e1c93a9d5ca47165941f006e9877748c2290b681711d5527a9661bbfb
7
- data.tar.gz: c268aa57f91a45d32af129d493ebf5a3dfe7dc4941e47cfb13d1efcf02353ce8c7d2fbea1ba01c073d9f1365e8284d2c00e03355deb7e1791ca997440447615c
6
+ metadata.gz: c599c5411f8c66a3dbe6f4c0ded8f9ef6f202c25d66cbdee33ee141cbbd3d4afd1fbd4a19ae55ccc9fc303a44882b5c971359595b31e30f551e5f35ed3b4f13b
7
+ data.tar.gz: 77ac6e934e9276331670d270dee9d82b55bf2eb8c2e1ed90ee57a9b1798aa56bcca6142d46dcb98b140adc1dbf61982ff469b9c2950c62a9a7252a0813d08272
@@ -5,6 +5,6 @@ module Admin
5
5
 
6
6
  include Effective::CrudController
7
7
 
8
- submit :mark_paid, 'Save and Mark Paid'
8
+ submit :mark_registered, 'Save and Mark Registered'
9
9
  end
10
10
  end
@@ -5,7 +5,7 @@ module Admin
5
5
 
6
6
  include Effective::CrudController
7
7
 
8
- submit :mark_paid, 'Save and Mark Paid'
8
+ submit :mark_registered, 'Save and Mark Registered'
9
9
 
10
10
  end
11
11
  end
@@ -4,6 +4,7 @@ module Admin
4
4
  scope :unarchived, label: "All"
5
5
  scope :purchased
6
6
  scope :deferred
7
+ scope :not_purchased
7
8
  scope :archived
8
9
  end
9
10
 
@@ -35,7 +36,7 @@ module Admin
35
36
  end
36
37
 
37
38
  collection do
38
- scope = Effective::EventAddon.deep.purchased_or_deferred.includes(:purchased_order, :owner)
39
+ scope = Effective::EventAddon.deep.registered.includes(:purchased_order, :owner)
39
40
 
40
41
  if attributes[:event_id].present?
41
42
  scope = scope.where(event: event)
@@ -4,15 +4,20 @@ module Admin
4
4
  scope :unarchived, label: "All"
5
5
  scope :purchased
6
6
  scope :deferred
7
+ scope :not_purchased
7
8
  scope :archived
8
9
  end
9
10
 
10
11
  datatable do
12
+ order :registered_at, :asc
13
+
11
14
  col :updated_at, visible: false
12
15
  col :created_at, visible: false
13
16
  col :id, visible: false
14
17
  col :archived, visible: false
15
18
 
19
+ col :registered_at
20
+
16
21
  col :event
17
22
 
18
23
  col :owner, visible: false
@@ -24,12 +29,36 @@ module Admin
24
29
  col :event_ticket, search: :string
25
30
  end
26
31
 
27
- col :purchased_order, visible: false
32
+ col :waitlisted do |registrant|
33
+ if registrant.promoted?
34
+ 'Promoted'
35
+ elsif registrant.waitlisted?
36
+ 'Waitlisted'
37
+ else
38
+ '-'
39
+ end
40
+ end
41
+
42
+ col :promoted, visible: false
28
43
 
44
+ col :name do |er|
45
+ if er.first_name.present?
46
+ "#{er.first_name} #{er.last_name}<br><small>#{mail_to(er.email)}</small>"
47
+ elsif er.owner.present?
48
+ er.owner.to_s + ' - GUEST'
49
+ else
50
+ 'Unknown'
51
+ end
52
+ end
53
+
29
54
  col :user, label: 'Member'
30
- col :first_name
31
- col :last_name
32
- col :email
55
+
56
+ col :orders, visible: false
57
+ col :price, as: :price
58
+
59
+ col :first_name, visible: false
60
+ col :last_name, visible: false
61
+ col :email, visible: false
33
62
  col :company, visible: false
34
63
 
35
64
  col :response1
@@ -40,7 +69,7 @@ module Admin
40
69
  end
41
70
 
42
71
  collection do
43
- scope = Effective::EventRegistrant.deep.purchased_or_deferred
72
+ scope = Effective::EventRegistrant.deep.registered
44
73
 
45
74
  if attributes[:event_id].present?
46
75
  scope = scope.where(event: event)
@@ -7,7 +7,8 @@ class Admin::EffectiveEventRegistrationsDatatable < Effective::Datatable
7
7
  col :created_at, label: 'Created', visible: false
8
8
  col :updated_at, label: 'Updated', visible: false
9
9
 
10
- col :submitted_at, label: 'Submitted', visible: false, as: :date
10
+ col :submitted_at, label: 'Submitted', as: :date
11
+ col :completed_at, label: 'Completed', as: :date
11
12
 
12
13
  col :event, search: :string
13
14
  col :owner
@@ -20,7 +21,7 @@ class Admin::EffectiveEventRegistrationsDatatable < Effective::Datatable
20
21
  end
21
22
 
22
23
  collection do
23
- EffectiveEvents.EventRegistration.all.deep.done
24
+ EffectiveEvents.EventRegistration.all.deep.where.not(status: :draft)
24
25
  end
25
26
 
26
27
  end
@@ -40,6 +40,7 @@ module Admin
40
40
 
41
41
  col :capacity_available, visible: false
42
42
  col :capacity, label: 'Capacity Total', visible: false
43
+ col :waitlist, visible: false
43
44
 
44
45
  col :question1, visible: false
45
46
  col :question2, visible: false
@@ -2,6 +2,7 @@
2
2
 
3
3
  class EffectiveEventRegistrantsDatatable < Effective::Datatable
4
4
  datatable do
5
+ order :id
5
6
 
6
7
  col :name do |er|
7
8
  if er.first_name.present?
@@ -13,9 +14,12 @@ class EffectiveEventRegistrantsDatatable < Effective::Datatable
13
14
  end
14
15
  end
15
16
 
17
+ col :id, visible: false
18
+
16
19
  col :event_ticket, search: :string, label: 'Ticket' do |er|
17
20
  [
18
21
  er.event_ticket.to_s,
22
+ (content_tag(:span, 'Waitlist', class: 'badge badge-warning') if er.waitlisted_not_promoted?),
19
23
  (content_tag(:span, 'Archived', class: 'badge badge-warning') if er.event_ticket&.archived?)
20
24
  ].compact.join('<br>').html_safe
21
25
  end
@@ -36,8 +40,8 @@ class EffectiveEventRegistrantsDatatable < Effective::Datatable
36
40
  end.join.html_safe
37
41
  end
38
42
 
39
- col :price, as: :price
40
-
43
+ # This is the non-waitlisted full price
44
+ col :event_ticket_price, as: :price, label: 'Price'
41
45
  col :archived, visible: false
42
46
 
43
47
  # no actions_col
@@ -38,10 +38,9 @@ module EffectiveEventsHelper
38
38
  title = ticket.to_s
39
39
  price = effective_events_ticket_price(event, ticket)
40
40
 
41
- remaining = (ticket.capacity.present? ? "#{ticket.capacity_available} remaining" : nil)
41
+ label = [title, price].compact.join(' - ')
42
42
 
43
- label = [title, price, remaining].compact.join(' - ')
44
- disabled = { disabled: :disabled } unless (authorized || event.event_ticket_available?(ticket, quantity: 1))
43
+ disabled = { disabled: :disabled } unless (authorized || ticket.waitlist? || event.event_ticket_available?(ticket, quantity: 1))
45
44
 
46
45
  [label, ticket.to_param, disabled].compact
47
46
  end
@@ -18,6 +18,9 @@ module EffectiveEventsEventRegistration
18
18
  end
19
19
 
20
20
  included do
21
+ # Needs to be first up here. Before the acts_as_purchasable_parent one voids the order
22
+ around_destroy :around_destroy_deferred_event_registration, if: -> { submit_order&.deferred? }
23
+
21
24
  acts_as_purchasable_parent
22
25
  acts_as_tokened
23
26
 
@@ -128,6 +131,15 @@ module EffectiveEventsEventRegistration
128
131
  # If we're submitted. Try to move to completed.
129
132
  before_save(if: -> { submitted? }) { try_completed! }
130
133
 
134
+ def around_destroy_deferred_event_registration
135
+ raise('expecting a deferred submit order') unless submit_order&.deferred?
136
+
137
+ waitlisted_event_tickets_was = event_tickets().select(&:waitlist?)
138
+ yield
139
+ waitlisted_event_tickets_was.each { |event_ticket| event_ticket.update_waitlist! }
140
+ true
141
+ end
142
+
131
143
  def delayed_payment_date_upcoming?
132
144
  event&.delayed_payment_date_upcoming?
133
145
  end
@@ -180,7 +192,13 @@ module EffectiveEventsEventRegistration
180
192
  true
181
193
  end
182
194
 
195
+ def after_submit_deferred!
196
+ update_deferred_event_registration!
197
+ end
198
+
183
199
  def after_submit_purchased!
200
+ event_registrants.each { |event_registrant| event_registrant.registered! }
201
+
184
202
  notifications = event.event_notifications.select(&:registrant_purchased?)
185
203
  notifications.each { |notification| notification.notify!(event_registrants: event_registrants) }
186
204
  end
@@ -200,12 +218,19 @@ module EffectiveEventsEventRegistration
200
218
  end
201
219
 
202
220
  def tickets!
203
- after_commit { update_submit_fees_and_order! } if submit_order.present?
221
+ after_commit do
222
+ update_submit_fees_and_order! if submit_order.present?
223
+ update_deferred_event_registration! if submit_order&.deferred?
224
+ end
225
+
204
226
  save!
205
227
  end
206
228
 
207
229
  def addons!
208
- after_commit { update_submit_fees_and_order! } if submit_order.present?
230
+ after_commit do
231
+ update_submit_fees_and_order! if submit_order.present?
232
+ end
233
+
209
234
  save!
210
235
  end
211
236
 
@@ -265,11 +290,17 @@ module EffectiveEventsEventRegistration
265
290
  event_addons
266
291
  end
267
292
 
293
+ def event_tickets
294
+ present_event_registrants.map(&:event_ticket).uniq
295
+ end
296
+
268
297
  def unavailable_event_tickets
269
298
  unavailable = []
270
299
 
271
300
  present_event_registrants.map(&:event_ticket).group_by { |t| t }.each do |event_ticket, event_tickets|
272
- unavailable << event_ticket unless event.event_ticket_available?(event_ticket, quantity: event_tickets.length)
301
+ unless event_ticket.waitlist? || event.event_ticket_available?(event_ticket, quantity: event_tickets.length)
302
+ unavailable << event_ticket
303
+ end
273
304
  end
274
305
 
275
306
  unavailable
@@ -317,6 +348,18 @@ module EffectiveEventsEventRegistration
317
348
 
318
349
  private
319
350
 
351
+ def update_deferred_event_registration!
352
+ raise('expected a deferred submit order') unless submit_order&.deferred?
353
+
354
+ # Mark registered anyone who hasn't been registered yet. They are now!
355
+ event_registrants.reject(&:registered?).each { |event_registrant| event_registrant.registered! }
356
+
357
+ # Update the waitlist for any event tickets
358
+ event_tickets.select(&:waitlist?).each { |event_ticket| event_ticket.update_waitlist! }
359
+
360
+ true
361
+ end
362
+
320
363
  def present_event_registrants
321
364
  event_registrants.reject(&:marked_for_destruction?).reject(&:archived?)
322
365
  end
@@ -15,18 +15,18 @@ module Effective
15
15
  has_many :event_products, -> { EventProduct.sorted }, inverse_of: :event, dependent: :destroy
16
16
  accepts_nested_attributes_for :event_products, allow_destroy: true
17
17
 
18
- has_many :event_registrants, -> { order(:event_ticket_id, :created_at) }, inverse_of: :event
18
+ has_many :event_registrants, -> { order(:event_ticket_id).order(:id) }, inverse_of: :event
19
19
  accepts_nested_attributes_for :event_registrants, allow_destroy: true
20
20
 
21
- has_many :event_addons, -> { order(:event_product_id, :created_at) }, inverse_of: :event
21
+ has_many :event_addons, -> { order(:event_product_id).order(:id) }, inverse_of: :event
22
22
  accepts_nested_attributes_for :event_addons, allow_destroy: true
23
23
 
24
24
  has_many :event_notifications, -> { order(:id) }, inverse_of: :event, dependent: :destroy
25
25
  accepts_nested_attributes_for :event_notifications, allow_destroy: true
26
26
 
27
27
  # Used by the registration_available checks
28
- has_many :registered_event_registrants, -> { EventRegistrant.registered }, class_name: 'Effective::EventRegistrant', inverse_of: :event
29
- has_many :registered_event_addons, -> { EventAddon.registered }, class_name: 'Effective::EventAddon', inverse_of: :event
28
+ has_many :registered_event_registrants, -> { EventRegistrant.registered.unarchived }, class_name: 'Effective::EventRegistrant', inverse_of: :event
29
+ has_many :registered_event_addons, -> { EventAddon.registered.unarchived }, class_name: 'Effective::EventAddon', inverse_of: :event
30
30
 
31
31
  # rich_text_body - Used by the select step
32
32
  has_many_rich_texts
@@ -192,6 +192,10 @@ module Effective
192
192
  rich_text_excerpt
193
193
  end
194
194
 
195
+ def any_waitlist?
196
+ event_tickets.any? { |et| et.waitlist? }
197
+ end
198
+
195
199
  def published?
196
200
  return false if draft?
197
201
  return false if published_at.blank?
@@ -214,6 +218,7 @@ module Effective
214
218
 
215
219
  def sold_out?
216
220
  return false unless event_tickets.present?
221
+ return false if any_waitlist?
217
222
  event_tickets.none? { |event_ticket| event_ticket_available?(event_ticket, quantity: 1) }
218
223
  end
219
224
 
@@ -42,7 +42,7 @@ module Effective
42
42
 
43
43
  scope :sorted, -> { order(:id) }
44
44
  scope :deep, -> { includes(:event, :event_product) }
45
- scope :registered, -> { purchased_or_deferred.unarchived }
45
+ scope :registered, -> { purchased_or_deferred }
46
46
 
47
47
  before_validation(if: -> { event_registration.present? }) do
48
48
  self.event ||= event_registration.event
@@ -84,8 +84,12 @@ module Effective
84
84
  event_product.qb_item_name
85
85
  end
86
86
 
87
- # This is the Admin Save and Mark Paid action
88
- def mark_paid!
87
+ def registered?
88
+ purchased_or_deferred?
89
+ end
90
+
91
+ # This is the Admin Save and Mark Registered historic action
92
+ def mark_registered!
89
93
  raise('expected a blank event registration') if event_registration.present?
90
94
 
91
95
  save!
@@ -34,6 +34,10 @@ module Effective
34
34
  blank_registrant :boolean
35
35
  member_registrant :boolean
36
36
 
37
+ waitlisted :boolean
38
+ promoted :boolean # An admin marked this registrant as promoted from the waitlist
39
+ registered_at :datetime # When the order is deferred or purchased
40
+
37
41
  # Question Responses
38
42
  question1 :text
39
43
  question2 :text
@@ -54,8 +58,10 @@ module Effective
54
58
  end
55
59
 
56
60
  scope :sorted, -> { order(:last_name) }
57
- scope :deep, -> { includes(:event, :event_ticket) }
58
- scope :registered, -> { purchased_or_deferred.unarchived }
61
+ scope :deep, -> { includes(:event, :event_ticket, :owner) }
62
+
63
+ #scope :registered, -> { purchased_or_deferred.unarchived }
64
+ scope :registered, -> { where.not(registered_at: nil) }
59
65
 
60
66
  before_validation(if: -> { event_registration.present? }) do
61
67
  self.event ||= event_registration.event
@@ -74,6 +80,10 @@ module Effective
74
80
  assign_price()
75
81
  end
76
82
 
83
+ validate(if: -> { event_ticket.present? }, unless: -> { purchased? }) do
84
+ errors.add(:waitlisted, 'is not permitted for a non-waitlist event ticket') if waitlisted? && !event_ticket.waitlist?
85
+ end
86
+
77
87
  validates :user_id, uniqueness: { scope: [:event_id], allow_blank: true, message: 'is already registered for this event' }
78
88
  validates :price, presence: true, numericality: { greater_than_or_equal_to: 0 }
79
89
  validates :email, email: true
@@ -97,16 +107,30 @@ module Effective
97
107
  validates :email, presence: true, unless: -> { user.present? }
98
108
  end
99
109
 
110
+ after_defer do
111
+ registered! if event_registration.blank? && !registered?
112
+ end
113
+
114
+ after_purchase do
115
+ registered! if event_registration.blank? && !registered?
116
+ end
117
+
100
118
  def to_s
101
119
  persisted? ? title : 'registrant'
102
120
  end
103
121
 
104
122
  def title
105
- "#{event_ticket} - #{last_first_name}"
123
+ [event_ticket.to_s, name, ('WAITLIST' if waitlisted_not_promoted?)].compact.join(' - ')
106
124
  end
107
125
 
108
126
  def name
109
- first_name.present? ? "#{first_name} #{last_name}" : "GUEST"
127
+ if first_name.present?
128
+ "#{first_name} #{last_name}"
129
+ elsif owner.present?
130
+ owner.to_s + ' - GUEST'
131
+ else
132
+ 'GUEST'
133
+ end
110
134
  end
111
135
 
112
136
  def last_first_name
@@ -125,27 +149,48 @@ module Effective
125
149
  event_ticket&.qb_item_name
126
150
  end
127
151
 
128
- # This is the Admin Save and Mark Paid action
129
- def mark_paid!
130
- raise('expected a blank event registration') if event_registration.present?
152
+ def registered?
153
+ registered_at.present?
154
+ end
131
155
 
156
+ # Called by an event_registration after_defer and after_purchase
157
+ def registered!
158
+ self.registered_at ||= Time.zone.now
132
159
  save!
160
+ end
133
161
 
134
- order = Effective::Order.new(items: self, user: owner)
135
- order.mark_as_purchased!
162
+ # This is the Admin Save and Mark Registered action
163
+ def mark_registered!
164
+ registered!
165
+ end
166
+
167
+ def promote!
168
+ raise('expected a waitlist? event_ticket') unless event_ticket.waitlist?
169
+
170
+ update!(promoted: true)
171
+ orders.reject(&:purchased?).each { |order| order.update_purchasable_attributes! }
136
172
 
137
173
  true
138
174
  end
139
175
 
140
- private
176
+ def unpromote!
177
+ raise('expected a waitlist? event_ticket') unless event_ticket.waitlist?
141
178
 
142
- def assign_price
143
- raise('is already purchased') if purchased?
179
+ update!(promoted: false)
180
+ orders.reject(&:purchased?).each { |order| order.update_purchasable_attributes! }
181
+
182
+ true
183
+ end
184
+
185
+ def registered?
186
+ registered_at.present?
187
+ end
144
188
 
189
+ def event_ticket_price
145
190
  raise('expected an event') if event.blank?
146
191
  raise('expected an event ticket') if event_ticket.blank?
147
192
 
148
- price = if event.early_bird?
193
+ if event.early_bird?
149
194
  event_ticket.early_bird_price # Early Bird Pricing
150
195
  elsif event_ticket.regular?
151
196
  event_ticket.regular_price
@@ -156,6 +201,18 @@ module Effective
156
201
  else
157
202
  raise("Unexpected event ticket price calculation")
158
203
  end
204
+ end
205
+
206
+ def waitlisted_not_promoted?
207
+ (waitlisted? && !promoted?)
208
+ end
209
+
210
+ private
211
+
212
+ def assign_price
213
+ raise('is already purchased') if purchased?
214
+
215
+ price = waitlisted_not_promoted? ? 0 : event_ticket_price
159
216
 
160
217
  assign_attributes(price: price)
161
218
  end
@@ -8,9 +8,11 @@ module Effective
8
8
 
9
9
  belongs_to :event
10
10
 
11
- has_many :event_registrants
11
+ has_many :event_registrants, -> { order(:id) }, inverse_of: :event_ticket
12
+ accepts_nested_attributes_for :event_registrants
13
+
12
14
  has_many :purchased_event_registrants, -> { EventRegistrant.purchased.unarchived }, class_name: 'Effective::EventRegistrant'
13
- has_many :registered_event_registrants, -> { EventRegistrant.registered.unarchived }, class_name: 'Effective::EventRegistrant'
15
+ has_many :registered_event_registrants, -> { EventRegistrant.registered.unarchived.order(:registered_at).order(:id) }, class_name: 'Effective::EventRegistrant'
14
16
 
15
17
  log_changes(to: :event) if respond_to?(:log_changes)
16
18
 
@@ -20,7 +22,9 @@ module Effective
20
22
 
21
23
  effective_resource do
22
24
  title :string
25
+
23
26
  capacity :integer
27
+ waitlist :boolean
24
28
 
25
29
  category :string
26
30
 
@@ -63,10 +67,34 @@ module Effective
63
67
  validates :early_bird_price, presence: true, if: -> { event&.early_bird_end_at.present? }
64
68
  validates :early_bird_price, numericality: { greater_than_or_equal_to: 0, allow_blank: true }
65
69
 
70
+ validates :capacity, numericality: { greater_than_or_equal_to: 0, allow_blank: true }
71
+ validates :capacity, numericality: { greater_than_or_equal_to: 1, message: 'must have a non-zero capacity when using waitlist' }, if: -> { waitlist? }
72
+
66
73
  def to_s
67
74
  title.presence || 'New Event Ticket'
68
75
  end
69
76
 
77
+ # This is supposed to be an indempotent big update the world thing
78
+ def update_waitlist!
79
+ return false unless waitlist?
80
+
81
+ changed_event_registrants = registered_event_registrants.each_with_index.map do |event_registrant, index|
82
+ next if event_registrant.purchased?
83
+
84
+ waitlisted_was = event_registrant.waitlisted?
85
+ waitlisted = (waitlist? && index >= capacity)
86
+ next if waitlisted == waitlisted_was
87
+
88
+ event_registrant.update!(waitlisted: waitlisted) # Updates price
89
+ event_registrant
90
+ end.compact
91
+
92
+ orders = changed_event_registrants.flat_map { |event_registrant| event_registrant.deferred_orders }.compact.uniq
93
+ orders.each { |order| order.update_purchasable_attributes! }
94
+
95
+ true
96
+ end
97
+
70
98
  def capacity_available
71
99
  return nil if capacity.blank?
72
100
  [(capacity - registered_event_registrants_count), 0].max
@@ -95,5 +123,6 @@ module Effective
95
123
  def member_or_non_member?
96
124
  category == 'Member or Non-Member'
97
125
  end
126
+
98
127
  end
99
128
  end
@@ -16,6 +16,6 @@
16
16
  = f.check_box :archived, label: "Archive this addon. It will be displayed as archived on the owner's event registration"
17
17
 
18
18
  - if f.object.new_record?
19
- = f.submit 'Save and Mark Paid'
19
+ = f.submit 'Save and Mark Registered'
20
20
  - else
21
21
  = f.submit 'Save'
@@ -11,11 +11,13 @@
11
11
  - else
12
12
  = f.static_field :owner
13
13
 
14
+ = f.datetime_field :registered_at
15
+
14
16
  = render 'effective/event_registrants/fields', f: f, event: event_registrant.event, namespace: :admin
15
17
 
16
18
  = f.check_box :archived, label: "Archive this registrant. It will be displayed as archived on the owner's event registration"
17
19
 
18
20
  - if f.object.new_record?
19
- = f.submit 'Save and Mark Paid'
21
+ = f.submit 'Save and Mark Registered'
20
22
  - else
21
23
  = f.submit 'Save'
@@ -5,25 +5,32 @@
5
5
  = f.text_field :title
6
6
 
7
7
  .row
8
- .col= f.select :category, Effective::EventTicket::CATEGORIES, hint: "Determines who can purchase this ticket at which price. Also determines if the dropdown of related members is displayed to the purchaser."
9
- .col= f.number_field :capacity, hint: "The number of registrations will be limited to capacity.<br>Leave blank for unlimited capacity."
10
-
11
- = f.show_if(:category, 'Regular') do
12
- .alert.alert-info.mb-4
13
- %strong Regular Ticket:
14
- Anyone will be able to purchase this ticket. They will be asked for a first name, last name and email. Will not display the dropdown list of related members. Only the regular price applies.
15
-
16
- = f.show_if(:category, 'Member Only') do
17
- .alert.alert-info.mb-4
18
- %strong Member Only Ticket:
19
- Only members will be able to purchase this ticket. They must select a member from the dropdown list of related members. Only the member price applies.
20
-
21
- = f.show_if(:category, 'Member or Non-Member') do
22
- .alert.alert-info.mb-4
23
- %strong Member or Non-Member Ticket:
24
- Anyone will be able to purchase this ticket. They can select a member from the dropdown list of related members to receive the member pricing.
25
- Or, they can enter a first name, last name and email to receive the regular pricing.
26
-
8
+ .col
9
+ = f.select :category, Effective::EventTicket::CATEGORIES, hint: "Determines who can purchase this ticket at which price. Also determines if the dropdown of related members is displayed to the purchaser."
10
+
11
+ = f.show_if(:category, 'Regular') do
12
+ .alert.alert-info.mb-4
13
+ %strong Regular Ticket:
14
+ Anyone will be able to purchase this ticket. They will be asked for a first name, last name and email. Will not display the dropdown list of related members. Only the regular price applies.
15
+
16
+ = f.show_if(:category, 'Member Only') do
17
+ .alert.alert-info.mb-4
18
+ %strong Member Only Ticket:
19
+ Only members will be able to purchase this ticket. They must select a member from the dropdown list of related members. Only the member price applies.
20
+
21
+ = f.show_if(:category, 'Member or Non-Member') do
22
+ .alert.alert-info.mb-4
23
+ %strong Member or Non-Member Ticket:
24
+ Anyone will be able to purchase this ticket. They can select a member from the dropdown list of related members to receive the member pricing.
25
+ Or, they can enter a first name, last name and email to receive the regular pricing.
26
+
27
+ .col
28
+ = f.number_field :capacity, hint: "The number of registrations will be limited to capacity.<br>Leave blank for unlimited capacity."
29
+
30
+ - if f.object.event&.delayed?
31
+ = f.check_box :waitlist,
32
+ label: "Yes, add to waitlist once capacity is reached",
33
+ hint: "requires a delayed payment event. Once capacity is reached, new registrations will be added to the waitlist. Payment information is collected but not charged for waitlisted registrants."
27
34
  .row
28
35
  .col-md-6
29
36
  - if f.object.event&.early_bird_end_at.present?
@@ -37,9 +44,9 @@
37
44
 
38
45
  .row
39
46
  - if defined?(EffectiveQbSync)
40
- .col= f.text_field :qb_item_name, label: 'QuickBooks item name'
41
- .col= f.check_box :tax_exempt
47
+ .col-md-6= f.text_field :qb_item_name, label: 'QuickBooks item name'
42
48
 
49
+ = f.check_box :tax_exempt
43
50
  = f.check_box :archived, label: 'Archive this ticket. It will be unavailable for purchase.'
44
51
 
45
52
  = card('Questions') do
@@ -0,0 +1,43 @@
1
+ .card.mb-4
2
+ .card-body
3
+ - tickets = resource.event.event_tickets.reject(&:archived?)
4
+
5
+ - member_column = tickets.any? { |ticket| ticket.member_only? || ticket.member_or_non_member? }
6
+ - waitlist = tickets.any? { |ticket| ticket.waitlist? }
7
+
8
+ %p This event has the following tickets available for purchase:
9
+
10
+ %table.table
11
+ %thead
12
+ %tr
13
+ %th Ticket
14
+ - if member_column
15
+ %th Member Price
16
+ %th #{'Regualar ' if member_column}Price
17
+
18
+ %tbody
19
+ - tickets.each do |ticket|
20
+ %tr
21
+ %td
22
+ = ticket.to_s
23
+ - if ticket.capacity.present?
24
+ %br
25
+ %small
26
+ #{ticket.capacity_available} remaining
27
+ - if ticket.waitlist?
28
+ before waitlist
29
+
30
+ - if member_column
31
+ %td
32
+ - if ticket.member_price.present?
33
+ = price_to_currency(ticket.member_price)
34
+ - else
35
+ = '-'
36
+ %td
37
+ - if ticket.regular_price.present?
38
+ = price_to_currency(ticket.regular_price)
39
+ - else
40
+ = '-'
41
+
42
+ - if waitlist
43
+ %p If the ticket capacity has been reached you will be added to the waitlist.
@@ -13,7 +13,7 @@
13
13
  %p
14
14
  = ticket
15
15
  = ' - '
16
- = price_to_currency(registrant.price)
16
+ = price_to_currency(registrant.event_ticket_price)
17
17
 
18
18
  = fr.check_box :blank_registrant, label: "I will return and add this ticket's information later"
19
19
 
@@ -8,16 +8,14 @@
8
8
 
9
9
  = render 'effective/event_registrations/content', resource: resource
10
10
 
11
- .card
12
- .card-body
13
- = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
14
- = f.hidden_field :id
11
+ = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
12
+ = f.hidden_field :id
15
13
 
16
- = f.has_many :event_addons, f.object.build_event_addons do |fp|
17
- = render('effective/event_addons/fields', f: fp, event: f.object.event)
14
+ = f.has_many :event_addons, f.object.build_event_addons, reorder: false do |fp|
15
+ = render('effective/event_addons/fields', f: fp, event: f.object.event)
18
16
 
19
- - if resource.delayed_payment_date_upcoming? && resource.submit_order.present?
20
- .alert.alert-info If the price of your registration changes, your order will be automatically updated.
17
+ - if resource.delayed_payment_date_upcoming? && resource.submit_order&.delayed? && resource.submit_order&.deferred?
18
+ .alert.alert-info If the price of your registration changes, your order will be automatically updated.
21
19
 
22
- %hr
23
- = f.save 'Save and Continue'
20
+ %hr
21
+ = f.save 'Save and Continue'
@@ -3,17 +3,17 @@
3
3
 
4
4
  - if resource.submit_order.deferred?
5
5
  = card do
6
- = render_checkout_step2(resource.submit_order, purchased_url: wizard_path(:complete), deferred_url: wizard_path(:submitted), declined_url: wizard_path(:checkout))
6
+ = render_checkout_step2(resource.submit_order, purchased_url: wizard_path(:complete), deferred_url: wizard_path(:submitted), declined_url: wizard_path(:checkout), skip_order: resource.submit_order.delayed?)
7
7
  - elsif resource.event.registerable? == false
8
8
  .alert.alert-danger Your selected event is no longer available for registration.
9
9
  - elsif resource.unavailable_event_tickets.present?
10
10
  .alert.alert-danger Your selected number of event tickets are no longer available.
11
11
  - elsif resource.unavailable_event_products.present?
12
12
  .alert.alert-danger Your selected number of event add-ons are no longer available.
13
+
13
14
  - else
14
15
  = card do
15
- = render_checkout_step2(resource.submit_order, purchased_url: wizard_path(:complete), deferred_url: wizard_path(:submitted), declined_url: wizard_path(:checkout))
16
-
16
+ = render_checkout_step2(resource.submit_order, purchased_url: wizard_path(:complete), deferred_url: wizard_path(:submitted), declined_url: wizard_path(:checkout), skip_order: resource.submit_order.delayed?)
17
17
 
18
18
  - if resource.submit_order.delayed? && resource.submit_order.deferred?
19
19
  = link_to 'Save and Continue', wizard_path(:submitted), class: 'btn btn-primary'
@@ -22,8 +22,12 @@
22
22
 
23
23
  = card do
24
24
  %h3 All done
25
- %p Your registration for #{resource.event} has been submitted. You're all done!
25
+ %p The registration for #{resource.event} has been submitted. You're all done!
26
26
  %p= resource.submit_order.delayed_payment_info
27
+
28
+ - if resource.event_registrants.any?(&:waitlisted?)
29
+ %p Any waitlist tickets are not charged. If you are moved off the waitlist, the full price will be charged.
30
+
27
31
  %p You can #{link_to('change your registration', wizard_path(:tickets))} until the payment date.
28
32
 
29
33
  = render 'effective/event_registrations/event_registration', event_registration: resource
@@ -5,4 +5,4 @@
5
5
 
6
6
  = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
7
7
  = f.hidden_field :id
8
- = f.submit 'Save and Continue', class: 'btn btn-primary'
8
+ = f.save 'Save and Continue'
@@ -1,16 +1,19 @@
1
1
  = render 'layout' do
2
+
3
+ = render 'effective/event_registrations/event_tickets', resource: resource
4
+
2
5
  = render 'effective/event_registrations/content', resource: resource
3
6
 
4
- .card
5
- .card-body
6
- = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
7
- = f.hidden_field :id
7
+ = effective_form_with(model: resource, url: wizard_path(step), method: :put) do |f|
8
+ = f.hidden_field :id
9
+
10
+ %p Please select one or more tickets to register for this event:
8
11
 
9
- = f.has_many :event_registrants, f.object.build_event_registrants do |fr|
10
- = render('effective/event_registrants/fields', f: fr, event: f.object.event, namespace: :events)
12
+ = f.has_many :event_registrants, f.object.build_event_registrants, reorder: false do |fr|
13
+ = render('effective/event_registrants/fields', f: fr, event: f.object.event, namespace: :events)
11
14
 
12
- - if resource.delayed_payment_date_upcoming? && resource.submit_order.present?
13
- .alert.alert-info If the price of your registration changes, your order will be automatically updated.
15
+ - if resource.delayed_payment_date_upcoming? && resource.submit_order&.delayed? && resource.submit_order&.deferred?
16
+ .alert.alert-info If the price of your registration changes, your order will be automatically updated.
14
17
 
15
- %hr
16
- = f.save 'Save and Continue'
18
+ %hr
19
+ = f.save 'Save and Continue'
@@ -0,0 +1,13 @@
1
+ en:
2
+ effective_events:
3
+ name: 'Effective Events'
4
+ acronym: 'Events'
5
+
6
+ activerecord:
7
+ models:
8
+ effective/event: 'Event'
9
+ effective/event_registrant: 'Event Registrant'
10
+
11
+ attributes:
12
+ effective/event_registrant:
13
+ user: 'Member'
data/config/routes.rb CHANGED
@@ -42,6 +42,9 @@ EffectiveEvents::Engine.routes.draw do
42
42
  end
43
43
 
44
44
  resources :event_registrants, except: [:show] do
45
+ post :promote, on: :member
46
+ post :unpromote, on: :member
47
+
45
48
  post :archive, on: :member
46
49
  post :unarchive, on: :member
47
50
  end
@@ -41,6 +41,7 @@ class CreateEffectiveEvents < ActiveRecord::Migration[6.0]
41
41
 
42
42
  t.string :title
43
43
  t.integer :capacity
44
+ t.boolean :waitlist, default: false
44
45
  t.string :category
45
46
 
46
47
  t.integer :regular_price
@@ -84,6 +85,11 @@ class CreateEffectiveEvents < ActiveRecord::Migration[6.0]
84
85
  t.boolean :blank_registrant, default: false
85
86
  t.boolean :member_registrant, default: false
86
87
 
88
+ t.boolean :waitlisted, default: false
89
+ t.boolean :promoted, default: false
90
+
91
+ t.datetime :registered_at
92
+
87
93
  t.text :response1
88
94
  t.text :response2
89
95
  t.text :response3
@@ -1,3 +1,3 @@
1
1
  module EffectiveEvents
2
- VERSION = '0.13.1'.freeze
2
+ VERSION = '0.14.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_events
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.1
4
+ version: 0.14.0
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: 2024-06-04 00:00:00.000000000 Z
11
+ date: 2024-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -275,6 +275,7 @@ files:
275
275
  - app/views/effective/event_registrations/_content.html.haml
276
276
  - app/views/effective/event_registrations/_dashboard.html.haml
277
277
  - app/views/effective/event_registrations/_event_registration.html.haml
278
+ - app/views/effective/event_registrations/_event_tickets.html.haml
278
279
  - app/views/effective/event_registrations/_form_blank_registrants.html.haml
279
280
  - app/views/effective/event_registrations/_layout.html.haml
280
281
  - app/views/effective/event_registrations/_orders.html.haml
@@ -298,6 +299,7 @@ files:
298
299
  - app/views/effective/events_mailer/event_registrant_purchased.liquid
299
300
  - app/views/simple_calendar/_month_calendar.html.haml
300
301
  - config/effective_events.rb
302
+ - config/locales/effective_events.en.yml
301
303
  - config/locales/en.yml
302
304
  - config/routes.rb
303
305
  - db/migrate/101_create_effective_events.rb