spree_core 2.1.3 → 2.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|