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.
Files changed (89) hide show
  1. checksums.yaml +8 -8
  2. data/README.md +4 -6
  3. data/app/assets/javascripts/application.js +39 -0
  4. data/app/assets/javascripts/boxoffice.js +558 -0
  5. data/app/assets/javascripts/custom/kits-config.js +33 -0
  6. data/app/assets/javascripts/sales-console.js +29 -24
  7. data/app/assets/javascripts/store/store.js +11 -0
  8. data/app/assets/stylesheets/application.sass +66 -52
  9. data/app/assets/stylesheets/bootstrap-overrides.css +1 -1
  10. data/app/assets/stylesheets/boxoffice.css.scss +258 -0
  11. data/app/assets/stylesheets/pages/bootstrap-responsive.css +1109 -0
  12. data/app/assets/stylesheets/sass/store.sass +52 -62
  13. data/app/assets/stylesheets/storefront.css +1 -1
  14. data/app/concerns/itemable.rb +1 -1
  15. data/app/concerns/pdf_generation.rb +1 -11
  16. data/app/concerns/search_by_dates.rb +20 -0
  17. data/app/controllers/events_controller.rb +1 -0
  18. data/app/controllers/export_controller.rb +11 -1
  19. data/app/controllers/imports_controller.rb +2 -3
  20. data/app/controllers/member_cards_controller.rb +2 -3
  21. data/app/controllers/orders_controller.rb +42 -0
  22. data/app/controllers/organizations_controller.rb +1 -0
  23. data/app/controllers/passes_reports_controller.rb +21 -1
  24. data/app/controllers/regular_donation_kits_controller.rb +32 -0
  25. data/app/controllers/store/donations_controller.rb +19 -1
  26. data/app/controllers/store/orders_controller.rb +15 -5
  27. data/app/helpers/artfully_ose_helper.rb +28 -18
  28. data/app/models/database_views/item_view.rb +41 -0
  29. data/app/models/event.rb +10 -2
  30. data/app/models/ext/integrations.rb +5 -0
  31. data/app/models/fee_strategy.rb +47 -1
  32. data/app/models/imports/events_import.rb +6 -7
  33. data/app/models/item.rb +4 -3
  34. data/app/models/job/order_processor.rb +4 -3
  35. data/app/models/kit.rb +18 -0
  36. data/app/models/kits/regular_donation_kit.rb +51 -9
  37. data/app/models/member.rb +4 -3
  38. data/app/models/membership.rb +1 -2
  39. data/app/models/membership_sale_search.rb +2 -20
  40. data/app/models/membership_type.rb +1 -2
  41. data/app/models/order.rb +29 -1
  42. data/app/models/order_handler.rb +4 -3
  43. data/app/models/organization.rb +17 -9
  44. data/app/models/pass.rb +18 -17
  45. data/app/models/pass_sale_search.rb +18 -0
  46. data/app/models/pass_type.rb +7 -2
  47. data/app/models/passes_report.rb +117 -0
  48. data/app/models/sale_search.rb +3 -21
  49. data/app/models/user.rb +1 -2
  50. data/app/serializers/show_serializer.rb +15 -0
  51. data/app/views/contributions/index.html.haml +2 -2
  52. data/app/views/events/_header.html.haml +1 -0
  53. data/app/views/events/_ticket_type_fields.html.haml +31 -15
  54. data/app/views/events/edit.html.haml +6 -1
  55. data/app/views/imports/_export_links.html.haml +3 -0
  56. data/app/views/imports/donations/_pending.html.haml +24 -6
  57. data/app/views/imports/events/_pending.html.haml +16 -5
  58. data/app/views/imports/index.html.haml +6 -4
  59. data/app/views/imports/people/_pending.html.haml +20 -25
  60. data/app/views/layouts/storefront.html.haml +9 -5
  61. data/app/views/order_mailer/confirmation_for.html.haml +11 -5
  62. data/app/views/order_mailer/confirmation_for_refund.html.haml +6 -2
  63. data/app/views/order_mailer/confirmation_for_refund.text.haml +4 -3
  64. data/app/views/orders/membership.html.haml +15 -19
  65. data/app/views/orders/passes.html.haml +97 -0
  66. data/app/views/orders/sales.html.haml +2 -2
  67. data/app/views/organizations/_form.html.haml +4 -37
  68. data/app/views/pass_types/index.html.haml +1 -1
  69. data/app/views/passes_reports/index.html.haml +79 -1
  70. data/app/views/people/show.html.haml +16 -15
  71. data/app/views/regular_donation_kits/edit.html.haml +140 -0
  72. data/app/views/store/checkouts/thanks.html.haml +14 -6
  73. data/app/views/store/donations/index.html.haml +19 -2
  74. data/app/views/store/events/calendar.html.haml +1 -0
  75. data/app/views/store/events/index.html.haml +1 -0
  76. data/app/views/store/events/show.html.haml +1 -0
  77. data/app/views/store/events/single_show.html.haml +2 -0
  78. data/app/views/store/orders/show.html.haml +10 -10
  79. data/app/views/store/passes/index.html.haml +2 -2
  80. data/app/views/store/shared/_donate_form.html.haml +31 -12
  81. data/app/views/store/shared/_small_donate_form.html.haml +22 -0
  82. data/app/views/users/invitations/edit.html.haml +3 -42
  83. data/config/locales/en.yml +3 -1
  84. data/config/routes.rb +4 -1
  85. data/db/migrate/20140603200735_add_subtitle_to_events.rb +5 -0
  86. data/lib/artfully_ose/version.rb +1 -1
  87. data/spec/factories/kit_factories.rb +5 -4
  88. metadata +25 -13
  89. data/app/assets/javascripts/box-office.js +0 -262
