spree_core 2.0.13 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/spree/base_controller.rb +3 -0
  3. data/app/helpers/spree/base_helper.rb +6 -16
  4. data/app/helpers/spree/products_helper.rb +3 -8
  5. data/app/helpers/spree/taxons_helper.rb +1 -1
  6. data/app/mailers/spree/base_mailer.rb +0 -5
  7. data/app/models/spree/ability.rb +10 -7
  8. data/app/models/spree/address.rb +7 -17
  9. data/app/models/spree/adjustment.rb +15 -11
  10. data/app/models/spree/app_configuration.rb +0 -5
  11. data/app/models/spree/billing_integration.rb +0 -1
  12. data/app/models/spree/calculator/flat_percent_item_total.rb +1 -3
  13. data/app/models/spree/calculator/flat_rate.rb +2 -4
  14. data/app/models/spree/calculator/flexi_rate.rb +6 -9
  15. data/app/models/spree/calculator/per_item.rb +2 -4
  16. data/app/models/spree/calculator/percent_per_item.rb +1 -3
  17. data/app/models/spree/calculator/price_sack.rb +4 -9
  18. data/app/models/spree/calculator/shipping/flat_percent_item_total.rb +1 -2
  19. data/app/models/spree/calculator/shipping/flat_rate.rb +2 -4
  20. data/app/models/spree/calculator/shipping/flexi_rate.rb +4 -9
  21. data/app/models/spree/calculator/shipping/per_item.rb +2 -3
  22. data/app/models/spree/calculator/shipping/price_sack.rb +4 -9
  23. data/app/models/spree/classification.rb +0 -3
  24. data/app/models/spree/country.rb +1 -3
  25. data/app/models/spree/credit_card.rb +37 -38
  26. data/app/models/spree/gateway/bogus_simple.rb +0 -8
  27. data/app/models/spree/gateway.rb +1 -3
  28. data/app/models/spree/image.rb +1 -3
  29. data/app/models/spree/inventory_unit.rb +5 -8
  30. data/app/models/spree/legacy_user.rb +0 -4
  31. data/app/models/spree/line_item.rb +2 -15
  32. data/app/models/spree/option_type.rb +2 -5
  33. data/app/models/spree/option_value.rb +1 -3
  34. data/app/models/spree/order/checkout.rb +4 -13
  35. data/app/models/spree/order.rb +47 -99
  36. data/app/models/spree/order_contents.rb +4 -7
  37. data/app/models/spree/order_inventory.rb +4 -8
  38. data/app/models/spree/order_updater.rb +13 -12
  39. data/app/models/spree/payment/processing.rb +12 -19
  40. data/app/models/spree/payment.rb +17 -30
  41. data/app/models/spree/payment_method.rb +2 -3
  42. data/app/models/spree/preference.rb +1 -1
  43. data/app/models/spree/preferences/configuration.rb +1 -1
  44. data/app/models/spree/preferences/preferable.rb +1 -1
  45. data/app/models/spree/preferences/store.rb +1 -1
  46. data/app/models/spree/price.rb +0 -7
  47. data/app/models/spree/product/scopes.rb +16 -17
  48. data/app/models/spree/product.rb +27 -62
  49. data/app/models/spree/product_property.rb +3 -5
  50. data/app/models/spree/promotion/actions/create_adjustment.rb +9 -8
  51. data/app/models/spree/promotion/actions/create_line_items.rb +1 -2
  52. data/app/models/spree/promotion/rules/first_order.rb +1 -1
  53. data/app/models/spree/promotion/rules/item_total.rb +2 -4
  54. data/app/models/spree/promotion/rules/product.rb +2 -2
  55. data/app/models/spree/promotion/rules/user.rb +1 -3
  56. data/app/models/spree/promotion.rb +23 -24
  57. data/app/models/spree/promotion_action.rb +0 -2
  58. data/app/models/spree/promotion_action_line_item.rb +1 -3
  59. data/app/models/spree/promotion_rule.rb +0 -2
  60. data/app/models/spree/property.rb +2 -4
  61. data/app/models/spree/prototype.rb +0 -2
  62. data/app/models/spree/return_authorization.rb +6 -9
  63. data/app/models/spree/role.rb +0 -2
  64. data/app/models/spree/shipment.rb +19 -25
  65. data/app/models/spree/shipping_calculator.rb +0 -2
  66. data/app/models/spree/shipping_category.rb +0 -2
  67. data/app/models/spree/shipping_method.rb +6 -20
  68. data/app/models/spree/shipping_rate.rb +12 -10
  69. data/app/models/spree/state.rb +2 -4
  70. data/app/models/spree/stock/availability_validator.rb +2 -2
  71. data/app/models/spree/stock/estimator.rb +6 -20
  72. data/app/models/spree/stock/packer.rb +1 -1
  73. data/app/models/spree/stock/quantifier.rb +2 -3
  74. data/app/models/spree/stock/splitter/base.rb +1 -1
  75. data/app/models/spree/stock_item.rb +8 -18
  76. data/app/models/spree/stock_location.rb +2 -11
  77. data/app/models/spree/stock_movement.rb +2 -5
  78. data/app/models/spree/stock_transfer.rb +0 -2
  79. data/app/models/spree/tax_category.rb +0 -2
  80. data/app/models/spree/tax_rate.rb +12 -12
  81. data/app/models/spree/taxon.rb +1 -13
  82. data/app/models/spree/taxonomy.rb +3 -6
  83. data/app/models/spree/tracker.rb +0 -2
  84. data/app/models/spree/variant/scopes.rb +2 -2
  85. data/app/models/spree/variant.rb +13 -31
  86. data/app/models/spree/zone.rb +2 -7
  87. data/app/models/spree/zone_member.rb +0 -2
  88. data/app/views/spree/payments/_payment.html.erb +1 -3
  89. data/config/locales/en.yml +11 -26
  90. data/db/default/spree/countries.rb +230 -229
  91. data/db/default/spree/states.rb +57 -56
  92. data/db/default/spree/zones.rb +5 -5
  93. data/db/migrate/20130213191427_create_default_stock.rb +4 -7
  94. data/db/migrate/20130417120035_update_adjustment_states.rb +2 -2
  95. data/db/migrate/20130417123427_add_shipping_rates_to_shipments.rb +1 -1
  96. data/db/migrate/20130509115210_add_number_to_stock_transfer.rb +1 -1
  97. data/db/migrate/20130611054351_rename_shipping_methods_zones_to_spree_shipping_methods_zones.rb +0 -5
  98. data/db/migrate/20130611185927_add_user_id_index_to_spree_orders.rb +5 -0
  99. data/db/migrate/20130618041418_add_updated_at_to_spree_countries.rb +9 -0
  100. data/db/migrate/20130619012236_add_updated_at_to_spree_states.rb +9 -0
  101. data/db/migrate/20130802022321_migrate_tax_categories_to_line_items.rb +4 -5
  102. data/db/migrate/20130806145853_set_default_stock_location_on_shipments.rb +1 -1
  103. data/lib/generators/spree/dummy/dummy_generator.rb +3 -14
  104. data/lib/generators/spree/dummy/templates/rails/database.yml +0 -10
  105. data/lib/generators/spree/dummy/templates/rails/test.rb +2 -7
  106. data/lib/generators/spree/install/install_generator.rb +11 -8
  107. data/lib/spree/core/calculated_adjustments.rb +9 -8
  108. data/lib/spree/core/controller_helpers/auth.rb +2 -3
  109. data/lib/spree/core/controller_helpers/order.rb +8 -13
  110. data/lib/spree/core/controller_helpers/ssl.rb +13 -22
  111. data/lib/spree/core/controller_helpers/strong_parameters.rb +36 -0
  112. data/lib/spree/core/delegate_belongs_to.rb +0 -2
  113. data/lib/spree/core/engine.rb +1 -5
  114. data/lib/spree/core/ext/active_record.rb +2 -9
  115. data/lib/spree/core/permalinks.rb +1 -5
  116. data/lib/spree/core/product_duplicator.rb +2 -16
  117. data/lib/spree/core/product_filters.rb +37 -33
  118. data/lib/spree/core/search/base.rb +1 -1
  119. data/lib/spree/core/version.rb +1 -1
  120. data/lib/spree/core.rb +3 -31
  121. data/lib/spree/i18n.rb +0 -1
  122. data/lib/spree/money.rb +2 -177
  123. data/lib/spree/permitted_attributes.rb +95 -0
  124. data/lib/spree/promo/coupon_applicator.rb +4 -12
  125. data/lib/spree/testing_support/capybara_ext.rb +13 -17
  126. data/lib/spree/testing_support/common_rake.rb +1 -1
  127. data/lib/spree/testing_support/controller_requests.rb +3 -3
  128. data/lib/spree/testing_support/factories/credit_card_factory.rb +1 -1
  129. data/lib/spree/testing_support/factories/product_factory.rb +0 -4
  130. data/lib/spree/testing_support/factories/shipping_method_factory.rb +1 -3
  131. data/lib/spree/testing_support/factories/user_factory.rb +1 -1
  132. data/lib/spree/testing_support/factories/variant_factory.rb +0 -15
  133. data/lib/spree/testing_support/factories.rb +1 -1
  134. data/lib/spree/testing_support/order_walkthrough.rb +1 -1
  135. data/lib/tasks/core.rake +2 -2
  136. data/vendor/assets/javascripts/jquery.payment.js +497 -0
  137. metadata +166 -172
  138. data/app/views/spree/admin/shared/_report_order_criteria.html.erb +0 -17
  139. data/db/migrate/20130417120034_add_index_to_source_columns_on_adjustments.rb +0 -5
  140. data/db/migrate/20130830001033_add_shipping_category_to_shipping_methods_and_products.rb +0 -15
  141. data/db/migrate/20130830001159_migrate_old_shipping_calculators.rb +0 -19
  142. data/db/migrate/20130909115621_change_states_required_for_countries.rb +0 -9
  143. data/db/migrate/20131001013410_remove_unused_credit_card_fields.rb +0 -12
  144. data/db/migrate/20131026154747_add_track_inventory_to_variant.rb +0 -5
  145. data/db/migrate/20131113035136_add_channel_to_spree_orders.rb +0 -5
  146. data/db/migrate/20140120160805_add_index_to_variant_id_and_currency_on_prices.rb +0 -5
  147. data/db/migrate/20140205181631_default_variant_weight_to_zero.rb +0 -11
  148. data/db/migrate/20140415041315_add_user_id_created_by_id_index_to_order.rb +0 -5
  149. data/lib/spree/core/preference_rescue.rb +0 -25
