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,12 +1,23 @@
1
1
  module Spree
2
- # An abstract class which is implemented most commonly as a `Spree::Gateway`.
2
+ # A base class which is used for implementing payment methods.
3
+ #
4
+ # See https://github.com/solidusio/solidus_gateway/ for
5
+ # offically supported payment method implementations.
6
+ #
7
+ # Uses STI (single table inheritance) to store all implemented payment methods
8
+ # in one table (+spree_payment_methods+).
9
+ #
10
+ # This class is not meant to be instantiated. Please create instances of concrete payment methods.
3
11
  #
4
12
  class PaymentMethod < Spree::Base
13
+ preference :server, :string, default: 'test'
14
+ preference :test_mode, :boolean, default: true
15
+
5
16
  acts_as_paranoid
6
17
  acts_as_list
7
18
  DISPLAY = [:both, :front_end, :back_end]
8
19
 
9
- validates :name, presence: true
20
+ validates :name, :type, presence: true
10
21
 
11
22
  has_many :payments, class_name: "Spree::Payment", inverse_of: :payment_method
12
23
  has_many :credit_cards, class_name: "Spree::CreditCard"
@@ -22,19 +33,85 @@ module Spree
22
33
  store.payment_methods.empty? ? all : where(id: store.payment_method_ids)
23
34
  end
24
35
 
36
+ delegate :authorize, :purchase, :capture, :void, :credit, to: :gateway
37
+
25
38
  include Spree::Preferences::StaticallyConfigurable
26
39
 
27
- def self.providers
28
- Rails.application.config.spree.payment_methods
40
+ class << self
41
+ def providers
42
+ Spree::Deprecation.warn 'Spree::PaymentMethod.providers is deprecated and will be deleted in Solidus 3.0. ' \
43
+ 'Please use Rails.application.config.spree.payment_methods instead'
44
+ Rails.application.config.spree.payment_methods
45
+ end
46
+
47
+ def available(display_on = nil, store: nil)
48
+ Spree::Deprecation.warn "Spree::PaymentMethod.available is deprecated."\
49
+ "Please use .active, .available_to_users, and .available_to_admin scopes instead."\
50
+ "For payment methods associated with a specific store, use Spree::PaymentMethod.available_to_store(your_store)"\
51
+ " as the base applying any further filtering"
52
+
53
+ display_on = display_on.to_s
54
+
55
+ available_payment_methods =
56
+ case display_on
57
+ when 'front_end'
58
+ active.available_to_users
59
+ when 'back_end'
60
+ active.available_to_admin
61
+ else
62
+ active.available_to_users.available_to_admin
63
+ end
64
+ available_payment_methods.select do |p|
65
+ store.nil? || store.payment_methods.empty? || store.payment_methods.include?(p)
66
+ end
67
+ end
68
+
69
+ def active?
70
+ where(type: to_s, active: true).count > 0
71
+ end
72
+
73
+ def find_with_destroyed(*args)
74
+ unscoped { find(*args) }
75
+ end
76
+ end
77
+
78
+ # Represents the gateway of this payment method
79
+ #
80
+ # The gateway is responsible for communicating with the providers API.
81
+ #
82
+ # It implements methods for:
83
+ #
84
+ # - authorize
85
+ # - purchase
86
+ # - capture
87
+ # - void
88
+ # - credit
89
+ #
90
+ def gateway
91
+ gateway_options = options
92
+ gateway_options.delete :login if gateway_options.key?(:login) && gateway_options[:login].nil?
93
+ if gateway_options[:server]
94
+ ActiveMerchant::Billing::Base.mode = gateway_options[:server].to_sym
95
+ end
96
+ @gateway ||= gateway_class.new(gateway_options)
29
97
  end
98
+ alias_method :provider, :gateway
99
+ deprecate provider: :gateway, deprecator: Spree::Deprecation
30
100
 
