spree_core 1.3.2 → 1.3.3

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 (182) hide show
  1. checksums.yaml +7 -0
  2. data/app/assets/images/credit_cards/icons/diners_club.png +0 -0
  3. data/app/assets/javascripts/admin/address_states.js +1 -1
  4. data/app/assets/javascripts/admin/admin.js.erb +18 -13
  5. data/app/assets/javascripts/admin/calculator.js +2 -2
  6. data/app/assets/javascripts/admin/checkouts/edit.js +3 -1
  7. data/app/assets/javascripts/admin/image_settings.js.erb +5 -5
  8. data/app/assets/javascripts/admin/orders/edit.js +1 -1
  9. data/app/assets/javascripts/admin/payments/new.js +9 -2
  10. data/app/assets/javascripts/admin/spree_core.js +13 -1
  11. data/app/assets/javascripts/admin/taxon_autocomplete.js.erb +2 -1
  12. data/app/assets/javascripts/admin/variant_autocomplete.js.erb +23 -19
  13. data/app/assets/javascripts/spree.js.coffee +37 -0
  14. data/app/assets/javascripts/store/checkout.js.coffee +8 -0
  15. data/app/assets/javascripts/store/product.js.coffee +1 -1
  16. data/app/assets/javascripts/store/spree_core.js +1 -0
  17. data/app/assets/stylesheets/admin/components/_sidebar.scss +7 -6
  18. data/app/assets/stylesheets/admin/components/_states.scss +16 -12
  19. data/app/assets/stylesheets/admin/globals/_variables.scss +102 -96
  20. data/app/assets/stylesheets/admin/shared/_forms.scss +2 -1
  21. data/app/assets/stylesheets/admin/shared/_icons.scss +2 -1
  22. data/app/assets/stylesheets/admin/shared/_layout.scss +4 -1
  23. data/app/assets/stylesheets/admin/shared/_tables.scss +8 -0
  24. data/app/assets/stylesheets/store/screen.css.scss +28 -16
  25. data/app/assets/stylesheets/store/variables.css.scss +4 -2
  26. data/app/controllers/spree/admin/adjustments_controller.rb +7 -3
  27. data/app/controllers/spree/admin/base_controller.rb +10 -5
  28. data/app/controllers/spree/admin/line_items_controller.rb +3 -2
  29. data/app/controllers/spree/admin/orders/customer_details_controller.rb +3 -2
  30. data/app/controllers/spree/admin/orders_controller.rb +2 -2
  31. data/app/controllers/spree/admin/payments_controller.rb +1 -1
  32. data/app/controllers/spree/admin/resource_controller.rb +5 -5
  33. data/app/controllers/spree/admin/shipments_controller.rb +1 -1
  34. data/app/controllers/spree/admin/taxons_controller.rb +1 -1
  35. data/app/controllers/spree/admin/users_controller.rb +96 -0
  36. data/app/controllers/spree/checkout_controller.rb +18 -7
  37. data/app/controllers/spree/locale_controller.rb +1 -1
  38. data/app/controllers/spree/orders_controller.rb +20 -2
  39. data/app/controllers/spree/taxons_controller.rb +8 -2
  40. data/app/helpers/spree/admin/images_helper.rb +6 -2
  41. data/app/helpers/spree/admin/navigation_helper.rb +23 -3
  42. data/app/helpers/spree/base_helper.rb +9 -5
  43. data/app/helpers/spree/orders_helper.rb +13 -0
  44. data/app/helpers/spree/products_helper.rb +9 -4
  45. data/app/mailers/spree/base_mailer.rb +16 -0
  46. data/app/mailers/spree/order_mailer.rb +12 -11
  47. data/app/mailers/spree/shipment_mailer.rb +4 -7
  48. data/app/mailers/spree/test_mailer.rb +2 -3
  49. data/app/models/spree/adjustment.rb +1 -1
  50. data/app/models/spree/app_configuration.rb +9 -3
  51. data/app/models/spree/calculator/default_tax.rb +5 -1
  52. data/app/models/spree/calculator/per_item.rb +1 -1
  53. data/app/models/spree/classification.rb +7 -0
  54. data/app/models/spree/credit_card.rb +1 -5
  55. data/app/models/spree/gateway.rb +10 -2
  56. data/app/models/spree/gateway/bogus.rb +5 -5
  57. data/app/models/spree/inventory_unit.rb +4 -4
  58. data/app/models/spree/line_item.rb +1 -1
  59. data/app/models/spree/order.rb +15 -35
  60. data/app/models/spree/order/checkout.rb +52 -0
  61. data/app/models/spree/order_populator.rb +11 -3
  62. data/app/models/spree/order_updater.rb +3 -4
  63. data/app/models/spree/payment.rb +23 -1
  64. data/app/models/spree/payment/processing.rb +18 -5
  65. data/app/models/spree/payment_method.rb +4 -0
  66. data/app/models/spree/price.rb +2 -3
  67. data/app/models/spree/product.rb +12 -4
  68. data/app/models/spree/product/scopes.rb +29 -16
  69. data/app/models/spree/return_authorization.rb +1 -1
  70. data/app/models/spree/shipment.rb +1 -1
  71. data/app/models/spree/taxon.rb +21 -6
  72. data/app/models/spree/variant.rb +7 -2
  73. data/app/views/spree/admin/adjustments/_adjustments_table.html.erb +2 -2
  74. data/app/views/spree/admin/countries/index.html.erb +1 -0
  75. data/app/views/spree/admin/general_settings/edit.html.erb +10 -2
  76. data/app/views/spree/admin/images/index.html.erb +7 -3
  77. data/app/views/spree/admin/mail_methods/index.html.erb +2 -2
  78. data/app/views/spree/admin/orders/_form.html.erb +3 -3
  79. data/app/views/spree/admin/orders/_line_item.html.erb +1 -1
  80. data/app/views/spree/admin/orders/customer_details/_form.html.erb +4 -4
  81. data/app/views/spree/admin/orders/index.html.erb +60 -52
  82. data/app/views/spree/admin/payment_methods/_form.html.erb +2 -2
  83. data/app/views/spree/admin/payment_methods/index.html.erb +39 -35
  84. data/app/views/spree/admin/payments/_list.html.erb +1 -1
  85. data/app/views/spree/admin/payments/source_forms/_gateway.html.erb +10 -10
  86. data/app/views/spree/admin/products/_form.html.erb +7 -12
  87. data/app/views/spree/admin/products/index.html.erb +13 -15
  88. data/app/views/spree/admin/products/new.html.erb +3 -1
  89. data/app/views/spree/admin/return_authorizations/_form.html.erb +7 -7
  90. data/app/views/spree/admin/return_authorizations/edit.html.erb +17 -11
  91. data/app/views/spree/admin/return_authorizations/index.html.erb +4 -4
  92. data/app/views/spree/admin/return_authorizations/new.html.erb +13 -6
  93. data/app/views/spree/admin/shared/_alert.html.erb +1 -1
  94. data/app/views/spree/admin/shared/_configuration_menu.html.erb +6 -2
  95. data/app/views/spree/admin/shared/_order_details.html.erb +6 -6
  96. data/app/views/spree/admin/shared/_order_tabs.html.erb +20 -37
  97. data/app/views/spree/admin/shared/_routes.html.erb +1 -1
  98. data/app/views/spree/admin/shared/_tabs.html.erb +1 -2
  99. data/app/views/spree/admin/shared/_update_order_state.js +1 -1
  100. data/app/views/spree/admin/shipments/_form.html.erb +24 -24
  101. data/app/views/spree/admin/shipments/edit.html.erb +13 -13
  102. data/app/views/spree/admin/shipments/index.html.erb +35 -31
  103. data/app/views/spree/admin/shipping_methods/index.html.erb +35 -32
  104. data/app/views/spree/admin/tax_rates/_form.html.erb +2 -1
  105. data/app/views/spree/admin/taxons/_form.html.erb +17 -0
  106. data/app/views/spree/admin/taxons/edit.html.erb +3 -1
  107. data/app/views/spree/admin/trackers/_form.html.erb +2 -2
  108. data/app/views/spree/admin/trackers/index.html.erb +2 -2
  109. data/app/views/spree/admin/users/_form.html.erb +37 -0
  110. data/app/views/spree/admin/users/edit.html.erb +28 -0
  111. data/app/views/spree/admin/users/index.html.erb +53 -0
  112. data/app/views/spree/admin/users/new.html.erb +23 -0
  113. data/app/views/spree/admin/users/show.html.erb +21 -0
  114. data/app/views/spree/admin/variants/edit.html.erb +6 -4
  115. data/app/views/spree/admin/variants/index.html.erb +1 -1
  116. data/app/views/spree/admin/zones/_form.html.erb +4 -4
  117. data/app/views/spree/admin/zones/index.html.erb +3 -1
  118. data/app/views/spree/checkout/_delivery.html.erb +1 -1
  119. data/app/views/spree/checkout/_payment.html.erb +1 -1
  120. data/app/views/spree/checkout/_summary.html.erb +2 -2
  121. data/app/views/spree/checkout/payment/_gateway.html.erb +6 -6
  122. data/app/views/spree/layouts/spree_application.html.erb +0 -1
  123. data/app/views/spree/order_mailer/cancel_email.text.erb +1 -1
  124. data/app/views/spree/order_mailer/confirm_email.text.erb +1 -1
  125. data/app/views/spree/orders/_adjustments.html.erb +1 -1
  126. data/app/views/spree/orders/_line_item.html.erb +2 -2
  127. data/app/views/spree/orders/edit.html.erb +1 -1
  128. data/app/views/spree/payments/_payment.html.erb +16 -0
  129. data/app/views/spree/products/_cart_form.html.erb +2 -2
  130. data/app/views/spree/products/_thumbnails.html.erb +3 -3
  131. data/app/views/spree/products/index.html.erb +1 -1
  132. data/app/views/spree/shared/_google_analytics.html.erb +1 -1
  133. data/app/views/spree/shared/_main_nav_bar.html.erb +1 -1
  134. data/app/views/spree/shared/_order_details.html.erb +10 -24
  135. data/app/views/spree/shared/_products.html.erb +1 -1
  136. data/app/views/spree/shared/_search.html.erb +1 -1
  137. data/app/views/spree/taxons/show.html.erb +1 -1
  138. data/config/initializers/check_for_orphaned_preferences.rb +5 -2
  139. data/config/locales/en.yml +24 -5
  140. data/config/routes.rb +4 -3
  141. data/db/default/spree/countries.rb +229 -0
  142. data/db/default/spree/roles.rb +2 -0
  143. data/db/default/spree/states.rb +57 -0
  144. data/db/default/spree/zones.rb +17 -0
  145. data/db/migrate/20130207155350_add_order_id_index_to_payments.rb +9 -0
  146. data/db/migrate/20130208032954_add_primary_to_spree_products_taxons.rb +5 -0
  147. data/db/migrate/20130222032153_add_order_id_index_to_shipments.rb +5 -0
  148. data/db/migrate/20130226032817_change_meta_description_on_spree_products_to_text.rb +5 -0
  149. data/db/migrate/20130226054936_add_variant_id_index_to_spree_prices.rb +5 -0
  150. data/db/migrate/20130319062004_change_orders_total_precision.rb +8 -0
  151. data/db/migrate/20130319063911_change_spree_payments_amount_precision.rb +7 -0
  152. data/db/migrate/20130319064308_change_spree_return_authorization_amount_precision.rb +7 -0
  153. data/db/migrate/20130319082943_change_adjustments_amount_precision.rb +7 -0
  154. data/db/migrate/20130328195253_add_seo_metas_to_taxons.rb +9 -0
  155. data/lib/generators/spree/dummy/templates/rails/database.yml +9 -6
  156. data/lib/spree/core/calculated_adjustments.rb +1 -1
  157. data/lib/spree/core/controller_helpers/auth.rb +3 -3
  158. data/lib/spree/core/controller_helpers/common.rb +1 -11
  159. data/lib/spree/core/controller_helpers/order.rb +15 -12
  160. data/lib/spree/core/engine.rb +5 -1
  161. data/lib/spree/core/mail_interceptor.rb +4 -6
  162. data/lib/spree/core/mail_settings.rb +52 -23
  163. data/lib/spree/core/permalinks.rb +1 -3
  164. data/lib/spree/core/testing_support/authorization_helpers.rb +1 -1
  165. data/lib/spree/core/testing_support/capybara_ext.rb +30 -0
  166. data/lib/spree/core/testing_support/common_rake.rb +8 -0
  167. data/lib/spree/core/testing_support/factories/payment_factory.rb +1 -1
  168. data/lib/spree/core/testing_support/factories/product_factory.rb +7 -2
  169. data/lib/spree/core/testing_support/factories/variant_factory.rb +4 -2
  170. data/lib/spree/core/validators/email.rb +1 -1
  171. data/lib/spree/core/version.rb +1 -1
  172. data/lib/spree/money.rb +14 -3
  173. data/lib/spree/product_filters.rb +1 -1
  174. data/vendor/assets/javascripts/jquery.jstree/jquery.jstree.js +3 -2
  175. data/vendor/assets/javascripts/jsuri.js +2 -0
  176. data/vendor/assets/javascripts/modernizr.js +3 -3
  177. metadata +78 -69
  178. data/db/default/spree/countries.yml +0 -1589
  179. data/db/default/spree/roles.yml +0 -5
  180. data/db/default/spree/states.yml +0 -256
  181. data/db/default/spree/zone_members.yml +0 -169
  182. data/db/default/spree/zones.yml +0 -13