@@ -8,14 +8,14 @@ module Spree
8
8
 
9
9
  # Get current line item for variant if exists
10
10
  # Add variant qty to line_item
11
- def add(variant, quantity=1, currency=nil, shipment=nil)
11
+ def add(variant, quantity = 1, currency = nil, shipment = nil)
12
12
  line_item = order.find_line_item_by_variant(variant)
13
13
  add_to_line_item(line_item, variant, quantity, currency, shipment)
14
14
  end
15
15
 
16
16
  # Get current line item for variant
17
17
  # Remove variant qty from line_item
18
- def remove(variant, quantity=1, shipment=nil)
18
+ def remove(variant, quantity = 1, shipment = nil)
19
19
  line_item = order.find_line_item_by_variant(variant)
20
20
 
21
21
  unless line_item
@@ -32,21 +32,18 @@ module Spree
32
32
  line_item.target_shipment = shipment
33
33
  line_item.quantity += quantity.to_i
34
34
  line_item.currency = currency unless currency.nil?
35
- line_item.save
36
35
  else
37
- line_item = LineItem.new(quantity: quantity)
36
+ line_item = order.line_items.new(quantity: quantity, variant: variant)
38
37
  line_item.target_shipment = shipment
39
- line_item.variant = variant
40
38
  if currency
