diff --git a/core/spec/models/spree/order_updater_spec.rb b/core/spec/models/spree/order_updater_spec.rb index 8f5163c04f5..e79a0da63b7 100644 --- a/core/spec/models/spree/order_updater_spec.rb +++ b/core/spec/models/spree/order_updater_spec.rb @@ -29,6 +29,15 @@ module Spree }.to change { order.item_total }.to 20 end + it "respects changes to item totals" do + updater.recalculate + order.line_items.first.update(quantity: 2) + + expect { + updater.recalculate + }.to change { order.item_total }.by 10 + end + it "update shipment total" do create(:shipment, order:, cost: 10) expect { @@ -58,212 +67,208 @@ module Spree end end - describe '#recalculate_adjustments ' do - describe 'promotion recalculation' do - it "calls the Promotion Adjustments Recalculator" do - adjuster = double(:call) - expect(Spree::Config.promotions.order_adjuster_class).to receive(:new).and_return(adjuster) - expect(adjuster).to receive(:call) - order.recalculate - end + describe 'promotion recalculation' do + it "calls the Promotion Adjustments Recalculator" do + adjuster = double(:call) + expect(Spree::Config.promotions.order_adjuster_class).to receive(:new).and_return(adjuster) + expect(adjuster).to receive(:call) + order.recalculate end + end - describe 'tax recalculation' do - let(:tax_category) { create(:tax_category) } - let(:ship_address) { create(:address, state: new_york) } - let(:new_york) { create(:state, state_code: "NY") } - let(:new_york_tax_zone) { create(:zone, states: [new_york]) } + describe 'tax recalculation' do + let(:tax_category) { create(:tax_category) } + let(:ship_address) { create(:address, state: new_york) } + let(:new_york) { create(:state, state_code: "NY") } + let(:new_york_tax_zone) { create(:zone, states: [new_york]) } + + let!(:new_york_tax_rate) do + create( + :tax_rate, + name: "New York Sales Tax", + tax_categories: [tax_category], + zone: new_york_tax_zone, + included_in_price: false, + amount: 0.1 + ) + end - let!(:new_york_tax_rate) do + let(:order) do + create( + :order_with_line_items, + line_items_attributes: [{ price: 10, variant: variant }], + ship_address: ship_address, + ) + end + let(:line_item) { order.line_items[0] } + + let(:variant) { create(:variant, tax_category:) } + + context 'when the item quantity has changed' do + before do + line_item.update!(quantity: 2) + end + + it 'updates the additional_tax_total' do + expect { + order.recalculate + }.to change { + line_item.additional_tax_total + }.from(1).to(2) + end + end + + context 'when the address has changed to a different state' do + let(:oregon) { create(:state, state_code: "OR") } + let(:oregon_tax_zone) { create(:zone, states: [oregon]) } + let!(:oregon_tax_rate) do create( :tax_rate, - name: "New York Sales Tax", + name: "Oregon Sales Tax", tax_categories: [tax_category], - zone: new_york_tax_zone, + zone: oregon_tax_zone, included_in_price: false, - amount: 0.1 + amount: 0.2 ) end - - let(:order) do - create( - :order_with_line_items, - line_items_attributes: [{ price: 10, variant: variant }], - ship_address: ship_address, - ) + let(:new_address) { create(:address, state: oregon) } + let(:shipping_method) { create(:shipping_method, tax_category:, zones: [oregon_tax_zone, new_york_tax_zone], cost: 10) } + let(:shipping_rate) do + create(:shipping_rate, cost: 10, shipping_method: shipping_method) end - let(:line_item) { order.line_items[0] } + let(:shipment) { order.shipments[0] } - let(:variant) { create(:variant, tax_category:) } + subject do + order.ship_address = new_address + order.bill_address = new_address - context 'when the item quantity has changed' do - before do - line_item.update!(quantity: 2) - end - - it 'updates the additional_tax_total' do - expect { - order.recalculate - }.to change { - line_item.additional_tax_total - }.from(1).to(2) - end + order.recalculate end - context 'when the address has changed to a different state' do - let(:oregon) { create(:state, state_code: "OR") } - let(:oregon_tax_zone) { create(:zone, states: [oregon]) } - let!(:oregon_tax_rate) do - create( - :tax_rate, - name: "Oregon Sales Tax", - tax_categories: [tax_category], - zone: oregon_tax_zone, - included_in_price: false, - amount: 0.2 - ) - end - let(:new_address) { create(:address, state: oregon) } - let(:shipping_method) { create(:shipping_method, tax_category:, zones: [oregon_tax_zone, new_york_tax_zone], cost: 10) } - let(:shipping_rate) do - create(:shipping_rate, cost: 10, shipping_method: shipping_method) - end - let(:shipment) { order.shipments[0] } - - subject do - order.ship_address = new_address - order.bill_address = new_address + before do + shipment.shipping_rates = [shipping_rate] + shipment.selected_shipping_rate_id = shipping_rate.id + order.recalculate + end - order.recalculate - end + it 'updates the taxes to reflect the new state' do + expect { + subject + }.to change { + order.additional_tax_total + }.from(2).to(4) + end - before do - shipment.shipping_rates = [shipping_rate] - shipment.selected_shipping_rate_id = shipping_rate.id - order.recalculate - end + it 'updates the shipment taxes to reflect the new state' do + expect { + subject + }.to change { + order.shipments.first.additional_tax_total + }.from(1).to(2) + .and change { + order.shipments.first.adjustments.first.amount + }.from(1).to(2) + end - it 'updates the taxes to reflect the new state' do - expect { - subject - }.to change { - order.additional_tax_total - }.from(2).to(4) - end + it 'updates the line item taxes to reflect the new state' do + expect { + subject + }.to change { + order.line_items.first.additional_tax_total + }.from(1).to(2) + .and change { + order.line_items.first.adjustments.first.amount + }.from(1).to(2) + end + end - it 'updates the shipment taxes to reflect the new state' do - expect { - subject - }.to change { - order.shipments.first.additional_tax_total - }.from(1).to(2) - .and change { - order.shipments.first.adjustments.first.amount - }.from(1).to(2) - end + context "with an order-level tax adjustment" do + let(:colorado) { create(:state, state_code: "CO") } + let(:colorado_tax_zone) { create(:zone, states: [colorado]) } + let(:ship_address) { create(:address, state: colorado) } - it 'updates the line item taxes to reflect the new state' do - expect { - subject - }.to change { - order.line_items.first.additional_tax_total - }.from(1).to(2) - .and change { - order.line_items.first.adjustments.first.amount - }.from(1).to(2) - end + let!(:colorado_delivery_fee) do + create( + :tax_rate, + amount: 0.27, + calculator: Spree::Calculator::FlatFee.new, + level: "order", + name: "Colorado Delivery Fee", + tax_categories: [tax_category], + zone: colorado_tax_zone + ) end - context "with an order-level tax adjustment" do - let(:colorado) { create(:state, state_code: "CO") } - let(:colorado_tax_zone) { create(:zone, states: [colorado]) } - let(:ship_address) { create(:address, state: colorado) } - - let!(:colorado_delivery_fee) do - create( - :tax_rate, - amount: 0.27, - calculator: Spree::Calculator::FlatFee.new, - level: "order", - name: "Colorado Delivery Fee", - tax_categories: [tax_category], - zone: colorado_tax_zone - ) - end - - before { order.recalculate } + before { order.recalculate } - it "updates the order-level tax adjustment" do - expect { - order.ship_address = create(:address) - order.recalculate - }.to change { order.additional_tax_total }.from(0.27).to(0). - and change { order.adjustment_total }.from(0.27).to(0) - end + it "updates the order-level tax adjustment" do + expect { + order.ship_address = create(:address) + order.recalculate + }.to change { order.additional_tax_total }.from(0.27).to(0). + and change { order.adjustment_total }.from(0.27).to(0) + end - it "deletes the order-level tax adjustments when it persists the order" do - expect { - order.ship_address = create(:address) - order.recalculate - }.to change { order.all_adjustments.count }.from(1).to(0) - end + it "deletes the order-level tax adjustments when it persists the order" do + expect { + order.ship_address = create(:address) + order.recalculate + }.to change { order.all_adjustments.count }.from(1).to(0) end + end - context 'with a custom tax_calculator_class' do - let(:custom_calculator_class) { double } - let(:custom_calculator_instance) { double } - - before do - order # generate this first so we can expect it - stub_spree_preferences(tax_calculator_class: custom_calculator_class) - - allow(custom_calculator_class).to receive(:new).and_return(custom_calculator_instance) - allow(custom_calculator_instance).to receive(:calculate).and_return( - Spree::Tax::OrderTax.new( - order_id: order.id, - order_taxes: [ - Spree::Tax::ItemTax.new( - label: "Delivery Fee", - tax_rate: new_york_tax_rate, - amount: 2.60, - included_in_price: false - ) - ], - line_item_taxes: [ - Spree::Tax::ItemTax.new( - item_id: line_item.id, - label: "Item Tax", - tax_rate: new_york_tax_rate, - amount: 1.40, - included_in_price: false - ) - ], - shipment_taxes: [] - ) + context 'with a custom tax_calculator_class' do + let(:custom_calculator_class) { double } + let(:custom_calculator_instance) { double } + + before do + order # generate this first so we can expect it + stub_spree_preferences(tax_calculator_class: custom_calculator_class) + + allow(custom_calculator_class).to receive(:new).and_return(custom_calculator_instance) + allow(custom_calculator_instance).to receive(:calculate).and_return( + Spree::Tax::OrderTax.new( + order_id: order.id, + order_taxes: [ + Spree::Tax::ItemTax.new( + label: "Delivery Fee", + tax_rate: new_york_tax_rate, + amount: 2.60, + included_in_price: false + ) + ], + line_item_taxes: [ + Spree::Tax::ItemTax.new( + item_id: line_item.id, + label: "Item Tax", + tax_rate: new_york_tax_rate, + amount: 1.40, + included_in_price: false + ) + ], + shipment_taxes: [] ) - end + ) + end - it 'uses the configured class' do - order.recalculate + it 'uses the configured class' do + order.recalculate - expect(custom_calculator_class).to have_received(:new).with(order).at_least(:once) - expect(custom_calculator_instance).to have_received(:calculate).at_least(:once) - end + expect(custom_calculator_class).to have_received(:new).with(order).at_least(:once) + expect(custom_calculator_instance).to have_received(:calculate).at_least(:once) + end - it 'updates the aggregate columns' do - expect { - order.recalculate - }.to change { order.reload.additional_tax_total }.to(4.00) - .and change { order.reload.adjustment_total }.to(4.00) - end + it 'updates the aggregate columns' do + expect { + order.recalculate + }.to change { order.reload.additional_tax_total }.to(4.00) + .and change { order.reload.adjustment_total }.to(4.00) end end end context "updating shipment state" do - before do - allow(order).to receive_messages backordered?: false - end + let(:order) { create :order_with_line_items } it "logs a state change for the shipment" do create :shipment, order:, state: "pending" @@ -278,37 +283,76 @@ module Spree }.to change { Spree::StateChange.where(name: "shipment").count }.by(1) end - it "is backordered" do - allow(order).to receive_messages backordered?: true - updater.recalculate_shipment_state + context "when an inventory unit is backordered" do + before do + create :inventory_unit, order: order, state: "backordered" + end + + it "has a 'backorder' shipment state" do + updater.recalculate_shipment_state + expect(order.shipment_state).to eq('backorder') + end + end + + context "when there are no shipments" do + before do + order.shipments.destroy_all + end + + it "has no shipment state" do + updater.recalculate_shipment_state + expect(order.shipment_state).to be_nil + end + end + + context "when the order's shipments are 'shipped'" do + before do + inventory_unit = order.inventory_units.first + inventory_unit.update!(state: "shipped") + order.shipments.first.update!(state: "shipped") + end - expect(order.shipment_state).to eq('backorder') + it "is shipped" do + updater.recalculate_shipment_state + expect(order.shipment_state).to eq("shipped") + end end - it "is nil" do - updater.recalculate_shipment_state - expect(order.shipment_state).to be_nil + context "when the order's shipments are 'ready'" do + let(:order) { create :order_ready_to_ship, shipment_state: "pending" } + + it "is ready" do + updater.recalculate_shipment_state + expect(order.shipment_state).to eq("ready") + end end - ["shipped", "ready", "pending"].each do |state| - it "is #{state}" do - create(:shipment, order:, state:) + context "when the order's shipments are 'pending'" do + it "is pending" do + create(:shipment, order: order, state: "pending") updater.recalculate_shipment_state - expect(order.shipment_state).to eq(state) + expect(order.shipment_state).to eq("pending") end end - it "is partial" do - create(:shipment, order:, state: 'pending') - create(:shipment, order:, state: 'ready') - updater.recalculate_shipment_state - expect(order.shipment_state).to eq('partial') + context "when some of the order's shipments are shipped" do + before do + inventory_unit = create :inventory_unit, order: order, state: "shipped" + inventory_unit.shipment.update!(state: "shipped") + + create :inventory_unit, order: order, state: "on_hand" + end + + it "has a shipment state of 'partial'" do + updater.recalculate_shipment_state + expect(order.shipment_state).to eq('partial') + end end end context "updating payment state" do let(:order) { create(:order) } - let(:updater) { order.recalculator } + let(:updater) { described_class.new(order) } before { allow(order).to receive(:refund_total).and_return(0) } it "logs a state change for the payment" do diff --git a/core/spec/models/spree/shipment_spec.rb b/core/spec/models/spree/shipment_spec.rb index aaa20cb64da..2fe7f56292f 100644 --- a/core/spec/models/spree/shipment_spec.rb +++ b/core/spec/models/spree/shipment_spec.rb @@ -581,14 +581,23 @@ end end - context "updates cost when selected shipping rate is present" do - let(:shipment) { create(:shipment) } - before { shipment.selected_shipping_rate.update!(cost: 5) } + describe "#update_amounts" do + let(:shipment) { create(:shipment, cost: 3) } - it "updates shipment totals" do - expect { - shipment.update_amounts - }.to change { shipment.cost }.to(5) + context 'when the selected shipping rate cost is different than the current shipment cost' do + before { shipment.selected_shipping_rate.update!(cost: 5) } + + it "updates the shipments cost" do + expect { + shipment.update_amounts + }.to change { shipment.reload.cost }.to(5) + end + + it 'changes the updated_at column' do + expect { + shipment.update_amounts + }.to change { shipment.reload.updated_at } + end end end diff --git a/promotions/spec/lib/solidus_promotions/engine_spec.rb b/promotions/spec/lib/solidus_promotions/engine_spec.rb index 5d133b56ae4..9f283875161 100644 --- a/promotions/spec/lib/solidus_promotions/engine_spec.rb +++ b/promotions/spec/lib/solidus_promotions/engine_spec.rb @@ -4,6 +4,15 @@ RSpec.describe SolidusPromotions::Engine do describe "initializer.flickwerk_alias" do + around do |example| + original_value = Spree::Config.order_recalculator_class_name + Spree::Config.order_recalculator_class = "ThisIsATestClassName" + + example.run + + Spree::Config.order_recalculator_class = original_value + end + it "sets the Flickwerk alias for order_recalculator_class" do Flickwerk.aliases["Spree::Config.order_recalculator_class"] = nil @@ -12,7 +21,7 @@ initializer = SolidusPromotions::Engine.initializers.find { |i| i.name == "solidus_promotions.flickwerk_alias" } initializer.run - expect(Flickwerk.aliases["Spree::Config.order_recalculator_class"]).to eq("Spree::OrderUpdater") + expect(Flickwerk.aliases["Spree::Config.order_recalculator_class"]).to eq("ThisIsATestClassName") end end end diff --git a/promotions/spec/models/solidus_promotions/order_adjuster_spec.rb b/promotions/spec/models/solidus_promotions/order_adjuster_spec.rb index 6a13304dca7..c19771c8fca 100644 --- a/promotions/spec/models/solidus_promotions/order_adjuster_spec.rb +++ b/promotions/spec/models/solidus_promotions/order_adjuster_spec.rb @@ -10,6 +10,32 @@ let(:promotion) { create(:solidus_promotion, apply_automatically: true) } let(:calculator) { SolidusPromotions::Calculators::Percent.new(preferred_percent: 10) } + context "adding discounted line items" do + let(:variant) { create(:variant, price: 100) } + let(:benefit) do + SolidusPromotions::Benefits::CreateDiscountedItem.create( + promotion: promotion, + calculator: calculator, + preferences: { variant_id: variant.id } + ) + end + let(:adjustable) { order } + + subject do + benefit + discounter.call + end + + it "creates a line item of the given variant with a discount adjustment corresponding to the calculator" do + expect { + subject + }.to change { order.line_items.count }.by(1) + + expect(order.line_items.last.variant).to eq(variant) + expect(order.line_items.last.adjustments.promotion.first&.amount).to eq(-10) + end + end + context "adjusting line items" do let(:benefit) do SolidusPromotions::Benefits::AdjustLineItem.create(promotion: promotion, calculator: calculator)