mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-27 01:43:22 +00:00
Sync stock with DFC catalog after cart update
This commit is contained in:
@@ -11,6 +11,8 @@ class CartController < BaseController
|
||||
order.cap_quantity_at_stock!
|
||||
order.recreate_all_fees!
|
||||
|
||||
StockSyncJob.sync_linked_catalogs(order)
|
||||
|
||||
render json: { error: false, stock_levels: stock_levels(order) }, status: :ok
|
||||
else
|
||||
render json: { error: cart_service.errors.full_messages.join(",") },
|
||||
|
||||
52
app/jobs/stock_sync_job.rb
Normal file
52
app/jobs/stock_sync_job.rb
Normal file
@@ -0,0 +1,52 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class StockSyncJob < ApplicationJob
|
||||
# No retry but stay as failed job:
|
||||
sidekiq_options retry: 0
|
||||
|
||||
# We synchronise stock of stock-controlled variants linked to a remote
|
||||
# product. These variants are rare though and we check first before we
|
||||
# enqueue a new job. That should save some time loading the order with
|
||||
# all the stock data to make this decision.
|
||||
def self.sync_linked_catalogs(order)
|
||||
stock_controlled_variants = order.variants.reject(&:on_demand)
|
||||
links = SemanticLink.where(variant_id: stock_controlled_variants.map(&:id))
|
||||
semantic_ids = links.pluck(:semantic_id)
|
||||
|
||||
return if semantic_ids.empty?
|
||||
|
||||
user = order.distributor.owner
|
||||
reference_id = semantic_ids.first # Assuming one catalog for now.
|
||||
perform_later(user, reference_id)
|
||||
rescue StandardError => e
|
||||
# Errors here shouldn't affect the shopping. So let's report them
|
||||
# separately:
|
||||
Bugsnag.notify(e) do |payload|
|
||||
payload.add_metadata(:order, order)
|
||||
end
|
||||
end
|
||||
|
||||
def perform(user, semantic_id)
|
||||
urls = FdcUrlBuilder.new(semantic_id)
|
||||
json_catalog = DfcRequest.new(user).call(urls.catalog_url)
|
||||
graph = DfcIo.import(json_catalog)
|
||||
|
||||
products = graph.select do |subject|
|
||||
subject.is_a? DataFoodConsortium::Connector::SuppliedProduct
|
||||
end
|
||||
products_by_id = products.index_by(&:semanticId)
|
||||
product_ids = products_by_id.keys
|
||||
variants = Spree::Variant.where(supplier: user.enterprises)
|
||||
.includes(:semantic_links).references(:semantic_links)
|
||||
.where(semantic_links: { semantic_id: product_ids })
|
||||
|
||||
variants.each do |variant|
|
||||
next if variant.on_demand
|
||||
|
||||
product = products_by_id[variant.semantic_links[0].semantic_id]
|
||||
catalog_item = product&.catalogItems&.first
|
||||
CatalogItemBuilder.apply_stock(catalog_item, variant)
|
||||
variant.stock_items[0].save!
|
||||
end
|
||||
end
|
||||
end
|
||||
53
spec/jobs/stock_sync_job_spec.rb
Normal file
53
spec/jobs/stock_sync_job_spec.rb
Normal file
@@ -0,0 +1,53 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe StockSyncJob do
|
||||
let(:order) { create(:order_with_totals, distributor:) }
|
||||
let(:distributor) { build(:enterprise, owner: user) }
|
||||
let(:user) { build(:testdfc_user) }
|
||||
let(:beans) { order.variants.first }
|
||||
let(:beans_retail_link) {
|
||||
"https://env-0105831.jcloud-ver-jpe.ik-server.com/api/dfc/Enterprises/test-hodmedod/SuppliedProducts/44519466467635"
|
||||
}
|
||||
|
||||
describe ".sync_linked_catalogs" do
|
||||
subject { StockSyncJob.sync_linked_catalogs(order) }
|
||||
it "ignores products without semantic link" do
|
||||
expect { subject }.not_to enqueue_job(StockSyncJob)
|
||||
end
|
||||
|
||||
it "enqueues backorder" do
|
||||
beans.semantic_links << SemanticLink.new(
|
||||
semantic_id: beans_retail_link
|
||||
)
|
||||
|
||||
expect { subject }.to enqueue_job(StockSyncJob)
|
||||
.with(user, beans_retail_link)
|
||||
end
|
||||
|
||||
it "reports errors" do
|
||||
expect(order).to receive(:variants).and_raise("test error")
|
||||
expect(Bugsnag).to receive(:notify).and_call_original
|
||||
|
||||
expect { subject }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
describe "#peform" do
|
||||
subject { StockSyncJob.perform_now(user, beans_retail_link) }
|
||||
|
||||
before do
|
||||
distributor.save!
|
||||
user.enterprises << distributor
|
||||
beans.update!(supplier: distributor)
|
||||
beans.semantic_links << SemanticLink.new(semantic_id: beans_retail_link)
|
||||
end
|
||||
|
||||
it "updates stock" do
|
||||
expect { VCR.use_cassette(:fdc_catalog) { subject } }.to change {
|
||||
beans.on_demand
|
||||
}.from(false).to(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user