Skip to content

Commit cc2d218

Browse files
authored
Merge pull request #2089 from govuk-forms/send-bounce-notifications-to-org-admins-after-7-days
Send bounce notifications to org admins after a week
2 parents fd6531f + 8e4acf8 commit cc2d218

11 files changed

Lines changed: 133 additions & 23 deletions

app/jobs/schedule_bounce_notifications_job.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ def perform
66
CurrentJobLoggingAttributes.job_class = self.class.name
77
CurrentJobLoggingAttributes.job_id = job_id
88

9-
SendBounceNotificationsJob.perform_later(bounced_on_date: Time.zone.yesterday.to_date)
9+
SendBounceNotificationsJob.perform_later(bounced_on_date: 1.day.ago.to_date, user_role: :group_admin)
10+
SendBounceNotificationsJob.perform_later(bounced_on_date: 8.days.ago.to_date, user_role: :organisation_admin)
1011
end
1112
end
Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
class SendBounceNotificationsJob < ApplicationJob
22
queue_as :bounce_notifications
33

4-
def perform(bounced_on_date:)
4+
def perform(bounced_on_date:, user_role:)
55
CloudWatchService.record_job_started_metric(self.class.name)
66
CurrentJobLoggingAttributes.job_class = self.class.name
77
CurrentJobLoggingAttributes.job_id = job_id
@@ -11,11 +11,15 @@ def perform(bounced_on_date:)
1111
form = deliveries.first.form
1212
group = Api::V2::GroupResource.find(form_id)
1313

14-
group.group_admin_users.each do |user|
15-
BounceNotificationMailer.bounce_notification_to_group_admins_email(form:, user:, deliveries:).deliver_now
14+
users = user_role == :organisation_admin ? group.organisation.organisation_admin_users : group.group_admin_users
15+
16+
users.each do |user|
17+
BounceNotificationMailer.bounce_notification_email(
18+
form:, group_name: group.name, user:, user_role:, deliveries:, bounced_on_date:,
19+
).deliver_now
1620
end
1721

18-
Rails.logger.info "Sent bounce notifications to group admins for bounced deliveries on #{bounced_on_date.strftime('%-d %B %Y')} for form #{form_id}"
22+
Rails.logger.info "Sent bounce notifications to #{user_role.to_s.gsub('_', ' ')} users for bounced deliveries on #{bounced_on_date.strftime('%-d %B %Y')} for form #{form_id}"
1923
end
2024
end
2125
end

app/mailers/bounce_notification_mailer.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
class BounceNotificationMailer < GovukNotifyRails::Mailer
22
include NotifyUtils
33

4-
def bounce_notification_to_group_admins_email(form:, user:, deliveries:)
4+
def bounce_notification_email(form:, group_name:, user:, user_role:, deliveries:, bounced_on_date:)
55
set_template(Settings.govuk_notify.bounce_notification_to_group_admins_template_id)
66

77
# We're assuming that all bounces are for the same reason
@@ -21,6 +21,9 @@ def bounce_notification_to_group_admins_email(form:, user:, deliveries:)
2121
hard_bounce: make_notify_boolean(hard_bounce),
2222
soft_bounce: make_notify_boolean(!hard_bounce),
2323
deadline_date: deadline_date(deliveries),
24+
is_organisation_admin: make_notify_boolean(user_role == :organisation_admin),
25+
is_group_admin: make_notify_boolean(user_role == :group_admin),
26+
contacted_group_admins_paragraph: contacted_group_admins_paragraph(user_role:, group_name:, bounced_on_date:),
2427
)
2528

2629
mail(to: user.email)
@@ -64,4 +67,11 @@ def deadline_date(deliveries)
6467
deadline_date_time = earliest_submission_date_time + Settings.submissions.maximum_retention_seconds.seconds
6568
deadline_date_time.strftime("%-d %B %Y")
6669
end
70+
71+
def contacted_group_admins_paragraph(user_role:, group_name:, bounced_on_date:)
72+
return "" if user_role == :group_admin
73+
74+
contacted_date = (bounced_on_date + 1.day).strftime("%-d %B %Y")
75+
I18n.t("mailer.bounce_notification.contacted_group_admins_paragraph", group_name:, contacted_date:)
76+
end
6777
end

