artfully_ose 1.2.0.pre.24 → 1.2.0.pre.26

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +9 -9
  2. data/app/assets/fonts/FontAwesome.otf +0 -0
  3. data/app/assets/fonts/fontawesome-webfont.eot +0 -0
  4. data/app/assets/fonts/fontawesome-webfont.svg +504 -0
  5. data/app/assets/fonts/fontawesome-webfont.ttf +0 -0
  6. data/app/assets/fonts/fontawesome-webfont.woff +0 -0
  7. data/app/assets/javascripts/application.js +5 -0
  8. data/app/assets/javascripts/custom/people.js +68 -5
  9. data/app/assets/javascripts/sales-console.js +153 -0
  10. data/app/assets/javascripts/store/jquery.validate.additional-methods.js +476 -159
  11. data/app/assets/javascripts/store/jquery.validate.js +449 -406
  12. data/app/assets/stylesheets/application.sass +49 -0
  13. data/app/assets/stylesheets/font-awesome.css.scss +1566 -0
  14. data/app/concerns/cart_finder.rb +5 -0
  15. data/app/controllers/console_sales_controller.rb +182 -0
  16. data/app/controllers/events_controller.rb +1 -1
  17. data/app/controllers/events_pass_types_controller.rb +10 -0
  18. data/app/controllers/memberships_controller.rb +3 -3
  19. data/app/controllers/people_controller.rb +2 -1
  20. data/app/controllers/sales_controller.rb +51 -13
  21. data/app/controllers/shows_controller.rb +2 -2
  22. data/app/controllers/store/orders_controller.rb +0 -9
  23. data/app/helpers/artfully_ose_helper.rb +4 -2
  24. data/app/helpers/sales_helper.rb +29 -0
  25. data/app/models/cart.rb +6 -1
  26. data/app/models/checkout.rb +20 -13
  27. data/app/models/comp.rb +13 -16
  28. data/app/models/console_sale.rb +30 -0
  29. data/app/models/database_views/item_view.rb +3 -0
  30. data/app/models/job/checkout_processor.rb +10 -5
  31. data/app/models/kits/mailchimp_kit.rb +2 -1
  32. data/app/models/kits/passes_kit.rb +1 -1
  33. data/app/models/kits/scannable_tickets_kit.rb +3 -4
  34. data/app/models/membership.rb +18 -3
  35. data/app/models/membership_change.rb +1 -1
  36. data/app/models/membership_type.rb +2 -3
  37. data/app/models/order.rb +20 -17
  38. data/app/models/order_handler.rb +21 -0
  39. data/app/models/orders/comp_order.rb +1 -1
  40. data/app/models/parsed_row.rb +45 -2
  41. data/app/models/pass.rb +13 -0
  42. data/app/models/pass_type.rb +16 -0
  43. data/app/models/payment.rb +17 -0
  44. data/app/models/payments/cash_payment.rb +3 -1
  45. data/app/models/payments/check_payment.rb +33 -0
  46. data/app/models/payments/comp_payment.rb +3 -1
  47. data/app/models/payments/credit_card_payment.rb +8 -18
  48. data/app/models/person.rb +27 -3
  49. data/app/models/sale.rb +13 -10
  50. data/app/models/ticket.rb +1 -1
  51. data/app/views/console_sales/_aloha.html.haml +26 -0
  52. data/app/views/console_sales/_cart.html.haml +60 -0
  53. data/app/views/console_sales/_payment.html.haml +111 -0
  54. data/app/views/console_sales/_shows.html.haml +24 -0
  55. data/app/views/console_sales/new.html.haml +143 -0
  56. data/app/views/events/_day_date_show.html.haml +2 -2
  57. data/app/views/events/_ticket_type_fields.html.haml +1 -1
  58. data/app/views/events_pass_types/_form.html.haml +1 -1
  59. data/app/views/events_pass_types/index.html.haml +2 -1
  60. data/app/views/exchanges/new.html.haml +1 -1
  61. data/app/views/imports/people/_new.html.haml +12 -0
  62. data/app/views/kits/_list.html.haml +2 -1
  63. data/app/views/layouts/_menu.html.haml +0 -3
  64. data/app/views/membership_comps/create.html.haml +3 -1
  65. data/app/views/membership_types/_form.html.haml +0 -1
  66. data/app/views/orders/_item_table.haml +5 -3
  67. data/app/views/passes/index.html.haml +2 -2
  68. data/app/views/people/_edit_modal.html.haml +7 -0
  69. data/app/views/people/_work_with_menu.html.haml +4 -1
  70. data/app/views/people/show.html.haml +13 -4
  71. data/app/views/refunds/new.html.haml +1 -1
  72. data/app/views/sales/_boxoffice.html.haml +141 -0
  73. data/app/views/sales/_doorlist.html.haml +79 -0
  74. data/app/views/sales/new.html.haml +33 -125
  75. data/app/views/searches/show.html.haml +1 -1
  76. data/app/views/segments/show.html.haml +1 -1
  77. data/app/views/shows/_ticket_table.html.haml +1 -1
  78. data/app/views/shows/_work_with.html.haml +1 -1
  79. data/app/views/shows/index.html.haml +5 -6
  80. data/app/views/shows/show.html.haml +2 -2
  81. data/config/initializers/devise.rb +1 -1
  82. data/config/initializers/paperclip.rb +16 -0
  83. data/config/routes.rb +10 -1
  84. data/db/migrate/20140507184646_add_bday_attributes_to_person.rb +7 -0
  85. data/db/migrate/20140507201048_add_check_number_to_orders.rb +5 -0
  86. data/db/migrate/20140619194127_add_do_not_call_to_people.artfully_ose_engine.rb +5 -0
  87. data/db/migrate/20140623160543_add_index_to_items.rb +7 -0
  88. data/lib/artfully_ose/engine.rb +5 -4
  89. data/lib/artfully_ose/version.rb +1 -1
  90. data/spec/factories/person_factories.rb +3 -0
  91. metadata +43 -20
