solidus_core 2.9.6 → 2.10.0.beta1

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

Potentially problematic release.


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

Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/app/models/concerns/spree/user_address_book.rb +2 -2
  3. data/app/models/spree/address.rb +2 -2
  4. data/app/models/spree/adjustment.rb +9 -9
  5. data/app/models/spree/asset.rb +1 -1
  6. data/app/models/spree/base.rb +0 -2
  7. data/app/models/spree/calculator.rb +1 -1
  8. data/app/models/spree/carton.rb +3 -3
  9. data/app/models/spree/classification.rb +2 -2
  10. data/app/models/spree/credit_card.rb +2 -2
  11. data/app/models/spree/customer_return.rb +1 -1
  12. data/app/models/spree/image/paperclip_attachment.rb +1 -1
  13. data/app/models/spree/inventory_unit.rb +4 -4
  14. data/app/models/spree/line_item.rb +3 -3
  15. data/app/models/spree/line_item_action.rb +2 -2
  16. data/app/models/spree/log_entry.rb +1 -1
  17. data/app/models/spree/option_value.rb +1 -1
  18. data/app/models/spree/option_values_variant.rb +2 -2
  19. data/app/models/spree/order.rb +8 -25
  20. data/app/models/spree/order_cancellations.rb +1 -1
  21. data/app/models/spree/order_contents.rb +2 -2
  22. data/app/models/spree/order_mutex.rb +1 -1
  23. data/app/models/spree/order_promotion.rb +3 -3
  24. data/app/models/spree/order_shipping.rb +1 -1
  25. data/app/models/spree/order_taxation.rb +1 -1
  26. data/app/models/spree/payment/processing.rb +1 -1
  27. data/app/models/spree/payment.rb +4 -4
  28. data/app/models/spree/payment_capture_event.rb +1 -1
  29. data/app/models/spree/payment_method/bogus_credit_card.rb +1 -1
  30. data/app/models/spree/payment_method.rb +3 -0
  31. data/app/models/spree/payment_source.rb +1 -1
  32. data/app/models/spree/price.rb +2 -2
  33. data/app/models/spree/product/scopes.rb +217 -210
  34. data/app/models/spree/product.rb +2 -2
  35. data/app/models/spree/product_option_type.rb +2 -2
  36. data/app/models/spree/product_promotion_rule.rb +2 -2
  37. data/app/models/spree/product_property.rb +2 -2
  38. data/app/models/spree/promotion.rb +1 -1
  39. data/app/models/spree/promotion_action.rb +1 -1
  40. data/app/models/spree/promotion_code.rb +2 -2
  41. data/app/models/spree/promotion_code_batch.rb +1 -1
  42. data/app/models/spree/promotion_rule.rb +1 -1
  43. data/app/models/spree/promotion_rule_role.rb +2 -2
  44. data/app/models/spree/promotion_rule_store.rb +2 -2
  45. data/app/models/spree/promotion_rule_taxon.rb +2 -2
  46. data/app/models/spree/promotion_rule_user.rb +2 -2
  47. data/app/models/spree/refund.rb +3 -3
  48. data/app/models/spree/reimbursement/credit.rb +2 -2
  49. data/app/models/spree/reimbursement.rb +2 -2
  50. data/app/models/spree/reimbursement_tax_calculator.rb +1 -1
  51. data/app/models/spree/return_authorization.rb +3 -3
  52. data/app/models/spree/return_item.rb +10 -10
  53. data/app/models/spree/role_user.rb +2 -2
  54. data/app/models/spree/shipment.rb +3 -3
  55. data/app/models/spree/shipping_manifest.rb +3 -3
  56. data/app/models/spree/shipping_method.rb +1 -1
  57. data/app/models/spree/shipping_method_category.rb +2 -2
  58. data/app/models/spree/shipping_method_stock_location.rb +2 -2
  59. data/app/models/spree/shipping_method_zone.rb +2 -2
  60. data/app/models/spree/shipping_rate.rb +2 -2
  61. data/app/models/spree/shipping_rate_tax.rb +2 -2
  62. data/app/models/spree/state.rb +1 -1
  63. data/app/models/spree/state_change.rb +2 -2
  64. data/app/models/spree/stock_item.rb +2 -2
  65. data/app/models/spree/stock_location.rb +2 -2
  66. data/app/models/spree/stock_movement.rb +2 -2
  67. data/app/models/spree/store_credit.rb +8 -8
  68. data/app/models/spree/store_credit_event.rb +3 -3
  69. data/app/models/spree/store_payment_method.rb +2 -2
  70. data/app/models/spree/store_shipping_method.rb +2 -2
  71. data/app/models/spree/tax_rate.rb +1 -1
  72. data/app/models/spree/tax_rate_tax_category.rb +2 -2
  73. data/app/models/spree/unit_cancel.rb +5 -2
  74. data/app/models/spree/user_address.rb +2 -2
  75. data/app/models/spree/user_stock_location.rb +2 -2
  76. data/app/models/spree/variant/scopes.rb +37 -29
  77. data/app/models/spree/variant.rb +1 -1
  78. data/app/models/spree/variant_property_rule.rb +1 -1
  79. data/app/models/spree/variant_property_rule_condition.rb +2 -2
  80. data/app/models/spree/variant_property_rule_value.rb +2 -2
  81. data/app/models/spree/wallet_payment_source.rb +2 -2
  82. data/app/models/spree/zone_member.rb +2 -2
  83. data/config/initializers/inflections.rb +5 -0
  84. data/config/locales/en.yml +22 -3
  85. data/lib/generators/spree/dummy/dummy_generator.rb +2 -0
  86. data/lib/generators/spree/install/install_generator.rb +0 -3
  87. data/lib/spree/core/controller_helpers/strong_parameters.rb +7 -21
  88. data/lib/spree/core/environment_extension.rb +0 -9
  89. data/lib/spree/core/importer/order.rb +3 -4
  90. data/lib/spree/core/importer/product.rb +2 -2
  91. data/lib/spree/core/version.rb +1 -1
  92. data/lib/spree/permission_sets/default_customer.rb +1 -1
  93. data/lib/spree/permitted_attributes.rb +6 -75
  94. data/lib/spree/testing_support/dummy_app/rake_tasks.rb +6 -2
  95. data/solidus_core.gemspec +4 -4
  96. data/spec/lib/search/base_spec.rb +5 -1
  97. data/spec/lib/search/variant_spec.rb +1 -1
  98. data/spec/lib/spree/core/controller_helpers/strong_parameters_spec.rb +1 -8
  99. data/spec/lib/spree/core/environment_extension_spec.rb +1 -12
  100. data/spec/lib/spree/event_spec.rb +15 -3
  101. data/spec/migrate/20190106184413_remove_code_from_spree_promotions_spec.rb +18 -4
  102. data/spec/models/spree/calculator/refunds/default_refund_amount_spec.rb +1 -1
  103. data/spec/models/spree/carton_spec.rb +2 -2
  104. data/spec/models/spree/concerns/user_methods_spec.rb +3 -3
  105. data/spec/models/spree/credit_card_spec.rb +2 -2
  106. data/spec/models/spree/customer_return_spec.rb +1 -1
  107. data/spec/models/spree/order/checkout_spec.rb +1 -1
  108. data/spec/models/spree/order/outstanding_balance_integration_spec.rb +5 -5
  109. data/spec/models/spree/order/payment_spec.rb +1 -1
  110. data/spec/models/spree/order_contents_spec.rb +1 -1
  111. data/spec/models/spree/order_inventory_spec.rb +3 -3
  112. data/spec/models/spree/order_shipping_spec.rb +1 -1
  113. data/spec/models/spree/order_spec.rb +6 -6
  114. data/spec/models/spree/payment_create_spec.rb +2 -2
  115. data/spec/models/spree/payment_spec.rb +12 -12
  116. data/spec/models/spree/promotion/rules/first_repeat_purchase_since_spec.rb +2 -2
  117. data/spec/models/spree/promotion/rules/nth_order_spec.rb +2 -2
  118. data/spec/models/spree/promotion/rules/taxon_spec.rb +0 -1
  119. data/spec/models/spree/promotion_spec.rb +0 -4
  120. data/spec/models/spree/reimbursement_type/original_payment_spec.rb +2 -2
  121. data/spec/models/spree/return_item_spec.rb +11 -11
  122. data/spec/models/spree/shipment_spec.rb +6 -6
  123. data/spec/models/spree/stock/inventory_units_finalizer_spec.rb +4 -4
  124. data/spec/models/spree/stock/quantifier_spec.rb +2 -2
  125. data/spec/models/spree/store_credit_spec.rb +7 -7
  126. data/spec/models/spree/tax_category_spec.rb +1 -1
  127. data/spec/models/spree/taxon_spec.rb +2 -2
  128. data/spec/models/spree/unit_cancel_spec.rb +5 -0
  129. data/spec/models/spree/user_spec.rb +3 -3
  130. metadata +32 -32
  131. data/spec/lib/spree/permitted_attributes_spec.rb +0 -41
