spree_core 2.1.3 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/helpers/spree/admin/images_helper.rb +1 -1
- data/app/helpers/spree/base_helper.rb +5 -2
- data/app/models/spree/address.rb +9 -1
- data/app/models/spree/adjustment.rb +2 -2
- data/app/models/spree/calculator/default_tax.rb +5 -1
- data/app/models/spree/credit_card.rb +2 -0
- data/app/models/spree/gateway.rb +1 -1
- data/app/models/spree/inventory_unit.rb +5 -4
- data/app/models/spree/legacy_user.rb +2 -11
- data/app/models/spree/line_item.rb +2 -3
- data/app/models/spree/log_entry.rb +4 -0
- data/app/models/spree/option_type.rb +6 -0
- data/app/models/spree/option_value.rb +12 -1
- data/app/models/spree/order.rb +34 -16
- data/app/models/spree/order/checkout.rb +4 -0
- data/app/models/spree/order_inventory.rb +1 -1
- data/app/models/spree/payment.rb +10 -2
- data/app/models/spree/payment/processing.rb +5 -4
- data/app/models/spree/payment_method.rb +2 -0
- data/app/models/spree/price.rb +5 -0
- data/app/models/spree/product.rb +6 -5
- data/app/models/spree/product/scopes.rb +12 -6
- data/app/models/spree/product_property.rb +1 -1
- data/app/models/spree/promotion.rb +1 -8
- data/app/models/spree/promotion/rules/user_logged_in.rb +1 -3
- data/app/models/spree/property.rb +8 -0
- data/app/models/spree/shipment.rb +9 -14
- data/app/models/spree/shipping_method.rb +3 -2
- data/app/models/spree/shipping_rate.rb +7 -9
- data/app/models/spree/stock/estimator.rb +21 -14
- data/app/models/spree/stock/package.rb +1 -1
- data/app/models/spree/stock/packer.rb +1 -1
- data/app/models/spree/stock/quantifier.rb +11 -2
- data/app/models/spree/stock_item.rb +2 -2
- data/app/models/spree/stock_location.rb +8 -0
- data/app/models/spree/stock_movement.rb +3 -1
- data/app/models/spree/taxon.rb +2 -2
- data/app/models/spree/variant.rb +19 -4
- data/app/models/spree/zone.rb +1 -1
- data/app/views/spree/shared/_routes.html.erb +1 -1
- data/config/locales/en.yml +15 -1
- data/db/default/spree/countries.rb +7 -7
- data/db/migrate/20130417120034_add_index_to_source_columns_on_adjustments.rb +5 -0
- data/db/migrate/20130802022321_migrate_tax_categories_to_line_items.rb +5 -2
- data/db/migrate/20131026154747_add_track_inventory_to_variant.rb +5 -0
- data/db/migrate/20131120234456_add_updated_at_to_variants.rb +5 -0
- data/db/migrate/20131211192741_unique_shipping_method_categories.rb +24 -0
- data/db/migrate/20140120160805_add_index_to_variant_id_and_currency_on_prices.rb +5 -0
- data/lib/generators/spree/dummy/dummy_generator.rb +14 -3
- data/lib/generators/spree/dummy/templates/rails/database.yml +10 -0
- data/lib/spree/core.rb +3 -0
- data/lib/spree/core/controller_helpers/order.rb +4 -1
- data/lib/spree/core/controller_helpers/ssl.rb +5 -7
- data/lib/spree/core/controller_helpers/strong_parameters.rb +6 -0
- data/lib/spree/core/delegate_belongs_to.rb +16 -10
- data/lib/spree/core/engine.rb +11 -2
- data/lib/spree/core/mail_method.rb +27 -0
- data/lib/spree/core/mail_settings.rb +33 -38
- data/lib/spree/core/permalinks.rb +5 -1
- data/lib/spree/core/s3_support.rb +1 -1
- data/lib/spree/core/user_address.rb +30 -0
- data/lib/spree/core/validators/email.rb +9 -3
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/i18n.rb +1 -0
- data/lib/spree/migrations.rb +55 -0
- data/lib/spree/money.rb +171 -1
- data/lib/spree/permitted_attributes.rb +6 -6
- data/lib/spree/testing_support/capybara_ext.rb +6 -5
- data/lib/spree/testing_support/controller_requests.rb +20 -4
- data/lib/spree/testing_support/factories/product_factory.rb +4 -0
- data/lib/spree/testing_support/factories/variant_factory.rb +15 -0
- metadata +158 -164
@@ -23,7 +23,7 @@ module Spree
|
|
23
23
|
# We should not define price scopes here, as they require something slightly different
|
24
24
|
next if name.to_s.include?("master_price")
|
25
25
|
parts = name.to_s.match(/(.*)_by_(.*)/)
|
26
|
-
self.scope(name.to_s, -> {
|
26
|
+
self.scope(name.to_s, -> { order("#{Product.quoted_table_name}.#{parts[2]} #{parts[1] == 'ascend' ? "ASC" : "DESC"}") })
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -207,13 +207,19 @@ module Spree
|
|
207
207
|
group("spree_products.id").joins(:taxons).where(Taxon.arel_table[:name].eq(name))
|
208
208
|
end
|
209
209
|
|
210
|
-
|
211
|
-
# problem shown in #1247.
|
212
|
-
def self.group_by_products_id
|
210
|
+
def self.distinct_by_product_ids(sort_order=nil)
|
213
211
|
if (ActiveRecord::Base.connection.adapter_name == 'PostgreSQL')
|
214
|
-
|
212
|
+
sort_column = sort_order.split(" ").first
|
213
|
+
# Don't allow sort_column, a variable coming from params,
|
214
|
+
# to be anything but a column in the database
|
215
|
+
if column_names.include?(sort_column)
|
216
|
+
distinct_fields = ["id", sort_column].compact.join(",")
|
217
|
+
select("DISTINCT ON(#{distinct_fields}) spree_products.*")
|
218
|
+
else
|
219
|
+
scoped
|
220
|
+
end
|
215
221
|
else
|
216
|
-
|
222
|
+
select("DISTINCT spree_products.*")
|
217
223
|
end
|
218
224
|
end
|
219
225
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Spree
|
2
2
|
class ProductProperty < ActiveRecord::Base
|
3
|
-
belongs_to :product,
|
3
|
+
belongs_to :product, touch: true, class_name: 'Spree::Product'
|
4
4
|
belongs_to :property, class_name: 'Spree::Property'
|
5
5
|
|
6
6
|
validates :property, presence: true
|
@@ -21,13 +21,6 @@ module Spree
|
|
21
21
|
validates :path, presence: true, if: lambda{|r| r.event_name == 'spree.content.visited' }
|
22
22
|
validates :usage_limit, numericality: { greater_than: 0, allow_nil: true }
|
23
23
|
|
24
|
-
# TODO: This shouldn't be necessary with :autosave option but nested attribute updating of actions is broken without it
|
25
|
-
after_save :save_rules_and_actions
|
26
|
-
|
27
|
-
def save_rules_and_actions
|
28
|
-
(rules + actions).each &:save
|
29
|
-
end
|
30
|
-
|
31
24
|
def self.advertised
|
32
25
|
where(advertise: true)
|
33
26
|
end
|
@@ -91,7 +84,7 @@ module Spree
|
|
91
84
|
end
|
92
85
|
|
93
86
|
def credits
|
94
|
-
Adjustment.promotion.where(originator_id: actions.map(&:id))
|
87
|
+
Adjustment.eligible.promotion.where(originator_id: actions.map(&:id))
|
95
88
|
end
|
96
89
|
|
97
90
|
def credits_count
|
@@ -9,11 +9,19 @@ module Spree
|
|
9
9
|
|
10
10
|
scope :sorted, -> { order(:name) }
|
11
11
|
|
12
|
+
after_touch :touch_all_products
|
13
|
+
|
12
14
|
def self.find_all_by_prototype(prototype)
|
13
15
|
id = prototype
|
14
16
|
id = prototype.id if prototype.class == Prototype
|
15
17
|
joins("LEFT JOIN properties_prototypes ON property_id = #{self.table_name}.id").
|
16
18
|
where(prototype_id: id)
|
17
19
|
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def touch_all_products
|
24
|
+
products.each(&:touch)
|
25
|
+
end
|
18
26
|
end
|
19
27
|
end
|
@@ -2,7 +2,7 @@ require 'ostruct'
|
|
2
2
|
|
3
3
|
module Spree
|
4
4
|
class Shipment < ActiveRecord::Base
|
5
|
-
belongs_to :order, class_name: 'Spree::Order'
|
5
|
+
belongs_to :order, class_name: 'Spree::Order', touch: true
|
6
6
|
belongs_to :address, class_name: 'Spree::Address'
|
7
7
|
belongs_to :stock_location, class_name: 'Spree::StockLocation'
|
8
8
|
|
@@ -12,7 +12,6 @@ module Spree
|
|
12
12
|
has_many :inventory_units, dependent: :delete_all
|
13
13
|
has_one :adjustment, as: :source, dependent: :destroy
|
14
14
|
|
15
|
-
before_create :generate_shipment_number
|
16
15
|
after_save :ensure_correct_adjustment, :update_order
|
17
16
|
|
18
17
|
attr_accessor :special_instructions
|
@@ -20,7 +19,7 @@ module Spree
|
|
20
19
|
accepts_nested_attributes_for :address
|
21
20
|
accepts_nested_attributes_for :inventory_units
|
22
21
|
|
23
|
-
make_permalink field: :number
|
22
|
+
make_permalink field: :number, length: 11, prefix: 'H'
|
24
23
|
|
25
24
|
scope :shipped, -> { with_state('shipped') }
|
26
25
|
scope :ready, -> { with_state('ready') }
|
@@ -164,8 +163,8 @@ module Spree
|
|
164
163
|
end
|
165
164
|
|
166
165
|
def line_items
|
167
|
-
if order.complete? and Spree::Config
|
168
|
-
order.line_items.select { |li| inventory_units.pluck(:variant_id).include?(li.variant_id) }
|
166
|
+
if order.complete? and Spree::Config.track_inventory_levels
|
167
|
+
order.line_items.select { |li| !li.should_track_inventory? || inventory_units.pluck(:variant_id).include?(li.variant_id) }
|
169
168
|
else
|
170
169
|
order.line_items
|
171
170
|
end
|
@@ -238,17 +237,13 @@ module Spree
|
|
238
237
|
end
|
239
238
|
|
240
239
|
def manifest_restock(item)
|
241
|
-
|
242
|
-
|
240
|
+
if item.states["on_hand"].to_i > 0
|
241
|
+
stock_location.restock item.variant, item.states["on_hand"], self
|
242
|
+
end
|
243
243
|
|
244
|
-
|
245
|
-
|
246
|
-
record = true
|
247
|
-
while record
|
248
|
-
random = "H#{Array.new(11) { rand(9) }.join}"
|
249
|
-
record = self.class.where(number: random).first
|
244
|
+
if item.states["backordered"].to_i > 0
|
245
|
+
stock_location.restock_backordered item.variant, item.states["backordered"]
|
250
246
|
end
|
251
|
-
self.number = random
|
252
247
|
end
|
253
248
|
|
254
249
|
def description_for_shipping_charge
|
@@ -8,7 +8,7 @@ module Spree
|
|
8
8
|
has_many :shipments
|
9
9
|
has_many :shipping_method_categories
|
10
10
|
has_many :shipping_categories, through: :shipping_method_categories
|
11
|
-
has_many :shipping_rates
|
11
|
+
has_many :shipping_rates, inverse_of: :shipping_method
|
12
12
|
|
13
13
|
has_and_belongs_to_many :zones, :join_table => 'spree_shipping_methods_zones',
|
14
14
|
:class_name => 'Spree::Zone',
|
@@ -30,7 +30,8 @@ module Spree
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def build_tracking_url(tracking)
|
33
|
-
|
33
|
+
return if tracking.blank? || tracking_url.blank?
|
34
|
+
tracking_url.gsub(/:tracking/, ERB::Util.url_encode(tracking)) # :url_encode exists in 1.8.7 through 2.1.0
|
34
35
|
end
|
35
36
|
|
36
37
|
def self.calculators
|
@@ -1,19 +1,13 @@
|
|
1
1
|
module Spree
|
2
2
|
class ShippingRate < ActiveRecord::Base
|
3
3
|
belongs_to :shipment, class_name: 'Spree::Shipment'
|
4
|
-
belongs_to :shipping_method, class_name: 'Spree::ShippingMethod'
|
4
|
+
belongs_to :shipping_method, class_name: 'Spree::ShippingMethod', inverse_of: :shipping_rates
|
5
5
|
|
6
|
-
scope :
|
6
|
+
scope :with_shipping_method,
|
7
7
|
-> { includes(:shipping_method).
|
8
|
-
where(ShippingMethod.on_frontend_query).
|
9
|
-
references(:shipping_method).
|
10
|
-
order("cost ASC") }
|
11
|
-
scope :backend,
|
12
|
-
-> { includes(:shipping_method).
|
13
|
-
where(ShippingMethod.on_backend_query).
|
14
8
|
references(:shipping_method).
|
15
9
|
order("cost ASC") }
|
16
|
-
|
10
|
+
|
17
11
|
delegate :order, :currency, to: :shipment
|
18
12
|
delegate :name, to: :shipping_method
|
19
13
|
|
@@ -28,5 +22,9 @@ module Spree
|
|
28
22
|
end
|
29
23
|
|
30
24
|
alias_method :display_cost, :display_price
|
25
|
+
|
26
|
+
def shipping_method
|
27
|
+
Spree::ShippingMethod.unscoped { super }
|
28
|
+
end
|
31
29
|
end
|
32
30
|
end
|
@@ -9,20 +9,17 @@ module Spree
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def shipping_rates(package, frontend_only = true)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
available_rates.select! { |rate| rate.shipping_method.frontend? } if frontend_only
|
17
|
-
choose_default_shipping_rate(available_rates)
|
18
|
-
end
|
19
|
-
|
20
|
-
sort_shipping_rates(shipping_rates)
|
12
|
+
rates = calculate_shipping_rates(package)
|
13
|
+
rates.select! { |rate| rate.shipping_method.frontend? } if frontend_only
|
14
|
+
choose_default_shipping_rate(rates)
|
15
|
+
sort_shipping_rates(rates)
|
21
16
|
end
|
22
17
|
|
23
18
|
private
|
24
19
|
def choose_default_shipping_rate(shipping_rates)
|
25
|
-
shipping_rates.
|
20
|
+
unless shipping_rates.empty?
|
21
|
+
shipping_rates.min_by(&:cost).selected = true
|
22
|
+
end
|
26
23
|
end
|
27
24
|
|
28
25
|
def sort_shipping_rates(shipping_rates)
|
@@ -39,12 +36,22 @@ module Spree
|
|
39
36
|
def shipping_methods(package)
|
40
37
|
package.shipping_methods.select do |ship_method|
|
41
38
|
calculator = ship_method.calculator
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
39
|
+
begin
|
40
|
+
calculator.available?(package) &&
|
41
|
+
ship_method.include?(order.ship_address) &&
|
42
|
+
(calculator.preferences[:currency].nil? ||
|
43
|
+
calculator.preferences[:currency] == currency)
|
44
|
+
rescue Exception => exception
|
45
|
+
log_calculator_exception(ship_method, exception)
|
46
|
+
end
|
46
47
|
end
|
47
48
|
end
|
49
|
+
|
50
|
+
def log_calculator_exception(ship_method, exception)
|
51
|
+
Rails.logger.info("Something went wrong calculating rates with the #{ship_method.name} (ID=#{ship_method.id}) shipping method.")
|
52
|
+
Rails.logger.info("*" * 50)
|
53
|
+
Rails.logger.info(exception.backtrace.join("\n"))
|
54
|
+
end
|
48
55
|
end
|
49
56
|
end
|
50
57
|
end
|
@@ -20,7 +20,7 @@ module Spree
|
|
20
20
|
def default_package
|
21
21
|
package = Package.new(stock_location, order)
|
22
22
|
order.line_items.each do |line_item|
|
23
|
-
if
|
23
|
+
if line_item.should_track_inventory?
|
24
24
|
next unless stock_location.stock_item(line_item.variant)
|
25
25
|
|
26
26
|
on_hand, backordered = stock_location.fill_status(line_item.variant, line_item.quantity)
|
@@ -4,12 +4,12 @@ module Spree
|
|
4
4
|
attr_reader :stock_items
|
5
5
|
|
6
6
|
def initialize(variant)
|
7
|
-
@variant = variant
|
7
|
+
@variant = resolve_variant_id(variant)
|
8
8
|
@stock_items = Spree::StockItem.joins(:stock_location).where(:variant_id => @variant, Spree::StockLocation.table_name =>{ :active => true})
|
9
9
|
end
|
10
10
|
|
11
11
|
def total_on_hand
|
12
|
-
if
|
12
|
+
if @variant.should_track_inventory?
|
13
13
|
stock_items.sum(:count_on_hand)
|
14
14
|
else
|
15
15
|
Float::INFINITY
|
@@ -23,6 +23,15 @@ module Spree
|
|
23
23
|
def can_supply?(required)
|
24
24
|
total_on_hand >= required || backorderable?
|
25
25
|
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# return variant when passed either variant object or variant id
|
30
|
+
def resolve_variant_id(variant)
|
31
|
+
variant = Spree::Variant.find_by_id(variant) unless variant.respond_to?(:should_track_inventory?)
|
32
|
+
variant
|
33
|
+
end
|
34
|
+
|
26
35
|
end
|
27
36
|
end
|
28
37
|
end
|
@@ -3,13 +3,13 @@ module Spree
|
|
3
3
|
acts_as_paranoid
|
4
4
|
|
5
5
|
belongs_to :stock_location, class_name: 'Spree::StockLocation'
|
6
|
-
belongs_to :variant, class_name: 'Spree::Variant'
|
6
|
+
belongs_to :variant, class_name: 'Spree::Variant', touch: true
|
7
7
|
has_many :stock_movements
|
8
8
|
|
9
9
|
validates_presence_of :stock_location, :variant
|
10
10
|
validates_uniqueness_of :variant_id, scope: [:stock_location_id, :deleted_at]
|
11
11
|
|
12
|
-
delegate :weight, to: :variant
|
12
|
+
delegate :weight, :should_track_inventory?, to: :variant
|
13
13
|
|
14
14
|
def backordered_inventory_units
|
15
15
|
Spree::InventoryUnit.backordered_for_stock_item(self)
|
@@ -44,6 +44,14 @@ module Spree
|
|
44
44
|
move(variant, quantity, originator)
|
45
45
|
end
|
46
46
|
|
47
|
+
def restock_backordered(variant, quantity, originator = nil)
|
48
|
+
item = stock_item_or_create(variant)
|
49
|
+
item.update_columns(
|
50
|
+
count_on_hand: item.count_on_hand + quantity,
|
51
|
+
updated_at: Time.now
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
47
55
|
def unstock(variant, quantity, originator = nil)
|
48
56
|
move(variant, -quantity, originator)
|
49
57
|
end
|
@@ -16,10 +16,12 @@ module Spree
|
|
16
16
|
end
|
17
17
|
|
18
18
|
private
|
19
|
+
|
19
20
|
def update_stock_item_quantity
|
20
|
-
return unless
|
21
|
+
return unless self.stock_item.should_track_inventory?
|
21
22
|
stock_item.adjust_count_on_hand quantity
|
22
23
|
end
|
24
|
+
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
data/app/models/spree/taxon.rb
CHANGED
@@ -16,7 +16,7 @@ module Spree
|
|
16
16
|
url: '/spree/taxons/:id/:style/:basename.:extension',
|
17
17
|
path: ':rails_root/public/spree/taxons/:id/:style/:basename.:extension',
|
18
18
|
default_url: '/assets/default_taxon.png'
|
19
|
-
|
19
|
+
|
20
20
|
include Spree::Core::S3Support
|
21
21
|
supports_s3 :icon
|
22
22
|
|
@@ -76,7 +76,7 @@ module Spree
|
|
76
76
|
#
|
77
77
|
# See #3390 for background.
|
78
78
|
def child_index=(idx)
|
79
|
-
move_to_child_with_index(parent, idx.to_i)
|
79
|
+
move_to_child_with_index(parent, idx.to_i) unless self.new_record?
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
data/app/models/spree/variant.rb
CHANGED
@@ -23,7 +23,7 @@ module Spree
|
|
23
23
|
class_name: 'Spree::Price',
|
24
24
|
dependent: :destroy
|
25
25
|
|
26
|
-
delegate_belongs_to :default_price, :display_price, :display_amount, :price, :price=, :currency
|
26
|
+
delegate_belongs_to :default_price, :display_price, :display_amount, :price, :price=, :currency
|
27
27
|
|
28
28
|
has_many :prices,
|
29
29
|
class_name: 'Spree::Price',
|
@@ -40,7 +40,7 @@ module Spree
|
|
40
40
|
after_create :set_position
|
41
41
|
|
42
42
|
# default variant scope only lists non-deleted variants
|
43
|
-
scope :deleted, lambda { where(
|
43
|
+
scope :deleted, lambda { where.not(deleted_at: nil) }
|
44
44
|
|
45
45
|
def self.active(currency = nil)
|
46
46
|
joins(:prices).where(deleted_at: nil).where('spree_prices.currency' => currency || Spree::Config[:currency]).where('spree_prices.amount IS NOT NULL')
|
@@ -76,6 +76,12 @@ module Spree
|
|
76
76
|
deleted_at
|
77
77
|
end
|
78
78
|
|
79
|
+
def options=(options = {})
|
80
|
+
options.each do |option|
|
81
|
+
set_option_value(option[:name], option[:value])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
79
85
|
def set_option_value(opt_name, opt_value)
|
80
86
|
# no option values on master
|
81
87
|
return if self.is_master
|
@@ -94,7 +100,6 @@ module Spree
|
|
94
100
|
# then we have to check to make sure that the product has the option type
|
95
101
|
unless self.product.option_types.include? option_type
|
96
102
|
self.product.option_types << option_type
|
97
|
-
self.product.save
|
98
103
|
end
|
99
104
|
end
|
100
105
|
|
@@ -127,13 +132,17 @@ module Spree
|
|
127
132
|
"#{name} - #{sku}"
|
128
133
|
end
|
129
134
|
|
135
|
+
def sku_and_options_text
|
136
|
+
"#{sku} #{options_text}".strip
|
137
|
+
end
|
138
|
+
|
130
139
|
# Product may be created with deleted_at already set,
|
131
140
|
# which would make AR's default finder return nil.
|
132
141
|
# This is a stopgap for that little problem.
|
133
142
|
def product
|
134
143
|
Spree::Product.unscoped { super }
|
135
144
|
end
|
136
|
-
|
145
|
+
|
137
146
|
def in_stock?(quantity=1)
|
138
147
|
Spree::Stock::Quantifier.new(self).can_supply?(quantity)
|
139
148
|
end
|
@@ -142,6 +151,12 @@ module Spree
|
|
142
151
|
Spree::Stock::Quantifier.new(self).total_on_hand
|
143
152
|
end
|
144
153
|
|
154
|
+
# Shortcut method to determine if inventory tracking is enabled for this variant
|
155
|
+
# This considers both variant tracking flag and site-wide inventory tracking settings
|
156
|
+
def should_track_inventory?
|
157
|
+
self.track_inventory? && Spree::Config.track_inventory_levels
|
158
|
+
end
|
159
|
+
|
145
160
|
private
|
146
161
|
# strips all non-price-like characters from the price, taking into account locale settings
|
147
162
|
def parse_price(price)
|