From 899dffec966c34fdc21ee54dc6cfb77cff9bfe65 Mon Sep 17 00:00:00 2001 From: Cillian O'Ruanaidh Date: Fri, 22 Jan 2021 14:42:41 +0000 Subject: [PATCH] When an order is cancelled by a customer send an email to the shop. Fixes #6435 i.e. If the customer paid for their order by Stripe/Paypal then the Enterprise needs to know that the order was cancelled in order to arrange a refund. Refunds are not automatically processed when an order is cancelled. This will send a very basic email to the shop, it only includes a link to view the cancelled order in the admin area initially. I created a CustomerOrderCancellation object here because orders can be cancelled in two ways (1) by the customer, so an email should be sent to the shop. (2) by the shop, so an email doesn't need to be sent. However the code for cancelling order happens in Order#cancel via the state machine. Rather than passing some sort of parameter into #cancel to indicate whether it is a customer or shop cancelled order it might be clearer to have a CustomerOrderCancellation object, there could be other differences between customer or shop cancelled orders in future maybe. --- app/controllers/spree/orders_controller.rb | 2 +- app/mailers/spree/order_mailer.rb | 10 ++++++ app/services/customer_order_cancellation.rb | 17 ++++++++++ .../cancel_email_for_shop.html.haml | 7 +++++ config/locales/en.yml | 5 +++ spec/mailers/order_mailer_spec.rb | 15 +++++++++ .../customer_order_cancellation_spec.rb | 31 +++++++++++++++++++ 7 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 app/services/customer_order_cancellation.rb create mode 100644 app/views/spree/order_mailer/cancel_email_for_shop.html.haml create mode 100644 spec/services/customer_order_cancellation_spec.rb diff --git a/app/controllers/spree/orders_controller.rb b/app/controllers/spree/orders_controller.rb index b343875e4d..bf4bd814e9 100644 --- a/app/controllers/spree/orders_controller.rb +++ b/app/controllers/spree/orders_controller.rb @@ -136,7 +136,7 @@ module Spree @order = Spree::Order.find_by!(number: params[:id]) authorize! :cancel, @order - if @order.cancel + if CustomerOrderCancellation.new(@order).call flash[:success] = I18n.t(:orders_your_order_has_been_cancelled) else flash[:error] = I18n.t(:orders_could_not_cancel) diff --git a/app/mailers/spree/order_mailer.rb b/app/mailers/spree/order_mailer.rb index 953a092bc0..b566f0878b 100644 --- a/app/mailers/spree/order_mailer.rb +++ b/app/mailers/spree/order_mailer.rb @@ -17,6 +17,16 @@ module Spree end end + def cancel_email_for_shop(order) + @order = order + I18n.with_locale valid_locale(@order.distributor.owner) do + subject = I18n.t('spree.order_mailer.cancel_email_for_shop.subject') + mail(to: @order.distributor.contact.email, + from: from_address, + subject: subject) + end + end + def confirm_email_for_customer(order_or_order_id, resend = false) @order = find_order(order_or_order_id) I18n.with_locale valid_locale(@order.user) do diff --git a/app/services/customer_order_cancellation.rb b/app/services/customer_order_cancellation.rb new file mode 100644 index 0000000000..2e3f51c6e6 --- /dev/null +++ b/app/services/customer_order_cancellation.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class CustomerOrderCancellation + def initialize(order) + @order = order + end + + def call + return unless order.cancel + + Spree::OrderMailer.cancel_email_for_shop(order).deliver_later + end + + private + + attr_reader :order +end diff --git a/app/views/spree/order_mailer/cancel_email_for_shop.html.haml b/app/views/spree/order_mailer/cancel_email_for_shop.html.haml new file mode 100644 index 0000000000..59602d691e --- /dev/null +++ b/app/views/spree/order_mailer/cancel_email_for_shop.html.haml @@ -0,0 +1,7 @@ +%p + = t('spree.order_mailer.cancel_email_for_shop.greeting', name: @order.distributor.name) + +%p + = t('spree.order_mailer.cancel_email_for_shop.intro', number: @order.number) + += link_to t('spree.order_mailer.cancel_email_for_shop.view_cancelled_order'), spree.admin_order_url(@order) diff --git a/config/locales/en.yml b/config/locales/en.yml index e2cb53cd22..a39ac74bd8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3631,6 +3631,11 @@ See the %{link} to find out more about %{sitename}'s features and to start using paid_order: "Your order was paid so %{distributor} has refunded the full amount" credit_order: "Your order was paid so your account has been credited" subject: "Cancellation of Order" + cancel_email_for_shop: + greeting: "Dear %{name}," + subject: "Cancellation of Order" + intro: "A customer has cancelled their order #%{number}." + view_cancelled_order: "View cancelled order" confirm_email: subject: "Order Confirmation" invoice_email: diff --git a/spec/mailers/order_mailer_spec.rb b/spec/mailers/order_mailer_spec.rb index 25a52802b6..4012518651 100644 --- a/spec/mailers/order_mailer_spec.rb +++ b/spec/mailers/order_mailer_spec.rb @@ -77,6 +77,21 @@ describe Spree::OrderMailer do end end + describe "#cancel_email_for_shop" do + let(:distributor) { create(:distributor_enterprise) } + let(:order) { create(:order, distributor: distributor, state: "canceled") } + let(:admin_order_link_href) { "href=\"#{spree.admin_order_url(order)}\"" } + let(:mail) { Spree::OrderMailer.cancel_email_for_shop(order) } + + it "sends an email to the distributor" do + expect(mail.to).to eq([distributor.contact.email]) + end + + it "includes a link to the cancelled order in admin" do + expect(mail.body).to match /#{admin_order_link_href}/ + end + end + describe "order confimation" do let(:bill_address) { create(:address) } let(:distributor_address) { create(:address, address1: "distributor address", city: 'The Shire', zipcode: "1234") } diff --git a/spec/services/customer_order_cancellation_spec.rb b/spec/services/customer_order_cancellation_spec.rb new file mode 100644 index 0000000000..8801aee5f5 --- /dev/null +++ b/spec/services/customer_order_cancellation_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe CustomerOrderCancellation do + let(:mail_mock) { double(:mailer_mock, deliver_later: true) } + before do + allow(Spree::OrderMailer).to receive(:cancel_email_for_shop) { mail_mock } + end + + context "when an order is cancelled successfully" do + it "notifies the distributor by email" do + order = create(:order, completed_at: Time.now, state: 'complete') + + CustomerOrderCancellation.new(order).call + + expect(Spree::OrderMailer).to have_received(:cancel_email_for_shop).with(order) + expect(mail_mock).to have_received(:deliver_later) + end + end + + context "when the order fails to cancel" do + it "doesn't notify the distributor by email" do + order = create(:order, state: 'canceled') + + CustomerOrderCancellation.new(order).call + + expect(Spree::OrderMailer).to_not have_received(:cancel_email_for_shop) + end + end +end