solidus_core 2.2.2 → 2.3.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of solidus_core might be problematic. Click here for more details.

Files changed (175) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -7
  3. data/app/assets/javascripts/spree.js.erb +2 -2
  4. data/app/helpers/spree/base_helper.rb +3 -4
  5. data/app/models/spree/address.rb +1 -1
  6. data/app/models/spree/adjustment.rb +3 -1
  7. data/app/models/spree/app_configuration.rb +43 -0
  8. data/app/models/spree/billing_integration.rb +2 -2
  9. data/app/models/spree/calculator/default_tax.rb +3 -1
  10. data/app/models/spree/calculator/distributed_amount.rb +24 -0
  11. data/app/models/spree/calculator/free_shipping.rb +0 -1
  12. data/app/models/spree/calculator/tiered_flat_rate.rb +17 -3
  13. data/app/models/spree/calculator/tiered_percent.rb +18 -3
  14. data/app/models/spree/distributed_amounts_handler.rb +43 -0
  15. data/app/models/spree/gateway/bogus.rb +7 -83
  16. data/app/models/spree/gateway/bogus_simple.rb +7 -20
  17. data/app/models/spree/gateway.rb +8 -58
  18. data/app/models/spree/image.rb +1 -1
  19. data/app/models/spree/line_item.rb +1 -1
  20. data/app/models/spree/option_value.rb +1 -1
  21. data/app/models/spree/order/checkout.rb +1 -4
  22. data/app/models/spree/order/number_generator.rb +43 -0
  23. data/app/models/spree/order.rb +33 -38
  24. data/app/models/spree/order_contents.rb +1 -1
  25. data/app/models/spree/order_taxation.rb +79 -0
  26. data/app/models/spree/order_update_attributes.rb +0 -2
  27. data/app/models/spree/order_updater.rb +55 -33
  28. data/app/models/spree/payment.rb +0 -1
  29. data/app/models/spree/payment_method/bogus_credit_card.rb +87 -0
  30. data/app/models/spree/payment_method/check.rb +14 -6
  31. data/app/models/spree/payment_method/credit_card.rb +41 -0
  32. data/app/models/spree/payment_method/simple_bogus_credit_card.rb +24 -0
  33. data/app/models/spree/payment_method/store_credit.rb +5 -13
  34. data/app/models/spree/payment_method.rb +126 -40
  35. data/app/models/spree/preferences/preferable.rb +5 -1
  36. data/app/models/spree/preferences/store.rb +2 -2
  37. data/app/models/spree/product/scopes.rb +14 -1
  38. data/app/models/spree/product.rb +10 -4
  39. data/app/models/spree/promotion_action.rb +4 -0
  40. data/app/models/spree/promotion_code/batch_builder.rb +3 -2
  41. data/app/models/spree/promotion_rule.rb +4 -0
  42. data/app/models/spree/role.rb +2 -0
  43. data/app/models/spree/role_user.rb +2 -0
  44. data/app/models/spree/shipment.rb +4 -2
  45. data/app/models/spree/shipping_method.rb +3 -1
  46. data/app/models/spree/shipping_rate.rb +1 -1
  47. data/app/models/spree/state.rb +10 -2
  48. data/app/models/spree/stock_item.rb +3 -3
  49. data/app/models/spree/store.rb +5 -0
  50. data/app/models/spree/store_credit.rb +2 -2
  51. data/app/models/spree/store_credit_event.rb +1 -1
  52. data/app/models/spree/store_selector/by_server_name.rb +30 -0
  53. data/app/models/spree/store_selector/legacy.rb +48 -0
  54. data/app/models/spree/tax/item_tax.rb +20 -0
  55. data/app/models/spree/tax/order_adjuster.rb +2 -14
  56. data/app/models/spree/tax/order_tax.rb +18 -0
  57. data/app/models/spree/tax/shipping_rate_taxer.rb +4 -13
  58. data/app/models/spree/tax/tax_helpers.rb +5 -3
  59. data/app/models/spree/tax_calculator/default.rb +83 -0
  60. data/app/models/spree/tax_calculator/shipping_rate.rb +46 -0
  61. data/app/models/spree/tax_category.rb +9 -1
  62. data/app/models/spree/tax_rate.rb +31 -7
  63. data/app/models/spree/tax_rate_tax_category.rb +6 -0
  64. data/app/models/spree/taxon.rb +1 -1
  65. data/app/models/spree/variant.rb +1 -1
  66. data/app/views/spree/{shipment_mailer → carton_mailer}/shipped_email.html.erb +3 -3
  67. data/app/views/spree/order_mailer/cancel_email.html.erb +3 -3
  68. data/app/views/spree/order_mailer/confirm_email.html.erb +2 -2
  69. data/app/views/spree/order_mailer/inventory_cancellation_email.html.erb +26 -0
  70. data/app/views/spree/reimbursement_mailer/reimbursement_email.html.erb +2 -2
  71. data/app/views/spree/test_mailer/test_email.html.erb +2 -2
  72. data/config/locales/en.yml +66 -57
  73. data/db/default/spree/refund_reasons.rb +1 -0
  74. data/db/default/spree/shipping_categories.rb +1 -0
  75. data/db/default/spree/stock_locations.rb +2 -0
  76. data/db/default/spree/stores.rb +3 -4
  77. data/db/migrate/20170412103617_transform_tax_rate_category_relation.rb +48 -0
  78. data/db/migrate/20170422134804_add_roles_unique_constraints.rb +6 -0
  79. data/db/migrate/20170522143442_add_time_range_to_tax_rate.rb +6 -0
  80. data/db/migrate/20170608074534_rename_bogus_gateways.rb +13 -0
  81. data/lib/generators/spree/custom_user/custom_user_generator.rb +1 -1
  82. data/lib/generators/spree/dummy/dummy_generator.rb +10 -4
  83. data/lib/generators/spree/dummy/templates/rails/database.yml +12 -12
  84. data/lib/generators/spree/install/install_generator.rb +5 -5
  85. data/lib/generators/spree/install/templates/config/initializers/{spree.rb → solidus.rb} +0 -0
  86. data/lib/solidus/migrations/rename_gateways.rb +39 -0
  87. data/lib/spree/core/controller_helpers/auth.rb +1 -1
  88. data/lib/spree/core/controller_helpers/order.rb +10 -5
  89. data/lib/spree/core/controller_helpers/store.rb +1 -9
  90. data/lib/spree/core/current_store.rb +6 -14
  91. data/lib/spree/core/engine.rb +4 -3
  92. data/lib/spree/core/importer/order.rb +4 -4
  93. data/lib/spree/core/version.rb +1 -1
  94. data/lib/spree/core.rb +0 -1
  95. data/lib/spree/localized_number.rb +2 -1
  96. data/lib/spree/permitted_attributes.rb +12 -6
  97. data/lib/spree/testing_support/capybara_ext.rb +0 -1
  98. data/lib/spree/testing_support/factories/adjustment_factory.rb +5 -1
  99. data/lib/spree/testing_support/factories/order_factory.rb +26 -24
  100. data/lib/spree/testing_support/factories/payment_factory.rb +4 -0
  101. data/lib/spree/testing_support/factories/payment_method_factory.rb +3 -3
  102. data/lib/spree/testing_support/factories/shipment_factory.rb +7 -3
  103. data/lib/spree/testing_support/factories/tax_rate_factory.rb +1 -1
  104. data/lib/spree/testing_support/factories/variant_factory.rb +3 -1
  105. data/lib/tasks/migrations/copy_order_bill_address_to_credit_card.rake +0 -4
  106. data/lib/tasks/migrations/migrate_user_addresses.rake +2 -2
  107. data/lib/tasks/migrations/rename_gateways.rake +19 -0
  108. data/solidus_core.gemspec +2 -3
  109. data/spec/lib/spree/core/controller_helpers/order_spec.rb +32 -6
  110. data/spec/lib/spree/core/controller_helpers/payment_parameters_spec.rb +0 -1
  111. data/spec/lib/spree/core/current_store_spec.rb +6 -11
  112. data/spec/lib/spree/core/price_migrator_spec.rb +4 -4
  113. data/spec/lib/spree/core/testing_support/factories/order_factory_spec.rb +199 -91
  114. data/spec/lib/spree/core/testing_support/factories/variant_factory_spec.rb +18 -0
  115. data/spec/lib/spree/localized_number_spec.rb +6 -0
  116. data/spec/mailers/carton_mailer_spec.rb +3 -3
  117. data/spec/models/spree/address_spec.rb +3 -3
  118. data/spec/models/spree/adjustment_spec.rb +71 -27
  119. data/spec/models/spree/calculator/default_tax_spec.rb +72 -1
  120. data/spec/models/spree/calculator/distributed_amount_spec.rb +32 -0
  121. data/spec/models/spree/calculator/tiered_flat_rate_spec.rb +20 -1
  122. data/spec/models/spree/calculator/tiered_percent_spec.rb +20 -1
  123. data/spec/models/spree/distributed_amounts_handler_spec.rb +79 -0
  124. data/spec/models/spree/gateway/bogus_simple.rb +7 -13
  125. data/spec/models/spree/gateway/bogus_spec.rb +8 -4
  126. data/spec/models/spree/gateway_spec.rb +6 -105
  127. data/spec/models/spree/image_spec.rb +23 -0
  128. data/spec/models/spree/order/checkout_spec.rb +3 -18
  129. data/spec/models/spree/order/number_generator_spec.rb +45 -0
  130. data/spec/models/spree/order/outstanding_balance_integration_spec.rb +135 -0
  131. data/spec/models/spree/order/payment_spec.rb +7 -2
  132. data/spec/models/spree/order/state_machine_spec.rb +4 -2
  133. data/spec/models/spree/order_capturing_spec.rb +8 -8
  134. data/spec/models/spree/order_contents_spec.rb +8 -1
  135. data/spec/models/spree/order_shipping_spec.rb +5 -1
  136. data/spec/models/spree/order_spec.rb +156 -83
  137. data/spec/models/spree/order_taxation_spec.rb +126 -0
  138. data/spec/models/spree/order_update_attributes_spec.rb +1 -5
  139. data/spec/models/spree/order_updater_spec.rb +20 -21
  140. data/spec/models/spree/payment_create_spec.rb +14 -6
  141. data/spec/models/spree/payment_method/bogus_credit_card_spec.rb +8 -0
  142. data/spec/models/spree/payment_method/check_spec.rb +78 -0
  143. data/spec/models/spree/payment_method/credit_card_spec.rb +66 -0
  144. data/spec/models/spree/payment_method/simple_bogus_credit_card_spec.rb +18 -0
  145. data/spec/models/spree/payment_method_spec.rb +47 -2
  146. data/spec/models/spree/payment_spec.rb +6 -8
  147. data/spec/models/spree/preference_spec.rb +1 -1
  148. data/spec/models/spree/price_spec.rb +1 -1
  149. data/spec/models/spree/product/scopes_spec.rb +46 -0
  150. data/spec/models/spree/promotion_action_spec.rb +4 -0
  151. data/spec/models/spree/promotion_code/batch_builder_spec.rb +25 -3
  152. data/spec/models/spree/promotion_code_batch_spec.rb +0 -6
  153. data/spec/models/spree/promotion_handler/coupon_spec.rb +1 -1
  154. data/spec/models/spree/promotion_rule_spec.rb +5 -0
  155. data/spec/models/spree/reimbursement_type/original_payment_spec.rb +1 -1
  156. data/spec/models/spree/shipment_spec.rb +24 -3
  157. data/spec/models/spree/shipping_rate_spec.rb +5 -5
  158. data/spec/models/spree/state_spec.rb +31 -4
  159. data/spec/models/spree/stock/coordinator_spec.rb +24 -0
  160. data/spec/models/spree/stock/estimator_spec.rb +1 -1
  161. data/spec/models/spree/store_selector/by_server_name_spec.rb +26 -0
  162. data/spec/models/spree/store_selector/legacy_spec.rb +44 -0
  163. data/spec/models/spree/store_spec.rb +10 -2
  164. data/spec/models/spree/tax/order_adjuster_spec.rb +11 -21
  165. data/spec/models/spree/tax/shipping_rate_taxer_spec.rb +10 -3
  166. data/spec/models/spree/tax/taxation_integration_spec.rb +43 -8
  167. data/spec/models/spree/tax_calculator/default_spec.rb +54 -0
  168. data/spec/models/spree/tax_rate_spec.rb +92 -0
  169. data/spec/models/spree/variant/vat_price_generator_spec.rb +4 -4
  170. data/spec/models/spree/variant_spec.rb +8 -2
  171. data/spec/spec_helper.rb +2 -1
  172. data/spec/support/test_gateway.rb +1 -1
  173. metadata +45 -24
  174. data/app/models/spree/tax/item_adjuster.rb +0 -51
  175. data/spec/models/spree/tax/item_adjuster_spec.rb +0 -82