@@ -33,7 +33,11 @@ module Spree
33
33
 
34
34
  def compute_line_item(line_item)
35
35
  if line_item.product.tax_category == rate.tax_category
36
- deduced_total_by_rate(line_item.total, rate)
36
+ if rate.included_in_price
37
+ deduced_total_by_rate(line_item.total, rate)
38
+ else
39
+ round_to_two_places(line_item.total * rate.amount)
40
+ end
37
41
  else
38
42
  0
39
43
  end
@@ -14,7 +14,7 @@ module Spree
14
14
  def compute(object=nil)
15
15
  return 0 if object.nil?
16
16
  self.preferred_amount * object.line_items.reduce(0) do |sum, value|
17
- if !matching_products || matching_products.include?(value.product)
17
+ if matching_products.blank? || matching_products.include?(value.product)
18
18
  value_to_add = value.quantity
19
19
  else
20
20
  value_to_add = 0
@@ -0,0 +1,7 @@
1
+ module Spree
2
+ class Classification < ActiveRecord::Base
3
+ self.table_name = 'spree_products_taxons'
4
+ belongs_to :product, :class_name => "Spree::Product"
5
+ belongs_to :taxon, :class_name => "Spree::Taxon"
6
+ end
7
+ end
@@ -14,10 +14,6 @@ module Spree
14
14
  attr_accessible :first_name, :last_name, :number, :verification_value, :year,
