spree_core 4.1.10 → 4.2.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/spree/base_controller.rb +1 -0
  3. data/app/helpers/spree/base_helper.rb +61 -14
  4. data/app/helpers/spree/mail_helper.rb +29 -0
  5. data/app/helpers/spree/products_helper.rb +2 -1
  6. data/app/mailers/spree/base_mailer.rb +17 -3
  7. data/app/mailers/spree/order_mailer.rb +11 -2
  8. data/app/mailers/spree/reimbursement_mailer.rb +4 -2
  9. data/app/mailers/spree/shipment_mailer.rb +4 -2
  10. data/app/models/concerns/spree/default_price.rb +2 -1
  11. data/app/models/concerns/spree/user_methods.rb +11 -5
  12. data/app/models/concerns/spree/user_payment_source.rb +1 -1
  13. data/app/models/spree/address.rb +13 -1
  14. data/app/models/spree/adjustment.rb +1 -0
  15. data/app/models/spree/app_configuration.rb +4 -0
  16. data/app/models/spree/credit_card.rb +5 -0
  17. data/app/models/spree/line_item.rb +12 -2
  18. data/app/models/spree/log_entry.rb +1 -1
  19. data/app/models/spree/option_type.rb +7 -1
  20. data/app/models/spree/order.rb +33 -7
  21. data/app/models/spree/order/address_book.rb +7 -20
  22. data/app/models/spree/order/payments.rb +10 -2
  23. data/app/models/spree/preferences/store.rb +1 -1
  24. data/app/models/spree/price.rb +26 -2
  25. data/app/models/spree/product.rb +17 -7
  26. data/app/models/spree/promotion.rb +10 -15
  27. data/app/models/spree/promotion_handler/coupon.rb +2 -3
  28. data/app/models/spree/promotion_handler/promotion_duplicator.rb +9 -3
  29. data/app/models/spree/reimbursement.rb +2 -0
  30. data/app/models/spree/shipment.rb +2 -5
  31. data/app/models/spree/stock_location.rb +13 -2
  32. data/app/models/spree/store.rb +27 -2
  33. data/app/models/spree/variant.rb +15 -2
  34. data/app/models/spree/zone.rb +4 -0
  35. data/app/presenters/spree/variant_presenter.rb +9 -1
  36. data/app/presenters/spree/variants/option_types_presenter.rb +1 -0
  37. data/app/views/layouts/spree/base_mailer.html.erb +45 -40
  38. data/app/views/spree/order_mailer/cancel_email.html.erb +19 -25
  39. data/app/views/spree/order_mailer/cancel_email.text.erb +24 -2
  40. data/app/views/spree/order_mailer/confirm_email.html.erb +18 -65
  41. data/app/views/spree/order_mailer/confirm_email.text.erb +2 -1
  42. data/app/views/spree/order_mailer/store_owner_notification_email.html.erb +23 -0
  43. data/app/views/spree/order_mailer/store_owner_notification_email.text.erb +38 -0
  44. data/app/views/spree/reimbursement_mailer/reimbursement_email.html.erb +53 -58
  45. data/app/views/spree/reimbursement_mailer/reimbursement_email.text.erb +3 -1
  46. data/app/views/spree/shared/_base_mailer_footer.html.erb +6 -14
  47. data/app/views/spree/shared/_base_mailer_header.html.erb +12 -32
  48. data/app/views/spree/shared/_base_mailer_stylesheets.html.erb +293 -625
  49. data/app/views/spree/shared/_purchased_items_table.html.erb +60 -0
  50. data/app/views/spree/shared/purchased_items_table/_adjustment.html.erb +13 -0
  51. data/app/views/spree/shared/purchased_items_table/_line_item.html.erb +27 -0
  52. data/app/views/spree/shared/purchased_items_table/_subtotal.html.erb +13 -0
  53. data/app/views/spree/shared/purchased_items_table/_total.html.erb +13 -0
  54. data/app/views/spree/shipment_mailer/shipped_email.html.erb +31 -36
  55. data/app/views/spree/shipment_mailer/shipped_email.text.erb +2 -1
  56. data/config/initializers/assets.rb +1 -0
  57. data/config/locales/en.yml +125 -16
  58. data/db/default/spree/countries.rb +10 -4
  59. data/db/default/spree/states.rb +42 -5
  60. data/db/default/spree/stores.rb +17 -12
  61. data/db/default/spree/zones.rb +1 -1
  62. data/db/migrate/20140309033438_create_store_from_preferences.rb +1 -1
  63. data/db/migrate/20191017121054_add_supported_currencies_to_store.rb +10 -0
  64. data/db/migrate/20200102141311_add_social_to_spree_stores.rb +3 -0
  65. data/db/migrate/20200308210757_add_default_locale_to_spree_store.rb +7 -0
  66. data/db/migrate/20200310145140_add_customer_support_email_to_spree_store.rb +7 -0
  67. data/db/migrate/20200421095017_add_compare_at_amount_to_spree_prices.rb +7 -0
  68. data/db/migrate/20200423123001_add_default_country_id_to_spree_store.rb +9 -0
  69. data/db/migrate/20200430072209_add_footer_fields_to_spree_stores.rb +8 -0
  70. data/db/migrate/20200513154939_add_show_property_to_spree_product_properties.rb +5 -0
  71. data/db/migrate/20200607161221_add_store_owner_order_notification_delivered_to_spree_orders.rb +7 -0
  72. data/db/migrate/20200607161222_add_new_order_notifications_email_to_spree_stores.rb +7 -0
  73. data/db/migrate/20200610113542_add_label_to_spree_addresses.rb +5 -0
  74. data/db/migrate/20200826075557_add_unique_index_on_taxon_id_and_product_id_to_spree_products_taxons.rb +5 -0
  75. data/db/migrate/20201006110150_add_checkout_zone_field_to_store.rb +12 -0
  76. data/db/migrate/20201012091259_add_filterable_column_to_spree_option_types.rb +6 -0
  77. data/db/migrate/20201013084504_add_seo_robots_to_spree_stores.rb +5 -0
  78. data/lib/generators/spree/install/templates/vendor/assets/javascripts/spree/backend/all.js +0 -2
  79. data/lib/generators/spree/install/templates/vendor/assets/javascripts/spree/frontend/all.js +0 -2
  80. data/lib/generators/spree/mailers_preview/mailers_preview_generator.rb +23 -0
  81. data/lib/generators/spree/mailers_preview/templates/mailers/previews/order_preview.rb +13 -0
  82. data/lib/generators/spree/mailers_preview/templates/mailers/previews/reimbursement_preview.rb +5 -0
  83. data/lib/generators/spree/mailers_preview/templates/mailers/previews/shipment_preview.rb +5 -0
  84. data/lib/generators/spree/mailers_preview/templates/mailers/previews/user_preview.rb +11 -0
  85. data/lib/spree/core.rb +2 -0
  86. data/lib/spree/core/controller_helpers/common.rb +1 -0
  87. data/lib/spree/core/controller_helpers/currency_helpers.rb +15 -0
  88. data/lib/spree/core/controller_helpers/order.rb +9 -4
  89. data/lib/spree/core/controller_helpers/store.rb +12 -1
  90. data/lib/spree/core/importer/order.rb +9 -9
  91. data/lib/spree/core/version.rb +1 -1
  92. data/lib/spree/permitted_attributes.rb +8 -5
  93. data/lib/spree/testing_support/authorization_helpers.rb +7 -4
  94. data/lib/spree/testing_support/factories/promotion_factory.rb +29 -17
  95. data/lib/spree/testing_support/factories/store_factory.rb +11 -8
  96. data/lib/spree/testing_support/factories/zone_factory.rb +1 -1
  97. data/lib/spree/testing_support/i18n.rb +1 -1
  98. data/spree_core.gemspec +6 -5
  99. data/vendor/assets/javascripts/cleave.js +1669 -0
  100. metadata +57 -16
  101. data/app/views/spree/order_mailer/_adjustment.html.erb +0 -8
  102. data/app/views/spree/order_mailer/_subtotal.html.erb +0 -8
  103. data/app/views/spree/order_mailer/_total.html.erb +0 -8
