Add mail method and template for sending placement summary emails to shop owners

This commit is contained in:
Rob Harrington
2017-11-24 11:36:36 +11:00
parent cf30b7c883
commit 13d01df4d8
8 changed files with 255 additions and 1 deletions

View File

@@ -26,6 +26,14 @@ class StandingOrderMailer < Spree::BaseMailer
send_mail(order)
end
def placement_summary_email(summary)
@shop = Enterprise.find(summary.shop_id)
@summary = summary
mail(:to => @shop.email,
:from => from_address,
:subject => "#{Spree::Config[:site_name]} #{t('standing_order_mailer.placement_summary_email.subject')}")
end
private
def send_mail(order)

View File

@@ -0,0 +1,21 @@
- summary.issues.each do |type, messages|
- orders = summary.orders_affected_by(type)
%h4= t(".#{type}.title", count: orders.count)
%p= t(".#{type}.explainer")
- separator = messages.values.any? ? ": " : ", "
- orders.each_with_index do |order, i|
%a{ href: spree.order_url(order) }>= order.number
= separator if messages.values.any? || i < orders.count - 1
- if messages.values.any?
= messages[order.id] || t(".no_message_provided")
%br
- if summary.unrecorded_ids.any?
- orders = summary.orders_affected_by(:other)
%h4= t(".other.title", count: orders.count)
%p= t(".other.explainer")
- orders.each_with_index do |order, i|
%a{ href: spree.order_url(order) }>= order.number
= ", " if i < orders.count - 1

View File

@@ -0,0 +1,10 @@
%p.callout
= t(".total", count: summary.order_count)
- if summary.issue_count == 0
= t(".success_all")
- elsif summary.issue_count < summary.order_count
= t(".success_some", count: summary.success_count)
- else
= t(".success_zero")
= t(".issues") if summary.issue_count > 0

View File

@@ -0,0 +1,22 @@
%table.social.white-bg{:width => "100%"}
%tr
%td
%table.column{:align => "left"}
%tr
%td
%h3
= t(".greeting", name: @shop.contact)
%h4
= t(".intro")
%table.column{:align => "left"}
%tr
%td{:align => "right"}
%img.float-right{:src => "#{@shop.logo.url(:medium)}"}/
%span.clear
= render 'summary_overview', summary: @summary
= render 'summary_detail', summary: @summary
%p &nbsp;
= render 'shared/mailers/signoff'
= render 'shared/mailers/social_and_contact'

View File

@@ -107,6 +107,35 @@ en:
producer_mailer:
order_cycle:
subject: "Order cycle report for %{producer}"
standing_order_mailer:
placement_summary_email:
subject: Your Standing Order summary
greeting: "Hi %{name},"
intro: "Below is a summary of the standing orders that have just been placed for %{shop}."
summary_overview:
total: A total of %{count} standing orders were marked for automatic placement.
success_zero: Of these, none were processed successfully.
success_some: Of these, %{count} were processed successfully.
success_all: All were processed successfully.
issues: Details of the issues encountered are provided below.
summary_detail:
no_message_provided: No error message provided
changes:
title: Insufficient Stock (%{count} orders)
explainer: These orders were processed but insufficient stock was available for some requested items
empty:
title: No stock (%{count} orders)
explainer: These orders were unable to be processed because no stock was available for any requested items
complete:
title: Already Processed (%{count} orders)
explainer: These orders were already marked as complete, and were therefore left untouched
failure:
title: Failed To Process (%{count} orders)
explainer: Automatic processing of these orders failed due to an error. The error has been listed where possible.
other:
title: Other failure (%{count} orders)
explainer: Automatic processing of these orders failed for an unknown reason. This should not occur, please contact us if you are seeing this.
home: "OFN"
title: Open Food Network
welcome_to: 'Welcome to '

View File

@@ -33,5 +33,17 @@ module OpenFoodNetwork
def issue_count
(@order_ids - @success_ids).count
end
def orders_affected_by(type)
case type
when :other then Spree::Order.where(id: unrecorded_ids)
else Spree::Order.where(id: issues[type].keys)
end
end
def unrecorded_ids
recorded_ids = issues.values.map(&:keys).flatten
@order_ids - @success_ids - recorded_ids
end
end
end

View File

@@ -69,7 +69,7 @@ module OpenFoodNetwork
end
end
describe "#lissue_count" do
describe "#issue_count" do
let(:order_ids) { [1,3,5,7,9] }
let(:success_ids) { [1,2,3,4,5] }
@@ -79,5 +79,47 @@ module OpenFoodNetwork
expect(summary.issue_count).to be 2 # 7 & 9
end
end
describe "#orders_affected_by" do
let(:order1) { create(:order) }
let(:order2) { create(:order) }
before do
allow(summary).to receive(:unrecorded_ids) { [order1.id] }
allow(summary).to receive(:issues) { { failure: { order2.id => "A message" } } }
end
context "when the issue type is :other" do
let(:orders) { summary.orders_affected_by(:other) }
it "returns orders specified by unrecorded_ids" do
expect(orders).to include order1
expect(orders).to_not include order2
end
end
context "when the issue type is :other" do
let(:orders) { summary.orders_affected_by(:failure) }
it "returns orders specified by the relevant issue hash" do
expect(orders).to include order2
expect(orders).to_not include order1
end
end
end
describe "#unrecorded_ids" do
let(:issues) { { type: { 7 => "message", 8 => "message" } } }
before do
summary.instance_variable_set(:@order_ids, [1,3,5,7,9])
summary.instance_variable_set(:@success_ids, [1,2,3,4,5])
summary.instance_variable_set(:@issues, issues)
end
it "returns order_ids that are not marked as an issue or a success" do
expect(summary.unrecorded_ids).to eq [9]
end
end
end
end