15
15
  :month, :gateway_customer_profile_id, :gateway_payment_profile_id
16
16
 
17
- def number=(num)
18
- @number = num.gsub(/[^0-9]/, '') rescue nil
19
- end
20
-
21
17
  def set_last_digits
22
18
  number.to_s.gsub!(/\s/,'')
23
19
  verification_value.to_s.gsub!(/\s/,'')
@@ -64,7 +60,7 @@ module Spree
64
60
 
65
61
  # needed for some of the ActiveMerchant gateways (eg. SagePay)
66
62
  def brand
67
- cc_type
63
+ spree_cc_type
68
64
  end
69
65
 
70
66
  scope :with_payment_profile, lambda { where('gateway_customer_profile_id IS NOT NULL') }
@@ -21,7 +21,9 @@ module Spree
21
21
  def provider
22
22
  gateway_options = options
23
23
  gateway_options.delete :login if gateway_options.has_key?(:login) and gateway_options[:login].nil?
24
- ActiveMerchant::Billing::Base.gateway_mode = gateway_options[:server].to_sym
24
+ if gateway_options[:server]
25
+ ActiveMerchant::Billing::Base.gateway_mode = gateway_options[:server].to_sym
26
+ end
25
27
  @provider ||= provider_class.new(gateway_options)