@@ -7,6 +7,11 @@ module CartFinder
7
7
 
8
8
  included do |c|
9
9
  c.helper_method :current_cart
10
+ c.helper_method :current_sales_console_cart
11
+ end
12
+
13
+ def current_sales_console_cart
14
+ current_cart(ConsoleSale::Cart)
10
15
  end
11
16
 
12
17
  def current_box_office_cart
@@ -0,0 +1,182 @@
1
+ class ConsoleSalesController < ArtfullyOseController
2
+ before_filter :verify_person
3
+ include CartFinder
4
+
5
+ rescue_from ActiveRecord::RecordInvalid, :with => :checkout_error
6
+
7
+ def new
8
+ @events = Event.where(:id => current_organization.shows.unplayed.pluck(:event_id).uniq)
9
+ @membership_types = current_organization.membership_types.sales_valid
10
+
11
+ @membership_types_hash = {}
12
+ @membership_types.each {|mt| @membership_types_hash[mt.id] = {:allow_multiple_memberships => mt.allow_multiple_memberships?,:formatted_ends_at => I18n.l(mt.ends_at, :format => :date_for_input)}}
13
+
14
+ @pass_types = current_organization.pass_types.all
15
+ end
16
+
17
+ def update
18
+ handler = OrderHandler.new(current_sales_console_cart, nil)
19
+ handler.handle(params, current_organization)
20
+ flash[:alert] = handler.error unless handler.error.blank?
21
+ redirect_to new_console_sale_path(:person_id => @person.try(:id))
22
+ end
23
+
24
+ def destroy
25
+ current_sales_console_cart.clear!
26
+ redirect_to new_console_sale_path(:person_id => @person.try(:id))
27
+ end
28
+
29
+ def create
30
+ unless payment_method_is_vaild? &&
31
+ membership_and_pass_requirements_met &&
32
+ member_is_valid?
33
+
34
+ redirect_to new_console_sale_path(:person_id => @person.try(:id)) and return
35
+ end
36
+
37
+ @checkout = ConsoleSale::Checkout.new(current_sales_console_cart, payment, params[:order_notes])
38
+
39
+ if @checkout.valid? && @checkout.finish
40
+ @order = @checkout.order
41
+ flash[:notice] = success_flash_message(@order, @checkout.person.email)
42
+ redirect_to new_console_sale_path
43
+ else
44
+ flash[:error] = @checkout.message
45
+ redirect_to new_console_sale_path(:person_id => @person.try(:id))
46
+ end
47
+ rescue Exception => e
48
+ checkout_error(e)
49
+ end
50
+
51
+ def events
52
+ @event = current_organization.events.find(params[:event_id])
53
+ @shows = @event.upcoming_shows(:all)
54
+ render :json => @shows
55
+ end
56
+
57
+ def shows
58
+ @show = current_organization.shows.find(params[:show_id])
59
+ @ticket_types = @show.chart.sections.first.ticket_types_for(nil)
60
+ render :partial => "shows"
61
+ end
62
+
63
+ private
64
+ def checkout_error(e = nil)
65
+ Exceptional.context(:params => filter(params))
66
+ unless e.nil?
67
+ Exceptional.handle(e, "Sales console checkout")
68
+ Rails.logger.error(e.backtrace)
69
+ Rails.logger.error(e.message)
70
+ end
71
+ flash[:error] = "We're sorry but we could not process the sale. Please make sure all fields are filled out accurately"
72
+ redirect_to new_console_sale_path(:person_id => @person.try(:id))
73
+ end
74
+
75
+ def membership_and_pass_requirements_met
76
+ if (current_sales_console_cart.memberships.any? || current_sales_console_cart.passes.any?)
77
+ unless params[:customer][:email].present?
78
+ flash[:error] = "Sorry, we can't sell memberships or passes to patrons without an email."
79
+ return false
80
+ end
81
+ end
82
+ true
83
+ end
84
+
85
+ def payment_method_is_vaild?
86
+ if params[:payment_method].blank?
87
+ flash[:error] = "Please pick a payment method."
88
+ return false
89
+ end
90
+
91
+ #Would be better to have the items themselves declare which paryment methods are valid
92
+ if current_sales_console_cart.donations.any? && payment.is_a?(CompPayment)
93
+ flash[:error] = "Sorry, we can't process this comp because we can't comp donations."
94
+ return false
95
+ end
96
+
97
+ true
98
+ end
99
+
100
+ #
101
+ # Returns false if there are member tickets in the cart and
102
+ # the email provided is not a valid member
103
+ #
104
+ def member_is_valid?
105
+ member_tickets = current_sales_console_cart.tickets.select{|t| t.ticket_type.member_ticket == true}
106
+ if member_tickets.any?
107
+ member_email = payment.customer.email
108
+ if member_email.nil?
109
+ flash[:error] = "Sorry!"
110
+ return false
111
+ end
112
+
113
+ member = Member.where(:organization_id => current_user.current_organization)
114
+ .where(:email => member_email)
115
+ .first
116
+
117
+ if member.nil?
118
+ flash[:error] = "Sorry, we can't sell these tickets to non-member."
119
+ return false
120
+ end
121
+
122
+ member_tickets.each do |member_ticket|
123
+ membership_type = member_ticket.ticket_type.membership_type
124
+ if !member.current_membership_types.include? membership_type
125
+ flash[:error] = "Sorry, the member #{member_email} isn't eligible to purchase these tickets."
126
+ return false
127
+ end
128
+ end
129
+ end
130
+
131
+ true
132
+ end
133
+
134
+ def success_flash_message(order, email)
135
+ str = "Sale processed."
136
+ str += "<br/>Order number is #{@order.id}. <a target='_blank' href='#{Rails.application.routes.url_helpers.order_path(@order)}'>Click here to view the order</a>."
137
+
138
+ if @checkout.person.email.blank?
139
+ str += "<br/>No confirmation email was sent because we don't have an email on file for this patron."
140
+ else
141
+ str += "<br/>A confirmation email was sent to #{@checkout.person.email}."
142
+ end
143
+ str
144
+ end
145
+
146
+ def verify_person
147
+ @person_id = params[:person_id]
148
+ if @person_id.present?
149
+ @person = Person.find(@person_id)
150
+ if @person.organization_id != current_organization.id
151
+ raise CanCan::AccessDenied
152
+ end
153
+ end
154
+ end
155
+
156
+ def person
157
+ return @person unless @person.nil?
158
+ Person.first_or_create(person_attributes)
159
+ end
160
+
161
+ def person_attributes
162
+ {
163
+ :id => params[:customer][:id],
164
+ :first_name => params[:customer][:first_name],
165
+ :last_name => params[:customer][:last_name],
166
+ :email => params[:customer][:email],
167
+ :phones_attributes => params[:customer][:phones_attributes],
168
+ :organization => current_organization
169
+ }
170
+ end
171
+
172
+ def payment
173
+ @payment ||= Payment.create(params[:payment_method], params)
174
+ @payment
175
+ end
176
+
177
+ def filter(params)
178
+ filters = Rails.application.config.filter_parameters
179
+ f = ActionDispatch::Http::ParameterFilter.new filters
180
+ f.filter params
181
+ end
182
+ end
@@ -124,7 +124,7 @@ class EventsController < ArtfullyOseController
124
124
  if user_just_uploaded_an_image?