@@ -1,5 +1,6 @@
1
1
  require 'spree/core/validators/email'
2
2
  require 'spree/order/checkout'
3
+ require 'spree/order/number_generator'
3
4
 
4
5
  module Spree
5
6
  # The customers cart until completed, then acts as permanent record of the transaction.
@@ -298,25 +299,15 @@ module Spree
298
299
  assign_attributes(attrs_to_set)
299
300
  end
300
301
 
301
- def generate_order_number(options = {})
302
- options[:length] ||= ORDER_NUMBER_LENGTH
303
- options[:letters] ||= ORDER_NUMBER_LETTERS
304
- options[:prefix] ||= ORDER_NUMBER_PREFIX
305
-
306
- possible = (0..9).to_a
307
- possible += ('A'..'Z').to_a if options[:letters]
308
-
309
- self.number ||= loop do
310
- # Make a random number.
311
- random = "#{options[:prefix]}#{(0...options[:length]).map { possible.sample }.join}"
312
- # Use the random number if no other order exists with it.
313
- if self.class.exists?(number: random)
314
- # If over half of all possible options are taken add another digit.
315
- options[:length] += 1 if self.class.count > (10**options[:length] / 2)
316
- else
317
- break random
318
- end
302
+ def generate_order_number(options = nil)
303
+ if options
304
+ Spree::Deprecation.warn \
305
+ "Passing options to Order#generate_order_number is deprecated. " \
306
+ "Please add your own instance of the order number generator " \
307
+ "with your options (#{options.inspect}) and store it as " \
308
+ "Spree::Config.order_number_generator in your stores config."
319
309
  end