@@ -13,8 +13,8 @@ module ArtfullyOseHelper
13
13
  end
14
14
  end
15
15
 
16
- def with_kit(kit)
17
- organization = current_user.try(:current_organization) || @store_organization
16
+ def with_kit(kit, organization = nil)
17
+ organization = organization || current_user.try(:current_organization) || @store_organization
18
18
  if organization.has_kit? kit
19
19
  yield
20
20
  end
@@ -29,11 +29,11 @@ module ArtfullyOseHelper
29
29
  def build_order_location(order)
30
30
  order.location
31
31
  end
32
-
32
+
33
33
  def channel_checkbox(channel)
34
34
  channel.to_s.eql?("storefront") ? "Storefront & Widgets" : channel.to_s.humanize
35
35
  end
36
-
36
+
37
37
  def channel_text(channel)
38
38
  case channel.to_s
39
39
  when "members"
@@ -44,7 +44,7 @@ module ArtfullyOseHelper
44
44
  channel.to_s.humanize
45
45
  end
46
46
  end
47
-
47
+
48
48
  def build_action_path(target, action)
49
49
  action_path_name = action.new_record? ? "actions" : "actions"
50
50
  "#{target.class.name.downcase}_#{action_path_name}_path"
@@ -71,17 +71,17 @@ module ArtfullyOseHelper
71
71
  def get_selected_class(selected_object, menu_object)
72
72
  selected_object.try(:id) == menu_object.id ? "active" : "unselected"
73
73
  end
74
-
74
+
75
75
  #For use with Font Awesome icon %i classes
76
76
  def icon_link_to(text, href, icon, class_names, id, html_attributes={})
77
77
  s = "<a href='#{href}' class='#{class_names}' id='#{id}' "
78
78
  html_attributes.each do |k,v|
79
- s = s + " #{k}=#{v} "
79
+ s = s + " #{k}=#{v} "
80
80
  end
81
81
  s = s + "><i class='fa #{icon}'></i> #{text}</a>"
