spree_core 3.6.6 → 3.7.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (218) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/spree.js +60 -0
  3. data/app/finders/spree/countries/find.rb +31 -0
  4. data/app/finders/spree/credit_cards/find.rb +12 -0
  5. data/app/finders/spree/line_items/find_by_variant.rb +13 -0
  6. data/app/finders/spree/orders/find_current.rb +34 -0
  7. data/app/finders/spree/products/find.rb +93 -0
  8. data/app/finders/spree/taxons/find.rb +79 -0
  9. data/app/helpers/spree/base_helper.rb +7 -3
  10. data/app/helpers/spree/products_helper.rb +2 -1
  11. data/app/models/concerns/spree/adjustment_source.rb +11 -6
  12. data/app/models/concerns/spree/user_address.rb +2 -2
  13. data/app/models/concerns/spree/vat_price_calculation.rb +2 -1
  14. data/app/models/spree/ability.rb +2 -2
  15. data/app/models/spree/address.rb +23 -14
  16. data/app/models/spree/adjustable/adjustments_updater.rb +1 -1
  17. data/app/models/spree/adjustable/promotion_accumulator.rb +2 -1
  18. data/app/models/spree/adjustment.rb +18 -3
  19. data/app/models/spree/asset/support/active_storage.rb +1 -1
  20. data/app/models/spree/calculator.rb +1 -4
  21. data/app/models/spree/calculator/flexi_rate.rb +8 -11
  22. data/app/models/spree/calculator/returns/default_refund_amount.rb +3 -1
  23. data/app/models/spree/calculator/shipping/flexi_rate.rb +13 -13
  24. data/app/models/spree/country.rb +4 -0
  25. data/app/models/spree/credit_card.rb +8 -2
  26. data/app/models/spree/customer_return.rb +1 -0
  27. data/app/models/spree/fulfilment_changer.rb +117 -0
  28. data/app/models/spree/gateway.rb +1 -0
  29. data/app/models/spree/gateway/bogus.rb +3 -2
  30. data/app/models/spree/image.rb +13 -0
  31. data/app/models/spree/image/configuration/active_storage.rb +3 -3
  32. data/app/models/spree/inventory_unit.rb +1 -1
  33. data/app/models/spree/line_item.rb +3 -2
  34. data/app/models/spree/order.rb +72 -28
  35. data/app/models/spree/order/checkout.rb +10 -0
  36. data/app/models/spree/order/store_credit.rb +14 -35
  37. data/app/models/spree/order_contents.rb +22 -118
  38. data/app/models/spree/order_inventory.rb +6 -2
  39. data/app/models/spree/order_merger.rb +1 -3
  40. data/app/models/spree/order_promotion.rb +10 -0
  41. data/app/models/spree/order_updater.rb +11 -8
  42. data/app/models/spree/payment.rb +9 -3
  43. data/app/models/spree/payment/processing.rb +4 -4
  44. data/app/models/spree/preferences/store.rb +3 -3
  45. data/app/models/spree/product.rb +32 -0
  46. data/app/models/spree/product/scopes.rb +13 -34
  47. data/app/models/spree/product_property.rb +1 -1
  48. data/app/models/spree/promotion.rb +2 -0
  49. data/app/models/spree/promotion/actions/create_adjustment.rb +6 -1
  50. data/app/models/spree/promotion/actions/create_item_adjustments.rb +10 -2
  51. data/app/models/spree/promotion/actions/create_line_items.rb +3 -2
  52. data/app/models/spree/promotion/actions/free_shipping.rb +1 -0
  53. data/app/models/spree/promotion/rules/item_total.rb +2 -2
  54. data/app/models/spree/promotion_handler/coupon.rb +10 -6
  55. data/app/models/spree/promotion_handler/page.rb +1 -1
  56. data/app/models/spree/promotion_handler/promotion_duplicator.rb +4 -4
  57. data/app/models/spree/refund.rb +1 -1
  58. data/app/models/spree/reimbursement/reimbursement_type_engine.rb +1 -0
  59. data/app/models/spree/reimbursement_tax_calculator.rb +2 -2
  60. data/app/models/spree/return_item.rb +7 -4
  61. data/app/models/spree/return_item/eligibility_validator/default.rb +6 -6
  62. data/app/models/spree/return_item/exchange_variant_eligibility/same_product.rb +1 -1
  63. data/app/models/spree/shipment.rb +27 -34
  64. data/app/models/spree/shipping_method.rb +2 -0
  65. data/app/models/spree/shipping_rate.rb +31 -16
  66. data/app/models/spree/stock/adjuster.rb +1 -0
  67. data/app/models/spree/stock/estimator.rb +2 -0
  68. data/app/models/spree/stock/inventory_unit_builder.rb +5 -5
  69. data/app/models/spree/stock/packer.rb +1 -0
  70. data/app/models/spree/stock/quantifier.rb +16 -4
  71. data/app/models/spree/stock_item.rb +1 -0
  72. data/app/models/spree/stock_location.rb +1 -1
  73. data/app/models/spree/stock_movement.rb +1 -0
  74. data/app/models/spree/stock_transfer.rb +1 -1
  75. data/app/models/spree/store_credit.rb +5 -10
  76. data/app/models/spree/tax_rate.rb +3 -0
  77. data/app/models/spree/taxon.rb +2 -2
  78. data/app/models/spree/taxon_image.rb +18 -0
  79. data/app/models/spree/{taxon_icon → taxon_image}/configuration/active_storage.rb +2 -2
  80. data/app/models/spree/{taxon_icon → taxon_image}/configuration/paperclip.rb +1 -1
  81. data/app/models/spree/variant.rb +28 -2
  82. data/app/models/spree/zone.rb +5 -5
  83. data/app/paginators/spree/shared/paginate.rb +19 -0
  84. data/app/services/spree/cart/add_item.rb +43 -0
  85. data/app/services/spree/cart/create.rb +21 -0
  86. data/app/services/spree/cart/recalculate.rb +32 -0
  87. data/app/services/spree/cart/remove_item.rb +37 -0
  88. data/app/services/spree/cart/remove_line_item.rb +16 -0
  89. data/app/services/spree/cart/set_quantity.rb +22 -0
  90. data/app/services/spree/cart/update.rb +37 -0
  91. data/app/services/spree/checkout/add_store_credit.rb +51 -0
  92. data/app/services/spree/checkout/advance.rb +18 -0
  93. data/app/services/spree/checkout/complete.rb +23 -0
  94. data/app/services/spree/checkout/get_shipping_rates.rb +48 -0
  95. data/app/services/spree/checkout/next.rb +13 -0
  96. data/app/services/spree/checkout/remove_store_credit.rb +17 -0
  97. data/app/services/spree/checkout/update.rb +13 -0
  98. data/app/services/spree/compare_line_items.rb +21 -0
  99. data/app/services/spree/generate_token.rb +20 -0
  100. data/app/sorters/spree/products/sort.rb +58 -0
  101. data/config/locales/en.yml +20 -1
  102. data/db/default/spree/countries.rb +1 -1
  103. data/db/default/spree/default_reimbursement_type.rb +1 -1
  104. data/db/default/spree/roles.rb +2 -2
  105. data/db/default/spree/states.rb +2 -1
  106. data/db/default/spree/stores.rb +1 -1
  107. data/db/default/spree/zones.rb +5 -6
  108. data/db/migrate/20120831092320_spree_one_two.rb +36 -36
  109. data/db/migrate/20120831092359_spree_promo_one_two.rb +1 -1
  110. data/db/migrate/20130211190146_create_spree_stock_items.rb +1 -1
  111. data/db/migrate/20130211191120_create_spree_stock_locations.rb +1 -1
  112. data/db/migrate/20130301162924_create_shipping_method_categories.rb +1 -1
  113. data/db/migrate/20130304162240_create_spree_shipping_rates.rb +1 -1
  114. data/db/migrate/20130305143310_create_stock_movements.rb +1 -1
  115. data/db/migrate/20130418125341_create_spree_stock_transfers.rb +1 -1
  116. data/db/migrate/20140205120320_create_spree_payment_capture_events.rb +1 -1
  117. data/db/migrate/20140309023735_migrate_old_preferences.rb +5 -1
  118. data/db/migrate/20140309024355_create_spree_stores.rb +1 -1
  119. data/db/migrate/20140625214618_create_spree_refunds.rb +1 -1
  120. data/db/migrate/20140702140656_create_spree_return_authorization_inventory_unit.rb +1 -1
  121. data/db/migrate/20140713140455_create_spree_return_authorization_reasons.rb +1 -1
  122. data/db/migrate/20140713140527_create_spree_refund_reasons.rb +1 -1
  123. data/db/migrate/20140715182625_create_spree_promotion_categories.rb +1 -1
  124. data/db/migrate/20140718133010_create_spree_customer_returns.rb +1 -1
  125. data/db/migrate/20140725131539_create_spree_reimbursements.rb +1 -1
  126. data/db/migrate/20140731150017_create_spree_reimbursement_types.rb +1 -1
  127. data/db/migrate/20150118210639_create_spree_store_credits.rb +1 -1
  128. data/db/migrate/20150118211500_create_spree_store_credit_categories.rb +1 -1
  129. data/db/migrate/20150118212051_create_spree_store_credit_events.rb +1 -1
  130. data/db/migrate/20150118212101_create_spree_store_credit_types.rb +1 -1
  131. data/db/migrate/20150309161154_ensure_payments_have_numbers.rb +6 -2
  132. data/db/migrate/20180613080857_rename_guest_token_to_token_in_orders.rb +5 -0
  133. data/db/migrate/20180915160001_add_timestamps_to_spree_prices.rb +12 -0
  134. data/db/migrate/20181024100754_add_deleted_at_to_spree_credit_cards.rb +6 -0
  135. data/lib/generators/spree/custom_user/custom_user_generator.rb +1 -3
  136. data/lib/generators/spree/dummy/dummy_generator.rb +30 -33
  137. data/lib/generators/spree/dummy/templates/rails/database.yml +6 -1
  138. data/lib/generators/spree/dummy/templates/rails/test.rb +2 -0
  139. data/lib/generators/spree/dummy_model/dummy_model_generator.rb +1 -1
  140. data/lib/generators/spree/install/install_generator.rb +2 -1
  141. data/lib/spree/core.rb +2 -4
  142. data/lib/spree/core/controller_helpers/auth.rb +15 -5
  143. data/lib/spree/core/controller_helpers/common.rb +0 -11
  144. data/lib/spree/core/controller_helpers/order.rb +6 -6
  145. data/lib/spree/core/controller_helpers/respond_with.rb +3 -1
  146. data/lib/spree/core/controller_helpers/strong_parameters.rb +1 -0
  147. data/lib/spree/core/engine.rb +9 -3
  148. data/lib/spree/core/importer/order.rb +8 -12
  149. data/lib/spree/core/product_duplicator.rb +6 -1
  150. data/lib/spree/core/product_filters.rb +15 -14
  151. data/lib/spree/core/query_filters.rb +11 -0
  152. data/lib/spree/core/query_filters/comparable.rb +46 -0
  153. data/lib/spree/core/query_filters/date.rb +8 -0
  154. data/lib/spree/core/query_filters/number.rb +8 -0
  155. data/lib/spree/core/query_filters/text.rb +32 -0
  156. data/lib/spree/core/token_generator.rb +2 -2
  157. data/lib/spree/core/version.rb +1 -1
  158. data/lib/spree/i18n.rb +1 -1
  159. data/lib/spree/migrations.rb +2 -0
  160. data/lib/spree/money.rb +12 -12
  161. data/lib/spree/permitted_attributes.rb +11 -6
  162. data/lib/spree/service_module.rb +98 -0
  163. data/lib/spree/testing_support/capybara_config.rb +20 -0
  164. data/lib/spree/testing_support/capybara_ext.rb +6 -3
  165. data/lib/spree/testing_support/factories.rb +1 -1
  166. data/lib/spree/testing_support/factories/address_factory.rb +10 -9
  167. data/lib/spree/testing_support/factories/adjustment_factory.rb +8 -6
  168. data/lib/spree/testing_support/factories/country_factory.rb +4 -4
  169. data/lib/spree/testing_support/factories/credit_card_factory.rb +7 -5
  170. data/lib/spree/testing_support/factories/customer_return_factory.rb +1 -1
  171. data/lib/spree/testing_support/factories/image_factory.rb +7 -1
  172. data/lib/spree/testing_support/factories/inventory_unit_factory.rb +5 -4
  173. data/lib/spree/testing_support/factories/line_item_factory.rb +3 -2
  174. data/lib/spree/testing_support/factories/options_factory.rb +2 -3
  175. data/lib/spree/testing_support/factories/order_factory.rb +16 -12
  176. data/lib/spree/testing_support/factories/order_promotion_factory.rb +6 -0
  177. data/lib/spree/testing_support/factories/payment_factory.rb +9 -7
  178. data/lib/spree/testing_support/factories/payment_method_factory.rb +8 -8
  179. data/lib/spree/testing_support/factories/price_factory.rb +2 -2
  180. data/lib/spree/testing_support/factories/product_factory.rb +10 -10
  181. data/lib/spree/testing_support/factories/promotion_category_factory.rb +1 -1
  182. data/lib/spree/testing_support/factories/promotion_factory.rb +14 -4
  183. data/lib/spree/testing_support/factories/property_factory.rb +2 -2
  184. data/lib/spree/testing_support/factories/prototype_factory.rb +3 -3
  185. data/lib/spree/testing_support/factories/refund_factory.rb +6 -6
  186. data/lib/spree/testing_support/factories/reimbursement_factory.rb +1 -1
  187. data/lib/spree/testing_support/factories/reimbursement_type_factory.rb +2 -2
  188. data/lib/spree/testing_support/factories/return_authorization_factory.rb +4 -3
  189. data/lib/spree/testing_support/factories/role_factory.rb +1 -1
  190. data/lib/spree/testing_support/factories/shipment_factory.rb +3 -3
  191. data/lib/spree/testing_support/factories/shipping_method_factory.rb +4 -4
  192. data/lib/spree/testing_support/factories/state_factory.rb +2 -5
  193. data/lib/spree/testing_support/factories/stock_factory.rb +3 -3
  194. data/lib/spree/testing_support/factories/stock_item_factory.rb +1 -1
  195. data/lib/spree/testing_support/factories/stock_location_factory.rb +7 -7
  196. data/lib/spree/testing_support/factories/stock_movement_factory.rb +3 -3
  197. data/lib/spree/testing_support/factories/store_credit_category_factory.rb +2 -2
  198. data/lib/spree/testing_support/factories/store_credit_type_factory.rb +1 -1
  199. data/lib/spree/testing_support/factories/store_factory.rb +5 -4
  200. data/lib/spree/testing_support/factories/tax_rate_factory.rb +2 -1
  201. data/lib/spree/testing_support/factories/user_factory.rb +4 -4
  202. data/lib/spree/testing_support/factories/variant_factory.rb +15 -15
  203. data/lib/spree/testing_support/factories/zone_factory.rb +3 -3
  204. data/lib/spree/testing_support/image_helpers.rb +19 -0
  205. data/lib/tasks/core.rake +21 -4
  206. data/lib/tasks/exchanges.rake +4 -4
  207. data/spree_core.gemspec +3 -3
  208. data/vendor/assets/javascripts/fetch.umd.js +531 -0
  209. data/vendor/assets/javascripts/polyfill.min.js +1 -0
  210. metadata +50 -17
  211. data/app/assets/javascripts/spree.js.coffee +0 -59
  212. data/app/models/spree/taxon_icon.rb +0 -5
  213. data/lib/generators/spree/dummy/templates/initializers/custom_user.rb +0 -1
  214. data/lib/spree/core/environment.rb +0 -15
  215. data/lib/spree/core/environment/calculators.rb +0 -11
  216. data/lib/spree/core/environment_extension.rb +0 -28
  217. data/lib/spree/core/validators/email.rb +0 -8
  218. data/lib/spree/promo/environment.rb +0 -9