31
- def provider_class
32
- raise ::NotImplementedError, "You must implement provider_class method for #{self.class}."
101
+ # Represents all preferences as a Hash
102
+ #
103
+ # Each preference is a key holding the value(s) and gets passed to the gateway via +gateway_options+
104
+ #
105
+ # @return Hash
106
+ def options
107
+ preferences.to_hash
33
108
  end
34
109
 
35
- # The class that will process payments for this payment type, used for @payment.source
36
- # e.g. CreditCard in the case of a the Gateway payment type
37
- # nil means the payment method doesn't require a source e.g. check
110
+ # The class that will store payment sources (re)usable with this payment method
111
+ #
112
+ # Used by Spree::Payment as source (e.g. Spree::CreditCard in the case of a credit card payment method).
113
+ #
114
+ # Returning nil means the payment method doesn't support storing sources (e.g. Spree::PaymentMethod::Check)
38
115
  def payment_source_class
39
116
  raise ::NotImplementedError, "You must implement payment_source_class method for #{self.class}."
40
117
  end
@@ -62,39 +139,27 @@ module Spree
62
139
  end
63
140
  end
64
141
 
65
- def self.available(display_on=nil, store: nil)
66
- Spree::Deprecation.warn "Spree::PaymentMethod.available is deprecated."\
67
- "Please use .active, .available_to_users, and .available_to_admin scopes instead."\
68
- "For payment methods associated with a specific store, use Spree::PaymentMethod.available_to_store(your_store)"\
69
- " as the base applying any further filtering"
70
-
71
- display_on = display_on.to_s
72
-
73
- available_payment_methods =
74
- case display_on
75
- when 'front_end'
76
- active.available_to_users
77
- when 'back_end'
78
- active.available_to_admin
79
- else
80
- active.available_to_users.available_to_admin
81
- end
82
- available_payment_methods.select do |p|
83
- store.nil? || store.payment_methods.empty? || store.payment_methods.include?(p)
84
- end
85
- end
86
-
87
- def self.active?
88
- where(type: to_s, active: true).count > 0
89
- end
90
-
91
- def method_type
142
+ # Used as partial name for your payment method
143
+ #
144
+ # Currently your payment method needs to provide these partials:
145
+ #
146
+ # 1. app/views/spree/checkout/payment/_{partial_name}.html.erb
147
+ # The form your customer enters the payment information in during checkout
148
+ #
149
+ # 2. app/views/spree/checkout/existing_payment/_{partial_name}.html.erb
150
+ # The payment information of your customers reusable sources during checkout
151
+ #
152
+ # 3. app/views/spree/admin/payments/source_forms/_{partial_name}.html.erb
153
+ # The form an admin enters payment information in when creating orders in the backend
154
+ #
155
+ # 4. app/views/spree/admin/payments/source_views/_{partial_name}.html.erb
156
+ # The view that represents your payment method on orders in the backend
157
+ #
158
+ def partial_name
92
159
  type.demodulize.downcase
93
160
  end
94
-
95
- def self.find_with_destroyed(*args)
96
- unscoped { find(*args) }
97
- end
161
+ alias_method :method_type, :partial_name
162
+ deprecate method_type: :partial_name, deprecator: Spree::Deprecation
98
163
 
99
164
  def payment_profiles_supported?
100
165
  false
@@ -114,6 +179,11 @@ module Spree
114
179
  auto_capture.nil? ? Spree::Config[:auto_capture] : auto_capture
115
180
  end
116
181
 
182
+ # Check if given source is supported by this payment method
183
+ #
184
+ # Please implement validation logic in your payment method implementation
185
+ #
186
+ # @see Spree::PaymentMethod::CreditCard#supports?
117
187
  def supports?(_source)
118
188
  true
119
189
  end
@@ -125,5 +195,21 @@ module Spree
125
195
  def store_credit?
126
196
  is_a? Spree::PaymentMethod::StoreCredit
127
197
  end