310
+ self.number ||= Spree::Config.order_number_generator.generate
320
311
  end
321
312
 
322
313
  def shipped_shipments
@@ -364,15 +355,18 @@ module Spree
364
355
  end
365
356
  deprecate create_tax_charge!: :update!, deprecator: Spree::Deprecation
366
357
 
358
+ def reimbursement_total
359
+ reimbursements.sum(:total)
360
+ end
361
+
367
362
  def outstanding_balance
368
363
  # If reimbursement has happened add it back to total to prevent balance_due payment state
369
364
  # See: https://github.com/spree/spree/issues/6229
370
- adjusted_payment_total = payment_total + refund_total
371
365
 
372
366
  if state == 'canceled'
373
- -1 * adjusted_payment_total
367
+ -1 * payment_total
374
368
  else
375
- total - adjusted_payment_total
369
+ total - reimbursement_total - payment_total
376
370
  end
377
371
  end
378
372
 
@@ -475,6 +469,7 @@ module Spree
475
469
  line_items.destroy_all
476
470
  adjustments.destroy_all
477
471
  shipments.destroy_all
472
+ order_promotions.destroy_all
478
473
 
479
474
  update!
480
475
  end
@@ -497,6 +492,7 @@ module Spree
497
492
  end
498
493
  end
499
494
  end