82
82
  s.html_safe
83
83
  end
84
-
84
+
85
85
  #
86
86
  # just name the image, this method will prepend the path and append the .png
87
87
  # icon_tag('111-logo')
@@ -93,12 +93,12 @@ module ArtfullyOseHelper
93
93
  def time_zone_description(tz)
94
94
  ActiveSupport::TimeZone.create(tz)
95
95
  end
96
-
96
+
97
97
  #This is for the widget generator, DO NOT use anywhere else
98
98
  def fully_qualified_asset_path(asset)
99
99
  "#{asset_path(asset, :digest => false)}"
100
100
  end
101
-
101
+
102
102
  def events_to_options(selected_event_id = nil)
103
103
  @events = current_user.current_organization.events
104
104
  @events_array = @events.map { |event| [event.name, event.id] }
@@ -132,7 +132,7 @@ module ArtfullyOseHelper
132
132
  str += " (#{number_as_cents item.nongift_amount} Non-deductible)" unless item.nongift_amount.nil? || item.nongift_amount == 0
133
133
  str
134
134
  end
135
-
135
+
136
136
  #This method will not prepend the $
137
137
  def number_to_dollars(cents)
138
138
  cents.to_i / 100.00
@@ -241,7 +241,7 @@ module ArtfullyOseHelper
241
241
 
242
242
  select_tag show_id, raw(options), :class => "span3"
243
243
  end
244
-
244
+
245
245
  def select_membership_type_for_sales_search(membership_types, membership_type_id, default)
246
246
  options =
