artfully_ose 1.2.0.pre.21 → 1.2.0.pre.23
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/change-membership.js +8 -4
- data/app/assets/javascripts/custom/show.js +13 -8
- data/app/assets/javascripts/locationselector.js +1 -1
- data/app/assets/javascripts/store/store.js +7 -0
- data/app/assets/stylesheets/application.sass +21 -1
- data/app/assets/stylesheets/sass/_tags.sass +13 -2
- data/app/controllers/discounts_controller.rb +8 -1
- data/app/controllers/events_controller.rb +29 -13
- data/app/controllers/events_pass_types_controller.rb +75 -0
- data/app/controllers/imports_controller.rb +10 -3
- data/app/controllers/membership_types_controller.rb +1 -0
- data/app/controllers/pass_types_controller.rb +42 -0
- data/app/controllers/passes_controller.rb +13 -0
- data/app/controllers/passes_kits_controller.rb +25 -0
- data/app/controllers/passes_reports_controller.rb +5 -0
- data/app/controllers/sales_controller.rb +0 -2
- data/app/controllers/searches_controller.rb +10 -3
- data/app/controllers/sections_controller.rb +1 -1
- data/app/controllers/segments_controller.rb +4 -4
- data/app/controllers/shows_controller.rb +21 -47
- data/app/controllers/store/checkouts_controller.rb +6 -1
- data/app/controllers/store/orders_controller.rb +2 -0
- data/app/controllers/store/passes_controller.rb +9 -0
- data/app/controllers/store/store_controller.rb +7 -2
- data/app/helpers/link_helper.rb +10 -0
- data/app/mailers/reports_mailer.rb +5 -4
- data/app/models/cart.rb +21 -16
- data/app/models/checkout.rb +6 -1
- data/app/models/daily_membership_report.rb +1 -1
- data/app/models/daily_pass_report.rb +48 -0
- data/app/models/database_views/item_view.rb +69 -19
- data/app/models/event.rb +5 -0
- data/app/models/events_pass_type.rb +16 -0
- data/app/models/ext/integrations.rb +1 -1
- data/app/models/ext/preprocessor.rb +1 -0
- data/app/models/import.rb +18 -15
- data/app/models/imports/donations_import.rb +1 -1
- data/app/models/imports/events_import.rb +33 -28
- data/app/models/item.rb +13 -3
- data/app/models/job/daily_email_report_job.rb +8 -2
- data/app/models/job/order_processor.rb +10 -1
- data/app/models/job/show_creator.rb +2 -1
- data/app/models/kit.rb +1 -1
- data/app/models/kits/passes_kit.rb +62 -0
- data/app/models/membership.rb +19 -6
- data/app/models/membership_change.rb +18 -7
- data/app/models/membership_comp.rb +1 -0
- data/app/models/membership_type.rb +1 -1
- data/app/models/order.rb +18 -9
- data/app/models/order_handler.rb +22 -0
- data/app/models/organization.rb +7 -0
- data/app/models/pass.rb +45 -0
- data/app/models/pass_type.rb +19 -0
- data/app/models/person.rb +4 -0
- data/app/models/search.rb +170 -63
- data/app/models/section.rb +2 -0
- data/app/models/show.rb +26 -2
- data/app/models/show_touch.rb +12 -0
- data/app/models/ticket.rb +6 -0
- data/app/models/ticket/pricing.rb +1 -0
- data/app/models/ticket/reports.rb +16 -0
- data/app/models/ticket/sale_transitions.rb +4 -0
- data/app/models/ticket/transfers.rb +4 -1
- data/app/presenters/event_presenter.rb +1 -1
- data/app/views/discounts/_form.html.haml +1 -1
- data/app/views/events/_menu.html.haml +4 -13
- data/app/views/events/_share_and_sell.haml +2 -1
- data/app/views/events_pass_types/_form.html.haml +25 -0
- data/app/views/events_pass_types/edit.html.haml +22 -0
- data/app/views/events_pass_types/index.html.haml +43 -0
- data/app/views/events_pass_types/new.html.haml +22 -0
- data/app/views/imports/index.html.haml +2 -2
- data/app/views/layouts/_menu.html.haml +2 -1
- data/app/views/layouts/storefront.html.haml +3 -1
- data/app/views/membership_cancellations/_form.html.haml +1 -1
- data/app/views/membership_cancellations/_processing.html.haml +1 -3
- data/app/views/membership_types/index.html.haml +1 -1
- data/app/views/memberships/index.html.haml +16 -25
- data/app/views/orders/_item_table.haml +2 -2
- data/app/views/pass_types/_form.html.haml +70 -0
- data/app/views/pass_types/_pass_type_fees.html.haml +48 -0
- data/app/views/pass_types/edit.html.haml +4 -0
- data/app/views/pass_types/index.html.haml +32 -0
- data/app/views/pass_types/new.html.haml +4 -0
- data/app/views/passes/index.html.haml +40 -0
- data/app/views/passes_kits/edit.html.haml +30 -0
- data/app/views/passes_reports/index.html.haml +2 -0
- data/app/views/people/_header.html.haml +6 -1
- data/app/views/reports_mailer/daily.html.haml +19 -0
- data/app/views/searches/_form.html.haml +17 -1
- data/app/views/shared/_show_time_and_calendar.html.haml +21 -0
- data/app/views/shared/_tags.html.haml +2 -2
- data/app/views/shows/_controls.html.haml +4 -4
- data/app/views/shows/_glance.html.haml +0 -4
- data/app/views/shows/_sections_table.html.haml +6 -4
- data/app/views/shows/_work_with.html.haml +2 -2
- data/app/views/shows/index.html.haml +79 -20
- data/app/views/shows/new.html.haml +1 -21
- data/app/views/statements/_passes_table.html.haml +25 -0
- data/app/views/statements/show.html.haml +4 -1
- data/app/views/store/checkouts/thanks.html.haml +13 -5
- data/app/views/store/orders/show.html.haml +26 -1
- data/app/views/store/passes/index.html.haml +33 -0
- data/config/locales/en.yml +2 -0
- data/config/routes.rb +8 -2
- data/db/migrate/20140207135731_update_items_view.rb +38 -0
- data/db/migrate/20140210154723_add_cached_stats.rb +5 -0
- data/db/migrate/20140218202726_cache_stats.rb +7 -0
- data/db/migrate/20140304171625_passes_ahoy.rb +52 -0
- data/db/migrate/20140304174807_add_passes_kit.rb +5 -0
- data/db/migrate/20140307144454_add_events_pass_types.rb +9 -0
- data/db/migrate/20140307193350_add_pass_id_to_cart.rb +7 -0
- data/db/migrate/20140314162422_add_total_paid_to_membership.rb +13 -0
- data/db/migrate/20140319191237_add_show_dates_to_advanced_search.rb +6 -0
- data/db/migrate/20140328172333_add_cols_to_events_pass_types.rb +6 -0
- data/db/migrate/20140328174217_add_deleted_at_to_ept.rb +9 -0
- data/db/migrate/20140328185432_add_active_to_ept.rb +5 -0
- data/db/migrate/20140328192612_add_pass_type_id_to_search.rb +5 -0
- data/lib/artfully_ose.rb +3 -3
- data/lib/artfully_ose/version.rb +1 -1
- data/spec/factories/kit_factories.rb +5 -0
- data/spec/factories/membership_factories.rb +1 -0
- metadata +45 -6
@@ -28,6 +28,7 @@ class OrderProcessor < Struct.new(:order, :options)
|
|
28
28
|
self.order.create_donation_actions unless skip_actions
|
29
29
|
self.order.create_purchase_action unless skip_actions
|
30
30
|
process_memberships
|
31
|
+
process_passes
|
31
32
|
end
|
32
33
|
|
33
34
|
begin
|
@@ -60,7 +61,15 @@ class OrderProcessor < Struct.new(:order, :options)
|
|
60
61
|
self.order.memberships.each do |membership_item|
|
61
62
|
Member.for(membership_item.product, self.order.person)
|
62
63
|
end
|
63
|
-
self.order.
|
64
|
+
self.order.create_generic_action("memberships")
|
65
|
+
end
|
66
|
+
|
67
|
+
def process_passes
|
68
|
+
self.order.passes.each do |pass_item|
|
69
|
+
pass_item.product.person = self.order.person
|
70
|
+
pass_item.product.save
|
71
|
+
end
|
72
|
+
self.order.create_generic_action("passes")
|
64
73
|
end
|
65
74
|
|
66
75
|
def generate_pdf
|
@@ -26,7 +26,8 @@ class ShowCreator < Struct.new(:datetimes, :show_params, :chart_params, :event,
|
|
26
26
|
@show.chart_id = @show.chart.id
|
27
27
|
|
28
28
|
@show.datetime = DateTime.parse(datetime_string).change(:offset => offset(datetime_string, event.time_zone))
|
29
|
-
@show.go!(publish)
|
29
|
+
@show.go!(publish)
|
30
|
+
@show.refresh_stats
|
30
31
|
end
|
31
32
|
end
|
32
33
|
end
|
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, MembershipKit, ScannableTicketsKit ].freeze
|
64
|
+
@subklasses ||= [ TicketingKit, RegularDonationKit, SponsoredDonationKit, ResellerKit, MailchimpKit, MembershipKit, PassesKit, ScannableTicketsKit ].freeze
|
65
65
|
end
|
66
66
|
|
67
67
|
def self.pad_with_new_kits(kits = [])
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class PassesKit < 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, :passes
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
before_save :initialize_accessors
|
19
|
+
before_save :sanitize_accessors
|
20
|
+
|
21
|
+
ACCESSORS = [ :marketing_copy_heading, :marketing_copy_sidebar ]
|
22
|
+
|
23
|
+
ACCESSORS.each do |accessor|
|
24
|
+
attr_accessible accessor
|
25
|
+
end
|
26
|
+
|
27
|
+
store :settings, :accessors => ACCESSORS
|
28
|
+
|
29
|
+
def friendly_name
|
30
|
+
"Passes"
|
31
|
+
end
|
32
|
+
|
33
|
+
def no_bank_account?
|
34
|
+
errors.add(:requirements, "Your organization needs bank account information first.") if organization.bank_account.nil?
|
35
|
+
organization.bank_account.nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
def pitch
|
39
|
+
"Sell Passes!"
|
40
|
+
end
|
41
|
+
|
42
|
+
def configured?
|
43
|
+
passes_state == "configured"
|
44
|
+
end
|
45
|
+
|
46
|
+
def configured!
|
47
|
+
settings[:passes_state] = "configured"
|
48
|
+
save
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize_accessors
|
52
|
+
ACCESSORS.each do |accessor|
|
53
|
+
self.send("#{accessor}=", "") if self.send("#{accessor}").nil?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def sanitize_accessors
|
58
|
+
ACCESSORS.each do |accessor|
|
59
|
+
self.send("#{accessor}=", (sanitize self.send(accessor)))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/app/models/membership.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
#
|
2
|
+
# service_fee represents the fee displayed to the patron in the cart.
|
3
|
+
# If the producer is eating the fee, service_fee is 0
|
4
|
+
#
|
1
5
|
class Membership < ActiveRecord::Base
|
2
6
|
belongs_to :organization
|
3
7
|
belongs_to :member
|
@@ -18,17 +22,30 @@ class Membership < ActiveRecord::Base
|
|
18
22
|
scope :lapsed, lambda { |time = Time.now, since = (time - 1.year)| where("ends_at < ?", time).where("ends_at > ?", since.midnight) }
|
19
23
|
scope :past, lambda { |time = Time.now| where("ends_at < ?", time - 1.year) }
|
20
24
|
|
25
|
+
belongs_to :changed_membership, :class_name => "Membership"
|
26
|
+
has_one :changed_to, :class_name => "Membership", :foreign_key => "changed_membership_id"
|
27
|
+
|
21
28
|
def self.for(membership_type, member = nil)
|
22
29
|
new.tap do |membership|
|
23
30
|
membership.membership_type = membership_type
|
24
31
|
membership.organization = membership_type.organization
|
25
32
|
membership.price = membership_type.price_for(member)
|
33
|
+
membership.cart_price = membership.price
|
26
34
|
membership.sold_price = membership.price
|
35
|
+
membership.total_paid = membership.price
|
27
36
|
membership.starts_at = membership_type.starts_at
|
28
37
|
membership.ends_at = membership_type.ends_at
|
29
38
|
end
|
30
39
|
end
|
31
40
|
|
41
|
+
def changed?
|
42
|
+
self.changed_to.present?
|
43
|
+
end
|
44
|
+
|
45
|
+
def changee?
|
46
|
+
self.changed_membership.present?
|
47
|
+
end
|
48
|
+
|
32
49
|
def update_member_counters
|
33
50
|
self.member.try(:count_memberships)
|
34
51
|
end
|
@@ -42,16 +59,12 @@ class Membership < ActiveRecord::Base
|
|
42
59
|
items.first
|
43
60
|
end
|
44
61
|
|
45
|
-
def cart_price
|
46
|
-
price
|
47
|
-
end
|
48
|
-
|
49
62
|
def self.realized_fee
|
50
63
|
0
|
51
64
|
end
|
52
65
|
|
53
|
-
def realized_fee
|
54
|
-
self.membership_type.hide_fee? ? self.
|
66
|
+
def realized_fee
|
67
|
+
self.membership_type.hide_fee? ? self.cart_price * MembershipType::SERVICE_FEE : 0
|
55
68
|
end
|
56
69
|
|
57
70
|
def expired?
|
@@ -50,7 +50,7 @@ class MembershipChange
|
|
50
50
|
|
51
51
|
# Add the new memberships
|
52
52
|
new_memberships.each do |membership|
|
53
|
-
cart.memberships << membership
|
53
|
+
@cart.memberships << membership
|
54
54
|
end
|
55
55
|
end
|
56
56
|
@cart
|
@@ -91,12 +91,17 @@ class MembershipChange
|
|
91
91
|
@new_memberships = membership_ids.map do |old_membership_id|
|
92
92
|
old = old_memberships[old_membership_id]
|
93
93
|
|
94
|
-
|
95
|
-
membership
|
96
|
-
membership.
|
97
|
-
|
98
|
-
membership.price
|
99
|
-
membership.
|
94
|
+
membership = Membership.for(membership_type)
|
95
|
+
membership.starts_at = old.starts_at
|
96
|
+
membership.ends_at = old.ends_at
|
97
|
+
|
98
|
+
membership.price = membership.membership_type.price
|
99
|
+
membership.cart_price = price
|
100
|
+
membership.sold_price = price
|
101
|
+
membership.total_paid = old.total_paid + price
|
102
|
+
membership.member = person.member
|
103
|
+
|
104
|
+
membership.changed_membership = old
|
100
105
|
|
101
106
|
membership
|
102
107
|
end
|
@@ -169,8 +174,14 @@ class MembershipChange
|
|
169
174
|
end
|
170
175
|
|
171
176
|
class Order < ::Order
|
177
|
+
before_create :set_details
|
178
|
+
|
172
179
|
def membership_action_class
|
173
180
|
ChangeAction
|
174
181
|
end
|
182
|
+
|
183
|
+
def set_details
|
184
|
+
self.details = "Membership type change."
|
185
|
+
end
|
175
186
|
end
|
176
187
|
end
|
@@ -89,6 +89,7 @@ class MembershipComp
|
|
89
89
|
membership = Membership.for(self.membership_type)
|
90
90
|
membership.ends_at = self.ends_at
|
91
91
|
membership.sold_price = 0
|
92
|
+
membership.total_paid = 0
|
92
93
|
membership.welcome_message = self.welcome_message
|
93
94
|
membership.send_email = self.send_email
|
94
95
|
membership.save
|
data/app/models/order.rb
CHANGED
@@ -238,6 +238,10 @@ class Order < ActiveRecord::Base
|
|
238
238
|
items(reload).select(&:membership?)
|
239
239
|
end
|
240
240
|
|
241
|
+
def passes(reload=false)
|
242
|
+
items(reload).select(&:pass?)
|
243
|
+
end
|
244
|
+
|
241
245
|
def membership_types
|
242
246
|
memberships.collect{|item| item.product.membership_type}.uniq
|
243
247
|
end
|
@@ -374,7 +378,7 @@ class Order < ActiveRecord::Base
|
|
374
378
|
self.person.calculate_lifetime_donations
|
375
379
|
end
|
376
380
|
|
377
|
-
def
|
381
|
+
def action_class
|
378
382
|
GetAction
|
379
383
|
end
|
380
384
|
|
@@ -428,24 +432,29 @@ class Order < ActiveRecord::Base
|
|
428
432
|
end
|
429
433
|
end
|
430
434
|
|
431
|
-
|
432
|
-
|
433
|
-
|
435
|
+
#
|
436
|
+
# Creates actions for collection_name
|
437
|
+
#
|
438
|
+
# For example, if we're creating actions for the passses on this order, call order.create_generic_action('passes')
|
439
|
+
# This order must respond to .send(collection_name)
|
440
|
+
#
|
441
|
+
def create_generic_action(collection_name)
|
442
|
+
unless self.passes.empty?
|
443
|
+
action = self.action_class.new
|
434
444
|
action.person = self.person
|
435
445
|
action.subject = self
|
436
446
|
action.organization = self.organization
|
437
|
-
action.details =
|
447
|
+
action.details = action_details(self.send(collection_name).length, collection_name)
|
438
448
|
action.occurred_at = self.created_at
|
439
449
|
|
440
|
-
#Weird, but Rails can't initialize these so the subtype is hardcoded in the model
|
441
450
|
action.subtype = action.subtype
|
442
451
|
action.save!
|
443
452
|
action
|
444
453
|
end
|
445
454
|
end
|
446
|
-
|
447
|
-
def
|
448
|
-
details = "#{pluralize(
|
455
|
+
|
456
|
+
def action_details(quantity, thing)
|
457
|
+
details = "#{pluralize(quantity, thing)}"
|
449
458
|
details = details + ". #{self.notes}" unless self.notes.blank?
|
450
459
|
details
|
451
460
|
end
|
data/app/models/order_handler.rb
CHANGED
@@ -60,6 +60,14 @@ class OrderHandler
|
|
60
60
|
@discount
|
61
61
|
end
|
62
62
|
|
63
|
+
def apply_pass(params)
|
64
|
+
if params[:pass_code].present?
|
65
|
+
@pass = Pass.where(:pass_code => params[:pass_code]).first
|
66
|
+
@pass.apply_pass_to_cart(self.cart)
|
67
|
+
end
|
68
|
+
@pass
|
69
|
+
end
|
70
|
+
|
63
71
|
def handle_donation(params, organization)
|
64
72
|
if params[:donation_amount]
|
65
73
|
self.cart.clear_donations
|
@@ -90,4 +98,18 @@ class OrderHandler
|
|
90
98
|
|
91
99
|
end
|
92
100
|
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# Note that this handles passes *currently being purchased* NOT passes that are being applied to the cart
|
104
|
+
#
|
105
|
+
def handle_passes(params)
|
106
|
+
unless params[:pass_type].blank?
|
107
|
+
pass_type_id = params[:pass_type][:id]
|
108
|
+
quantity = params[:quantity].to_i
|
109
|
+
(1..quantity).each do |i|
|
110
|
+
self.cart.passes << Pass.for(PassType.find(pass_type_id))
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
93
115
|
end
|
data/app/models/organization.rb
CHANGED
@@ -22,6 +22,9 @@ class Organization < ActiveRecord::Base
|
|
22
22
|
has_many :membership_types
|
23
23
|
has_many :memberships
|
24
24
|
has_many :members
|
25
|
+
|
26
|
+
has_many :pass_types
|
27
|
+
|
25
28
|
has_many :searches
|
26
29
|
|
27
30
|
has_many :users, :through => :user_memberships, :order => 'user_memberships.owner desc'
|
@@ -110,6 +113,10 @@ class Organization < ActiveRecord::Base
|
|
110
113
|
self.kits.where(:type => "MembershipKit").first
|
111
114
|
end
|
112
115
|
|
116
|
+
def passes_kit
|
117
|
+
self.kits.where(:type => "PassesKit").first
|
118
|
+
end
|
119
|
+
|
113
120
|
delegate :can?, :cannot?, :to => :ability
|
114
121
|
def ability
|
115
122
|
OrganizationAbility.new(self)
|
data/app/models/pass.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
class Pass < ActiveRecord::Base
|
2
|
+
belongs_to :pass_type
|
3
|
+
belongs_to :person
|
4
|
+
belongs_to :organization
|
5
|
+
has_many :tickets
|
6
|
+
|
7
|
+
def self.for(pass_type)
|
8
|
+
new.tap do |pass|
|
9
|
+
pass.pass_type = pass_type
|
10
|
+
pass.organization = pass_type.organization
|
11
|
+
pass.price = pass_type.price
|
12
|
+
pass.sold_price = pass.price
|
13
|
+
pass.tickets_allowed = pass_type.tickets_allowed
|
14
|
+
pass.tickets_allowed = 0
|
15
|
+
pass.pass_code = new_pass_code
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def cart_price
|
20
|
+
price
|
21
|
+
end
|
22
|
+
|
23
|
+
def realized_fee
|
24
|
+
self.pass_type.hide_fee? ? self.price * PassType::SERVICE_FEE : 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.new_pass_code(size=8)
|
28
|
+
# Avoid confusable characters like 1/L and 0/O
|
29
|
+
charset = %w{ 2 3 4 6 7 9 A C D E F G H J K M N P Q R T V W X Y Z}
|
30
|
+
(0...size).map{ charset.to_a[rand(charset.size)] }.join
|
31
|
+
end
|
32
|
+
|
33
|
+
def apply_pass_to_cart(cart)
|
34
|
+
transaction do
|
35
|
+
cart.applied_pass = self
|
36
|
+
cart.tickets.each do |ticket|
|
37
|
+
ticket.pass = self
|
38
|
+
ticket.cart_price = 0
|
39
|
+
ticket.save
|
40
|
+
end
|
41
|
+
FeeCalculator.apply(FeeStrategy.new).to(cart)
|
42
|
+
cart.save
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class PassType < ActiveRecord::Base
|
2
|
+
belongs_to :organization
|
3
|
+
has_many :passes
|
4
|
+
|
5
|
+
attr_accessible :name, :description, :hide_fee, :thanks_copy, :sales_start_at, :sales_end_at,
|
6
|
+
:on_sale, :price, :tickets_allowed, :starts_at, :ends_at
|
7
|
+
|
8
|
+
validates :name, :description, :price, :tickets_allowed, :presence => true
|
9
|
+
|
10
|
+
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)
|
11
|
+
scope :on_sale, where(:on_sale => true)
|
12
|
+
scope :not_ended, where('ends_at > ?', DateTime.now)
|
13
|
+
|
14
|
+
SERVICE_FEE = 0.05
|
15
|
+
|
16
|
+
def passerize
|
17
|
+
self.name.end_with?("Pass") ? self.name : self.name + " Pass"
|
18
|
+
end
|
19
|
+
end
|
data/app/models/person.rb
CHANGED
@@ -26,6 +26,7 @@ class Person < ActiveRecord::Base
|
|
26
26
|
has_one :address, :validate => false
|
27
27
|
has_one :member
|
28
28
|
has_many :memberships, :through => :member
|
29
|
+
has_many :passes
|
29
30
|
|
30
31
|
accepts_nested_attributes_for :address, :allow_destroy => false
|
31
32
|
accepts_nested_attributes_for :phones, :reject_if => lambda { |p| p[:number].blank? }, :allow_destroy => true
|
@@ -282,10 +283,12 @@ class Person < ActiveRecord::Base
|
|
282
283
|
end
|
283
284
|
|
284
285
|
def self.first_or_create(attributes=nil, options ={}, &block)
|
286
|
+
Rails.logger.debug("Person.first_or_create #{attributes.inspect}")
|
285
287
|
attributes[:organization_id] ||= attributes[:organization].try(:id)
|
286
288
|
raise(ArgumentError, "You must include an organization when searching for people") if attributes[:organization_id].blank?
|
287
289
|
|
288
290
|
attributes.delete(:organization)
|
291
|
+
return Person.where(:id => attributes[:id]).where(:organization_id => attributes[:organization_id]).first if attributes[:id].present?
|
289
292
|
return Person.create(attributes, options, &block) if attributes[:email].blank?
|
290
293
|
|
291
294
|
Person.where(:email => attributes[:email]).where(:organization_id => attributes[:organization_id]).first || Person.create(attributes, options, &block)
|
@@ -296,6 +299,7 @@ class Person < ActiveRecord::Base
|
|
296
299
|
raise(ArgumentError, "You must include an organization when searching for people") if attributes[:organization_id].blank?
|
297
300
|
|
298
301
|
attributes.delete(:organization)
|
302
|
+
return Person.where(:id => attributes[:id]).where(:organization_id => attributes[:organization_id]).first if attributes[:id].present?
|
299
303
|
return Person.new(attributes, options, &block) if attributes[:email].blank?
|
300
304
|
|
301
305
|
Person.where(:email => attributes[:email]).where(:organization_id => attributes[:organization_id]).first || Person.new(attributes, options, &block)
|
data/app/models/search.rb
CHANGED
@@ -11,9 +11,10 @@ class Search < ActiveRecord::Base
|
|
11
11
|
:min_lifetime_value, :max_lifetime_value,
|
12
12
|
:min_donations_amount, :max_donations_amount,
|
13
13
|
:min_donations_date, :max_donations_date, :discount_code,
|
14
|
-
:membership_status, :
|
14
|
+
:membership_status, :pass_type_id, :membership_type_id, :membership_type,
|
15
|
+
:show_date_start, :show_date_end
|
15
16
|
|
16
|
-
|
17
|
+
ANY_EVENT = -1
|
17
18
|
|
18
19
|
def length
|
19
20
|
people.length
|
@@ -31,6 +32,10 @@ class Search < ActiveRecord::Base
|
|
31
32
|
Delayed::Job.enqueue(ActionJob.new(action, people))
|
32
33
|
end
|
33
34
|
|
35
|
+
def event_name
|
36
|
+
event.try(:name) || Event::ANY_EVENT_TEXT
|
37
|
+
end
|
38
|
+
|
34
39
|
def description
|
35
40
|
conditions = []
|
36
41
|
|
@@ -38,10 +43,13 @@ class Search < ActiveRecord::Base
|
|
38
43
|
|
39
44
|
if event_id.present?
|
40
45
|
if has_purchased_for
|
41
|
-
conditions << "Purchased tickets for #{
|
46
|
+
conditions << "Purchased tickets for #{event_name}."
|
42
47
|
else
|
43
|
-
conditions << "Have not purchased tickets for #{
|
48
|
+
conditions << "Have not purchased tickets for #{event_name}."
|
44
49
|
end
|
50
|
+
|
51
|
+
conditions << "For show dates after #{show_date_start.strftime('%D')}" if show_date_start.present?
|
52
|
+
conditions << "For show dates through #{show_date_end.strftime('%D')}" if show_date_end.present?
|
45
53
|
end
|
46
54
|
|
47
55
|
if zip.present? || state.present?
|
@@ -118,87 +126,186 @@ class Search < ActiveRecord::Base
|
|
118
126
|
end
|
119
127
|
end
|
120
128
|
|
129
|
+
def offset_show_date_start
|
130
|
+
return nil if self.show_date_start.blank?
|
131
|
+
@offset_show_date_start ||= self.show_date_start.to_datetime.change(:offset => offset(self.show_date_start))
|
132
|
+
end
|
133
|
+
|
134
|
+
def offset_show_date_end
|
135
|
+
return nil if self.show_date_end.blank?
|
136
|
+
@offset_show_date_end ||= self.show_date_end.to_datetime.end_of_day.change(:offset => offset(self.show_date_end))
|
137
|
+
end
|
138
|
+
|
139
|
+
def offset(datetime)
|
140
|
+
@offset ||= datetime.in_time_zone(ActiveSupport::TimeZone.create(self.organization.time_zone)).formatted_offset
|
141
|
+
end
|
142
|
+
|
121
143
|
private
|
122
144
|
|
123
|
-
|
124
|
-
|
125
|
-
|
145
|
+
def find_people
|
146
|
+
column_names = Person.column_names.collect {|cn| "people.#{cn}" }
|
147
|
+
column_names << "lower(people.last_name) AS ordered_last_names"
|
126
148
|
|
127
|
-
|
128
|
-
|
129
|
-
|
149
|
+
people = Person.where(:organization_id => organization_id)
|
150
|
+
people = people.where(:dummy => false)
|
151
|
+
people = people.order('ordered_last_names ASC')
|
130
152
|
|
131
|
-
|
132
|
-
|
153
|
+
people = people.where("people.type" => person_type) unless person_type.blank?
|
154
|
+
people = people.where("people.subtype" => person_subtype) unless person_type.blank? || person_subtype.blank?
|
133
155
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
if event_id.present?
|
138
|
-
people = people.joins("LEFT JOIN `tickets` ON `tickets`.`buyer_id` = `people`.`id` ")
|
139
|
-
.joins("LEFT JOIN `shows` ON `shows`.`id` = `tickets`.`show_id` ")
|
140
|
-
.joins("LEFT JOIN `events` ON `events`.`id` = `shows`.`event_id`")
|
156
|
+
people = people.tagged_with(tagging) unless tagging.blank?
|
157
|
+
people = people.joins(:address) unless zip.blank? && state.blank?
|
141
158
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
159
|
+
people = add_event_query(people, column_names)
|
160
|
+
|
161
|
+
people = people.where("addresses.zip" => zip.to_s) unless zip.blank?
|
162
|
+
people = people.where("addresses.state" => state) unless state.blank?
|
163
|
+
people = people.where("people.lifetime_value >= ?", min_lifetime_value * 100.0) unless min_lifetime_value.blank?
|
164
|
+
people = people.where("people.lifetime_value <= ?", max_lifetime_value * 100.0) unless max_lifetime_value.blank?
|
165
|
+
|
166
|
+
unless discount_code.blank?
|
167
|
+
people = people.joins(:orders => [:items => [:discount]])
|
168
|
+
people = (discount_code == Discount::ALL_DISCOUNTS_STRING) ? people.where("items.discount_id is not null") : people.where("discounts.code = ?", discount_code)
|
146
169
|
end
|
147
|
-
end
|
148
|
-
|
149
170
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
171
|
+
unless [min_donations_amount, max_donations_amount, min_donations_date, max_donations_date].all?(&:blank?)
|
172
|
+
people = people.joins(:orders => :items)
|
173
|
+
people = people.where("orders.created_at >= ?", min_donations_date) unless min_donations_date.blank?
|
174
|
+
people = people.where("orders.created_at <= ?", max_donations_date + 1.day) unless max_donations_date.blank?
|
175
|
+
people = people.where("items.product_type = 'Donation'")
|
176
|
+
people = people.group("people.id")
|
177
|
+
if min_donations_amount.blank?
|
178
|
+
people = people.having("SUM(items.price + items.nongift_amount) >= 1")
|
179
|
+
else
|
180
|
+
people = people.having("SUM(items.price + items.nongift_amount) >= ?", min_donations_amount * 100.0)
|
181
|
+
end
|
182
|
+
people = people.having("SUM(items.price + items.nongift_amount) <= ?", max_donations_amount * 100.0) unless max_donations_amount.blank?
|
183
|
+
end
|
154
184
|
|
155
|
-
|
156
|
-
|
157
|
-
|
185
|
+
### MEMBERSHIP ##
|
186
|
+
|
187
|
+
if searching_membership?
|
188
|
+
#Necessary because we need a left join for the "Not" condition to work
|
189
|
+
people = people.joins('left join members on members.person_id = people.id')
|
190
|
+
end
|
191
|
+
|
192
|
+
if membership_status.present?
|
193
|
+
people = people.merge(Member.current) if membership_status == "Current"
|
194
|
+
people = people.merge(Member.lapsed) if membership_status == "Lapsed"
|
195
|
+
people = people.merge(Member.past) if membership_status == "Past"
|
196
|
+
people = people.where("members.id is null") if membership_status == "None"
|
197
|
+
end
|
198
|
+
|
199
|
+
if membership_type_id.present?
|
200
|
+
people = people.joins('inner join memberships on members.id = memberships.member_id')
|
201
|
+
people = people.joins('inner join membership_types on membership_types.id = memberships.membership_type_id')
|
202
|
+
|
203
|
+
people = people.where('membership_types.id = ?', membership_type_id)
|
204
|
+
end
|
205
|
+
|
206
|
+
people.select(column_names).group("people.id")
|
158
207
|
end
|
159
208
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
people = people.group("people.id")
|
166
|
-
if min_donations_amount.blank?
|
167
|
-
people = people.having("SUM(items.price + items.nongift_amount) >= 1")
|
168
|
-
else
|
169
|
-
people = people.having("SUM(items.price + items.nongift_amount) >= ?", min_donations_amount * 100.0)
|
209
|
+
def add_event_query(people, column_names)
|
210
|
+
if any_event?
|
211
|
+
people = add_any_event_query(people, column_names)
|
212
|
+
elsif specific_event?
|
213
|
+
people = add_specific_event_query(people, column_names)
|
170
214
|
end
|
171
|
-
|
215
|
+
|
216
|
+
people
|
172
217
|
end
|
173
218
|
|
174
|
-
|
219
|
+
def add_specific_event_query(people, column_names)
|
220
|
+
if has_purchased_for
|
221
|
+
people = people.joins("LEFT JOIN `tickets` ON `tickets`.`buyer_id` = `people`.`id` ")
|
222
|
+
.joins("LEFT JOIN `shows` ON `shows`.`id` = `tickets`.`show_id` ")
|
223
|
+
.joins("LEFT JOIN `events` ON `events`.`id` = `shows`.`event_id`")
|
224
|
+
|
225
|
+
people = people.where("events.id" => event_id)
|
175
226
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
227
|
+
if show_date_search?
|
228
|
+
people = people.where("shows.datetime >= ?", offset_show_date_start) unless show_date_start.blank?
|
229
|
+
people = people.where("shows.datetime <= ?", offset_show_date_end) unless show_date_end.blank?
|
230
|
+
end
|
180
231
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
232
|
+
elsif !has_purchased_for
|
233
|
+
if show_date_search?
|
234
|
+
people_subquery = Ticket.joins(:show)
|
235
|
+
.where("shows.event_id = ?", event_id)
|
236
|
+
.where("tickets.buyer_id = people.id")
|
237
|
+
|
238
|
+
people_subquery = people_subquery.where("shows.datetime >= ?", offset_show_date_start) unless show_date_start.blank?
|
239
|
+
people_subquery = people_subquery.where("shows.datetime <= ?", offset_show_date_end) unless show_date_end.blank?
|
240
|
+
|
241
|
+
people = people.where("NOT EXISTS (#{people_subquery.to_sql})")
|
242
|
+
else
|
243
|
+
#
|
244
|
+
# Had to use a correlated subquery here. Sorry.
|
245
|
+
# ActiveRecord 3.2 does not support NOT IN, so we have to do some manual work here.
|
246
|
+
# AR 4.0 has NOT IN
|
247
|
+
#
|
248
|
+
people_subquery = Person.select("people.id")
|
249
|
+
.joins("LEFT JOIN `tickets` ON `tickets`.`buyer_id` = `people`.`id` ")
|
250
|
+
.joins("LEFT JOIN `shows` ON `shows`.`id` = `tickets`.`show_id` ")
|
251
|
+
.joins("LEFT JOIN `events` ON `events`.`id` = `shows`.`event_id`")
|
252
|
+
.where("events.id" => event_id)
|
253
|
+
people = people.where("people.id not in (#{people_subquery.to_sql})")
|
254
|
+
end
|
255
|
+
|
256
|
+
people
|
257
|
+
end
|
258
|
+
|
259
|
+
people
|
186
260
|
end
|
187
261
|
|
188
|
-
|
189
|
-
people = people.joins(
|
190
|
-
|
262
|
+
def add_any_event_query(people, column_names)
|
263
|
+
people = people.joins("LEFT JOIN `tickets` ON `tickets`.`buyer_id` = `people`.`id` ")
|
264
|
+
|
265
|
+
if has_purchased_for
|
266
|
+
column_names << "count(tickets.id) as ticket_count"
|
267
|
+
if show_date_search?
|
268
|
+
people = people.joins("LEFT JOIN `shows` ON `shows`.`id` = `tickets`.`show_id` ")
|
269
|
+
people = people.where("shows.datetime >= ?", offset_show_date_start) unless show_date_start.blank?
|
270
|
+
people = people.where("shows.datetime <= ?", offset_show_date_end) unless show_date_end.blank?
|
271
|
+
end
|
272
|
+
people = people.having("ticket_count > 0")
|
273
|
+
elsif !has_purchased_for
|
274
|
+
if show_date_search?
|
275
|
+
people_subquery = Ticket.joins(:show)
|
276
|
+
.where("tickets.buyer_id = people.id")
|
277
|
+
|
278
|
+
people_subquery = people_subquery.where("shows.datetime >= ?", offset_show_date_start) unless show_date_start.blank?
|
279
|
+
people_subquery = people_subquery.where("shows.datetime <= ?", offset_show_date_end) unless show_date_end.blank?
|
280
|
+
|
281
|
+
people = people.where("NOT EXISTS (#{people_subquery.to_sql})")
|
282
|
+
else
|
283
|
+
column_names << "count(tickets.id) as ticket_count"
|
284
|
+
people = people.having("ticket_count = 0")
|
285
|
+
end
|
286
|
+
end
|
191
287
|
|
192
|
-
people
|
288
|
+
people
|
193
289
|
end
|
194
290
|
|
195
|
-
|
196
|
-
|
291
|
+
def searching_for_event?
|
292
|
+
event_id.present?
|
293
|
+
end
|
197
294
|
|
295
|
+
def show_date_search?
|
296
|
+
show_date_start.present? || show_date_end.present?
|
297
|
+
end
|
198
298
|
|
299
|
+
def any_event?
|
300
|
+
self.event_id == ANY_EVENT
|
301
|
+
end
|
199
302
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
303
|
+
def specific_event?
|
304
|
+
self.event_id.present? && self.event_id > ANY_EVENT
|
305
|
+
end
|
306
|
+
|
307
|
+
def searching_membership?
|
308
|
+
membership_status.present? ||
|
309
|
+
membership_type_id.present?
|
310
|
+
end
|
204
311
|
end
|