effective_events 0.19.2 → 0.20.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/effective_events/base.js +32 -0
- data/app/controllers/effective/event_registrants_select2_ajax_controller.rb +61 -0
- data/app/controllers/effective/event_registrations_controller.rb +17 -1
- data/app/datatables/admin/effective_event_registrants_datatable.rb +2 -1
- data/app/datatables/effective_event_registrants_datatable.rb +11 -11
- data/app/datatables/effective_event_registrations_datatable.rb +2 -1
- data/app/helpers/effective_events_helper.rb +2 -19
- data/app/mailers/effective/events_mailer.rb +1 -1
- data/app/models/concerns/effective_events_event_registration.rb +141 -51
- data/app/models/effective/event.rb +32 -12
- data/app/models/effective/event_notification.rb +1 -1
- data/app/models/effective/event_registrant.rb +171 -45
- data/app/models/effective/event_ticket.rb +20 -17
- data/app/models/effective/event_ticket_selection.rb +28 -0
- data/app/views/admin/event_registrants/_form.html.haml +16 -3
- data/app/views/admin/event_tickets/_form.html.haml +1 -1
- data/app/views/effective/event_registrants/_fields.html.haml +47 -28
- data/app/views/effective/event_registrations/_details.html.haml +1 -0
- data/app/views/effective/event_registrations/_fields_event_registrants.html.haml +26 -0
- data/app/views/effective/event_registrations/_fields_event_ticket_selections.html.haml +85 -0
- data/app/views/effective/event_registrations/_form_blank_registrants.html.haml +12 -14
- data/app/views/effective/event_registrations/_layout.html.haml +9 -1
- data/app/views/effective/event_registrations/billing.html.haml +1 -1
- data/app/views/effective/event_registrations/details.html.haml +10 -0
- data/app/views/effective/event_registrations/summary.html.haml +2 -2
- data/app/views/effective/event_registrations/tickets.html.haml +3 -13
- data/app/views/effective/events/show.html.haml +34 -34
- data/config/effective_events.rb +11 -4
- data/config/routes.rb +5 -0
- data/db/migrate/101_create_effective_events.rb +16 -1
- data/lib/effective_events/version.rb +1 -1
- data/lib/effective_events.rb +6 -4
- metadata +8 -7
- data/app/views/effective/event_registrants/_fields_member_only.html.haml +0 -10
- data/app/views/effective/event_registrants/_fields_member_or_non_member.html.haml +0 -34
- data/app/views/effective/event_registrants/_fields_questions.html.haml +0 -8
- data/app/views/effective/event_registrants/_fields_regular.html.haml +0 -8
- data/app/views/effective/event_registrations/_event_tickets.html.haml +0 -68
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31dd5da1350e8c47f262ae186a59758c589747ad054bb2d9c73a766aab858673
|
4
|
+
data.tar.gz: 3523f02edb4b5e1e5144c4ae43686ab077a4873f0ce5346fb27429a05853e2de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5a012b7f897b93841d14080109278adc3d52231ffc9e0e5fc71c3379406d28cbf5ed6b11a3455eaeb8737b7065abc87ad78eea8b7441e16d34d886c919d00bd
|
7
|
+
data.tar.gz: 1f21df543c199fbb5f0cfb565b928d4d29405c731dc24dd8824baeb5b83e3436f63966100af4cefac0a1700757737e5e52f982a4ddc9e0ea36055186e5dac1fc
|
@@ -0,0 +1,32 @@
|
|
1
|
+
// For the Ticket Details screen
|
2
|
+
$(document).on('select2:select', '[data-event-registrant-user-search]', function(event) {
|
3
|
+
var data = event.params.data['data'];
|
4
|
+
var $form = $(event.currentTarget).closest('.event-registrant-user-fields')
|
5
|
+
|
6
|
+
// Set the organization_id
|
7
|
+
$form.find('input[type="hidden"][name$="[organization_id]"]').val(data.organization_id || '')
|
8
|
+
$form.find('input[type="hidden"][name$="[organization_type]"]').val(data.organization_type || '')
|
9
|
+
|
10
|
+
// Disable everything else
|
11
|
+
$form.find('input[name$="[first_name]"]').val(data.first_name || '').prop('disabled', true)
|
12
|
+
$form.find('input[name$="[last_name]"]').val(data.last_name || '').prop('disabled', true)
|
13
|
+
$form.find('input[name$="[email]"]').val(data.email || '').prop('disabled', true)
|
14
|
+
|
15
|
+
$form.find('select[name$="[organization_id]"]').val(data.organization_id || '').trigger('change').prop('disabled', true)
|
16
|
+
$form.find('input[name$="[company]"]').val(data.company || '').prop('disabled', true)
|
17
|
+
});
|
18
|
+
|
19
|
+
$(document).on('select2:unselect', '[data-event-registrant-user-search]', function(event) {
|
20
|
+
var $form = $(event.currentTarget).closest('.event-registrant-user-fields')
|
21
|
+
|
22
|
+
// Unset the organization_id
|
23
|
+
$form.find('input[type="hidden"][name$="[organization_id]"]').val('')
|
24
|
+
|
25
|
+
// Enable everything else
|
26
|
+
$form.find('input[name$="[first_name]"]').val('').prop('disabled', false)
|
27
|
+
$form.find('input[name$="[last_name]"]').val('').prop('disabled', false)
|
28
|
+
$form.find('input[name$="[email]"]').val('').prop('disabled', false)
|
29
|
+
|
30
|
+
$form.find('select[name$="[organization_id]"]').val('').trigger('change').prop('disabled', false)
|
31
|
+
$form.find('input[name$="[company]"]').val('').prop('disabled', false)
|
32
|
+
});
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Effective
|
2
|
+
class EventRegistrantsSelect2AjaxController < ApplicationController
|
3
|
+
before_action(:authenticate_user!) if defined?(Devise)
|
4
|
+
|
5
|
+
include Effective::Select2AjaxController
|
6
|
+
|
7
|
+
def users
|
8
|
+
authorize! :users, Effective::EventRegistrant
|
9
|
+
|
10
|
+
with_organizations = current_user.class.try(:effective_memberships_organization_user?)
|
11
|
+
|
12
|
+
collection = current_user.class.all
|
13
|
+
collection = collection.includes(:organizations) if with_organizations
|
14
|
+
|
15
|
+
respond_with_select2_ajax(collection, skip_authorize: true) do |user|
|
16
|
+
data = { first_name: user.first_name, last_name: user.last_name, email: user.email }
|
17
|
+
|
18
|
+
if with_organizations
|
19
|
+
data[:company] = user.organizations.first.try(:to_s)
|
20
|
+
data[:organization_id] = user.organizations.first.try(:id)
|
21
|
+
data[:organization_type] = user.organizations.first.try(:class).try(:name)
|
22
|
+
end
|
23
|
+
|
24
|
+
{
|
25
|
+
id: user.to_param,
|
26
|
+
text: to_select2(user, with_organizations),
|
27
|
+
data: data
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def organizations
|
33
|
+
raise('expected EffectiveEvents.organization_enabled?') unless EffectiveEvents.organization_enabled?
|
34
|
+
|
35
|
+
klass = EffectiveMemberships.Organization
|
36
|
+
raise('an EffectiveMemberships.Organization is required') unless klass.try(:effective_memberships_organization?)
|
37
|
+
|
38
|
+
collection = klass.all
|
39
|
+
|
40
|
+
# Authorize
|
41
|
+
EffectiveResources.authorize!(self, :member_organizations, collection.klass)
|
42
|
+
|
43
|
+
respond_with_select2_ajax(collection, skip_authorize: true) do |organization|
|
44
|
+
{ id: organization.to_param, text: organization.to_s }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def to_select2(resource, with_organizations)
|
51
|
+
organizations = Array(resource.try(:organizations)).join(', ') if with_organizations
|
52
|
+
|
53
|
+
[
|
54
|
+
"<span>#{resource}</span>",
|
55
|
+
"<small><#{resource.try(:public_email).presence || resource.email}></small>",
|
56
|
+
("<small>#{organizations}</small>" if organizations.present?)
|
57
|
+
].compact.join(' ')
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -7,6 +7,7 @@ module Effective
|
|
7
7
|
include Effective::WizardController
|
8
8
|
|
9
9
|
before_action :redirect_unless_registerable, only: [:new, :show]
|
10
|
+
before_action :expire_ticket_selection_window, only: [:show]
|
10
11
|
|
11
12
|
resource_scope -> {
|
12
13
|
event = Effective::Event.find(params[:event_id])
|
@@ -18,12 +19,27 @@ module Effective
|
|
18
19
|
return if resource.blank?
|
19
20
|
return if resource.was_submitted?
|
20
21
|
return if resource.event.blank?
|
21
|
-
return if resource.event.registerable?
|
22
22
|
return if resource.submit_order&.deferred?
|
23
|
+
return if resource.event.registerable? && !resource.event.sold_out?(except: resource)
|
23
24
|
|
24
25
|
flash[:danger] = "Your selected event is no longer available for registration. This event registration is no longer available."
|
25
26
|
return redirect_to('/dashboard')
|
26
27
|
end
|
27
28
|
|
29
|
+
def expire_ticket_selection_window
|
30
|
+
return if resource.blank?
|
31
|
+
return if resource.was_submitted?
|
32
|
+
return if resource.event.blank?
|
33
|
+
return if resource.selection_not_expired?
|
34
|
+
|
35
|
+
resource.ticket_selection_expired!
|
36
|
+
|
37
|
+
flash[:danger] = "Your ticket reservation window has expired. Your tickets are no longer reserved. Please start over."
|
38
|
+
|
39
|
+
return redirect_to(wizard_path(:start))
|
40
|
+
end
|
41
|
+
|
42
|
+
# TODO: Add better permitted params
|
43
|
+
|
28
44
|
end
|
29
45
|
end
|
@@ -2,6 +2,7 @@ module Admin
|
|
2
2
|
class EffectiveEventRegistrantsDatatable < Effective::Datatable
|
3
3
|
filters do
|
4
4
|
scope :unarchived, label: "All"
|
5
|
+
scope :registered
|
5
6
|
scope :purchased
|
6
7
|
scope :deferred
|
7
8
|
scope :not_purchased
|
@@ -56,7 +57,7 @@ module Admin
|
|
56
57
|
end
|
57
58
|
|
58
59
|
col :user, label: 'Member', visible: false
|
59
|
-
col :
|
60
|
+
col :organization, visible: false
|
60
61
|
|
61
62
|
col :orders, visible: false
|
62
63
|
col :price, as: :price
|
@@ -6,8 +6,11 @@ class EffectiveEventRegistrantsDatatable < Effective::Datatable
|
|
6
6
|
|
7
7
|
col :name do |er|
|
8
8
|
if er.first_name.present?
|
9
|
-
|
10
|
-
|
9
|
+
[
|
10
|
+
"#{er.first_name} #{er.last_name}",
|
11
|
+
("<small>#{er.organization || er.company}</small>" if er.organization || er.company.present?),
|
12
|
+
("<small>#{er.email}</small>" if er.email.present?)
|
13
|
+
].compact.join('<br>').html_safe
|
11
14
|
elsif er.owner.present?
|
12
15
|
er.owner.to_s + ' - GUEST'
|
13
16
|
else
|
@@ -18,14 +21,12 @@ class EffectiveEventRegistrantsDatatable < Effective::Datatable
|
|
18
21
|
col :id, visible: false
|
19
22
|
|
20
23
|
col :event_ticket, search: :string, label: 'Ticket' do |er|
|
21
|
-
[
|
22
|
-
er.event_ticket.to_s,
|
23
|
-
(content_tag(:span, 'Waitlist', class: 'badge badge-warning') if er.waitlisted_not_promoted?),
|
24
|
-
(content_tag(:span, 'Archived', class: 'badge badge-warning') if er.event_ticket&.archived?)
|
25
|
-
].compact.join('<br>').html_safe
|
24
|
+
[er.event_ticket.to_s, er.details.presence].compact.join('<br>').html_safe
|
26
25
|
end
|
27
26
|
|
28
27
|
col :user, label: 'Member', visible: false
|
28
|
+
col :organization, visible: false
|
29
|
+
|
29
30
|
col :first_name, visible: false
|
30
31
|
col :last_name, visible: false
|
31
32
|
col :email, visible: false
|
@@ -35,14 +36,13 @@ class EffectiveEventRegistrantsDatatable < Effective::Datatable
|
|
35
36
|
col :response2, visible: false
|
36
37
|
col :response3, visible: false
|
37
38
|
|
38
|
-
col :
|
39
|
+
col :responses, label: 'Details' do |registrant|
|
39
40
|
[registrant.response1.presence, registrant.response2.presence, registrant.response3.presence].compact.map do |response|
|
40
41
|
content_tag(:div, response)
|
41
42
|
end.join.html_safe
|
42
43
|
end
|
43
44
|
|
44
|
-
|
45
|
-
col :event_ticket_price, as: :price, label: 'Price'
|
45
|
+
col :price, as: :price
|
46
46
|
col :archived, visible: false
|
47
47
|
|
48
48
|
# no actions_col
|
@@ -56,7 +56,7 @@ class EffectiveEventRegistrantsDatatable < Effective::Datatable
|
|
56
56
|
end
|
57
57
|
|
58
58
|
if event_registration.present?
|
59
|
-
scope = scope.where(event_registration_id: event_registration)
|
59
|
+
scope = scope.where(event_registration_id: event_registration).sorted
|
60
60
|
end
|
61
61
|
|
62
62
|
scope
|
@@ -17,6 +17,7 @@ class EffectiveEventRegistrationsDatatable < Effective::Datatable
|
|
17
17
|
col :owner, visible: false, search: :string
|
18
18
|
col :status, visible: false
|
19
19
|
col :event_registrants, label: 'Registrants', search: :string
|
20
|
+
|
20
21
|
col :event_addons, label: 'Add-ons', search: :string
|
21
22
|
col :orders, action: :show, visible: false, search: :string
|
22
23
|
|
@@ -31,7 +32,7 @@ class EffectiveEventRegistrationsDatatable < Effective::Datatable
|
|
31
32
|
end
|
32
33
|
|
33
34
|
# Register Again
|
34
|
-
if er.event.registerable?
|
35
|
+
if er.event.registerable? && !er.event.sold_out?
|
35
36
|
url = er.event.external_registration_url.presence || effective_events.new_event_event_registration_path(er.event)
|
36
37
|
dropdown_link_to('Register Again', url)
|
37
38
|
end
|
@@ -52,6 +52,8 @@ module EffectiveEventsHelper
|
|
52
52
|
|
53
53
|
tickets.map do |ticket|
|
54
54
|
title = ticket.to_s
|
55
|
+
title = "#{title} (archived)" if ticket.archived?
|
56
|
+
|
55
57
|
price = effective_events_ticket_price(event, ticket)
|
56
58
|
|
57
59
|
label = [title, price].compact.join(' - ')
|
@@ -81,23 +83,4 @@ module EffectiveEventsHelper
|
|
81
83
|
end
|
82
84
|
end
|
83
85
|
|
84
|
-
def effective_events_event_registrant_user_collection(event_registrant)
|
85
|
-
raise("expected an Effective::EventRegistrant") unless event_registrant.kind_of?(Effective::EventRegistrant)
|
86
|
-
|
87
|
-
Array(event_registrant.event_registration&.event_ticket_member_users).map do |user|
|
88
|
-
["<span>#{user}</span> <small>#{user.email}</small>", user.to_param]
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def effective_events_event_registrant_user_hint
|
93
|
-
url = if current_user.class.try(:effective_memberships_organization_user?)
|
94
|
-
organization = current_user.membership_organizations.first || current_user.organizations.first
|
95
|
-
effective_memberships.edit_organization_path(organization, anchor: 'tab-representatives') if organization
|
96
|
-
end
|
97
|
-
|
98
|
-
return if url.blank?
|
99
|
-
|
100
|
-
"Can't find the member you need? <a href='#{url}' target='blank'>Click here</a> to add them to your #{EffectiveResources.etd(EffectiveMemberships.Organization)}."
|
101
|
-
end
|
102
|
-
|
103
86
|
end
|
@@ -8,7 +8,7 @@ module Effective
|
|
8
8
|
raise('expected an Effective::EventRegistrant') unless resource.kind_of?(Effective::EventRegistrant)
|
9
9
|
|
10
10
|
@assigns = assigns_for(resource)
|
11
|
-
mail(to: resource.
|
11
|
+
mail(to: resource.email, **headers_for(resource, opts))
|
12
12
|
end
|
13
13
|
|
14
14
|
protected
|
@@ -15,12 +15,13 @@ module EffectiveEventsEventRegistration
|
|
15
15
|
|
16
16
|
module ClassMethods
|
17
17
|
def effective_events_event_registration?; true; end
|
18
|
+
|
19
|
+
def selection_window
|
20
|
+
30.minutes
|
21
|
+
end
|
18
22
|
end
|
19
23
|
|
20
24
|
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
|
-
|
24
25
|
acts_as_purchasable_parent
|
25
26
|
acts_as_tokened
|
26
27
|
|
@@ -33,6 +34,7 @@ module EffectiveEventsEventRegistration
|
|
33
34
|
acts_as_wizard(
|
34
35
|
start: 'Start',
|
35
36
|
tickets: 'Tickets',
|
37
|
+
details: 'Ticket Details',
|
36
38
|
addons: 'Add-ons',
|
37
39
|
summary: 'Review',
|
38
40
|
billing: 'Billing Address',
|
@@ -52,10 +54,13 @@ module EffectiveEventsEventRegistration
|
|
52
54
|
# Effective Namespace
|
53
55
|
belongs_to :event, class_name: 'Effective::Event'
|
54
56
|
|
55
|
-
has_many :
|
57
|
+
has_many :event_ticket_selections, -> { order(:id) }, class_name: 'Effective::EventTicketSelection', inverse_of: :event_registration, dependent: :destroy
|
58
|
+
accepts_nested_attributes_for :event_ticket_selections, reject_if: :all_blank, allow_destroy: true
|
59
|
+
|
60
|
+
has_many :event_registrants, -> { order(:event_ticket_id, :id) }, class_name: 'Effective::EventRegistrant', inverse_of: :event_registration, dependent: :destroy
|
56
61
|
accepts_nested_attributes_for :event_registrants, reject_if: :all_blank, allow_destroy: true
|
57
62
|
|
58
|
-
has_many :event_addons, -> { order(:id) }, class_name: 'Effective::EventAddon', inverse_of: :event_registration, dependent: :destroy
|
63
|
+
has_many :event_addons, -> { order(:event_product_id, :id) }, class_name: 'Effective::EventAddon', inverse_of: :event_registration, dependent: :destroy
|
59
64
|
accepts_nested_attributes_for :event_addons, reject_if: :all_blank, allow_destroy: true
|
60
65
|
|
61
66
|
has_many :orders, -> { order(:id) }, as: :parent, class_name: 'Effective::Order', dependent: :nullify
|
@@ -83,9 +88,10 @@ module EffectiveEventsEventRegistration
|
|
83
88
|
timestamps
|
84
89
|
end
|
85
90
|
|
86
|
-
scope :deep, -> {
|
91
|
+
scope :deep, -> {
|
87
92
|
includes(:owner)
|
88
93
|
.includes(event: [:rich_texts, event_products: :purchased_event_addons, event_tickets: :purchased_event_registrants])
|
94
|
+
.includes(event_ticket_selections: [:event_ticket])
|
89
95
|
.includes(event_registrants: [event_ticket: :purchased_event_registrants])
|
90
96
|
.includes(event_addons: [event_product: :purchased_event_addons])
|
91
97
|
}
|
@@ -106,18 +112,32 @@ module EffectiveEventsEventRegistration
|
|
106
112
|
|
107
113
|
# All Steps
|
108
114
|
validate(if: -> { event.present? }) do
|
109
|
-
|
115
|
+
errors.add(:base, "cannot register for an external registration event") if event.external_registration?
|
110
116
|
end
|
111
117
|
|
112
118
|
# Tickets Step
|
113
119
|
validate(if: -> { current_step == :tickets }) do
|
114
|
-
|
120
|
+
if event_ticket_selections.all? { |selection| selection.quantity.to_i == 0 }
|
121
|
+
errors.add(:event_ticket_selections, "Please select one or more tickets")
|
122
|
+
event_ticket_selections.each { |ets| ets.errors.add(:quantity, "can't be blank") }
|
123
|
+
end
|
115
124
|
end
|
116
125
|
|
117
126
|
# Validate all tickets are available for registration
|
118
127
|
validate(if: -> { current_step == :tickets }) do
|
119
128
|
unavailable_event_tickets.each do |event_ticket|
|
120
129
|
errors.add(:base, "The requested number of #{event_ticket} tickets are not available")
|
130
|
+
event_ticket_selections.find { |ets| ets.event_ticket == event_ticket }.errors.add(:quantity, "not available")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Validate the same registrant user isn't being registered twice
|
135
|
+
validate(if: -> { current_step == :details }) do
|
136
|
+
present_event_registrants.group_by { |er| er.user }.each do |user, event_registrants|
|
137
|
+
if user.present? && event_registrants.length > 1
|
138
|
+
errors.add(:base, "Unable to register #{user} more than once")
|
139
|
+
event_registrants.each { |er| er.errors.add(:user_id, "cannot be registered more than once") }
|
140
|
+
end
|
121
141
|
end
|
122
142
|
end
|
123
143
|
|
@@ -131,19 +151,6 @@ module EffectiveEventsEventRegistration
|
|
131
151
|
# If we're submitted. Try to move to completed.
|
132
152
|
before_save(if: -> { submitted? }) { try_completed! }
|
133
153
|
|
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
|
-
|
143
|
-
def delayed_payment_date_upcoming?
|
144
|
-
event&.delayed_payment_date_upcoming?
|
145
|
-
end
|
146
|
-
|
147
154
|
def can_visit_step?(step)
|
148
155
|
return false if step == :complete && !completed?
|
149
156
|
return true if step == :complete && completed?
|
@@ -164,17 +171,24 @@ module EffectiveEventsEventRegistration
|
|
164
171
|
|
165
172
|
def required_steps
|
166
173
|
return self.class.test_required_steps if Rails.env.test? && self.class.test_required_steps.present?
|
167
|
-
event&.event_products.unarchived.present? ? wizard_step_keys : (wizard_step_keys - [:addons])
|
168
|
-
end
|
169
174
|
|
170
|
-
|
171
|
-
|
175
|
+
with_addons = event.event_products.any? { |event_product| event_product.archived? == false }
|
176
|
+
|
177
|
+
with_addons ? wizard_step_keys : (wizard_step_keys - [:addons])
|
172
178
|
end
|
173
179
|
|
174
180
|
def delayed_payment_attributes
|
175
181
|
{ delayed_payment: event&.delayed_payment, delayed_payment_date: event&.delayed_payment_date }
|
176
182
|
end
|
177
183
|
|
184
|
+
def delayed_payment_date_upcoming?
|
185
|
+
event&.delayed_payment_date_upcoming?
|
186
|
+
end
|
187
|
+
|
188
|
+
def find_or_build_submit_fees
|
189
|
+
with_outstanding_coupon_fees(submit_fees)
|
190
|
+
end
|
191
|
+
|
178
192
|
# All Fees and Orders
|
179
193
|
def submit_fees
|
180
194
|
if defined?(EffectiveMemberships)
|
@@ -217,7 +231,95 @@ module EffectiveEventsEventRegistration
|
|
217
231
|
completed?
|
218
232
|
end
|
219
233
|
|
234
|
+
def display_countdown?
|
235
|
+
return false if done?
|
236
|
+
return false unless selected_at.present?
|
237
|
+
return false unless current_step.present?
|
238
|
+
|
239
|
+
[:start, :tickets, :submitted, :complete].exclude?(current_step)
|
240
|
+
end
|
241
|
+
|
242
|
+
# When we make a ticket selection, we assign the selected_at to all tickets
|
243
|
+
# So the max or the min should be the same here.
|
244
|
+
def selected_at
|
245
|
+
event_registrants.map(&:selected_at).compact.max
|
246
|
+
end
|
247
|
+
|
248
|
+
def selected_expires_at
|
249
|
+
selected_at + EffectiveEvents.EventRegistration.selection_window
|
250
|
+
end
|
251
|
+
|
252
|
+
def selected_expired?
|
253
|
+
return false if selected_at.blank?
|
254
|
+
Time.zone.now >= selected_expires_at
|
255
|
+
end
|
256
|
+
|
257
|
+
def selection_not_expired?
|
258
|
+
return true if selected_at.blank?
|
259
|
+
Time.zone.now < selected_expires_at
|
260
|
+
end
|
261
|
+
|
262
|
+
# Called by a before_action on the event registration show action
|
263
|
+
def ticket_selection_expired!
|
264
|
+
raise("unexpected submitted registration") if was_submitted?
|
265
|
+
raise("unexpected purchased order") if submit_order&.purchased?
|
266
|
+
raise("unexpected deferred order") if submit_order&.deferred?
|
267
|
+
|
268
|
+
event_registrants.each { |er| er.assign_attributes(selected_at: nil) }
|
269
|
+
event_ticket_selections.each { |ets| ets.assign_attributes(quantity: 0) }
|
270
|
+
assign_attributes(current_step: nil, wizard_steps: {}) # Reset all steps
|
271
|
+
|
272
|
+
save!
|
273
|
+
end
|
274
|
+
|
275
|
+
# This considers the event_ticket_selection and builds the appropriate event_registrants
|
276
|
+
def update_event_registrants
|
277
|
+
event_ticket_selections.each do |event_ticket_selection|
|
278
|
+
event_ticket = event_ticket_selection.event_ticket
|
279
|
+
quantity = event_ticket_selection.quantity.to_i
|
280
|
+
|
281
|
+
# All the registrants for this event ticket
|
282
|
+
registrants = event_registrants.select { |er| er.event_ticket == event_ticket }
|
283
|
+
|
284
|
+
# Delete over quantity
|
285
|
+
if (diff = registrants.length - quantity) > 0
|
286
|
+
registrants.last(diff).each { |er| er.mark_for_destruction }
|
287
|
+
end
|
288
|
+
|
289
|
+
# Create upto quantity
|
290
|
+
if (diff = quantity - registrants.length) > 0
|
291
|
+
diff.times { build_event_registrant(event_ticket: event_ticket) }
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
event_registrants
|
296
|
+
end
|
297
|
+
|
298
|
+
# Assigns the selected at time to start the reservation window
|
299
|
+
def select_event_registrants
|
300
|
+
now = Time.zone.now
|
301
|
+
present_event_registrants.each { |er| er.assign_attributes(selected_at: now) }
|
302
|
+
end
|
303
|
+
|
304
|
+
# Looks at any unselected event registrants and assigns a waitlist value
|
305
|
+
def waitlist_event_registrants
|
306
|
+
present_event_registrants.group_by { |er| er.event_ticket }.each do |event_ticket, event_registrants|
|
307
|
+
if event_ticket.waitlist?
|
308
|
+
capacity = event.capacity_available(event_ticket: event_ticket, event_registration: self)
|
309
|
+
event_registrants.each_with_index { |er, index| er.assign_attributes(waitlisted: index >= capacity) }
|
310
|
+
else
|
311
|
+
event_registrants.each { |er| er.assign_attributes(waitlisted: false) }
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
220
316
|
def tickets!
|
317
|
+
assign_attributes(current_step: :tickets) # Ensure the unavailable tickets validations are run
|
318
|
+
|
319
|
+
update_event_registrants
|
320
|
+
select_event_registrants
|
321
|
+
waitlist_event_registrants
|
322
|
+
|
221
323
|
after_commit do
|
222
324
|
update_submit_fees_and_order! if submit_order.present?
|
223
325
|
update_deferred_event_registration! if submit_order&.deferred?
|
@@ -252,27 +354,26 @@ module EffectiveEventsEventRegistration
|
|
252
354
|
true
|
253
355
|
end
|
254
356
|
|
255
|
-
|
256
|
-
|
257
|
-
registrant = event_registrants.find { |er| er.event_ticket == event_ticket && er.first_name == first_name && er.last_name == last_name && er.email == email }
|
258
|
-
registrant || event_registrants.build(event: event, event_ticket: event_ticket, owner: owner, first_name: first_name, last_name: last_name, email: email)
|
357
|
+
def build_event_registrant(event_ticket:)
|
358
|
+
event_registrants.build(event: event, event_ticket: event_ticket, owner: owner)
|
259
359
|
end
|
260
360
|
|
361
|
+
# Find or build - # Used for testing
|
362
|
+
# def event_registrant(event_ticket:, first_name:, last_name:, email:)
|
363
|
+
# registrant = event_registrants.find { |er| er.event_ticket == event_ticket && er.first_name == first_name && er.last_name == last_name && er.email == email }
|
364
|
+
# registrant || event_registrants.build(event: event, event_ticket: event_ticket, owner: owner, first_name: first_name, last_name: last_name, email: email)
|
365
|
+
# end
|
366
|
+
|
261
367
|
# Find or build. But it's not gonna work with more than 1. This is for testing only really.
|
262
368
|
def event_addon(event_product:, first_name:, last_name:, email:)
|
263
369
|
addon = event_addons.find { |er| er.event_product == event_product && er.first_name == first_name && er.last_name == last_name && er.email == email }
|
264
370
|
addon || event_addons.build(event: event, event_product: event_product, owner: owner, first_name: first_name, last_name: last_name, email: email)
|
265
371
|
end
|
266
372
|
|
267
|
-
#
|
268
|
-
def
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
event_registrants.build()
|
273
|
-
end
|
274
|
-
|
275
|
-
event_registrants
|
373
|
+
# Find or build
|
374
|
+
def event_ticket_selection(event_ticket:, quantity: 0)
|
375
|
+
selection = event_ticket_selections.find { |ets| ets.event_ticket == event_ticket }
|
376
|
+
selection || event_ticket_selections.build(event_ticket: event_ticket, quantity: quantity)
|
276
377
|
end
|
277
378
|
|
278
379
|
# This builds the default event addons used by the wizard form
|
@@ -298,9 +399,7 @@ module EffectiveEventsEventRegistration
|
|
298
399
|
unavailable = []
|
299
400
|
|
300
401
|
present_event_registrants.map(&:event_ticket).group_by { |t| t }.each do |event_ticket, event_tickets|
|
301
|
-
|
302
|
-
unavailable << event_ticket
|
303
|
-
end
|
402
|
+
unavailable << event_ticket unless event.event_ticket_available?(event_ticket, except: self, quantity: event_tickets.length)
|
304
403
|
end
|
305
404
|
|
306
405
|
unavailable
|
@@ -316,13 +415,6 @@ module EffectiveEventsEventRegistration
|
|
316
415
|
unavailable
|
317
416
|
end
|
318
417
|
|
319
|
-
def event_ticket_member_users
|
320
|
-
raise("expected owner to be a user") if owner.class.try(:effective_memberships_organization?)
|
321
|
-
users = [owner] + (owner.try(:organizations).try(:flat_map, &:users) || [])
|
322
|
-
|
323
|
-
users.select { |user| user.is_any?(:member) }.uniq
|
324
|
-
end
|
325
|
-
|
326
418
|
def update_blank_registrants!
|
327
419
|
# This method is called by the user on a submitted or completed event registration.
|
328
420
|
# Allow them to update blank registrants
|
@@ -343,6 +435,7 @@ module EffectiveEventsEventRegistration
|
|
343
435
|
raise('unable to make changes to event addons while updating blank registrants')
|
344
436
|
end
|
345
437
|
|
438
|
+
assign_attributes(current_step: :details) if current_step.blank? # Enables validations
|
346
439
|
save!
|
347
440
|
|
348
441
|
update_submit_fees_and_order! if submit_order.present? && !submit_order.purchased?
|
@@ -358,9 +451,6 @@ module EffectiveEventsEventRegistration
|
|
358
451
|
# Mark registered anyone who hasn't been registered yet. They are now!
|
359
452
|
event_registrants.reject(&:registered?).each { |event_registrant| event_registrant.registered! }
|
360
453
|
|
361
|
-
# Update the waitlist for any event tickets
|
362
|
-
event_tickets.select(&:waitlist?).each { |event_ticket| event_ticket.update_waitlist! }
|
363
|
-
|
364
454
|
true
|
365
455
|
end
|
366
456
|
|
@@ -15,7 +15,7 @@ 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
|
18
|
+
has_many :event_registrants, -> { order(:event_ticket_id, :id) }, inverse_of: :event
|
19
19
|
accepts_nested_attributes_for :event_registrants, allow_destroy: true
|
20
20
|
|
21
21
|
has_many :event_addons, -> { order(:event_product_id).order(:id) }, inverse_of: :event
|
@@ -190,11 +190,10 @@ module Effective
|
|
190
190
|
event_tickets.any? { |et| et.waitlist? }
|
191
191
|
end
|
192
192
|
|
193
|
+
# No longer includes sold_out? we check that separately
|
193
194
|
def registerable?
|
194
195
|
return false unless published?
|
195
196
|
return false if closed?
|
196
|
-
return false if sold_out?
|
197
|
-
|
198
197
|
(external_registration? && external_registration_url.present?) || event_tickets.present?
|
199
198
|
end
|
200
199
|
|
@@ -203,10 +202,13 @@ module Effective
|
|
203
202
|
registration_end_at < Time.zone.now
|
204
203
|
end
|
205
204
|
|
206
|
-
def sold_out?
|
205
|
+
def sold_out?(except: nil)
|
206
|
+
raise('expected except to be an EventRegistration') if except && !except.class.try(:effective_events_event_registration?)
|
207
|
+
|
207
208
|
return false unless event_tickets.present?
|
208
209
|
return false if any_waitlist?
|
209
|
-
|
210
|
+
|
211
|
+
event_tickets.none? { |event_ticket| event_ticket_available?(event_ticket, except: except, quantity: 1) }
|
210
212
|
end
|
211
213
|
|
212
214
|
def early_bird?
|
@@ -250,19 +252,37 @@ module Effective
|
|
250
252
|
start_at
|
251
253
|
end
|
252
254
|
|
255
|
+
# The amount of tickets that can be purchased except ones from an event registration
|
256
|
+
def capacity_selectable(event_ticket:, event_registration: nil)
|
257
|
+
return 0 if event_ticket.archived?
|
258
|
+
return 100 if event_ticket.capacity.blank?
|
259
|
+
return 100 if event_ticket.waitlist?
|
260
|
+
|
261
|
+
event_ticket.capacity_selectable(except: event_registration)
|
262
|
+
end
|
263
|
+
|
264
|
+
# The amount of tickets that can be purchased except ones from an event registration
|
265
|
+
def capacity_available(event_ticket:, event_registration: nil)
|
266
|
+
event_ticket.capacity_available(except: event_registration)
|
267
|
+
end
|
268
|
+
|
269
|
+
# Just used in tests so far
|
270
|
+
def capacity_taken(event_ticket:, event_registration: nil)
|
271
|
+
event_ticket.capacity_taken(except: event_registration)
|
272
|
+
end
|
273
|
+
|
253
274
|
# Can I register/purchase this many new event tickets?
|
254
|
-
def event_ticket_available?(event_ticket, quantity:)
|
275
|
+
def event_ticket_available?(event_ticket, except: nil, quantity: 0)
|
255
276
|
raise('expected an EventTicket') unless event_ticket.kind_of?(Effective::EventTicket)
|
277
|
+
raise('expected except to be an EventRegistration') if except && !except.class.try(:effective_events_event_registration?)
|
256
278
|
raise('expected quantity to be greater than 0') unless quantity.to_i > 0
|
257
279
|
|
258
280
|
return false if event_ticket.archived?
|
259
|
-
return true if event_ticket.capacity.blank?
|
281
|
+
return true if event_ticket.capacity.blank? # No capacity enforced
|
282
|
+
return true if event_ticket.waitlist? # Always available for waitlist
|
260
283
|
|
261
|
-
#
|
262
|
-
|
263
|
-
|
264
|
-
# If there's capacity for this many more
|
265
|
-
(registered + quantity) <= event_ticket.capacity
|
284
|
+
# Do we have any tickets available left?
|
285
|
+
event_ticket.capacity_available(except: except) >= quantity.to_i
|
266
286
|
end
|
267
287
|
|
268
288
|
# Can I register/purchase this many new event products?
|