@@ -2,159 +2,162 @@
2
2
 
3
3
  module Spree
4
4
  class Product < Spree::Base
5
- cattr_accessor :search_scopes do
6
- []
7
- end
5
+ module Scopes
6
+ def self.prepended(base)
7
+ base.class_eval do
8
+ cattr_accessor :search_scopes do
9
+ []
10
+ end
8
11
 
9
- def self.add_search_scope(name, &block)
10
- singleton_class.send(:define_method, name.to_sym, &block)
11
- search_scopes << name.to_sym
12
- end
12
+ def self.add_search_scope(name, &block)
13
+ singleton_class.send(:define_method, name.to_sym, &block)
14
+ search_scopes << name.to_sym
15
+ end
13
16
 
14
- def self.property_conditions(property)
15
- properties = Property.table_name
16
- case property
17
- when String then { "#{properties}.name" => property }
18
- when Property then { "#{properties}.id" => property.id }
19
- else { "#{properties}.id" => property.to_i }
20
- end
21
- end
17
+ def self.property_conditions(property)
18
+ properties = Property.table_name
19
+ case property
20
+ when String then { "#{properties}.name" => property }
21
+ when Property then { "#{properties}.id" => property.id }
22
+ else { "#{properties}.id" => property.to_i }
23
+ end
24
+ end
22
25
 
