artfully_ose 1.3.0.pre3 → 1.3.0.pre4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/loading-white.gif +0 -0
  3. data/app/assets/images/seating_temp/1.jpg +0 -0
  4. data/app/assets/images/seating_temp/10.jpg +0 -0
  5. data/app/assets/images/seating_temp/11.jpg +0 -0
  6. data/app/assets/images/seating_temp/2.jpg +0 -0
  7. data/app/assets/images/seating_temp/3.jpg +0 -0
  8. data/app/assets/images/seating_temp/4.jpg +0 -0
  9. data/app/assets/images/seating_temp/5.jpg +0 -0
  10. data/app/assets/images/seating_temp/6.jpg +0 -0
  11. data/app/assets/images/seating_temp/7.jpg +0 -0
  12. data/app/assets/images/seating_temp/8.jpg +0 -0
  13. data/app/assets/images/seating_temp/9.jpg +0 -0
  14. data/app/assets/images/seating_temp/icon_43405.png +0 -0
  15. data/app/assets/images/seating_temp/icon_43405.svg +3 -0
  16. data/app/assets/images/seating_temp/icon_43405_plus.png +0 -0
  17. data/app/assets/images/seating_temp/license.txt +8 -0
  18. data/app/assets/javascripts/application.js +1 -0
  19. data/app/assets/javascripts/custom/cookies.js +27 -0
  20. data/app/assets/javascripts/custom/inline-people-search.js +4 -0
  21. data/app/assets/javascripts/custom/job-monitor.js +0 -1
  22. data/app/assets/javascripts/exchange-seat-chart.js +5 -0
  23. data/app/assets/javascripts/seat-chart.js +606 -0
  24. data/app/assets/javascripts/show-seat-chart.js +17 -0
  25. data/app/assets/javascripts/store/store-seat-chart.js +76 -0
  26. data/app/assets/javascripts/store/store.js +14 -39
  27. data/app/assets/stylesheets/application.sass +49 -1
  28. data/app/assets/stylesheets/bootstrap-overrides.css +11 -3
  29. data/app/assets/stylesheets/sass/seat-chart.sass +311 -0
  30. data/app/assets/stylesheets/sass/store.sass +73 -19
  31. data/app/assets/stylesheets/storefront.css +2 -0
  32. data/app/concerns/pdf_generation.rb +1 -15
  33. data/app/controllers/advanced_searches_controller.rb +1 -3
  34. data/app/controllers/charts_controller.rb +14 -12
  35. data/app/controllers/console_sales_controller.rb +55 -0
  36. data/app/controllers/event_calendars_controller.rb +15 -0
  37. data/app/controllers/events_controller.rb +16 -5
  38. data/app/controllers/exchanges_controller.rb +32 -9
  39. data/app/controllers/export_controller.rb +8 -0
  40. data/app/controllers/members/sessions_controller.rb +0 -2
  41. data/app/controllers/membership_comps_controller.rb +2 -1
  42. data/app/controllers/prices_controller.rb +20 -0
  43. data/app/controllers/shows_controller.rb +3 -1
  44. data/app/controllers/store/events_controller.rb +6 -5
  45. data/app/controllers/store/orders_controller.rb +11 -13
  46. data/app/controllers/store/shows_controller.rb +1 -0
  47. data/app/controllers/store/store_controller.rb +11 -0
  48. data/app/controllers/tickets_controller.rb +0 -28
  49. data/app/controllers/user_memberships_controller.rb +7 -2
  50. data/app/controllers/venues_controller.rb +9 -0
  51. data/app/helpers/artfully_ose_helper.rb +18 -4
  52. data/app/helpers/seating_helper.rb +157 -0
  53. data/app/models/assigned_chart.rb +220 -0
  54. data/app/models/cart.rb +6 -1
  55. data/app/models/chart.rb +28 -17
  56. data/app/models/database_views/item_view.rb +76 -5
  57. data/app/models/discount.rb +4 -0
  58. data/app/models/discounts/buy_one_get_one_free_discount_type.rb +1 -1
  59. data/app/models/discounts/dollars_off_tickets_discount_type.rb +5 -1
  60. data/app/models/discounts/percentage_off_tickets_discount_type.rb +8 -4
  61. data/app/models/event.rb +86 -12
  62. data/app/models/general_admission_chart.rb +51 -0
  63. data/app/models/imports/events_import.rb +1 -1
  64. data/app/models/job/show_creator.rb +3 -10
  65. data/app/models/kit.rb +11 -1
  66. data/app/models/kits/assigned_seating_kit.rb +23 -0
  67. data/app/models/member_walkup.rb +1 -1
  68. data/app/models/order_handler.rb +22 -6
  69. data/app/models/organization.rb +4 -5
  70. data/app/models/person.rb +3 -1
  71. data/app/models/sale.rb +1 -1
  72. data/app/models/seat.rb +96 -0
  73. data/app/models/section.rb +1 -3
  74. data/app/models/show.rb +10 -17
  75. data/app/models/ticket.rb +30 -6
  76. data/app/models/ticket/locker.rb +3 -2
  77. data/app/models/ticket_type.rb +12 -6
  78. data/app/models/ticket_types_seat.rb +4 -0
  79. data/app/models/venue.rb +23 -0
  80. data/app/views/advanced_searches/filters/_birthday.html.haml +1 -0
  81. data/app/views/assigned_charts/_show.html.haml +12 -0
  82. data/app/views/console_sales/_aloha.html.haml +5 -4
  83. data/app/views/console_sales/new.html.haml +9 -0
  84. data/app/views/event_calendars/index.html.haml +34 -0
  85. data/app/views/events/_menu.html.haml +2 -1
  86. data/app/views/events/_section_fields.html.haml +13 -8
  87. data/app/views/events/new.html.haml +34 -4
  88. data/app/views/events/prices.html.haml +3 -4
  89. data/app/views/events/seating.html.haml +131 -0
  90. data/app/views/exchanges/_assigned_chart.html.haml +108 -0
  91. data/app/views/exchanges/_general_admission_chart.html.haml +55 -0
  92. data/app/views/exchanges/new.html.haml +1 -55
  93. data/app/views/general_admission_charts/_show.html.haml +2 -0
  94. data/app/views/layouts/_menu.html.haml +44 -31
  95. data/app/views/layouts/storefront.html.haml +72 -30
  96. data/app/views/membership_comps/new.html.haml +1 -0
  97. data/app/views/order_mailer/confirmation_for.html.haml +5 -3
  98. data/app/views/order_mailer/confirmation_for.text.haml +1 -2
  99. data/app/views/pdfs/order.html.haml +20 -6
  100. data/app/views/seating/_actions_menu.html.haml +10 -0
  101. data/app/views/seating/_chart.html.haml +15 -0
  102. data/app/views/seating/_chart_container.html.haml +2 -0
  103. data/app/views/seating/_chart_note_display.html.haml +7 -0
  104. data/app/views/seating/_hold_seats_modal.html.haml +39 -0
  105. data/app/views/seating/_layer_menu.html.haml +23 -0
  106. data/app/views/seating/_legend.html.haml +52 -0
  107. data/app/views/seating/_modify_seats_modal.html.haml +29 -0
  108. data/app/views/seating/_ticket_type_modals.html.haml +41 -0
  109. data/app/views/shows/_seat_chart.html.haml +122 -0
  110. data/app/views/shows/_sections_table.html.haml +3 -24
  111. data/app/views/shows/_ticket_table.html.haml +4 -2
  112. data/app/views/shows/_work_with.html.haml +4 -0
  113. data/app/views/shows/new.html.haml +2 -2
  114. data/app/views/shows/show.html.haml +93 -14
  115. data/app/views/store/assigned_charts/_show.html.haml +54 -0
  116. data/app/views/store/checkouts/shopping_cart_display/_tickets.haml +13 -0
  117. data/app/views/store/donations/index.html.haml +1 -1
  118. data/app/views/store/events/calendar.html.haml +94 -11
  119. data/app/views/store/events/index.html.haml +1 -1
  120. data/app/views/store/events/show.html.haml +36 -2
  121. data/app/views/store/events/single_show.html.haml +5 -4
  122. data/app/views/store/general_admission_charts/_show.html.haml +41 -0
  123. data/app/views/store/orders/show.html.haml +17 -4
  124. data/app/views/store/shared/_donate_form.html.haml +2 -1
  125. data/app/views/store/shared/_small_donate_form.html.haml +1 -1
  126. data/app/views/store/shows/_show.html.haml +1 -39
  127. data/app/views/ticket_types/edit.html.haml +1 -1
  128. data/app/views/user_memberships/_list.html.haml +1 -1
  129. data/app/views/venues/edit.html.haml +1 -1
  130. data/app/views/venues/show.html.haml +11 -0
  131. data/config/routes.rb +27 -10
  132. data/db/migrate/20140828174357_add_type_to_chart.rb +7 -0
  133. data/db/migrate/20140828195617_create_seats.rb +16 -0
  134. data/db/migrate/20140829135426_add_venue_to_chart.rb +5 -0
  135. data/db/migrate/20140829143520_add_assigned_to_event.rb +5 -0
  136. data/db/migrate/20140904164927_add_fields_to_seat.rb +14 -0
  137. data/db/migrate/20140912170010_add_note_to_seat.rb +6 -0
  138. data/db/migrate/20140919152307_add_seats_to_ticket.rb +5 -0
  139. data/db/migrate/20140930224543_add_public_note_to_chart.rb +5 -0
  140. data/db/migrate/20141001140737_add_seat_id_index_to_tickets.rb +5 -0
  141. data/db/migrate/20141001193242_associate_hold_with_person.rb +6 -0
  142. data/db/migrate/20141021175311_create_ticket_types_seats.rb +11 -0
  143. data/db/migrate/20150106161744_migrate_charts_back_to_venues.rb +18 -0
  144. data/db/migrate/20151105085424_add_creator_to_items_view.rb +40 -0
  145. data/lib/artfully_ose/version.rb +1 -1
  146. data/spec/factories/assigned_chart_factories.rb +18 -0
  147. data/spec/factories/chart_factories.rb +4 -8
  148. data/spec/factories/kit_factories.rb +4 -0
  149. data/spec/factories/seat_factories.rb +4 -0
  150. data/spec/factories/show_factories.rb +1 -1
  151. metadata +66 -5
  152. data/app/models/ticket/foundry.rb +0 -48
  153. data/app/models/ticket/template.rb +0 -40
  154. data/app/views/store/events/_calendar.html.haml +0 -13
