artfully_ose 1.2.0.pre.24 → 1.2.0.pre.26

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +9 -9
  2. data/app/assets/fonts/FontAwesome.otf +0 -0
  3. data/app/assets/fonts/fontawesome-webfont.eot +0 -0
  4. data/app/assets/fonts/fontawesome-webfont.svg +504 -0
  5. data/app/assets/fonts/fontawesome-webfont.ttf +0 -0
  6. data/app/assets/fonts/fontawesome-webfont.woff +0 -0
  7. data/app/assets/javascripts/application.js +5 -0
  8. data/app/assets/javascripts/custom/people.js +68 -5
  9. data/app/assets/javascripts/sales-console.js +153 -0
  10. data/app/assets/javascripts/store/jquery.validate.additional-methods.js +476 -159
  11. data/app/assets/javascripts/store/jquery.validate.js +449 -406
  12. data/app/assets/stylesheets/application.sass +49 -0
  13. data/app/assets/stylesheets/font-awesome.css.scss +1566 -0
  14. data/app/concerns/cart_finder.rb +5 -0
  15. data/app/controllers/console_sales_controller.rb +182 -0
  16. data/app/controllers/events_controller.rb +1 -1
  17. data/app/controllers/events_pass_types_controller.rb +10 -0
  18. data/app/controllers/memberships_controller.rb +3 -3
  19. data/app/controllers/people_controller.rb +2 -1
  20. data/app/controllers/sales_controller.rb +51 -13
  21. data/app/controllers/shows_controller.rb +2 -2
  22. data/app/controllers/store/orders_controller.rb +0 -9
  23. data/app/helpers/artfully_ose_helper.rb +4 -2
  24. data/app/helpers/sales_helper.rb +29 -0
  25. data/app/models/cart.rb +6 -1
  26. data/app/models/checkout.rb +20 -13
  27. data/app/models/comp.rb +13 -16
  28. data/app/models/console_sale.rb +30 -0
  29. data/app/models/database_views/item_view.rb +3 -0
  30. data/app/models/job/checkout_processor.rb +10 -5
  31. data/app/models/kits/mailchimp_kit.rb +2 -1
  32. data/app/models/kits/passes_kit.rb +1 -1
  33. data/app/models/kits/scannable_tickets_kit.rb +3 -4
  34. data/app/models/membership.rb +18 -3
  35. data/app/models/membership_change.rb +1 -1
  36. data/app/models/membership_type.rb +2 -3
  37. data/app/models/order.rb +20 -17
  38. data/app/models/order_handler.rb +21 -0
  39. data/app/models/orders/comp_order.rb +1 -1
  40. data/app/models/parsed_row.rb +45 -2
  41. data/app/models/pass.rb +13 -0
  42. data/app/models/pass_type.rb +16 -0
  43. data/app/models/payment.rb +17 -0
  44. data/app/models/payments/cash_payment.rb +3 -1
  45. data/app/models/payments/check_payment.rb +33 -0
  46. data/app/models/payments/comp_payment.rb +3 -1
  47. data/app/models/payments/credit_card_payment.rb +8 -18
  48. data/app/models/person.rb +27 -3
  49. data/app/models/sale.rb +13 -10
  50. data/app/models/ticket.rb +1 -1
  51. data/app/views/console_sales/_aloha.html.haml +26 -0
  52. data/app/views/console_sales/_cart.html.haml +60 -0
  53. data/app/views/console_sales/_payment.html.haml +111 -0
  54. data/app/views/console_sales/_shows.html.haml +24 -0
  55. data/app/views/console_sales/new.html.haml +143 -0
  56. data/app/views/events/_day_date_show.html.haml +2 -2
  57. data/app/views/events/_ticket_type_fields.html.haml +1 -1
  58. data/app/views/events_pass_types/_form.html.haml +1 -1
  59. data/app/views/events_pass_types/index.html.haml +2 -1
  60. data/app/views/exchanges/new.html.haml +1 -1
  61. data/app/views/imports/people/_new.html.haml +12 -0
  62. data/app/views/kits/_list.html.haml +2 -1
  63. data/app/views/layouts/_menu.html.haml +0 -3
  64. data/app/views/membership_comps/create.html.haml +3 -1
  65. data/app/views/membership_types/_form.html.haml +0 -1
  66. data/app/views/orders/_item_table.haml +5 -3
  67. data/app/views/passes/index.html.haml +2 -2
  68. data/app/views/people/_edit_modal.html.haml +7 -0
  69. data/app/views/people/_work_with_menu.html.haml +4 -1
  70. data/app/views/people/show.html.haml +13 -4
  71. data/app/views/refunds/new.html.haml +1 -1
  72. data/app/views/sales/_boxoffice.html.haml +141 -0
  73. data/app/views/sales/_doorlist.html.haml +79 -0
  74. data/app/views/sales/new.html.haml +33 -125
  75. data/app/views/searches/show.html.haml +1 -1
  76. data/app/views/segments/show.html.haml +1 -1
  77. data/app/views/shows/_ticket_table.html.haml +1 -1
  78. data/app/views/shows/_work_with.html.haml +1 -1
  79. data/app/views/shows/index.html.haml +5 -6
  80. data/app/views/shows/show.html.haml +2 -2
  81. data/config/initializers/devise.rb +1 -1
  82. data/config/initializers/paperclip.rb +16 -0
  83. data/config/routes.rb +10 -1
  84. data/db/migrate/20140507184646_add_bday_attributes_to_person.rb +7 -0
  85. data/db/migrate/20140507201048_add_check_number_to_orders.rb +5 -0
  86. data/db/migrate/20140619194127_add_do_not_call_to_people.artfully_ose_engine.rb +5 -0
  87. data/db/migrate/20140623160543_add_index_to_items.rb +7 -0
  88. data/lib/artfully_ose/engine.rb +5 -4
  89. data/lib/artfully_ose/version.rb +1 -1
  90. data/spec/factories/person_factories.rb +3 -0
  91. metadata +43 -20
