Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cart with soft-deletion #5361

Merged
4 changes: 4 additions & 0 deletions app/models/spree/stock/quantifier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ def initialize(variant)
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?
luisramos0 marked this conversation as resolved.
Show resolved Hide resolved

stock_items.sum(&:count_on_hand)
end

Expand Down
16 changes: 12 additions & 4 deletions app/services/cart_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,32 @@ def attempt_cart_add_variants(variants_data)

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

def indexed_variants(variants_data)
@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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in the long term we should soft-delete the line item upon soft-deletion of the variant, ala ON DELETE CASCADE only that it'll be an UPDATE statement in this case.

end

def attempt_cart_add(variant, quantity, max_quantity = nil)
quantity = quantity.to_i
max_quantity = max_quantity.to_i if max_quantity
Expand Down
2 changes: 1 addition & 1 deletion app/services/variants_stock_levels.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def call(order, requested_variant_ids)
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|
Expand Down
48 changes: 48 additions & 0 deletions spec/features/consumer/shopping/shopping_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,34 @@
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
Expand Down Expand Up @@ -543,4 +571,24 @@ def wait_for_debounce
# 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
8 changes: 8 additions & 0 deletions spec/models/order_cycle_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,14 @@
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
Expand Down
24 changes: 24 additions & 0 deletions spec/models/spree/stock/quantifier_spec.rb
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions spec/models/variant_override_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
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

Expand Down
57 changes: 47 additions & 10 deletions spec/services/cart_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,76 @@
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)
expect(li.final_weight_volume).to eq(1.0)
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)
expect(li.final_weight_volume).to eq(2.0)
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

Expand Down