495
+ deprecate :state_changed, deprecator: Spree::Deprecation
500
496
 
501
497
  def coupon_code=(code)
502
498
  @coupon_code = begin
@@ -696,16 +692,26 @@ module Spree
696
692
  self.ship_address = Spree::Address.immutable_merge(ship_address, attributes)
697
693
  end
698
694
 
699
- def assign_default_addresses!
695
+ # Assigns a default bill_address and ship_address to the order based on the
696
+ # associated user's bill_address and ship_address.
697
+ # @note This doesn't persist the change bill_address or ship_address
698
+ def assign_default_user_addresses
700
699
  if user
700
+ bill_address = (user.bill_address || user.default_address)
701
+ ship_address = (user.ship_address || user.default_address)
701
702
  # this is one of 2 places still using User#bill_address
702
- self.bill_address ||= user.bill_address if user.bill_address.try!(:valid?)
703
+ self.bill_address ||= bill_address if bill_address.try!(:valid?)
703
704
  # Skip setting ship address if order doesn't have a delivery checkout step
704
705
  # to avoid triggering validations on shipping address
705
- self.ship_address ||= user.ship_address if user.ship_address.try!(:valid?) && checkout_steps.include?("delivery")
706
+ self.ship_address ||= ship_address if ship_address.try!(:valid?) && checkout_steps.include?("delivery")
706
707
  end
707
708
  end
708
709
 
710
+ alias_method :assign_default_user_addresses!, :assign_default_user_addresses
711
+ deprecate assign_default_user_addresses!: :assign_default_user_addresses, deprecator: Spree::Deprecation
712
+ alias_method :assign_default_addresses!, :assign_default_user_addresses
713
+ deprecate assign_default_addresses!: :assign_default_user_addresses, deprecator: Spree::Deprecation
714
+
709
715
  def persist_user_address!
710
716
  if !temporary_address && user && user.respond_to?(:persist_order_address) && bill_address_id
711
717
  user.persist_order_address(self)
@@ -736,20 +742,9 @@ module Spree
736
742
  alias_method :assign_default_credit_card, :add_default_payment_from_wallet
737
743
  deprecate assign_default_credit_card: :add_default_payment_from_wallet, deprecator: Spree::Deprecation
738
744
 