@@ -19,12 +19,18 @@ module Spree
19
19
 
20
20
  default_scope { order(:position) }
21
21
 
22
+ scope :filterable, -> { where(filterable: true) }
23
+
22
24
  accepts_nested_attributes_for :option_values, reject_if: ->(ov) { ov[:name].blank? || ov[:presentation].blank? }, allow_destroy: true
23
25
 
24
26
  after_touch :touch_all_products
25
27
 
26
28
  def filter_param
27
- presentation.titleize.delete(' ').downcase
29
+ name.titleize.delete(' ').downcase
30
+ end
31
+
32
+ def self.color
33
+ find_by(name: 'color')
28
34
  end
29
35
 
30
36
  private
@@ -22,7 +22,7 @@ module Spree
22
22
  money_methods :outstanding_balance, :item_total, :adjustment_total,
23
23
  :included_tax_total, :additional_tax_total, :tax_total,
24
24
  :shipment_total, :promo_total, :total,
25
- :cart_promo_total
25
+ :cart_promo_total, :pre_tax_item_amount, :pre_tax_total
26
26
 
27
27
  alias display_ship_total display_shipment_total
28
28
  alias_attribute :ship_total, :shipment_total
@@ -174,7 +174,12 @@ module Spree
174
174
 
175
175
  # Sum of all line item amounts pre-tax