@@ -0,0 +1,29 @@
1
+ module SalesHelper
2
+ def door_list_sort(person)
3
+ result = if person.last_name.present?
4
+ person.last_name
5
+ elsif person.first_name.present?
6
+ person.first_name
7
+ else
8
+ person.email
9
+ end
10
+
11
+ result.downcase
12
+ end
13
+
14
+ def door_list_grouped_attribute(door_list_items, attr)
15
+ door_list_items.map {|d| d.send(attr)}.uniq.select {|n| n.present? }.to_sentence
16
+ end
17
+
18
+ def door_list_name_and_company(person)
19
+ result = []
20
+ result << content_tag(:strong, person.first_name) if person.first_name.present?
21
+ result << " #{person.last_name}" if person.last_name.present?
22
+ if person.company_name.present?
23
+ result << ", " if result.any?
24
+ result << person.company_name
25
+ end
26
+
27
+ safe_join(result)
28
+ end
29
+ end
data/app/models/cart.rb CHANGED
@@ -34,6 +34,11 @@ class Cart < ActiveRecord::Base
34
34
 
35
35
  delegate :empty?, :to => :items
36
36
 
37
+ #
38
+ # Note that this does return Item objects. It returns Tickets, Donations, Memberships, etc...
39
+ #
40
+ # TODO: Refactor this to another name to avoid confusion with Item
41
+ #
37
42
  def items
38
43
  self.tickets + self.donations + self.memberships + self.passes
39
44
  end
@@ -133,7 +138,7 @@ class Cart < ActiveRecord::Base
133
138
  end
134
139
 
135
140
  def generate_donations
136
- organizations_from_tickets.collect do |organization|
141
+ organizations.collect do |organization|
137
142
  if organization.can?(:receive, Donation)
138
143
  donation = Donation.new
139
144
  donation.organization = organization
@@ -2,23 +2,23 @@ class Checkout
2
2
  include ActiveSupport::Callbacks
3
3
  define_callbacks :payment
4
4
  define_callbacks :order