@@ -162,7 +162,7 @@ class EventsImport < Import
162
162
 
163
163
  raise Import::RuntimeError, 'No section found for ticket' unless section
164
164
 
165
- ticket = Ticket.build_one(show, section,1, true)
165
+ ticket = Ticket.build_one(show, section, true)
166
166
  ticket.ticket_type = ticket_type
167
167
  Rails.logger.info("Processing Import [#{id}] EVENT_IMPORT: Ticket built [#{ticket.inspect}]")
168
168
  ticket.sell_to person
@@ -1,26 +1,19 @@
1
1
  class ShowCreator < Struct.new(:datetimes, :show_params, :chart_params, :event, :organization, :publish)
2
2
 
3
- #
4
- # If datetime.length < 3, the shows will create right away. Otherwise, they'll be queued
5
- #
6
3
  def self.enqueue(datetimes, show_params, chart_params, event, organization, publish = false)
7
4
  datetimes ||= []
8
5
  creator = ShowCreator.new(datetimes, show_params, chart_params, event, organization, publish)
9
- if datetimes.length < 3
10
- creator.perform
11
- else
12
- Delayed::Job.enqueue(creator)
13
- end
6
+ Delayed::Job.enqueue(creator)
14
7
  end
15
8
 
16
9
  def perform
