artfully_ose 1.2.0.pre.27 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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