23
- scope :ascend_by_updated_at, -> { order(updated_at: :asc) }
24
- scope :descend_by_updated_at, -> { order(updated_at: :desc) }
25
- scope :ascend_by_name, -> { order(name: :asc) }
26
- scope :descend_by_name, -> { order(name: :desc) }
26
+ scope :ascend_by_updated_at, -> { order(updated_at: :asc) }
27
+ scope :descend_by_updated_at, -> { order(updated_at: :desc) }
28
+ scope :ascend_by_name, -> { order(name: :asc) }
29
+ scope :descend_by_name, -> { order(name: :desc) }
27
30
 
28
- add_search_scope :ascend_by_master_price do
29
- joins(master: :default_price).order(Spree::Price.arel_table[:amount].asc)
30
- end
31
+ add_search_scope :ascend_by_master_price do
32
+ joins(master: :default_price).order(Spree::Price.arel_table[:amount].asc)
33
+ end
31
34
 
32
- add_search_scope :descend_by_master_price do
33
- joins(master: :default_price).order(Spree::Price.arel_table[:amount].desc)
34
- end
35
+ add_search_scope :descend_by_master_price do
36
+ joins(master: :default_price).order(Spree::Price.arel_table[:amount].desc)
37
+ end
35
38
 
36
- add_search_scope :price_between do |low, high|
37
- joins(master: :default_price).where(Price.table_name => { amount: low..high })
38
- end
39
+ add_search_scope :price_between do |low, high|
40
+ joins(master: :default_price).where(Price.table_name => { amount: low..high })
41
+ end
39
42
 
40
- add_search_scope :master_price_lte do |price|
41
- joins(master: :default_price).where("#{price_table_name}.amount <= ?", price)
42
- end
43
+ add_search_scope :master_price_lte do |price|
44
+ joins(master: :default_price).where("#{price_table_name}.amount <= ?", price)
45
+ end
43
46
 
44
- add_search_scope :master_price_gte do |price|
45
- joins(master: :default_price).where("#{price_table_name}.amount >= ?", price)
46
- end
47
+ add_search_scope :master_price_gte do |price|
48
+ joins(master: :default_price).where("#{price_table_name}.amount >= ?", price)
49
+ end
47
50
 
48
- # This scope selects products in taxon AND all its descendants
49
- # If you need products only within one taxon use
50
- #
51
- # Spree::Product.joins(:taxons).where(Taxon.table_name => { id: taxon.id })
52
- #
53
- # If you're using count on the result of this scope, you must use the
54
- # `:distinct` option as well:
55
- #
56
- # Spree::Product.in_taxon(taxon).count(distinct: true)
57
- #
58
- # This is so that the count query is distinct'd:
59
- #
60
- # SELECT COUNT(DISTINCT "spree_products"."id") ...
61
- #
62
- # vs.
63
- #
64
- # SELECT COUNT(*) ...
65
- add_search_scope :in_taxon do |taxon|
66
- includes(:classifications)
67
- .where('spree_products_taxons.taxon_id' => taxon.self_and_descendants.pluck(:id))
68
- .select(Spree::Classification.arel_table[:position])
69
- .order(Spree::Classification.arel_table[:position].asc)
70
- end
51
+ # This scope selects products in taxon AND all its descendants
52
+ # If you need products only within one taxon use
53
+ #
54
+ # Spree::Product.joins(:taxons).where(Taxon.table_name => { id: taxon.id })
55
+ #
56
+ # If you're using count on the result of this scope, you must use the
57
+ # `:distinct` option as well:
58
+ #
59
+ # Spree::Product.in_taxon(taxon).count(distinct: true)
60
+ #
61
+ # This is so that the count query is distinct'd:
62
+ #
63
+ # SELECT COUNT(DISTINCT "spree_products"."id") ...
64
+ #
65
+ # vs.
66
+ #
67
+ # SELECT COUNT(*) ...
68
+ add_search_scope :in_taxon do |taxon|
69
+ includes(:classifications)
70
+ .where('spree_products_taxons.taxon_id' => taxon.self_and_descendants.pluck(:id))
71
+ .select(Spree::Classification.arel_table[:position])
72
+ .order(Spree::Classification.arel_table[:position].asc)
73
+ end
71
74
 