26
28
  end
27
29
 
@@ -33,7 +35,7 @@ module Spree
33
35
  if @provider.nil? || !@provider.respond_to?(method)
34
36
  super
35
37
  else
36
- provider.send(method)
38
+ provider.send(method, *args)
37
39
  end
38
40
  end
39
41
 
@@ -44,5 +46,11 @@ module Spree
44
46
  def method_type
45
47
  'gateway'
46
48
  end
49
+
50
+ def supports?(source)
51
+ return true unless provider_class.respond_to? :supports?
52
+ return false unless source.brand
53
+ provider_class.supports?(source.brand)
54
+ end
47
55
  end
48
56
  end
@@ -1,11 +1,11 @@
1
1
  module Spree
2
2
  class Gateway::Bogus < Gateway
3
- TEST_VISA = '4111111111111111'
4
- TEST_MC = '5500000000000004'
5
- TEST_AMEX = '340000000000009'
6
- TEST_DISC = '6011000000000004'
3
+ TEST_VISA = ['4111111111111111','4012888888881881','4222222222222']
4
+ TEST_MC = ['5500000000000004','5555555555554444','5105105105105100']
5
+ TEST_AMEX = ['378282246310005','371449635398431','378734493671000','340000000000009']
6
+ TEST_DISC = ['6011000000000004','6011111111111117','6011000990139424']
7
7
 
