Enable registration to proceed without requiring a ticket.

Requiring a ticket after registration is convoluted; while it may be neccesary
for some workflows, it definitely isn't for others, and the core of osem
doesn't need it... so let's make it optional.
This commit is contained in:
James Mason 2018-12-29 00:08:32 -08:00 committed by Henne Vogelsang
parent 228190396c
commit 072116fffe
12 changed files with 168 additions and 128 deletions

View file

@ -83,7 +83,6 @@ linters:
- "app/views/admin/questions/index.html.haml" - "app/views/admin/questions/index.html.haml"
- "app/views/admin/questions/show.html.haml" - "app/views/admin/questions/show.html.haml"
- "app/views/admin/registration_periods/_form.html.haml" - "app/views/admin/registration_periods/_form.html.haml"
- "app/views/admin/registration_periods/show.html.haml"
- "app/views/admin/registrations/_questions.html.haml" - "app/views/admin/registrations/_questions.html.haml"
- "app/views/admin/registrations/edit.html.haml" - "app/views/admin/registrations/edit.html.haml"
- "app/views/admin/registrations/index.csv.haml" - "app/views/admin/registrations/index.csv.haml"
@ -242,8 +241,6 @@ linters:
- "app/views/surveys/index.html.haml" - "app/views/surveys/index.html.haml"
- "app/views/surveys/show.html.haml" - "app/views/surveys/show.html.haml"
- "app/views/ticket_purchases/index.html.haml" - "app/views/ticket_purchases/index.html.haml"
- "app/views/tickets/_ticket.html.haml"
- "app/views/tickets/index.html.haml"
- "app/views/tracks/_form.html.haml" - "app/views/tracks/_form.html.haml"
- "app/views/tracks/index.html.haml" - "app/views/tracks/index.html.haml"
- "app/views/tracks/show.html.haml" - "app/views/tracks/show.html.haml"
@ -359,7 +356,6 @@ linters:
- "app/views/admin/questions/index.html.haml" - "app/views/admin/questions/index.html.haml"
- "app/views/admin/questions/show.html.haml" - "app/views/admin/questions/show.html.haml"
- "app/views/admin/registration_periods/_form.html.haml" - "app/views/admin/registration_periods/_form.html.haml"
- "app/views/admin/registration_periods/show.html.haml"
- "app/views/admin/registrations/edit.html.haml" - "app/views/admin/registrations/edit.html.haml"
- "app/views/admin/registrations/index.html.haml" - "app/views/admin/registrations/index.html.haml"
- "app/views/admin/reports/_all_events.html.haml" - "app/views/admin/reports/_all_events.html.haml"
@ -467,8 +463,6 @@ linters:
- "app/views/surveys/_list.html.haml" - "app/views/surveys/_list.html.haml"
- "app/views/surveys/index.html.haml" - "app/views/surveys/index.html.haml"
- "app/views/surveys/show.html.haml" - "app/views/surveys/show.html.haml"
- "app/views/tickets/_ticket.html.haml"
- "app/views/tickets/index.html.haml"
- "app/views/tracks/_form.html.haml" - "app/views/tracks/_form.html.haml"
- "app/views/tracks/index.html.haml" - "app/views/tracks/index.html.haml"
- "app/views/tracks/show.html.haml" - "app/views/tracks/show.html.haml"
@ -560,7 +554,6 @@ linters:
- "app/views/shared/_media_item.html.haml" - "app/views/shared/_media_item.html.haml"
- "app/views/shared/_media_items.html.haml" - "app/views/shared/_media_items.html.haml"
- "app/views/tickets/_ticket.html.haml" - "app/views/tickets/_ticket.html.haml"
- "app/views/tickets/index.html.haml"
- "app/views/tracks/index.html.haml" - "app/views/tracks/index.html.haml"
- "app/views/tracks/show.html.haml" - "app/views/tracks/show.html.haml"
@ -603,7 +596,6 @@ linters:
- "app/views/proposals/show.html.haml" - "app/views/proposals/show.html.haml"
- "app/views/schedules/events.html.haml" - "app/views/schedules/events.html.haml"
- "app/views/schedules/show.xml.haml" - "app/views/schedules/show.xml.haml"
- "app/views/tickets/index.html.haml"
# Offense count: 2 # Offense count: 2
FinalNewline: FinalNewline:
@ -642,7 +634,6 @@ linters:
- "app/views/conference_registrations/_volunteer.html.haml" - "app/views/conference_registrations/_volunteer.html.haml"
- "app/views/devise/passwords/new.html.haml" - "app/views/devise/passwords/new.html.haml"
- "app/views/payments/new.html.haml" - "app/views/payments/new.html.haml"
- "app/views/tickets/index.html.haml"
# Offense count: 13 # Offense count: 13