72
- # This scope selects products in all taxons AND all its descendants
73
- # If you need products only within one taxon use
74
- #
75
- # Spree::Product.taxons_id_eq([x,y])
76
- add_search_scope :in_taxons do |*taxons|
77
- taxons = get_taxons(taxons)
78
- taxons.first ? prepare_taxon_conditions(taxons) : where(nil)
79
- end
75
+ # This scope selects products in all taxons AND all its descendants
76
+ # If you need products only within one taxon use
77
+ #
78
+ # Spree::Product.taxons_id_eq([x,y])
79
+ add_search_scope :in_taxons do |*taxons|
80
+ taxons = get_taxons(taxons)
81
+ taxons.first ? prepare_taxon_conditions(taxons) : where(nil)
82
+ end
80
83
 
81
- # a scope that finds all products having property specified by name, object or id
82
- add_search_scope :with_property do |property|
83
- joins(:properties).where(property_conditions(property))
84
- end
84
+ # a scope that finds all products having property specified by name, object or id
85
+ add_search_scope :with_property do |property|
86
+ joins(:properties).where(property_conditions(property))
87
+ end
85
88
 
86
- # a simple test for product with a certain property-value pairing
87
- # note that it can test for properties with NULL values, but not for absent values
88
- add_search_scope :with_property_value do |property, value|
89
- joins(:properties)
90
- .where("#{Spree::ProductProperty.table_name}.value = ?", value)
91
- .where(property_conditions(property))
92
- end
89
+ # a simple test for product with a certain property-value pairing
90
+ # note that it can test for properties with NULL values, but not for absent values
91
+ add_search_scope :with_property_value do |property, value|
92
+ joins(:properties)
93
+ .where("#{Spree::ProductProperty.table_name}.value = ?", value)
94
+ .where(property_conditions(property))
95
+ end
93
96
 
94
- add_search_scope :with_option do |option|
95
- option_types = Spree::OptionType.table_name
96
- conditions = case option
97
- when String then { "#{option_types}.name" => option }
98
- when OptionType then { "#{option_types}.id" => option.id }
99
- else { "#{option_types}.id" => option.to_i }
100
- end
97
+ add_search_scope :with_option do |option|
98
+ option_types = Spree::OptionType.table_name
99
+ conditions = case option
100
+ when String then { "#{option_types}.name" => option }
101
+ when OptionType then { "#{option_types}.id" => option.id }
102
+ else { "#{option_types}.id" => option.to_i }
103
+ end
101
104
 
102
- joins(:option_types).where(conditions)
103
- end
105
+ joins(:option_types).where(conditions)
106
+ end
104
107
 
105
- add_search_scope :with_option_value do |option, value|
106
- option_values = Spree::OptionValue.table_name
107
- option_type_id = case option
108
- when String then Spree::OptionType.find_by(name: option) || option.to_i
109
- when Spree::OptionType then option.id
110
- else option.to_i
111
- end
108
+ add_search_scope :with_option_value do |option, value|
109
+ option_values = Spree::OptionValue.table_name
110
+ option_type_id = case option
111
+ when String then Spree::OptionType.find_by(name: option) || option.to_i
112
+ when Spree::OptionType then option.id
113
+ else option.to_i
114
+ end
112
115
 
113
- conditions = "#{option_values}.name = ? AND #{option_values}.option_type_id = ?", value, option_type_id
114
- group('spree_products.id').joins(variants_including_master: :option_values).where(conditions)
115
- end
116
+ conditions = "#{option_values}.name = ? AND #{option_values}.option_type_id = ?", value, option_type_id
117
+ group('spree_products.id').joins(variants_including_master: :option_values).where(conditions)
118
+ end
116
119
 
117
- # Finds all products which have either:
118
- # 1) have an option value with the name matching the one given
119
- # 2) have a product property with a value matching the one given
120
- add_search_scope :with do |value|
121
- includes(variants_including_master: :option_values).
122
- includes(:product_properties).
123
- where("#{Spree::OptionValue.table_name}.name = ? OR #{Spree::ProductProperty.table_name}.value = ?", value, value)
124
- end
120
+ # Finds all products which have either:
121
+ # 1) have an option value with the name matching the one given
122
+ # 2) have a product property with a value matching the one given
123
+ add_search_scope :with do |value|
124
+ includes(variants_including_master: :option_values).
125
+ includes(:product_properties).
126
+ where("#{Spree::OptionValue.table_name}.name = ? OR #{Spree::ProductProperty.table_name}.value = ?", value, value)
127
+ end
125
128
 
126
- # Finds all products that have a name containing the given words.
127
- add_search_scope :in_name do |words|
128
- like_any([:name], prepare_words(words))
129
- end
129
+ # Finds all products that have a name containing the given words.
130
+ add_search_scope :in_name do |words|
131
+ like_any([:name], prepare_words(words))
132
+ end
130
133
 
