mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-08 03:10:20 +00:00
We have only one stock location and that has the default set to false. Now we can simplify code. The mentioned Bugsnag notification has not been found. The stock item is always present in this case but it doesn't hurt to guard against it with `&.`.
149 lines
5.1 KiB
Ruby
149 lines
5.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
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
|
|
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
|
|
total_on_hand
|
|
end
|
|
|
|
# Sets the stock level of the variant.
|
|
# This will only work if there is a stock item for the variant.
|
|
#
|
|
# @raise [StandardError] when the variant has no stock item
|
|
def on_hand=(new_level)
|
|
raise_error_if_no_stock_item_available
|
|
|
|
overwrite_stock_levels(new_level)
|
|
end
|
|
|
|
# Checks whether this variant is produced on demand.
|
|
def on_demand
|
|
# A variant that has not been saved yet or has been soft-deleted doesn't have a stock item
|
|
# This provides a default value for variant.on_demand
|
|
return false if new_record? || deleted?
|
|
|
|
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)
|
|
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)
|
|
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)
|
|
on_hand = if total_on_hand.to_i >= quantity || on_demand
|
|
quantity
|
|
else
|
|
[0, total_on_hand].max
|
|
end
|
|
|
|
backordered = 0
|
|
|
|
[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)
|
|
return if deleted_at
|
|
|
|
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:, 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
|
|
end
|