125
125
  messages_event_path(@event)
126
126
  elsif user_set_special_instructions?
127
- event_shows_path(@event)
127
+ new_event_show_path(@event)
128
128
  else
129
129
  edit_event_venue_path(@event)
130
130
  end
@@ -32,6 +32,11 @@ class EventsPassTypesController < ArtfullyOseController
32
32
  ept.active = params[:events_pass_type][:active]
33
33
  end
34
34
 
35
+ if @events_pass_type.ticket_types.empty?
36
+ flash[:error] = "Please select at least one Ticket Type for this pass."
37
+ redirect_to new_event_events_pass_type_path(@event) and return
38
+ end
39
+
35
40
  if @events_pass_type.save
36
41
  flash[:notice] = "Your pass has been attached to this event."
37
42
  else
@@ -51,6 +56,11 @@ class EventsPassTypesController < ArtfullyOseController
51
56
  @events_pass_type.excluded_shows = Set.new(params[:events_pass_type][:excluded_shows].reject!(&:blank?))
52
57
  @events_pass_type.active = params[:events_pass_type][:active]
53
58
 
59
+ if @events_pass_type.ticket_types.empty?
60
+ flash[:error] = "Please select at least one Ticket Type for this pass."
61
+ redirect_to new_event_events_pass_type_path(@event) and return
62
+ end
63
+
54
64
  if @events_pass_type.save