176
176
  def pre_tax_item_amount
177
- line_items.to_a.sum(&:pre_tax_amount)
177
+ line_items.sum(:pre_tax_amount)
178
+ end
179
+
180
+ # Sum of all line item and shipment pre-tax
181
+ def pre_tax_total
182
+ pre_tax_item_amount + shipments.sum(:pre_tax_amount)
178
183
  end
179
184
 
180
185
  def shipping_discount
@@ -297,15 +302,15 @@ module Spree
297
302
  def outstanding_balance
298
303
  if canceled?
299
304
  -1 * payment_total
300
- elsif refunds.exists?
301
- # If refund has happened add it back to total to prevent balance_due payment state
302
- # See: https://github.com/spree/spree/issues/6229 & https://github.com/spree/spree/issues/8136
303
- total - (payment_total + refunds.sum(:amount))
304
305
  else
305
- total - payment_total
306
+ total - (payment_total + reimbursement_paid_total)
306
307
  end
307
308
  end
308
309
 
310
+ def reimbursement_paid_total
311
+ reimbursements.sum(&:paid_amount)
312
+ end
313
+
309
314
  def outstanding_balance?
310
315
  outstanding_balance != 0
311
316
  end
@@ -351,6 +356,8 @@ module Spree
351
356
 
352
357
  deliver_order_confirmation_email unless confirmation_delivered?
353
358
 
359
+ deliver_store_owner_order_notification_email if deliver_store_owner_order_notification_email?
360
+
354
361
  consider_risk
355
362
  end
356
363
 
@@ -641,6 +648,13 @@ module Spree
641
648
  sum(:amount)
642
649
  end
643
650
 
651
+ def has_free_shipping?
652
+ shipment_adjustments.
653
+ joins(:promotion_action).
654
+ where(spree_adjustments: { eligible: true, source_type: 'Spree::PromotionAction' },
655
+ spree_promotion_actions: { type: 'Spree::Promotion::Actions::FreeShipping' }).exists?
656
+ end
657
+
644
658
  private
645
659
 
646
660
  def link_by_email
@@ -706,5 +720,17 @@ module Spree
706
720
  def credit_card_nil_payment?(attributes)
707
721
  payments.store_credits.present? && attributes[:amount].to_f.zero?
708
722
  end
723
+
724
+ # Returns true if:
725
+ # 1. an email address is set for new order notifications AND
726
+ # 2. no notification for this order has been sent yet.
727
+ def deliver_store_owner_order_notification_email?
728
+ store.new_order_notifications_email.present? && !store_owner_notification_delivered?
729
+ end
730
+
731
+ def deliver_store_owner_order_notification_email
732
+ OrderMailer.store_owner_notification_email(id).deliver_later
733
+ update_column(:store_owner_notification_delivered, true)
734
+ end
709
735
  end
710
736
  end
@@ -55,31 +55,18 @@ module Spree
55
55
  def update_or_create_address(attributes = {})
56
56
  return if attributes.blank?
57
57
 
58
- attributes = attributes.select { |_k, v| v.present? }
58
+ attributes.transform_values! { |v| v == '' ? nil : v }
59
59
 
60
- if user
61
- address = user.addresses.build(attributes.except(:id)).check
62
- return address if address.id
63
- end
64
-
65
- if attributes[:id]
66
- address = Spree::Address.find(attributes[:id])
67
- attributes.delete(:id)
60
+ default_address_scope = user ? user.addresses : ::Spree::Address
61
+ default_address = default_address_scope.find_by(id: attributes[:id])
68
62
 
69
- if address&.editable?
70
- address.update(attributes)
71
- return address
72
- else
73
- attributes.delete(:id)
74
- end
75
- end
63
+ if default_address&.editable?
64
+ default_address.update(attributes)
76
65
 
77
- unless attributes[:id]
78
- address = Spree::Address.new(attributes)
79
- address.save
66
+ return default_address
80
67
  end