17
10
  ActiveRecord::Base.transaction do
18
11
  datetimes.each do |datetime_string|
19
- @show = self.event.next_show
12
+ @show = self.event.build_next_show
20
13
 
21
14
  #clear the sections and replace them with whatever they entered
22
15
  @show.chart.sections = []
23
- @show.chart.update_attributes_from_params(chart_params)
16
+ @show.chart.update_attributes_from_params(chart_params, self.event.default_chart)
24
17
  @show.update_attributes(show_params)
25
18
  @show.organization = organization
26
19
  @show.chart_id = @show.chart.id
@@ -83,7 +83,17 @@ class Kit < ActiveRecord::Base
83
83
  end
84
84
 
85
85
  def self.subklasses
86
- @subklasses ||= [ TicketingKit, RegularDonationKit, SponsoredDonationKit, ResellerKit, MailchimpKit, MembershipKit, PassesKit, ScannableTicketsKit, RelationshipsKit ].freeze
86
+ @subklasses ||= [ TicketingKit,
87
+ RegularDonationKit,
88
+ SponsoredDonationKit,
89
+ ResellerKit,
90
+ MailchimpKit,
91
+ MembershipKit,
92
+ PassesKit,
93
+ ScannableTicketsKit,
94
+ RelationshipsKit,
95
+ CampaignsKit,
96
+ AssignedSeatingKit ].freeze
87
97
  end