55
65
  flash[:notice] = "Your pass has been updated."
56
66
  else
@@ -2,13 +2,13 @@ class MembershipsController < ArtfullyOseController
2
2
  before_filter :load_tags, :only => [:index]
3
3
 
4
4
  def index
5
- @person = Person.find(params[:person_id])
5
+ @person = current_organization.people.find(params[:person_id])
6
6
  @expired_count = @person.memberships.lapsed.count
7
7
  @membership_types = current_organization.membership_types
8
8
  end
9
9
 
10
10
  def bulk_update
11
- @person = Person.find(params[:person_id])
11
+ @person = current_organization.people.find(params[:person_id])
12
12
  extend_memberships(params)
13
13
  redirect_to person_memberships_path(@person)
14
14
  end
@@ -20,7 +20,7 @@ class MembershipsController < ArtfullyOseController
20
20
  #I hate how these are tied to the button text
21
21
  if params[:commit].eql? "Change Expiration"
22
22
  params[:membership_ids].each do |membership_id|
23
- Membership.find(membership_id).adjust_expiration_to(params[:ends_at])
23
+ current_organization.memberships.find(membership_id).adjust_expiration_to(params[:ends_at])
24
24
  end
25
25
  flash[:notice] = "Memberships have been adjusted."
26
26
  end
@@ -18,6 +18,7 @@ class PeopleController < ArtfullyOseController
18
18
  @person.company_name = person[:company_name] if person[:company_name].present?
19
19
  @person.subscribed_lists = person[:subscribed_lists] if person[:subscribed_lists].present?
20
20
  @person.do_not_email = person[:do_not_email] if person[:do_not_email].present?
21
+ @person.do_not_call = person[:do_not_call] if person[:do_not_call].present?
21
22
  @person.type = person[:type] || "Individual"
22
23
  @person.subtype = person[:subtype] || "Individual"
23
24
  @person.organization_id = current_user.current_organization.id
@@ -61,7 +62,7 @@ class PeopleController < ArtfullyOseController
61
62
  flash[:notice] << "Your changes have been saved"
62
63
  else
