mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Re-use BackorderUpdater to complete backorder
This commit is contained in:
@@ -24,8 +24,7 @@ class CompleteBackorderJob < ApplicationJob
|
||||
|
||||
urls = FdcUrlBuilder.new(order.lines[0].offer.offeredItem.semanticId)
|
||||
|
||||
variants = order_cycle.variants_distributed_by(distributor)
|
||||
adjust_quantities(order_cycle, user, order, urls, variants)
|
||||
BackorderUpdater.new.update(order, user, distributor, order_cycle)
|
||||
|
||||
FdcBackorderer.new(user, urls).complete_order(order)
|
||||
|
||||
@@ -36,55 +35,4 @@ class CompleteBackorderJob < ApplicationJob
|
||||
|
||||
raise
|
||||
end
|
||||
|
||||
# Check if we have enough stock to reduce the backorder.
|
||||
#
|
||||
# Our local stock can increase when users cancel their orders.
|
||||
# But stock levels could also have been adjusted manually. So we review all
|
||||
# quantities before finalising the order.
|
||||
def adjust_quantities(order_cycle, user, order, urls, variants)
|
||||
broker = FdcOfferBroker.new(user, urls)
|
||||
|
||||
order.lines.each do |line|
|
||||
line.quantity = line.quantity.to_i
|
||||
wholesale_product_id = line.offer.offeredItem.semanticId
|
||||
transformation = broker.wholesale_to_retail(wholesale_product_id)
|
||||
linked_variant = variants.linked_to(transformation.retail_product_id)
|
||||
|
||||
# Assumption: If a transformation is present then we only sell the retail
|
||||
# variant. If that can't be found, it was deleted and we'll ignore that
|
||||
# for now.
|
||||
next if linked_variant.nil?
|
||||
|
||||
# Find all line items for this order cycle
|
||||
# Update quantity accordingly
|
||||
if linked_variant.on_demand
|
||||
release_superfluous_stock(line, linked_variant, transformation)
|
||||
else
|
||||
aggregate_final_quantities(order_cycle, line, linked_variant, transformation)
|
||||
end
|
||||
end
|
||||
|
||||
# Clean up empty lines:
|
||||
order.lines.reject! { |line| line.quantity.zero? }
|
||||
end
|
||||
|
||||
def release_superfluous_stock(line, linked_variant, transformation)
|
||||
# Note that a division of integers dismisses the remainder, like `floor`:
|
||||
wholesale_items_contained_in_stock = linked_variant.on_hand / transformation.factor
|
||||
|
||||
# But maybe we didn't actually order that much:
|
||||
deductable_quantity = [line.quantity, wholesale_items_contained_in_stock].min
|
||||
line.quantity -= deductable_quantity
|
||||
|
||||
retail_stock_changes = deductable_quantity * transformation.factor
|
||||
linked_variant.on_hand -= retail_stock_changes
|
||||
end
|
||||
|
||||
def aggregate_final_quantities(order_cycle, line, variant, transformation)
|
||||
orders = order_cycle.orders.invoiceable
|
||||
quantity = Spree::LineItem.where(order: orders, variant:).sum(:quantity)
|
||||
wholesale_quantity = (quantity.to_f / transformation.factor).ceil
|
||||
line.quantity = wholesale_quantity
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,7 +8,9 @@ class BackorderUpdater
|
||||
# Given an OFN order was created, changed or cancelled,
|
||||
# we re-calculate how much to order in for every variant.
|
||||
def amend_backorder(order)
|
||||
variants = distributed_linked_variants(order)
|
||||
order_cycle = order.order_cycle
|
||||
distributor = order.distributor
|
||||
variants = distributed_linked_variants(order_cycle, distributor)
|
||||
|
||||
# Temporary code: once we don't need a variant link to look up the
|
||||
# backorder, we don't need this check anymore.
|
||||
@@ -17,8 +19,21 @@ class BackorderUpdater
|
||||
# cycle before and got ordered before being removed from the order cycle.
|
||||
return unless variants.any?
|
||||
|
||||
# We are assuming that all variants are linked to the same wholesale
|
||||
# shop and its catalog:
|
||||
reference_link = variants[0].semantic_links[0].semantic_id
|
||||
user = order.distributor.owner
|
||||
order_cycle = order.order_cycle
|
||||
urls = FdcUrlBuilder.new(reference_link)
|
||||
orderer = FdcBackorderer.new(user, urls)
|
||||
|
||||
backorder = orderer.find_open_order(order)
|
||||
|
||||
update(backorder, user, distributor, order_cycle)
|
||||
end
|
||||
|
||||
# Update a given backorder according to a distributor's order cycle.
|
||||
def update(backorder, user, distributor, order_cycle)
|
||||
variants = distributed_linked_variants(order_cycle, distributor)
|
||||
|
||||
# We are assuming that all variants are linked to the same wholesale
|
||||
# shop and its catalog:
|
||||
@@ -27,11 +42,10 @@ class BackorderUpdater
|
||||
orderer = FdcBackorderer.new(user, urls)
|
||||
broker = FdcOfferBroker.new(user, urls)
|
||||
|
||||
backorder = orderer.find_open_order(order)
|
||||
|
||||
updated_lines = update_order_lines(backorder, order_cycle, variants, broker, orderer)
|
||||
unprocessed_lines = backorder.lines.to_set - updated_lines
|
||||
cancel_stale_lines(unprocessed_lines, order, broker)
|
||||
managed_variants = managed_linked_variants(user, order_cycle, distributor)
|
||||
cancel_stale_lines(unprocessed_lines, managed_variants, broker)
|
||||
|
||||
# Clean up empty lines:
|
||||
backorder.lines.reject! { |line| line.quantity.zero? }
|
||||
@@ -54,8 +68,7 @@ class BackorderUpdater
|
||||
end
|
||||
end
|
||||
|
||||
def cancel_stale_lines(unprocessed_lines, order, broker)
|
||||
managed_variants = managed_linked_variants(order)
|
||||
def cancel_stale_lines(unprocessed_lines, managed_variants, broker)
|
||||
unprocessed_lines.each do |line|
|
||||
wholesale_quantity = line.quantity.to_i
|
||||
wholesale_product_id = line.offer.offeredItem.semanticId
|
||||
@@ -79,6 +92,8 @@ class BackorderUpdater
|
||||
end
|
||||
|
||||
def adjust_stock(variant, solution, line)
|
||||
line.quantity = line.quantity.to_i
|
||||
|
||||
if variant.on_hand.negative?
|
||||
needed_quantity = -1 * variant.on_hand # We need to replenish it.
|
||||
|
||||
@@ -92,7 +107,7 @@ class BackorderUpdater
|
||||
# and we'll account for that in our stock levels.
|
||||
retail_quantity = wholesale_quantity * solution.factor
|
||||
|
||||
line.quantity = line.quantity.to_i + wholesale_quantity
|
||||
line.quantity += wholesale_quantity
|
||||
variant.on_hand += retail_quantity
|
||||
else
|
||||
# Note that a division of integers dismisses the remainder, like `floor`:
|
||||
@@ -110,18 +125,15 @@ class BackorderUpdater
|
||||
end
|
||||
end
|
||||
|
||||
def managed_linked_variants(order)
|
||||
user = order.distributor.owner
|
||||
order_cycle = order.order_cycle
|
||||
|
||||
def managed_linked_variants(user, order_cycle, distributor)
|
||||
# These permissions may be too complex. Here may be scope to optimise.
|
||||
permissions = OpenFoodNetwork::OrderCyclePermissions.new(user, order_cycle)
|
||||
permissions.visible_variants_for_outgoing_exchanges_to(order.distributor)
|
||||
permissions.visible_variants_for_outgoing_exchanges_to(distributor)
|
||||
.where.associated(:semantic_links)
|
||||
end
|
||||
|
||||
def distributed_linked_variants(order)
|
||||
order.order_cycle.variants_distributed_by(order.distributor)
|
||||
def distributed_linked_variants(order_cycle, distributor)
|
||||
order_cycle.variants_distributed_by(distributor)
|
||||
.where.associated(:semantic_links)
|
||||
end
|
||||
|
||||
|
||||
@@ -110,8 +110,10 @@ RSpec.describe BackorderUpdater do
|
||||
end
|
||||
|
||||
describe "#distributed_linked_variants" do
|
||||
let(:order_cycle) { order.order_cycle }
|
||||
|
||||
it "selects available variants with semantic links" do
|
||||
variants = subject.distributed_linked_variants(order)
|
||||
variants = subject.distributed_linked_variants(order_cycle, distributor)
|
||||
expect(variants).to match_array [beans, chia_seed]
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user