8
- VALID_CCS = ['1', TEST_VISA, TEST_MC, TEST_AMEX, TEST_DISC]
8
+ VALID_CCS = ['1', TEST_VISA, TEST_MC, TEST_AMEX, TEST_DISC].flatten
9
9
 
10
10
  attr_accessor :test
11
11
 
@@ -1,9 +1,9 @@
1
1
  module Spree
2
2
  class InventoryUnit < ActiveRecord::Base
3
- belongs_to :variant
4
- belongs_to :order
5
- belongs_to :shipment
6
- belongs_to :return_authorization
3
+ belongs_to :variant, :class_name => "Spree::Variant"
4
+ belongs_to :order, :class_name => "Spree::Order"
5
+ belongs_to :shipment, :class_name => "Spree::Shipment"
6
+ belongs_to :return_authorization, :class_name => "Spree::ReturnAuthorization"
7
7
 
8
8
  scope :backordered, lambda { where(:state => 'backordered') }
9
9
  scope :shipped, lambda { where(:state => 'shipped') }
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  class LineItem < ActiveRecord::Base
3
3
  before_validation :adjust_quantity
4
- belongs_to :order
4
+ belongs_to :order, :class_name => "Spree::Order"
5
5
  belongs_to :variant, :class_name => "Spree::Variant"
6
6
 
7
7
  has_one :product, :through => :variant
@@ -24,7 +24,7 @@ module Spree
24
24
  order.payment_required?
25
25
  }
26
26
  go_to_state :confirm, :if => lambda { |order| order.confirmation_required? }
27
- go_to_state :complete, :if => lambda { |order| (order.payment_required? && order.payments.exists?) || !order.payment_required? }
27
+ go_to_state :complete, :if => lambda { |order| (order.payment_required? && order.has_unprocessed_payments?) || !order.payment_required? }
28
28
  remove_transition :from => :delivery, :to => :confirm
29
29
  end
30
30
 
@@ -164,7 +164,14 @@ module Spree
164
164
 
165
165
  # If true, causes the confirmation step to happen during the checkout process
166
166
  def confirmation_required?
167
- payment_method && payment_method.payment_profiles_supported?
167
+ payments.map(&:payment_method).any?(&:payment_profiles_supported?)
168
+ end
169
+
170
+ # Used by the checkout state machine to check for unprocessed payments
171
+ # The Order should be unable to proceed to complete if there are unprocessed
172
+ # payments and there is payment required.
173
+ def has_unprocessed_payments?
174
+ payments.with_state('checkout').reload.exists?
168
175
  end
169
176
 