131
- # Finds all products that have a name or meta_keywords containing the given words.
132
- add_search_scope :in_name_or_keywords do |words|
133
- like_any([:name, :meta_keywords], prepare_words(words))
134
- end
134
+ # Finds all products that have a name or meta_keywords containing the given words.
135
+ add_search_scope :in_name_or_keywords do |words|
136
+ like_any([:name, :meta_keywords], prepare_words(words))
137
+ end
135
138
 
136
- # Finds all products that have a name, description, meta_description or meta_keywords containing the given keywords.
137
- add_search_scope :in_name_or_description do |words|
138
- like_any([:name, :description, :meta_description, :meta_keywords], prepare_words(words))
139
- end
139
+ # Finds all products that have a name, description, meta_description or meta_keywords containing the given keywords.
140
+ add_search_scope :in_name_or_description do |words|
141
+ like_any([:name, :description, :meta_description, :meta_keywords], prepare_words(words))
142
+ end
140
143
 
141
- # Finds all products that have the ids matching the given collection of ids.
142
- # Alternatively, you could use find(collection_of_ids), but that would raise an exception if one product couldn't be found
143
- add_search_scope :with_ids do |*ids|
144
- where(id: ids)
145
- end
144
+ # Finds all products that have the ids matching the given collection of ids.
145
+ # Alternatively, you could use find(collection_of_ids), but that would raise an exception if one product couldn't be found
146
+ add_search_scope :with_ids do |*ids|
147
+ where(id: ids)
148
+ end
146
149
 