5
-
5
+
6
6
  include Ext::Callbacks::Checkout
7
7
  include Ext::Preprocessor
8
-
9
- attr_accessor :cart, :payment, :error
8
+
9
+ attr_accessor :cart, :payment, :error, :notes
10
10
  attr_reader :order, :person
11
11
 
12
12
  def self.for(cart, payment)
13
13
  cart.checkout_class.new(cart, payment)
14
14
  end
15
-
15
+
16
16
  def message
17
17
  message = @error || @payment.errors.full_messages.to_sentence.downcase
18
18
  message = message.gsub('customer', 'contact info')
19
19
  message = message.gsub('credit card is', 'payment details are')
20
20
  message = message[0].upcase + message[1..message.length] unless message.blank? #capitalize first word
21
-
21
+
22
22
  if message.blank?
23
23
  if @fafs_success == true
24
24
  message = "We've processed your donation but could not reserve your tickets. Please check your information and try again or contact us to complete your purchase."
@@ -26,12 +26,13 @@ class Checkout
26
26
  message = "We had a problem validating your payment. Wait a few moments and try again or contact us to complete your purchase."
27
27
  end
28
28
  end
29
-
29
+
30
30
  message
31
31
  end
32
32
 
33
- def initialize(cart, payment)
33
+ def initialize(cart, payment, notes=nil)
34
34
  @cart = cart
35
+ @notes = notes
35
36
  @payment = payment
36
37
  @customer = payment.customer
37
38
  @payment.amount = @cart.total
@@ -48,7 +49,7 @@ class Checkout
48
49
  unless (!!cart and !!payment and payment.valid?)
49
50
  return false
50
51
  end
51
-
52
+
52
53
  true
53
54
  end
54
55
 
@@ -77,7 +78,7 @@ class Checkout
77
78
  void_payment if @payment_approved
78
79
  raise e
79
80
  end
80
-
81
+
81
82
  capture_payment if @payment_approved
82
83
  @payment_approved || false
83
84
  end
@@ -85,7 +86,7 @@ class Checkout
85
86
  def log(message)
86
87
  Rails.logger.info "CHECKOUT: cart [#{cart.id}] #{message}"
87
88
  end
88
-
89
+
89
90
  def pay
90
91
  log "authorizing"
91
92
  options = {}
@@ -99,7 +100,7 @@ class Checkout
99
100
  def capture_payment
100
101
  log "capturing payment [#{@transaction_id}]"
101
102
  payment.capture(@transaction_id, {})
102
- log "payment captured"
103
+ log "payment captured"
103
104
  end
104
105
 
105
106
  def void_payment
@@ -108,6 +109,10 @@ class Checkout
108
109
  log "payment voided"
109
110
  end
110
111
 
112
+ def checkout_name
113
+ "checkout"
114
+ end
115
+
111
116
  private
112
117
  def create_sub_orders(order_timestamp)
113
118
  created_orders = []
@@ -122,14 +127,14 @@ class Checkout
122
127
  attributes[:organization] = organization
123
128
 
124
129
  @person = Person.first_or_create(attributes)
125
- CheckoutProcessor.process(@person, @customer, Address.from_payment(payment), payment.payment_phone_number, organization.time_zone)
130
+ CheckoutProcessor.process(@person, @customer, Address.from_payment(payment), payment.payment_phone_number, organization.time_zone, checkout_name)
126
131
  @order = new_order(organization, order_timestamp, @person)
127
132
  log(@order.save!)
128
133
  log("Created order [#{@order.id}]")
129
134
 
130
135
  created_orders << @order
131
136
  end
132
-
137
+
133
138
  created_orders
134
139
  end
135
140
 
@@ -147,9 +152,11 @@ class Checkout
147
152
  order.created_at = order_timestamp
148
153
  order.person = @person
149
154
  order.transaction_id = @payment.transaction_id
155
+ order.check_number = @payment.check_number if payment.kind_of?(CheckPayment)
150
156
  order.special_instructions = @cart.special_instructions
