mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Merge pull request #5361 from Matt-Yorkley/cart-with-soft-deletion
Cart with soft-deletion
This commit is contained in:
@@ -11,6 +11,10 @@ module Spree
|
||||
end
|
||||
|
||||
def total_on_hand
|
||||
# Associated stock_items no longer exist if the variant has been soft-deleted. A variant
|
||||
# may still be in an active cart after it's deleted, so this will mark it as out of stock.
|
||||
return 0 if @variant.deleted?
|
||||
|
||||
stock_items.sum(&:count_on_hand)
|
||||
end
|
||||
|
||||
|
||||
@@ -29,11 +29,15 @@ class CartService
|
||||
|
||||
variants_data.each do |variant_data|
|
||||
loaded_variant = loaded_variants[variant_data[:variant_id].to_i]
|
||||
|
||||
if loaded_variant.deleted?
|
||||
remove_deleted_variant(loaded_variant)
|
||||
next
|
||||
end
|
||||
|
||||
next unless varies_from_cart(variant_data, loaded_variant)
|
||||
|
||||
attempt_cart_add(
|
||||
loaded_variant, variant_data[:quantity], variant_data[:max_quantity]
|
||||
)
|
||||
attempt_cart_add(loaded_variant, variant_data[:quantity], variant_data[:max_quantity])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -41,12 +45,16 @@ class CartService
|
||||
@indexed_variants ||= begin
|
||||
variant_ids_in_data = variants_data.map{ |v| v[:variant_id] }
|
||||
|
||||
Spree::Variant.where(id: variant_ids_in_data).
|
||||
Spree::Variant.with_deleted.where(id: variant_ids_in_data).
|
||||
includes(:default_price, :stock_items, :product).
|
||||
index_by(&:id)
|
||||
end
|
||||
end
|
||||
|
||||
def remove_deleted_variant(variant)
|
||||
line_item_for_variant(variant).andand.destroy
|
||||
end
|
||||
|
||||
def attempt_cart_add(variant, quantity, max_quantity = nil)
|
||||
quantity = quantity.to_i
|
||||
max_quantity = max_quantity.to_i if max_quantity
|
||||
|
||||
@@ -8,7 +8,7 @@ class VariantsStockLevels
|
||||
variant_stock_levels = variant_stock_levels(order.line_items.includes(variant: :stock_items))
|
||||
|
||||
order_variant_ids = variant_stock_levels.keys
|
||||
missing_variants = Spree::Variant.includes(:stock_items).
|
||||
missing_variants = Spree::Variant.with_deleted.includes(:stock_items).
|
||||
where(id: (requested_variant_ids - order_variant_ids))
|
||||
|
||||
missing_variants.each do |missing_variant|
|
||||
|
||||
@@ -429,6 +429,34 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when a variant is soft-deleted" do
|
||||
describe "adding the soft-deleted variant to the cart" do
|
||||
it "handles it as if the variant has gone out of stock" do
|
||||
variant.delete
|
||||
|
||||
fill_in "variants[#{variant.id}]", with: '1'
|
||||
|
||||
expect_out_of_stock_behavior
|
||||
end
|
||||
end
|
||||
|
||||
context "when the soft-deleted variant has an associated override" do
|
||||
describe "adding the soft-deleted variant to the cart" do
|
||||
let!(:variant_override) {
|
||||
create(:variant_override, variant: variant, hub: distributor, count_on_hand: 100)
|
||||
}
|
||||
|
||||
it "handles it as if the variant has gone out of stock" do
|
||||
variant.delete
|
||||
|
||||
fill_in "variants[#{variant.id}]", with: '1'
|
||||
|
||||
expect_out_of_stock_behavior
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when no order cycles are available" do
|
||||
@@ -543,4 +571,24 @@ feature "As a consumer I want to shop with a distributor", js: true do
|
||||
# waiting period before submitting the data...
|
||||
sleep 0.6
|
||||
end
|
||||
|
||||
def expect_out_of_stock_behavior
|
||||
wait_for_debounce
|
||||
wait_until { !cart_dirty }
|
||||
|
||||
# Shows an "out of stock" modal, with helpful user feedback
|
||||
within(".out-of-stock-modal") do
|
||||
expect(page).to have_content I18n.t('js.out_of_stock.out_of_stock_text')
|
||||
end
|
||||
|
||||
# Removes the item from the client-side cart and marks the variant as unavailable
|
||||
expect(page).to have_field "variants[#{variant.id}]", with: '0', disabled: true
|
||||
expect(page).to have_selector "#variant-#{variant.id}.out-of-stock"
|
||||
expect(page).to have_selector "#variants_#{variant.id}[ofn-on-hand='0']"
|
||||
expect(page).to have_selector "#variants_#{variant.id}[disabled='disabled']"
|
||||
|
||||
# We need to wait again for the cart to finish updating in Angular or the test can fail
|
||||
# as the session cannot be reset properly (after the test) while it's still loading
|
||||
wait_until { !cart_dirty }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -227,6 +227,14 @@ describe OrderCycle do
|
||||
expect(oc.variants_distributed_by(d2)).not_to include p1_v_hidden, p1_v_deleted
|
||||
expect(oc.variants_distributed_by(d1)).to include p2_v
|
||||
end
|
||||
|
||||
context "with soft-deleted variants" do
|
||||
it "does not consider soft-deleted variants to be currently distributed in the oc" do
|
||||
p2_v.delete
|
||||
|
||||
expect(oc.variants_distributed_by(d1)).to_not include p2_v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when hub prefers product selection from inventory only" do
|
||||
|
||||
24
spec/models/spree/stock/quantifier_spec.rb
Normal file
24
spec/models/spree/stock/quantifier_spec.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
module Spree
|
||||
module Stock
|
||||
describe Quantifier do
|
||||
let(:quantifier) { Spree::Stock::Quantifier.new(variant) }
|
||||
let(:variant) { create(:variant, on_hand: 99) }
|
||||
|
||||
describe "#total_on_hand" do
|
||||
context "with a soft-deleted variant" do
|
||||
before do
|
||||
variant.delete
|
||||
end
|
||||
|
||||
it "returns zero stock for the variant" do
|
||||
expect(quantifier.total_on_hand).to eq 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -32,6 +32,11 @@ describe VariantOverride do
|
||||
expect(VariantOverride.indexed(hub1)).to eq( variant => vo1 )
|
||||
expect(VariantOverride.indexed(hub2)).to eq( variant => vo2 )
|
||||
end
|
||||
|
||||
it "does not include overrides for soft-deleted variants" do
|
||||
variant.delete
|
||||
expect(VariantOverride.indexed(hub1)).to eq( nil => vo1 )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -15,14 +15,18 @@ describe CartService do
|
||||
context "end-to-end" do
|
||||
let(:order) { create(:order, distributor: distributor, order_cycle: order_cycle) }
|
||||
let(:distributor) { create(:distributor_enterprise) }
|
||||
let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor], variants: [v]) }
|
||||
let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor],
|
||||
variants: [variant]) }
|
||||
let(:cart_service) { CartService.new(order) }
|
||||
let(:v) { create(:variant) }
|
||||
let(:variant) { create(:variant) }
|
||||
|
||||
describe "populate" do
|
||||
describe "#populate" do
|
||||
it "adds a variant" do
|
||||
cart_service.populate({ variants: { v.id.to_s => { quantity: '1', max_quantity: '2' } } }, true)
|
||||
li = order.find_line_item_by_variant(v)
|
||||
cart_service.populate(
|
||||
{ variants: { variant.id.to_s => { quantity: '1', max_quantity: '2' } } },
|
||||
true
|
||||
)
|
||||
li = order.find_line_item_by_variant(variant)
|
||||
expect(li).to be
|
||||
expect(li.quantity).to eq(1)
|
||||
expect(li.max_quantity).to eq(2)
|
||||
@@ -30,10 +34,13 @@ describe CartService do
|
||||
end
|
||||
|
||||
it "updates a variant's quantity, max quantity and final_weight_volume" do
|
||||
order.add_variant v, 1, 2
|
||||
order.add_variant variant, 1, 2
|
||||
|
||||
cart_service.populate({ variants: { v.id.to_s => { quantity: '2', max_quantity: '3' } } }, true)
|
||||
li = order.find_line_item_by_variant(v)
|
||||
cart_service.populate(
|
||||
{ variants: { variant.id.to_s => { quantity: '2', max_quantity: '3' } } },
|
||||
true
|
||||
)
|
||||
li = order.find_line_item_by_variant(variant)
|
||||
expect(li).to be
|
||||
expect(li.quantity).to eq(2)
|
||||
expect(li.max_quantity).to eq(3)
|
||||
@@ -41,13 +48,43 @@ describe CartService do
|
||||
end
|
||||
|
||||
it "removes a variant" do
|
||||
order.add_variant v, 1, 2
|
||||
order.add_variant variant, 1, 2
|
||||
|
||||
cart_service.populate({ variants: {} }, true)
|
||||
order.line_items(:reload)
|
||||
li = order.find_line_item_by_variant(v)
|
||||
li = order.find_line_item_by_variant(variant)
|
||||
expect(li).not_to be
|
||||
end
|
||||
|
||||
context "when a variant has been soft-deleted" do
|
||||
let(:relevant_line_item) { order.reload.find_line_item_by_variant(variant) }
|
||||
|
||||
describe "when the soft-deleted variant is not in the cart yet" do
|
||||
it "does not add the deleted variant to the cart" do
|
||||
variant.delete
|
||||
|
||||
cart_service.populate({ variants: { variant.id.to_s => { quantity: '2' } } }, true)
|
||||
|
||||
expect(relevant_line_item).to be_nil
|
||||
expect(cart_service.errors.count).to be 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "when the soft-deleted variant is already in the cart" do
|
||||
let!(:existing_line_item) {
|
||||
create(:line_item, variant: variant, quantity: 2, order: order)
|
||||
}
|
||||
|
||||
it "removes the line_item from the cart" do
|
||||
variant.delete
|
||||
|
||||
cart_service.populate({ variants: { variant.id.to_s => { quantity: '3' } } }, true)
|
||||
|
||||
expect(Spree::LineItem.where(id: relevant_line_item).first).to be_nil
|
||||
expect(cart_service.errors.count).to be 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user