config/locales/cy.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@ cy:
472472
bounced_daily_batch: Daily batch sent at %{sent_at_time} on %{sent_at_date}
473473
bounced_submission: "%{submission_reference} at %{sent_at_time} on %{sent_at_date}"
474474
bounced_weekly_batch: Weekly batch sent at %{sent_at_time} on %{sent_at_date}
475+
contacted_group_admins_paragraph: We emailed the group admins of the group the form is in, “%{group_name}”, on %{contacted_date}. We have not received a response.
475476
cannot_reply:
476477
contact_form_filler_html: "<p>If you need to contact the person who completed this form, you’ll need to contact them directly.</p>"
477478
contact_form_filler_plain: If you need to contact the person who completed this form, you’ll need to contact them directly.

config/locales/en.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@ en:
472472
bounced_daily_batch: Daily batch sent at %{sent_at_time} on %{sent_at_date}
473473
bounced_submission: "%{submission_reference} at %{sent_at_time} on %{sent_at_date}"
474474
bounced_weekly_batch: Weekly batch sent at %{sent_at_time} on %{sent_at_date}
475+
contacted_group_admins_paragraph: We emailed the group admins of the group the form is in, “%{group_name}”, on %{contacted_date}. We have not received a response.
475476
cannot_reply:
476477
contact_form_filler_html: "<p>If you need to contact the person who completed this form, you’ll need to contact them directly.</p>"
477478
contact_form_filler_plain: If you need to contact the person who completed this form, you’ll need to contact them directly.

config/recurring.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ production:
1717
schedule_weekly_batch_deliveries_job:
1818
class: ScheduleWeeklyBatchDeliveriesJob
1919
schedule: every Monday at 2:15am Europe/London
20+
schedule_bounce_notifications_job:
21+
class: ScheduleBounceNotificationsJob
22+
schedule: every day at 2:30am Europe/London
2023
development:
2124
delete_submissions_job:
2225
class: DeleteSubmissionsJob

config/settings.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ govuk_notify:
2727
form_submission_email_template_id: 427eb8bc-ce0d-40a3-bf54-d76e8c3ec916
2828
form_filler_confirmation_email_template_id: 2d1f36dc-9799-43dd-8673-b631f9e0b4a5
2929
form_filler_confirmation_email_welsh_template_id: b297c8f1-419e-4af4-b067-b8cad6364daf
30-
bounce_notification_to_group_admins_template_id: 9a93a110-c97d-4b51-87f3-a7ec5b9daddc
30+
bounce_notification_to_group_admins_template_id: 8062ca3f-3a21-4e1b-847f-cfa45c48ed87
3131

3232
# Configuration for Sentry
3333
# Sentry will only initialise if dsn is set to some other value

spec/factories/groups.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
end
1414

1515
organisation do
16-
{
16+
DataStruct.new({
1717
name: organisation_name,
18-
admin_users: build_list(:admin_user, organisation_admin_users_count),
19-
}
18+
organisation_admin_users: build_list(:admin_user, organisation_admin_users_count),
19+
})
2020
end
2121
end
2222
end
Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
require "rails_helper"
22

33
RSpec.describe ScheduleBounceNotificationsJob do
4-
it "schedules a SendBounceNotifications job with yesterday's date" do
5-
expect(SendBounceNotificationsJob).to receive(:perform_later).with(bounced_on_date: Time.zone.yesterday.to_date)
4+
before do
5+
allow(SendBounceNotificationsJob).to receive(:perform_later)
66

7-
described_class.perform_now
7+
travel_to Time.zone.local(2026, 5, 12, 15, 0, 0) do
8+
described_class.perform_now
9+
end
10+
end
11+
12+
it "schedules a SendBounceNotifications job to send to group admins with yesterday's date" do
13+
expect(SendBounceNotificationsJob).to have_received(:perform_later)
14+
.with({ bounced_on_date: Date.new(2026, 5, 11), user_role: :group_admin })
15+
.once
16+
end
17+
18+
it "schedules a SendBounceNotifications job to send to organisation admins with a date of 8 days ago" do
19+
expect(SendBounceNotificationsJob).to have_received(:perform_later)
20+
.with({ bounced_on_date: Date.new(2026, 5, 4), user_role: :organisation_admin })
21+
.once
822
end
923
end