739
- def payments_attributes=(attributes)
740
- validate_payments_attributes(attributes)
741
- super(attributes)
742
- end
743
-
744
- def validate_payments_attributes(attributes)
745
- attributes = Array.wrap(attributes)
746
- # Ensure the payment methods specified are allowed for this user
747
- payment_methods = Spree::PaymentMethod.where(id: available_payment_methods)
748
- attributes.each do |payment_attributes|
749
- payment_method_id = payment_attributes[:payment_method_id]
750
-
751
- # raise RecordNotFound unless it is an allowed payment method
752
- payment_methods.find(payment_method_id) if payment_method_id
745
+ def record_ip_address(ip_address)
746
+ if last_ip_address != ip_address
747
+ update_attributes!(last_ip_address: ip_address)
753
748
  end
754
749
  end
755
750
 
@@ -112,7 +112,7 @@ module Spree
112
112
  line_item.quantity -= quantity
113
113
  line_item.target_shipment = options[:shipment]
114
114
 
115
- if line_item.quantity == 0
115
+ if line_item.quantity <= 0
116
116
  order.line_items.destroy(line_item)
117
117
  else
118
118
  line_item.save!
@@ -0,0 +1,79 @@
1
+ module Spree
2
+ # Relatively simple class used to apply a {Spree::Tax::OrderTax} to a
3
+ # {Spree::Order}.
4
+ #
5
+ # This class will create or update adjustments on the taxed items and remove
6
+ # any now inapplicable tax adjustments from the order.
7
+ class OrderTaxation
8
+ # Create a new order taxation.
9
+ #
10
+ # @param [Spree::Order] order the order to apply taxes to
11
+ # @return [Spree::OrderTaxation] a {Spree::OrderTaxation} object
12
+ def initialize(order)
13
+ @order = order
14
+ end
15
+
16
+ # Apply taxes to the order.
17
+ #
18
+ # This method will create or update adjustments on all line items and
19
+ # shipments in the order to reflect the appropriate taxes passed in. It
20
+ # will also remove any now inapplicable tax adjustments.
21
+ #
22
+ # @param [Spree::Tax::OrderTax] taxes the taxes to apply to the order
23
+ # @return [void]
24
+ def apply(taxes)
25
+ @order.line_items.each do |item|
26
+ taxed_items = taxes.line_item_taxes.select { |i| i.item_id == item.id }
27
+ update_adjustments(item, taxed_items)
28
+ end
29
+
30
+ @order.shipments.each do |item|
31
+ taxed_items = taxes.shipment_taxes.select { |i| i.item_id == item.id }
32
+ update_adjustments(item, taxed_items)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ # Walk through the taxes for an item and update adjustments for it. Once
39
+ # all of the taxes have been added as adjustments, remove any old tax
40
+ # adjustments that weren't touched.
41
+ #
42
+ # @private
43
+ # @param [#adjustments] item a {Spree::LineItem} or {Spree::Shipment}
44
+ # @param [Array<Spree::Tax::ItemTax>] taxed_items a list of calculated taxes for an item
45
+ # @return [void]
46
+ def update_adjustments(item, taxed_items)
47
+ tax_adjustments = item.adjustments.select(&:tax?)
48
+
49
+ active_adjustments = taxed_items.map do |tax_item|
50
+ update_adjustment(item, tax_item)
51
+ end
52
+
53
+ # Remove any tax adjustments tied to rates which no longer match.
54
+ unmatched_adjustments = tax_adjustments - active_adjustments
55
+ item.adjustments.destroy(unmatched_adjustments)
56
+ end
57
+
58
+ # Update or create a new tax adjustment on an item.
59
+ #
60
+ # @private
61
+ # @param [#adjustments] item a {Spree::LineItem} or {Spree::Shipment}
62
+ # @param [Spree::Tax::ItemTax] tax_item calculated taxes for an item
63
+ # @return [Spree::Adjustment] the created or updated tax adjustment
64
+ def update_adjustment(item, tax_item)
65
+ tax_adjustment = item.adjustments.detect do |adjustment|
66
+ adjustment.source == tax_item.tax_rate
67
+ end
68
+
69
+ tax_adjustment ||= item.adjustments.new(
70
+ source: tax_item.tax_rate,
71
+ order_id: item.order_id,
72
+ label: tax_item.label,
73
+ included: tax_item.included_in_price
74
+ )
75
+ tax_adjustment.update_attributes!(amount: tax_item.amount)
76
+ tax_adjustment
77
+ end
78
+ end
79
+ end
@@ -14,8 +14,6 @@ module Spree
14
14
  # Assign the attributes to the order and save the order
15
15
  # @return true if saved, otherwise false and errors will be set on the order
16
16
  def apply
17
- order.validate_payments_attributes(@payments_attributes)
18
-
19
17
  assign_order_attributes
20
18
  assign_payments_attributes