41
39
  line_item.currency = currency unless currency.nil?
42
40
  line_item.price = variant.price_in(currency).amount
43
41
  else
44
42
  line_item.price = variant.price
45
43
  end
46
- order.line_items << line_item
47
- line_item
48
44
  end
49
45
 
46
+ line_item.save
50
47
  order.reload
51
48
  line_item
52
49
  end
@@ -32,7 +32,7 @@ module Spree
32
32
  end
33
33
 
34
34
  def inventory_units_for(variant)
35
- units = order.shipments.collect{|s| s.inventory_units.all}.flatten
35
+ units = order.shipments.collect{|s| s.inventory_units.to_a}.flatten
36
36
  units.group_by(&:variant_id)[variant.id] || []
37
37
  end
38
38
 
@@ -65,14 +65,10 @@ module Spree
65
65
  end
66
66
 
67
67
  def add_to_shipment(shipment, variant, quantity)
68
- if variant.should_track_inventory?
69
- on_hand, back_order = shipment.stock_location.fill_status(variant, quantity)
68
+ on_hand, back_order = shipment.stock_location.fill_status(variant, quantity)
70
69
 
71
- on_hand.times { shipment.set_up_inventory('on_hand', variant, order) }
72
- back_order.times { shipment.set_up_inventory('backordered', variant, order) }
73
- else
74
- quantity.times { shipment.set_up_inventory('on_hand', variant, order) }
75
- end
70
+ on_hand.times { shipment.set_up_inventory('on_hand', variant, order) }
71
+ back_order.times { shipment.set_up_inventory('backordered', variant, order) }
76
72
 
77
73
  # adding to this shipment, and removing from stock_location
78
74
  if order.completed?
@@ -25,7 +25,8 @@ module Spree
25
25
  update_shipment_state
26
26
  end
27
27
 
28
- update_adjustments
28
+ update_promotion_adjustments
29
+ update_shipping_adjustments
29
30
  # update totals a second time in case updated adjustments have an effect on the total
30
31
  update_totals
31
32
 
@@ -101,16 +102,10 @@ module Spree
101
102
  # The +payment_state+ value helps with reporting, etc. since it provides a quick and easy way to locate Orders needing attention.
102
103
  def update_payment_state
103
104
 
104
- # line_item are empty when user empties cart
105
+ #line_item are empty when user empties cart
105
106
  if line_items.empty? || round_money(order.payment_total) < round_money(order.total)