View file

@ -1094,7 +1094,6 @@ Style/EmptyLiteral:
Style/EmptyMethod: Style/EmptyMethod:
Exclude: Exclude:
- 'app/controllers/admin/lodgings_controller.rb' - 'app/controllers/admin/lodgings_controller.rb'
- 'app/controllers/admin/registration_periods_controller.rb'
- 'app/controllers/users_controller.rb' - 'app/controllers/users_controller.rb'
- 'db/migrate/20121223115125_create_tracks_table.rb' - 'db/migrate/20121223115125_create_tracks_table.rb'
- 'db/migrate/20121223115135_create_events_table.rb' - 'db/migrate/20121223115135_create_events_table.rb'

View file

@ -23,12 +23,6 @@ module Admin
end end
end end
def edit
end
def show
end
def update def update
@registration_period.assign_attributes(registration_period_params) @registration_period.assign_attributes(registration_period_params)
send_mail_on_reg_update = @conference.notify_on_registration_dates_changed? send_mail_on_reg_update = @conference.notify_on_registration_dates_changed?

View file

@ -16,9 +16,11 @@ class TicketPurchasesController < ApplicationController
elsif current_user.ticket_purchases.by_conference(@conference).paid.any? elsif current_user.ticket_purchases.by_conference(@conference).paid.any?
redirect_to conference_physical_tickets_path, redirect_to conference_physical_tickets_path,
notice: 'You have free tickets for the conference.' notice: 'You have free tickets for the conference.'
else elsif @conference.tickets.for_registration.any?
redirect_to conference_tickets_path(@conference.short_title), redirect_to conference_tickets_path(@conference.short_title),
error: 'Please get at least one ticket to continue.' error: 'Please get at least one ticket to continue.'
else
redirect_to conference_conference_registration_path(@conference.short_title)
end end
else else
redirect_to conference_tickets_path(@conference.short_title), redirect_to conference_tickets_path(@conference.short_title),

View file

@ -117,10 +117,7 @@ class AdminAbility
can :manage, Commercial, commercialable_type: 'Conference', can :manage, Commercial, commercialable_type: 'Conference',
commercialable_id: conf_ids commercialable_id: conf_ids
can :manage, Registration, conference_id: conf_ids can :manage, Registration, conference_id: conf_ids
can :manage, RegistrationPeriod do |registration_period| can :manage, RegistrationPeriod, conference_id: conf_ids
conference = registration_period.conference
conf_ids.include?(conference.id) && conference.tickets.for_registration.any?
end
can :manage, Booth, conference_id: conf_ids can :manage, Booth, conference_id: conf_ids
can :manage, Question, conference_id: conf_ids can :manage, Question, conference_id: conf_ids
can :manage, Question do |question| can :manage, Question do |question|

View file

@ -29,11 +29,7 @@ class Conference < ApplicationRecord
has_many :ticket_purchases, dependent: :destroy has_many :ticket_purchases, dependent: :destroy
has_many :payments, dependent: :destroy has_many :payments, dependent: :destroy
has_many :supporters, through: :ticket_purchases, source: :user has_many :supporters, through: :ticket_purchases, source: :user
has_many :tickets, dependent: :destroy do has_many :tickets, dependent: :destroy
def for_registration
where(registration_ticket: true)
end
end
has_many :resources, dependent: :destroy has_many :resources, dependent: :destroy
has_many :booths, dependent: :destroy has_many :booths, dependent: :destroy
has_many :confirmed_booths, -> { where(state: 'confirmed') }, class_name: 'Booth' has_many :confirmed_booths, -> { where(state: 'confirmed') }, class_name: 'Booth'

View file

@ -10,6 +10,8 @@ class Ticket < ApplicationRecord
monetize :price_cents, with_model_currency: :price_currency monetize :price_cents, with_model_currency: :price_currency
scope :for_registration, -> { where(registration_ticket: true) }
# This validation is for the sake of simplicity. # This validation is for the sake of simplicity.
# If we would allow different currencies per conference we also have to handle convertions between currencies! # If we would allow different currencies per conference we also have to handle convertions between currencies!
validate :tickets_of_conference_have_same_currency validate :tickets_of_conference_have_same_currency