21
19
 
@@ -44,26 +44,10 @@ module Spree
44
44
  #
45
45
  # The +shipment_state+ value helps with reporting, etc. since it provides a quick and easy way to locate Orders needing attention.
46
46
  def update_shipment_state
47
- if order.backordered?
48
- order.shipment_state = 'backorder'
49
- else
50
- # get all the shipment states for this order
51
- shipment_states = shipments.states
52
- if shipment_states.size > 1
53
- # multiple shiment states means it's most likely partially shipped
54
- order.shipment_state = 'partial'
55
- else
56
- # will return nil if no shipments are found
57
- order.shipment_state = shipment_states.first
58
- # TODO: inventory unit states?
59
- # if order.shipment_state && order.inventory_units.where(:shipment_id => nil).exists?
60
- # shipments exist but there are unassigned inventory units
61
- # order.shipment_state = 'partial'
62
- # end
63
- end
47
+ log_state_change('shipment') do
48
+ order.shipment_state = determine_shipment_state
64
49
  end
65
50
 
66
- order.state_changed('shipment')
67
51
  order.shipment_state
68
52
  end
69
53
 
@@ -76,22 +60,46 @@ module Spree
76
60
  #
77
61
  # The +payment_state+ value helps with reporting, etc. since it provides a quick and easy way to locate Orders needing attention.
78
62
  def update_payment_state
79
- last_state = order.payment_state
80
- if payments.present? && payments.valid.size == 0 && order.outstanding_balance != 0
81
- order.payment_state = 'failed'
82
- elsif order.state == 'canceled' && order.payment_total == 0
83
- order.payment_state = 'void'
84
- else
85
- order.payment_state = 'balance_due' if order.outstanding_balance > 0
86
- order.payment_state = 'credit_owed' if order.outstanding_balance < 0
87
- order.payment_state = 'paid' if !order.outstanding_balance?
63
+ log_state_change('payment') do
64
+ order.payment_state = determine_payment_state
88
65
  end
89
- order.state_changed('payment') if last_state != order.payment_state
66
+
90
67
  order.payment_state
91
68
  end
92
69
 
93
70
  private
94
71
 
72
+ def determine_payment_state
73
+ if payments.present? && payments.valid.empty? && order.outstanding_balance != 0
74
+ 'failed'
75
+ elsif order.state == 'canceled' && order.payment_total.zero?
76
+ 'void'
77
+ elsif order.outstanding_balance > 0
78
+ 'balance_due'
79
+ elsif order.outstanding_balance < 0
80
+ 'credit_owed'
81
+ else
82
+ # outstanding_balance == 0
83
+ 'paid'
84
+ end
85
+ end
86
+
87
+ def determine_shipment_state
88
+ if order.backordered?
89
+ 'backorder'
90
+ else
91
+ # get all the shipment states for this order
92
+ shipment_states = shipments.states
93
+ if shipment_states.size > 1
94
+ # multiple shiment states means it's most likely partially shipped
95
+ 'partial'
96
+ else
97
+ # will return nil if no shipments are found
98
+ shipment_states.first
99
+ end
100
+ end
101
+ end
102
+
95
103
  # This will update and select the best promotion adjustment, update tax
96
104
  # adjustments, update cancellation adjustments, and then update the total
97
105
  # fields (promo_total, included_tax_total, additional_tax_total, and
@@ -177,6 +185,21 @@ module Spree
177
185
  order.save!(validate: false)
178
186
  end
179
187
 
188
+ def log_state_change(name)
189
+ state = "#{name}_state"
190
+ old_state = order.public_send(state)
191
+ yield
192
+ new_state = order.public_send(state)
193
+ if old_state != new_state
194
+ order.state_changes.new(
195
+ previous_state: old_state,
196
+ next_state: new_state,
197
+ name: name,
198
+ user_id: order.user_id
199
+ )
200
+ end
201
+ end
202
+
180
203
  def round_money(n)
181
204
  (n * 100).round / 100.0
182
205
  end
@@ -229,11 +252,10 @@ module Spree
229
252
  [*line_items, *shipments].each do |item|
230
253
  # The cancellation_total isn't persisted anywhere but is included in
231
254
  # the adjustment_total
232
- item_cancellation_total = item.adjustments.select(&:cancellation?).sum(&:amount)
233
-
234
- item.adjustment_total = item.promo_total +
235
- item.additional_tax_total +
236
- item_cancellation_total
255
+ item.adjustment_total = item.adjustments.
256
+ select(&:eligible?).
257
+ reject(&:included?).
258
+ sum(&:amount)
237
259
 
238
260
  if item.changed?