88
98
 
89
99
  def self.pad_with_new_kits(kits = [])
@@ -0,0 +1,23 @@
1
+ class AssignedSeatingKit < Kit
2
+
3
+ acts_as_kit :admin_only => true do
4
+ self.configurable = false
5
+
6
+ state_machine do
7
+ state :cancelled, :enter => :kit_cancelled
8
+ event(:submit_for_approval) { transitions :from => :fresh, :to => :pending }
9
+ end
10
+
11
+ when_active do |organization|
12
+ organization.can :access, :assigned_seating
13
+ end
14
+ end
15
+
16
+ def friendly_name
17
+ "Assigned Seating"
18
+ end
19
+
20
+ def pitch
21
+ "Assigned Seating"
22
+ end
23
+ end
@@ -95,7 +95,7 @@ class MemberWalkup
95
95
  def ticket
96
96
  unless @ticket
97
97
  return nil unless valid?
98
- @ticket = ticket_type.available_tickets(1, member).first
98
+ @ticket = ticket_type.available_tickets(1, member)[0].first
99
99
  end
100
100
  @ticket
101
101
  end
@@ -17,14 +17,20 @@ class OrderHandler
17
17
  self.handle_discount_or_pass_code(params)
18
18
  end
19
19
 
20
+ def add_tickets(tickets, ticket_type)
21
+ #check availability, obv
22
+ Ticket.lock(tickets, ticket_type, self.cart)
23
+ end
24
+
20
25
  def handle_tickets(params)
21
26
  if params[:ticket_type_id]
22
27
  ticket_ids = []
23
28
  over_limit = []
24
29
 
25
30
  ticket_type = TicketType.find(params[:ticket_type_id])
31
+ seat = params[:seat_id].present? ? Seat.find(params[:seat_id]) : nil
26
32
  Rails.logger.debug("QUANTITY #{params[:quantity].to_i}")
27
- tickets = ticket_type.available_tickets(params[:quantity].to_i, member)
33
+ tickets, self.error = ticket_type.available_tickets(params[:quantity].to_i, member, seat, self.cart)
28
34
  ids = tickets.collect(&:id)
29
35
  Rails.logger.debug("TICKET IDS: #{ids}")
30
36
  Ticket.lock(tickets, ticket_type, self.cart)
@@ -35,6 +41,7 @@ class OrderHandler
35
41
  #TODO: return and display a sensible error message
36
42
  end
37
43
 
44
+
38
45
  params = params.merge(:tickets => ticket_ids) if ticket_ids.any?
39
46
  end
40
47
  params
@@ -124,16 +131,25 @@ class OrderHandler
124
131
  def handle_discount(params)
125
132
  begin
126
133
  discount = nil
