Files
openfoodnetwork/app/models/concerns/variant_stock.rb
luisramos0 12eab1bfa9 Merge variant_stock.count_on_hand into variant_stock.on_hand
variant.on_hand will not return infinity any longer if variant.on_demand is true. Clients of these methods will have to handle the combinations between on_hand and on_demand values
2019-02-18 22:00:16 +00:00

166 lines
6.1 KiB
Ruby

require 'active_support/concern'
# These methods were available in Spree 1, but were removed in Spree 2. We
# would still like to use them so that we still give support to the consumers
# of these methods, making the upgrade backward compatible.
#
# Therefore we use only a single stock item per variant, which is associated to
# a single stock location per instance (default stock location) and use it to
# track the `count_on_hand` value that was previously a database column on
# variants. See
# https://github.com/openfoodfoundation/openfoodnetwork/wiki/Spree-Upgrade%3A-Stock-locations
# for details.
#
# These methods are or may become deprecated.
module VariantStock
extend ActiveSupport::Concern
included do
attr_accessible :on_hand, :on_demand
after_update :save_stock
end
# Returns the number of items of the variant available.
# Spree computes total_on_hand as the sum of the count_on_hand of all its stock_items.
#
# @return [Float|Integer]
def on_hand
warn_deprecation(__method__, '#total_on_hand')
total_on_hand
end
# Sets the stock level of the variant.
# This will only work if `track_inventory_levels` config is set
# and if there is a stock item for the variant.
#
# @raise [StandardError] when the track_inventory_levels config key is not set
# and when the variant has no stock item
def on_hand=(new_level)
warn_deprecation(__method__, '#total_on_hand')
error = 'Cannot set on_hand value when Spree::Config[:track_inventory_levels] is false'
raise error unless Spree::Config.track_inventory_levels
raise_error_if_no_stock_item_available
overwrite_stock_levels(new_level)
end
# Checks whether this variant is produced on demand.
#
# In Spree 2.0 this attribute is removed in favour of
# track_inventory_levels only. It was initially introduced in
# https://github.com/openfoodfoundation/spree/commit/20b5ad9835dca7f41a40ad16c7b45f987eea6dcc
def on_demand
warn_deprecation(__method__, 'StockItem#backorderable?')
# A variant that has not been saved yet, doesn't have a stock item
# This provides a default value for variant.on_demand using Spree::StockLocation.backorderable_default
return Spree::StockLocation.first.backorderable_default if stock_items.empty?
stock_item.backorderable?
end
# Sets whether the variant can be ordered on demand or not. Note that
# although this modifies the stock item, it is not persisted in DB. This
# may be done to fire a single UPDATE statement when changing various
# variant attributes, for performance reasons.
#
# @raise [StandardError] when the variant has no stock item yet
def on_demand=(new_value)
warn_deprecation(__method__, 'StockItem#backorderable=')
raise_error_if_no_stock_item_available
# There should be only one at the default stock location.
#
# This would be better off as `stock_items.first.save` but then, for
# unknown reasons, it does not pass the test.
stock_items.each do |item|
item.backorderable = new_value
item.save
end
end
# Moving Spree::Stock::Quantifier.can_supply? to the variant enables us to override this behaviour for variant overrides
# We can have this responsibility here in the variant because there is only one stock item per variant
#
# Here we depend only on variant.total_on_hand and variant.on_demand.
# This way, variant_overrides only need to override variant.total_on_hand and variant.on_demand.
def can_supply?(quantity)
return true unless Spree::Config[:track_inventory_levels]
on_demand || total_on_hand >= quantity
end
# Moving Spree::StockLocation.fill_status to the variant enables us to override this behaviour for variant overrides
# We can have this responsibility here in the variant because there is only one stock item per variant
#
# Here we depend only on variant.total_on_hand and variant.on_demand.
# This way, variant_overrides only need to override variant.total_on_hand and variant.on_demand.
def fill_status(quantity)
if on_hand >= quantity
on_hand = quantity
backordered = 0
else
on_hand = [0, total_on_hand].max
backordered = on_demand ? (quantity - on_hand) : 0
end
[on_hand, backordered]
end
# We can have this responsibility here in the variant because there is only one stock item per variant
#
# This enables us to override this behaviour for variant overrides
def move(quantity, originator = nil)
raise_error_if_no_stock_item_available
# Creates a stock movement: it updates stock_item.count_on_hand and fills backorders
#
# This is the original Spree::StockLocation#move,
# except that we raise an error if the stock item is missing,
# because, unlike Spree, we should always have exactly one stock item per variant.
stock_item.stock_movements.create!(quantity: quantity, originator: originator)
end
private
# Persists the single stock item associated to this variant. As defined in
# the top-most comment, as there's a single stock location in the whole
# database, there can only be a stock item per variant. See StockItem's
# definition at
# https://github.com/openfoodfoundation/spree/blob/43950c3689a77a7f493cc6d805a0edccfe75ebc2/core/app/models/spree/stock_item.rb#L3-L4
# for details.
def save_stock
stock_item.save
end
def raise_error_if_no_stock_item_available
message = 'You need to save the variant to create a stock item before you can set stock levels.'
raise message if stock_items.empty?
end
# Overwrites stock_item.count_on_hand
#
# Calling stock_item.adjust_count_on_hand will bypass filling backorders and creating stock movements
# If that was required we could call self.move
def overwrite_stock_levels(new_level)
stock_item.adjust_count_on_hand(new_level.to_i - stock_item.count_on_hand)
end
# There shouldn't be any other stock items, because we should
# have only one stock location.
def stock_item
stock_items.first
end
def warn_deprecation(method_name, new_method_name)
ActiveSupport::Deprecation.warn(
"`##{method_name}` is deprecated and will be removed. " \
"Please use `#{new_method_name}` instead."
)
end
end