198
+
199
+ protected
200
+
201
+ # Represents the gateway class of this payment method
202
+ #
203
+ def gateway_class
204
+ if respond_to? :provider_class
205
+ Spree::Deprecation.warn \
206
+ "provider_class is deprecated and will be removed from Solidus 3.0 " \
207
+ "(use gateway_class instead)"
208
+ public_send :provider_class
209
+ else
210
+ raise ::NotImplementedError, "You must implement gateway_class method for #{self.class}."
211
+ end
212
+ end
213
+ deprecate provider_class: :gateway_class, deprecator: Spree::Deprecation
128
214
  end
129
215
  end
@@ -104,7 +104,11 @@ module Spree::Preferences::Preferable
104
104
  when :password
105
105
  value.to_s
106
106
  when :decimal
107
- BigDecimal.new(value.to_s)
107
+ begin
108
+ value.to_s.to_d
109
+ rescue ArgumentError
110
+ BigDecimal.new(0)
111
+ end
108
112
  when :integer
109
113
  value.to_i
110
114
  when :boolean
@@ -33,7 +33,7 @@ module Spree::Preferences
33
33
  # has been cleared from the cache
34
34
 
35
35
  # does it exist in the database?
36
- if preference = Spree::Preference.find_by_key(key)
36
+ if preference = Spree::Preference.find_by(key: key)
37
37
  # it does exist
38
38
  val = preference.value
39
39
  else
@@ -74,7 +74,7 @@ module Spree::Preferences
74
74
  def destroy(cache_key)
75
75
  return unless should_persist?
76
76
 
77
- preference = Spree::Preference.find_by_key(cache_key)
77
+ preference = Spree::Preference.find_by(key: cache_key)
78
78
  preference.destroy if preference
79
79
  end
80
80
 
@@ -171,9 +171,13 @@ module Spree
171
171
  where("#{Spree::Product.quoted_table_name}.deleted_at IS NULL or #{Spree::Product.quoted_table_name}.deleted_at >= ?", Time.current)
172
172
  end
173
173
 
174
+ scope :with_master_price, -> do
175
+ joins(:master).where(Spree::Price.where(Spree::Variant.arel_table[:id].eq(Spree::Price.arel_table[:variant_id])).exists)
176
+ end
177
+
174
178
  # Can't use add_search_scope for this as it needs a default argument
175
179
  def self.available(available_on = nil)
176
- joins(master: :prices).where("#{Spree::Product.quoted_table_name}.available_on <= ?", available_on || Time.current)
180
+ with_master_price.where("#{Spree::Product.quoted_table_name}.available_on <= ?", available_on || Time.current)
177
181
  end
178
182
  search_scopes << :available
179
183
 
@@ -181,7 +185,16 @@ module Spree
181
185
  group("spree_products.id").joins(:taxons).where(Spree::Taxon.arel_table[:name].eq(name))
182
186
  end
183
187
 
188
+ def self.with_variant_sku_cont(sku)
189
+ sku_match = "%#{sku}%"
190
+ variant_table = Spree::Variant.arel_table
191
+ subquery = Spree::Variant.where(variant_table[:sku].matches(sku_match).and(variant_table[:product_id].eq(arel_table[:id])))
192
+ where(subquery.exists)
193
+ end
194
+
184
195
  def self.distinct_by_product_ids(sort_order = nil)
196
+ Spree::Deprecation.warn "Product.distinct_by_product_ids is deprecated and should not be used"
197
+
185
198
  sort_column = sort_order.split(" ").first
186
199
 
187
200
  # Postgres will complain when using ordering by expressions not present in
@@ -80,7 +80,7 @@ module Spree
80
80
 
81
81
  after_initialize :ensure_master
82
82
 
83
- after_save :run_touch_callbacks, if: :changed?
83
+ after_save :run_touch_callbacks, if: :saved_changes?
84
84
  after_touch :touch_taxons
85
85
 
86
86
  before_validation :normalize_slug, on: :update
@@ -103,6 +103,10 @@ module Spree
103
103
  self.whitelisted_ransackable_associations = %w[stores variants_including_master master variants]
104
104
  self.whitelisted_ransackable_attributes = %w[slug]
105
105
 
106
+ def self.ransackable_scopes(_auth_object = nil)
107
+ %i(with_deleted with_variant_sku_cont)
108
+ end
109
+
106
110
  # @return [Boolean] true if there are any variants