View file

@ -8,29 +8,38 @@
.row .row
.col-md-8 .col-md-8
%dl.dl-horizontal %dl.dl-horizontal
%dt %dt Start Date
Start Date %dd= @registration_period.start_date
%dd %dt End Date
= @registration_period.start_date %dd= @registration_period.end_date
%dt %dt Ticket required?
End Date %dd= @conference.tickets.for_registration.any? ? 'Yes' : 'No'
%dd
= @registration_period.end_date .row
.col-md-12
%p
If you would like to require a ticket purchase for registration, at least
one
= link_to 'ticket', admin_conference_tickets_path(@conference)
must be set as a registration ticket, and the registration process
will not proceed without the purchase (or selection of a free) single
registration ticket.
%p
To make ticketing optional, do not mark any
= link_to 'tickets', admin_conference_tickets_path(@conference)
as registration tickets, and registration can proceed without them.
.row .row
.col-md-12.text-right .col-md-12.text-right
- if @registration_period .btn-group{ role: 'group' }
- if can? :update, @registration_period - if @registration_period
= link_to 'Edit', edit_admin_conference_registration_period_path, class: 'btn btn-primary' - if can? :update, @registration_period
- if can? :destroy, @registration_period = link_to 'Edit', edit_admin_conference_registration_period_path,
= link_to 'Delete', admin_conference_registration_period_path, class: 'btn btn-primary'
method: :delete, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger' - if can? :destroy, @registration_period
- else = link_to 'Delete', admin_conference_registration_period_path,
- if @conference.tickets.for_registration.empty? method: :delete, data: { confirm: 'Are you sure?' },
.h3.text-left class: 'btn btn-danger'
No Registration Tickets! - elsif can? :create, @conference.build_registration_period
%small = link_to 'New Registration Period',
= link_to 'Create registration tickets', new_admin_conference_ticket_path new_admin_conference_registration_period_path,
before creating the registration period. class: 'btn btn-primary'
- else
- if can? :create, @conference.build_registration_period
= link_to 'New Registration Period', new_admin_conference_registration_period_path, class: 'btn btn-primary'

View file