151
157
  order.payment_method = @payment.payment_method
152
158
  order.per_item_processing_charge = @payment.per_item_processing_charge
159
+ order.notes = @notes
153
160
 
154
161
  order << @cart.tickets.select { |ticket| ticket.organization_id == organization.id }
155
162
  order << @cart.donations
data/app/models/comp.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  class Comp
2
- include ActiveModel::Conversion
2
+ include ActiveModel::Conversion
3
3
  include ActiveModel::Validations
4
4
  extend ActiveModel::Naming
5
5
 
@@ -9,26 +9,27 @@ class Comp
9
9
  attr_accessor :comped_count, :uncomped_count, :details
10
10
 
11
11
  #tickets can be an array of tickets_ids or an array of tickets
12
- def initialize(show, tickets_or_ids, memberships, recipient, benefactor)
12
+ def initialize(show, tickets_or_ids, memberships, recipient, benefactor, notes=nil)
13
13
  @show = show
14
14
  @tickets = []
15
15
  @memberships = memberships
16
16
  load_tickets(tickets_or_ids)
17
17
  @recipient = Person.find(recipient) unless recipient.blank?
18
18
  @benefactor = benefactor
19
+ @notes = notes
19
20
  end
20
-
21
+
21
22
  def valid_recipient_and_benefactor
22
23
  if @recipient.nil?
23
24
  errors.add(:base, "Please select a person to comp to or create a new person record")
24
25
  return
25
26
  end
26
-
27
+
27
28
  if @benefactor.nil?
28
29
  errors.add(:base, "Please select a benefactor")
29
30
  return
30
31
  end
31
-
32
+
32
33
  unless @benefactor.current_organization.eql? @recipient.organization
33
34
  errors.add(:base, "Recipient and benefactor are from different organizations")
34
35
  end
@@ -44,7 +45,7 @@ class Comp
44
45
 
45
46
  def submit
46
47
  ActiveRecord::Base.transaction do
47
- create_order(@tickets, @memberships, @recipient, @benefactor)
48
+ create_order(@tickets, @memberships, @recipient, @benefactor, @notes)
48
49
  self.comped_count = tickets.size
49
50
  self.uncomped_count = 0
50
51
  end
@@ -58,15 +59,15 @@ class Comp
58
59
  @tickets << t
59
60
  end
60
61
  end
61
-
62
- def create_order(comped_tickets, comped_memberships, recipient, benefactor)
62
+
63
+ def create_order(comped_tickets, comped_memberships, recipient, benefactor, notes)
63
64
  @order = CompOrder.new
64
65
  @order << comped_tickets
65
66
  @order << comped_memberships
66
67
  @order.person = recipient
67
68
  @order.organization = benefactor.current_organization
68
- @order.details = order_details
69
- @order.notes = order_notes
69
+ @order.details = order_details
70
+ @order.notes = @notes
70
71
  @order.skip_email = true
71
72
  @order.to_comp!
72
73
  f = @order.save
@@ -74,10 +75,6 @@ class Comp
74
75
  end
75
76
 
76
77
  def order_details
77
- "Comped by: #{@benefactor.email}"
78
- end
79
-
80
- def order_notes
81
- "#{@notes}"
78
+ "Comped by: #{@benefactor.email}"
82
79
  end
83
- end
80
+ end
@@ -0,0 +1,30 @@
1
+ module ConsoleSale
2
+ class Checkout < ::Checkout
3
+ def order_class
4
+ ConsoleSale::Order
5
+ end
6
+
7
+ def checkout_name
8
+ "sales console"
9
+ end
10
+ end
11
+
12
+ class Cart < ::Cart
13
+ def calculate_fees(obj)
14
+ FeeCalculator.apply(ConsoleSale::FeeStrategy.new).to(self)
15
+ end
16
+ end
17
+
18
+ class Order < ::Order
19
+ def self.location
20
+ "Sales Console"
21
+ end
22
+ end
23
+
24
+ class FeeStrategy < ::FeeStrategy
25
+ def apply_to_cart(cart)
26
+ return if cart.is_a? BoxOffice::Cart
27
+ cart.items.each {|i| i.update_column(:service_fee, 0)}
28
+ end
29
+ end
30
+ end
@@ -58,6 +58,7 @@ class ItemView < ActiveRecord::Base
58
58
  person("Linked In Url") { |person| person.linked_in_url }