107
111
  def has_variants?
108
112
  variants.any?
@@ -339,10 +343,12 @@ module Spree
339
343
  # Iterate through this products taxons and taxonomies and touch their timestamps in a batch
340
344
  def touch_taxons
341
345
  taxons_to_touch = taxons.map(&:self_and_ancestors).flatten.uniq
342
- Spree::Taxon.where(id: taxons_to_touch.map(&:id)).update_all(updated_at: Time.current)
346
+ unless taxons_to_touch.empty?
347
+ Spree::Taxon.where(id: taxons_to_touch.map(&:id)).update_all(updated_at: Time.current)
343
348
 
344
- taxonomy_ids_to_touch = taxons_to_touch.map(&:taxonomy_id).flatten.uniq
345
- Spree::Taxonomy.where(id: taxonomy_ids_to_touch).update_all(updated_at: Time.current)
349
+ taxonomy_ids_to_touch = taxons_to_touch.map(&:taxonomy_id).flatten.uniq
350
+ Spree::Taxonomy.where(id: taxonomy_ids_to_touch).update_all(updated_at: Time.current)
351
+ end
346
352
  end
347
353
 
348
354
  def remove_taxon(taxon)
@@ -36,5 +36,9 @@ module Spree
36
36
  end
37
37
  end
38
38
  end
39
+
40
+ def to_partial_path
41
+ "spree/admin/promotions/actions/#{model_name.element}"
42
+ end
39
43
  end
40
44
  end
@@ -2,10 +2,11 @@ class ::Spree::PromotionCode::BatchBuilder
2
2
  attr_reader :promotion_code_batch
3
3
  delegate :promotion, :number_of_codes, :base_code, to: :promotion_code_batch
4
4
 
5
- class_attribute :random_code_length, :batch_size, :sample_characters
5
+ class_attribute :random_code_length, :batch_size, :sample_characters, :join_characters
6
6
  self.random_code_length = 6
7
7
  self.batch_size = 1_000
8
8
  self.sample_characters = ('a'..'z').to_a + (2..9).to_a.map(&:to_s)
9
+ self.join_characters = "_"
9
10
 
10
11
  def initialize(promotion_code_batch)
11
12
  @promotion_code_batch = promotion_code_batch
@@ -54,7 +55,7 @@ class ::Spree::PromotionCode::BatchBuilder
54
55
  sample_characters.sample
55
56
  end.join
56
57
 
57
- "#{base_code}_#{suffix}"
58
+ "#{base_code}#{join_characters}#{suffix}"
58
59
  end
59
60
 
60
61
  def get_unique_codes(code_set)
@@ -30,6 +30,10 @@ module Spree
30
30
  @eligibility_errors ||= ActiveModel::Errors.new(self)
31
31
  end
32
32
 
33
+ def to_partial_path
34
+ "spree/admin/promotions/rules/#{model_name.element}"
35
+ end
36
+
33
37
  private
34
38
 
35
39
  def unique_per_promotion
@@ -3,6 +3,8 @@ module Spree
3
3
  has_many :role_users, class_name: "Spree::RoleUser", dependent: :destroy
4
4
  has_many :users, through: :role_users
5
5
 
6
+ validates_uniqueness_of :name
7
+
6
8
  def admin?
7
9
  name == "admin"
8
10
  end
@@ -6,6 +6,8 @@ module Spree
6
6
 
7
7
  after_create :auto_generate_spree_api_key
8
8
 
9
+ validates_uniqueness_of :role_id, scope: :user_id
10
+
9
11
  private
10
12
 
11
13
  def auto_generate_spree_api_key
@@ -95,6 +95,7 @@ module Spree
95
95
  def add_shipping_method(shipping_method, selected = false)
96
96
  shipping_rates.create(shipping_method: shipping_method, selected: selected, cost: cost)
97
97
  end
98
+ deprecate :add_shipping_method, deprecator: Spree::Deprecation
98
99
 
99
100
  def after_cancel