170
177
  # Indicates the number of items in the order
@@ -214,7 +221,7 @@ module Spree
214
221
  end
215
222
 
216
223
  def updater
217
- OrderUpdater.new(self)
224
+ @updater ||= OrderUpdater.new(self)
218
225
  end
219
226
 
220
227
  def update!
@@ -375,11 +382,11 @@ module Spree
375
382
  adjustments.each { |adjustment| adjustment.update_column('locked', true) }
376
383
 
377
384
  # update payment and shipment(s) states, and save
378
- updater = OrderUpdater.new(self)
379
385
  updater.update_payment_state
380
386
  shipments.each { |shipment| shipment.update!(self) }
381
387
  updater.update_shipment_state
382
388
  save
389
+ updater.run_hooks
383
390
 
384
391
  deliver_order_confirmation_email
385
392
 
@@ -393,7 +400,7 @@ module Spree
393
400
 
394
401
  def deliver_order_confirmation_email
395
402
  begin
396
- OrderMailer.confirm_email(self).deliver
403
+ OrderMailer.confirm_email(self.id).deliver
397
404
  rescue Exception => e
398
405
  logger.error("#{e.class.name}: #{e.message}")
399
406
  logger.error(e.backtrace * "\n")
@@ -408,23 +415,8 @@ module Spree
408
415
  end
409
416
 
410
417
  def rate_hash