147
- # Sorts products from most popular (popularity is extracted from how many
148
- # times use has put product in cart, not completed orders)
149
- #
150
- # there is alternative faster and more elegant solution, it has small drawback though,
151
- # it doesn stack with other scopes :/
152
- #
153
- # joins: "LEFT OUTER JOIN (SELECT line_items.variant_id as vid, COUNT(*) as cnt FROM line_items GROUP BY line_items.variant_id) AS popularity_count ON variants.id = vid",
154
- # order: 'COALESCE(cnt, 0) DESC'
155
- add_search_scope :descend_by_popularity do
156
- joins(:master).
157
- order(%{
150
+ # Sorts products from most popular (popularity is extracted from how many
151
+ # times use has put product in cart, not completed orders)
152
+ #
153
+ # there is alternative faster and more elegant solution, it has small drawback though,
154
+ # it doesn stack with other scopes :/
155
+ #
156
+ # joins: "LEFT OUTER JOIN (SELECT line_items.variant_id as vid, COUNT(*) as cnt FROM line_items GROUP BY line_items.variant_id) AS popularity_count ON variants.id = vid",
157
+ # order: 'COALESCE(cnt, 0) DESC'
158
+ add_search_scope :descend_by_popularity do
159
+ joins(:master).
160
+ order(%{
158
161
  COALESCE((
159
162
  SELECT
160
163
  COUNT(#{Spree::LineItem.quoted_table_name}.id)
@@ -168,93 +171,97 @@ module Spree
168
171
  popular_variants.product_id = #{Spree::Product.quoted_table_name}.id
169
172
  ), 0) DESC
170
173
  })
171
- end
172
-
173
- add_search_scope :not_deleted do
174
- where("#{Spree::Product.quoted_table_name}.deleted_at IS NULL or #{Spree::Product.quoted_table_name}.deleted_at >= ?", Time.current)
175
- end
176
-
177
- scope :with_master_price, -> do
178
- joins(:master).where(Spree::Price.where(Spree::Variant.arel_table[:id].eq(Spree::Price.arel_table[:variant_id])).arel.exists)
179
- end
180
-
181
- # Can't use add_search_scope for this as it needs a default argument
182
- def self.available(available_on = nil)
183
- with_master_price.where("#{Spree::Product.quoted_table_name}.available_on <= ?", available_on || Time.current)
184
- end
185
- search_scopes << :available
186
-
187
- add_search_scope :taxons_name_eq do |name|
188
- group("spree_products.id").joins(:taxons).where(Spree::Taxon.arel_table[:name].eq(name))
189
- end
190
-
191
- def self.with_variant_sku_cont(sku)
192
- sku_match = "%#{sku}%"
193
- variant_table = Spree::Variant.arel_table
194
- subquery = Spree::Variant.where(variant_table[:sku].matches(sku_match).and(variant_table[:product_id].eq(arel_table[:id])))
195
- where(subquery.arel.exists)
196
- end
174
+ end
197
175
 
198
- def self.distinct_by_product_ids(sort_order = nil)
199
- Spree::Deprecation.warn "Product.distinct_by_product_ids is deprecated and should not be used"
200
-
201
- sort_column = sort_order.split(" ").first
202
-
203
- # Postgres will complain when using ordering by expressions not present in
204
- # SELECT DISTINCT. e.g.
205
- #
206
- # PG::InvalidColumnReference: ERROR: for SELECT DISTINCT, ORDER BY
207
- # expressions must appear in select list. e.g.
208
- #
209
- # SELECT DISTINCT "spree_products".* FROM "spree_products" LEFT OUTER JOIN
210
- # "spree_variants" ON "spree_variants"."product_id" = "spree_products"."id" AND "spree_variants"."is_master" = 't'
211
- # AND "spree_variants"."deleted_at" IS NULL LEFT OUTER JOIN "spree_prices" ON
212
- # "spree_prices"."variant_id" = "spree_variants"."id" AND "spree_prices"."currency" = 'USD'
213
- # AND "spree_prices"."deleted_at" IS NULL WHERE "spree_products"."deleted_at" IS NULL AND ('t'='t')
214
- # ORDER BY "spree_prices"."amount" ASC LIMIT 10 OFFSET 0
215
- #
216
- # Don't allow sort_column, a variable coming from params,
217
- # to be anything but a column in the database
218
- if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' && !column_names.include?(sort_column)
219
- all
220
- else
221
- distinct
222
- end
223
- end
176
+ add_search_scope :not_deleted do
177
+ where("#{Spree::Product.quoted_table_name}.deleted_at IS NULL or #{Spree::Product.quoted_table_name}.deleted_at >= ?", Time.current)
178
+ end
224
179
 
225
- class << self
226
- private
180
+ scope :with_master_price, -> do
181
+ joins(:master).where(Spree::Price.where(Spree::Variant.arel_table[:id].eq(Spree::Price.arel_table[:variant_id])).arel.exists)
182
+ end
183
+ # Can't use add_search_scope for this as it needs a default argument
184
+ def self.available(available_on = nil)
185
+ with_master_price.where("#{Spree::Product.quoted_table_name}.available_on <= ?", available_on || Time.current)
186
+ end
187
+ search_scopes << :available
227
188
 
228
- def price_table_name
229
- Spree::Price.quoted_table_name
230
- end
189
+ add_search_scope :taxons_name_eq do |name|
190
+ group("spree_products.id").joins(:taxons).where(Spree::Taxon.arel_table[:name].eq(name))
191
+ end
231
192
 
232
- # specifically avoid having an order for taxon search (conflicts with main order)
233
- def prepare_taxon_conditions(taxons)
234
- ids = taxons.map { |taxon| taxon.self_and_descendants.pluck(:id) }.flatten.uniq
235
- joins(:taxons).where("#{Spree::Taxon.table_name}.id" => ids)
236
- end
193
+ def self.with_variant_sku_cont(sku)
194
+ sku_match = "%#{sku}%"
195
+ variant_table = Spree::Variant.arel_table
196
+ subquery = Spree::Variant.where(variant_table[:sku].matches(sku_match).and(variant_table[:product_id].eq(arel_table[:id])))
197
+ where(subquery.arel.exists)
198
+ end
237
199
 
238
- # Produce an array of keywords for use in scopes.
239
- # Always return array with at least an empty string to avoid SQL errors
240
- def prepare_words(words)
241
- return [''] if words.blank?
242
- a = words.split(/[,\s]/).map(&:strip)
243
- a.any? ? a : ['']
244
- end
200
+ def self.distinct_by_product_ids(sort_order = nil)
201
+ Spree::Deprecation.warn "Product.distinct_by_product_ids is deprecated and should not be used"
202
+
203
+ sort_column = sort_order.split(" ").first
204
+
205
+ # Postgres will complain when using ordering by expressions not present in
206
+ # SELECT DISTINCT. e.g.
207
+ #
208
+ # PG::InvalidColumnReference: ERROR: for SELECT DISTINCT, ORDER BY
209
+ # expressions must appear in select list. e.g.
210
+ #
211
+ # SELECT DISTINCT "spree_products".* FROM "spree_products" LEFT OUTER JOIN
212
+ # "spree_variants" ON "spree_variants"."product_id" = "spree_products"."id" AND "spree_variants"."is_master" = 't'
213
+ # AND "spree_variants"."deleted_at" IS NULL LEFT OUTER JOIN "spree_prices" ON
214
+ # "spree_prices"."variant_id" = "spree_variants"."id" AND "spree_prices"."currency" = 'USD'
215
+ # AND "spree_prices"."deleted_at" IS NULL WHERE "spree_products"."deleted_at" IS NULL AND ('t'='t')
216
+ # ORDER BY "spree_prices"."amount" ASC LIMIT 10 OFFSET 0
217
+ #
218
+ # Don't allow sort_column, a variable coming from params,
219
+ # to be anything but a column in the database
220
+ if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' && !column_names.include?(sort_column)
221
+ all
222
+ else
223
+ distinct
224
+ end
225
+ end
245
226
 
246
- def get_taxons(*ids_or_records_or_names)
247
- taxons = Spree::Taxon.table_name
248
- ids_or_records_or_names.flatten.map { |t|
249
- case t
250
- when Integer then Spree::Taxon.find_by(id: t)
251
- when ActiveRecord::Base then t
252
- when String
253
- Spree::Taxon.find_by(name: t) ||
254
- Spree::Taxon.where("#{taxons}.permalink LIKE ? OR #{taxons}.permalink = ?", "%/#{t}/", "#{t}/").first
227
+ class << self
228
+ private
229
+
230
+ def price_table_name
231
+ Spree::Price.quoted_table_name
232
+ end
233
+
234
+ # specifically avoid having an order for taxon search (conflicts with main order)
235
+ def prepare_taxon_conditions(taxons)
236
+ ids = taxons.map { |taxon| taxon.self_and_descendants.pluck(:id) }.flatten.uniq
237
+ joins(:taxons).where("#{Spree::Taxon.table_name}.id" => ids)
238
+ end
239
+
240
+ # Produce an array of keywords for use in scopes.
241
+ # Always return array with at least an empty string to avoid SQL errors
242
+ def prepare_words(words)
243
+ return [''] if words.blank?
244
+ a = words.split(/[,\s]/).map(&:strip)
245
+ a.any? ? a : ['']
246
+ end
247
+
248
+ def get_taxons(*ids_or_records_or_names)
249
+ taxons = Spree::Taxon.table_name
250
+ ids_or_records_or_names.flatten.map { |t|
251
+ case t
252
+ when Integer then Spree::Taxon.find_by(id: t)
253
+ when ActiveRecord::Base then t
254
+ when String
255
+ Spree::Taxon.find_by(name: t) ||
256
+ Spree::Taxon.where("#{taxons}.permalink LIKE ? OR #{taxons}.permalink = ?", "%/#{t}/", "#{t}/").first
257
+ end
258
+ }.compact.flatten.uniq
259
+ end
255
260
  end
256
- }.compact.flatten.uniq
261
+ end
257
262
  end
263
+
264
+ ::Spree::Product.prepend self
258
265
  end
259
266
  end
260
267
  end
@@ -44,8 +44,8 @@ module Spree
44
44
  has_many :product_promotion_rules, dependent: :destroy
45
45
  has_many :promotion_rules, through: :product_promotion_rules
46
46
 
47
- belongs_to :tax_category, class_name: 'Spree::TaxCategory'
48
- belongs_to :shipping_category, class_name: 'Spree::ShippingCategory', inverse_of: :products
47
+ belongs_to :tax_category, class_name: 'Spree::TaxCategory', optional: true
48
+ belongs_to :shipping_category, class_name: 'Spree::ShippingCategory', inverse_of: :products, optional: true
49
49
 
50
50
  has_one :master,
51
51
  -> { where(is_master: true).with_deleted },
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Spree
4
4
  class ProductOptionType < Spree::Base
5
- belongs_to :product, class_name: 'Spree::Product', inverse_of: :product_option_types, touch: true
6
- belongs_to :option_type, class_name: 'Spree::OptionType', inverse_of: :product_option_types
5
+ belongs_to :product, class_name: 'Spree::Product', inverse_of: :product_option_types, touch: true, optional: true
6
+ belongs_to :option_type, class_name: 'Spree::OptionType', inverse_of: :product_option_types, optional: true
7
7
  acts_as_list scope: :product
8
8
  end
9
9
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Spree
4
4
  class ProductPromotionRule < Spree::Base
5
- belongs_to :product
6
- belongs_to :promotion_rule
5
+ belongs_to :product, optional: true
6
+ belongs_to :promotion_rule, optional: true
7
7
  end
8
8
  end
@@ -6,8 +6,8 @@ module Spree
6
6
 
7
7
  acts_as_list scope: :product
8
8
 
9
- belongs_to :product, touch: true, class_name: 'Spree::Product', inverse_of: :product_properties
10
- belongs_to :property, class_name: 'Spree::Property', inverse_of: :product_properties
9
+ belongs_to :product, touch: true, class_name: 'Spree::Product', inverse_of: :product_properties, optional: true
10
+ belongs_to :property, class_name: 'Spree::Property', inverse_of: :product_properties, optional: true
11
11
 
12
12
  self.whitelisted_ransackable_attributes = ['value']
13
13
  end
@@ -7,7 +7,7 @@ module Spree
7
7
 
8
8
  attr_reader :eligibility_errors
9
9
 
10
- belongs_to :promotion_category
10
+ belongs_to :promotion_category, optional: true
11
11
 
12
12
  has_many :promotion_rules, autosave: true, dependent: :destroy, inverse_of: :promotion
13
13
  alias_method :rules, :promotion_rules
@@ -14,7 +14,7 @@ module Spree
14
14
  include Discard::Model
15
15
  self.discard_column = :deleted_at
16
16
 
17
- belongs_to :promotion, class_name: 'Spree::Promotion', inverse_of: :promotion_actions
17
+ belongs_to :promotion, class_name: 'Spree::Promotion', inverse_of: :promotion_actions, optional: true
18
18
 
19
19
  scope :of_type, ->(t) { where(type: Array.wrap(t).map(&:to_s)) }
20
20
  scope :shipping, -> { of_type(Spree::Config.environment.promotions.shipping_actions.to_a) }
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Spree::PromotionCode < Spree::Base
4
- belongs_to :promotion, inverse_of: :codes
5
- belongs_to :promotion_code_batch, class_name: "Spree::PromotionCodeBatch"
4
+ belongs_to :promotion, inverse_of: :codes, optional: true
5
+ belongs_to :promotion_code_batch, class_name: "Spree::PromotionCodeBatch", optional: true
6
6
  has_many :adjustments
7
7
 
8
8
  validates :value, presence: true, uniqueness: { allow_blank: true }
@@ -5,7 +5,7 @@ module Spree
5
5
  class CantProcessStartedBatch < StandardError
6
6
  end
7
7
 
8
- belongs_to :promotion, class_name: "Spree::Promotion"
8
+ belongs_to :promotion, class_name: "Spree::Promotion", optional: true
9
9
  has_many :promotion_codes, class_name: "Spree::PromotionCode", dependent: :destroy
10
10
 
11
11
  validates :number_of_codes, numericality: { greater_than: 0 }
@@ -3,7 +3,7 @@
3
3
  module Spree
4
4
  # Base class for all promotion rules
5
5
  class PromotionRule < Spree::Base
6
- belongs_to :promotion, class_name: 'Spree::Promotion', inverse_of: :promotion_rules
6
+ belongs_to :promotion, class_name: 'Spree::Promotion', inverse_of: :promotion_rules, optional: true
7
7
 
8
8
  scope :of_type, ->(t) { where(type: t) }
9
9
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Spree
4
4
  class PromotionRuleRole < ActiveRecord::Base
5
- belongs_to :promotion_rule, class_name: 'Spree::PromotionRule'
6
- belongs_to :role, class_name: 'Spree::Role'
5
+ belongs_to :promotion_rule, class_name: 'Spree::PromotionRule', optional: true
6
+ belongs_to :role, class_name: 'Spree::Role', optional: true
7
7
  end
8
8
  end
@@ -4,7 +4,7 @@ module Spree
4
4
  class PromotionRuleStore < Spree::Base
5
5
  self.table_name = "spree_promotion_rules_stores"
6
6
 
7
- belongs_to :promotion_rule, class_name: "Spree::PromotionRule"
8
- belongs_to :store, class_name: "Spree::Store"
7
+ belongs_to :promotion_rule, class_name: "Spree::PromotionRule", optional: true
8
+ belongs_to :store, class_name: "Spree::Store", optional: true
9
9
  end
10
10
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Spree
4
4
  class PromotionRuleTaxon < Spree::Base
5
- belongs_to :promotion_rule
6
- belongs_to :taxon
5
+ belongs_to :promotion_rule, optional: true
6
+ belongs_to :taxon, optional: true
7
7
  end
8
8
  end
@@ -4,7 +4,7 @@ module Spree
4
4
  class PromotionRuleUser < Spree::Base
5
5
  self.table_name = 'spree_promotion_rules_users'
6
6
 
7
- belongs_to :promotion_rule, class_name: 'Spree::PromotionRule'
8
- belongs_to :user, class_name: Spree::UserClassHandle.new
7
+ belongs_to :promotion_rule, class_name: 'Spree::PromotionRule', optional: true
8
+ belongs_to :user, class_name: Spree::UserClassHandle.new, optional: true
9
9
  end
10
10
  end
@@ -2,9 +2,9 @@
2
2
 
3
3
  module Spree
4
4
  class Refund < Spree::Base
5
- belongs_to :payment, inverse_of: :refunds
6
- belongs_to :reason, class_name: 'Spree::RefundReason', foreign_key: :refund_reason_id
7
- belongs_to :reimbursement, inverse_of: :refunds
5
+ belongs_to :payment, inverse_of: :refunds, optional: true
6
+ belongs_to :reason, class_name: 'Spree::RefundReason', foreign_key: :refund_reason_id, optional: true
7
+ belongs_to :reimbursement, inverse_of: :refunds, optional: true
8
8
 
9
9
  has_many :log_entries, as: :source
10
10
 
@@ -6,8 +6,8 @@ module Spree
6
6
  class_attribute :default_creditable_class
7
7
  self.default_creditable_class = Spree::StoreCredit
8
8
 
9
- belongs_to :reimbursement, inverse_of: :credits
10
- belongs_to :creditable, polymorphic: true
9
+ belongs_to :reimbursement, inverse_of: :credits, optional: true
10
+ belongs_to :creditable, polymorphic: true, optional: true
11
11
 
12
12
  validates :creditable, presence: true
13
13