247
247
  [
@@ -252,10 +252,20 @@ module ArtfullyOseHelper
252
252
  select_tag membership_type_id, raw(options), :class => "span3"
253
253
  end
254
254
 
255
+ def select_pass_type_for_sales_search(pass_types, pass_type_id, default)
256
+ options =
257
+ [
258
+ content_tag(:option, " --- All Pass Types --- ", :value => ""),
259
+ options_from_collection_for_select(pass_types, :id, :name, default)
260
+ ].join
261
+
262
+ select_tag pass_type_id, raw(options), :class => "span3"
263
+ end
264
+
255
265
  def nav_dropdown(text, link='#')
256
266
  link_to ERB::Util.html_escape(text) + ' <b class="caret"></b>'.html_safe, link, :class => 'dropdown-toggle', 'data-toggle' => 'dropdown'
257
267
  end
258
-
268
+
259
269
  def bootstrapped_type(type)
260
270
  case type
261
271
  when :notice then "alert alert-info"
@@ -268,7 +278,7 @@ module ArtfullyOseHelper
268
278
  def link_to_remove_fields(name, f)
269
279
  f.hidden_field(:_destroy) + link_to(name, "#", :onclick => "remove_fields(this); return false;")
270
280
  end
271
-
281
+
272
282
  def link_to_add_fields(name, f, association, view_path = '', additional_javascript=nil)
273
283
  new_object = f.object.class.reflect_on_association(association).klass.new
274
284
  fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
@@ -278,17 +288,17 @@ module ArtfullyOseHelper
278
288
  end
279
289
  link_to name, "#", :onclick => "add_fields(this, \"#{association}\", \"#{escape_javascript(fields)}\"); #{additional_javascript} return false;"
280
290
  end
281
-
291
+
282
292
  def ticket_seller_name(ticket)
283
293
  end
284
-
294
+
285
295
  def credit_card_message
286
296
  end
287
-
297
+
288
298
  def date_field_tag(name, value = nil, options = {})
289
299
  text_field_tag(name, value, options.stringify_keys.update("type" => "date"))
290
300
  end
291
-
301
+
292
302
  def datetime_field_tag(name, value = nil, options = {})
293
303
  text_field_tag(name, value, options.stringify_keys.update("type" => "datetime"))
294
304
  end
@@ -145,4 +145,45 @@ class ItemView < ActiveRecord::Base
145
145
  person("Do Not Email") { |person| person.do_not_email }
146
146
  person("Do Not Call") { |person| person.do_not_call }
147
147
  end
148
+
149
+ comma :pass_sale do
150
+ created_at_local_to_organization("Date of Purchase")
151
+
152
+ item('Pass Type') { |item| item.product.pass_type.name }
153
+ payment_method("Payment Method")
154
+ price("Price") { |cents| number_to_currency(cents.to_f/100) }
155
+ special_instructions("Special Instructions")
156
+ notes("Notes")
157
+
158
+ person("Email") { |person| person.email }
159
+ person("Salutation") { |person| person.salutation }
160
+ person("First Name") { |person| person.first_name }
161
+ person("Middle Name") { |person| person.middle_name }
162
+ person("Last Name") { |person| person.last_name }
163
+ person("Suffix") { |person| person.suffix }
164
+ person("Title") { |person| person.title }
165
+ person("Type") { |person| person.type }
166
+ person("Subtype") { |person| person.subtype }
167
+ person("Company Name") { |person| person.company_name }
168
+
169
+ person("Address 1") { |person| person.address && person.address.address1 }
170
+ person("Address 2") { |person| person.address && person.address.address2 }
171
+ person("City") { |person| person.address && person.address.city }
172
+ person("State") { |person| person.address && person.address.state }
173
+ person("Zip") { |person| person.address && person.address.zip }
174
+ person("Country") { |person| person.address && person.address.country }
175
+ person("Phone1 type") { |person| person.phones[0] && person.phones[0].kind }
176
+ person("Phone1 number") { |person| person.phones[0] && person.phones[0].number }
177
+ person("Phone2 type") { |person| person.phones[1] && person.phones[1].kind }
178
+ person("Phone2 number") { |person| person.phones[1] && person.phones[1].number }
179
+ person("Phone3 type") { |person| person.phones[2] && person.phones[2].kind }
180
+ person("Phone3 number") { |person| person.phones[2] && person.phones[2].number }
181
+ person("Website") { |person| person.website }
182
+ person("Twitter Handle") { |person| person.twitter_handle }
183
+ person("Facebook URL") { |person| person.facebook_url }
184
+ person("Linked In Url") { |person| person.linked_in_url }
185
+ person("Tags") { |person| person.tags.join("|") }
186
+ person("Do Not Email") { |person| person.do_not_email }
187
+ person("Do Not Call") { |person| person.do_not_call }
188
+ end
148
189
  end
@@ -10,8 +10,9 @@ class Event < ActiveRecord::Base
10
10
 
11
11
  attr_accessible :name, :producer, :description, :contact_email, :contact_phone, :image, :venue_attributes,
12
12
  :show_special_instructions, :special_instructions_caption, :public, :primary_category,
13
- :secondary_categories, :primary_category_other, :secondary_category_other, :members_only
14
-
13
+ :secondary_categories, :primary_category_other, :secondary_category_other, :members_only,
14
+ :subtitle
15
+
15
16
  store :cached_stats, :accessors => [ :on_sale, :off_sale, :sold, :sales_total ]
16
17
 
17
18
  belongs_to :organization
@@ -28,6 +29,7 @@ class Event < ActiveRecord::Base
28
29
  validates :contact_email, :presence => true, :email => true
29
30
  validates :name, :presence => true
30
31
  validates :organization_id, :presence => true
32
+ validate :subtitle, length: { maximum: 255 }
31
33
 
32
34
  has_attached_file :image,
33
35
  :storage => :s3,
@@ -59,9 +61,15 @@ class Event < ActiveRecord::Base
59
61
  ANY_EVENT_TEXT = "(Any Event)"
60
62
  ANY_EVENT_ID = "-1"
61
63
 
64
+ searchable do
65
+ text :name
66
+ end
67
+ include Ext::DelayedIndexing
68
+
62
69
  #
63
70
  # Run synchronously only with great care.
64
71
  #
72
+
65
73
  def refresh_stats
66
74
  self.on_sale = self.glance.available.on_sale
67
75
  self.off_sale = self.glance.available.off_sale
@@ -3,6 +3,10 @@ module Ext
3
3
  module Member
4
4
  end
5
5
 
6
+ module ServiceFee
7
+ SERVICE_FEE = 0
8
+ end
9
+
6
10
  module User
7
11
  def need_more_info?
8
12
  false
@@ -46,6 +50,7 @@ module Ext
46
50
  kit.save
47
51
  end
48
52
  end
53
+ validates_presence_of :name, :email, :time_zone
49
54
  end
50
55
  end
51
56
 
@@ -1,5 +1,51 @@
1
1
  class FeeStrategy
2
+ def ticket_fee
3
+ ARTFULLY_CONFIG[:ticket_fee] || 0
4
+ end
5
+
2
6
  def apply_to_cart(cart)
3
- #noop
7
+ return if cart.is_a? BoxOffice::Cart
8
+
9
+ handle_tickets(cart)
10
+ handle_memberships(cart)
11
+ handle_passes(cart)
12
+ end
13
+
14
+ def handle_tickets(cart)
15
+ cart.tickets.each do |ticket|
16
+ if ticket.price > 0
17
+ ticket.service_fee = ticket_fee
18
+ elsif ticket.price == 0
19
+ ticket.service_fee = 0
20
+ end
21
+
22
+ if ticket.cart_price == 0 && waive_fee_for?(ticket)
23
+ ticket.service_fee = 0
24
+ end
25
+
26
+ ticket.save
27
+ end
28
+ end
29
+
30
+ def handle_memberships(cart)
31
+ cart.memberships.each do |membership|
32
+ membership.service_fee = membership.membership_type.hide_fee? ? 0 : (membership.cart_price || membership.price) * MembershipType::SERVICE_FEE
33
+ membership.save
34
+ end
35
+ end
36
+
37
+ def handle_passes(cart)
38
+ cart.passes.each do |pass|
39
+ pass.service_fee = pass.pass_type.hide_fee? ? 0 : pass.price * PassType::SERVICE_FEE
40
+ pass.save
41
+ end
42
+ end
43
+
44
+ def waive_fee_for?(ticket)
45
+ #
46
+ # This match is too tightly coupled to discount. Also, horrible.
47
+ # Check needs to be made because cart_price == 0 && BOGO means fee is applied
48
+ #
49
+ (ticket.discount.try(:promotion_type) == "DollarsOffTickets") || ticket.pass.present?
4
50
  end
5
51
  end
@@ -193,14 +193,13 @@ class EventsImport < Import
193
193
  order
194
194
  end
195
195
 
196
- def events_hash
197
- return @events if @events
198
- @events = {}
199
- parsed_rows.each do |row|
196
+ def parse_and_hash(rows)
197
+ rows.reduce({}) do | all,imported_row|
198
+ row = ParsedRow.new(headers, imported_row.content)
200
199
  key = (row.event_name || "") + (row.venue_name || "") + (row.show_date.to_s || "")
201
- @events[key] ||= []
202
- @events[key] << row
200
+ all[key] ||= []
201
+ all[key] << row
202
+ all
203
203
  end
204
- @events
205
204
  end
206
205
  end
@@ -22,9 +22,10 @@ class Item < ActiveRecord::Base
22
22
  validates_presence_of :product_type, :price, :realized_price, :net
23
23
  validates_inclusion_of :product_type, :in => %( Ticket Donation Membership Pass)
24
24
 
25
- scope :donation, where(:product_type => 'Donation')
26
- scope :membership, where(:product_type => 'Membership')
27
- scope :ticket, where(:product_type => 'Ticket')
25
+ scope :donation, where(:product_type => 'Donation')
26
+ scope :membership, where(:product_type => 'Membership')
27
+ scope :ticket, where(:product_type => 'Ticket')
28
+ scope :sold_or_comped, where("state in ('purchased','comped','settled')")
28
29
 
29
30
  scope :imported, joins(:order).merge(Order.imported)
30
31
  scope :not_imported, joins(:order).merge(Order.not_imported)
@@ -85,9 +85,10 @@ class OrderProcessor < Struct.new(:order, :options)
85
85
  end
86
86
 
87
87
  def generate_pdf
88
- pdf_generator = PdfGeneration.new(order)
89
- download_url = pdf_generator.generate
90
- order.pdf = URI.parse(download_url)
88
+ pdf = PdfGeneration.new(order).generate
89
+ file = Tempfile.new(["#{order.id}", '.pdf'])
90
+ file << pdf.force_encoding("UTF-8")
91
+ order.pdf = file
91
92
  order.save
92
93
  end
93
94
  end
@@ -23,6 +23,7 @@ class Kit < ActiveRecord::Base
23
23
 
24
24
  event(:activate, :success => :record_activation) { transitions :from => [:fresh, :pending], :to => :activated, :guard => :activatable? }
25
25
  event(:approve, :success => :record_approval) { transitions :from => :pending, :to => :activated, :guard => :approvable? }
26
+ event(:admin_approve, :success => :record_approval) { transitions :from => :pending, :to => :activated, :guard => :admin_approvable? }
26
27
  event(:cancel) { transitions :from => [:activated, :pending, :rejected ], :to => :cancelled }
27
28
  event(:reactivate) { transitions :from => :cancelled, :to => :activated, :guard => :activatable? }
28
29
  event(:activate_without_pending) { transitions :from => [:fresh, :pending, :cancelled], :to => :activated }
@@ -48,6 +49,11 @@ class Kit < ActiveRecord::Base
48
49
  approval_requirements[:if] << options.delete(:if) if options.has_key?(:if)
49
50
  end
50
51
 
52
+ def self.admin_approve(options)
53
+ admin_approval_requirements[:unless] << options.delete(:unless) if options.has_key?(:unless)
54
+ admin_approval_requirements[:if] << options.delete(:if) if options.has_key?(:if)
55
+ end
56
+
51
57
  def self.activation_requirements
52
58
  @requirements ||= Hash.new { |h,k| h[k] = [] }
53
59
  end
@@ -56,6 +62,10 @@ class Kit < ActiveRecord::Base
56
62
  @approval_requirements ||= Hash.new { |h,k| h[k] = [] }
57
63
  end
58
64
 
65
+ def self.admin_approval_requirements
66
+ @admin_approval_requirements ||= Hash.new { |h,k| h[k] = [] }
67
+ end
68
+
59
69
  def self.when_active(&block)
60
70
  self.ability_proc = Proc.new(&block)
61
71
  end
@@ -108,6 +118,10 @@ class Kit < ActiveRecord::Base
108
118
  check_approval
109
119
  end
110
120
 
121
+ def admin_approvable?
122
+ check_admin_approval
123
+ end
124
+
111
125
  class DuplicateError < StandardError
112
126
  end
113
127
 
@@ -124,6 +138,10 @@ class Kit < ActiveRecord::Base
124
138
  check_unlesses(self.class.approval_requirements[:unless]) and check_ifs(self.class.approval_requirements[:if])
125
139
  end
126
140
 
141
+ def check_admin_approval
142
+ check_unlesses(self.class.admin_approval_requirements[:unless]) and check_ifs(self.class.admin_approval_requirements[:if])
143
+ end
144
+
127
145
  def check_unlesses(unlesses)
128
146
  return true if unlesses.empty?
129
147
  unlesses.all? { |req| !self.send(req) }
@@ -1,35 +1,58 @@
1
1
  class RegularDonationKit < Kit
2
2
  acts_as_kit :with_approval => true do
3
+ self.configurable = true
4
+
3
5
  activate :if => :has_tax_info?
4
6
  activate :if => :exclusive?
5
- approve :unless => :no_bank_account?
7
+
8
+ #
9
+ # So, this is horrible. Sorry.
10
+ # Leaving "approve" out of this list sets the kit to auto-approve which is the
11
+ # opposite of what we want. So, this kludge is here to force the kit to
12
+ # never approve until you call kit.admin_approve
13
+ #
14
+ approve :if => :dont_approve
15
+ admin_approve :unless => :no_bank_account?
6
16
 
7
17
  when_active do |organization|
8
18
  organization.can :receive, Donation
9
19
  end
10
20
  end
11
21
 
22
+ ACCESSORS = [
23
+ :about_organization_text, :donation_ask_text, :donate_now_text, :donation_nudge_text,
24
+ :suggested_gifts, :open_gift_field, :thanks_msg_text, :email_msg_text, :donation_only_storefront,
25
+ ]
26
+
27
+ attr_accessible *ACCESSORS
28
+
29
+ store :settings, :accessors => ACCESSORS
30
+
31
+ def dont_approve
32
+ false
33
+ end
34
+
12
35
  def has_tax_info?
13
- errors.add(:requirements, "Your organization's tax information is missing or incomplete. Please complete it in order to active this kit.") unless organization.has_tax_info?
36
+ errors.add(:requirements, 'Your organization\'s tax information is missing or incomplete. Please complete it in order to active this kit.') unless organization.has_tax_info?
14
37
  organization.has_tax_info?
15
38
  end
16
-
39
+
17
40
  def friendly_name
18
- "Charity Donations"
41
+ '501(c)(3) Donations'
19
42
  end
20
-
43
+
21
44
  def pitch
22
- "Receive donations for a 501(c)(3)"
45
+ 'Receive donations for a 501(c)(3)'
23
46
  end
24
47
 
25
48
  def exclusive?
26
49
  exclusive = !organization.kits.where(:type => alternatives.collect(&:to_s)).any?
27
- errors.add(:requirements, "You have already activated a mutually exclusive kit.") unless exclusive
50
+ errors.add(:requirements, 'You have already activated a mutually exclusive kit.') unless exclusive
28
51
  exclusive
29
52
  end
30
53
 
31
54
  def no_bank_account?
32
- errors.add(:requirements, "Your organization needs bank account information first.") if organization.bank_account.nil?
55
+ errors.add(:requirements, 'Your organization needs bank account information first.') if organization.bank_account.nil?
33
56
  organization.bank_account.nil?
34
57
  end
35
58
 
@@ -41,4 +64,23 @@ class RegularDonationKit < Kit
41
64
  AdminMailer.donation_kit_notification(self).deliver
42
65
  ProducerMailer.donation_kit_notification(self, organization.owner).deliver
43
66
  end
44
- end
67
+
68
+ def suggested_gifts=(value)
69
+ value = value.values if value.is_a?(Hash)
70
+
71
+ value = value.map do |suggested_gift|
72
+ suggested_gift = { :amount => suggested_gift } if [String, Fixnum, Bignum, Float, BigDecimal].any? { |klass| suggested_gift.is_a?(klass) }
73
+ suggested_gift[:amount] = suggested_gift[:amount].to_i
74
+ suggested_gift.delete :level_name if suggested_gift[:level_name].blank?
75
+ suggested_gift.with_indifferent_access
76
+ end.reject do |suggested_gift|
77
+ suggested_gift[:amount].to_i <= 0
78
+ end.sort do |l, r|
79
+ l[:amount] <=> r[:amount]
80
+ end
81
+
82
+ self.settings = {} unless self.settings.is_a?(Hash)
83
+ self.settings[:suggested_gifts] = value
84
+ self.settings_will_change!
85
+ end
86
+ end