411
- return @rate_hash if @rate_hash.present?
412
-
413
- # reserve one slot for each shipping method computation
414
- computed_costs = Array.new(available_shipping_methods(:front_end).size)
415
-
416
- # create all the threads and kick off their execution
417
- threads = available_shipping_methods(:front_end).each_with_index.map do |ship_method, index|
418
- Thread.new { computed_costs[index] = [ship_method, ship_method.calculator.compute(self)] }
419
- end
420
-
421
- # wait for all threads to finish
422
- threads.map(&:join)
423
-
424
- # now consolidate and memoize the threaded results
425
- @rate_hash ||= computed_costs.map do |pair|
426
- ship_method,cost = *pair
427
- next unless cost
418
+ @rate_hash ||= available_shipping_methods.collect do |ship_method|
419
+ next unless cost = ship_method.calculator.compute(self)
428
420
  ShippingRate.new( :id => ship_method.id,
429
421
  :shipping_method => ship_method,
430
422
  :name => ship_method.name,
@@ -437,22 +429,10 @@ module Spree
437
429
  payment_state == 'paid'
438
430
  end
439
431
 
440
- def payment
441
- payments.first
442
- end
443
-
444
432
  def available_payment_methods
445
433
  @available_payment_methods ||= PaymentMethod.available(:front_end)
446
434
  end
447
435
 
448
- def payment_method
449
- if payment and payment.payment_method
450
- payment.payment_method
451
- else
452
- available_payment_methods.first
453
- end
454
- end
455
-
456
436
  def pending_payments
457
437
  payments.select {|p| p.state == "checkout"}
458
438
  end
@@ -565,7 +545,7 @@ module Spree
565
545
  restock_items!
566
546
 
567
547
  #TODO: make_shipments_pending
568
- OrderMailer.cancel_email(self).deliver
548
+ OrderMailer.cancel_email(self.id).deliver
569
549
  unless %w(partial shipped).include?(shipment_state)
570
550
  self.payment_state = 'credit_owed'
571
551
  end
@@ -7,6 +7,7 @@ module Spree
7
7
  class_attribute :previous_states
8
8
  class_attribute :checkout_flow
9
9
  class_attribute :checkout_steps
10
+ class_attribute :removed_transitions
10
11
 
11
12
  def self.checkout_flow(&block)
12
13
  if block_given?
@@ -22,6 +23,7 @@ module Spree
22
23
  self.checkout_steps = ActiveSupport::OrderedHash.new
23
24
  self.next_event_transitions = []
24
25
  self.previous_states = [:cart]
26
+ self.removed_transitions = []
25
27
 
26
28
  # Build the checkout flow using the checkout_flow defined either
27
29
  # within the Order class, or a decorator for that class.
@@ -93,7 +95,40 @@ module Spree
93
95
  end
94
96
  end
95
97
 
98
+ def self.insert_checkout_step(name, options = {})
99
+ before = options.delete(:before)
100
+ after = options.delete(:after) unless before
101
+ after = self.checkout_steps.keys.last unless before || after
102
+
103
+ cloned_steps = self.checkout_steps.clone
104
+ cloned_removed_transitions = self.removed_transitions.clone
105
+ self.checkout_flow do
106
+ cloned_steps.each_pair do |key, value|
107
+ self.go_to_state(name, options) if key == before
108
+ self.go_to_state(key, value)
109
+ self.go_to_state(name, options) if key == after
110
+ end
111
+ cloned_removed_transitions.each do |transition|
112
+ self.remove_transition(transition)
113
+ end
114
+ end
115
+ end
116
+
117
+ def self.remove_checkout_step(name)
118
+ cloned_steps = self.checkout_steps.clone
119
+ cloned_removed_transitions = self.removed_transitions.clone
120
+ self.checkout_flow do
121
+ cloned_steps.each_pair do |key, value|
122
+ self.go_to_state(key, value) unless key == name
123
+ end
124
+ cloned_removed_transitions.each do |transition|
125
+ self.remove_transition(transition)
126
+ end
127
+ end
128
+ end
129
+
96
130
  def self.remove_transition(options={})
131
+ self.removed_transitions << options
97
132
  if transition = find_transition(options)
98
133
  self.next_event_transitions.delete(transition)
99
134
  end
@@ -131,6 +166,23 @@ module Spree
131
166
  steps << "complete" unless steps.include?("complete")
132
167
  steps
133
168
  end
169
+
170
+ def has_checkout_step?(step)
171
+ step.present? ? self.checkout_steps.include?(step) : false
172
+ end
173
+
174
+ def checkout_step_index(step)
175
+ self.checkout_steps.index(step)
176
+ end
177
+
178
+ def self.removed_transitions
179
+ @removed_transitions ||= []
180
+ end
181
+
182
+ def can_go_to_state?(state)
183
+ return false unless self.state.present? && has_checkout_step?(state) && has_checkout_step?(self.state)
184
+ checkout_step_index(state) > checkout_step_index(self.state)
185
+ end
134
186
  end
135
187
  end
136
188
  end
@@ -38,6 +38,11 @@ module Spree
38
38
 
39
39
  def attempt_cart_add(variant_id, quantity)
40
40
  quantity = quantity.to_i
41
+ if quantity > Spree::Config[:max_quantity]
42
+ errors.add(:base, I18n.t(:please_enter_reasonable_quantity, :scope => :order_populator))
43
+ return false
44
+ end
45
+
41
46
  variant = Spree::Variant.find(variant_id)
42
47
  if quantity > 0
43
48
  if check_stock_levels(variant, quantity)
@@ -55,12 +60,15 @@ module Spree
55
60
  if on_hand >= quantity || Spree::Config[:allow_backorders]
56
61
  return true
57
62
  else
58
- errors.add(:base, %Q{There are only #{on_hand} of #{display_name.inspect} remaining.} +
59
- %Q{ Please select a quantity less than or equal to this value.})
63
+ remainder_message = I18n.t(:remainder_message,
64
+ :item => display_name.inspect,
65
+ :on_hand => on_hand,
66
+ :scope => :order_populator)
67
+ errors.add(:base, remainder_message)
60
68
  return false
61
69
  end
62
70
  else
63
- errors.add(:base, %Q{#{display_name.inspect} is out of stock.})
71
+ errors.add(:base, I18n.t(:out_of_stock, :item => display_name.inspect, :scope => :order_populator))
64
72
  return false
65
73
  end
66
74
  end
@@ -34,11 +34,10 @@ module Spree
34
34
  :total => order.total
35
35
  })
36
36
 
37
- #ensure checkout payment always matches order total
38
- if order.payment and order.payment.checkout? and order.payment.amount != order.total
39
- order.payment.update_attributes_without_callbacks(:amount => order.total)
40
- end
37
+ run_hooks
38
+ end
41
39
 
40
+ def run_hooks
42
41
  update_hooks.each { |hook| order.send hook }
43
42
  end
44
43
 
@@ -14,6 +14,8 @@ module Spree
14
14
 
15
15
  # update the order totals, etc.
16
16
  after_save :update_order
17
+ # invalidate previously entered payments
18
+ after_create :invalidate_old_payments
17
19
 
18
20
  attr_accessor :source_attributes
19
21
  after_initialize :build_source
@@ -25,6 +27,15 @@ module Spree
25
27
  scope :completed, with_state('completed')
26
28
  scope :pending, with_state('pending')
27
29
  scope :failed, with_state('failed')
30
+ scope :valid, where("state NOT IN (?)", %w(failed invalid))
31
+
32
+ after_rollback :persist_invalid
33
+
34
+ def persist_invalid
35
+ return unless ['failed', 'invalid'].include?(state)
36
+ state_will_change!
37
+ save
38
+ end
28
39
 
29
40
  # order state machine (see http://github.com/pluginaweek/state_machine/tree/master for details)
30
41
  state_machine :initial => 'checkout' do
@@ -47,15 +58,20 @@ module Spree
47
58
  event :void do
48
59
  transition :from => ['pending', 'completed', 'checkout'], :to => 'void'
49
60
  end
61
+ # when the card brand isnt supported
62
+ event :invalidate do
63
+ transition :from => ['checkout'], :to => 'invalid'
64
+ end
50
65
  end
51
66
 
52
67
  def currency
53
68
  order.currency
54
69
  end
55
70
 
56
- def display_amount
71
+ def money
57
72
  Spree::Money.new(amount, { :currency => currency })
58
73
  end
74
+ alias display_amount money
59
75
 
60
76
  def offsets_total
61
77
  offsets.pluck(:amount).sum
@@ -106,6 +122,12 @@ module Spree
106
122
  gateway_error e
107
123
  end
108
124
 
125
+ def invalidate_old_payments
126
+ order.payments.with_state('checkout').where("id != ?", self.id).each do |payment|
127
+ payment.invalidate!
128
+ end
129
+ end
130
+
109
131
  def update_order
110
132
  order.payments.reload
111
133
  order.update!
@@ -5,10 +5,15 @@ module Spree
5
5
  if payment_method && payment_method.source_required?
6
6
  if source
7
7
  if !processing?
8
- if payment_method.auto_capture?
9
- purchase!
8
+ if payment_method.supports?(source)
9
+ if payment_method.auto_capture?
10
+ purchase!
11
+ else
12
+ authorize!
13
+ end
10
14
  else
11
- authorize!
15
+ invalidate!
16
+ raise Core::GatewayError.new(I18n.t(:payment_method_not_supported))
12
17
  end
13
18
  end
14
19
  else
@@ -39,7 +44,7 @@ module Spree
39
44
  response = payment_method.capture(self, source, gateway_options)
40
45
  else
41
46
  # Standard ActiveMerchant capture usage
42
- response = payment_method.capture((amount * 100).round,
47
+ response = payment_method.capture(money.money.cents,
43
48
  response_code,
44
49
  gateway_options)
45
50
  end
@@ -111,7 +116,11 @@ module Spree
111
116
  options = { :email => order.email,
112
117
  :customer => order.email,
113
118
  :ip => order.last_ip_address,
114
- :order_id => "#{order.number}-#{self.identifier}" }
119
+ # Need to pass in a unique identifier here to make some
120
+ # payment gateways happy.
121
+ #
122
+ # For more information, please see Spree::Payment#set_unique_identifier
123
+ :order_id => gateway_order_id }
115
124
 
116
125
  options.merge!({ :shipping => order.ship_total * 100,
117
126
  :tax => order.tax_total * 100,
@@ -187,5 +196,9 @@ module Spree
187
196
  raise Core::GatewayError.new(message)
188
197
  end
189
198
 
199
+ # The unique identifier to be passed in to the payment gateway
200
+ def gateway_order_id
201
+ "#{order.number}-#{self.identifier}"
202
+ end
190
203
  end
191
204
  end