@ -97,37 +97,48 @@
- if @conference.tickets.any? - if @conference.tickets.any?
.row .row
.col-md-12 .col-md-12
-if @tickets.any? %h4
%h4 %span.fa-stack
%span.fa-stack %i.fa.fa-square-o.fa-stack-2x
%i.fa.fa-square-o.fa-stack-2x %i.fa.fa-ticket.fa-stack-1x
%i.fa.fa-ticket.fa-stack-1x Tickets
Ticket Purchases
= "(#{@tickets.first.price.symbol}#{humanized_money @total_price})"
%ul
.col-md-12
- @ticket_payments.each_pair do |ticket_id, tickets|
%li
= @total_quantity[ticket_id]
= tickets.first.title
= word_pluralize(@total_quantity[ticket_id], 'Ticket')
for
= tickets.first.price.symbol
= humanized_money @total_price_per_ticket[ticket_id]
%br
- if @tickets.any? - if @tickets.any?
.btn-group Total Purchased:
= link_to 'View all tickets', conference_physical_tickets_path(@conference.short_title), class: "btn btn-success" = "(#{@tickets.first.price.symbol}#{humanized_money @total_price})"
= link_to 'Get more tickets', conference_tickets_path(@conference.short_title), class: "btn btn-default" %ul
.col-md-12
- @ticket_payments.each_pair do |ticket_id, tickets|
%li
= @total_quantity[ticket_id]
= tickets.first.title
= word_pluralize(@total_quantity[ticket_id], 'Ticket')
for
= tickets.first.price.symbol
= humanized_money @total_price_per_ticket[ticket_id]
%br
.btn-group{ role: 'group' }
= link_to 'View all tickets',
conference_physical_tickets_path(@conference.short_title),
class: 'btn btn-success'
= link_to 'Get more tickets',
conference_tickets_path(@conference.short_title),
class: 'btn btn-default'
- else - else
You haven't bought any tickets.
= link_to 'Please get some tickets to support us!', conference_tickets_path(@conference.short_title)
%p %p
(Your participation won't be valid without getting a registration ticket) You haven't bought any tickets.
= link_to 'Please get some tickets to support us!', conference_tickets_path(@conference.short_title)
- if @conference.tickets.for_registration.any?
%p
%em
Your participation won't be valid without getting a
registration ticket.
= link_to 'Get tickets',
conference_tickets_path(@conference.short_title),
class: 'btn btn-default'
.row .row
.col-md-12 .col-md-12
-if @registration - if @registration
.btn-group-vertical.pull-right .btn-group-vertical.pull-right
= link_to 'Edit your Registration', edit_conference_conference_registration_path(@conference.short_title), class: 'btn btn-success', disabled: @conference.end_date < Date.today = link_to 'Edit your Registration', edit_conference_conference_registration_path(@conference.short_title), class: 'btn btn-success', disabled: @conference.end_date < Date.today
- if @tickets.any? - if @tickets.any?

View file

@ -2,18 +2,26 @@
%td.col-sm-8.col-md-4 %td.col-sm-8.col-md-4
.media .media
.media-body .media-body
%h4.media-heading %h4.media-heading= ticket.title
= ticket.title %h5.media-heading= markdown(ticket.description)
%h5.media-heading - if @conference.tickets.for_registration.any?
- unless ticket.description.blank? %td.col-sm-1.col-md-2.text-center
= markdown(ticket.description) = ticket.registration_ticket? ? 'Yes' : 'No'
%td.col-sm-1.col-md-2.text-center
= ticket.registration_ticket? ? 'Yes' : 'No'
%td.col-sm-1.col-md-1 %td.col-sm-1.col-md-1
- options = { type: 'number', min: 0, max: 99, class: "form-control quantity", 'data-id' => ticket.id } :ruby
- if ticket.registration_ticket? options = {
- options[:max] = 1 type: 'number',
- options[:disabled] = current_user.tickets.for_registration(ticket.conference).present? min: 0,
max: 99,
class: 'form-control quantity',
'data-id' => ticket.id
}
if ticket.registration_ticket?
options[:max] = 1
options[:disabled] = current_user.tickets.for_registration(
ticket.conference
).present?
end
= text_field_tag("tickets[][#{ticket.id}]", 0, options) = text_field_tag("tickets[][#{ticket.id}]", 0, options)
%td.col-sm-1.col-md-1.text-center %td.col-sm-1.col-md-1.text-center
= ticket.price.symbol = ticket.price.symbol

View file

@ -5,25 +5,26 @@
%h1 %h1
Tickets Tickets
%p.lead %p.lead
Please choose your tickets for Please choose your tickets for
%strong %strong
= @conference.title = @conference.title
here* = form_tag(conference_ticket_purchases_path, method: :post) do |f|
=form_tag(conference_ticket_purchases_path, method: :post) do |f|
%table.table.table-hover %table.table.table-hover
%thead %thead
%tr %tr
%th Ticket %th Ticket
%th Registration Ticket - if @conference.tickets.for_registration.any?
%th Registration Ticket
%th Quantity %th Quantity
%th Price %th Price
%th Total %th Total
%tbody %tbody
- @conference.tickets.each do |ticket| - @conference.tickets.each do |ticket|
= render partial: 'ticket', f: f, locals: {ticket: ticket} = render 'ticket', f: f, ticket: ticket
%tr %tr
%td %td
%td - if @conference.tickets.for_registration.any?
%td
%td %td
%td.col-sm-1.col-md-1.text-center %td.col-sm-1.col-md-1.text-center
%h4 %h4
@ -32,20 +33,20 @@
%h4 %h4
%strong %strong
= @conference.tickets.first.price.symbol = @conference.tickets.first.price.symbol
%span{id: 'total_price'} %span{ id: 'total_price' }
0 0
.pull-right .pull-right
.btn-group-vertical .btn-group-vertical
= button_tag(type: 'submit', class: 'btn btn-success btn-lg') do = button_tag(type: 'submit', class: 'btn btn-success btn-lg') do
Continue Continue
%i.fa.fa-shopping-cart %i.fa.fa-shopping-cart
- if current_user.ticket_purchases.by_conference(@conference).any? = link_to 'Back to registration',
= link_to 'Back to registration', conference_conference_registration_path(@conference.short_title), class: 'btn btn-default btn-sm' conference_conference_registration_path(@conference.short_title),
- else class: 'btn btn-default btn-sm'
= link_to 'Cancel registration', conference_conference_registration_path(@conference.short_title), method: :delete, class: 'btn btn-danger btn-sm', - if @conference.tickets.for_registration.any?
data: { confirm: 'Are you sure you want to unregister?' } .row
.row .col-md-13
.col-md-13 %p.text-muted.text-center
%p.text-muted.text-center %small
%small Getting a registration ticket is mandatory. Your participation
* Getting a registration ticket is mandatory. Your participation will not be valid until you get a registration ticket. will not be valid until you get a registration ticket.

View file

@ -3,19 +3,22 @@
require 'spec_helper' require 'spec_helper'
feature RegistrationPeriod do feature RegistrationPeriod do
# It is necessary to use bang version of let to build roles before user # It is necessary to use bang version of let to build roles before user
let!(:conference) { create(:conference) } let!(:conference) { create(:conference) }
let!(:organizer_role) { Role.find_by(name: 'organizer', resource: conference) } let!(:organizer_role) { Role.find_by(name: 'organizer', resource: conference) }
let!(:organizer) { create(:user, email: 'admin@example.com', role_ids: [organizer_role.id]) } let!(:organizer) { create(:user, email: 'admin@example.com', role_ids: [organizer_role.id]) }
let!(:registration_ticket) { create(:registration_ticket, conference: conference) } let(:start_date) { Date.today }
let(:end_date) { Date.today + 5 }
shared_examples 'successfully' do context 'as organizer' do
scenario 'create and update registration period', js: true do before do
sign_in organizer sign_in organizer
visit admin_conference_registration_period_path( visit admin_conference_registration_period_path(conference_id: conference)
conference_id: conference.short_title) click_link 'New Registration Period'
end
scenario 'requires start date and end date', feature: true do
visit admin_conference_registration_period_path(conference_id: conference)
click_link 'New Registration Period' click_link 'New Registration Period'
click_button 'Save Registration Period' click_button 'Save Registration Period'
@ -23,27 +26,54 @@ feature RegistrationPeriod do
expect(flash) expect(flash)
.to eq('An error prohibited the Registration Period from being saved: ' \ .to eq('An error prohibited the Registration Period from being saved: ' \
"Start date can't be blank. End date can't be blank.") "Start date can't be blank. End date can't be blank.")
page.find('#flash .close').click end
page
.execute_script("$('#registration-period-start-datepicker').val('" +
"#{Date.today.strftime('%d/%m/%Y')}')")
page
.execute_script("$('#registration-period-end-datepicker').val('" +
"#{(Date.today + 5).strftime('%d/%m/%Y')}')")
click_button 'Save Registration Period' context 'with tickets' do
page.find('#flash') let!(:registration_ticket) do
expect(flash).to eq('Registration Period successfully updated.') create(:registration_ticket, conference: conference)
expect(current_path).to eq(admin_conference_registration_period_path(conference.short_title)) end
registration_period = RegistrationPeriod.where(conference_id: conference.id).first it 'creates registration period', feature: true, js: true do
registration_period.reload page
expect(registration_period.start_date).to eq(Date.today) .execute_script("$('#registration-period-start-datepicker').val('" +
expect(registration_period.end_date).to eq(Date.today + 5) "#{start_date.strftime('%d/%m/%Y')}')")
page
.execute_script("$('#registration-period-end-datepicker').val('" +
"#{end_date.strftime('%d/%m/%Y')}')")
click_button 'Save Registration Period'
page.find('#flash')
expect(flash).to eq('Registration Period successfully updated.')
expect(current_path).to eq(admin_conference_registration_period_path(conference.short_title))
expect(page).to have_text("Ticket required?\nYes")
registration_period = RegistrationPeriod.where(conference_id: conference.id).first
registration_period.reload
expect(registration_period.start_date).to eq(start_date)
expect(registration_period.end_date).to eq(end_date)
end
end
context 'without tickets' do
it 'creates registration period', feature: true, js: true do
page
.execute_script("$('#registration-period-start-datepicker').val('" +
"#{start_date.strftime('%d/%m/%Y')}')")
page
.execute_script("$('#registration-period-end-datepicker').val('" +
"#{end_date.strftime('%d/%m/%Y')}')")
click_button 'Save Registration Period'
page.find('#flash')
expect(flash).to eq('Registration Period successfully updated.')
expect(current_path).to eq(admin_conference_registration_period_path(conference.short_title))
expect(page).to have_text("Ticket required?\nNo")
registration_period = RegistrationPeriod.where(conference_id: conference.id).first
registration_period.reload
expect(registration_period.start_date).to eq(start_date)
expect(registration_period.end_date).to eq(end_date)
end
end end
end end
describe 'organizer' do
it_behaves_like 'successfully'
end
end end