artfully_ose 1.2.0.pre.19 → 1.2.0.pre.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +28 -0
- data/app/assets/javascripts/change-membership.js +66 -16
- data/app/assets/javascripts/store/store.js +4 -0
- data/app/assets/stylesheets/sass/store.sass +2 -0
- data/app/{models/carts → concerns}/cart_finder.rb +0 -0
- data/app/{models/actions → concerns}/immutable_action.rb +0 -0
- data/app/concerns/pdf_generation.rb +71 -0
- data/app/{models/orders → concerns}/unrefundable.rb +0 -0
- data/app/controllers/export_controller.rb +1 -5
- data/app/controllers/imports_controller.rb +12 -0
- data/app/controllers/members/index_controller.rb +1 -1
- data/app/controllers/members/members_controller.rb +6 -1
- data/app/controllers/members/passwords_controller.rb +10 -0
- data/app/controllers/members/people_controller.rb +17 -0
- data/app/controllers/membership_cancellations_controller.rb +46 -0
- data/app/controllers/membership_comps_controller.rb +97 -0
- data/app/controllers/membership_types_controller.rb +6 -2
- data/app/controllers/people_controller.rb +15 -3
- data/app/controllers/sales_controller.rb +4 -0
- data/app/controllers/searches_controller.rb +2 -2
- data/app/controllers/sections_controller.rb +1 -1
- data/app/controllers/shows_controller.rb +52 -3
- data/app/controllers/store/checkouts_controller.rb +2 -7
- data/app/controllers/store/memberships_controller.rb +2 -1
- data/app/helpers/artfully_ose_helper.rb +4 -3
- data/app/mailers/order_mailer.rb +3 -3
- data/app/models/actions/comp_action.rb +15 -0
- data/app/models/address.rb +7 -0
- data/app/models/{carts/cart.rb → cart.rb} +0 -19
- data/app/models/checkout.rb +53 -15
- data/app/models/comp.rb +21 -11
- data/app/models/database_views/item_view.rb +4 -0
- data/app/models/event.rb +1 -0
- data/app/models/{ext/ext.rb → ext.rb} +14 -1
- data/app/models/ext/preprocessor.rb +6 -0
- data/app/models/job/order_processor.rb +6 -3
- data/app/models/job/show_creator.rb +6 -2
- data/app/models/kit.rb +1 -1
- data/app/models/kits/membership_kit.rb +8 -1
- data/app/models/kits/scannable_tickets_kit.rb +24 -0
- data/app/models/member.rb +59 -13
- data/app/models/membership.rb +2 -14
- data/app/models/membership_cancellation.rb +80 -0
- data/app/models/membership_comp.rb +103 -0
- data/app/models/membership_comp_job.rb +32 -0
- data/app/models/membership_type.rb +3 -3
- data/app/models/{orders/order.rb → order.rb} +8 -2
- data/app/models/orders/comp_order.rb +4 -0
- data/app/models/organization.rb +5 -1
- data/app/models/{payments/payment.rb → payment.rb} +12 -1
- data/app/models/payments/credit_card_payment.rb +36 -5
- data/app/models/person.rb +11 -1
- data/app/models/rolling_membership_type.rb +3 -1
- data/app/models/sale.rb +1 -1
- data/app/models/search.rb +1 -6
- data/app/models/seasonal_membership_type.rb +1 -0
- data/app/models/ticket.rb +2 -0
- data/app/models/ticket/qr_code.rb +1 -1
- data/app/models/ticket/transfers.rb +6 -3
- data/app/models/ticket_type.rb +6 -0
- data/app/models/user.rb +2 -1
- data/app/views/addresses/_address.html.haml +7 -6
- data/app/views/events/_ticket_type_fields.html.haml +2 -2
- data/app/views/imports/index.html.haml +2 -1
- data/app/views/layouts/_menu.html.haml +3 -2
- data/app/views/layouts/storefront.html.haml +17 -13
- data/app/views/members/index/index.html.haml +5 -11
- data/app/views/members/invitations/edit.html.haml +17 -0
- data/app/views/members/mailer/_invitation_body.html.haml +18 -0
- data/app/views/members/mailer/invitation_instructions.html.haml +6 -12
- data/app/views/members/mailer/reset_password_instructions.html.erb +8 -0
- data/app/views/members/passwords/edit.html.haml +18 -0
- data/app/views/members/sessions/new.html.haml +19 -0
- data/app/views/membership_cancellations/_form.html.haml +39 -0
- data/app/views/membership_cancellations/_modal.html.haml +2 -0
- data/app/views/membership_cancellations/_processing.html.haml +14 -0
- data/app/views/membership_cancellations/create.js.erb +3 -0
- data/app/views/membership_cancellations/new.js.erb +3 -0
- data/app/views/membership_comps/confirm.html.haml +85 -0
- data/app/views/membership_comps/create.html.haml +18 -0
- data/app/views/membership_comps/new.html.haml +82 -0
- data/app/views/membership_mailer/cancellation_confirmation.html.haml +9 -0
- data/app/views/membership_mailer/cancellation_confirmation.text.haml +6 -0
- data/app/views/membership_mailer/refund_problem.html.haml +18 -0
- data/app/views/membership_mailer/refund_problem.text.haml +14 -0
- data/app/views/membership_types/_form.html.haml +18 -53
- data/app/views/membership_types/_membership_type_fees.html.haml +0 -0
- data/app/views/membership_types/index.html.haml +1 -5
- data/app/views/memberships/_membership_scripts.js.erb +15 -0
- data/app/views/memberships/index.html.haml +18 -17
- data/app/views/order_mailer/confirmation_for.html.haml +8 -0
- data/app/views/orders/_order_sidebar.html.haml +1 -3
- data/app/views/organizations/_form.html.haml +6 -0
- data/app/views/pdfs/member.html.haml +66 -0
- data/app/views/pdfs/order.html.haml +84 -0
- data/app/views/people/_comp_membership.html.haml +23 -0
- data/app/views/people/_edit_modal.html.haml +6 -3
- data/app/views/people/show.html.haml +1 -2
- data/app/views/searches/_form.html.haml +34 -33
- data/app/views/searches/show.html.haml +4 -1
- data/app/views/segments/show.html.haml +4 -1
- data/app/views/shows/_ticket_table.html.haml +1 -1
- data/app/views/shows/index.html.haml +8 -2
- data/app/views/store/checkouts/_membership_info.html.haml +1 -0
- data/app/views/store/checkouts/thanks.html.haml +7 -3
- data/app/views/store/events/index.html.haml +9 -3
- data/app/views/store/events/single_show.html.haml +4 -1
- data/app/views/store/memberships/index.html.haml +2 -1
- data/app/views/store/orders/show.html.haml +2 -2
- data/config/initializers/devise.rb +1 -1
- data/config/initializers/paperclip.rb +39 -0
- data/config/locales/devise_invitable.en.yml +1 -1
- data/config/locales/en.yml +9 -0
- data/config/routes.rb +14 -7
- data/db/migrate/20131223215615_goodbye_order_view.rb +6 -0
- data/db/migrate/20140114011856_add_welcome_message_to_memberships.rb +5 -0
- data/db/migrate/20140114212127_add_send_email_to_memberships.rb +5 -0
- data/db/migrate/20140125183606_index_memberships_and_types.rb +6 -0
- data/db/migrate/20140128213101_add_pdf_to_members.rb +5 -0
- data/db/migrate/20140128223140_add_uuid_to_member.rb +6 -0
- data/db/migrate/20140128223948_add_qr_code_to_member.rb +9 -0
- data/lib/artfully_ose/common_abilities.rb +1 -1
- data/lib/artfully_ose/engine.rb +23 -2
- data/lib/artfully_ose/version.rb +1 -1
- data/spec/factories/action_factories.rb +19 -0
- data/spec/factories/address_factories.rb +11 -0
- data/spec/factories/cart_factories.rb +26 -0
- data/spec/factories/chart_factories.rb +52 -0
- data/spec/factories/discount_factories.rb +11 -0
- data/spec/factories/donation_factories.rb +14 -0
- data/spec/factories/event_factories.rb +29 -0
- data/spec/factories/import_factories.rb +61 -0
- data/spec/factories/item_factories.rb +51 -0
- data/spec/factories/kit_factories.rb +27 -0
- data/spec/factories/member_factories.rb +19 -0
- data/spec/factories/membership_factories.rb +8 -0
- data/spec/factories/membership_type_factories.rb +18 -0
- data/spec/factories/order_factories.rb +40 -0
- data/spec/factories/organization_factories.rb +44 -0
- data/spec/factories/payments_factories.rb +97 -0
- data/spec/factories/person_factories.rb +44 -0
- data/spec/factories/phone_factories.rb +6 -0
- data/spec/factories/section_factories.rb +21 -0
- data/spec/factories/segment_factories.rb +6 -0
- data/spec/factories/show_factories.rb +33 -0
- data/spec/factories/ticket_factories.rb +57 -0
- data/spec/factories/ticket_offer_factories.rb +16 -0
- data/spec/factories/ticket_type_factories.rb +8 -0
- data/spec/factories/user_factories.rb +24 -0
- metadata +75 -17
- data/app/controllers/membership_awards_controller.rb +0 -60
- data/app/models/database_views/order_view.rb +0 -34
- data/app/models/membership_award.rb +0 -24
- data/app/models/orders/pdf_generation.rb +0 -75
- data/app/views/members/invitations/edit.html.erb +0 -14
- data/app/views/membership_awards/confirm.html.haml +0 -105
- data/app/views/membership_awards/create.html.haml +0 -1
- data/app/views/membership_awards/new.html.haml +0 -79
@@ -11,6 +11,10 @@ class ItemView < ActiveRecord::Base
|
|
11
11
|
|
12
12
|
default_scope order('created_at desc')
|
13
13
|
|
14
|
+
def self.sales_export_filename_for(organization)
|
15
|
+
"exports/Artfully-Ticket-Sales-Export-#{organization.id}.csv"
|
16
|
+
end
|
17
|
+
|
14
18
|
comma :donation do
|
15
19
|
created_at_local_to_organization("Date")
|
16
20
|
email("Email")
|
data/app/models/event.rb
CHANGED
@@ -41,6 +41,7 @@ class Event < ActiveRecord::Base
|
|
41
41
|
validates_attachment_content_type :image, :content_type => ["image/jpeg", "image/gif", "image/png"]
|
42
42
|
|
43
43
|
before_create :set_primary_category
|
44
|
+
before_create { self.public = true }
|
44
45
|
after_create :create_default_chart
|
45
46
|
|
46
47
|
serialize :secondary_categories, Array
|
@@ -26,7 +26,7 @@ module Ext
|
|
26
26
|
|
27
27
|
def set_uuid
|
28
28
|
if self.uuid.nil?
|
29
|
-
self.uuid = SecureRandom.uuid
|
29
|
+
self.uuid = PREFIX + SecureRandom.uuid
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -34,4 +34,17 @@ module Ext
|
|
34
34
|
PREFIX + SecureRandom.uuid
|
35
35
|
end
|
36
36
|
end
|
37
|
+
|
38
|
+
module S3Link
|
39
|
+
def download_link_for(field)
|
40
|
+
s3 = AWS::S3.new(
|
41
|
+
:access_key_id => ENV['S3_ACCESS_KEY_ID'],
|
42
|
+
:secret_access_key => ENV['S3_SECRET_ACCESS_KEY']
|
43
|
+
)
|
44
|
+
bucket = s3.buckets[ENV['S3_BUCKET']]
|
45
|
+
object = bucket.objects[self.send(field).path]
|
46
|
+
url = object.url_for(:read, :expires => 10*60)
|
47
|
+
url.to_s
|
48
|
+
end
|
49
|
+
end
|
37
50
|
end
|
@@ -31,7 +31,10 @@ class OrderProcessor < Struct.new(:order, :options)
|
|
31
31
|
end
|
32
32
|
|
33
33
|
begin
|
34
|
-
|
34
|
+
order.tickets.each do |item|
|
35
|
+
item.product.generate_qr_code_without_delay
|
36
|
+
generate_pdf if order.organization.can? :access, :scannable_tickets
|
37
|
+
end
|
35
38
|
rescue Exception => e
|
36
39
|
Exceptional.context(:order_id => order.id)
|
37
40
|
Exceptional.handle(e, "Could not generate PDF for order")
|
@@ -55,13 +58,13 @@ class OrderProcessor < Struct.new(:order, :options)
|
|
55
58
|
|
56
59
|
def process_memberships
|
57
60
|
self.order.memberships.each do |membership_item|
|
58
|
-
Member.for(membership_item.product)
|
61
|
+
Member.for(membership_item.product, self.order.person)
|
59
62
|
end
|
60
63
|
self.order.create_membership_action
|
61
64
|
end
|
62
65
|
|
63
66
|
def generate_pdf
|
64
|
-
pdf_generator =
|
67
|
+
pdf_generator = PdfGeneration.new(order)
|
65
68
|
download_url = pdf_generator.generate
|
66
69
|
order.pdf = URI.parse(download_url)
|
67
70
|
order.save
|
@@ -15,7 +15,7 @@ class ShowCreator < Struct.new(:datetimes, :show_params, :chart_params, :event,
|
|
15
15
|
|
16
16
|
def perform
|
17
17
|
ActiveRecord::Base.transaction do
|
18
|
-
datetimes.each do |
|
18
|
+
datetimes.each do |datetime_string|
|
19
19
|
@show = self.event.next_show
|
20
20
|
|
21
21
|
#clear the sections and replace them with whatever they entered
|
@@ -25,10 +25,14 @@ class ShowCreator < Struct.new(:datetimes, :show_params, :chart_params, :event,
|
|
25
25
|
@show.organization = organization
|
26
26
|
@show.chart_id = @show.chart.id
|
27
27
|
|
28
|
-
@show.datetime =
|
28
|
+
@show.datetime = DateTime.parse(datetime_string).change(:offset => offset(datetime_string, event.time_zone))
|
29
29
|
@show.go!(publish)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
+
def offset(datetime_string, time_zone)
|
35
|
+
ActiveSupport::TimeZone.create(event.time_zone).parse(datetime_string).formatted_offset
|
36
|
+
end
|
37
|
+
|
34
38
|
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 ].freeze
|
64
|
+
@subklasses ||= [ TicketingKit, RegularDonationKit, SponsoredDonationKit, ResellerKit, MailchimpKit, MembershipKit, ScannableTicketsKit ].freeze
|
65
65
|
end
|
66
66
|
|
67
67
|
def self.pad_with_new_kits(kits = [])
|
@@ -11,10 +11,11 @@ class MembershipKit < Kit
|
|
11
11
|
end
|
12
12
|
|
13
13
|
when_active do |organization|
|
14
|
-
organization.can :access, :
|
14
|
+
organization.can :access, :membership
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
before_save :initialize_accessors
|
18
19
|
before_save :sanitize_accessors
|
19
20
|
|
20
21
|
ACCESSORS = [ :marketing_copy_heading, :marketing_copy_sidebar ]
|
@@ -47,6 +48,12 @@ class MembershipKit < Kit
|
|
47
48
|
save
|
48
49
|
end
|
49
50
|
|
51
|
+
def initialize_accessors
|
52
|
+
ACCESSORS.each do |accessor|
|
53
|
+
self.send("#{accessor}=", "") if self.send("#{accessor}").nil?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
50
57
|
def sanitize_accessors
|
51
58
|
ACCESSORS.each do |accessor|
|
52
59
|
self.send("#{accessor}=", (sanitize self.send(accessor)))
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class ScannableTicketsKit < Kit
|
2
|
+
|
3
|
+
acts_as_kit :admin_only => true do
|
4
|
+
approve :unless => :no_bank_account?
|
5
|
+
|
6
|
+
self.configurable = false
|
7
|
+
|
8
|
+
state_machine do
|
9
|
+
state :cancelled, :enter => :kit_cancelled
|
10
|
+
end
|
11
|
+
|
12
|
+
when_active do |organization|
|
13
|
+
organization.can :access, :scannable_tickets
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def friendly_name
|
18
|
+
"Scannable Tickets"
|
19
|
+
end
|
20
|
+
|
21
|
+
def pitch
|
22
|
+
"Your patrons will receive barcoded tickets that you can scan at the door."
|
23
|
+
end
|
24
|
+
end
|
data/app/models/member.rb
CHANGED
@@ -2,6 +2,8 @@ class Member < ActiveRecord::Base
|
|
2
2
|
|
3
3
|
include Ext::DeviseConfiguration
|
4
4
|
include Ext::Integrations::Member
|
5
|
+
include Ext::Uuid
|
6
|
+
include Ext::S3Link
|
5
7
|
|
6
8
|
has_many :memberships
|
7
9
|
belongs_to :organization
|
@@ -10,7 +12,7 @@ class Member < ActiveRecord::Base
|
|
10
12
|
validates :organization, :presence => true
|
11
13
|
validates :member_number, :presence => true
|
12
14
|
|
13
|
-
|
15
|
+
before_validation :set_member_number, :unless => :persisted?
|
14
16
|
|
15
17
|
CURRENT = :current
|
16
18
|
LAPSED = :lapsed
|
@@ -21,6 +23,10 @@ class Member < ActiveRecord::Base
|
|
21
23
|
scope :lapsed, where("lapsed_memberships_count > 0").where("current_memberships_count = 0")
|
22
24
|
scope :past, where("past_memberships_count > 0").where("lapsed_memberships_count = 0").where("current_memberships_count = 0")
|
23
25
|
|
26
|
+
has_attached_file :pdf, MEMBER_PDF_STORAGE
|
27
|
+
has_attached_file :qr_code, MEMBER_QR_STORAGE
|
28
|
+
delegate :url, :to => :qr_code, :prefix => true
|
29
|
+
|
24
30
|
#
|
25
31
|
# devise_invitable needs this otherwise it can't set the :from param in an email
|
26
32
|
#
|
@@ -100,26 +106,66 @@ class Member < ActiveRecord::Base
|
|
100
106
|
super && !suspended?
|
101
107
|
end
|
102
108
|
|
103
|
-
def self.find_by_membership(membership)
|
104
|
-
Member.find_by_email_and_organization_id(
|
109
|
+
def self.find_by_membership(membership, person)
|
110
|
+
Member.find_by_email_and_organization_id(person.email, membership.organization) #TODO: Biggest join ever
|
111
|
+
end
|
112
|
+
|
113
|
+
def member_through
|
114
|
+
self.memberships.map{|membership| membership.ends_at}.max
|
105
115
|
end
|
106
116
|
|
107
117
|
#
|
108
|
-
# * Creates
|
118
|
+
# * Creates a member for this membership if one does not already exist
|
109
119
|
# * Attaches that member to the applicable person
|
120
|
+
# * Invites that member if membership.send_email is true
|
121
|
+
# * Generates a new qr code if this is a new member
|
122
|
+
# * Generates a membership card if this is a new member
|
110
123
|
#
|
111
|
-
|
112
|
-
|
124
|
+
# This method CANNOT be run in any synchronous flow
|
125
|
+
#
|
126
|
+
def self.for(membership, person)
|
127
|
+
member = Member.find_by_membership(membership, person)
|
128
|
+
|
113
129
|
if member.nil?
|
114
|
-
member = Member.
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
130
|
+
member = Member.create!({:email => person.email,
|
131
|
+
:organization => membership.organization,
|
132
|
+
:person => person,
|
133
|
+
:password => SecureRandom.hex(32)}, :without_protection => true)
|
134
|
+
|
135
|
+
member.person.create_address if member.person.address.nil?
|
136
|
+
|
137
|
+
membership.member = member
|
138
|
+
membership.save
|
139
|
+
|
140
|
+
member.generate_qr_code
|
141
|
+
member.generate_pdf
|
119
142
|
|
120
|
-
|
121
|
-
|
143
|
+
member.invite! if membership.send_email
|
144
|
+
else
|
145
|
+
membership.member = member
|
146
|
+
membership.save
|
147
|
+
end
|
122
148
|
|
123
149
|
member
|
124
150
|
end
|
151
|
+
|
152
|
+
def generate_pdf
|
153
|
+
pdf_generator = PdfGeneration.new(self)
|
154
|
+
download_url = pdf_generator.generate
|
155
|
+
self.pdf = URI.parse(download_url)
|
156
|
+
self.save
|
157
|
+
end
|
158
|
+
|
159
|
+
def generate_qr_code
|
160
|
+
file = Tempfile.new(['qr-code', '.png'])
|
161
|
+
Ticket::QRCode.new(self, nil).render(file)
|
162
|
+
self.qr_code = file
|
163
|
+
|
164
|
+
# If we don't save the ticket here, paperclip will leak a file handle.
|
165
|
+
# Even though we close our Tempfile below, paperclip copies that into
|
166
|
+
# another Tempfile, and only closes the handle when the ticket is saved.
|
167
|
+
save
|
168
|
+
|
169
|
+
file.close
|
170
|
+
end
|
125
171
|
end
|
data/app/models/membership.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
class Membership < ActiveRecord::Base
|
2
|
-
include Itemable
|
3
2
|
belongs_to :organization
|
4
3
|
belongs_to :member
|
5
4
|
belongs_to :membership_type
|
@@ -24,6 +23,7 @@ class Membership < ActiveRecord::Base
|
|
24
23
|
membership.membership_type = membership_type
|
25
24
|
membership.organization = membership_type.organization
|
26
25
|
membership.price = membership_type.price_for(member)
|
26
|
+
membership.sold_price = membership.price
|
27
27
|
membership.starts_at = membership_type.starts_at
|
28
28
|
membership.ends_at = membership_type.ends_at
|
29
29
|
end
|
@@ -33,18 +33,6 @@ class Membership < ActiveRecord::Base
|
|
33
33
|
self.member.try(:count_memberships)
|
34
34
|
end
|
35
35
|
|
36
|
-
# TODO: DJ this. Could be thousands of people.
|
37
|
-
def self.for_award(membership_award)
|
38
|
-
membership_award.people.each do |people|
|
39
|
-
membership = Membership.for(membership_award.membership_type)
|
40
|
-
membership.ends_at = membership_award.ends_at
|
41
|
-
membership.sold_price = membership_award.sold_price
|
42
|
-
|
43
|
-
# create order
|
44
|
-
# Member.for(membership)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
36
|
def adjust_expiration_to(new_ends_at)
|
49
37
|
self.ends_at = new_ends_at
|
50
38
|
self.save
|
@@ -71,7 +59,7 @@ class Membership < ActiveRecord::Base
|
|
71
59
|
end
|
72
60
|
|
73
61
|
def refundable?
|
74
|
-
|
62
|
+
true
|
75
63
|
end
|
76
64
|
|
77
65
|
def exchangeable?
|
@@ -0,0 +1,80 @@
|
|
1
|
+
class MembershipCancellation
|
2
|
+
attr_reader :cancelled_memberships
|
3
|
+
attr_reader :membership_ids
|
4
|
+
|
5
|
+
def initialize(membership_ids=nil)
|
6
|
+
@cancelled_memberships = []
|
7
|
+
@membership_ids = Array.wrap(membership_ids) unless membership_ids.nil?
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.enqueue(membership_ids)
|
11
|
+
membership_ids = Array.wrap(membership_ids)
|
12
|
+
Delayed::Job.enqueue(MembershipCancellation.new(membership_ids))
|
13
|
+
end
|
14
|
+
|
15
|
+
def expired_at
|
16
|
+
@expired_at ||= 1.second.ago
|
17
|
+
end
|
18
|
+
|
19
|
+
def memberships
|
20
|
+
@memberships ||= Membership.where(id: membership_ids)
|
21
|
+
end
|
22
|
+
|
23
|
+
def non_refundables
|
24
|
+
@non_refundables ||= memberships.select { |m| !m.item.refundable? || !m.item.order.credit?}
|
25
|
+
end
|
26
|
+
|
27
|
+
def refund_available?
|
28
|
+
!refundables.blank?
|
29
|
+
end
|
30
|
+
|
31
|
+
def refund_amount
|
32
|
+
@refund_amount ||= refundables.map(&:price).sum
|
33
|
+
end
|
34
|
+
|
35
|
+
def refundables
|
36
|
+
@refundables ||= memberships.select {|m| m.item.refundable? && m.item.order.credit? }
|
37
|
+
end
|
38
|
+
|
39
|
+
def refundables_for(order)
|
40
|
+
refundables.select { |r| r.item.order.id == order.id }
|
41
|
+
end
|
42
|
+
|
43
|
+
def refundable_orders
|
44
|
+
@refundable_orders ||= refundables.map(&:item).map(&:order).uniq
|
45
|
+
end
|
46
|
+
|
47
|
+
def perform
|
48
|
+
Order.transaction do
|
49
|
+
|
50
|
+
# Handle refunds
|
51
|
+
refundable_orders.each do |order|
|
52
|
+
# Find refundable memberships for this order
|
53
|
+
refundables = refundables_for(order)
|
54
|
+
|
55
|
+
# Submit the refund
|
56
|
+
refund = Refund.new(order, refundables.map(&:item))
|
57
|
+
refund.submit(:send_email_confirmation => true)
|
58
|
+
|
59
|
+
|
60
|
+
cancel_memberships!(refundables) if refund.successful?
|
61
|
+
end
|
62
|
+
|
63
|
+
# Cancel non-refundable memberships
|
64
|
+
cancel_memberships!(non_refundables)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def owner
|
69
|
+
@owner ||= memberships.first.item.order.person
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def cancel_memberships!(memberships)
|
75
|
+
memberships.map do |m|
|
76
|
+
m.adjust_expiration_to expired_at
|
77
|
+
@cancelled_memberships << m
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
class MembershipComp
|
2
|
+
QUEUE = "comp"
|
3
|
+
|
4
|
+
include ActiveModel::Conversion
|
5
|
+
include ActiveModel::Validations
|
6
|
+
extend ActiveModel::Naming
|
7
|
+
|
8
|
+
validate :people_belong_to_this_org,
|
9
|
+
:expiration_date_cannot_be_in_the_past,
|
10
|
+
:we_have_some_people
|
11
|
+
validates :number_of_memberships, numericality: true
|
12
|
+
|
13
|
+
attr_accessor :people,
|
14
|
+
:person_id,
|
15
|
+
:segment_id,
|
16
|
+
:search_id,
|
17
|
+
:organization,
|
18
|
+
:membership_type,
|
19
|
+
:number_of_memberships,
|
20
|
+
:ends_at,
|
21
|
+
:welcome_message,
|
22
|
+
:send_email,
|
23
|
+
:notes,
|
24
|
+
:sold_price,
|
25
|
+
:benefactor
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
self.people = []
|
29
|
+
end
|
30
|
+
|
31
|
+
def persisted?
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
def people_ids
|
36
|
+
people.nil? ? [] : people.collect(&:id)
|
37
|
+
end
|
38
|
+
|
39
|
+
def people_belong_to_this_org
|
40
|
+
people.each do |person|
|
41
|
+
Rails.logger.info "FLARG #{person.organization.id} #{organization}"
|
42
|
+
if person.organization != organization
|
43
|
+
errors.add(:base, "Person does not belong to this organization. Please contact Artful.ly support if this problem persists.")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Weird Rails/DJ bug where calling validate on membership_comp caused the errors
|
50
|
+
# to hang around. DJ would choke when Syck tried to deserialize the errors attr
|
51
|
+
# The error message was horrible and misleading (uninitialized constant Syck::Syck)
|
52
|
+
#
|
53
|
+
# It's probably because we're including ActiveModel::Validations on MembershipComp
|
54
|
+
#
|
55
|
+
def clear_errors
|
56
|
+
@errors = nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def we_have_some_people
|
60
|
+
if people.blank?
|
61
|
+
errors.add(:base, "You haven't selected any people to receive memberships")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def expiration_date_cannot_be_in_the_past
|
66
|
+
if !ends_at.present?
|
67
|
+
errors.add(:base, "Please enter a membership expiration date")
|
68
|
+
end
|
69
|
+
|
70
|
+
if DateTime.parse(ends_at) < Date.today
|
71
|
+
errors.add(:base, "Membership expiration date can't be in the past")
|
72
|
+
end
|
73
|
+
|
74
|
+
rescue
|
75
|
+
errors.add(:base, "Please enter a membership expiration date")
|
76
|
+
end
|
77
|
+
|
78
|
+
def award
|
79
|
+
return false unless valid?
|
80
|
+
MembershipCompJob.enqueue self
|
81
|
+
end
|
82
|
+
|
83
|
+
def perform
|
84
|
+
self.people.each do |person|
|
85
|
+
next if person.email.blank?
|
86
|
+
next if person.company?
|
87
|
+
memberships = []
|
88
|
+
self.number_of_memberships.to_i.times do
|
89
|
+
membership = Membership.for(self.membership_type)
|
90
|
+
membership.ends_at = self.ends_at
|
91
|
+
membership.sold_price = 0
|
92
|
+
membership.welcome_message = self.welcome_message
|
93
|
+
membership.send_email = self.send_email
|
94
|
+
membership.save
|
95
|
+
memberships << membership
|
96
|
+
end
|
97
|
+
|
98
|
+
comp = Comp.new(nil, [], memberships, person, self.benefactor)
|
99
|
+
comp.notes = self.notes
|
100
|
+
comp.submit
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|