239
261
  item.update_columns(
@@ -40,7 +40,6 @@ module Spree
40
40
 
41
41
  validates :amount, numericality: true
42
42
  validates :source, presence: true, if: :source_required?
43
- validates :payment_method, presence: true
44
43
 
45
44
  default_scope -> { order(:created_at) }
46
45
 
@@ -0,0 +1,87 @@
1
+ module Spree
2
+ class PaymentMethod::BogusCreditCard < PaymentMethod::CreditCard
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
+
8
+ VALID_CCS = ['1', TEST_VISA, TEST_MC, TEST_AMEX, TEST_DISC].flatten
9
+
10
+ attr_accessor :test
11
+
12
+ def gateway_class
13
+ self.class
14
+ end
15
+
16
+ def create_profile(payment)
17
+ return if payment.source.has_payment_profile?
18
+ # simulate the storage of credit card profile using remote service
19
+ if success = VALID_CCS.include?(payment.source.number)
20
+ payment.source.update_attributes(gateway_customer_profile_id: generate_profile_id(success))
21
+ end
22
+ end
23
+
24
+ def authorize(_money, credit_card, _options = {})
25
+ profile_id = credit_card.gateway_customer_profile_id
26
+ if VALID_CCS.include?(credit_card.number) || (profile_id && profile_id.starts_with?('BGS-'))
27
+ ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345', avs_result: { code: 'D' })
28
+ else
29
+ ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', { message: 'Bogus Gateway: Forced failure' }, test: true)
30
+ end
31
+ end
32
+
33
+ def purchase(_money, credit_card, _options = {})
34
+ profile_id = credit_card.gateway_customer_profile_id
35
+ if VALID_CCS.include?(credit_card.number) || (profile_id && profile_id.starts_with?('BGS-'))
36
+ ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345', avs_result: { code: 'M' })
37
+ else
38
+ ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', message: 'Bogus Gateway: Forced failure', test: true)
39
+ end
40
+ end
41
+
42
+ def credit(_money, _credit_card, _response_code, _options = {})
43
+ ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345')
44
+ end
45
+
46
+ def capture(_money, authorization, _gateway_options)
47
+ if authorization == '12345'
48
+ ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true)
49
+ else
50
+ ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', error: 'Bogus Gateway: Forced failure', test: true)
51
+ end
52
+ end
53
+
54
+ def void(_response_code, _credit_card, _options = {})
55
+ ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345')
56
+ end
57
+
58
+ def cancel(_response_code)
59
+ ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345')
60
+ end
61
+
62
+ def test?
63
+ # Test mode is not really relevant with bogus gateway (no such thing as live server)
64
+ true
65
+ end
66
+
67
+ def payment_profiles_supported?
68
+ true
69
+ end
70
+
71
+ def actions
72
+ %w(capture void credit)
73
+ end
74
+
75
+ private
76
+
77
+ def generate_profile_id(success)
78
+ record = true
79
+ prefix = success ? 'BGS' : 'FAIL'
80
+ while record
81
+ random = "#{prefix}-#{Array.new(6){ rand(6) }.join}"
82
+ record = Spree::CreditCard.where(gateway_customer_profile_id: random).first
83
+ end
84
+ random
85
+ end
86
+ end
87
+ end
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  class PaymentMethod::Check < PaymentMethod
3
3
  def actions
4
- %w{capture void}
4
+ %w{capture void credit}
5
5
  end
6
6
 
7
7
  # Indicates whether its possible to capture the payment
@@ -14,18 +14,26 @@ module Spree
14
14
  payment.state != 'void'
15
15
  end
16
16
 
17
- def capture(*_args)
18
- ActiveMerchant::Billing::Response.new(true, "", {}, {})
17
+ def capture(*)
18
+ simulated_successful_billing_response
19
19
  end
20
20
 
21
- def cancel(response); end
21
+ def cancel(*); end
22
22
 
23
- def void(*_args)
24
- ActiveMerchant::Billing::Response.new(true, "", {}, {})
23
+ def void(*)
24
+ simulated_successful_billing_response
25
+ end
26
+
27
+ def credit(*)
28
+ simulated_successful_billing_response
25
29
  end
26
30
 
27
31
  def source_required?
28
32
  false
29
33
  end
34
+
35
+ def simulated_successful_billing_response
36
+ ActiveMerchant::Billing::Response.new(true, "", {}, {})
37
+ end
30
38
  end
31
39
  end
