artfully_ose 1.2.0.pre.15 → 1.2.0.pre.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/app/assets/javascripts/application.js +22 -1
- data/app/assets/javascripts/bootstrap-wysihtml5.js +511 -0
- data/app/assets/javascripts/custom/prices.js +1 -0
- data/app/assets/javascripts/wysihtml5-0.3.0.min.js +261 -0
- data/app/assets/stylesheets/application.sass +25 -1
- data/app/assets/stylesheets/bootstrap-wysihtml5.css +102 -0
- data/app/assets/stylesheets/bootstrap.css +1 -0
- data/app/assets/stylesheets/sass/store.sass +22 -0
- data/app/assets/stylesheets/wysiwyg-color.css +67 -0
- data/app/controllers/exchanges_controller.rb +7 -1
- data/app/controllers/membership_kits_controller.rb +25 -0
- data/app/controllers/mobile/dashboard_controller.rb +17 -0
- data/app/controllers/mobile/events_controller.rb +16 -0
- data/app/controllers/mobile/orders_controller.rb +60 -0
- data/app/controllers/mobile/shows_controller.rb +33 -0
- data/app/controllers/mobile/tickets_controller.rb +85 -0
- data/app/controllers/mobile/users_controller.rb +32 -0
- data/app/controllers/people_controller.rb +8 -6
- data/app/controllers/refunds_controller.rb +7 -7
- data/app/controllers/searches_controller.rb +3 -4
- data/app/controllers/store/checkouts_controller.rb +4 -4
- data/app/controllers/store/events_controller.rb +14 -2
- data/app/controllers/store/memberships_controller.rb +2 -0
- data/app/controllers/store/orders_controller.rb +1 -1
- data/app/controllers/tickets_controller.rb +2 -2
- data/app/helpers/artfully_ose_helper.rb +21 -0
- data/app/mailers/order_mailer.rb +28 -1
- data/app/models/address.rb +1 -1
- data/app/models/checkout.rb +8 -14
- data/app/models/contribution.rb +1 -0
- data/app/models/event.rb +17 -6
- data/app/models/exchange.rb +7 -5
- data/app/models/ext/integrations.rb +8 -3
- data/app/models/item.rb +4 -0
- data/app/models/job/checkout_processor.rb +41 -0
- data/app/models/job/order_mailer_job.rb +8 -0
- data/app/models/job/order_processor.rb +50 -9
- data/app/models/kit.rb +1 -1
- data/app/models/kits/membership_kit.rb +55 -0
- data/app/models/member.rb +16 -4
- data/app/models/membership_type.rb +7 -0
- data/app/models/order_handler.rb +4 -3
- data/app/models/orders/imported_order.rb +4 -0
- data/app/models/orders/order.rb +16 -5
- data/app/models/orders/pdf_generation.rb +75 -0
- data/app/models/organization.rb +7 -0
- data/app/models/person.rb +15 -0
- data/app/models/refund.rb +5 -2
- data/app/models/section.rb +16 -2
- data/app/models/show.rb +2 -8
- data/app/models/show_stats_view.rb +10 -0
- data/app/models/ticket.rb +38 -15
- data/app/models/ticket/qr_code.rb +21 -0
- data/app/models/ticket/template.rb +1 -0
- data/app/models/ticket/transfers.rb +5 -1
- data/app/models/ticket_summary.rb +5 -2
- data/app/models/ticket_type.rb +42 -7
- data/app/models/user.rb +3 -0
- data/app/models/user_membership.rb +1 -0
- data/app/views/actions/get/_show.html.haml +1 -12
- data/app/views/contributions/_form.html.haml +1 -1
- data/app/views/events/_ticket_type_fields.html.haml +21 -0
- data/app/views/events/image.html.haml +1 -1
- data/app/views/exchanges/new.html.haml +51 -28
- data/app/views/layouts/_google_analytics.html.haml +5 -2
- data/app/views/layouts/members.html.haml +1 -1
- data/app/views/layouts/storefront.html.haml +1 -1
- data/app/views/members/invitations/edit.html.erb +14 -0
- data/app/views/members/invitations/new.html.erb +14 -0
- data/app/views/members/mailer/invitation_instructions.html.haml +11 -0
- data/app/views/membership_kits/edit.html.haml +68 -0
- data/app/views/membership_types/index.html.haml +7 -3
- data/app/views/order_mailer/confirmation_for_exchange.html.haml +34 -0
- data/app/views/order_mailer/confirmation_for_exchange.text.haml +26 -0
- data/app/views/order_mailer/confirmation_for_refund.html.haml +29 -0
- data/app/views/order_mailer/confirmation_for_refund.text.haml +22 -0
- data/app/views/organizations/_form.html.haml +1 -1
- data/app/views/people/_header.html.haml +4 -4
- data/app/views/people/index.html.haml +18 -6
- data/app/views/refunds/new.html.haml +41 -27
- data/app/views/store/checkouts/_membership_info.html.haml +11 -0
- data/app/views/store/checkouts/thanks.html.haml +2 -10
- data/app/views/store/events/_calendar.html.haml +1 -4
- data/app/views/store/events/calendar.html.haml +49 -0
- data/app/views/store/events/show.html.haml +3 -8
- data/app/views/store/events/single_show.html.haml +2 -2
- data/app/views/store/memberships/index.html.haml +3 -11
- data/app/views/store/shows/_show.html.haml +6 -6
- data/config/initializers/paperclip.rb +14 -0
- data/config/routes.rb +35 -0
- data/db/migrate/20130722153731_add_autentication_token_to_users.rb +5 -0
- data/db/migrate/20130723212712_add_qr_code_to_tickets.rb +9 -0
- data/db/migrate/20130820011240_add_uuid_to_tickets.rb +13 -0
- data/db/migrate/20130820025134_make_ticket_uuid_not_null.rb +11 -0
- data/db/migrate/20130823191625_add_pdf_to_orders.rb +5 -0
- data/db/migrate/20131127162818_add_buyer_id_index_to_tickets.rb +5 -0
- data/db/migrate/20131127164000_member_tickets.rb +9 -0
- data/lib/artfully_ose.rb +3 -1
- data/lib/artfully_ose/engine.rb +5 -4
- data/lib/artfully_ose/version.rb +1 -1
- metadata +51 -2
data/app/models/kit.rb
CHANGED
@@ -61,7 +61,7 @@ class Kit < ActiveRecord::Base
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def self.subklasses
|
64
|
-
@subklasses ||= [ TicketingKit, RegularDonationKit, SponsoredDonationKit, ResellerKit, MailchimpKit ].freeze
|
64
|
+
@subklasses ||= [ TicketingKit, RegularDonationKit, SponsoredDonationKit, ResellerKit, MailchimpKit, MembershipKit ].freeze
|
65
65
|
end
|
66
66
|
|
67
67
|
def self.pad_with_new_kits(kits = [])
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class MembershipKit < Kit
|
2
|
+
include ActionView::Helpers::SanitizeHelper
|
3
|
+
|
4
|
+
acts_as_kit :with_approval => true, :admin_only => true do
|
5
|
+
approve :unless => :no_bank_account?
|
6
|
+
|
7
|
+
self.configurable = true
|
8
|
+
|
9
|
+
state_machine do
|
10
|
+
state :cancelled, :enter => :kit_cancelled
|
11
|
+
end
|
12
|
+
|
13
|
+
when_active do |organization|
|
14
|
+
organization.can :access, :reselling
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
before_save :sanitize_accessors
|
19
|
+
|
20
|
+
ACCESSORS = [ :marketing_copy_heading, :marketing_copy_sidebar, :thanks_copy, :invitation_email_text_copy ]
|
21
|
+
|
22
|
+
ACCESSORS.each do |accessor|
|
23
|
+
attr_accessible accessor
|
24
|
+
end
|
25
|
+
|
26
|
+
store :settings, :accessors => ACCESSORS
|
27
|
+
|
28
|
+
def friendly_name
|
29
|
+
"Membership"
|
30
|
+
end
|
31
|
+
|
32
|
+
def no_bank_account?
|
33
|
+
errors.add(:requirements, "Your organization needs bank account information first.") if organization.bank_account.nil?
|
34
|
+
organization.bank_account.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
def pitch
|
38
|
+
"Sell Memberships!"
|
39
|
+
end
|
40
|
+
|
41
|
+
def configured?
|
42
|
+
membership_state == "configured"
|
43
|
+
end
|
44
|
+
|
45
|
+
def configured!
|
46
|
+
settings[:membership_state] = "configured"
|
47
|
+
save
|
48
|
+
end
|
49
|
+
|
50
|
+
def sanitize_accessors
|
51
|
+
ACCESSORS.each do |accessor|
|
52
|
+
self.send("#{accessor}=", (sanitize self.send(accessor)))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/app/models/member.rb
CHANGED
@@ -17,9 +17,9 @@ class Member < ActiveRecord::Base
|
|
17
17
|
PAST = :past
|
18
18
|
NONE = :none
|
19
19
|
|
20
|
-
scope
|
21
|
-
scope
|
22
|
-
scope
|
20
|
+
scope :current, where("current_memberships_count > 0")
|
21
|
+
scope :lapsed, where("lapsed_memberships_count > 0").where("current_memberships_count = 0")
|
22
|
+
scope :past, where("past_memberships_count > 0").where("lapsed_memberships_count = 0").where("current_memberships_count = 0")
|
23
23
|
|
24
24
|
#
|
25
25
|
# devise_invitable needs this otherwise it can't set the :from param in an email
|
@@ -33,6 +33,18 @@ class Member < ActiveRecord::Base
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
def current_membership_types
|
37
|
+
memberships.current.collect(&:membership_type)
|
38
|
+
end
|
39
|
+
|
40
|
+
def member_tickets_purchased_for(event)
|
41
|
+
Ticket.joins(:ticket_type)
|
42
|
+
.joins(:show => :event)
|
43
|
+
.where(:buyer_id => self.person.id)
|
44
|
+
.where('shows.event_id' => event.id)
|
45
|
+
.where('ticket_types.member_ticket = 1')
|
46
|
+
end
|
47
|
+
|
36
48
|
#
|
37
49
|
# This is always run DJ'd
|
38
50
|
#
|
@@ -47,7 +59,7 @@ class Member < ActiveRecord::Base
|
|
47
59
|
#
|
48
60
|
# Intentionally did not use a state machine for this for a few reasons
|
49
61
|
# 1) I'm not all that happy with transitions
|
50
|
-
# 2) Can't find another
|
62
|
+
# 2) Can't find another state machine that I like and is worth the cost of switching to
|
51
63
|
# 3) This works just fine. I prefer calculating state on the fly here because we're couning the memberships
|
52
64
|
# on this member anyway
|
53
65
|
# 4) Determining a lapsed or past member is a touch more complicated than it sounds
|
@@ -14,6 +14,7 @@ class MembershipType < ActiveRecord::Base
|
|
14
14
|
|
15
15
|
scope :storefront, where(:on_sale => true).where("sales_start_at < ? or sales_start_at is null", DateTime.now).where("sales_end_at > ? or sales_end_at is null", DateTime.now)
|
16
16
|
scope :on_sale, where(:on_sale => true)
|
17
|
+
scope :not_ended, where('ends_at > ?', DateTime.now)
|
17
18
|
|
18
19
|
comma do
|
19
20
|
name
|
@@ -22,6 +23,9 @@ class MembershipType < ActiveRecord::Base
|
|
22
23
|
fee
|
23
24
|
number_of_tickets
|
24
25
|
type
|
26
|
+
memberships 'Memberships sold' do |m|
|
27
|
+
m.count
|
28
|
+
end
|
25
29
|
members { |m| m.count }
|
26
30
|
duration
|
27
31
|
period
|
@@ -32,6 +36,9 @@ class MembershipType < ActiveRecord::Base
|
|
32
36
|
sales_end_at
|
33
37
|
end
|
34
38
|
|
39
|
+
def self.in_play
|
40
|
+
self.find((MembershipType.not_ended.pluck(:id).uniq + Membership.current.select(:membership_type_id).uniq.pluck(:membership_type_id)))
|
41
|
+
end
|
35
42
|
|
36
43
|
def membershipize
|
37
44
|
self.name.end_with?("Membership") ? self.name : self.name + " Membership"
|
data/app/models/order_handler.rb
CHANGED
@@ -2,10 +2,11 @@
|
|
2
2
|
# Handles orders in progress for which customers have not paid
|
3
3
|
#
|
4
4
|
class OrderHandler
|
5
|
-
attr_accessor :discount_error, :over_limit, :cart, :error
|
5
|
+
attr_accessor :discount_error, :over_limit, :cart, :error, :member
|
6
6
|
|
7
|
-
def initialize(cart)
|
7
|
+
def initialize(cart, member)
|
8
8
|
self.cart = cart
|
9
|
+
self.member = member
|
9
10
|
end
|
10
11
|
|
11
12
|
def handle_tickets(params)
|
@@ -15,7 +16,7 @@ class OrderHandler
|
|
15
16
|
|
16
17
|
ticket_type = TicketType.find(params[:ticket_type_id])
|
17
18
|
Rails.logger.debug("QUANTITY #{params[:quantity].to_i}")
|
18
|
-
tickets = ticket_type.available_tickets(params[:quantity].to_i)
|
19
|
+
tickets = ticket_type.available_tickets(params[:quantity].to_i, member)
|
19
20
|
ids = tickets.collect(&:id)
|
20
21
|
Rails.logger.debug("TICKET IDS: #{ids}")
|
21
22
|
Ticket.lock(tickets, ticket_type, self.cart)
|
data/app/models/orders/order.rb
CHANGED
@@ -25,7 +25,7 @@ class Order < ActiveRecord::Base
|
|
25
25
|
has_many :items, :dependent => :destroy
|
26
26
|
has_many :actions, :foreign_key => "subject_id", :dependent => :destroy
|
27
27
|
|
28
|
-
attr_accessor :skip_actions
|
28
|
+
attr_accessor :skip_actions, :skip_email
|
29
29
|
|
30
30
|
set_watch_for :created_at, :local_to => :organization
|
31
31
|
set_watch_for :created_at, :local_to => :self, :as => :admins
|
@@ -49,6 +49,8 @@ class Order < ActiveRecord::Base
|
|
49
49
|
scope :csv_not_imported, where("import_id IS NULL")
|
50
50
|
scope :artfully, where("transaction_id IS NOT NULL")
|
51
51
|
|
52
|
+
has_attached_file :pdf
|
53
|
+
|
52
54
|
searchable do
|
53
55
|
text :details, :id, :type, :location, :transaction_id, :payment_method, :special_instructions
|
54
56
|
|
@@ -115,6 +117,10 @@ class Order < ActiveRecord::Base
|
|
115
117
|
def discount_codes
|
116
118
|
tickets.collect(&:discount).uniq.compact
|
117
119
|
end
|
120
|
+
|
121
|
+
def imported?
|
122
|
+
!self.import_id.nil?
|
123
|
+
end
|
118
124
|
|
119
125
|
def total_discount
|
120
126
|
tickets.collect(&:total_discount).sum
|
@@ -169,6 +175,15 @@ class Order < ActiveRecord::Base
|
|
169
175
|
def destroyable?
|
170
176
|
( (type.eql? "ApplicationOrder") || (type.eql? "ImportedOrder") ) && !is_fafs? && !artfully? && has_single_donation?
|
171
177
|
end
|
178
|
+
|
179
|
+
def skip_confirmation_email?
|
180
|
+
@skip_email || anonymous_purchase? || imported? || self.person.email.blank?
|
181
|
+
end
|
182
|
+
|
183
|
+
def process
|
184
|
+
@skip_actions ||= false
|
185
|
+
OrderProcessor.process(self, {:skip_actions => @skip_actions, :skip_email => self.skip_confirmation_email?})
|
186
|
+
end
|
172
187
|
|
173
188
|
def assignable?
|
174
189
|
anonymous_purchase? && parent.nil?
|
@@ -355,10 +370,6 @@ class Order < ActiveRecord::Base
|
|
355
370
|
self.person.calculate_lifetime_donations
|
356
371
|
end
|
357
372
|
|
358
|
-
def process
|
359
|
-
OrderProcessor.process(self, @skip_actions)
|
360
|
-
end
|
361
|
-
|
362
373
|
def purchase_action_class
|
363
374
|
GetAction
|
364
375
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'digest/md5'
|
3
|
+
|
4
|
+
module Orders
|
5
|
+
class PdfGeneration
|
6
|
+
PDF_POST_URL = "http://quick-pdf.geminisbs.net/pdfs.json"
|
7
|
+
PDF_GET_URL = "http://quick-pdf.geminisbs.net/pdfs/:id.json?api_key=:key"
|
8
|
+
PDF_DOWNLOAD_URL = "http://quick-pdf.geminisbs.net/pdfs/:id/download?api_key=:key"
|
9
|
+
|
10
|
+
ERB_TEMPLATE = <<-ERB
|
11
|
+
<h1>Your Tickets</h1>
|
12
|
+
<% order.tickets.map(&:product).each do |ticket| %>
|
13
|
+
<img src="<%= ticket.qr_code_url %>" />
|
14
|
+
<% end %>
|
15
|
+
ERB
|
16
|
+
|
17
|
+
def initialize(order, pdf_api_key = PDF_API_KEY, http_party = HTTParty, sleeper = Kernel)
|
18
|
+
@order = order
|
19
|
+
@pdf_api_key = pdf_api_key
|
20
|
+
@http_party = http_party
|
21
|
+
@sleeper = sleeper
|
22
|
+
end
|
23
|
+
|
24
|
+
def generate
|
25
|
+
response = http_party.post(PDF_POST_URL, pdf_attributes)
|
26
|
+
|
27
|
+
json = JSON.parse(response.body)
|
28
|
+
while !json["complete"]
|
29
|
+
response = http_party.get(status_url(json))
|
30
|
+
json = JSON.parse(response.body)
|
31
|
+
|
32
|
+
sleeper.sleep 1 unless json["complete"]
|
33
|
+
end
|
34
|
+
|
35
|
+
download_url(json)
|
36
|
+
end
|
37
|
+
|
38
|
+
def content
|
39
|
+
ERB.new(ERB_TEMPLATE).result(binding)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
attr_reader :order, :pdf_api_key, :http_party, :sleeper
|
44
|
+
|
45
|
+
def pdf_attributes
|
46
|
+
{
|
47
|
+
:body => {
|
48
|
+
:api_key => pdf_api_key,
|
49
|
+
:pdf => {
|
50
|
+
:html_doc => content,
|
51
|
+
:filename => filename,
|
52
|
+
:pdf_options => {
|
53
|
+
:margin_top => "0.5in",
|
54
|
+
:margin_bottom => "0.5in",
|
55
|
+
:margin_left => "0.5in",
|
56
|
+
:margin_right => "0.5in"
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def filename
|
64
|
+
"#{Digest::MD5.hexdigest order.id.to_s}.pdf"
|
65
|
+
end
|
66
|
+
|
67
|
+
def status_url(json)
|
68
|
+
PDF_GET_URL.gsub(":id", json["id"].to_s).gsub(":key", pdf_api_key)
|
69
|
+
end
|
70
|
+
|
71
|
+
def download_url(json)
|
72
|
+
PDF_DOWNLOAD_URL.gsub(":id", json["id"].to_s).gsub(":key", pdf_api_key)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/app/models/organization.rb
CHANGED
@@ -83,6 +83,9 @@ class Organization < ActiveRecord::Base
|
|
83
83
|
UserMembership.promote(user, self)
|
84
84
|
end
|
85
85
|
|
86
|
+
#
|
87
|
+
# In service of the user_memberships.owner migration.
|
88
|
+
#
|
86
89
|
def previous_owner
|
87
90
|
users.order('user_memberships.id asc').first
|
88
91
|
end
|
@@ -99,6 +102,10 @@ class Organization < ActiveRecord::Base
|
|
99
102
|
name
|
100
103
|
end
|
101
104
|
|
105
|
+
def membership_kit
|
106
|
+
self.kits.where(:type => "MembershipKit").first
|
107
|
+
end
|
108
|
+
|
102
109
|
delegate :can?, :cannot?, :to => :ability
|
103
110
|
def ability
|
104
111
|
OrganizationAbility.new(self)
|
data/app/models/person.rb
CHANGED
@@ -330,6 +330,21 @@ class Person < ActiveRecord::Base
|
|
330
330
|
person
|
331
331
|
end
|
332
332
|
|
333
|
+
def update_name(first_name, last_name)
|
334
|
+
return if (self.first_name == first_name) && (self.last_name == last_name)
|
335
|
+
|
336
|
+
ActiveRecord::Base.transaction do
|
337
|
+
str = "Name changed from checkout."
|
338
|
+
unless (self.first_name.blank? && self.last_name.blank?)
|
339
|
+
str += " Old name was #{self.first_name} #{self.last_name}."
|
340
|
+
end
|
341
|
+
new_note(str, Time.now, nil, self.organization.id)
|
342
|
+
self.first_name = first_name.try(:capitalize)
|
343
|
+
self.last_name = last_name.try(:capitalize)
|
344
|
+
self.save
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
333
348
|
# Needs a serious refactor
|
334
349
|
def update_address(new_address, time_zone, user = nil, updated_by = nil)
|
335
350
|
unless new_address.nil?
|
data/app/models/refund.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
class Refund
|
2
|
-
attr_accessor :order, :refund_order, :items, :message
|
2
|
+
attr_accessor :order, :refund_order, :items, :message, :send_email_confirmation
|
3
3
|
|
4
4
|
BRAINTREE_UNSETTLED_MESSAGE = "Cannot refund a transaction unless it is settled. (91506)"
|
5
|
-
FRIENDLY_UNSETTLED_MESSAGE = "
|
5
|
+
FRIENDLY_UNSETTLED_MESSAGE = "Unfortunately we cannot refund credit card transactions until the day after they were processed. Please re-issue the refund tomorrow."
|
6
6
|
|
7
7
|
def initialize(order, items)
|
8
8
|
self.order = order
|
@@ -11,6 +11,8 @@ class Refund
|
|
11
11
|
|
12
12
|
def submit(options = {})
|
13
13
|
return_items_to_inventory = options[:and_return] || false
|
14
|
+
@send_email_confirmation = options[:send_email_confirmation] || false
|
15
|
+
|
14
16
|
|
15
17
|
ActiveRecord::Base.transaction do
|
16
18
|
items.each do |i|
|
@@ -75,6 +77,7 @@ class Refund
|
|
75
77
|
@refund_order.parent = order
|
76
78
|
@refund_order.for_organization order.organization
|
77
79
|
@refund_order.items = items.collect(&:to_refund)
|
80
|
+
@refund_order.skip_email = !send_email_confirmation
|
78
81
|
@refund_order.save!
|
79
82
|
@refund_order
|
80
83
|
end
|
data/app/models/section.rb
CHANGED
@@ -53,8 +53,22 @@ class Section < ActiveRecord::Base
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
def ticket_types_for(member)
|
57
|
-
|
56
|
+
def ticket_types_for(member = nil)
|
57
|
+
types = []
|
58
|
+
|
59
|
+
# Add storefront types regardless of anything
|
60
|
+
types << ticket_types.storefront
|
61
|
+
unless member.nil?
|
62
|
+
|
63
|
+
#Add "unrestricted member" ticket_types that aren't tired to a particular membership type
|
64
|
+
types << ticket_types.members.select{|ticket_type| !ticket_type.member_ticket?} unless member.nil?
|
65
|
+
|
66
|
+
#Now add member ticket_types that apply to this particular member
|
67
|
+
membership_type_ids = member.nil? ? [] : member.current_membership_types.collect(&:id)
|
68
|
+
types << ticket_types.select{|ticket_type| ticket_type.members && membership_type_ids.include?(ticket_type.membership_type_id) }
|
69
|
+
end
|
70
|
+
|
71
|
+
types.flatten.uniq
|
58
72
|
end
|
59
73
|
|
60
74
|
def dup!
|
data/app/models/show.rb
CHANGED
@@ -146,7 +146,7 @@ class Show < ActiveRecord::Base
|
|
146
146
|
"show_time" => show_time,
|
147
147
|
"datetime" => datetime_local_to_event,
|
148
148
|
"destroyable" => destroyable?,
|
149
|
-
"chart" => chart_for("storefront", options[:organization_id]).as_json(options)
|
149
|
+
"chart" => chart_for("storefront", options[:member], options[:organization_id]).as_json(options)
|
150
150
|
}
|
151
151
|
end
|
152
152
|
|
@@ -156,7 +156,7 @@ class Show < ActiveRecord::Base
|
|
156
156
|
def as_widget_json(options = {})
|
157
157
|
as_json.merge(:event => event.as_json,
|
158
158
|
:venue => event.venue.as_json,
|
159
|
-
:chart => chart_for("storefront", options[:organization_id]).as_json(options))
|
159
|
+
:chart => chart_for("storefront", options[:member], options[:organization_id]).as_json(options))
|
160
160
|
end
|
161
161
|
|
162
162
|
def bulk_on_sale(ids)
|
@@ -224,12 +224,6 @@ class Show < ActiveRecord::Base
|
|
224
224
|
tickets.select(&:compable?)
|
225
225
|
end
|
226
226
|
|
227
|
-
def as_widget_json(options = {})
|
228
|
-
as_json.merge(:event => event.as_json,
|
229
|
-
:venue => event.venue.as_json,
|
230
|
-
:chart => chart_for("storefront", options[:organization_id]).as_json(options))
|
231
|
-
end
|
232
|
-
|
233
227
|
def sections_for(member)
|
234
228
|
member.nil? ? self.chart.sections.storefront : self.chart.sections.members
|
235
229
|
end
|
data/app/models/ticket.rb
CHANGED
@@ -6,8 +6,11 @@
|
|
6
6
|
include Ticket::Pricing
|
7
7
|
include Ticket::Transfers
|
8
8
|
include Ticket::SaleTransitions
|
9
|
+
include Ext::Uuid
|
9
10
|
extend ActionView::Helpers::TextHelper
|
10
|
-
|
11
|
+
|
12
|
+
class TicketAlreadyValidated < StandardError; end
|
13
|
+
|
11
14
|
attr_accessible :section_id, :section, :venue, :cart_price
|
12
15
|
|
13
16
|
belongs_to :buyer, :class_name => "Person"
|
@@ -16,7 +19,7 @@
|
|
16
19
|
belongs_to :section
|
17
20
|
belongs_to :cart
|
18
21
|
belongs_to :discount
|
19
|
-
belongs_to :action, :foreign_key => "validated_action_id"
|
22
|
+
belongs_to :action, :foreign_key => "validated_action_id", :class_name => "GoAction"
|
20
23
|
|
21
24
|
#
|
22
25
|
# This refs the ticket_type that the ticket was sold under, NOT an array of ticket types available
|
@@ -25,6 +28,10 @@
|
|
25
28
|
|
26
29
|
has_many :items, :as => :product
|
27
30
|
|
31
|
+
has_attached_file :qr_code, TICKET_QR_STORAGE
|
32
|
+
|
33
|
+
delegate :url, :to => :qr_code, :prefix => true
|
34
|
+
|
28
35
|
delegate :event, :to => :show
|
29
36
|
def self.sold_after(datetime)
|
30
37
|
sold.where("sold_at > ?", datetime)
|
@@ -210,27 +217,43 @@
|
|
210
217
|
})
|
211
218
|
t.show = show
|
212
219
|
t.organization = show.organization
|
220
|
+
t.set_uuid
|
213
221
|
t.state = 'on_sale' if on_sale
|
214
222
|
t
|
215
223
|
end
|
216
224
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
+
def generate_qr_code
|
226
|
+
file = Tempfile.new(['qr-code', '.png'])
|
227
|
+
QRCode.new(self, sold_item.try(:order)).render(file)
|
228
|
+
self.qr_code = file
|
229
|
+
|
230
|
+
# If we don't save the ticket here, paperclip will leak a file handle.
|
231
|
+
# Even though we close our Tempfile below, paperclip copies that into
|
232
|
+
# another Tempfile, and only closes the handle when the ticket is saved.
|
233
|
+
save
|
234
|
+
|
235
|
+
file.close
|
236
|
+
end
|
237
|
+
|
238
|
+
def validate_ticket!(user = nil)
|
239
|
+
if committed? && !validated?
|
240
|
+
Ticket.transaction do
|
241
|
+
self.action = GoAction.for(self.show, self.buyer, Time.now) do |go_action|
|
242
|
+
go_action.creator = user
|
243
|
+
end
|
244
|
+
self.validated = true
|
245
|
+
save
|
225
246
|
end
|
226
|
-
self.update_attributes({:validated_action_id => go_action.id, :validated => true}, :without_protection => true)
|
227
247
|
end
|
228
248
|
end
|
229
249
|
|
230
|
-
def unvalidate_ticket
|
231
|
-
|
232
|
-
|
233
|
-
|
250
|
+
def unvalidate_ticket!
|
251
|
+
if validated?
|
252
|
+
Ticket.transaction do
|
253
|
+
action.destroy unless self.action.tickets.count > 1
|
254
|
+
self.validated = false
|
255
|
+
save
|
256
|
+
end
|
234
257
|
end
|
235
258
|
end
|
236
259
|
end
|