100
101
  manifest.each { |item| manifest_restock(item) }
@@ -171,7 +172,7 @@ module Spree
171
172
  end
172
173
 
173
174
  def refresh_rates
174
- return shipping_rates if shipped? || order.completed?
175
+ return shipping_rates if shipped?
175
176
  return [] unless can_get_rates?
176
177
 
177
178
  # StockEstimator.new assigment below will replace the current shipping_method
@@ -250,7 +251,7 @@ module Spree
250
251
  end
251
252
 
252
253
  def shipping_method
253
- selected_shipping_rate.try(:shipping_method) || shipping_rates.first.try(:shipping_method)
254
+ selected_shipping_rate.try(:shipping_method)
254
255
  end
255
256
 
256
257
  # Only one of either included_tax_total or additional_tax_total is set
@@ -332,6 +333,7 @@ module Spree
332
333
  order.contents.add(variant, quantity, { shipment: new_shipment })
333
334
 
334
335
  refresh_rates
336
+ new_shipment.refresh_rates
335
337
  save!
336
338
  new_shipment.save!
337
339
  end
@@ -36,10 +36,12 @@ module Spree
36
36
  # cause this to return incorrect results.
37
37
  join_table = Spree::ShippingMethodCategory.arel_table
38
38
  having = join_table[:id].count(true).eq(shipping_category_ids.count)
39
- joins(:shipping_method_categories).
39
+ subquery = joins(:shipping_method_categories).
40
40
  where(spree_shipping_method_categories: { shipping_category_id: shipping_category_ids }).
41
41
  group('spree_shipping_methods.id').
42
42
  having(having)
43
+
44
+ where(id: subquery.select(:id))
43
45
  end
44
46
 
45
47
  # @param stock_location [Spree::StockLocation] stock location
@@ -3,7 +3,7 @@ module Spree
3
3
  # method has been selected to deliver the shipment.
4
4
  #
5
5
  class ShippingRate < Spree::Base
6
- belongs_to :shipment, class_name: 'Spree::Shipment'
6
+ belongs_to :shipment, class_name: 'Spree::Shipment', touch: true
7
7
  belongs_to :shipping_method, -> { with_deleted }, class_name: 'Spree::ShippingMethod', inverse_of: :shipping_rates
8
8
 
9
9
  has_many :taxes,
@@ -5,8 +5,16 @@ module Spree
5
5
 
6
6
  validates :country, :name, presence: true
7
7
 
8
- def self.find_all_by_name_or_abbr(name_or_abbr)
9
- where('name = ? OR abbr = ?', name_or_abbr, name_or_abbr)
8
+ scope :with_name_or_abbr, ->(name_or_abbr) do
9
+ where(
10
+ arel_table[:name].matches(name_or_abbr).or(
11
+ arel_table[:abbr].matches(name_or_abbr)
12
+ )
13
+ )
14
+ end
15
+ class << self
16
+ alias_method :find_all_by_name_or_abbr, :with_name_or_abbr
17
+ deprecate find_all_by_name_or_abbr: :with_name_or_abbr, deprecator: Spree::Deprecation
10
18
  end
11
19
 
12
20
  # table of { country.id => [ state.id , state.name ] }, arrays sorted by name
@@ -15,7 +15,7 @@ module Spree
15
15
  # @return [String] the name of this stock item's variant
16
16
  delegate :name, to: :variant, prefix: true
17
17
 
18
- after_save :conditional_variant_touch, if: :changed?
18
+ after_save :conditional_variant_touch, if: :saved_changes?
19
19
  after_touch { variant.touch }
20
20
 
21
21
  self.whitelisted_ransackable_attributes = ['count_on_hand', 'stock_location_id']
@@ -103,8 +103,8 @@ module Spree
103
103
  def should_touch_variant?
104
104
  # the variant_id changes from nil when a new stock location is added
105
105
  inventory_cache_threshold &&
