artfully_ose 1.2.0.pre.27 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +4 -6
- data/app/assets/javascripts/application.js +39 -0
- data/app/assets/javascripts/boxoffice.js +558 -0
- data/app/assets/javascripts/custom/kits-config.js +33 -0
- data/app/assets/javascripts/sales-console.js +29 -24
- data/app/assets/javascripts/store/store.js +11 -0
- data/app/assets/stylesheets/application.sass +66 -52
- data/app/assets/stylesheets/bootstrap-overrides.css +1 -1
- data/app/assets/stylesheets/boxoffice.css.scss +258 -0
- data/app/assets/stylesheets/pages/bootstrap-responsive.css +1109 -0
- data/app/assets/stylesheets/sass/store.sass +52 -62
- data/app/assets/stylesheets/storefront.css +1 -1
- data/app/concerns/itemable.rb +1 -1
- data/app/concerns/pdf_generation.rb +1 -11
- data/app/concerns/search_by_dates.rb +20 -0
- data/app/controllers/events_controller.rb +1 -0
- data/app/controllers/export_controller.rb +11 -1
- data/app/controllers/imports_controller.rb +2 -3
- data/app/controllers/member_cards_controller.rb +2 -3
- data/app/controllers/orders_controller.rb +42 -0
- data/app/controllers/organizations_controller.rb +1 -0
- data/app/controllers/passes_reports_controller.rb +21 -1
- data/app/controllers/regular_donation_kits_controller.rb +32 -0
- data/app/controllers/store/donations_controller.rb +19 -1
- data/app/controllers/store/orders_controller.rb +15 -5
- data/app/helpers/artfully_ose_helper.rb +28 -18
- data/app/models/database_views/item_view.rb +41 -0
- data/app/models/event.rb +10 -2
- data/app/models/ext/integrations.rb +5 -0
- data/app/models/fee_strategy.rb +47 -1
- data/app/models/imports/events_import.rb +6 -7
- data/app/models/item.rb +4 -3
- data/app/models/job/order_processor.rb +4 -3
- data/app/models/kit.rb +18 -0
- data/app/models/kits/regular_donation_kit.rb +51 -9
- data/app/models/member.rb +4 -3
- data/app/models/membership.rb +1 -2
- data/app/models/membership_sale_search.rb +2 -20
- data/app/models/membership_type.rb +1 -2
- data/app/models/order.rb +29 -1
- data/app/models/order_handler.rb +4 -3
- data/app/models/organization.rb +17 -9
- data/app/models/pass.rb +18 -17
- data/app/models/pass_sale_search.rb +18 -0
- data/app/models/pass_type.rb +7 -2
- data/app/models/passes_report.rb +117 -0
- data/app/models/sale_search.rb +3 -21
- data/app/models/user.rb +1 -2
- data/app/serializers/show_serializer.rb +15 -0
- data/app/views/contributions/index.html.haml +2 -2
- data/app/views/events/_header.html.haml +1 -0
- data/app/views/events/_ticket_type_fields.html.haml +31 -15
- data/app/views/events/edit.html.haml +6 -1
- data/app/views/imports/_export_links.html.haml +3 -0
- data/app/views/imports/donations/_pending.html.haml +24 -6
- data/app/views/imports/events/_pending.html.haml +16 -5
- data/app/views/imports/index.html.haml +6 -4
- data/app/views/imports/people/_pending.html.haml +20 -25
- data/app/views/layouts/storefront.html.haml +9 -5
- data/app/views/order_mailer/confirmation_for.html.haml +11 -5
- data/app/views/order_mailer/confirmation_for_refund.html.haml +6 -2
- data/app/views/order_mailer/confirmation_for_refund.text.haml +4 -3
- data/app/views/orders/membership.html.haml +15 -19
- data/app/views/orders/passes.html.haml +97 -0
- data/app/views/orders/sales.html.haml +2 -2
- data/app/views/organizations/_form.html.haml +4 -37
- data/app/views/pass_types/index.html.haml +1 -1
- data/app/views/passes_reports/index.html.haml +79 -1
- data/app/views/people/show.html.haml +16 -15
- data/app/views/regular_donation_kits/edit.html.haml +140 -0
- data/app/views/store/checkouts/thanks.html.haml +14 -6
- data/app/views/store/donations/index.html.haml +19 -2
- data/app/views/store/events/calendar.html.haml +1 -0
- data/app/views/store/events/index.html.haml +1 -0
- data/app/views/store/events/show.html.haml +1 -0
- data/app/views/store/events/single_show.html.haml +2 -0
- data/app/views/store/orders/show.html.haml +10 -10
- data/app/views/store/passes/index.html.haml +2 -2
- data/app/views/store/shared/_donate_form.html.haml +31 -12
- data/app/views/store/shared/_small_donate_form.html.haml +22 -0
- data/app/views/users/invitations/edit.html.haml +3 -42
- data/config/locales/en.yml +3 -1
- data/config/routes.rb +4 -1
- data/db/migrate/20140603200735_add_subtitle_to_events.rb +5 -0
- data/lib/artfully_ose/version.rb +1 -1
- data/spec/factories/kit_factories.rb +5 -4
- metadata +25 -13
- data/app/assets/javascripts/box-office.js +0 -262
data/app/models/member.rb
CHANGED
@@ -169,9 +169,10 @@ class Member < ActiveRecord::Base
|
|
169
169
|
end
|
170
170
|
|
171
171
|
def generate_pdf
|
172
|
-
|
173
|
-
|
174
|
-
|
172
|
+
pdf = PdfGeneration.new(self).generate
|
173
|
+
file = Tempfile.new(["#{self.id}", '.pdf'])
|
174
|
+
file << pdf.force_encoding("UTF-8")
|
175
|
+
self.pdf = file
|
175
176
|
self.save
|
176
177
|
end
|
177
178
|
|
data/app/models/membership.rb
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
#
|
5
5
|
class Membership < ActiveRecord::Base
|
6
6
|
include Extendable
|
7
|
-
extend ActionView::Helpers::TextHelper
|
8
7
|
|
9
8
|
belongs_to :organization
|
10
9
|
belongs_to :member
|
@@ -72,7 +71,7 @@ class Membership < ActiveRecord::Base
|
|
72
71
|
if membership_types.length > 1
|
73
72
|
"multiple memberships"
|
74
73
|
else
|
75
|
-
pluralize(memberships.length, "#{memberships.first.membership_type.membershipize}")
|
74
|
+
ActionController::Base.helpers.pluralize(memberships.length, "#{memberships.first.membership_type.membershipize}")
|
76
75
|
end
|
77
76
|
else
|
78
77
|
"No memberships"
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class MembershipSaleSearch
|
2
|
+
include SearchByDates
|
2
3
|
|
3
|
-
attr_reader :
|
4
|
-
attr_reader :organization, :membership_type
|
4
|
+
attr_reader :membership_type
|
5
5
|
|
6
6
|
def initialize(terms)
|
7
7
|
@organization = terms[:organization]
|
@@ -15,22 +15,4 @@ class MembershipSaleSearch
|
|
15
15
|
def results
|
16
16
|
@results ||= Order.membership_sale_search(self).select(&:has_membership?)
|
17
17
|
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def start_with(start)
|
22
|
-
start.present? ? DateTime.parse(start) : default_start
|
23
|
-
end
|
24
|
-
|
25
|
-
def stop_with(stop)
|
26
|
-
stop.present? ? Sundial.midnightish(@organization, stop) : default_stop
|
27
|
-
end
|
28
|
-
|
29
|
-
def default_start
|
30
|
-
DateTime.now.in_time_zone(@organization.time_zone).beginning_of_month
|
31
|
-
end
|
32
|
-
|
33
|
-
def default_stop
|
34
|
-
DateTime.now.in_time_zone(@organization.time_zone).end_of_day
|
35
|
-
end
|
36
18
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
class MembershipType < ActiveRecord::Base
|
2
|
+
include Ext::Integrations::ServiceFee
|
2
3
|
extend ::ArtfullyOseHelper
|
3
4
|
attr_accessible :name, :price, :fee, :number_of_shows,
|
4
5
|
:plan, :on_sale, :description, :ends_at,
|
@@ -20,8 +21,6 @@ class MembershipType < ActiveRecord::Base
|
|
20
21
|
scope :sales_valid, where("sales_start_at < ? or sales_start_at is null", DateTime.now).where("sales_end_at > ? or sales_end_at is null", DateTime.now)
|
21
22
|
scope :not_ended, where('ends_at > ?', DateTime.now)
|
22
23
|
|
23
|
-
SERVICE_FEE = 0.05
|
24
|
-
|
25
24
|
comma do
|
26
25
|
name
|
27
26
|
description
|
data/app/models/order.rb
CHANGED
@@ -121,6 +121,10 @@ class Order < ActiveRecord::Base
|
|
121
121
|
def discount_codes
|
122
122
|
tickets.collect(&:discount).uniq.compact
|
123
123
|
end
|
124
|
+
|
125
|
+
def pass_codes
|
126
|
+
tickets.collect(&:pass).uniq.compact
|
127
|
+
end
|
124
128
|
|
125
129
|
def imported?
|
126
130
|
!self.import_id.nil?
|
@@ -256,7 +260,6 @@ class Order < ActiveRecord::Base
|
|
256
260
|
|
257
261
|
def self.membership_sale_search(search)
|
258
262
|
standard = ::Order.
|
259
|
-
where("orders.type != ?", ::Reseller::Order.name).
|
260
263
|
joins(:items).
|
261
264
|
joins("INNER JOIN memberships ON (items.product_type = #{::Item.sanitize('Membership')} AND items.product_id = memberships.id)").
|
262
265
|
joins("INNER JOIN membership_types ON (membership_types.id = memberships.membership_type_id)").
|
@@ -270,6 +273,23 @@ class Order < ActiveRecord::Base
|
|
270
273
|
standard = standard.where("membership_types.id = ?", search.membership_type.id) if search.membership_type if search.membership_type
|
271
274
|
standard.all
|
272
275
|
end
|
276
|
+
|
277
|
+
def self.pass_sale_search(search)
|
278
|
+
standard = ::Order.
|
279
|
+
where("orders.type != ?", ::Reseller::Order.name).
|
280
|
+
joins(:items).
|
281
|
+
joins("INNER JOIN passes ON (items.product_type = #{::Item.sanitize('Pass')} AND items.product_id = passes.id)").
|
282
|
+
joins("INNER JOIN pass_types ON (pass_types.id = passes.pass_type_id)").
|
283
|
+
includes([:organization, :person]).
|
284
|
+
group('orders.id')
|
285
|
+
|
286
|
+
|
287
|
+
standard = standard.after(search.start) if search.start
|
288
|
+
standard = standard.before(search.stop) if search.stop
|
289
|
+
standard = standard.where('orders.organization_id = ?', search.organization.id) if search.organization
|
290
|
+
standard = standard.where("pass_types.id = ?", search.pass_type.id) if search.pass_type if search.pass_type
|
291
|
+
standard.all
|
292
|
+
end
|
273
293
|
|
274
294
|
def has_single_donation?
|
275
295
|
(donations.size == 1) && tickets.empty?
|
@@ -308,6 +328,14 @@ class Order < ActiveRecord::Base
|
|
308
328
|
items.select(&:membership?).present?
|
309
329
|
end
|
310
330
|
|
331
|
+
def has_pass?
|
332
|
+
items.select(&:pass?).present?
|
333
|
+
end
|
334
|
+
|
335
|
+
def items_that_used_pass
|
336
|
+
items.select{ |i| i.pass_id.present? }
|
337
|
+
end
|
338
|
+
|
311
339
|
def sum_donations
|
312
340
|
all_donations.collect{|item| item.total_price.to_i}.sum
|
313
341
|
end
|
data/app/models/order_handler.rb
CHANGED
@@ -41,15 +41,16 @@ class OrderHandler
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def handle_donation(params, organization)
|
44
|
-
|
44
|
+
donation_amount = params[:donation_amount].present? ? params[:donation_amount] : params[:donation_amount_fixed]
|
45
|
+
if donation_amount
|
45
46
|
self.cart.clear_donations
|
46
|
-
if
|
47
|
+
if donation_amount.to_i == 0
|
47
48
|
self.error = "Please enter a donation amount."
|
48
49
|
return
|
49
50
|
end
|
50
51
|
|
51
52
|
donation = Donation.new
|
52
|
-
donation.amount =
|
53
|
+
donation.amount = donation_amount.to_i * 100
|
53
54
|
donation.organization = organization
|
54
55
|
self.cart.donations << donation
|
55
56
|
end
|
data/app/models/organization.rb
CHANGED
@@ -34,14 +34,10 @@ class Organization < ActiveRecord::Base
|
|
34
34
|
|
35
35
|
has_many :imports
|
36
36
|
|
37
|
-
validates_presence_of :name, :email, :time_zone, :country, :state, :zip, :phone_number, :discipline
|
38
|
-
validates :ein, :presence => true, :if => :updating_tax_info
|
39
|
-
validates :legal_organization_name, :presence => true, :if => :updating_tax_info
|
40
|
-
|
41
37
|
scope :receiving_sales_email, where(:receive_daily_sales_report => true)
|
42
38
|
|
43
39
|
is_sluggable :name, :editable => true
|
44
|
-
validates :cached_slug,
|
40
|
+
validates :cached_slug,
|
45
41
|
:exclusion => { :in => %w(orders tickets admin artfully statements settlements search searches store event events show shows reseller members mobile member resellers merges people phones refunds exchanges),
|
46
42
|
:message => "we can't set your Storefront URL to \"%{value}\"" },
|
47
43
|
:uniqueness => { :message => "\"%{value}\" has already been taken"}
|
@@ -142,9 +138,11 @@ class Organization < ActiveRecord::Base
|
|
142
138
|
end
|
143
139
|
|
144
140
|
def authorization_hash
|
145
|
-
{
|
141
|
+
{
|
142
|
+
:authorized => can?(:receive, Donation),
|
146
143
|
:type => donation_type,
|
147
|
-
:fsp_name => name_for_donations
|
144
|
+
:fsp_name => name_for_donations
|
145
|
+
}
|
148
146
|
end
|
149
147
|
|
150
148
|
def donations
|
@@ -155,14 +153,24 @@ class Organization < ActiveRecord::Base
|
|
155
153
|
Item.includes(:show => [:event => :venue], :order => [:person => :address]).where(:product_type => "Ticket", :orders => { :organization_id => id })
|
156
154
|
end
|
157
155
|
|
156
|
+
def kit(name)
|
157
|
+
name = "#{name.to_s.camelize.gsub(/Kit$/, '')}Kit"
|
158
|
+
kits.select { |k| k.class.name == name }.first
|
159
|
+
end
|
160
|
+
|
158
161
|
def has_kit?(name)
|
159
|
-
|
162
|
+
name = "#{name.to_s.camelize.gsub(/Kit$/, '')}Kit"
|
163
|
+
kits.select { |k| k.class.name == name && k.activated? }.any?
|
160
164
|
end
|
161
165
|
|
162
166
|
def individuals
|
163
167
|
people.where(:type => 'Individual')
|
164
168
|
end
|
165
169
|
|
170
|
+
def has_active_donation_only_storefront?
|
171
|
+
self.has_kit?(:regular_donation) && self.kit(:regular_donation).donation_only_storefront == '1'
|
172
|
+
end
|
173
|
+
|
166
174
|
private
|
167
175
|
|
168
176
|
def check_for_duplicates(kit)
|
@@ -170,7 +178,7 @@ class Organization < ActiveRecord::Base
|
|
170
178
|
end
|
171
179
|
|
172
180
|
def donation_type
|
173
|
-
return :sponsored if kits.where(:type => "SponsoredDonationKit").any? && kits.where(:type => "SponsoredDonationKit").first.activated?
|
174
181
|
return :regular if kits.where(:type => "RegularDonationKit").any? && kits.where(:type => "RegularDonationKit").first.activated?
|
182
|
+
return :sponsored if kits.where(:type => "SponsoredDonationKit").any? && kits.where(:type => "SponsoredDonationKit").first.activated?
|
175
183
|
end
|
176
184
|
end
|
data/app/models/pass.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
class Pass < ActiveRecord::Base
|
2
2
|
include Extendable
|
3
|
-
include ActionView::Helpers::TextHelper
|
4
3
|
|
5
4
|
belongs_to :pass_type
|
6
5
|
belongs_to :person
|
@@ -19,6 +18,7 @@ class Pass < ActiveRecord::Base
|
|
19
18
|
|
20
19
|
scope :not_expired, lambda { |time = DateTime.now| where("#{Pass.table_name}.ends_at > ?", time) }
|
21
20
|
scope :expired, lambda { |time = DateTime.now| where("#{Pass.table_name}.ends_at < ?", time) }
|
21
|
+
scope :owned, where('person_id is not null')
|
22
22
|
|
23
23
|
def self.for(pass_type)
|
24
24
|
new.tap do |pass|
|
@@ -31,7 +31,7 @@ class Pass < ActiveRecord::Base
|
|
31
31
|
pass.starts_at = pass_type.starts_at
|
32
32
|
pass.ends_at = pass_type.ends_at
|
33
33
|
end
|
34
|
-
end
|
34
|
+
end
|
35
35
|
|
36
36
|
def adjust_ends_at
|
37
37
|
self.ends_at = self.ends_at.end_of_day unless self.ends_at.blank?
|
@@ -96,7 +96,7 @@ class Pass < ActiveRecord::Base
|
|
96
96
|
if pass_types.length > 1
|
97
97
|
"multiple passes"
|
98
98
|
else
|
99
|
-
pluralize(passes.length, "#{passes.first.pass_type.passerize}")
|
99
|
+
ActionController::Base.helpers.pluralize(passes.length, "#{passes.first.pass_type.passerize}")
|
100
100
|
end
|
101
101
|
else
|
102
102
|
"No passes"
|
@@ -109,25 +109,22 @@ class Pass < ActiveRecord::Base
|
|
109
109
|
.where(:event_id => event.id)
|
110
110
|
.where(:pass_type_id => self.pass_type_id).first
|
111
111
|
|
112
|
-
Rails.logger.debug ("PASSES the ept is [#{@ept.inspect}]")
|
113
|
-
|
114
112
|
return 0 if @ept.nil?
|
115
113
|
return self.tickets_allowed if @ept.limit_per_pass.nil?
|
116
|
-
|
117
|
-
Rails.logger.debug ("PASSES tickets remaining to this event [#{@ept.limit_per_pass - event.tickets.where(:pass_id => self.id).count}]")
|
118
|
-
|
119
114
|
return @ept.limit_per_pass - event.tickets.where(:pass_id => self.id).count
|
120
115
|
end
|
121
116
|
|
122
117
|
def apply_pass_to_cart(cart)
|
118
|
+
Rails.logger.debug ("PASSES Applying pass [#{self.id}] to cart [#{cart.id}]")
|
123
119
|
cart.prepare_for_pass!
|
124
120
|
|
125
121
|
#the number of tickets remining for this pass. We go straight to the DB here
|
126
122
|
#to avoid a race condition on pass.tickets_remaining
|
127
|
-
|
123
|
+
tickets_remaining_on_pass = self.tickets_allowed - tickets_purchased
|
124
|
+
Rails.logger.debug ("PASSES [#{tickets_remaining_on_pass}] tickets remaining on this pass")
|
128
125
|
|
129
126
|
self.errors.add(:base, EXPIRED_ERROR) and return if self.expired?
|
130
|
-
self.errors.add(:base, OUT_OF_TICKETS) and return if
|
127
|
+
self.errors.add(:base, OUT_OF_TICKETS) and return if tickets_remaining_on_pass < 1
|
131
128
|
|
132
129
|
#The list of tickets w'eve applied this pass too
|
133
130
|
tickets_applied = []
|
@@ -148,20 +145,24 @@ class Pass < ActiveRecord::Base
|
|
148
145
|
(pass_errors << TICKET_TYPE_NOT_ELIGIBLE and next) unless self.applies_to? (ticket.ticket_type)
|
149
146
|
(pass_errors << ORG_ERROR and next) unless (self.organization == ticket.organization)
|
150
147
|
|
151
|
-
|
152
|
-
|
153
|
-
tickets_remaining_for_this_event
|
148
|
+
event = ticket.show.event
|
149
|
+
tickets_remaining_for_this_event = tickets_remaining_for(event)
|
150
|
+
Rails.logger.debug ("PASSES [#{tickets_remaining_for_this_event}] tickets remaining to event [#{event.id}]")
|
154
151
|
over_event_limit = true if tickets_remaining_for_this_event == 0
|
155
152
|
|
156
|
-
tickets_remaining = [
|
153
|
+
tickets_remaining = [tickets_remaining_on_pass, tickets_remaining_for_this_event].min
|
154
|
+
Rails.logger.debug ("PASSES [#{tickets_remaining}] tickets remaining after considering pass and event")
|
157
155
|
|
158
156
|
if tickets_remaining > 0
|
157
|
+
Rails.logger.debug ("PASSES adding pass [#{self.id}] to ticket [#{ticket.id}]")
|
159
158
|
ticket.pass = self
|
160
159
|
ticket.cart_price = 0
|
161
160
|
ticket.save
|
162
161
|
tickets_applied << ticket
|
163
|
-
|
162
|
+
tickets_remaining_on_pass = tickets_remaining_on_pass - 1
|
163
|
+
Rails.logger.debug ("PASSES Now only [#{tickets_remaining_on_pass}] tickets remaining on this pass")
|
164
164
|
else
|
165
|
+
Rails.logger.debug ("PASSES rejecting ticket [#{ticket.id}]")
|
165
166
|
tickets_rejected << tickets
|
166
167
|
end
|
167
168
|
end
|
@@ -171,7 +172,7 @@ class Pass < ActiveRecord::Base
|
|
171
172
|
if tickets_applied.length == 0
|
172
173
|
str += "There are no tickets"
|
173
174
|
else
|
174
|
-
str += "Only #{pluralize(tickets_applied.length, 'ticket')}"
|
175
|
+
str += "Only #{ActionController::Base.helpers.pluralize(tickets_applied.length, 'ticket')}"
|
175
176
|
str += tickets_applied.length > 1 ? " were" : " was"
|
176
177
|
end
|
177
178
|
|
@@ -179,7 +180,7 @@ class Pass < ActiveRecord::Base
|
|
179
180
|
str += " for this event" if over_event_limit
|
180
181
|
|
181
182
|
if tickets_applied.length > 0
|
182
|
-
str += ". We've applied your pass to #{pluralize(tickets_applied.length, 'ticket')} and left the others in your cart."
|
183
|
+
str += ". We've applied your pass to #{ActionController::Base.helpers.pluralize(tickets_applied.length, 'ticket')} and left the others in your cart."
|
183
184
|
end
|
184
185
|
|
185
186
|
pass_errors << str
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class PassSaleSearch
|
2
|
+
include SearchByDates
|
3
|
+
|
4
|
+
attr_reader :pass_type
|
5
|
+
|
6
|
+
def initialize(terms)
|
7
|
+
@organization = terms[:organization]
|
8
|
+
@pass_type = terms[:pass_type]
|
9
|
+
@start = start_with(terms[:start])
|
10
|
+
@stop = stop_with(terms[:stop])
|
11
|
+
|
12
|
+
@results = yield(results) if block_given?
|
13
|
+
end
|
14
|
+
|
15
|
+
def results
|
16
|
+
@results ||= Order.pass_sale_search(self).select(&:has_pass?)
|
17
|
+
end
|
18
|
+
end
|
data/app/models/pass_type.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
class PassType < ActiveRecord::Base
|
2
|
+
include Ext::Integrations::ServiceFee
|
2
3
|
include OhNoes::Destroy
|
3
4
|
extend ::ArtfullyOseHelper
|
4
5
|
|
@@ -8,13 +9,13 @@ class PassType < ActiveRecord::Base
|
|
8
9
|
attr_accessible :name, :description, :hide_fee, :thanks_copy, :email_copy, :sales_start_at, :sales_end_at,
|
9
10
|
:on_sale, :price, :tickets_allowed, :starts_at, :ends_at
|
10
11
|
|
11
|
-
validates :name, :description, :price, :tickets_allowed, :presence => true
|
12
|
+
validates :name, :description, :price, :tickets_allowed, :starts_at, :ends_at, :presence => true
|
12
13
|
|
13
14
|
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)
|
14
15
|
scope :on_sale, where(:on_sale => true)
|
15
16
|
scope :not_ended, where('ends_at > ?', DateTime.now)
|
16
17
|
|
17
|
-
|
18
|
+
ALL_PASSES_STRING = "ALL PASSES"
|
18
19
|
|
19
20
|
comma do
|
20
21
|
name
|
@@ -30,6 +31,10 @@ class PassType < ActiveRecord::Base
|
|
30
31
|
sales_start_at
|
31
32
|
sales_end_at
|
32
33
|
end
|
34
|
+
|
35
|
+
def sold
|
36
|
+
self.passes.select{ |p| p.person_id.present? }
|
37
|
+
end
|
33
38
|
|
34
39
|
def passerize
|
35
40
|
self.name.end_with?("Pass") ? self.name : self.name + " Pass"
|
@@ -0,0 +1,117 @@
|
|
1
|
+
class PassesReport
|
2
|
+
attr_accessor :pass_type, :header, :start_date, :end_date, :rows, :counts
|
3
|
+
attr_accessor :tickets_sold, :passes_sold, :total_tickets, :tickets_remaining, :original_price, :discounted
|
4
|
+
extend ::ArtfullyOseHelper
|
5
|
+
|
6
|
+
def pass_type_name
|
7
|
+
pass_type.try(:name) || PassType::ALL_PASSES_STRING
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(organization, pass_type, start_date, end_date)
|
11
|
+
self.pass_type = pass_type
|
12
|
+
self.start_date = start_date
|
13
|
+
self.end_date = end_date
|
14
|
+
|
15
|
+
@orders = find_orders
|
16
|
+
|
17
|
+
self.rows = []
|
18
|
+
@orders.each do |order|
|
19
|
+
self.rows << Row.new(order)
|
20
|
+
end
|
21
|
+
|
22
|
+
build_header
|
23
|
+
|
24
|
+
self.passes_sold = count_passes_sold
|
25
|
+
self.total_tickets = calculate_total_tickets
|
26
|
+
self.tickets_sold = self.rows.inject(0) { |total, row| total + row.ticket_count}
|
27
|
+
self.tickets_remaining= self.total_tickets - self.tickets_sold
|
28
|
+
self.original_price = self.rows.inject(0) { |total, row| total + row.original_price }
|
29
|
+
self.discounted = self.rows.inject(0) { |total, row| total + row.discounted }
|
30
|
+
end
|
31
|
+
|
32
|
+
def calculate_total_tickets
|
33
|
+
@passes = Pass.owned
|
34
|
+
if self.pass_type.present?
|
35
|
+
@passes = @passes.where(:pass_type_id => self.pass_type.id)
|
36
|
+
end
|
37
|
+
|
38
|
+
@passes.sum(:tickets_allowed)
|
39
|
+
end
|
40
|
+
|
41
|
+
def count_passes_sold
|
42
|
+
@items = Item.sold_or_comped.where(:product_type => "Pass")
|
43
|
+
@items = @items.joins("INNER join passes ON items.product_id = passes.id")
|
44
|
+
@items = @items.joins("INNER join pass_types ON passes.pass_type_id = pass_types.id")
|
45
|
+
|
46
|
+
if self.pass_type.present?
|
47
|
+
@items = @items.where("pass_types.id" => self.pass_type)
|
48
|
+
end
|
49
|
+
|
50
|
+
@items = @items.joins(:order)
|
51
|
+
@items = @items.where('orders.created_at > ?',self.start_date) unless start_date.blank?
|
52
|
+
@items = @items.where('orders.created_at < ?',self.end_date) unless end_date.blank?
|
53
|
+
@items.count
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_orders
|
57
|
+
@orders = Order.includes(:person, :items => [:show => :event])
|
58
|
+
.joins(:items)
|
59
|
+
.joins("INNER join passes ON items.pass_id = passes.id")
|
60
|
+
.joins("INNER join pass_types ON passes.pass_type_id = pass_types.id")
|
61
|
+
.group('orders.id')
|
62
|
+
.order('orders.created_at desc')
|
63
|
+
|
64
|
+
if pass_type.nil?
|
65
|
+
@orders = @orders.where("pass_types.id is not null")
|
66
|
+
else
|
67
|
+
@orders = @orders.where("pass_types.id" => self.pass_type.id)
|
68
|
+
end
|
69
|
+
|
70
|
+
@orders = @orders.where('orders.created_at > ?',self.start_date) unless start_date.blank?
|
71
|
+
@orders = @orders.where('orders.created_at < ?',self.end_date) unless end_date.blank?
|
72
|
+
@orders
|
73
|
+
end
|
74
|
+
|
75
|
+
def build_header
|
76
|
+
self.header = pass_type_name
|
77
|
+
if self.start_date.blank? && self.end_date.blank?
|
78
|
+
return
|
79
|
+
elsif self.start_date.blank?
|
80
|
+
self.header = self.header + " through #{I18n.localize(DateTime.parse(self.end_date), :format => :slashed_date)}"
|
81
|
+
elsif self.end_date.blank?
|
82
|
+
self.header = self.header + " since #{I18n.localize(DateTime.parse(self.start_date), :format => :slashed_date)}"
|
83
|
+
else
|
84
|
+
self.header = self.header + " from #{I18n.localize(DateTime.parse(self.start_date), :format => :slashed_date)} through #{I18n.localize(DateTime.parse(self.end_date), :format => :slashed_date)}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Row
|
89
|
+
attr_accessor :order, :show, :pass_type, :ticket_count, :original_price, :gross, :discounted
|
90
|
+
|
91
|
+
def initialize(order)
|
92
|
+
self.order = order
|
93
|
+
self.pass_type = order.pass_codes.first.pass_type.name
|
94
|
+
self.show = order.items_that_used_pass.first.show
|
95
|
+
self.original_price = order.items_that_used_pass.inject(0) { |total, item| total + item.original_price }
|
96
|
+
self.gross = order.items_that_used_pass.inject(0) { |total, item| total + item.price }
|
97
|
+
self.discounted = self.original_price - self.gross
|
98
|
+
self.ticket_count = order.items_that_used_pass.length
|
99
|
+
self.ticket_count = self.ticket_count * -1 if !order.items_that_used_pass.select(&:refund?).empty?
|
100
|
+
end
|
101
|
+
|
102
|
+
comma do
|
103
|
+
pass_type("Pass")
|
104
|
+
order("Order") { |order| order.id }
|
105
|
+
order("Order Date") { |order| order.created_at }
|
106
|
+
order("First Name") { |order| order.person.first_name }
|
107
|
+
order("Last Name") { |order| order.person.last_name }
|
108
|
+
order("Email") { |order| order.person.email }
|
109
|
+
show("Event") { |show| show.event.name }
|
110
|
+
ticket_count
|
111
|
+
original_price { |original_price| DiscountsReport.number_as_cents original_price }
|
112
|
+
discounted { |discounted| DiscountsReport.number_as_cents discounted }
|
113
|
+
gross { |gross| DiscountsReport.number_as_cents gross }
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|