mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-01-24 20:36:49 +00:00
Add mail method and template for sending placement summary emails to shop owners
This commit is contained in:
@@ -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)
|
||||
|
||||
21
app/views/standing_order_mailer/_summary_detail.html.haml
Normal file
21
app/views/standing_order_mailer/_summary_detail.html.haml
Normal 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
|
||||
10
app/views/standing_order_mailer/_summary_overview.html.haml
Normal file
10
app/views/standing_order_mailer/_summary_overview.html.haml
Normal 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
|
||||
@@ -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
|
||||
= render 'shared/mailers/signoff'
|
||||
= render 'shared/mailers/social_and_contact'
|
||||
@@ -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 '
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user