106
- (count_on_hand_changed? && count_on_hand_change.any? { |c| c < inventory_cache_threshold }) ||
107
- variant_id_changed?
106
+ (saved_change_to_count_on_hand && saved_change_to_count_on_hand.any? { |c| c < inventory_cache_threshold }) ||
107
+ saved_change_to_variant_id?
108
108
  end
109
109
 
110
110
  def inventory_cache_threshold
@@ -21,7 +21,12 @@ module Spree
21
21
 
22
22
  scope :by_url, lambda { |url| where("url like ?", "%#{url}%") }
23
23
 
24
+ class << self
25
+ deprecate :by_url, "Spree::Store.by_url is DEPRECATED", deprecator: Spree::Deprecation
26
+ end
27
+
24
28
  def self.current(store_key)
29
+ Spree::Deprecation.warn "Spree::Store.current is DEPRECATED"
25
30
  current_store = Store.find_by(code: store_key) || Store.by_url(store_key).first if store_key
26
31
  current_store || Store.default
27
32
  end
@@ -239,7 +239,7 @@ class Spree::StoreCredit < Spree::PaymentSource
239
239
  end
240
240
 
241
241
  def store_event
242
- return unless amount_changed? || amount_used_changed? || amount_authorized_changed? || [ELIGIBLE_ACTION, INVALIDATE_ACTION].include?(action)
242
+ return unless saved_change_to_amount? || saved_change_to_amount_used? || saved_change_to_amount_authorized? || [ELIGIBLE_ACTION, INVALIDATE_ACTION].include?(action)
243
243
 
244
244
  event = if action
245
245
  store_credit_events.build(action: action)
@@ -285,7 +285,7 @@ class Spree::StoreCredit < Spree::PaymentSource
285
285
  def associate_credit_type
286
286
  unless type_id
287
287
  credit_type_name = category.try(:non_expiring?) ? Spree.t("store_credit.non_expiring") : Spree.t("store_credit.expiring")
288
- self.credit_type = Spree::StoreCreditType.find_by_name(credit_type_name)
288
+ self.credit_type = Spree::StoreCreditType.find_by(name: credit_type_name)
289
289
  end
290
290
  end
291
291
  end
@@ -48,7 +48,7 @@ module Spree
48
48
  end
49
49
 
50
50
  def order
51
- Spree::Payment.find_by_response_code(authorization_code).try(:order)
51
+ Spree::Payment.find_by(response_code: authorization_code).try(:order)
52
52
  end
53
53
  end
54
54
  end