127
- self.cart.tickets.each do |ticket|
128
- discount = Discount.find_by_code_and_event_id(params[:discount_or_pass_code].upcase, ticket.show.event.id)
129
- unless discount.nil?
130
- discount.apply_discount_to_cart(self.cart)
131
- break
134
+
135
+ #
136
+ # If we have an event_id (discount was entered from specific event storefront)
137
+ # then find the discount with that. Otherwise, interrogate the cart to get
138
+ # events fro the tickets
139
+ #
140
+ if params[:event_id].present?
141
+ discount = Discount.find_by_code_and_event_id(params[:discount_or_pass_code].upcase, params[:event_id])
142
+ else
143
+ self.cart.tickets.each do |ticket|
144
+ discount = Discount.find_by_code_and_event_id(params[:discount_or_pass_code].upcase, ticket.show.event.id)
145
+ break if discount
132
146
  end
133
147
  end
134
148
 
135
149
  if discount.nil?
136
150
  self.error = "We could not find your discount. Please try again."
151
+ else
152
+ discount.apply_discount_to_cart(self.cart)
137
153
  end
138
154
  rescue RuntimeError => e
139
155
  self.error = e.message
@@ -7,6 +7,7 @@ class Organization < ActiveRecord::Base
7
7
  attr_accessible :name, :time_zone, :ein, :legal_organization_name, :email, :receive_daily_sales_report, :country, :state, :zip, :phone_number, :website, :discipline, :cached_slug, :fiscal_month, :fiscal_day, :budget_restrictions_attributes
8
8
 
9
9
  has_many :events
10
+ has_many :venues
10
11
  has_many :charts
11
12
  has_many :shows
12
13
  has_many :tickets
@@ -113,13 +114,11 @@ class Organization < ActiveRecord::Base
113
114
  users.order('user_memberships.id asc').first
114
115
  end
115
116
 
116
- #
117
- # A shorthand for "first org admin" basically meaning "Just get me someone at the org"
118
- #
119
117
  def owner
120
- @owner ||= UserMembership.includes(:user).where(:organization_id => self.id).where(:organization_administrator => true).first.try(:user)
118
+ # @owner ||= UserMembership.includes(:user).where(:organization_id => self.id).where(:organization_administrator => true).first.try(:user)
119
+ @owner ||= user_memberships.select{|um| um.organization_administrator == true}.first.try(:user)
121
120
  end
122
-
121
+
123
122
  def daily_sales_report_recipients
124
123
  @recipients ||= UserMembership.includes(:user).where(:organization_id => self.id).where(:receive_daily_sales_report => true).collect(&:user)
125
124
  end
@@ -41,6 +41,7 @@ class Person < ActiveRecord::Base
41
41
  has_one :member
42
42
  has_many :memberships, :through => :member
43
43
  has_many :passes
44
+ has_many :holds, :class_name => "Seat", :foreign_key => :held_for_id
44
45
 
45
46
  has_many :relationships, :foreign_key => :person_id, :class_name => 'Relationship', :dependent => :destroy
46
47
 
@@ -151,7 +152,7 @@ class Person < ActiveRecord::Base
151
152
  # Tickets are a special case
152
153
  #
153
154
  def self.mergables
154
- [:actions, :phones, :notes, :orders, :soft_credits, :scheduled_pledge_payments, :passes, :subscribed_lists]
155
+ [:actions, :phones, :notes, :orders, :soft_credits, :scheduled_pledge_payments, :passes, :subscribed_lists, :holds]
155
156
  end
156
157
 
157
158
  def self.loser_attributes_update_if_winner_not_present
@@ -807,6 +808,7 @@ class Person < ActiveRecord::Base
807
808
 
808
809
  def subscribe_to_default_mailchimp_list!
809
810
  return unless mailchimp_kit
811
+ return if subscribed_lists.where(:list_id => mailchimp_kit.default_list_id).exists?
810
812
  subscribed_lists.create(:list_id => mailchimp_kit.default_list_id, :single_optin => true)
811
813
  end
