effective_events 0.11.3 → 0.12.0
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 +4 -4
- data/app/datatables/admin/effective_event_registrants_datatable.rb +6 -3
- data/app/datatables/admin/effective_event_tickets_datatable.rb +11 -2
- data/app/datatables/admin/effective_events_datatable.rb +2 -0
- data/app/datatables/effective_event_registrants_datatable.rb +17 -3
- data/app/helpers/effective_events_helper.rb +34 -1
- data/app/models/concerns/effective_events_event_registration.rb +52 -10
- data/app/models/effective/event.rb +9 -7
- data/app/models/effective/event_registrant.rb +84 -18
- data/app/models/effective/event_ticket.rb +43 -13
- data/app/views/admin/event_tickets/_form.html.haml +44 -8
- data/app/views/admin/events/_form_event.html.haml +19 -13
- data/app/views/effective/event_registrants/_fields.html.haml +23 -8
- data/app/views/effective/event_registrants/_fields_member_only.html.haml +6 -0
- data/app/views/effective/event_registrants/_fields_member_or_non_member.html.haml +20 -0
- data/app/views/effective/event_registrants/_fields_questions.html.haml +11 -0
- data/app/views/effective/event_registrants/_fields_regular.html.haml +8 -0
- data/app/views/effective/event_registrations/_orders.html.haml +1 -1
- data/app/views/effective/event_registrations/checkout.html.haml +2 -2
- data/app/views/effective/event_registrations/complete.html.haml +18 -0
- data/app/views/effective/event_registrations/submitted.html.haml +2 -5
- data/app/views/effective/events/show.html.haml +1 -1
- data/db/migrate/101_create_effective_events.rb +19 -0
- data/db/seeds.rb +3 -0
- data/lib/effective_events/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5ea3b86b0a1ca6c11ee6a2a9ee5fba8dec34473c435299bed967cf129912b0dc
|
4
|
+
data.tar.gz: 1164dce2a11f7a65e95c113d2ef9f3f0c254da666c206dbba67b7729e441953f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f9c4899dfc735f56d2b34889f8905b4315dcbf59ab9a9967154ac1f8eb9a0d210982cf21d551a7d0076eeac4ec4ca6ddbaf5d1ba384257e542a5b6d6d32d22e3
|
7
|
+
data.tar.gz: ec2ed614931b908976b4abb959e9f69d4c16de58b8cafc1cc22223d1a2061fb847500e254a391dba162b5a6065b1faf00065a1be569789c8a344be6bbcaf9c29
|
@@ -26,12 +26,15 @@ module Admin
|
|
26
26
|
|
27
27
|
col :purchased_order, visible: false
|
28
28
|
|
29
|
+
col :user
|
29
30
|
col :first_name
|
30
31
|
col :last_name
|
31
32
|
col :email
|
32
|
-
col :company
|
33
|
-
|
34
|
-
col :
|
33
|
+
col :company, visible: false
|
34
|
+
|
35
|
+
col :response1
|
36
|
+
col :response2
|
37
|
+
col :response3
|
35
38
|
|
36
39
|
actions_col
|
37
40
|
end
|
@@ -16,8 +16,11 @@ module Admin
|
|
16
16
|
col :event
|
17
17
|
|
18
18
|
col :title
|
19
|
+
col :category, visible: false
|
20
|
+
|
21
|
+
col :early_bird_price, as: :price, visible: event.early_bird_end_at.present?
|
19
22
|
col :regular_price, as: :price
|
20
|
-
col :
|
23
|
+
col :member_price, as: :price
|
21
24
|
|
22
25
|
col :capacity_to_s, label: 'Capacity' do |ticket|
|
23
26
|
if ticket.capacity.present?
|
@@ -25,6 +28,8 @@ module Admin
|
|
25
28
|
end
|
26
29
|
end
|
27
30
|
|
31
|
+
col :category, visible: false
|
32
|
+
|
28
33
|
col :registered_event_registrants_count, label: 'Registered' do |event|
|
29
34
|
event.event_registrants.registered.unarchived.count
|
30
35
|
end
|
@@ -33,8 +38,12 @@ module Admin
|
|
33
38
|
event.event_registrants.purchased.unarchived.count
|
34
39
|
end
|
35
40
|
|
36
|
-
col :capacity, visible: false
|
37
41
|
col :capacity_available, visible: false
|
42
|
+
col :capacity, label: 'Capacity Total', visible: false
|
43
|
+
|
44
|
+
col :question1, visible: false
|
45
|
+
col :question2, visible: false
|
46
|
+
col :question3, visible: false
|
38
47
|
|
39
48
|
actions_col
|
40
49
|
end
|
@@ -4,7 +4,13 @@ class EffectiveEventRegistrantsDatatable < Effective::Datatable
|
|
4
4
|
datatable do
|
5
5
|
|
6
6
|
col :name do |er|
|
7
|
-
|
7
|
+
if er.first_name.present?
|
8
|
+
"#{er.first_name} #{er.last_name}<br><small>#{mail_to(er.email)}</small>"
|
9
|
+
elsif er.owner.present?
|
10
|
+
er.owner.to_s + ' - GUEST'
|
11
|
+
else
|
12
|
+
'Unknown'
|
13
|
+
end
|
8
14
|
end
|
9
15
|
|
10
16
|
col :event_ticket, search: :string, label: 'Ticket' do |er|
|
@@ -14,13 +20,21 @@ class EffectiveEventRegistrantsDatatable < Effective::Datatable
|
|
14
20
|
].compact.join('<br>').html_safe
|
15
21
|
end
|
16
22
|
|
23
|
+
col :user, visible: false
|
17
24
|
col :first_name, visible: false
|
18
25
|
col :last_name, visible: false
|
19
26
|
col :email, visible: false
|
20
27
|
col :company, visible: false
|
21
|
-
col :number, visible: false, label: 'Designations'
|
22
28
|
|
23
|
-
col :
|
29
|
+
col :response1, visible: false
|
30
|
+
col :response2, visible: false
|
31
|
+
col :response3, visible: false
|
32
|
+
|
33
|
+
col :details do |registrant|
|
34
|
+
[registrant.response1.presence, registrant.response2.presence, registrant.response3.presence].compact.map do |response|
|
35
|
+
content_tag(:div, response)
|
36
|
+
end.join.html_safe
|
37
|
+
end
|
24
38
|
|
25
39
|
col :price, as: :price
|
26
40
|
|
@@ -7,16 +7,37 @@ module EffectiveEventsHelper
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
+
def effective_events_ticket_price(event, ticket)
|
11
|
+
raise('expected an Effective::Event') unless event.kind_of?(Effective::Event)
|
12
|
+
raise('expected an Effective::EventTicket') unless ticket.kind_of?(Effective::EventTicket)
|
13
|
+
|
14
|
+
prices = [
|
15
|
+
(ticket.early_bird_price if event.early_bird?),
|
16
|
+
(ticket.regular_price if ticket.regular? || ticket.member_or_non_member?),
|
17
|
+
(ticket.member_price if ticket.member_only? || ticket.member_or_non_member?)
|
18
|
+
].compact.sort.uniq
|
19
|
+
|
20
|
+
if prices.length > 1
|
21
|
+
"#{(prices.first == 0 ? '$0' : price_to_currency(prices.first))} to #{(prices.last == 0 ? '$0' : price_to_currency(prices.last))}"
|
22
|
+
else
|
23
|
+
(prices.first == 0 ? '$0' : price_to_currency(prices.first))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
10
27
|
def effective_events_event_tickets_collection(event, namespace = nil)
|
11
28
|
raise('expected an Effective::Event') unless event.kind_of?(Effective::Event)
|
12
29
|
|
13
30
|
# Allow an admin to assign archived tickets
|
14
31
|
authorized = (namespace == :admin)
|
32
|
+
member = current_user.try(:is_any?, :member)
|
33
|
+
|
15
34
|
tickets = (authorized ? event.event_tickets : event.event_tickets.reject(&:archived?))
|
35
|
+
tickets = tickets.reject { |ticket| ticket.member_only? } unless (authorized || member)
|
16
36
|
|
17
37
|
tickets.map do |ticket|
|
18
38
|
title = ticket.to_s
|
19
|
-
price = (
|
39
|
+
price = effective_events_ticket_price(event, ticket)
|
40
|
+
|
20
41
|
remaining = (ticket.capacity.present? ? "#{ticket.capacity_available} remaining" : nil)
|
21
42
|
|
22
43
|
label = [title, price, remaining].compact.join(' - ')
|
@@ -44,4 +65,16 @@ module EffectiveEventsHelper
|
|
44
65
|
[label, product.to_param, disabled].compact
|
45
66
|
end
|
46
67
|
end
|
68
|
+
|
69
|
+
def effective_events_event_tickets_user_hint
|
70
|
+
url = if current_user.class.try(:effective_memberships_organization_user?)
|
71
|
+
organization = current_user.membership_organizations.first || current_user.organizations.first
|
72
|
+
effective_memberships.edit_organization_path(organization, anchor: 'tab-representatives') if organization
|
73
|
+
end
|
74
|
+
|
75
|
+
return if url.blank?
|
76
|
+
|
77
|
+
"Can't find the person you need? <a href='#{url}' target='blank'>Click here</a> to add them to your organization."
|
78
|
+
end
|
79
|
+
|
47
80
|
end
|
@@ -23,7 +23,8 @@ module EffectiveEventsEventRegistration
|
|
23
23
|
|
24
24
|
acts_as_statused(
|
25
25
|
:draft, # Just Started
|
26
|
-
:submitted
|
26
|
+
:submitted, # Order has been submitted with a deferred payment processor.
|
27
|
+
:completed # All done. Order purchased.
|
27
28
|
)
|
28
29
|
|
29
30
|
acts_as_wizard(
|
@@ -33,7 +34,8 @@ module EffectiveEventsEventRegistration
|
|
33
34
|
summary: 'Review',
|
34
35
|
billing: 'Billing Address',
|
35
36
|
checkout: 'Checkout',
|
36
|
-
submitted: 'Submitted'
|
37
|
+
submitted: 'Submitted',
|
38
|
+
complete: 'Completed'
|
37
39
|
)
|
38
40
|
|
39
41
|
acts_as_purchasable_wizard
|
@@ -70,6 +72,7 @@ module EffectiveEventsEventRegistration
|
|
70
72
|
|
71
73
|
# Dates
|
72
74
|
submitted_at :datetime
|
75
|
+
completed_at :datetime
|
73
76
|
|
74
77
|
# Acts as Wizard
|
75
78
|
wizard_steps :text, permitted: false
|
@@ -85,9 +88,9 @@ module EffectiveEventsEventRegistration
|
|
85
88
|
}
|
86
89
|
|
87
90
|
scope :sorted, -> { order(:id) }
|
88
|
-
|
89
|
-
scope :in_progress, -> { where
|
90
|
-
scope :done, -> { where(status:
|
91
|
+
|
92
|
+
scope :in_progress, -> { where(status: [:draft, :submitted]) }
|
93
|
+
scope :done, -> { where(status: :completed) }
|
91
94
|
|
92
95
|
scope :for, -> (user) { where(owner: user) }
|
93
96
|
|
@@ -119,6 +122,16 @@ module EffectiveEventsEventRegistration
|
|
119
122
|
end
|
120
123
|
end
|
121
124
|
|
125
|
+
# If we're submitted. Try to move to completed.
|
126
|
+
before_save(if: -> { submitted? }) { try_completed! }
|
127
|
+
|
128
|
+
def can_visit_step?(step)
|
129
|
+
return false if step == :complete && !completed?
|
130
|
+
return true if step == :complete && completed?
|
131
|
+
|
132
|
+
can_revisit_completed_steps(step)
|
133
|
+
end
|
134
|
+
|
122
135
|
def required_steps
|
123
136
|
return self.class.test_required_steps if Rails.env.test? && self.class.test_required_steps.present?
|
124
137
|
event&.event_products.present? ? wizard_step_keys : (wizard_step_keys - [:addons])
|
@@ -137,6 +150,14 @@ module EffectiveEventsEventRegistration
|
|
137
150
|
end
|
138
151
|
end
|
139
152
|
|
153
|
+
# When the submit_order is deferred or purchased, we call submit!
|
154
|
+
# When the order is a deferred payment processor, we continue to the :submitted step
|
155
|
+
# When the order is a regular processor, the before_save will call complete! and we continue to the :complete step
|
156
|
+
# Purchasing the order later on will automatically call
|
157
|
+
def submit_wizard_on_deferred_order?
|
158
|
+
true
|
159
|
+
end
|
160
|
+
|
140
161
|
def after_submit_purchased!
|
141
162
|
notifications = event.event_notifications.select(&:registrant_purchased?)
|
142
163
|
notifications.each { |notification| notification.notify!(event_registrants: event_registrants) }
|
@@ -149,11 +170,29 @@ module EffectiveEventsEventRegistration
|
|
149
170
|
end
|
150
171
|
|
151
172
|
def in_progress?
|
152
|
-
draft?
|
173
|
+
draft? || submitted?
|
153
174
|
end
|
154
175
|
|
155
176
|
def done?
|
156
|
-
|
177
|
+
completed?
|
178
|
+
end
|
179
|
+
|
180
|
+
def try_completed!
|
181
|
+
return false unless submitted?
|
182
|
+
return false unless submit_order&.purchased?
|
183
|
+
complete!
|
184
|
+
end
|
185
|
+
|
186
|
+
def complete!
|
187
|
+
raise('event registration must be submitted to complete!') unless submitted?
|
188
|
+
raise('expected a purchased order') unless submit_order&.purchased?
|
189
|
+
|
190
|
+
wizard_steps[:checkout] ||= Time.zone.now
|
191
|
+
wizard_steps[:submitted] ||= Time.zone.now
|
192
|
+
wizard_steps[:complete] = Time.zone.now
|
193
|
+
|
194
|
+
completed!
|
195
|
+
true
|
157
196
|
end
|
158
197
|
|
159
198
|
# Find or build
|
@@ -178,7 +217,6 @@ module EffectiveEventsEventRegistration
|
|
178
217
|
last_name: owner.try(:last_name),
|
179
218
|
email: owner.try(:email),
|
180
219
|
company: owner.try(:company),
|
181
|
-
number: owner.try(:membership).try(:number) || owner.try(:number)
|
182
220
|
)
|
183
221
|
end
|
184
222
|
|
@@ -203,7 +241,9 @@ module EffectiveEventsEventRegistration
|
|
203
241
|
def unavailable_event_tickets
|
204
242
|
unavailable = []
|
205
243
|
|
206
|
-
present_event_registrants.
|
244
|
+
changed_event_registrants = present_event_registrants.select { |er| er.new_record? || er.event_ticket_id_changed? }
|
245
|
+
|
246
|
+
changed_event_registrants.map(&:event_ticket).group_by { |t| t }.each do |event_ticket, event_tickets|
|
207
247
|
unavailable << event_ticket unless event.event_ticket_available?(event_ticket, quantity: event_tickets.length)
|
208
248
|
end
|
209
249
|
|
@@ -213,7 +253,9 @@ module EffectiveEventsEventRegistration
|
|
213
253
|
def unavailable_event_products
|
214
254
|
unavailable = []
|
215
255
|
|
216
|
-
present_event_addons.
|
256
|
+
changed_event_products = present_event_addons.select { |er| er.new_record? || er.event_product_id_changed? }
|
257
|
+
|
258
|
+
changed_event_products.map(&:event_product).group_by { |p| p }.each do |event_product, event_products|
|
217
259
|
unavailable << event_product unless event.event_product_available?(event_product, quantity: event_products.length)
|
218
260
|
end
|
219
261
|
|
@@ -66,6 +66,8 @@ module Effective
|
|
66
66
|
external_registration :boolean
|
67
67
|
external_registration_url :string
|
68
68
|
|
69
|
+
allow_blank_registrants :boolean
|
70
|
+
|
69
71
|
# Access
|
70
72
|
roles_mask :integer
|
71
73
|
authenticate_user :boolean
|
@@ -82,7 +84,7 @@ module Effective
|
|
82
84
|
}
|
83
85
|
|
84
86
|
scope :published, -> { where(draft: false).where(arel_table[:published_at].lt(Time.zone.now)) }
|
85
|
-
scope :unpublished, -> { where(draft: true).where(arel_table[:published_at].gteq(Time.zone.now)) }
|
87
|
+
scope :unpublished, -> { where(draft: true).or(where(arel_table[:published_at].gteq(Time.zone.now))) }
|
86
88
|
|
87
89
|
scope :upcoming, -> { where(arel_table[:end_at].gt(Time.zone.now)) }
|
88
90
|
scope :past, -> { where(arel_table[:end_at].lteq(Time.zone.now)) }
|
@@ -134,27 +136,27 @@ module Effective
|
|
134
136
|
validates :registration_end_at, presence: true, unless: -> { external_registration? }
|
135
137
|
|
136
138
|
validate(if: -> { start_at && end_at }) do
|
137
|
-
|
139
|
+
errors.add(:end_at, 'must be after start date') unless start_at < end_at
|
138
140
|
end
|
139
141
|
|
140
142
|
validate(if: -> { start_at && registration_start_at }) do
|
141
|
-
|
143
|
+
errors.add(:registration_start_at, 'must be before start date') unless registration_start_at < start_at
|
142
144
|
end
|
143
145
|
|
144
146
|
validate(if: -> { registration_start_at && registration_end_at }) do
|
145
|
-
|
147
|
+
errors.add(:registration_end_at, 'must be after start registration date') unless registration_start_at < registration_end_at
|
146
148
|
end
|
147
149
|
|
148
150
|
validate(if: -> { start_at && early_bird_end_at }) do
|
149
|
-
|
151
|
+
errors.add(:early_bird_end_at, 'must be before start date') unless early_bird_end_at < start_at
|
150
152
|
end
|
151
153
|
|
152
154
|
validate(if: -> { file.attached? }) do
|
153
|
-
|
155
|
+
errors.add(:file, 'must be an image') unless file.image?
|
154
156
|
end
|
155
157
|
|
156
158
|
validate(if: -> { category.present? }) do
|
157
|
-
|
159
|
+
errors.add(:category, 'is not included in the list') unless EffectiveEvents.categories.include?(category)
|
158
160
|
end
|
159
161
|
|
160
162
|
def to_s
|
@@ -20,21 +20,33 @@ module Effective
|
|
20
20
|
# This fee when checked out through the event registration
|
21
21
|
belongs_to :event_registration, polymorphic: true, optional: true
|
22
22
|
|
23
|
+
# Required for member-only tickets. The user attending.
|
24
|
+
belongs_to :user, polymorphic: true, optional: true
|
25
|
+
|
23
26
|
effective_resource do
|
24
|
-
first_name
|
25
|
-
last_name
|
26
|
-
email
|
27
|
+
first_name :string
|
28
|
+
last_name :string
|
29
|
+
email :string
|
30
|
+
company :string
|
31
|
+
|
32
|
+
blank_registrant :boolean
|
33
|
+
member_registrant :boolean
|
27
34
|
|
28
|
-
|
29
|
-
|
30
|
-
|
35
|
+
# Question Responses
|
36
|
+
question1 :text
|
37
|
+
question2 :text
|
38
|
+
question3 :text
|
31
39
|
|
32
|
-
archived
|
40
|
+
archived :boolean
|
33
41
|
|
34
42
|
# Acts as Purchasable
|
35
|
-
price
|
36
|
-
qb_item_name
|
37
|
-
tax_exempt
|
43
|
+
price :integer
|
44
|
+
qb_item_name :string
|
45
|
+
tax_exempt :boolean
|
46
|
+
|
47
|
+
# Historical. Not used anymore.
|
48
|
+
number :string
|
49
|
+
notes :text
|
38
50
|
|
39
51
|
timestamps
|
40
52
|
end
|
@@ -43,17 +55,44 @@ module Effective
|
|
43
55
|
scope :deep, -> { includes(:event, :event_ticket) }
|
44
56
|
scope :registered, -> { purchased_or_deferred.unarchived }
|
45
57
|
|
46
|
-
validates :first_name, presence: true
|
47
|
-
validates :last_name, presence: true
|
48
|
-
validates :email, presence: true, email: true
|
49
|
-
|
50
58
|
before_validation(if: -> { event_registration.present? }) do
|
51
59
|
self.event ||= event_registration.event
|
52
60
|
self.owner ||= event_registration.owner
|
53
61
|
end
|
54
62
|
|
55
|
-
before_validation(if: -> {
|
56
|
-
assign_attributes(
|
63
|
+
before_validation(if: -> { blank_registrant? }) do
|
64
|
+
assign_attributes(user: nil, first_name: nil, last_name: nil, email: nil)
|
65
|
+
end
|
66
|
+
|
67
|
+
before_validation(if: -> { user.present? }) do
|
68
|
+
assign_attributes(first_name: user.first_name, last_name: user.last_name, email: user.email)
|
69
|
+
end
|
70
|
+
|
71
|
+
before_validation(if: -> { event_ticket.present? }, unless: -> { purchased? }) do
|
72
|
+
assign_price()
|
73
|
+
end
|
74
|
+
|
75
|
+
validates :user_id, uniqueness: { scope: [:event_id], allow_blank: true, message: 'is already registered for this event' }
|
76
|
+
validates :price, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
77
|
+
validates :email, email: true
|
78
|
+
|
79
|
+
# Member Only Ticket
|
80
|
+
with_options(if: -> { event_ticket&.member_only? }, unless: -> { blank_registrant? }) do
|
81
|
+
validates :user_id, presence: { message: 'Please select a member' }
|
82
|
+
end
|
83
|
+
|
84
|
+
# Regular Ticket
|
85
|
+
with_options(if: -> { event_ticket&.regular? }, unless: -> { blank_registrant? }) do
|
86
|
+
validates :first_name, presence: true
|
87
|
+
validates :last_name, presence: true
|
88
|
+
validates :email, presence: true
|
89
|
+
end
|
90
|
+
|
91
|
+
with_options(if: -> { event_ticket&.member_or_non_member? && !blank_registrant? }) do
|
92
|
+
validates :user_id, presence: { message: 'Please select a member' }, unless: -> { first_name.present? || last_name.present? || email.present? }
|
93
|
+
validates :first_name, presence: true, unless: -> { user.present? }
|
94
|
+
validates :last_name, presence: true, unless: -> { user.present? }
|
95
|
+
validates :email, presence: true, unless: -> { user.present? }
|
57
96
|
end
|
58
97
|
|
59
98
|
def to_s
|
@@ -65,11 +104,15 @@ module Effective
|
|
65
104
|
end
|
66
105
|
|
67
106
|
def name
|
68
|
-
"#{first_name} #{last_name}"
|
107
|
+
first_name.present? ? "#{first_name} #{last_name}" : "GUEST"
|
69
108
|
end
|
70
109
|
|
71
110
|
def last_first_name
|
72
|
-
"#{last_name}, #{first_name}"
|
111
|
+
first_name.present? ? "#{last_name}, #{first_name}" : "GUEST"
|
112
|
+
end
|
113
|
+
|
114
|
+
def member_present?
|
115
|
+
user&.is?(:member) || (blank_registrant? && member_registrant?)
|
73
116
|
end
|
74
117
|
|
75
118
|
def tax_exempt
|
@@ -92,5 +135,28 @@ module Effective
|
|
92
135
|
true
|
93
136
|
end
|
94
137
|
|
138
|
+
private
|
139
|
+
|
140
|
+
def assign_price
|
141
|
+
raise('is already purchased') if purchased?
|
142
|
+
|
143
|
+
raise('expected an event') if event.blank?
|
144
|
+
raise('expected an event ticket') if event_ticket.blank?
|
145
|
+
|
146
|
+
price = if event.early_bird?
|
147
|
+
event_ticket.early_bird_price # Early Bird Pricing
|
148
|
+
elsif event_ticket.regular?
|
149
|
+
event_ticket.regular_price
|
150
|
+
elsif event_ticket.member_only?
|
151
|
+
event_ticket.member_price
|
152
|
+
elsif event_ticket.member_or_non_member?
|
153
|
+
(member_present? ? event_ticket.member_price : event_ticket.regular_price)
|
154
|
+
else
|
155
|
+
raise("Unexpected event ticket price calculation")
|
156
|
+
end
|
157
|
+
|
158
|
+
assign_attributes(price: price)
|
159
|
+
end
|
160
|
+
|
95
161
|
end
|
96
162
|
end
|
@@ -16,19 +16,29 @@ module Effective
|
|
16
16
|
|
17
17
|
has_rich_text :body
|
18
18
|
|
19
|
+
CATEGORIES = ['Regular', 'Member Only', 'Member or Non-Member']
|
20
|
+
|
19
21
|
effective_resource do
|
20
|
-
title
|
21
|
-
capacity
|
22
|
+
title :string
|
23
|
+
capacity :integer
|
24
|
+
|
25
|
+
category :string
|
26
|
+
|
27
|
+
# Questions
|
28
|
+
question1 :text
|
29
|
+
question2 :text
|
30
|
+
question3 :text
|
22
31
|
|
23
32
|
# Pricing
|
24
|
-
|
25
|
-
|
33
|
+
early_bird_price :integer
|
34
|
+
member_price :integer
|
35
|
+
regular_price :integer
|
26
36
|
|
27
|
-
qb_item_name
|
28
|
-
tax_exempt
|
37
|
+
qb_item_name :string
|
38
|
+
tax_exempt :boolean
|
29
39
|
|
30
|
-
position
|
31
|
-
archived
|
40
|
+
position :integer
|
41
|
+
archived :boolean
|
32
42
|
|
33
43
|
timestamps
|
34
44
|
end
|
@@ -37,21 +47,26 @@ module Effective
|
|
37
47
|
scope :deep, -> { with_rich_text_body.includes(:event, :purchased_event_registrants) }
|
38
48
|
|
39
49
|
before_validation(if: -> { event.present? }) do
|
50
|
+
self.category ||= CATEGORIES.first
|
40
51
|
self.position ||= (event.event_tickets.map(&:position).compact.max || -1) + 1
|
41
52
|
end
|
42
53
|
|
43
54
|
validates :title, presence: true, uniqueness: { scope: [:event_id] }
|
44
|
-
validates :
|
55
|
+
validates :category, presence: true
|
56
|
+
|
57
|
+
validates :regular_price, presence: true, if: -> { member_or_non_member? || regular? }
|
58
|
+
validates :regular_price, numericality: { greater_than_or_equal_to: 0, allow_blank: true }
|
59
|
+
|
60
|
+
validates :member_price, presence: true, if: -> { member_or_non_member? || member_only? }
|
61
|
+
validates :member_price, numericality: { greater_than_or_equal_to: 0, allow_blank: true }
|
62
|
+
|
45
63
|
validates :early_bird_price, presence: true, if: -> { event&.early_bird_end_at.present? }
|
64
|
+
validates :early_bird_price, numericality: { greater_than_or_equal_to: 0, allow_blank: true }
|
46
65
|
|
47
66
|
def to_s
|
48
67
|
title.presence || 'New Event Ticket'
|
49
68
|
end
|
50
69
|
|
51
|
-
def price
|
52
|
-
event.early_bird? ? early_bird_price : regular_price
|
53
|
-
end
|
54
|
-
|
55
70
|
def capacity_available
|
56
71
|
return nil if capacity.blank?
|
57
72
|
[(capacity - registered_event_registrants_count), 0].max
|
@@ -65,5 +80,20 @@ module Effective
|
|
65
80
|
purchased_event_registrants.length
|
66
81
|
end
|
67
82
|
|
83
|
+
def questions
|
84
|
+
[question1.presence, question2.presence, question3.presence].compact
|
85
|
+
end
|
86
|
+
|
87
|
+
def regular?
|
88
|
+
category == 'Regular'
|
89
|
+
end
|
90
|
+
|
91
|
+
def member_only?
|
92
|
+
category == 'Member Only'
|
93
|
+
end
|
94
|
+
|
95
|
+
def member_or_non_member?
|
96
|
+
category == 'Member or Non-Member'
|
97
|
+
end
|
68
98
|
end
|
69
99
|
end
|
@@ -1,16 +1,52 @@
|
|
1
1
|
= effective_form_with(model: [:admin, event_ticket], engine: true) do |f|
|
2
2
|
= f.hidden_field :event_id
|
3
3
|
|
4
|
-
=
|
5
|
-
|
4
|
+
= card(et(Effective::EventTicket)) do
|
5
|
+
= f.text_field :title
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
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 all 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
10
|
|
11
|
-
|
12
|
-
|
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 all members. Only the regular price applies.
|
13
15
|
|
14
|
-
|
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 all 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 all 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
|
+
.row
|
28
|
+
.col-md-6
|
29
|
+
- if f.object.event&.early_bird_end_at.present?
|
30
|
+
= f.price_field :early_bird_price, hint: 'A price of $0 will allow a checkout for free. Leave blank for no early bird price.'
|
31
|
+
|
32
|
+
= f.show_if_any(:category, ['Regular', 'Member or Non-Member']) do
|
33
|
+
= f.price_field :regular_price, hint: 'A price of $0 will allow a checkout for free.'
|
34
|
+
|
35
|
+
= f.show_if_any(:category, ['Member Only', 'Member or Non-Member']) do
|
36
|
+
= f.price_field :member_price, hint: 'A price of $0 will allow a checkout for free when selecting a member.'
|
37
|
+
|
38
|
+
.row
|
39
|
+
- if defined?(EffectiveQbSync)
|
40
|
+
.col= f.text_field :qb_item_name, label: 'QuickBooks item name'
|
41
|
+
.col= f.check_box :tax_exempt
|
42
|
+
|
43
|
+
= f.check_box :archived, label: 'Archive this ticket. It will be unavailable for purchase.'
|
44
|
+
|
45
|
+
= card('Questions') do
|
46
|
+
%p Add upto 3 questions
|
47
|
+
|
48
|
+
= f.text_field :question1, label: 'Question 1'
|
49
|
+
= f.text_field :question2, label: 'Question 2'
|
50
|
+
= f.text_field :question3, label: 'Question 3'
|
15
51
|
|
16
52
|
= effective_submit(f)
|
@@ -1,30 +1,36 @@
|
|
1
1
|
= effective_form_with(model: [:admin, event], engine: true) do |f|
|
2
2
|
= f.text_field :title, label: "Title"
|
3
3
|
|
4
|
-
- categories = EffectiveEvents.categories
|
5
|
-
- if categories.present?
|
6
|
-
= f.select :category, categories, hint: 'optional category'
|
7
|
-
|
8
|
-
- if event.class.respond_to?(:acts_as_tagged?)
|
9
|
-
= render 'effective/tags/fields', f: f
|
10
|
-
|
11
|
-
= f.datetime_field :published_at, label: 'Publish date', hint: 'When should this be displayed on the website.'
|
12
|
-
= f.check_box :draft, hint: 'Save this event as a draft. It will not be accessible on the website.'
|
13
|
-
|
14
4
|
- if f.object.persisted? || f.object.errors.include?(:slug)
|
15
5
|
- current_url = (effective_events.event_url(f.object) rescue nil)
|
16
6
|
= f.text_field :slug, hint: "The slug controls this event's internet address. Be careful, changing the slug will break links that other websites may have to the old address.<br>#{('This event is currently reachable via ' + link_to(current_url.gsub(f.object.slug, '<strong>' + f.object.slug + '</strong>').html_safe, current_url)) if current_url }".html_safe
|
17
7
|
|
18
|
-
|
19
|
-
|
8
|
+
.row
|
9
|
+
.col
|
10
|
+
- categories = EffectiveEvents.categories
|
11
|
+
- if categories.present?
|
12
|
+
= f.select :category, categories, hint: 'optional category'
|
13
|
+
.col
|
14
|
+
- if event.class.respond_to?(:acts_as_tagged?)
|
15
|
+
= render 'effective/tags/fields', f: f
|
20
16
|
|
21
|
-
|
17
|
+
= f.check_box :draft, hint: 'Save this event as a draft. It will not be accessible on the website.'
|
18
|
+
.row
|
19
|
+
.col-md-6= f.datetime_field :published_at, label: 'Publish date', hint: 'When should this be displayed on the website.'
|
20
|
+
|
21
|
+
.row
|
22
|
+
.col-md-6= f.datetime_field :start_at, label: "Event Start"
|
23
|
+
.col-md-6= f.datetime_field :end_at, label: "Event End"
|
24
|
+
|
25
|
+
%h2 Registration
|
22
26
|
%p The online registration and purchase for this event will be available between:
|
23
27
|
= f.datetime_field :registration_start_at, label: "Registration Start"
|
24
28
|
= f.datetime_field :registration_end_at, label: "Registration End"
|
25
29
|
= f.datetime_field :early_bird_end_at, label: "Early Bird End",
|
26
30
|
hint: 'Event tickets can be purchased for early bird pricing before this date. Afterwards regular pricing applies.'
|
27
31
|
|
32
|
+
= f.check_box :allow_blank_registrants, label: "Yes, allow blank ticket registrations", hint: "Allow event tickets to be purchased without adding a name. The purchaser may return to add names later."
|
33
|
+
|
28
34
|
= f.check_box :external_registration, label: "Yes, the registration for this event is handled by another website"
|
29
35
|
|
30
36
|
= f.show_if(:external_registration, true) do
|
@@ -8,13 +8,28 @@
|
|
8
8
|
- if f.object.purchased? && namespace == :admin
|
9
9
|
= f.select :event_ticket_id, effective_events_event_tickets_collection(event, namespace), label: 'Change Ticket', hint: 'Admin only. Change the purchased ticket. This will not create charges, alter the original order, or consider ticket capacity.'
|
10
10
|
|
11
|
-
.
|
12
|
-
|
13
|
-
.col-lg= f.text_field :last_name
|
14
|
-
.col-lg= f.text_field :number, label: 'Professional Designations', hint: 'member or registrant number'
|
11
|
+
- event.event_tickets.each do |ticket|
|
12
|
+
- next if ticket.member_only? && !current_user&.is?(:member)
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
= f.show_if(:event_ticket_id, ticket.id) do
|
15
|
+
- if event.allow_blank_registrants? && (f.object.new_record? || f.object.blank_registrant?)
|
16
|
+
= f.check_box :blank_registrant, label: "I will return and add this ticket's information later"
|
17
|
+
- else
|
18
|
+
= f.hidden_field :blank_registrant, value: false
|
19
|
+
|
20
|
+
- if ticket.member_or_non_member?
|
21
|
+
= f.show_if(:blank_registrant, true, nested: true) do
|
22
|
+
= f.check_box :member_registrant, label: 'Yes, this ticket will be for a member'
|
23
|
+
|
24
|
+
= f.show_if(:blank_registrant, false, nested: true) do
|
25
|
+
- if ticket.regular?
|
26
|
+
= render('effective/event_registrants/fields_regular', f: f)
|
27
|
+
- elsif ticket.member_only?
|
28
|
+
= render('effective/event_registrants/fields_member_only', f: f)
|
29
|
+
- elsif ticket.member_or_non_member?
|
30
|
+
= render('effective/event_registrants/fields_member_or_non_member', f: f)
|
31
|
+
- else
|
32
|
+
- raise("Unexpected ticket category: #{ticket.category || 'nil'}")
|
33
|
+
|
34
|
+
= render('effective/event_registrants/fields_questions', f: f, ticket: ticket)
|
19
35
|
|
20
|
-
= f.text_area :notes, label: 'Dietary restrictions or other notes'
|
@@ -0,0 +1,6 @@
|
|
1
|
+
- # Choose member
|
2
|
+
- klass = (f.object.user || current_user).class
|
3
|
+
- ajax_url = (@select2_users_ajax_path || effective_memberships.member_users_membership_select2_ajax_index_path) unless Rails.env.test?
|
4
|
+
|
5
|
+
= f.hidden_field :user_type, value: klass.name
|
6
|
+
= f.select :user_id, klass.all, ajax_url: ajax_url, label: 'Member', required: true, hint: effective_events_event_tickets_user_hint()
|
@@ -0,0 +1,20 @@
|
|
1
|
+
- # Choose member
|
2
|
+
- klass = (f.object.user || current_user).class
|
3
|
+
- ajax_url = (@select2_users_ajax_path || effective_memberships.member_users_membership_select2_ajax_index_path) unless Rails.env.test?
|
4
|
+
|
5
|
+
= f.hidden_field :user_type, value: klass.name
|
6
|
+
= f.select :user_id, klass.all, ajax_url: ajax_url, label: 'Member', hint: effective_events_event_tickets_user_hint()
|
7
|
+
|
8
|
+
= f.show_if(:user_id, '', nested: true) do
|
9
|
+
%p.text-center - or -
|
10
|
+
|
11
|
+
- # Choose non-member
|
12
|
+
.row
|
13
|
+
.col-md= f.text_field :first_name
|
14
|
+
.col-md= f.text_field :last_name
|
15
|
+
|
16
|
+
.row
|
17
|
+
.col-md= f.email_field :email
|
18
|
+
.col-md= f.text_field :company
|
19
|
+
|
20
|
+
%hr
|
@@ -0,0 +1,11 @@
|
|
1
|
+
- if ticket.questions.present?
|
2
|
+
%p Please answer the following questions:
|
3
|
+
|
4
|
+
- if ticket.question1.present?
|
5
|
+
= f.text_field :response1, label: ticket.question1
|
6
|
+
|
7
|
+
- if ticket.question2.present?
|
8
|
+
= f.text_field :response2, label: ticket.question2
|
9
|
+
|
10
|
+
- if ticket.question3.present?
|
11
|
+
= f.text_field :response3, label: ticket.question3
|
@@ -4,7 +4,7 @@
|
|
4
4
|
- if resource.submit_order.deferred?
|
5
5
|
.card
|
6
6
|
.card-body
|
7
|
-
= render_checkout_step2(resource.submit_order, purchased_url: wizard_path(:
|
7
|
+
= render_checkout_step2(resource.submit_order, purchased_url: wizard_path(:complete), deferred_url: wizard_path(:submitted), declined_url: wizard_path(:checkout))
|
8
8
|
- elsif resource.event.registerable? == false
|
9
9
|
.alert.alert-danger Your selected event is no longer available for registration.
|
10
10
|
- elsif resource.unavailable_event_tickets.present?
|
@@ -14,5 +14,5 @@
|
|
14
14
|
- else
|
15
15
|
.card
|
16
16
|
.card-body
|
17
|
-
= render_checkout_step2(resource.submit_order, purchased_url: wizard_path(:
|
17
|
+
= render_checkout_step2(resource.submit_order, purchased_url: wizard_path(:complete), deferred_url: wizard_path(:submitted), declined_url: wizard_path(:checkout))
|
18
18
|
|
@@ -0,0 +1,18 @@
|
|
1
|
+
= render 'layout' do
|
2
|
+
= render 'effective/event_registrations/content', resource: resource
|
3
|
+
|
4
|
+
- raise('expected a completed event_registration') unless resource.completed?
|
5
|
+
- raise('expected a purchased event_registration submit_order') unless resource.submit_order&.purchased?
|
6
|
+
|
7
|
+
.alert.alert-warning.mb-4
|
8
|
+
Successfully paid on #{resource.submit_order.purchased_at.strftime('%F')}.
|
9
|
+
|
10
|
+
.mb-4
|
11
|
+
= link_to "Return to Dashboard", return_to_dashboard_path, class: 'btn btn-lg btn-primary btn-block'
|
12
|
+
|
13
|
+
= render 'effective/event_registrations/summary', event_registration: resource
|
14
|
+
= render 'effective/event_registrations/event_registration', event_registration: resource
|
15
|
+
= render 'effective/event_registrations/orders', event_registration: resource
|
16
|
+
|
17
|
+
= link_to "Return to Dashboard", return_to_dashboard_path, class: 'btn btn-lg btn-primary btn-block'
|
18
|
+
|
@@ -1,11 +1,8 @@
|
|
1
1
|
= render 'layout' do
|
2
2
|
= render 'effective/event_registrations/content', resource: resource
|
3
3
|
|
4
|
-
- raise('expected a submitted event_registration') unless resource.
|
5
|
-
- raise('expected a
|
6
|
-
|
7
|
-
.alert.alert-warning.mb-4
|
8
|
-
Successfully paid on #{resource.submit_order.purchased_at.strftime('%F')}.
|
4
|
+
- raise('expected a submitted event_registration') unless resource.submitted?
|
5
|
+
- raise('expected a deffered event_registration submit_order') unless resource.submit_order&.deferred?
|
9
6
|
|
10
7
|
.mb-4
|
11
8
|
= link_to "Return to Dashboard", return_to_dashboard_path, class: 'btn btn-lg btn-primary btn-block'
|
@@ -22,6 +22,8 @@ class CreateEffectiveEvents < ActiveRecord::Migration[6.0]
|
|
22
22
|
t.boolean :external_registration, default: false
|
23
23
|
t.string :external_registration_url
|
24
24
|
|
25
|
+
t.boolean :allow_blank_registrants, default: false
|
26
|
+
|
25
27
|
t.integer :roles_mask
|
26
28
|
t.boolean :authenticate_user, default: false
|
27
29
|
|
@@ -36,10 +38,16 @@ class CreateEffectiveEvents < ActiveRecord::Migration[6.0]
|
|
36
38
|
|
37
39
|
t.string :title
|
38
40
|
t.integer :capacity
|
41
|
+
t.string :category
|
39
42
|
|
40
43
|
t.integer :regular_price
|
44
|
+
t.integer :member_price
|
41
45
|
t.integer :early_bird_price
|
42
46
|
|
47
|
+
t.text :question1
|
48
|
+
t.text :question2
|
49
|
+
t.text :question3
|
50
|
+
|
43
51
|
t.string :qb_item_name
|
44
52
|
t.boolean :tax_exempt, default: false
|
45
53
|
|
@@ -59,6 +67,9 @@ class CreateEffectiveEvents < ActiveRecord::Migration[6.0]
|
|
59
67
|
t.integer :event_registration_id
|
60
68
|
t.string :event_registration_type
|
61
69
|
|
70
|
+
t.integer :user_id
|
71
|
+
t.string :user_type
|
72
|
+
|
62
73
|
t.string :first_name
|
63
74
|
t.string :last_name
|
64
75
|
t.string :email
|
@@ -67,6 +78,13 @@ class CreateEffectiveEvents < ActiveRecord::Migration[6.0]
|
|
67
78
|
t.string :number
|
68
79
|
t.text :notes
|
69
80
|
|
81
|
+
t.boolean :blank_registrant, default: false
|
82
|
+
t.boolean :member_registrant, default: false
|
83
|
+
|
84
|
+
t.text :response1
|
85
|
+
t.text :response2
|
86
|
+
t.text :response3
|
87
|
+
|
70
88
|
t.integer :purchased_order_id
|
71
89
|
t.integer :price
|
72
90
|
|
@@ -136,6 +154,7 @@ class CreateEffectiveEvents < ActiveRecord::Migration[6.0]
|
|
136
154
|
|
137
155
|
# Dates
|
138
156
|
t.datetime :submitted_at
|
157
|
+
t.datetime :completed_at
|
139
158
|
|
140
159
|
t.datetime :updated_at
|
141
160
|
t.datetime :created_at
|
data/db/seeds.rb
CHANGED
@@ -27,6 +27,7 @@ event.event_tickets.create!(
|
|
27
27
|
capacity: 10,
|
28
28
|
regular_price: 100_00,
|
29
29
|
early_bird_price: 50_00,
|
30
|
+
member_price: 75_00,
|
30
31
|
qb_item_name: 'Tickets'
|
31
32
|
)
|
32
33
|
|
@@ -35,6 +36,7 @@ event.event_tickets.create!(
|
|
35
36
|
capacity: 20,
|
36
37
|
regular_price: 200_00,
|
37
38
|
early_bird_price: 150_00,
|
39
|
+
member_price: 75_00,
|
38
40
|
qb_item_name: 'Tickets'
|
39
41
|
)
|
40
42
|
|
@@ -43,6 +45,7 @@ event.event_tickets.create!(
|
|
43
45
|
capacity: nil,
|
44
46
|
regular_price: 200_00,
|
45
47
|
early_bird_price: 150_00,
|
48
|
+
member_price: 75_00,
|
46
49
|
qb_item_name: 'Tickets'
|
47
50
|
)
|
48
51
|
|
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.
|
4
|
+
version: 0.12.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-
|
11
|
+
date: 2024-05-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -267,6 +267,10 @@ files:
|
|
267
267
|
- app/views/admin/events/_form_event_registration_content.html.haml
|
268
268
|
- app/views/effective/event_addons/_fields.html.haml
|
269
269
|
- app/views/effective/event_registrants/_fields.html.haml
|
270
|
+
- app/views/effective/event_registrants/_fields_member_only.html.haml
|
271
|
+
- app/views/effective/event_registrants/_fields_member_or_non_member.html.haml
|
272
|
+
- app/views/effective/event_registrants/_fields_questions.html.haml
|
273
|
+
- app/views/effective/event_registrants/_fields_regular.html.haml
|
270
274
|
- app/views/effective/event_registrations/_addons.html.haml
|
271
275
|
- app/views/effective/event_registrations/_content.html.haml
|
272
276
|
- app/views/effective/event_registrations/_dashboard.html.haml
|
@@ -278,6 +282,7 @@ files:
|
|
278
282
|
- app/views/effective/event_registrations/addons.html.haml
|
279
283
|
- app/views/effective/event_registrations/billing.html.haml
|
280
284
|
- app/views/effective/event_registrations/checkout.html.haml
|
285
|
+
- app/views/effective/event_registrations/complete.html.haml
|
281
286
|
- app/views/effective/event_registrations/start.html.haml
|
282
287
|
- app/views/effective/event_registrations/submitted.html.haml
|
283
288
|
- app/views/effective/event_registrations/summary.html.haml
|