diff --git a/app/services/standing_order_form.rb b/app/services/standing_order_form.rb index b749861c06..4163a3932e 100644 --- a/app/services/standing_order_form.rb +++ b/app/services/standing_order_form.rb @@ -3,13 +3,6 @@ require 'open_food_network/proxy_order_syncer' class StandingOrderForm attr_accessor :standing_order, :params, :fee_calculator, :order_update_issues, :validator, :order_updater - delegate :orders, :order_cycles, :bill_address, :ship_address, :standing_line_items, to: :standing_order - delegate :shop, :shop_id, :customer, :customer_id, :begins_at, :ends_at, :proxy_orders, to: :standing_order - delegate :schedule, :schedule_id, to: :standing_order - delegate :shipping_method, :shipping_method_id, :payment_method, :payment_method_id, to: :standing_order - delegate :shipping_method_id_changed?, :shipping_method_id_was, :payment_method_id_changed?, :payment_method_id_was, to: :standing_order - delegate :credit_card_id, :credit_card, to: :standing_order - delegate :json_errors, :valid?, to: :validator delegate :order_update_issues, to: :order_updater diff --git a/spec/services/standing_order_form_spec.rb b/spec/services/standing_order_form_spec.rb index b257e640c9..47ca2ae879 100644 --- a/spec/services/standing_order_form_spec.rb +++ b/spec/services/standing_order_form_spec.rb @@ -89,555 +89,6 @@ describe StandingOrderForm do end end - describe "changing the shipping method" do - let(:standing_order) { create(:standing_order, with_items: true, with_proxy_orders: true) } - let(:order) { standing_order.proxy_orders.first.initialise_order! } - let(:shipping_method) { standing_order.shipping_method } - let(:new_shipping_method) { create(:shipping_method, distributors: [standing_order.shop]) } - let(:invalid_shipping_method) { create(:shipping_method, distributors: [create(:enterprise)]) } - let(:form) { StandingOrderForm.new(standing_order, params) } - - context "when the shipping method on an order is the same as the standing order" do - let(:params) { { shipping_method_id: new_shipping_method.id } } - - context "and the shipping method is associated with the shop" do - it "updates the shipping_method on the order and on shipments" do - expect(order.shipments.first.shipping_method).to eq shipping_method - expect(form.save).to be true - expect(standing_order.reload.shipping_method).to eq new_shipping_method - expect(order.reload.shipping_method).to eq new_shipping_method - expect(order.shipments.first.shipping_method).to eq new_shipping_method - end - end - - context "and the shipping method is not associated with the shop" do - let(:params) { { shipping_method_id: invalid_shipping_method.id } } - - it "returns false and does not update the shipping method on the order or shipments" do - expect(order.shipments.first.shipping_method).to eq shipping_method - expect(form.save).to be false - expect(standing_order.reload.shipping_method).to eq shipping_method - expect(order.reload.shipping_method).to eq shipping_method - expect(order.shipments.first.shipping_method).to eq shipping_method - end - end - end - - context "when the shipping method on a shipment is not the same as the original shipping method on the standing order" do - let(:params) { { shipping_method_id: new_shipping_method.id } } - - context "when the shipping method on a shipment is the same as the new shipping method on the standing order" do - before do - # Updating the shipping method on a shipment updates the shipping method on the order, - # and vice-versa via logic in Spree's shipments controller. So updating both here mimics that - # behaviour. - order.shipments.first.update_attributes(shipping_method_id: new_shipping_method.id) - order.update_attributes(shipping_method_id: new_shipping_method.id) - expect(form.save).to be true - end - - it "does not update the shipping_method on the standing order or on the pre-altered shipment" do - expect(order.reload.shipping_method).to eq new_shipping_method - expect(order.reload.shipments.first.shipping_method).to eq new_shipping_method - expect(form.order_update_issues[order.id]).to be nil - end - end - - context "when the shipping method on a shipment is not the same as the new shipping method on the standing order" do - let(:changed_shipping_method) { create(:shipping_method) } - - before do - # Updating the shipping method on a shipment updates the shipping method on the order, - # and vice-versa via logic in Spree's shipments controller. So updating both here mimics that - # behaviour. - order.shipments.first.update_attributes(shipping_method_id: changed_shipping_method.id) - order.update_attributes(shipping_method_id: changed_shipping_method.id) - expect(form.save).to be true - end - - it "does not update the shipping_method on the standing order or on the pre-altered shipment" do - expect(order.reload.shipping_method).to eq changed_shipping_method - expect(order.reload.shipments.first.shipping_method).to eq changed_shipping_method - expect(form.order_update_issues[order.id]).to include "Shipping Method" - end - end - end - end - - describe "changing the payment method" do - let(:standing_order) { create(:standing_order, with_items: true, with_proxy_orders: true) } - let(:order) { standing_order.proxy_orders.first.initialise_order! } - let(:payment_method) { standing_order.payment_method } - let(:new_payment_method) { create(:payment_method, distributors: [standing_order.shop]) } - let(:invalid_payment_method) { create(:payment_method, distributors: [create(:enterprise)]) } - let(:bogus_payment_method) { create(:bogus_payment_method, distributors: [standing_order.shop]) } - let(:form) { StandingOrderForm.new(standing_order, params) } - - context "when the payment method on an order is the same as the standing order" do - let(:params) { { payment_method_id: new_payment_method.id } } - - context "and the submitted payment method is associated with the shop" do - context "and the submitted payment method is a Cash method" do - it "voids existing payments and creates a new payment with the relevant payment method" do - expect(order.payments.reload.first.payment_method).to eq payment_method - expect(form.save).to be true - payments = order.reload.payments - expect(payments.count).to be 2 - expect(payments.with_state('void').count).to be 1 - expect(payments.with_state('checkout').count).to be 1 - expect(payments.with_state('void').first.payment_method).to eq payment_method - expect(payments.with_state('checkout').first.payment_method).to eq new_payment_method - end - end - - context "and the submitted payment method is a Stripe method" do - let(:new_payment_method) { create(:stripe_payment_method, distributors: [standing_order.shop], preferred_enterprise_id: standing_order.shop.id) } - - context "with a credit card" do - let!(:user) { create(:user) } - let!(:credit_card) { create(:credit_card, user: user) } - - before do - standing_order.customer.update_attributes(user_id: user.id) - params[:credit_card_id] = credit_card.id - end - - it "voids existing payments and creates a new payment with the relevant payment method" do - expect(order.payments.reload.first.payment_method).to eq payment_method - expect(form.save).to be true - payments = order.reload.payments - expect(payments.count).to be 2 - expect(payments.with_state('void').count).to be 1 - expect(payments.with_state('checkout').count).to be 1 - expect(payments.with_state('void').first.payment_method).to eq payment_method - expect(payments.with_state('checkout').first.payment_method).to eq new_payment_method - end - end - - context "without a credit_card_id" do - it "requires a credit_card_id" do - expect(order.payments.reload.first.payment_method).to eq payment_method - expect(form.save).to be false - payments = order.reload.payments - expect(payments.count).to be 1 - expect(payments.with_state('void').count).to be 0 - expect(payments.with_state('checkout').count).to be 1 - expect(payments.with_state('checkout').first.payment_method).to eq payment_method - end - end - end - - context "and the submitted payment method is not a Stripe or Cash method" do - let(:params) { { payment_method_id: bogus_payment_method.id } } - - it "returns false and does not void existing payments or create a new payment" do - expect(order.payments.reload.first.payment_method).to eq payment_method - expect(form.save).to be false - payments = order.reload.payments - expect(payments.count).to be 1 - expect(payments.with_state('void').count).to be 0 - expect(payments.with_state('checkout').count).to be 1 - expect(payments.with_state('checkout').first.payment_method).to eq payment_method - end - end - end - - context "and the submitted payment method is not associated with the shop" do - let(:params) { { payment_method_id: invalid_payment_method.id } } - - it "returns false and does not void existing payments or create a new payment" do - expect(order.payments.reload.first.payment_method).to eq payment_method - expect(form.save).to be false - payments = order.reload.payments - expect(payments.count).to be 1 - expect(payments.with_state('void').count).to be 0 - expect(payments.with_state('checkout').count).to be 1 - expect(payments.with_state('checkout').first.payment_method).to eq payment_method - end - end - end - - context "when the payment method on a payment is not the same as the standing order" do - let(:params) { { payment_method_id: new_payment_method.id } } - - context "when the payment method on a payment is the same as the original payment method on the standing order" do - before do - order.payments.first.update_attribute(:payment_method_id, new_payment_method.id) - expect(form.save).to be true - end - - it "keeps pre-altered payments and doesn't add an issue to order_update_issues" do - payments = order.reload.payments - expect(payments.count).to be 1 - expect(payments.first.payment_method).to eq new_payment_method - expect(form.order_update_issues[order.id]).to be nil - end - end - - context "when the payment method on a shipment is not the same as the original payment method on the standing order" do - let(:changed_payment_method) { create(:payment_method) } - - before do - order.payments.first.update_attribute(:payment_method_id, changed_payment_method.id) - expect(form.save).to be true - end - - it "keeps pre-altered payments and adds an issue to order_update_issues" do - payments = order.reload.payments - expect(payments.count).to be 1 - expect(payments.first.payment_method).to eq changed_payment_method - expect(form.order_update_issues[order.id]).to include "Payment Method" - end - end - end - end - - describe "changing begins_at" do - let(:standing_order) { create(:standing_order, begins_at: Time.zone.now, ends_at: 2.months.from_now, with_items: true, with_proxy_orders: true) } - let(:form) { StandingOrderForm.new(standing_order, params) } - - before { standing_order.proxy_orders.each(&:initialise_order!) } - - context "to a date that is before ends_at" do - let(:params) { { begins_at: 1.month.from_now } } - - it "removes orders outside the newly specified date range, recreates proxy orders" do - expect(standing_order.reload.proxy_orders.count).to be 1 - expect(standing_order.reload.orders.count).to be 1 - expect(form.save).to be true - expect(standing_order.reload.proxy_orders.count).to be 0 - expect(standing_order.reload.orders.count).to be 0 - form.params = { begins_at: 1.month.ago } - expect(form.save).to be true - expect(standing_order.reload.proxy_orders.count).to be 1 - expect(standing_order.reload.orders.count).to be 0 - end - end - - context "to a date that is after ends_at" do - let(:params) { { begins_at: 3.months.from_now } } - - it "returns false, does not update begins_at and alter orders or proxy orders" do - expect(standing_order.reload.proxy_orders.count).to be 1 - expect(standing_order.reload.orders.count).to be 1 - expect(form.save).to be false - expect(standing_order.reload.begins_at).to be_within(5.seconds).of Time.zone.now - expect(standing_order.proxy_orders.count).to be 1 - expect(standing_order.orders.count).to be 1 - end - end - end - - describe "changing the billing address" do - let(:standing_order) { create(:standing_order, with_items: true, with_proxy_orders: true) } - let(:shipping_method) { standing_order.shipping_method } - let!(:order) { standing_order.proxy_orders.first.initialise_order! } - let!(:bill_address_attrs) { standing_order.bill_address.attributes } - let!(:ship_address_attrs) { standing_order.ship_address.attributes } - let(:params) { { bill_address_attributes: { id: bill_address_attrs["id"], firstname: "Bill", address1: "123 abc st", phone: "1123581321" } } } - let(:form) { StandingOrderForm.new(standing_order, params) } - - context "when a ship address is not required" do - before { shipping_method.update_attributes(require_ship_address: false) } - - context "when the bill_address on the order matches that on the standing order" do - it "updates all bill_address attrs and ship_address names + phone" do - expect(form.save).to be true - expect(form.order_update_issues.keys).to_not include order.id - order.reload; standing_order.reload; - expect(standing_order.bill_address.firstname).to eq "Bill" - expect(standing_order.bill_address.lastname).to eq bill_address_attrs["lastname"] - expect(standing_order.bill_address.address1).to eq "123 abc st" - expect(standing_order.bill_address.phone).to eq "1123581321" - expect(order.bill_address.firstname).to eq "Bill" - expect(order.bill_address.lastname).to eq bill_address_attrs["lastname"] - expect(order.bill_address.address1).to eq "123 abc st" - expect(order.bill_address.phone).to eq "1123581321" - expect(order.ship_address.firstname).to eq "Bill" - expect(order.ship_address.lastname).to eq ship_address_attrs["lastname"] - expect(order.ship_address.address1).to eq ship_address_attrs["address1"] - expect(order.ship_address.phone).to eq "1123581321" - end - end - - context "when the bill_address on the order doesn't match that on the standing order" do - before { order.bill_address.update_attributes(firstname: "Jane") } - it "does not update bill_address or ship_address on the order" do - expect(form.save).to be true - expect(form.order_update_issues.keys).to include order.id - order.reload; standing_order.reload; - expect(standing_order.bill_address.firstname).to eq "Bill" - expect(standing_order.bill_address.lastname).to eq bill_address_attrs["lastname"] - expect(standing_order.bill_address.address1).to eq "123 abc st" - expect(standing_order.bill_address.phone).to eq "1123581321" - expect(order.bill_address.firstname).to eq "Jane" - expect(order.bill_address.lastname).to eq bill_address_attrs["lastname"] - expect(order.bill_address.address1).to eq bill_address_attrs["address1"] - expect(order.bill_address.phone).to eq bill_address_attrs["phone"] - expect(order.ship_address.firstname).to eq "Jane" - expect(order.ship_address.lastname).to eq ship_address_attrs["lastname"] - expect(order.ship_address.address1).to eq ship_address_attrs["address1"] - expect(order.ship_address.phone).to eq ship_address_attrs["phone"] - end - end - end - - context "when a ship address is required" do - before { shipping_method.update_attributes(require_ship_address: true) } - - context "when the bill_address on the order matches that on the standing order" do - it "only updates bill_address attrs" do - expect(form.save).to be true - expect(form.order_update_issues.keys).to_not include order.id - order.reload; standing_order.reload; - expect(order.bill_address.firstname).to eq "Bill" - expect(order.bill_address.lastname).to eq bill_address_attrs["lastname"] - expect(order.bill_address.address1).to eq "123 abc st" - expect(order.bill_address.phone).to eq "1123581321" - expect(order.ship_address.firstname).to eq ship_address_attrs["firstname"] - expect(order.ship_address.lastname).to eq ship_address_attrs["lastname"] - expect(order.ship_address.address1).to eq ship_address_attrs["address1"] - expect(order.ship_address.phone).to eq ship_address_attrs["phone"] - end - end - - context "when the bill_address on the order doesn't match that on the standing order" do - before { order.bill_address.update_attributes(firstname: "Jane") } - it "does not update bill_address or ship_address on the order" do - expect(form.save).to be true - expect(form.order_update_issues.keys).to include order.id - order.reload; standing_order.reload; - expect(standing_order.bill_address.firstname).to eq "Bill" - expect(standing_order.bill_address.lastname).to eq bill_address_attrs["lastname"] - expect(standing_order.bill_address.address1).to eq "123 abc st" - expect(standing_order.bill_address.phone).to eq "1123581321" - expect(order.bill_address.firstname).to eq "Jane" - expect(order.bill_address.lastname).to eq bill_address_attrs["lastname"] - expect(order.bill_address.address1).to eq bill_address_attrs["address1"] - expect(order.bill_address.phone).to eq bill_address_attrs["phone"] - expect(order.ship_address.firstname).to eq ship_address_attrs["firstname"] - expect(order.ship_address.lastname).to eq ship_address_attrs["lastname"] - expect(order.ship_address.address1).to eq ship_address_attrs["address1"] - expect(order.ship_address.phone).to eq ship_address_attrs["phone"] - end - end - end - end - - describe "changing the ship address" do - let(:standing_order) { create(:standing_order, with_items: true, with_proxy_orders: true) } - let(:shipping_method) { standing_order.shipping_method } - let!(:order) { standing_order.proxy_orders.first.initialise_order! } - let!(:bill_address_attrs) { standing_order.bill_address.attributes } - let!(:ship_address_attrs) { standing_order.ship_address.attributes } - let(:params) { { ship_address_attributes: { id: ship_address_attrs["id"], firstname: "Ship", address1: "123 abc st", phone: "1123581321" } } } - let(:form) { StandingOrderForm.new(standing_order, params) } - - context "when a ship address is not required" do - before { shipping_method.update_attributes(require_ship_address: false) } - - it "does not change the ship address" do - expect(form.save).to be true - expect(form.order_update_issues.keys).to_not include order.id - order.reload; standing_order.reload; - expect(order.ship_address.firstname).to eq ship_address_attrs["firstname"] - expect(order.ship_address.lastname).to eq ship_address_attrs["lastname"] - expect(order.ship_address.address1).to eq ship_address_attrs["address1"] - expect(order.ship_address.phone).to eq ship_address_attrs["phone"] - end - - context "but the shipping method is being changed to one that requires a ship_address" do - let(:new_shipping_method) { create(:shipping_method, require_ship_address: true) } - before { params.merge!(shipping_method_id: new_shipping_method.id) } - - it "updates ship_address attrs" do - expect(form.save).to be true - expect(form.order_update_issues.keys).to_not include order.id - order.reload; standing_order.reload; - expect(order.ship_address.firstname).to eq "Ship" - expect(order.ship_address.lastname).to eq ship_address_attrs["lastname"] - expect(order.ship_address.address1).to eq "123 abc st" - expect(order.ship_address.phone).to eq "1123581321" - end - end - end - - context "when a ship address is required" do - before { shipping_method.update_attributes(require_ship_address: true) } - - context "when the ship address on the order matches that on the standing order" do - it "updates ship_address attrs" do - expect(form.save).to be true - expect(form.order_update_issues.keys).to_not include order.id - order.reload; standing_order.reload; - expect(standing_order.ship_address.firstname).to eq "Ship" - expect(standing_order.ship_address.lastname).to eq ship_address_attrs["lastname"] - expect(standing_order.ship_address.address1).to eq "123 abc st" - expect(standing_order.ship_address.phone).to eq "1123581321" - expect(order.ship_address.firstname).to eq "Ship" - expect(order.ship_address.lastname).to eq ship_address_attrs["lastname"] - expect(order.ship_address.address1).to eq "123 abc st" - expect(order.ship_address.phone).to eq "1123581321" - end - end - - context "when the ship address on the order doesn't match that on the standing order" do - before { order.ship_address.update_attributes(firstname: "Jane") } - it "does not update ship_address on the order" do - expect(form.save).to be true - expect(form.order_update_issues.keys).to include order.id - order.reload; standing_order.reload; - expect(standing_order.ship_address.firstname).to eq "Ship" - expect(standing_order.ship_address.lastname).to eq ship_address_attrs["lastname"] - expect(standing_order.ship_address.address1).to eq "123 abc st" - expect(standing_order.ship_address.phone).to eq "1123581321" - expect(order.ship_address.firstname).to eq "Jane" - expect(order.ship_address.lastname).to eq ship_address_attrs["lastname"] - expect(order.ship_address.address1).to eq ship_address_attrs["address1"] - expect(order.ship_address.phone).to eq ship_address_attrs["phone"] - end - end - end - end - - describe "changing the quantity of a line item" do - let(:standing_order) { create(:standing_order, with_items: true, with_proxy_orders: true) } - let(:order) { standing_order.proxy_orders.first.initialise_order! } - let(:sli) { standing_order.standing_line_items.first } - let(:variant) { sli.variant } - - before { variant.update_attribute(:count_on_hand, 2) } - - context "when quantity is within available stock" do - let(:params) { { standing_line_items_attributes: [{ id: sli.id, quantity: 2}] } } - let(:form) { StandingOrderForm.new(standing_order, params) } - - it "updates the line_item quantities and totals on all orders" do - expect(order.reload.total.to_f).to eq 59.97 - expect(form.save).to be true - line_items = Spree::LineItem.where(order_id: standing_order.orders, variant_id: sli.variant_id) - expect(line_items.map(&:quantity)).to eq [2] - expect(order.reload.total.to_f).to eq 79.96 - end - end - - context "when quantity is greater than available stock" do - let(:params) { { standing_line_items_attributes: [{ id: sli.id, quantity: 3}] } } - let(:form) { StandingOrderForm.new(standing_order, params) } - - it "updates the line_item quantities and totals on all orders" do - expect(order.reload.total.to_f).to eq 59.97 - expect(form.save).to be true - line_items = Spree::LineItem.where(order_id: standing_order.orders, variant_id: sli.variant_id) - expect(line_items.map(&:quantity)).to eq [3] - expect(order.reload.total.to_f).to eq 99.95 - end - end - - context "where the quantity of the item on an initialised order has already been changed" do - let(:params) { { standing_line_items_attributes: [{ id: sli.id, quantity: 3}] } } - let(:form) { StandingOrderForm.new(standing_order, params) } - let(:changed_line_item) { order.line_items.find_by_variant_id(sli.variant_id) } - - before { variant.update_attribute(:count_on_hand, 3) } - - context "when the changed line_item quantity matches the new quantity on the standing line item" do - before { changed_line_item.update_attributes(quantity: 3) } - - it "does not change the quantity, and doesn't add the order to order_update_issues" do - expect(order.reload.total.to_f).to eq 99.95 - expect(form.save).to be true - expect(changed_line_item.reload.quantity).to eq 3 - expect(order.reload.total.to_f).to eq 99.95 - expect(form.order_update_issues[order.id]).to be nil - end - end - - context "when the changed line_item quantity doesn't match the new quantity on the standing line item" do - before { changed_line_item.update_attributes(quantity: 2) } - - it "does not change the quantity, and adds the order to order_update_issues" do - expect(order.reload.total.to_f).to eq 79.96 - expect(form.save).to be true - expect(changed_line_item.reload.quantity).to eq 2 - expect(order.reload.total.to_f).to eq 79.96 - expect(form.order_update_issues[order.id]).to include "#{changed_line_item.product.name} - #{changed_line_item.full_name}" - end - end - end - end - - describe "adding a new line item" do - let(:variant) { create(:variant) } - let(:unavailable_variant) { create(:variant) } - let(:shop) { create(:enterprise) } - let(:order_cycle) { create(:simple_order_cycle, variants: [variant], coordinator: shop, distributors: [shop]) } - let(:schedule) { create(:schedule, order_cycles: [order_cycle] ) } - let(:standing_order) { create(:standing_order, schedule: schedule, shop: shop, with_items: true, with_proxy_orders: true) } - let(:order) { standing_order.proxy_orders.first.initialise_order! } - let(:form) { StandingOrderForm.new(standing_order, params) } - - context "that is available from the selected schedule" do - let(:params) { { standing_line_items_attributes: [{ id: nil, variant_id: variant.id, quantity: 1}] } } - - it "adds the line item and updates the total on all orders" do - expect(order.reload.total.to_f).to eq 59.97 - expect(form.save).to be true - line_items = Spree::LineItem.where(order_id: standing_order.orders, variant_id: variant.id) - expect(line_items.map(&:quantity)).to eq [1] - expect(order.reload.total.to_f).to eq 79.96 - end - end - - context "that is not available from the selected schedule" do - let(:params) { { standing_line_items_attributes: [{ id: nil, variant_id: unavailable_variant.id, quantity: 1}] } } - - it "returns false and does not add the line item or update the total on orders" do - expect(order.reload.total.to_f).to eq 59.97 - expect(form.save).to be false - line_items = Spree::LineItem.where(order_id: standing_order.orders, variant_id: variant.id) - expect(line_items.count).to be 0 - expect(order.reload.total.to_f).to eq 59.97 - end - end - end - - describe "removing an existing line item" do - let(:standing_order) { create(:standing_order, with_items: true, with_proxy_orders: true) } - let(:order) { standing_order.proxy_orders.first.initialise_order! } - let(:sli) { standing_order.standing_line_items.first } - let(:variant) { sli.variant } - let(:params) { { standing_line_items_attributes: [{ id: sli.id, _destroy: true }] } } - let(:form) { StandingOrderForm.new(standing_order, params) } - - context "that is not the last remaining item" do - it "removes the line item and updates totals on all orders" do - expect(order.reload.total.to_f).to eq 59.97 - expect(form.save).to be true - line_items = Spree::LineItem.where(order_id: standing_order.orders, variant_id: variant.id) - expect(line_items.count).to be 0 - expect(order.reload.total.to_f).to eq 39.98 - end - end - - context "that is the last remaining item" do - before do - standing_order.standing_line_items.where('variant_id != ?', variant.id).destroy_all - order.line_items.where('variant_id != ?', variant.id).destroy_all - standing_order.reload - order.reload - end - - it "returns false and does not remove the line item or update totals on orders" do - expect(order.reload.total.to_f).to eq 19.99 - expect(form.save).to be false - line_items = Spree::LineItem.where(order_id: standing_order.orders, variant_id: variant.id) - expect(line_items.count).to be 1 - expect(order.reload.total.to_f).to eq 19.99 - end - end - end - describe "validating price_estimates on standing line items" do let(:params) { { } } let(:form) { StandingOrderForm.new(nil, params) }