812
814
 
@@ -48,7 +48,7 @@ class Sale
48
48
  amount_requested = @quantities[ticket_type_id].to_i
49
49
  if amount_requested > 0
50
50
  ticket_type = TicketType.find(ticket_type_id)
51
- tickets_available_in_ticket_type = ticket_type.available_tickets(amount_requested)
51
+ tickets_available_in_ticket_type, error = ticket_type.available_tickets(amount_requested)
52
52
  if tickets_available_in_ticket_type.length != amount_requested
53
53
  errors.add(:base, "There aren't enough tickets available for that ticket type")
54
54
  else
@@ -0,0 +1,96 @@
1
+ class Seat < ActiveRecord::Base
2
+ # Even though we will eventually support multiple physical sections,
3
+ # Seat does not FK to Section. The reasons are:
4
+ #
5
+ # 1) section.rb has a ton of legacy junk in there.
6
+ # 2) section.rb is closely tied to ticket types, prices, etc...
7
+ #
8
+ # When we support multiple physical sections, we'll probably have Seat
9
+ # FK to a new object, like assigned_section or physical_section.
10
+
11
+ belongs_to :chart
12
+ belongs_to :held_for, :class_name => "Person", :foreign_key => :held_for_id
13
+ has_many :ticket_types_seats
14
+ has_many :ticket_types, :through => :ticket_types_seats, :dependent => :destroy
15
+ has_one :ticket
16
+
17
+ attr_accessible :row_index, # The 0-based row index of the seat irrespective of actual names
18
+ :col_index, # The 0-based column index of the seat irrespective of actual names
19
+ :row_label, # The actual name of the row "B" or "AAA"
20
+ :col_label, # The seat number. "17"
21
+ :label, # The physical label on the physical seat "4B", "B4", or "17".
22
+ :available # Is the seat available for purchase. Roughly analogous to "on_sale"
23
+
24
+ #
25
+ # "available" means that the seat isn't killed or held
26
+ # "available_for_purchase" means the underlying ticket can be purchased
27
+ #
28
+ def available_for_purchase
29
+ self.available && self.ticket && self.ticket.on_sale? && !self.ticket.locked?
30
+ end
31
+
32
+ def on_hold?
33
+ self.house_hold? ||
34
+ self.development_hold? ||
35
+ self.tech_hold? ||
36
+ self.contractual_hold? ||
37
+ self.person_hold? ||
38
+ self.press_hold
39
+ end
40
+
41
+ def dup!
42
+ Seat.new(self.attributes.reject { |key, value| ["chart_id", "id"].include? key }, :without_protection => true)
43
+ end
44
+
45
+ def update_ticket
46
+ if self.ticket.present?
47
+ if self.on_hold?
48
+ self.ticket.off_sale! if ticket.on_sale?
49
+ else
50
+ self.ticket.on_sale! if self.ticket.off_sale?
51
+ end
52
+ end
53
+ end
54
+
55
+ def applies_to_pass?(pass)
56
+ self.ticket_types.each do |ticket_type|
57
+ return true if ticket_type.applies_to_pass? pass
58
+ end
59
+ false
60
+ end
61
+
62
+ def hold_type
63
+ return nil unless self.on_hold?
64
+ return "House" if self.house_hold?
65
+ return "Development" if self.development_hold?
66
+ return "Tech" if self.tech_hold?
67
+ return "Contractural" if self.contractual_hold?
68
+ return "Person" if self.person_hold?
69
+ return "Press" if self.press_hold
70
+ end
71
+
72
+ def member_ticket_for?(membership_types)
73
+ return false if membership_types.empty?
74
+ self.ticket_types.select{|tt| membership_types.collect(&:id).include? tt.membership_type_id }.present?
75
+ end
76
+
77
+ #
78
+ # A human-readable qualifier to be displayed during ticket type selection
79
+ #
80
+ def qualifier
81
+ return "Wheelchair removable seat" if self.wheelchair
82
+ return "Wheelchair companion seat" if self.wheelchair_companion
83
+ return "Designated aisle seat" if self.aisle
84
+ end
85
+
86
+ #
87
+ # Don't make this a callback! Charts and shows and everything else have a process
88
+ # about creating tickets when a producer creates a show. This method is for seats created *after* a show
89
+ # has been created and put on sale
90
+ #
91
+ def create_ticket
92
+ if self.chart.show.present? && self.ticket.nil?
93
+ Ticket.build_one(self.chart.show, self.chart.sections.first, !self.on_hold?, self).save
94
+ end
95
+ end
96
+ end
@@ -1,7 +1,5 @@
1
1
  class Section < ActiveRecord::Base