106
- if payments.present?
107
- if payments.last.state == 'failed'
108
- order.payment_state = 'failed'
109
- elsif payments.last.state == 'completed'
110
- order.payment_state = 'credit_owed'
111
- else
112
- order.payment_state = 'balance_due'
113
- end
107
+ if payments.present? && payments.last.state == 'failed'
108
+ order.payment_state = 'failed'
114
109
  else
115
110
  order.payment_state = 'balance_due'
116
111
  end
@@ -130,11 +125,17 @@ module Spree
130
125
  #
131
126
  # Adjustments will check if they are still eligible. Ineligible adjustments
132
127
  # are preserved but not counted towards adjustment_total.
133
- def update_adjustments
134
- order.adjustments.reload.each { |adjustment| adjustment.update!(order) }
128
+ def update_promotion_adjustments
129
+ order.adjustments.reload.promotion.each { |adjustment| adjustment.update!(order) }
135
130
  choose_best_promotion_adjustment
136
131
  end
137
132
 
133
+ # Shipping adjustments don't receive order on update! because they calculated
134
+ # over a shipping / package object rather than an order object
135
+ def update_shipping_adjustments
136
+ order.adjustments.reload.shipping.each { |adjustment| adjustment.update! }
137
+ end
138
+
138
139
  private
139
140
 
140
141
  # Picks one (and only one) promotion to be eligible for this order
@@ -83,37 +83,30 @@ module Spree
83
83
 
84
84
  credit_amount ||= credit_allowed >= order.outstanding_balance.abs ? order.outstanding_balance.abs : credit_allowed.abs
85
85
  credit_amount = credit_amount.to_f
86
- credit_cents = Spree::Money.new(credit_amount, currency: currency).money.cents
87
86
 
88
87
  if payment_method.payment_profiles_supported?
89
- response = payment_method.credit(credit_cents, source, response_code, gateway_options)
88
+ response = payment_method.credit((credit_amount * 100).round, source, response_code, gateway_options)
90
89
  else
91
- response = payment_method.credit(credit_cents, response_code, gateway_options)
90
+ response = payment_method.credit((credit_amount * 100).round, response_code, gateway_options)
92
91
  end
93
92
 
94
93
  record_response(response)
95
94
 
96
95
  if response.success?
97
- self.class.create({ :order => order,
98
- :source => self,
99
- :payment_method => payment_method,
100
- :amount => credit_amount.abs * -1,
101
- :response_code => response.authorization,
102
- :state => 'completed' }, :without_protection => true)
96
+ self.class.create(
97
+ :order => order,
98
+ :source => self,
99
+ :payment_method => payment_method,
100
+ :amount => credit_amount.abs * -1,
101
+ :response_code => response.authorization,
102
+ :state => 'completed'
103
+ )
103
104
  else
104
105
  gateway_error(response)
105
106
  end
106
107
  end
107
108
  end
108
109
 
109
- def cancel!
110
- if payment_method.respond_to?(:cancel)
111
- payment_method.cancel(response_code)
112
- else
113
- credit!
114
- end
115
- end
116
-
117
110
  def partial_credit(amount)
118
111
  return if amount > credit_allowed
119
112
  started_processing!
@@ -148,7 +141,7 @@ module Spree
148
141
  protect_from_connection_error do
149
142
  check_environment
150
143
 