@@ -0,0 +1,30 @@
1
+ # Default implementation for finding the current store is given an HTTP request
2
+ #
3
+ # This is the new default behaviour, starting in Solidus 2.3.0. For the old
4
+ # behaviour see Spree::StoreSelector::Legacy.
5
+ #
6
+ # This attempts to find a Spree::Store with a URL matching the domain name of
7
+ # the request exactly. Failing that it will return the store marked as default.
8
+ module Spree
9
+ module StoreSelector
10
+ class ByServerName
11
+ def initialize(request)
12
+ @request = request
13
+ end
14
+
15
+ # Chooses the current store based on a request.
16
+ # @return [Spree::Store]
17
+ def store
18
+ server_name = @request.env['SERVER_NAME']
19
+
20
+ # We select a store which either matches our server name, or is default.
21
+ # We sort by `default ASC` so that a store matching SERVER_NAME will come
22
+ # first, and we will find that instead of the default.
23
+ store = Spree::Store.where(url: server_name).or(Store.where(default: true)).order(default: :asc).first
24
+
25
+ # Provide a fallback, mostly for legacy/testing purposes
26
+ store || Spree::Store.new
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,48 @@
1
+ # This class provides the old behaviour for finding a matching Spree::Store
2
+ # based on a request.
3
+ #
4
+ # To enable this, somewhere inside config/initializers/ add
5
+ #
6
+ # Spree::Config.current_store_selector_class = Spree::StoreSelector::Legacy
7
+ #
8
+ # This classes behaviour is somewhat complicated and has issues, which is why
9
+ # it has been replaced with Spree::StoreSelector::ByServerName by default.
10
+ #
11
+ # It will:
12
+ # * Find a "store_key"
13
+ # * from the HTTP_SPREE_STORE header, if it exists
14
+ # * or the server's domain name if HTTP_SPREE_STORE isn't set
15
+ # * Find a store, using the first match of:
16
+ # * having a code matching the store_key exactly
17
+ # * having a url which contains the store_key anywhere as a substring
18
+ # * has default set to true
19
+ #
20
+ module Spree
21
+ module StoreSelector
22
+ class Legacy
23
+ def initialize(request)
24
+ @request = request
25
+ end
26
+
27
+ # Chooses the current store based on a request.
28
+ # Checks request headers for HTTP_SPREE_STORE and falls back to
29
+ # looking up by the requesting server's name.
30
+ # @return [Spree::Store]
31
+ def store
32
+ current_store =
33
+ if store_key
34
+ Spree::Store.find_by(code: store_key) ||
35
+ Store.where("url like ?", "%#{store_key}%").first
36
+ end
37
+
38
+ current_store || Spree::Store.default
39
+ end
40
+
41
+ private
42
+
43
+ def store_key
44
+ @request.headers['HTTP_SPREE_STORE'] || @request.env['SERVER_NAME']
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,20 @@
1
+ module Spree
2
+ module Tax
3
+ # Simple object used to hold tax data for an item.
4
+ #
5
+ # This generic object will hold the amount of tax that should be applied to
6
+ # an item. (Either a {Spree::LineItem} or a {Spree::Shipment}.)
7
+ #
8
+ # @attr_reader [Integer] item_id the {Spree::LineItem} or {Spree::Shipment} ID
9
+ # @attr_reader [String] label information about the taxes
10
+ # @attr_reader [Spree::TaxRate] tax_rate will be used as the source for tax
11
+ # adjustments
12
+ # @attr_reader [BigDecimal] amount the amount of tax applied to the item
13
+ # @attr_reader [Boolean] included_in_price whether the amount is included
14
+ # in the items price, or additional tax.
15
+ class ItemTax
16
+ include ActiveModel::Model
17
+ attr_accessor :item_id, :label, :tax_rate, :amount, :included_in_price
18
+ end
19
+ end
20
+ end
@@ -4,8 +4,6 @@ module Spree
4
4
  class OrderAdjuster
5
5
  attr_reader :order
6
6
 
7
- include TaxHelpers
8
-
9
7
  # @param [Spree::Order] order to be adjusted
10
8
  def initialize(order)
11
9
  @order = order
@@ -14,18 +12,8 @@ module Spree
14
12
  # Creates tax adjustments for all taxable items (shipments and line items)
15
13
  # in the given order.
16
14
  def adjust!
17
- (order.line_items + order.shipments).each do |item|
18
- ItemAdjuster.new(item, order_wide_options).adjust!
19
- end
20
- end
21
-
22
- private
23
-
24
- def order_wide_options
25
- {
26
- rates_for_order: rates_for_order(order),
27
- rates_for_default_zone: rates_for_default_zone
28
- }
15
+ taxes = Spree::Config.tax_calculator_class.new(order).calculate
16
+ Spree::OrderTaxation.new(order).apply(taxes)
29
17
  end
30
18
  end
31
19
  end
@@ -0,0 +1,18 @@
1
+ module Spree
2
+ module Tax
3
+ # Simple object to pass back tax data from a calculator.
4
+ #
5
+ # Will be used by {Spree::OrderTaxation} to create or update tax
6
+ # adjustments on an order.
7
+ #
8
+ # @attr_reader [Integer] order_id the {Spree::Order} these taxes apply to
9
+ # @attr_reader [Array<Spree::Tax::ItemTax>] line_item_taxes an array of
10
+ # tax data for order's line items
11
+ # @attr_reader [Array<Spree::Tax::ItemTax>] shipment_taxes an array of
12
+ # tax data for the order's shipments
13
+ class OrderTax
14
+ include ActiveModel::Model
15
+ attr_accessor :order_id, :line_item_taxes, :shipment_taxes
16
+ end
17
+ end
18
+ end