2
- include Ticket::Foundry
3
2
  attr_accessor :skip_create_first_ticket_type
4
- foundry :with => lambda { { :section_id => id, :count => capacity } }
5
3
 
6
4
  attr_accessible :name, :capacity, :price, :chart_id, :description, :ticket_types_attributes, :members
7
5
  delegate :show, :to => :chart
@@ -74,7 +72,7 @@ class Section < ActiveRecord::Base
74
72
  end
75
73
 
76
74
  def dup!
77
- attrs = self.attributes.reject { |key, value| key == 'id' }
75
+ attrs = self.attributes.reject { |key, value| ["chart_id", "id"].include? key }
78
76
  self.class.new(attrs).tap do |copy|
79
77
  copy.ticket_types = self.ticket_types.collect { |ticket_type| ticket_type.dup! }
80
78
  end
@@ -1,5 +1,4 @@
1
1
  class Show < ActiveRecord::Base
2
- include Ticket::Foundry
3
2
  include Ticket::Reporting
4
3
  include ActiveRecord::Transitions
5
4
  include Ext::Resellable::Show
@@ -41,8 +40,6 @@ class Show < ActiveRecord::Base
41
40
  scope :played, lambda { where("shows.datetime < ?", Time.now) }
42
41
  scope :unplayed, lambda { where("shows.datetime > ?", Time.now) }
43
42
 
44
- foundry :using => :chart, :with => lambda {{:show_id => self.id, :organization_id => organization_id}}
45
-
46
43
  delegate :free?, :to => :event
47
44
 
48
45
  ANY_SHOW_ID = "-1"
@@ -51,7 +48,7 @@ class Show < ActiveRecord::Base
51
48
 
52
49
  #pending and built are deprecated, left in only because we have shows in production which are built
53
50
  state :pending
54
- state :built, :exit => :create_and_on_sale_tickets
51
+ state :built, :exit => :create_tickets
55
52
  state :published
56
53
  state :unpublished
57
54
 
@@ -115,13 +112,19 @@ class Show < ActiveRecord::Base
115
112
  self.cached_stats
116
113
  end
117
114
 
115
+ #
116
+ # uses cached_stats instead of going to the DB
117
+ #
118
+ def sold_out?
119
+ self.on_sale < 1
120
+ end
121
+
118
122
  def parsed_local_datetime
119
123
  @parsed_local_datetime = (DateTime.parse(self.local_datetime.to_s) rescue self.datetime_local_to_event)
120
124
  end
121
125
 
122
- def create_and_on_sale_tickets
123
- create_tickets
124
- bulk_on_sale(:all)
126
+ def create_tickets
127
+ self.chart.create_tickets
125
128
  end
126
129
 
127
130
  def unscoped_event
@@ -206,16 +209,6 @@ class Show < ActiveRecord::Base
206
209
  :chart => chart_for("storefront", options[:organization_id]).as_json(options))
207
210
  end
208
211
 
209
- def bulk_on_sale(ids)
210
- targets = (ids == :all) ? tickets : tickets.where(:id => ids)
211
- Ticket.put_on_sale(targets)
212
- end
213
-
214
- def bulk_off_sale(ids)
215
- targets = (ids == :all) ? tickets : tickets.where(:id => ids)
216
- Ticket.take_off_sale(targets)
217
- end
218
-
219
212
  def bulk_delete(ids)
