Find wholesale offer for retail variant

This commit is contained in:
Maikel Linke
2024-09-10 17:09:12 +10:00
parent 14c32c0d2e
commit efe2b724e6
5 changed files with 43 additions and 16 deletions

View File

@@ -36,23 +36,34 @@ class BackorderJob < ApplicationJob
orderer = FdcBackorderer.new(user)
backorder = orderer.find_or_build_order(order)
broker = load_broker(order.distributor.owner)
ordered_quantities = {}
linked_variants.each do |variant|
needed_quantity = -1 * variant.on_hand
offer = broker.best_offer(variant.semantic_links[0].semantic_id)
solution = broker.best_offer(variant.semantic_links[0].semantic_id)
line = orderer.find_or_build_order_line(backorder, offer)
line.quantity = line.quantity.to_i + needed_quantity
# The number of wholesale packs we need to order to fulfill the
# needed quantity.
# For example, we order 2 packs of 12 cans if we need 15 cans.
wholesale_quantity = (needed_quantity.to_f / solution.factor).ceil
# The number of individual retail items we get with the wholesale order.
# For example, if we order 2 packs of 12 cans, we will get 24 cans
# and we'll account for that in our stock levels.
retail_quantity = wholesale_quantity * solution.factor
line = orderer.find_or_build_order_line(backorder, solution.offer)
line.quantity = line.quantity.to_i + wholesale_quantity
ordered_quantities[variant] = retail_quantity
end
placed_order = orderer.send_order(backorder)
schedule_order_completion(user, order, placed_order) if orderer.new?(backorder)
# Once we have transformations and know the quantities in bulk products
# we will need to increase on_hand by the ordered quantity.
linked_variants.each do |variant|
variant.on_hand = 0
variant.on_hand += ordered_quantities[variant]
end
end

View File

@@ -2,13 +2,23 @@
# Finds wholesale offers for retail products.
class FdcOfferBroker
Solution = Struct.new(:product, :factor, :offer)
def initialize(catalog)
@catalog = catalog
end
def best_offer(product_id)
product = @catalog.find { |item| item.semanticId == product_id }
offer_of(product)
consumption_flow = catalog_item("#{product_id}/AsPlannedConsumptionFlow")
production_flow = catalog_item("#{product_id}/AsPlannedProductionFlow")
contained_quantity = consumption_flow.quantity.value.to_i
wholesale_product_id = production_flow.product
wholesale_product = catalog_item(wholesale_product_id )
offer = offer_of(wholesale_product)
Solution.new(wholesale_product, contained_quantity, offer)
end
def offer_of(product)
@@ -17,4 +27,9 @@ class FdcOfferBroker
offer.offeredItem = product
end
end
def catalog_item(id)
@catalog_by_id ||= @catalog.index_by(&:semanticId)
@catalog_by_id[id]
end
end

View File

@@ -35,7 +35,8 @@ RSpec.describe BackorderJob do
BackorderJob.check_stock(order)
}.to enqueue_job CompleteBackorderJob
expect(variant.on_hand).to eq 0
# We ordered a case of 12 cans: -3 + 12 = 9
expect(variant.on_hand).to eq 9
# Clean up after ourselves:
perform_enqueued_jobs(only: CompleteBackorderJob)

View File

@@ -11,14 +11,14 @@ RSpec.describe FdcOfferBroker do
}
describe ".best_offer" do
it "finds a linked offer", vcr: true do
offer = subject.best_offer(product.semanticId)
it "finds a linked wholesale offer", vcr: true do
solution = subject.best_offer(product.semanticId)
# This is the URL structure on the FDC API:
expect(offer.semanticId).to eq "#{product.semanticId}/Offer"
# Well, if you ask the orders endpoint, you actually get different ids
# for the same offers...
# These values depend on the test data but are a good sanity check:
expect(product.name).to eq "Baked British Beans - Retail can, 400g (can)"
expect(solution.product.name).to eq "Baked British Beans - Case, 12 x 400g (can)"
expect(solution.factor).to eq 12
expect(solution.offer.offeredItem).to eq solution.product
end
end
end