@@ -36,6 +36,7 @@ module Spree
36
36
 
37
37
  def update_backorder
38
38
  return if backorder_package.nil?
39
+
39
40
  if fulfilled?
40
41
  backorder_package.remove_item backorder_item
41
42
  elsif backorder_item.present?
@@ -33,6 +33,7 @@ module Spree
33
33
  cost = shipping_method.calculator.compute(package)
34
34
 
35
35
  next unless cost
36
+
36
37
  shipping_method.shipping_rates.new(
37
38
  cost: gross_amount(cost, taxation_options_for(shipping_method)),
38
39
  tax_rate: first_tax_rate_for(shipping_method.tax_category)
@@ -52,6 +53,7 @@ module Spree
52
53
 
53
54
  def first_tax_rate_for(tax_category)
54
55
  return unless @order.tax_zone && tax_category
56
+
55
57
  Spree::TaxRate.for_tax_category(tax_category).
56
58
  potential_rates_for_zone(@order.tax_zone).first
57
59
  end
@@ -10,11 +10,11 @@ module Spree
10
10
  # They go through multiple splits, avoid loading the
11
11
  # association to order until needed.
12
12
  InventoryUnit.new(
13
- pending: true,
14
- line_item: line_item,
15
- variant: line_item.variant,
16
- quantity: line_item.quantity,
17
- order_id: @order.id
13
+ pending: true,
14
+ line_item: line_item,
15
+ variant: line_item.variant,
16
+ quantity: line_item.quantity,
17
+ order_id: @order.id
18
18
  )
19
19
  end
20
20
  end
@@ -26,6 +26,7 @@ module Spree
26
26
  unit = inventory_unit.dup # Can be used by others, do not use directly
27
27
  if variant.should_track_inventory?
28
28
  next unless stock_location.stocks? variant
29
+
29
30
  on_hand, backordered = stock_location.fill_status(variant, unit.quantity)
30
31
  package.add(InventoryUnit.split(unit, backordered), :backordered) if backordered.positive?
31
32
  package.add(InventoryUnit.split(unit, on_hand), :on_hand) if on_hand.positive?
@@ -1,11 +1,11 @@
1
1
  module Spree
2
2
  module Stock
3
3
  class Quantifier
4
- attr_reader :stock_items, :variant
4
+ attr_reader :variant, :stock_location
5
5
 
6
- def initialize(variant)
7
- @variant = variant
8
- @stock_items = @variant.stock_items.with_active_stock_location
6
+ def initialize(variant, stock_location = nil)
7
+ @variant = variant
8
+ @stock_location = stock_location
9
9
  end
10
10
 
11
11
  def total_on_hand
@@ -23,6 +23,18 @@ module Spree
23
23
  def can_supply?(required = 1)
24
24
  variant.available? && (total_on_hand >= required || backorderable?)
25
25
  end
26
+
27
+ def stock_items
28
+ @stock_items ||= scope_to_location(variant.stock_items)
29
+ end
30
+
31
+ private
32
+
33
+ def scope_to_location(collection)
34
+ return collection.with_active_stock_location unless stock_location.present?
35
+
36
+ collection.where(stock_location: stock_location)
37
+ end
26
38
  end
27
39
  end
28
40
  end
@@ -75,6 +75,7 @@ module Spree
75
75
  # If stock was -20 but then was -25 (decrease of 5 units), do nothing.
76
76
  def process_backorders(number)
77
77
  return unless number.positive?
78
+
78
79
  units = backordered_inventory_units.first(number) # We can process atmost n backorders
79
80
 
80
81
  units.each do |unit|
@@ -7,7 +7,7 @@ module Spree
7
7
  belongs_to :state, class_name: 'Spree::State', optional: true
8
8
  belongs_to :country, class_name: 'Spree::Country'
9
9
 
10
- validates :name, presence: true
10
+ validates :name, presence: true, uniqueness: { allow_blank: true }
11
11
 
12
12
  scope :active, -> { where(active: true) }
13
13
  scope :order_default, -> { order(default: :desc, name: :asc) }
@@ -31,6 +31,7 @@ module Spree
31
31
 
32
32
  def update_stock_item_quantity
33
33
  return unless stock_item.should_track_inventory?
34
+
34
35
  stock_item.adjust_count_on_hand quantity
35
36
  end
36
37
  end
@@ -26,7 +26,7 @@ module Spree
26
26
  def transfer(source_location, destination_location, variants)
27
27
  transaction do
28
28
  variants.each_pair do |variant, quantity|
29
- source_location.unstock(variant, quantity, self) if source_location
29
+ source_location&.unstock(variant, quantity, self)
30
30
  destination_location.restock(variant, quantity, self)
31
31
 
32
32
  self.source_location = source_location
@@ -35,13 +35,8 @@ module Spree
35
35
 
36
36
  attr_accessor :action, :action_amount, :action_originator, :action_authorization_code
37
37
 
38
- def display_amount
39
- Spree::Money.new(amount)
40
- end
41
-
42
- def display_amount_used
43
- Spree::Money.new(amount_used)
44
- end
38
+ extend Spree::DisplayMoney
39
+ money_methods :amount, :amount_used
45
40
 
46
41
  def amount_remaining
47
42
  amount - amount_used - amount_authorized
@@ -204,9 +199,9 @@ module Spree
204
199
 
205
200
  def store_event
206
201
  return unless saved_change_to_amount? ||
207
- saved_change_to_amount_used? ||
208
- saved_change_to_amount_authorized? ||
209
- action == ELIGIBLE_ACTION
202
+ saved_change_to_amount_used? ||
203
+ saved_change_to_amount_authorized? ||
204
+ action == ELIGIBLE_ACTION
210
205
 
211
206
  event = if action
212
207
  store_credit_events.build(action: action)
@@ -30,6 +30,7 @@ module Spree
30
30
  # Gets the array of TaxRates appropriate for the specified tax zone
31
31
  def self.match(order_tax_zone)
32
32
  return [] unless order_tax_zone
33
+
33
34
  potential_rates_for_zone(order_tax_zone)
34
35
  end
35
36
 
@@ -82,6 +83,7 @@ module Spree
82
83
 
83
84
  def self.included_tax_amount_for(options)
84
85
  return 0 unless options[:tax_zone] && options[:tax_category]
86
+
85
87
  potential_rates_for_zone(options[:tax_zone]).
86
88
  included_in_price.
87
89
  for_tax_category(options[:tax_category]).
@@ -107,6 +109,7 @@ module Spree
107
109
 
108
110
  def amount_for_label
109
111
  return '' unless show_rate_in_label?
112
+
110
113
  ' ' + ActiveSupport::NumberHelper::NumberToPercentageConverter.convert(
111
114
  amount * 100,
112
115
  locale: I18n.locale
@@ -32,7 +32,7 @@ module Spree
32
32
  after_save :touch_ancestors_and_taxonomy
33
33
  after_touch :touch_ancestors_and_taxonomy
34
34
 
35
- has_one :icon, as: :viewable, dependent: :destroy, class_name: 'Spree::TaxonIcon'
35
+ has_one :icon, as: :viewable, dependent: :destroy, class_name: 'Spree::TaxonImage'
36
36
 
37
37
  self.whitelisted_ransackable_associations = %w[taxonomy]
38
38
 
@@ -97,7 +97,7 @@ module Spree
97
97
  end
98
98
 
99
99
  def check_for_root
100
- if taxonomy.try(:root).present? && parent_id == nil
100
+ if taxonomy.try(:root).present? && parent_id.nil?
101
101
  errors.add(:root_conflict, 'this taxonomy already has a root taxon')
102
102
  end
103
103
  end
@@ -0,0 +1,18 @@
1
+ module Spree
2
+ class TaxonImage < Asset
3
+ include Rails.application.config.use_paperclip ? Configuration::Paperclip : Configuration::ActiveStorage
4
+ include Rails.application.routes.url_helpers
5
+
6
+ def styles
7
+ self.class.styles.map do |_, size|
8
+ width, height = size[/(\d+)x(\d+)/].split('x')
9
+
10
+ {
11
+ url: polymorphic_path(attachment.variant(resize: size), only_path: true),
12
+ width: width,
13
+ height: height
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- class TaxonIcon < Asset
2
+ class TaxonImage < Asset
3
3
  module Configuration
4
4
  module ActiveStorage
5
5
  extend ActiveSupport::Concern
@@ -11,7 +11,7 @@ module Spree
11
11
 
12
12
  def self.styles
13
13
  @styles ||= {
14
- mini: '32x32>',
14
+ mini: '32x32>',
15
15
  normal: '128x128>'
16
16
  }
17
17
  end
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- class TaxonIcon < Asset
2
+ class TaxonImage < Asset
3
3
  module Configuration
4
4
  module Paperclip
5
5
  extend ActiveSupport::Concern
@@ -56,6 +56,22 @@ module Spree
56
56
  after_touch :clear_in_stock_cache
57
57
 
58
58
  scope :in_stock, -> { joins(:stock_items).where('count_on_hand > ? OR track_inventory = ?', 0, false) }
59
+ scope :backorderable, -> { joins(:stock_items).where(spree_stock_items: { backorderable: true }) }
60
+ scope :in_stock_or_backorderable, -> { in_stock.or(backorderable) }
61
+
62
+ scope :eligible, -> {
63
+ where(is_master: false).or(
64
+ where(
65
+ <<-SQL
66
+ #{Variant.quoted_table_name}.id IN (
67
+ SELECT MIN(#{Variant.quoted_table_name}.id) FROM #{Variant.quoted_table_name}
68
+ GROUP BY #{Variant.quoted_table_name}.product_id
69
+ HAVING COUNT(*) = 1
70
+ )
71
+ SQL
72
+ )
73
+ )
74
+ }
59
75
 
60
76
  scope :not_discontinued, -> do
61
77
  where(
@@ -159,6 +175,7 @@ module Spree
159
175
  end
160
176
  else
161
177
  return if current_value.name == opt_value
178
+
162
179
  option_values.delete(current_value)
163
180
  end
164
181
 
@@ -227,6 +244,10 @@ module Spree
227
244
 
228
245
  alias is_backorderable? backorderable?
229
246
 
247
+ def purchasable?
248
+ in_stock? || backorderable?
249
+ end
250
+
230
251
  # Shortcut method to determine if inventory tracking is enabled for this variant
231
252
  # This considers both variant tracking flag and site-wide inventory tracking settings
232
253
  def should_track_inventory?
@@ -253,6 +274,10 @@ module Spree
253
274
  !!discontinue_on && discontinue_on <= Time.current
254
275
  end
255
276
 
277
+ def backordered?
278
+ total_on_hand <= 0 && stock_items.exists?(backorderable: true)
279
+ end
280
+
256
281
  private
257
282
 
258
283
  def ensure_no_line_items
@@ -267,7 +292,7 @@ module Spree
267
292
  end
268
293
 
269
294
  def set_master_out_of_stock
270
- if product.master && product.master.in_stock?
295
+ if product.master&.in_stock?
271
296
  product.master.stock_items.update_all(backorderable: false)
272
297
  product.master.stock_items.each(&:reduce_count_on_hand_to_zero)
273
298
  end
@@ -276,8 +301,9 @@ module Spree
276
301
  # Ensures a new variant takes the product master price when price is not supplied
277
302
  def check_price
278
303
  if price.nil? && Spree::Config[:require_master_price]
279
- return errors.add(:base, :no_master_variant_found_to_infer_price) unless product && product.master
304
+ return errors.add(:base, :no_master_variant_found_to_infer_price) unless product&.master
280
305
  return errors.add(:base, :must_supply_price_for_variant_or_master) if self == product.master
306
+
281
307
  self.price = product.master.price
282
308
  end
283
309
  if price.present? && currency.nil?
@@ -52,12 +52,12 @@ module Spree
52
52
  # Returns nil in the case of no matches.
53
53
  def self.match(address)
54
54
  return unless address &&
55
- matches = includes(:zone_members).
55
+ matches = includes(:zone_members).
56
56
  order('spree_zones.zone_members_count', 'spree_zones.created_at').
57
57
  where("(spree_zone_members.zoneable_type = 'Spree::Country' AND " \
58
- 'spree_zone_members.zoneable_id = ?) OR ' \
59
- "(spree_zone_members.zoneable_type = 'Spree::State' AND " \
60
- 'spree_zone_members.zoneable_id = ?)', address.country_id, address.state_id).
58
+ 'spree_zone_members.zoneable_id = ?) OR ' \
59
+ "(spree_zone_members.zoneable_type = 'Spree::State' AND " \
60
+ 'spree_zone_members.zoneable_id = ?)', address.country_id, address.state_id).
61
61
  references(:zones)
62
62
 
63
63
  %w[state country].each do |zone_kind|
@@ -74,7 +74,7 @@ module Spree
74
74
  else
75
75
  not_nil_scope = members.where.not(zoneable_type: nil)
76
76
  zone_type = not_nil_scope.order('created_at ASC').pluck(:zoneable_type).last
77
- zone_type.demodulize.underscore if zone_type
77
+ zone_type&.demodulize&.underscore
78
78
  end
79
79
  end
80
80
 
@@ -0,0 +1,19 @@
1
+ module Spree
2
+ module Shared
3
+ class Paginate
4
+ def initialize(collection, params)
5
+ @collection = collection
6
+ @page = params[:page]
7
+ @per_page = params[:per_page]
8
+ end
9
+
10
+ def call
11
+ collection.page(page).per(per_page)
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :collection, :page, :per_page
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,43 @@
1
+ module Spree
2
+ module Cart
3
+ class AddItem
4
+ prepend Spree::ServiceModule::Base
5
+
6
+ def call(order:, variant:, quantity: nil, options: {})
7
+ ApplicationRecord.transaction do
8
+ run :add_to_line_item
9
+ run Spree::Cart::Recalculate
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def add_to_line_item(order:, variant:, quantity: nil, options: {})
16
+ options ||= {}
17
+ quantity ||= 1
18
+
19
+ line_item = Spree::LineItems::FindByVariant.new.execute(order: order, variant: variant, options: options)
20
+
21
+ line_item_created = line_item.nil?
22
+ if line_item.nil?
23
+ opts = ::Spree::PermittedAttributes.line_item_attributes.flatten.each_with_object({}) do |attribute, result|
24
+ result[attribute] = options[attribute]
25
+ end.merge(currency: order.currency).delete_if { |_key, value| value.nil? }
26
+
27
+ line_item = order.line_items.new(quantity: quantity,
28
+ variant: variant,
29
+ options: opts)
30
+ else
31
+ line_item.quantity += quantity.to_i
32
+ end
33
+
34
+ line_item.target_shipment = options[:shipment] if options.key? :shipment
35
+
36
+ return failure(line_item) unless line_item.save
37
+
38
+ ::Spree::TaxRate.adjust(order, [line_item.reload]) if line_item_created
39
+ success(order: order, line_item: line_item, line_item_created: line_item_created, options: options)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,21 @@
1
+ module Spree
2
+ module Cart
3
+ class Create
4
+ prepend Spree::ServiceModule::Base
5
+
6
+ def call(user:, store:, currency:, order_params: nil)
7
+ order_params ||= {}
8
+
9
+ default_params = {
10
+ user: user,
11
+ store: store,
12
+ currency: currency,
13
+ token: GenerateToken.new.call(Spree::Order)
14
+ }
15
+
16
+ order = Spree::Order.create!(default_params.merge(order_params))
17
+ success(order)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ module Spree
2
+ module Cart
3
+ class Recalculate
4
+ prepend Spree::ServiceModule::Base
5
+
6
+ def call(order:, line_item:, line_item_created: false, options: {})
7
+ order_updater = ::Spree::OrderUpdater.new(order)
8
+
9
+ order.payments.store_credits.checkout.destroy_all
10
+ order_updater.update
11
+
12
+ shipment = options[:shipment]
13
+ if shipment.present?
14
+ # ADMIN END SHIPMENT RATE FIX
15
+ # refresh shipments to ensure correct shipment amount is calculated when using price sack calculator
16
+ # for calculating shipment rates.
17
+ # Currently shipment rate is calculated on previous order total instead of current order total when updating a shipment from admin end.
18
+ order.refresh_shipment_rates(::Spree::ShippingMethod::DISPLAY_ON_BACK_END)
19
+ shipment.update_amounts
20
+ else
21
+ order.ensure_updated_shipments
22
+ end
23
+
24
+ ::Spree::PromotionHandler::Cart.new(order, line_item).activate
25
+ ::Spree::Adjustable::AdjustmentsUpdater.update(line_item)
26
+ ::Spree::TaxRate.adjust(order, [line_item.reload]) if line_item_created
27
+ order_updater.update
28
+ success(line_item)
29
+ end
30
+ end
31
+ end
32
+ end