220
213
  tickets.where(:id => ids).collect{ |ticket| ticket.id if ticket.destroy }#.compact
221
214
  end
@@ -21,6 +21,7 @@ class Ticket < ActiveRecord::Base
21
21
  belongs_to :pass
22
22
  belongs_to :discount
23
23
  belongs_to :action, :foreign_key => "validated_action_id", :class_name => "GoAction"
24
+ belongs_to :seat
24
25
 
25
26
  #
26
27
  # This refs the ticket_type that the ticket was sold under,
@@ -55,15 +56,34 @@ class Ticket < ActiveRecord::Base
55
56
  scope :sold_or_comped, where(:state => [:sold, :comped])
56
57
 
57
58
  state_machine :auto_scopes => true do
58
- state :off_sale
59
+ #
60
+ # :off_sale denotes that the tickets are not publicly available for purchase
61
+ # The tickets may still be sold by other means, such as the producer selling into a hold.
62
+ # Tickets may be off_Sale for a variety of reasons: seats are held, tickets explicitly
63
+ # set to off_sale, seats are killed, ticket refunded but not returned to inventory.
64
+ #
65
+ state :off_sale
66
+
67
+ #
68
+ # An :on_sale ticket is available for purchase through the storefront
69
+ #
59
70
  state :on_sale
71
+
72
+ #
73
+ # A :sold ticket has been purchased by a buyer
74
+ #
60
75
  state :sold
76
+
77
+ #
78
+ # A :comped ticket was given to a patron for no charge. It's similar to a $0 sold ticket
79
+ # but there are enough differences that it merited its own state
80
+ #
61
81
  state :comped
62
82
 
63
83
  event(:on_sale) { transitions :from => [ :on_sale, :off_sale ], :to => :on_sale }
64
84
  event(:off_sale) { transitions :from => [ :on_sale, :off_sale ], :to => :off_sale }
65
85
  event(:exchange, :success => :record_exchange) { transitions :from => [ :on_sale, :off_sale ], :to => :sold }
66
- event(:sell, :success => :record_sale) { transitions :from => [ :on_sale ], :to => :sold }
86
+ event(:sell, :success => :record_sale) { transitions :from => [ :on_sale, :off_sale ], :to => :sold }
67
87
  event(:comp, :success => :record_comp) { transitions :from => [ :on_sale, :off_sale ], :to => :comped }
68
88
  event(:return_to_inventory) { transitions :from => [ :comped, :sold ], :to => :on_sale }
69
89
  event(:return_off_sale) { transitions :from => [ :comped, :sold ], :to => :off_sale }
@@ -225,12 +245,15 @@ class Ticket < ActiveRecord::Base
225
245
  not committed?
226
246
  end
227
247
 
228
- #Bulk creation of tickets should use this method to ensure all tickets are created the same
229
- #Reminder that this returns a ActiveRecord::Import::Result, not an array of tickets
248
+ #
249
+ # Bulk creation of tickets should use this method to ensure all tickets are created the same
250
+ # Reminder that this returns a ActiveRecord::Import::Result, not an array of tickets
251
+ #
230
252
  def self.create_many(show, section, quantity, on_sale = false)
231
253
  new_tickets = []
254
+
232
255
  quantity.times do
233
- new_tickets << build_one(show, section, quantity, on_sale)
256
+ new_tickets << build_one(show, section, on_sale)
234
257
  end
235
258
 
236
259
  result = Ticket.import(new_tickets)
@@ -242,11 +265,12 @@ class Ticket < ActiveRecord::Base
242
265
  # This method does not refresh show stats
243
266
  # Callers should do that manually with show.delay.refresh_stats
244
267
  #
245
- def self.build_one(show, section, quantity, on_sale = false)
268
+ def self.build_one(show, section, on_sale = false, seat=nil)
246
269
  t = Ticket.new({
247
270
  :venue => show.event.venue.name,
248
271
  :section => section,
249
272
  })
273
+ t.seat = seat
250
274
  t.show = show
251
275
  t.organization = show.organization
252
276
  t.set_uuid