solidus_core 2.2.2 → 2.3.0.beta1

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 (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), {}, {})