81
68
 
82
- address
69
+ ::Spree::Address.find_or_create_by(attributes.except(:id, :updated_at, :created_at))
83
70
  end
84
71
  end
85
72
  end
@@ -32,7 +32,7 @@ module Spree
32
32
  end
33
33
 
34
34
  def pending_payments
35
- payments.select(&:pending?)
35
+ payments.pending
36
36
  end
37
37
 
38
38
  def unprocessed_payments
@@ -52,7 +52,7 @@ module Spree
52
52
 
53
53
  payment.public_send(method)
54
54
 
55
- if payment.completed? && payment_total != total
55
+ if payment.completed? && payment_total != total_without_pending_store_credits
56
56
  self.payment_total += payment.amount
57
57
  end
58
58
  end
@@ -60,6 +60,14 @@ module Spree
60
60
  result = !!Spree::Config[:allow_checkout_on_gateway_error]
61
61
  errors.add(:base, e.message) && (return result)
62
62
  end
63
+
64
+ # Pending store credits are not added to `self.payment_total`.
65
+ # It can cause a situation where the amount of the credit card payment reduced with store credits
66
+ # may be added twice to `self.payment_total` causing wrong `order.outstanding_balance`
67
+ # calculations and thus an incorrect payment state.
68
+ def total_without_pending_store_credits
69
+ total - payments.map { |p| p.amount if p.source.is_a?(Spree::StoreCredit) && p.pending? }.sum(&:to_f)
70
+ end
63
71
  end
64
72
  end
65
73
  end
@@ -6,7 +6,7 @@
6
6
 
7
7
  require 'singleton'
8
8
 
9
- DB_EXCEPTIONS = if defined? PG
9
+ DB_EXCEPTIONS ||= if defined? PG
10
10
  [PG::ConnectionBad, ActiveRecord::NoDatabaseError]
11
11
  elsif defined? Mysql2
12
12
  [Mysql2::Error::ConnectionError, ActiveRecord::NoDatabaseError]
@@ -15,10 +15,16 @@ module Spree
15
15
  less_than_or_equal_to: MAXIMUM_AMOUNT
16
16
  }
17
17
 
18
+ validates :compare_at_amount, allow_nil: true, numericality: {
19
+ greater_than_or_equal_to: 0,
20
+ less_than_or_equal_to: MAXIMUM_AMOUNT
21
+ }
22
+
18
23
  extend DisplayMoney
19
- money_methods :amount, :price
24
+ money_methods :amount, :price, :compare_at_amount
25
+ alias display_compare_at_price display_compare_at_amount
20
26
 
21
- self.whitelisted_ransackable_attributes = ['amount']
27
+ self.whitelisted_ransackable_attributes = ['amount', 'compare_at_amount']
22
28
 
23
29
  def money
24
30
  Spree::Money.new(amount || 0, currency: currency)
@@ -28,17 +34,35 @@ module Spree
28
34
  self[:amount] = Spree::LocalizedNumber.parse(amount)
29
35
  end
30
36
 
37
+ def compare_at_money
38
+ Spree::Money.new(compare_at_amount || 0, currency: currency)
39
+ end
40
+
41
+ def compare_at_amount=(compare_at_amount)
42
+ self[:compare_at_amount] = Spree::LocalizedNumber.parse(compare_at_amount)
43
+ end
44
+
31
45
  alias_attribute :price, :amount
46
+ alias_attribute :compare_at_price, :compare_at_amount
32
47
 
33
48
  def price_including_vat_for(price_options)
34
49
  options = price_options.merge(tax_category: variant.tax_category)
35
50
  gross_amount(price, options)
36
51
  end
37
52
 
53
+ def compare_at_price_including_vat_for(price_options)
54
+ options = price_options.merge(tax_category: variant.tax_category)
55
+ gross_amount(compare_at_price, options)
56
+ end
57
+
38
58
  def display_price_including_vat_for(price_options)
39
59
  Spree::Money.new(price_including_vat_for(price_options), currency: currency)
40
60
  end
41
61
 
62
+ def display_compare_at_price_including_vat_for(price_options)
63
+ Spree::Money.new(compare_at_price_including_vat_for(price_options), currency: currency)
64
+ end
65
+
42
66
  # Remove variant default_scope `deleted_at: nil`
43
67
  def variant
44
68
  Spree::Variant.unscoped { super }
@@ -113,17 +113,17 @@ module Spree
113
113
 