151
- response = payment_method.send(action, money.money.cents,
144
+ response = payment_method.send(action, (amount * 100).round,
152
145
  source,
153
146
  gateway_options)
154
147
  handle_response(response, success_state, :failure)
@@ -176,7 +169,7 @@ module Spree
176
169
  end
177
170
 
178
171
  def record_response(response)
179
- log_entries.create({:details => response.to_yaml}, :without_protection => true)
172
+ log_entries.create(:details => response.to_yaml)
180
173
  end
181
174
 
182
175
  def protect_from_connection_error
@@ -4,15 +4,16 @@ module Spree
4
4
 
5
5
  IDENTIFIER_CHARS = (('A'..'Z').to_a + ('0'..'9').to_a - %w(0 1 I O)).freeze
6
6
 
7
- belongs_to :order, class_name: 'Spree::Order', touch: true
7
+ belongs_to :order, class_name: 'Spree::Order'
8
8
  belongs_to :source, polymorphic: true
9
9
  belongs_to :payment_method, class_name: 'Spree::PaymentMethod'
10
10
 
11
- has_many :offsets, class_name: "Spree::Payment", foreign_key: :source_id, conditions: "source_type = 'Spree::Payment' AND amount < 0 AND state = 'completed'"
11
+ has_many :offsets, -> { where("source_type = 'Spree::Payment' AND amount < 0 AND state = 'completed'") },
12
+ class_name: "Spree::Payment", foreign_key: :source_id
12
13
  has_many :log_entries, as: :source
13
14
 
14
15
  before_validation :validate_source
15
- before_create :set_unique_identifier
16
+ before_save :set_unique_identifier
16
17
 
17
18
  after_save :create_payment_profile, if: :profiles_supported?
18
19
 
@@ -24,49 +25,45 @@ module Spree
24
25
  attr_accessor :source_attributes
25
26
  after_initialize :build_source
26
27
 
27
- attr_accessible :amount, :payment_method_id, :source_attributes
28
-
29
28
  scope :from_credit_card, -> { where(source_type: 'Spree::CreditCard') }
30
29
  scope :with_state, ->(s) { where(state: s.to_s) }
31
- scope :completed, with_state('completed')
32
- scope :pending, with_state('pending')
33
- scope :failed, with_state('failed')
34
- scope :valid, where("#{quoted_table_name}.state NOT IN (?)", %w(failed invalid))
30
+ scope :completed, -> { with_state('completed') }
31
+ scope :pending, -> { with_state('pending') }
32
+ scope :failed, -> { with_state('failed') }
33
+ scope :valid, -> { where('state NOT IN (?)', %w(failed invalid)) }
35
34
 
36
35
  after_rollback :persist_invalid
37
36
 
38
- validates :amount, numericality: true
39
-
40
37
  def persist_invalid
41
38
  return unless ['failed', 'invalid'].include?(state)
42
39
  state_will_change!
43
- save
40
+ save
44
41
  end
45
42
 
46
43
  # order state machine (see http://github.com/pluginaweek/state_machine/tree/master for details)
47
- state_machine initial: 'checkout' do
44
+ state_machine initial: :checkout do
48
45
  # With card payments, happens before purchase or authorization happens
49
46
  event :started_processing do
50
- transition from: ['checkout', 'pending', 'completed', 'processing'], to: 'processing'
47
+ transition from: [:checkout, :pending, :completed, :processing], to: :processing
51
48
  end
52
49
  # When processing during checkout fails
53
50
  event :failure do
54
- transition from: ['pending', 'processing'], to: 'failed'
51
+ transition from: [:pending, :processing], to: :failed
55
52
  end
56
53
  # With card payments this represents authorizing the payment
57
54
  event :pend do
58
- transition from: ['checkout', 'processing'], to: 'pending'
55
+ transition from: [:checkout, :processing], to: :pending
59
56
  end
60
57
  # With card payments this represents completing a purchase or capture transaction
61
58
  event :complete do
62
- transition from: ['processing', 'pending', 'checkout'], to: 'completed'
59
+ transition from: [:processing, :pending, :checkout], to: :completed
63
60
  end
64
61
  event :void do
65
- transition from: ['pending', 'completed', 'checkout'], to: 'void'
62
+ transition from: [:pending, :completed, :checkout], to: :void
66
63
  end
67
64
  # when the card brand isnt supported
68
65
  event :invalidate do
69
- transition from: ['checkout'], to: 'invalid'
66
+ transition from: [:checkout], to: :invalid
70
67
  end
71
68
  end
72
69
 
@@ -79,22 +76,12 @@ module Spree
79
76
  end
80
77
  alias display_amount money
81
78
 
82
- def amount=(amount)
83
- self[:amount] =
84
- case amount
85
- when String
86
- separator = I18n.t('number.currency.format.separator')
87
- number = amount.delete("^0-9-#{separator}").tr(separator, '.')
88
- number.to_d if number.present?
89
- end || amount
90
- end
91
-
92
79
  def offsets_total
93
80
  offsets.pluck(:amount).sum
94
81
  end
95
82
 
96
83
  def credit_allowed
97
- amount - offsets_total.abs
84
+ amount - offsets_total
98
85
  end
99
86
 
100
87
  def can_credit?
@@ -2,11 +2,10 @@ module Spree
2
2
  class PaymentMethod < ActiveRecord::Base
3
3
  acts_as_paranoid
4
4
  DISPLAY = [:both, :front_end, :back_end]
5
- default_scope where(deleted_at: nil)
5
+ default_scope -> { where(deleted_at: nil) }
6
6
 
7
7
  scope :production, -> { where(environment: 'production') }
8
8
 
9
- attr_accessible :name, :description, :environment, :display_on, :active
10
9
  validates :name, presence: true
11
10
 
12
11
  def self.providers
@@ -41,7 +40,7 @@ module Spree
41
40
  end
42
41
 
43
42
  def self.find_with_destroyed *args
44
- self.with_exclusive_scope { find(*args) }
43
+ unscoped { find(*args) }
45
44
  end
46
45
 
47
46
  def payment_profiles_supported?
@@ -1,5 +1,5 @@
1
1
  class Spree::Preference < ActiveRecord::Base
2
- attr_accessible :key, :value_type, :value
2
+ serialize :value
3
3
 
4
4
  validates :key, presence: true
5
5
  validates :value_type, presence: true
@@ -29,7 +29,7 @@ module Spree::Preferences
29
29
  end
30
30
 
31
31
  def preference_cache_key(name)
32
- [self.class.name, name].join('::').underscore
32
+ [ENV['RAILS_CACHE_ID'], self.class.name, name].flatten.join('::').underscore
33
33
  end
34
34
 
35
35
  def reset
@@ -78,7 +78,7 @@ module Spree::Preferences::Preferable
78
78
 
79
79
  def preference_cache_key(name)
80
80
  return unless id
81
- [self.class.name, name, id].join('::').underscore
81
+ [ENV["RAILS_CACHE_ID"], self.class.name, name, id].join('::').underscore
82
82
  end
83
83
 
84
84
  def save_pending_preferences
@@ -86,7 +86,7 @@ module Spree::Preferences
86
86
  end
87
87
 
88
88
  def should_persist?
89
- @persistence and Spree::Preference.table_exists?
89
+ @persistence && Spree::Preference.connected? && Spree::Preference.table_exists?
90
90
  end
91
91
 
92
92
  end
@@ -5,8 +5,6 @@ module Spree
5
5
  validate :check_price
6
6
  validates :amount, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
7
7
 
8
- attr_accessible :variant_id, :currency, :amount
9
-
10
8
  def display_amount
11
9
  money
12
10
  end
@@ -24,11 +22,6 @@ module Spree
24
22
  self[:amount] = parse_price(price)
25
23
  end
26
24
 
27
- # Remove variant default_scope `deleted_at: nil`
28
- def variant
29
- Spree::Variant.unscoped { super }
30
- end
31
-
32
25
  private
33
26
  def check_price
34
27
  raise "Price must belong to a variant" if variant.nil?
@@ -24,7 +24,7 @@ module Spree
24
24
  next if name.to_s.include?("master_price")
25
25
  parts = name.to_s.match(/(.*)_by_(.*)/)
26
26
  order_text = "#{Product.quoted_table_name}.#{parts[2]} #{parts[1] == 'ascend' ? "ASC" : "DESC"}"
27
- self.scope(name.to_s, order(order_text))
27
+ self.scope(name.to_s, -> { relation.order(order_text) })
28
28
  end
29
29
  end
30
30
 
@@ -68,11 +68,14 @@ module Spree
68
68
  #
69
69
  # SELECT COUNT(*) ...
70
70
  add_search_scope :in_taxon do |taxon|
71
- select("spree_products.id, spree_products.*").
72
- where(id: Classification.select('spree_products_taxons.product_id').
73
- joins(:taxon).
74
- where(Taxon.table_name => { :id => taxon.self_and_descendants.pluck(:id) })
75
- )
71
+ if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
72
+ scope = select("DISTINCT ON (spree_products.id) spree_products.*")
73
+ else
74
+ scope = select("DISTINCT(spree_products.id), spree_products.*")
75
+ end
76
+
77
+ scope.joins(:taxons).
78
+ where(Taxon.table_name => { :id => taxon.self_and_descendants.map(&:id) })
76
79
  end
77
80
 
78
81
  # This scope selects products in all taxons AND all its descendants
@@ -124,20 +127,20 @@ module Spree
124
127
  add_search_scope :with_option_value do |option, value|
125
128
  option_values = OptionValue.table_name
126
129
  option_type_id = case option
127
- when String then OptionType.find_by_name(option) || option.to_i
130
+ when String then OptionType.find_by(name: option) || option.to_i
128
131
  when OptionType then option.id
129
132
  else option.to_i
130
133
  end
131
134
 
132
135
  conditions = "#{option_values}.name = ? AND #{option_values}.option_type_id = ?", value, option_type_id
133
- group("spree_products.id").joins(:variants_including_master => :option_values).where(conditions)
136
+ group('spree_products.id').joins(variants_including_master: :option_values).where(conditions)
134
137
  end
135
138
 
136
139
  # Finds all products which have either:
137
140
  # 1) have an option value with the name matching the one given
138
141
  # 2) have a product property with a value matching the one given
139
142
  add_search_scope :with do |value|
140
- includes(:variants_including_master => :option_values).
143
+ includes(variants_including_master: :option_values).
141
144
  includes(:product_properties).
142
145
  where("#{OptionValue.table_name}.name = ? OR #{ProductProperty.table_name}.value = ?", value, value)
143
146
  end
@@ -160,7 +163,7 @@ module Spree
160
163
  # Finds all products that have the ids matching the given collection of ids.
161
164
  # Alternatively, you could use find(collection_of_ids), but that would raise an exception if one product couldn't be found
162
165
  add_search_scope :with_ids do |*ids|
163
- where(:id => ids)
166
+ where(id: ids)
164
167
  end
165
168
 
166
169
  # Sorts products from most popular (popularity is extracted from how many
@@ -195,11 +198,7 @@ module Spree
195
198
 
196
199
  # Can't use add_search_scope for this as it needs a default argument
197
200
  def self.available(available_on = nil, currency = nil)
198
- scope = joins(:master => :prices).where("#{Product.quoted_table_name}.available_on <= ?", available_on || Time.now)
199
- unless Spree::Config.show_products_without_price
200
- scope = scope.where('spree_prices.currency' => currency || Spree::Config[:currency]).where('spree_prices.amount IS NOT NULL')
201
- end
202
- scope
201
+ joins(:master => :prices).where("#{Product.quoted_table_name}.available_on <= ?", available_on || Time.now)
203
202
  end
204
203
  search_scopes << :available
205
204
 
@@ -249,10 +248,10 @@ module Spree
249
248
  taxons = Taxon.table_name
250
249
  ids_or_records_or_names.flatten.map { |t|
251
250
  case t
252
- when Integer then Taxon.find_by_id(t)
251
+ when Integer then Taxon.find_by(id: t)
253
252
  when ActiveRecord::Base then t
254
253
  when String
255
- Taxon.find_by_name(t) ||
254
+ Taxon.find_by(name: t) ||
256
255
  Taxon.where("#{taxons}.permalink LIKE ? OR #{taxons}.permalink = ?", "%/#{t}/", "#{t}/").first
257
256
  end
258
257
  }.compact.flatten.uniq
@@ -34,22 +34,22 @@ module Spree
34
34
  belongs_to :shipping_category, class_name: 'Spree::ShippingCategory'
35
35
 
36
36
  has_one :master,
37
+ -> { where is_master: true },
37
38
  class_name: 'Spree::Variant',
38
- conditions: { is_master: true },
39
39
  dependent: :destroy
40
40
 
41
41
  has_many :variants,
42
- class_name: 'Spree::Variant',
43
- conditions: { is_master: false, deleted_at: nil },
44
- order: "#{::Spree::Variant.quoted_table_name}.position ASC"
42
+ -> { where(is_master: false).order("#{::Spree::Variant.quoted_table_name}.position ASC") },
43
+ class_name: 'Spree::Variant'
45
44
 
46
45
  has_many :variants_including_master,
46
+ -> { order("#{::Spree::Variant.quoted_table_name}.position ASC") },
47
47
  class_name: 'Spree::Variant',
48
- dependent: :destroy,
49
- order: "#{::Spree::Variant.quoted_table_name}.position ASC"
48
+ dependent: :destroy
49
+
50
+ has_many :prices, -> { order('spree_variants.position, spree_variants.id, currency') }, through: :variants
50
51
 
51
- has_many :prices, through: :variants, order: "#{::Spree::Variant.quoted_table_name}.position, #{::Spree::Variant.quoted_table_name}.id, #{::Spree::Price.quoted_table_name}.currency"
52
- has_many :stock_items, through: :variants_including_master
52
+ has_many :stock_items, through: :variants
53
53
 
54
54
  delegate_belongs_to :master, :sku, :price, :currency, :display_amount, :display_price, :weight, :height, :width, :depth, :is_master, :has_default_price?, :cost_currency, :price_in, :amount_in
55
55
  delegate_belongs_to :master, :cost_price if Variant.table_exists? && Variant.column_names.include?('cost_price')
@@ -62,7 +62,7 @@ module Spree
62
62
  delegate :images, to: :master, prefix: true
63
63
  alias_method :images, :master_images
64
64
 
65
- has_many :variant_images, source: :images, through: :variants_including_master, order: :position
65
+ has_many :variant_images, -> { order(:position) }, source: :images, through: :variants_including_master
66
66
 
67
67
  accepts_nested_attributes_for :variants, allow_destroy: true
68
68
 
@@ -73,31 +73,6 @@ module Spree
73
73
 
74
74
  attr_accessor :option_values_hash
75
75
 
76
- attr_accessible :available_on,
77
- :cost_currency,
78
- :deleted_at,
79
- :depth,
80
- :description,
81
- :height,
82
- :meta_description,
83
- :meta_keywords,
84
- :name,
85
- :option_type_ids,
86
- :option_values_hash,
87
- :permalink,
88
- :price,
89
- :product_properties_attributes,
90
- :prototype_id,
91
- :shipping_category_id,
92
- :sku,
93
- :tax_category_id,
94
- :taxon_ids,
95
- :weight,
96
- :width,
97
- :variants_attributes
98
-
99
- attr_accessible :cost_price if Variant.table_exists? && Variant.column_names.include?('cost_price')
100
-
101
76
  accepts_nested_attributes_for :product_properties, allow_destroy: true, reject_if: lambda { |pp| pp[:property_name].blank? }
102
77
 
103
78
  make_permalink order: :name
@@ -108,11 +83,6 @@ module Spree
108
83
 
109
84
  before_destroy :punch_permalink
110
85
 
111
- def variants_with_only_master
112
- ActiveSupport::Deprecation.warn("[SPREE] Spree::Product#variants_with_only_master will be deprecated in Spree 1.3. Please use Spree::Product#master instead.")
113
- master
114
- end
115
-
116
86
  def to_param
117
87
  permalink.present? ? permalink : (permalink_was || name.to_s.to_url)
118
88
  end
@@ -141,7 +111,7 @@ module Spree
141
111
  return if option_values_hash.nil?
142
112
  option_values_hash.keys.map(&:to_i).each do |id|
143
113
  self.option_type_ids << id unless option_type_ids.include?(id)
144
- product_option_types.create({option_type_id: id}, without_protection: true) unless product_option_types.pluck(:option_type_id).include?(id)
114
+ product_option_types.create(option_type_id: id) unless product_option_types.pluck(:option_type_id).include?(id)
145
115
  end
146
116
  end
147
117
 
@@ -159,11 +129,8 @@ module Spree
159
129
  !!deleted_at
160
130
  end
161
131
 
162
- # determine if product is available.
163
- # deleted products and products with nil or future available_on date
164
- # are not available
165
132
  def available?
166
- !(available_on.nil? || available_on.future?) && !deleted?
133
+ !(available_on.nil? || available_on.future?)
167
134
  end
168
135
 
169
136
  # split variants list into hash which shows mapping of opt value onto matching variants
@@ -199,19 +166,14 @@ module Spree
199
166
  end
200
167
 
201
168
  def property(property_name)
202
- return nil unless prop = properties.find_by_name(property_name)
203
- product_properties.find_by_property_id(prop.id).try(:value)
169
+ return nil unless prop = properties.find_by(name: property_name)
170
+ product_properties.find_by(property: prop).try(:value)
204
171
  end
205
172
 
206
173
  def set_property(property_name, property_value)
207
174
  ActiveRecord::Base.transaction do
208
- # Works around spree_i18n #301
209
- property = if Property.exists?(name: property_name)
210
- Property.where(name: property_name).first
211
- else
212
- Property.create(name: property_name, presentation: property_name)
213
- end
214
- product_property = ProductProperty.where(product_id: self.id, property_id: property.id).first_or_initialize
175
+ property = Property.where(name: property_name).first_or_create!(presentation: property_name)
176
+ product_property = ProductProperty.where(product: self, property: property).first_or_initialize
215
177
  product_property.value = property_value
216
178
  product_property.save!
217
179
  end
@@ -223,10 +185,10 @@ module Spree
223
185
  end
224
186
 
225
187
  def total_on_hand
226
- if self.variants_including_master.any? { |v| !v.should_track_inventory? }
227
- Float::INFINITY
228
- else
188
+ if Spree::Config.track_inventory_levels
229
189
  self.stock_items.sum(&:count_on_hand)
190
+ else
191
+ Float::INFINITY
230
192
  end
231
193
  end
232
194
 
@@ -234,7 +196,7 @@ module Spree
234
196
  # which would make AR's default finder return nil.
235
197
  # This is a stopgap for that little problem.
236
198
  def master
237
- super || variants_including_master.with_deleted.where(:is_master => true).first
199
+ super || variants_including_master.with_deleted.where(is_master: true).first
238
200
  end
239
201
 
240
202
  private
@@ -246,15 +208,18 @@ module Spree
246
208
  values = values.inject(values.shift) { |memo, value| memo.product(value).map(&:flatten) }
247
209
 
248
210
  values.each do |ids|
249
- variant = variants.create({ option_value_ids: ids, price: master.price }, without_protection: true)
211
+ variant = variants.create(
212
+ option_value_ids: ids,
213
+ price: master.price
214
+ )
250
215
  end
251
216
  save
252
217
  end
253
218
 
254
219
  def add_properties_and_option_types_from_prototype
255
- if prototype_id && prototype = Spree::Prototype.find_by_id(prototype_id)
220
+ if prototype_id && prototype = Spree::Prototype.find_by(id: prototype_id)
256
221
  prototype.properties.each do |property|
257
- product_properties.create({property: property}, without_protection: true)
222
+ product_properties.create(property: property)
258
223
  end
259
224
  self.option_types = prototype.option_types
260
225
  end
@@ -268,7 +233,7 @@ module Spree
268
233
  # there's a weird quirk with the delegate stuff that does not automatically save the delegate object
269
234
  # when saving so we force a save using a hook.
270
235
  def save_master
271
- master.save if master && (master.changed? || master.new_record? || (master.default_price && (master.default_price.changed? || master.default_price.new_record?)))
236
+ master.save if master && (master.changed? || master.new_record? || (master.default_price && (master.default_price.changed || master.default_price.new_record)))
272
237
  end
273
238
 
274
239
  def ensure_master
@@ -278,7 +243,7 @@ module Spree
278
243
 
279
244
  def punch_permalink
280
245
  update_attribute :permalink, "#{Time.now.to_i}_#{permalink}" # punch permalink with date prefix
281
- end
246
+ end
282
247
  end
283
248
  end
284
249