View File

@@ -103,4 +103,114 @@ describe StandingOrderMailer do
expect(body).to include "This is a payment failure error"
end
end
describe "order placement summary" do
let!(:shop) { create(:enterprise) }
let!(:summary) { double(:summary, shop_id: shop.id) }
let(:body) { strip_tags(StandingOrderMailer.deliveries.last.body.encoded) }
let(:scope) { "standing_order_mailer" }
before { allow(summary).to receive(:unrecorded_ids) { [] } }
context "when no issues were encountered while processing standing orders" do
before do
allow(summary).to receive(:order_count) { 37 }
allow(summary).to receive(:issue_count) { 0 }
allow(summary).to receive(:issues) { {} }
StandingOrderMailer.placement_summary_email(summary).deliver
end
it "sends the email, which notifies the enterprise that all orders were successfully processed" do
expect(body).to include I18n.t("#{scope}.placement_summary_email.intro")
expect(body).to include I18n.t("#{scope}.summary_overview.total", count: 37)
expect(body).to include I18n.t("#{scope}.summary_overview.success_all")
expect(body).to_not include I18n.t("#{scope}.summary_overview.issues")
end
end
context "when some issues were encountered while processing standing orders" do
let(:order1) { double(:order, id: 1, number: "R123456", to_s: "R123456") }
let(:order2) { double(:order, id: 2, number: "R654321", to_s: "R654321") }
before do
allow(summary).to receive(:order_count) { 37 }
allow(summary).to receive(:success_count) { 35 }
allow(summary).to receive(:issue_count) { 2 }
allow(summary).to receive(:issues) { { failure: { 1 => "Some Error Message", 2 => nil } } }
allow(summary).to receive(:orders_affected_by) { [order1, order2] }
end
context "when no unrecorded issues are present" do
it "sends the email, which notifies the enterprise that some issues were encountered" do
StandingOrderMailer.placement_summary_email(summary).deliver
expect(body).to include I18n.t("#{scope}.placement_summary_email.intro")
expect(body).to include I18n.t("#{scope}.summary_overview.total", count: 37)
expect(body).to include I18n.t("#{scope}.summary_overview.success_some", count: 35)
expect(body).to include I18n.t("#{scope}.summary_overview.issues")
expect(body).to include I18n.t("#{scope}.summary_detail.failure.title", count: 2)
expect(body).to include I18n.t("#{scope}.summary_detail.failure.explainer")
# Lists orders for which an error was encountered
expect(body).to include order1.number
expect(body).to include order2.number
# Reports error messages provided by the summary, or default if none provided
expect(body).to include "Some Error Message"
expect(body).to include I18n.t("#{scope}.summary_detail.no_message_provided")
end
end
context "when some undocumented orders are present" do
let(:order3) { double(:order, id: 3, number: "R333333", to_s: "R333333") }
let(:order4) { double(:order, id: 4, number: "R444444", to_s: "R444444") }
before do
allow(summary).to receive(:unrecorded_ids) { [3, 4] }
end
it "sends the email, which notifies the enterprise that some issues were encountered" do
expect(summary).to receive(:orders_affected_by).with(:other) { [order3, order4] }
StandingOrderMailer.placement_summary_email(summary).deliver
expect(body).to include I18n.t("#{scope}.summary_detail.failure.title", count: 2)
expect(body).to include I18n.t("#{scope}.summary_detail.failure.explainer")
expect(body).to include I18n.t("#{scope}.summary_detail.other.title", count: 2)
expect(body).to include I18n.t("#{scope}.summary_detail.other.explainer")
# Lists orders for which no error or success was recorded
expect(body).to include order3.number
expect(body).to include order4.number
end
end
end
context "when no standing orders were processed successfully" do
let(:order1) { double(:order, id: 1, number: "R123456", to_s: "R123456") }
let(:order2) { double(:order, id: 2, number: "R654321", to_s: "R654321") }
before do
allow(summary).to receive(:order_count) { 2 }
allow(summary).to receive(:success_count) { 0 }
allow(summary).to receive(:issue_count) { 2 }
allow(summary).to receive(:issues) { { changes: { 1 => nil, 2 => nil } } }
allow(summary).to receive(:orders_affected_by) { [order1, order2] }
StandingOrderMailer.placement_summary_email(summary).deliver
end
it "sends the email, which notifies the enterprise that some issues were encountered" do
expect(body).to include I18n.t("#{scope}.placement_summary_email.intro")
expect(body).to include I18n.t("#{scope}.summary_overview.total", count: 2)
expect(body).to include I18n.t("#{scope}.summary_overview.success_zero")
expect(body).to include I18n.t("#{scope}.summary_overview.issues")
expect(body).to include I18n.t("#{scope}.summary_detail.changes.title", count: 2)
expect(body).to include I18n.t("#{scope}.summary_detail.changes.explainer")
# Lists orders for which an error was encountered
expect(body).to include order1.number
expect(body).to include order2.number
# No error messages reported when non provided
expect(body).to_not include I18n.t("#{scope}.summary_detail.no_message_provided")
end
end
end
end