114
114
  self.whitelisted_ransackable_associations = %w[taxons stores variants_including_master master variants]
115
115
  self.whitelisted_ransackable_attributes = %w[description name slug discontinue_on]
116
- self.whitelisted_ransackable_scopes = %w[not_discontinued]
116
+ self.whitelisted_ransackable_scopes = %w[not_discontinued search_by_name]
117
117
 
118
118
  [
119
119
  :sku, :price, :currency, :weight, :height, :width, :depth, :is_master,
120
- :cost_currency, :price_in, :amount_in, :cost_price
120
+ :cost_currency, :price_in, :amount_in, :cost_price, :compare_at_price
121
121
  ].each do |method_name|
122
122
  delegate method_name, :"#{method_name}=", to: :find_or_build_master
123
123
  end
124
124
 
125
125
  delegate :display_amount, :display_price, :has_default_price?,
126
- :images, to: :find_or_build_master
126
+ :display_compare_at_price, :images, to: :find_or_build_master
127
127
 
128
128
  alias master_images images
129
129
 
@@ -160,10 +160,8 @@ module Spree
160
160
  #
161
161
  # @return [Spree::Variant]
162
162
  def default_variant
163
- track_inventory = Spree::Config[:track_inventory_levels]
164
-
165
- Rails.cache.fetch("spree/default-variant/#{cache_key_with_version}/#{track_inventory}") do
166
- if track_inventory && variants.in_stock_or_backorderable.any?
163
+ Rails.cache.fetch(default_variant_cache_key) do
164
+ if Spree::Config[:track_inventory_levels] && variants.in_stock_or_backorderable.any?
167
165
  variants.in_stock_or_backorderable.first
168
166
  else
169
167
  has_variants? ? variants.first : master
@@ -252,6 +250,14 @@ module Spree
252
250
  where conditions.inject(:or)
253
251
  end
254
252
 
253
+ def self.search_by_name(query)
254
+ if defined?(SpreeGlobalize)
255
+ joins(:translations).order(:name).where("LOWER(#{Product::Translation.table_name}.name) LIKE LOWER(:query)", query: "%#{query}%").distinct
256
+ else
257
+ where("LOWER(#{Product.table_name}.name) LIKE LOWER(:query)", query: "%#{query}%")
258
+ end
259
+ end
260
+
255
261
  # Suitable for displaying only variants that has at least one option value.
256
262
  # There may be scenarios where an option type is removed and along with it
257
263
  # all option values. At that point all variants associated with only those
@@ -344,6 +350,10 @@ module Spree
344
350
  save
345
351
  end
346
352
 
353
+ def default_variant_cache_key
354
+ "spree/default-variant/#{cache_key_with_version}/#{Spree::Config[:track_inventory_levels]}"
355
+ end
356
+
347
357
  def ensure_master
348
358
  return unless new_record?
349
359
 
@@ -28,6 +28,8 @@ module Spree
28
28
 
29
29
  before_save :normalize_blank_values
30
30
 
31
+ before_validation :normalize_code
32
+
31
33
  scope :coupons, -> { where.not(code: nil) }
32
34
  scope :advertised, -> { where(advertise: true) }