63
64
  errs = [@person.errors.delete(:"relationships.base")].flatten + [@person.errors.full_messages.to_sentence].flatten
64
- flash[:alert] = errs.blank? ? "Sorry, we couldn't save your changes. Make sure you entered a first name, last name or email address." : errs
65
+ flash[:alert] = errs.blank? ? "Sorry, we couldn't save your changes. Make sure you entered a first name, last name or email address." : errs.compact
65
66
  end
66
67
  redirect_to_person(@person, params)
67
68
  end
@@ -1,10 +1,10 @@
1
1
  class SalesController < ArtfullyOseController
2
2
  include CartFinder
3
3
  before_filter :find_event, :find_show, :find_people, :find_dummy
4
- before_filter :create_door_list, :only => ['show', 'new']
4
+ before_filter :create_door_list, :only => ['show', 'new', 'door_list']
5
5
 
6
6
  def show
7
- redirect_to new_event_show_sales_path(@event, @show)
7
+ redirect_to new_event_show_sales_path(@event,@show,:render => 'boxoffice')
8
8
  end
9
9
 
10
10
  def new
@@ -12,14 +12,39 @@ class SalesController < ArtfullyOseController
12
12
  @sale = Sale.new(@show, @show.chart.ticket_types.box_office, current_box_office_cart, {})
13
13
  @tickets_remaining = tickets_remaining
14
14
  setup_defaults
15
+ response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate" # http://stackoverflow.com/a/5493543/2063202
15
16
  end
16
17
 
17
18
  def create
18
19
  current_box_office_cart.clear!
19
- @sale = Sale.new(@show, @show.chart.ticket_types.box_office, current_box_office_cart, params[:quantities])
20
+
21
+ @sale = Sale.new(@show, @show.chart.ticket_types.box_office, current_box_office_cart, params[:quantities], params[:order_notes])
22
+
23
+ # handle donation
24
+ if params[:donation].present? && params[:donation].to_i > 0
25
+ donation = Donation.new
26
+ donation.amount = params[:donation].to_i * 100
27
+ donation.organization_id = @event.organization_id
28
+ current_box_office_cart.donations << donation
29
+ end
30
+
31
+ # handle discount
32
+ begin
33
+ discount = Discount.find_by_code_and_event_id(params[:discount].upcase, @event)
34
+ discount.apply_discount_to_cart(current_box_office_cart)
35
+ rescue RuntimeError => e
36
+ discount_error = e.message
37
+ rescue NoMethodError => e
38
+ discount_error = "We could not find your discount. Please try again." if params[:discount].present?
39
+ end
40
+
20
41
  if checking_out?
21
42
  if @sale.sell(payment)
22
- @sale.message = "Sold #{self.class.helpers.pluralize(@sale.tickets.length, 'ticket')}. Order total was #{self.class.helpers.number_as_cents @sale.cart.total}"
43
+ @sale.message = "Sold #{self.class.helpers.pluralize(@sale.tickets.length, 'ticket')}. Order total was #{self.class.helpers.number_as_cents @sale.order.total}"
44
+
45
+ if params[:auto_check_in].present?
46
+ @sale.tickets.map {|t| t.reload; t.validate_ticket!(current_user)}
47
+ end
23
48
  end
24
49
  end
25
50
 
@@ -27,19 +52,21 @@ class SalesController < ArtfullyOseController
27
52
  @sale.error = "#{@sale.errors.full_messages.to_sentence.capitalize}."
28
53
  flash[:error] = @sale.error
29
54
  Ticket.unlock(@sale.tickets, @sale.cart)
30
- render :js => "window.location = '#{new_event_show_sales_path(@event, @show)}'"
55
+ render :js => "window.location = '#{new_event_show_sales_path(@event,@show,:render => 'boxoffice')}'"
31
56
  return
32
57
  end
33
58
 
34
59
  render :json => @sale.as_json
35
60
  .merge(:total => @sale.cart.total)
36
61
  .merge(:tickets_remaining => tickets_remaining)
37
- .merge(:door_list_rows => door_list_rows),
62
+ .merge(:door_list_rows => door_list_rows)
63
+ .merge(:discount_error => discount_error)
64
+ .merge(:discount_amount => current_box_office_cart.discount_amount),
38
65
  :status => 200