spec/jobs/send_bounce_notifications_job_spec.rb

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
require "rails_helper"
22

3-
RSpec.describe SendBounceNotificationsJob do
3+
RSpec.describe SendBounceNotificationsJob, :capture_logging do
44
include ActiveSupport::Testing::TimeHelpers
55
include ActiveJob::TestHelper
66

77
let(:bounced_on_date) { Date.new(2026, 5, 6) }
8+
let(:user_role) { :group_admin }
89

910
let(:form_with_bounces) { build :form }
1011
let(:other_form_with_bounces) { build :form }
@@ -24,10 +25,10 @@
2425
end
2526
end
2627

27-
context "when there are multiple forms with bounces on the date", :capture_logging do
28+
context "when there are multiple forms with bounces on the date" do
2829
before do
2930
create_list :delivery, 2, :bounced, :daily_scheduled_delivery, form_id: other_form_with_bounces.id, failed_at: Time.zone.local(2026, 5, 6, 22, 59, 0)
30-
described_class.perform_now(bounced_on_date:)
31+
described_class.perform_now(bounced_on_date:, user_role:)
3132
end
3233

3334
it "sends an email per form with bounced submissions" do
@@ -42,21 +43,22 @@
4243
expect(log_lines).to include(
4344
hash_including(
4445
"level" => "INFO",
45-
"message" => "Sent bounce notifications to group admins for bounced deliveries on 6 May 2026 for form #{form_with_bounces.form_id}",
46+
"message" => "Sent bounce notifications to group admin users for bounced deliveries on 6 May 2026 for form #{form_with_bounces.form_id}",
4647
),
4748
hash_including(
4849
"level" => "INFO",
49-
"message" => "Sent bounce notifications to group admins for bounced deliveries on 6 May 2026 for form #{other_form_with_bounces.form_id}",
50+
"message" => "Sent bounce notifications to group admin users for bounced deliveries on 6 May 2026 for form #{other_form_with_bounces.form_id}",
5051
),
5152
)
5253
end
5354
end
5455

55-
context "when the group has multiple group admins" do
56+
context "when notifying the group admins" do
57+
let(:user_role) { :group_admin }
5658
let(:group) { build :group, group_admin_users_count: 2 }
5759

5860
before do
59-
described_class.perform_now(bounced_on_date:)
61+
described_class.perform_now(bounced_on_date:, user_role:)
6062
end
6163

6264
it "sends an email to each group admin" do
@@ -66,5 +68,40 @@
6668
group.group_admin_users.second.email,
6769
)
6870
end
71+
72+
it "logs that it sent the notifications to the group admins" do
73+
expect(log_lines).to include(
74+
hash_including(
75+
"level" => "INFO",
76+
"message" => "Sent bounce notifications to group admin users for bounced deliveries on 6 May 2026 for form #{form_with_bounces.form_id}",
77+
),
78+
)
79+
end
80+
end
81+
82+
context "when notifying the organisation admins" do
83+
let(:user_role) { :organisation_admin }
84+
let(:group) { build :group, group_admin_users_count: 1, organisation_admin_users_count: 2 }
85+
86+
before do
87+
described_class.perform_now(bounced_on_date:, user_role:)
88+
end
89+
90+
it "sends an email to each organisation admin" do
91+
expect(ActionMailer::Base.deliveries.size).to eq 2
92+
expect(ActionMailer::Base.deliveries.map(&:to).flatten).to contain_exactly(
93+
group.organisation.organisation_admin_users.first.email,
94+
group.organisation.organisation_admin_users.second.email,
95+
)
96+
end
97+
98+
it "logs that it sent the notifications to the organisation admins" do
99+
expect(log_lines).to include(
100+
hash_including(
101+
"level" => "INFO",
102+
"message" => "Sent bounce notifications to organisation admin users for bounced deliveries on 6 May 2026 for form #{form_with_bounces.form_id}",
103+
),
104+
)
105+
end
69106
end
70107
end

0 commit comments

Comments
 (0)