33
35
  scope :applied, lambda {
@@ -192,21 +194,10 @@ module Spree
192
194
  end
193
195
 
194
196
  def used_by?(user, excluded_orders = [])
195
- [
196
- :adjustments,
197
- :line_item_adjustments,
198
- :shipment_adjustments
199
- ].any? do |adjustment_type|
200
- user.orders.complete.joins(adjustment_type).where(
201
- spree_adjustments: {
202
- source_type: 'Spree::PromotionAction',
203
- source_id: actions.map(&:id),
204
- eligible: true
205
- }
206
- ).where.not(
207
- id: excluded_orders.map(&:id)
208
- ).any?
209
- end
197
+ user.orders.complete.joins(:promotions).joins(:all_adjustments).
198
+ where.not(spree_orders: { id: excluded_orders.map(&:id) }).
199
+ where(spree_promotions: { id: id }).
200
+ where(spree_adjustments: { source_type: 'Spree::PromotionAction', eligible: true }).any?
210
201
  end
211
202
 
212
203
  private
@@ -227,6 +218,10 @@ module Spree
227
218
  end
228
219
  end
229
220
 
221
+ def normalize_code
222
+ self.code = code.strip if code.present?
223
+ end
224
+
230
225
  def match_all?
231
226
  match_policy == 'all'
232
227
  end
@@ -25,10 +25,9 @@ module Spree
25
25
 
26
26
  def remove(coupon_code)
27
27
  promotion = order.promotions.with_coupon_code(coupon_code)
28
-
29
28
  if promotion.present?
30
29
  # Order promotion has to be destroyed before line item removing
31
- order.order_promotions.find_by!(promotion_id: promotion.id).destroy
30
+ order.order_promotions.where(promotion_id: promotion.id).destroy_all
32
31
 
33
32
  remove_promotion_adjustments(promotion)
34
33
  remove_promotion_line_items(promotion)
@@ -76,7 +75,7 @@ module Spree
76
75
  line_item = order.find_line_item_by_variant(item.variant)
77
76
  next if line_item.blank?
78
77
 
79
- Spree::Dependencies.cart_remove_item_service(order: order, item: item.variant, quantity: item.quantity)
78
+ Spree::Dependencies.cart_remove_item_service.constantize.call(order: order, item: item.variant, quantity: item.quantity)
80
79
  end
81
80
  end
82
81
 
@@ -1,15 +1,16 @@
1
1
  module Spree
2
2
  module PromotionHandler
3
3
  class PromotionDuplicator
4
- def initialize(promotion)
4
+ def initialize(promotion, random_string: nil)
5
5
  @promotion = promotion
6
+ @random_string = random_string || generate_random_string(4)
6
7
  end
7
8
 
8
9
  def duplicate
9
10
  @new_promotion = @promotion.dup
10
- @new_promotion.path = "#{@promotion.path}_new"
11
+ @new_promotion.path = "#{@promotion.path}_#{@random_string}"
11
12
  @new_promotion.name = "New #{@promotion.name}"
12
- @new_promotion.code = "#{@promotion.code}_new"
13
+ @new_promotion.code = "#{@promotion.code}_#{@random_string}"
13
14
 
14
15
  ActiveRecord::Base.transaction do
15
16
  @new_promotion.save
@@ -33,6 +34,11 @@ module Spree
33
34
  end
34
35
  end
35
36
 
37
+ def generate_random_string(number)
38
+ charset = Array('A'..'Z') + Array('a'..'z')
39
+ Array.new(number) { charset.sample }.join
40
+ end
41
+
36
42
  def copy_actions
37
43
  @promotion.promotion_actions.each do |action|
38
44
  new_action = action.dup
@@ -60,6 +60,8 @@ module Spree
60
60
  class_attribute :reimbursement_failure_hooks
61
61
  self.reimbursement_failure_hooks = []
62
62
 
63
+ delegate :store, :currency, to: :order
64
+
63
65
  state_machine :reimbursement_status, initial: :pending do
64
66
  event :errored do
65
67
  transition to: :errored, from: :pending
@@ -42,6 +42,8 @@ module Spree
42
42
  scope :reverse_chronological, -> { order(Arel.sql('coalesce(spree_shipments.shipped_at, spree_shipments.created_at) desc'), id: :desc) }
43
43
  scope :valid, -> { where.not(state: :canceled) }
44
44
 
45
+ delegate :store, :currency, to: :order
46
+
45
47
  # shipment state machine (see http://github.com/pluginaweek/state_machine/tree/master for details)
46
48
  state_machine initial: :pending, use_transactions: false do
47
49
  event :ready do
@@ -104,11 +106,6 @@ module Spree
104
106
  inventory_units.any?(&:backordered?)
105
107
  end
106
108
 
107
- # TODO: delegate currency to Order, order.currency is mandatory
108
- def currency
109
- order ? order.currency : Spree::Config[:currency]
110
- end
111
-
112
109
  # Determines the appropriate +state+ according to the following logic:
113
110
  #
114
111
  # pending unless order is complete and +order.payment_state+ is +paid+
@@ -106,8 +106,19 @@ module Spree
106
106
  private
107
107
 
108
108
  def create_stock_items
109
- Variant.includes(:product).find_each do |variant|
110
- propagate_variant(variant)
109
+ variants_scope = Spree::Variant
110
+ prepared_stock_items = variants_scope.ids.map do |variant_id|
111
+ Hash[
112
+ 'stock_location_id', id,
113
+ 'variant_id', variant_id,
114
+ 'backorderable', backorderable_default,
115
+ 'created_at', Time.current,
116
+ 'updated_at', Time.current
117
+ ]
118
+ end
119
+ if prepared_stock_items.any?
120
+ stock_items.insert_all(prepared_stock_items)
121
+ variants_scope.touch_all
111
122
  end
112
123
  end
113
124