59
59
  person("Tags") { |person| person.tags.join("|") }
60
60
  person("Do Not Email") { |person| person.do_not_email }
61
+ person("Do Not Call") { |person| person.do_not_call }
61
62
  person("Household Name") { |person| person.household && person.household.name }
62
63
  end
63
64
 
@@ -100,6 +101,7 @@ class ItemView < ActiveRecord::Base
100
101
  person("Linked In Url") { |person| person.linked_in_url }
101
102
  person("Tags") { |person| person.tags.join("|") }
102
103
  person("Do Not Email") { |person| person.do_not_email }
104
+ person("Do Not Call") { |person| person.do_not_call }
103
105
  person("Household Name") { |person| person.household && person.household.name }
104
106
  end
105
107
 
@@ -141,5 +143,6 @@ class ItemView < ActiveRecord::Base
141
143
  person("Linked In Url") { |person| person.linked_in_url }
142
144
  person("Tags") { |person| person.tags.join("|") }
143
145
  person("Do Not Email") { |person| person.do_not_email }
146
+ person("Do Not Call") { |person| person.do_not_call }
144
147
  end
145
148
  end
@@ -7,20 +7,21 @@
7
7
  #
8
8
  # This class WILL NOT RUN ASYNC in test envs
9
9
  #
10
- class CheckoutProcessor < Struct.new(:person, :first_name, :last_name, :address_hash, :phone, :time_zone)
10
+ class CheckoutProcessor < Struct.new(:person, :first_name, :last_name, :address_hash, :phone, :time_zone, :checkout_name)
11
11
  QUEUE = "checkout"
12
12
 
13
- def initialize(person, customer, address, phone, time_zone)
13
+ def initialize(person, customer, address, phone, time_zone, checkout_name = "checkout")
14
14
  self.person = person
15
15
  self.first_name = customer.first_name
16
16
  self.last_name = customer.last_name
17
17
  self.address_hash = address.attributes.to_options unless address.nil?
18
18
  self.phone = phone
19
19
  self.time_zone = time_zone
20
+ self.checkout_name = checkout_name
20
21
  end
21
22
 
22
- def self.process(person, customer, address, phone, time_zone)
23
- job = CheckoutProcessor.new(person, customer, address, phone, time_zone)
23
+ def self.process(person, customer, address, phone, time_zone, checkout_name)
24
+ job = CheckoutProcessor.new(person, customer, address, phone, time_zone, checkout_name)
24
25
 
25
26
  if job.run_now?
26
27
  job.perform