@@ -0,0 +1,41 @@
1
+ module Spree
2
+ # An implementation of a `Spree::PaymentMethod` for credit card payments.
3
+ #
4
+ # It's a good candidate as base class for other credit card based payment methods.
5
+ #
6
+ # See https://github.com/solidusio/solidus_gateway/ for
7
+ # officially supported payment method implementations.
8
+ #
9
+ class PaymentMethod::CreditCard < PaymentMethod
10
+ def payment_source_class
11
+ CreditCard
12
+ end
13
+
14
+ def partial_name
15
+ 'gateway'
16
+ end
17
+
18
+ def supports?(source)
19
+ return true unless gateway_class.respond_to? :supports?
20
+ return true if source.brand && gateway_class.supports?(source.brand)
21
+ source.has_payment_profile?
22
+ end
23
+
24
+ def reusable_sources_by_order(order)
25
+ source_ids = order.payments.where(payment_method_id: id).pluck(:source_id).uniq
26
+ payment_source_class.where(id: source_ids).select(&:reusable?)
27
+ end
28
+ alias_method :sources_by_order, :reusable_sources_by_order
29
+ deprecate sources_by_order: :reusable_sources_by_order, deprecator: Spree::Deprecation
30
+
31
+ def reusable_sources(order)
32
+ if order.completed?
33
+ reusable_sources_by_order(order)
34
+ elsif order.user_id
35
+ order.user.wallet.wallet_payment_sources.map(&:payment_source).select(&:reusable?)
36
+ else
37
+ []
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,24 @@
1
+ module Spree
2
+ # Bogus Gateway that doesn't support payment profiles.
3
+ class PaymentMethod::SimpleBogusCreditCard < PaymentMethod::BogusCreditCard
4
+ def payment_profiles_supported?
5
+ false
6
+ end
7
+
8
+ def authorize(_money, credit_card, _options = {})
9
+ if VALID_CCS.include? credit_card.number
10
+ ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345', avs_result: { code: 'A' })
11
+ else
12
+ ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', { message: 'Bogus Gateway: Forced failure' }, test: true)
13
+ end
14
+ end
15
+
16
+ def purchase(_money, credit_card, _options = {})
17
+ if VALID_CCS.include? credit_card.number
18
+ ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345', avs_result: { code: 'A' })
19
+ else
20
+ ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', message: 'Bogus Gateway: Forced failure', test: true)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -4,19 +4,11 @@ module Spree
4
4
  ::Spree::StoreCredit
5
5
  end
6
6
 
7
- def can_capture?(payment)
8
- ['checkout', 'pending'].include?(payment.state)
9
- end
10
-
11
- def can_void?(payment)
12
- payment.pending?
13
- end
14
-
15
7
  def authorize(amount_in_cents, provided_store_credit, gateway_options = {})
16
8
  if provided_store_credit.nil?
17
9
  ActiveMerchant::Billing::Response.new(false, Spree.t('store_credit.unable_to_find'), {}, {})
18
10
  else
19
- action = -> (store_credit) {
11
+ action = ->(store_credit) {
20
12
  store_credit.authorize(
21
13
  amount_in_cents / 100.0.to_d,
22
14
  gateway_options[:currency],
@@ -28,7 +20,7 @@ module Spree
28
20
  end
29
21
 
30
22
  def capture(amount_in_cents, auth_code, gateway_options = {})
31
- action = -> (store_credit) {
23
+ action = ->(store_credit) {
32
24
  store_credit.capture(
33
25
  amount_in_cents / 100.0.to_d,
34
26
  auth_code,
@@ -55,14 +47,14 @@ module Spree
55
47
  end
56
48
 
57
49
  def void(auth_code, gateway_options = {})
58
- action = -> (store_credit) {
50
+ action = ->(store_credit) {
59
51
  store_credit.void(auth_code, action_originator: gateway_options[:originator])
60
52
  }
61
53
  handle_action(action, :void, auth_code)
62
54
  end
63
55
 
64
56
  def credit(amount_in_cents, auth_code, gateway_options = {})
65
- action = -> (store_credit) do
57
+ action = ->(store_credit) do
66
58
  currency = gateway_options[:currency] || store_credit.currency
67
59
  originator = gateway_options[:originator]
68
60
 
@@ -109,7 +101,7 @@ module Spree
109
101
 
110
102
  def handle_action(action, action_name, auth_code)
111
103
  # Find first event with provided auth_code
112
- store_credit = Spree::StoreCreditEvent.find_by_authorization_code(auth_code).try(:store_credit)
104
+ store_credit = Spree::StoreCreditEvent.find_by(authorization_code: auth_code).try(:store_credit)
113
105
 
114
106
  if store_credit.nil?
115
107
  ActiveMerchant::Billing::Response.new(false, Spree.t('store_credit.unable_to_find_for_action', auth_code: auth_code, action: action_name), {}, {})