39
66
  end
40
67
 
41
68
  def checking_out?
42
- !params[:commit].blank?
69
+ params[:commit].present?
43
70
  end
44
71
 
45
72
  def door_list_rows
@@ -61,6 +88,11 @@ class SalesController < ArtfullyOseController
61
88
  door_list_rows
62
89
  end
63
90
 
91
+ def door_list
92
+ # create_door_list
93
+ render :partial => "sales/doorlist"
94
+ end
95
+
64
96
  private
65
97
 
66
98
  # TODO: this should be pulled into TicketTypeSerializer
@@ -120,11 +152,12 @@ class SalesController < ArtfullyOseController
120
152
 
121
153
  def person_attributes
122
154
  {
123
- :id => params[:person][:id],
124
- :first_name => params[:person][:first_name],
125
- :last_name => params[:person][:last_name],
126
- :email => params[:person][:email],
127
- :organization => current_organization
155
+ :id => params[:person][:id],
156
+ :first_name => params[:person][:first_name],
157
+ :last_name => params[:person][:last_name],
158
+ :email => params[:person][:email],
159
+ :phones_attributes => params[:person][:phones_attributes],
160
+ :organization => current_organization
128
161
  }
129
162
  end
130
163
 
@@ -146,7 +179,12 @@ class SalesController < ArtfullyOseController
146
179
 
147
180
  params[:benefactor] = current_user
148
181
 
149
- payment = Payment.create(params[:payment_method], params)
182
+ # set payment as cash if total is 0 (e.g. free tickets)
183
+ if params[:payment_method].blank? && params[:total] == '0'
184
+ payment = Payment.create('cash', params)
185
+ else
186
+ payment = Payment.create(params[:payment_method], params)
187
+ end
150
188
  payment.customer = person
151
189
  payment
152
190
  end
@@ -1,5 +1,5 @@
1
1
  class ShowsController < ArtfullyOseController
2
- before_filter :find_event, :only => [ :index, :calendar, :show, :new, :edit, :duplicate, :on_sale, :off_sale ]
2
+ before_filter :find_event, :only => [ :index, :calendar, :show, :new, :edit, :duplicate, :on_sale, :off_sale, :destroy ]
3
3
  before_filter :check_for_charts, :only => [ :index, :new ]
4
4
 
5
5
  rescue_from CanCan::AccessDenied do |exception|
@@ -119,7 +119,7 @@ class ShowsController < ArtfullyOseController
119
119
  end
120
120
  else
121
121
  flash[:error] = "Sorry, this show can not be deleted. Contact support for further help."
122
- redirect_to show_path(@show)
122
+ redirect_to event_path(@event)
123
123
  end
124
124
  end
125
125
 
@@ -51,15 +51,6 @@ class Store::OrdersController < Store::StoreController
51
51
 
52
52
  private
53
53
 
54
- #
55
- # The storefront javascript depends on this total_string to determine
56
- # if this is a free order or not. Don't re-word this string without also
57
- # addressing the javascript in store.js
58
- #
59
- def total_string
60
- "#{pluralize(current_cart.items.length, 'item')}, #{number_as_cents current_cart.total}"
61
- end
62
-
63
54
  def event
64
55
  current_cart.tickets.first.event
65
56
  end
@@ -129,7 +129,7 @@ module ArtfullyOseHelper
129
129
 
130
130
  def amount_and_nongift(item)
131
131
  str = number_as_cents item.total_price
132
- str += " (#{number_as_cents item.nongift_amount} Non-deductible)" unless item.nongift_amount.nil?
132
+ str += " (#{number_as_cents item.nongift_amount} Non-deductible)" unless item.nongift_amount.nil? || item.nongift_amount == 0
133
133
  str
134
134
  end
135
135
 
@@ -139,7 +139,9 @@ module ArtfullyOseHelper
139
139
  end
140
140
 
141
141
  def number_as_cents(cents, options = {})
142
- number_to_currency(number_to_dollars(cents), options)
142
+ result = number_to_currency(number_to_dollars(cents), options)
143
+ result = result.split('.').first if result.split('.').last == '00' && options[:cents_if_needed] == true
144
+ result
143
145
  end
144
146
 
145
147
  def sorted_us_state_names