@@ -31,11 +32,15 @@ class CheckoutProcessor < Struct.new(:person, :first_name, :last_name, :address_
31
32
 
32
33
  def perform
33
34
  self.person.update_name(self.first_name, self.last_name)
34
- self.person.update_address(self.address_hash, self.time_zone, nil, "checkout")
35
+ self.person.update_address(self.address_hash, self.time_zone, nil, checkout_name) if address_exists?
35
36
  self.person.add_phone_if_missing(self.phone)
36
37
  end
37
38
 
38
39
  def run_now?
39
40
  Rails.env.test?
40
41
  end
42
+
43
+ def address_exists?
44
+ self.address_hash.present? && self.address_hash.values.select{|values| values.present?}.any?
45
+ end
41
46
  end
@@ -7,6 +7,7 @@ class MailchimpKit < Kit
7
7
 
8
8
  state_machine do
9
9
  state :cancelled, :enter => :kit_cancelled
10
+ event(:submit_for_approval) { transitions :from => :fresh, :to => :pending }
10
11
  end
11
12
 
12
13
  when_active do
@@ -312,7 +313,7 @@ class MailchimpKit < Kit
312
313
  hear_action = HearAction.for_organization(organization)
313
314
  hear_action.details = %{"#{data["subject"].truncate(25)}" delivered to #{list_name(list_id)} MailChimp list.}
314
315
  hear_action.occurred_at = occurred_at
315
- hear_action.subtype = "Email (Sent)"
316
+ hear_action.subtype = "Mailchimp (Sent)"
316
317
  hear_action.person = person
317
318
  hear_action.save
318
319
  end
@@ -5,7 +5,7 @@ class PassesKit < Kit
5
5
  approve :unless => :no_bank_account?
6
6
 
7
7
  self.configurable = true
8
-
8
+
9
9
  state_machine do
10
10
  state :cancelled, :enter => :kit_cancelled
11
11
  end
@@ -1,12 +1,11 @@
1
1
  class ScannableTicketsKit < Kit
2
2
 
3
- acts_as_kit :admin_only => true do
4
- approve :unless => :no_bank_account?
5
-
3
+ acts_as_kit do
6
4
  self.configurable = false
7
5
 
8
6
  state_machine do
9
7
  state :cancelled, :enter => :kit_cancelled
8
+ event(:submit_for_approval) { transitions :from => :fresh, :to => :pending }
10
9
  end
11
10
 
12
11
  when_active do |organization|
@@ -16,7 +15,7 @@ class ScannableTicketsKit < Kit
16
15
 
17
16
  def friendly_name
18
17
  "Scannable Tickets"
19
- end
18
+ end
20
19
 
21
20
  def pitch
22
21
  "Your patrons will receive barcoded tickets that you can scan at the door."
@@ -4,6 +4,7 @@
4
4
  #
5
5
  class Membership < ActiveRecord::Base
6
6
  include Extendable
7
+ extend ActionView::Helpers::TextHelper
7
8
 
8
9
  belongs_to :organization
9
10
  belongs_to :member
@@ -20,9 +21,10 @@ class Membership < ActiveRecord::Base
20
21
  # Make sure you're scoping when you use these.
21
22
  # organization.memberships.lapsed
22
23
  #
23
- scope :current, lambda { |time = Time.now| where("ends_at > ?", time) }
24
- scope :lapsed, lambda { |time = Time.now, since = (time - 1.year)| where("ends_at < ?", time).where("ends_at > ?", since.midnight) }
25
- scope :past, lambda { |time = Time.now| where("ends_at < ?", time - 1.year) }
24
+ scope :expired, lambda { |time = Time.now| where("ends_at < ?", time) }
25
+ scope :current, lambda { |time = Time.now| where("ends_at > ?", time) }
26
+ scope :lapsed, lambda { |time = Time.now, since = (time - 1.year)| where("ends_at < ?", time).where("ends_at > ?", since.midnight) }
27
+ scope :past, lambda { |time = Time.now| where("ends_at < ?", time - 1.year) }
26
28
 
27
29
  belongs_to :changed_membership, :class_name => "Membership"
28
30
  has_one :changed_to, :class_name => "Membership", :foreign_key => "changed_membership_id"
@@ -64,6 +66,19 @@ class Membership < ActiveRecord::Base
64
66
  self.membership_type.hide_fee? ? self.cart_price * MembershipType::SERVICE_FEE : 0
65
67
  end
66
68
 
69
+ def self.to_sentence(memberships)
70
+ if memberships.any?
71
+ membership_types = memberships.collect(&:membership_type).uniq
72
+ if membership_types.length > 1
73
+ "multiple memberships"
74
+ else
75
+ pluralize(memberships.length, "#{memberships.first.membership_type.membershipize}")
76
+ end
77
+ else
78
+ "No memberships"
79
+ end
80
+ end
81
+
67
82
  def expired?
68
83
  ends_at < Time.now
69
84
  end
@@ -176,7 +176,7 @@ class MembershipChange
176
176
  class Order < ::Order
177
177
  before_create :set_details
178
178
 
179
- def membership_action_class
179
+ def action_class
180
180
  ChangeAction
181
181
  end
182
182