diff --git a/app/controllers/admin/organizations_controller.rb b/app/controllers/admin/organizations_controller.rb
index 65771b5e1b..a26861fa6e 100644
--- a/app/controllers/admin/organizations_controller.rb
+++ b/app/controllers/admin/organizations_controller.rb
@@ -73,6 +73,7 @@ def organization_params
params.require(:organization)
.permit(:name, :street, :city, :state, :zipcode, :email, :url, :logo, :intake_location, :default_email_text, :account_request_id, :bank_is_set_up,
:reminder_schedule_definition, :deadline_day,
+ :deadline_reminders_enabled, :distribution_reminders_enabled,
users_attributes: %i(name email organization_admin), account_request_attributes: %i(ndbn_member_id id))
end
diff --git a/app/controllers/distributions_controller.rb b/app/controllers/distributions_controller.rb
index afe636a89e..6328d0b80b 100644
--- a/app/controllers/distributions_controller.rb
+++ b/app/controllers/distributions_controller.rb
@@ -293,7 +293,7 @@ def send_notification(org, dist, subject: 'Your Distribution', distribution_chan
end
def schedule_reminder_email(distribution)
- return if distribution.past? || !distribution.partner.send_reminders
+ return if distribution.past? || !distribution.partner.send_reminders || !distribution.organization.distribution_reminders_enabled
DistributionMailer.reminder_email(distribution.id).deliver_later(wait_until: distribution.issued_at - 1.day)
end
diff --git a/app/controllers/organizations_controller.rb b/app/controllers/organizations_controller.rb
index 23677bc92f..8bc30a4b3d 100644
--- a/app/controllers/organizations_controller.rb
+++ b/app/controllers/organizations_controller.rb
@@ -102,6 +102,7 @@ def organization_params
:ytd_on_distribution_printout, :one_step_partner_invite,
:hide_value_columns_on_receipt, :hide_package_column_on_receipt,
:signature_for_distribution_pdf, :receive_email_on_requests,
+ :deadline_reminders_enabled, :distribution_reminders_enabled,
:bank_is_set_up,
:include_in_kind_values_in_exported_files,
:include_packages_in_distribution_export,
diff --git a/app/javascript/controllers/reminder_toggle_controller.js b/app/javascript/controllers/reminder_toggle_controller.js
new file mode 100644
index 0000000000..54085ebd56
--- /dev/null
+++ b/app/javascript/controllers/reminder_toggle_controller.js
@@ -0,0 +1,23 @@
+import { Controller } from "@hotwired/stimulus"
+
+/*
+ * ReminderToggleController shows/hides dependent reminder-schedule fields based
+ * on the selected value of a Yes/No radio-button group. Used on the organization
+ * settings form so the reminder schedule and reminder email text are only shown
+ * when "Send monthly deadline reminder emails?" is set to Yes.
+ */
+export default class extends Controller {
+ static targets = ["source", "dependentField"]
+
+ connect() {
+ this.toggle()
+ }
+
+ toggle() {
+ const selected = this.sourceTargets.find((input) => input.checked)
+ const show = selected?.value === "true"
+ this.dependentFieldTargets.forEach((field) => {
+ field.classList.toggle("d-none", !show)
+ })
+ }
+}
diff --git a/app/mailers/distribution_mailer.rb b/app/mailers/distribution_mailer.rb
index d1afad34f0..c3148495ad 100644
--- a/app/mailers/distribution_mailer.rb
+++ b/app/mailers/distribution_mailer.rb
@@ -45,6 +45,7 @@ def reminder_email(distribution_id)
requester_email = distribution.request ? distribution.request.requester.email : @partner.email
return if @distribution.past? || !@partner.send_reminders || @partner.deactivated?
+ return unless @distribution.organization.distribution_reminders_enabled
mail(to: requester_email, cc: @partner.email, subject: "#{@partner.name} Distribution Reminder")
end
diff --git a/app/mailers/reminder_deadline_mailer.rb b/app/mailers/reminder_deadline_mailer.rb
index 5df6f382af..009f9bfdcf 100644
--- a/app/mailers/reminder_deadline_mailer.rb
+++ b/app/mailers/reminder_deadline_mailer.rb
@@ -3,6 +3,11 @@ class ReminderDeadlineMailer < ApplicationMailer
def notify_deadline(partner)
@partner = partner
@organization = partner.organization
+
+ # Do not send even if the partner (or its group) has reminders on when the
+ # organization has turned monthly deadline reminders off.
+ return unless @organization.deadline_reminders_enabled
+
@deadline = deadline_date(partner)
reminder_email_text = @organization.reminder_email_text
@reminder_email_text_interpolated = TextInterpolatorService.new(reminder_email_text.body.to_s, {
diff --git a/app/models/organization.rb b/app/models/organization.rb
index d6020c521e..e60c03d35e 100644
--- a/app/models/organization.rb
+++ b/app/models/organization.rb
@@ -6,8 +6,10 @@
# bank_is_set_up :boolean default(FALSE), not null
# city :string
# deadline_day :integer
+# deadline_reminders_enabled :boolean default(FALSE), not null
# default_storage_location :integer
# distribute_monthly :boolean default(FALSE), not null
+# distribution_reminders_enabled :boolean default(FALSE), not null
# email :string
# enable_child_based_requests :boolean default(TRUE), not null
# enable_individual_requests :boolean default(TRUE), not null
diff --git a/app/services/partners/fetch_partners_to_remind_now_service.rb b/app/services/partners/fetch_partners_to_remind_now_service.rb
index ed96f1f851..8bffd579c2 100644
--- a/app/services/partners/fetch_partners_to_remind_now_service.rb
+++ b/app/services/partners/fetch_partners_to_remind_now_service.rb
@@ -4,9 +4,10 @@ def fetch
current_day = Time.current
deactivated_status = ::Partner.statuses[:deactivated]
- partners_with_group_reminders = ::Partner.left_joins(:partner_group)
+ partners_with_group_reminders = ::Partner.left_joins(:partner_group, :organization)
.where.not(partner_groups: {reminder_schedule_definition: nil})
.where.not(partner_groups: {deadline_day: nil})
+ .where(organizations: {deadline_reminders_enabled: true})
.where.not(status: deactivated_status)
# where partner groups have reminder schedule match
@@ -17,6 +18,7 @@ def fetch
partners_with_only_organization_reminders = ::Partner.left_joins(:partner_group, :organization)
.where(partner_groups: {reminder_schedule_definition: nil})
.where(send_reminders: true)
+ .where(organizations: {deadline_reminders_enabled: true})
.where.not(organizations: {deadline_day: nil})
.where.not(organizations: {reminder_schedule_definition: nil})
.where.not(status: deactivated_status)
diff --git a/app/views/admin/organizations/new.html.erb b/app/views/admin/organizations/new.html.erb
index c6157177d6..9ef67e148e 100644
--- a/app/views/admin/organizations/new.html.erb
+++ b/app/views/admin/organizations/new.html.erb
@@ -47,6 +47,8 @@
<%= f.input :city %>
<%= f.input :state, collection: us_states, class: "form-control", placeholder: "state" %>
<%= f.input :zipcode %>
+ <%= f.input :deadline_reminders_enabled, label: 'Send monthly deadline reminder emails to partners?', as: :radio_buttons, collection: [[true, 'Yes'], [false, 'No']], label_method: :second, value_method: :first %>
+ <%= f.input :distribution_reminders_enabled, label: 'Send day-before distribution reminder emails to partners?', as: :radio_buttons, collection: [[true, 'Yes'], [false, 'No']], label_method: :second, value_method: :first %>
<%= render 'shared/deadline_day_fields', parent_form: f, parent_object: @organization %>
<%= f.simple_fields_for :account_request do |account_request| %>
<%= account_request.input :ndbn_member, label: 'NDBN Membership', wrapper: :input_group do %>
diff --git a/app/views/distributions/_form.html.erb b/app/views/distributions/_form.html.erb
index 669e2de9b5..ea43d69faa 100644
--- a/app/views/distributions/_form.html.erb
+++ b/app/views/distributions/_form.html.erb
@@ -23,7 +23,11 @@
<%= f.input :issued_at, as: :datetime, ampm: true, minute_step: 15, label: "Distribution date and time", html5: true, :input_html => { :value => date_place_holder&.strftime("%Y-%m-%dT%0k:%M")} %>
- <%= f.input :reminder_email_enabled, as: :boolean, checked_value: true, unchecked_value: false, label: "Send email reminder the day before?" %>
+ <% if current_organization.distribution_reminders_enabled %>
+ <%= f.input :reminder_email_enabled, as: :boolean, checked_value: true, unchecked_value: false, label: "Send email reminder the day before?" %>
+ <% else %>
+ <%= render 'shared/reminders_disabled_notice', reminder_type: :distribution %>
+ <% end %>
<%= f.input :agency_rep, label: "Agency representative" %>
diff --git a/app/views/organizations/_details.html.erb b/app/views/organizations/_details.html.erb
index bf6ba21d48..a9d7b6758c 100644
--- a/app/views/organizations/_details.html.erb
+++ b/app/views/organizations/_details.html.erb
@@ -174,37 +174,51 @@
Other emails
-
Reminder emails are sent
+
Send monthly deadline reminder emails to partners?
- <%= fa_icon "calendar" %>
- <%= @organization.reminder_schedule&.show_description.blank? ? 'Not defined' : @organization.reminder_schedule&.show_description %>
- <% if @organization.reminder_schedule.valid? %>
-
- <%= "Your next reminder date is #{@organization.reminder_schedule.next_occurrence&.strftime('%a %b %d %Y')}." %>
-
- <% end %>
+ <%= humanize_boolean(@organization.deadline_reminders_enabled) %>
-
Deadline day in reminder email
+
Send day-before distribution reminder emails to partners?
- <%= fa_icon "calendar" %>
- <%= @organization.deadline_day.blank? ? 'Not defined' : "The #{@organization.deadline_day.ordinalize} after the reminder." %>
-
- <% if @organization.deadline_day && @organization.reminder_schedule.valid? %>
- <%= "The deadline on your next reminder email will be #{DeadlineService.new(
- deadline_day: @organization.deadline_day,
- today: @organization.reminder_schedule.next_occurrence).next_deadline&.strftime('%a %b %d %Y')}." %>
- <% end %>
-
+ <%= humanize_boolean(@organization.distribution_reminders_enabled) %>
-
-
Additional text for reminder email
-
- <%= @organization.reminder_email_text.presence || 'Not defined' %>
+ <% if @organization.deadline_reminders_enabled %>
+
+
Reminder emails are sent
+
+ <%= fa_icon "calendar" %>
+ <%= @organization.reminder_schedule&.show_description.blank? ? 'Not defined' : @organization.reminder_schedule&.show_description %>
+ <% if @organization.reminder_schedule.valid? %>
+
+ <%= "Your next reminder date is #{@organization.reminder_schedule.next_occurrence&.strftime('%a %b %d %Y')}." %>
+
+ <% end %>
+
-
+
+
Deadline day in reminder email
+
+ <%= fa_icon "calendar" %>
+ <%= @organization.deadline_day.blank? ? 'Not defined' : "The #{@organization.deadline_day.ordinalize} after the reminder." %>
+
+ <% if @organization.deadline_day && @organization.reminder_schedule.valid? %>
+ <%= "The deadline on your next reminder email will be #{DeadlineService.new(
+ deadline_day: @organization.deadline_day,
+ today: @organization.reminder_schedule.next_occurrence).next_deadline&.strftime('%a %b %d %Y')}." %>
+ <% end %>
+
+
+
+
+
Additional text for reminder email
+
+ <%= @organization.reminder_email_text.presence || 'Not defined' %>
+
+
+ <% end %>
Distribution email content
diff --git a/app/views/organizations/edit.html.erb b/app/views/organizations/edit.html.erb
index 3187584045..2b29e3872d 100644
--- a/app/views/organizations/edit.html.erb
+++ b/app/views/organizations/edit.html.erb
@@ -149,11 +149,17 @@
Other emails
- <%= render 'shared/deadline_day_fields', parent_form: f, parent_object: current_organization %>
- <% default_reminder_email_text_hint = "You can use the variable
%{partner_name} to include the partner's name in the message." %>
- <%= f.input :reminder_email_text, label: "Additional text for reminder email", hint: default_reminder_email_text_hint.html_safe do %>
- <%= f.rich_text_area :reminder_email_text, placeholder: 'Enter reminder email content...' %>
- <% end %>
+
+ <%= f.input :deadline_reminders_enabled, label: 'Send monthly deadline reminder emails to partners?', as: :radio_buttons, collection: [[true, 'Yes'], [false, 'No']], label_method: :second, value_method: :first, input_html: { data: { 'reminder-toggle-target': 'source', action: 'change->reminder-toggle#toggle' } } %>
+ <%= f.input :distribution_reminders_enabled, label: 'Send day-before distribution reminder emails to partners?', as: :radio_buttons, collection: [[true, 'Yes'], [false, 'No']], label_method: :second, value_method: :first %>
+
+ <%= render 'shared/deadline_day_fields', parent_form: f, parent_object: current_organization %>
+ <% default_reminder_email_text_hint = "You can use the variable %{partner_name} to include the partner's name in the message." %>
+ <%= f.input :reminder_email_text, label: "Additional text for reminder email", hint: default_reminder_email_text_hint.html_safe do %>
+ <%= f.rich_text_area :reminder_email_text, placeholder: 'Enter reminder email content...' %>
+ <% end %>
+
+
<% default_email_text_hint = "You can use the variables
%{partner_name},
%{delivery_method},
%{distribution_date}, and
%{comment} to include the partner's name, delivery method, distribution date, and comments sent in the request." %>
<%= f.input :default_email_text, label: "Distribution email content", hint: default_email_text_hint.html_safe do %>
<%= f.rich_text_area :default_email_text, placeholder: 'Enter distribution email content...' %>
diff --git a/app/views/partner_groups/_form.html.erb b/app/views/partner_groups/_form.html.erb
index 57ec911265..1163ffdc33 100644
--- a/app/views/partner_groups/_form.html.erb
+++ b/app/views/partner_groups/_form.html.erb
@@ -20,18 +20,24 @@
<%= f.association :item_categories, collection: @item_categories, as: :check_boxes, label: '' %>
-
+
Do you want to send deadline reminders to them every month?
- <%= f.input :send_reminders, as: :boolean, label: "Yes", input_html: { data: { action: 'click->checkbox-with-nested-element#toggleNestedElementVisibility', 'checkbox-with-nested-element-target': 'checkbox' } } %>
+ <% if current_organization.deadline_reminders_enabled %>
+
+ <%= f.input :send_reminders, as: :boolean, label: "Yes", input_html: { data: { action: 'click->checkbox-with-nested-element#toggleNestedElementVisibility', 'checkbox-with-nested-element-target': 'checkbox' } } %>
-
-
-
That's great!
-
You must fill out the details below to send reminders.
-
+
+
+
That's great!
+
You must fill out the details below to send reminders.
+
- <%= render 'shared/deadline_day_fields', parent_form: f, parent_object: @partner_group %>
-
+ <%= render 'shared/deadline_day_fields', parent_form: f, parent_object: @partner_group %>
+
+
+ <% else %>
+ <%= render 'shared/reminders_disabled_notice', reminder_type: :deadline %>
+ <% end %>
diff --git a/app/views/partners/_form.html.erb b/app/views/partners/_form.html.erb
index fe8ef9efa1..f6529c2883 100644
--- a/app/views/partners/_form.html.erb
+++ b/app/views/partners/_form.html.erb
@@ -41,6 +41,9 @@
Reminder
Please note that reminders will be sent out according to the settings of their partner group OR organization. This won't work unless you've set up at least one.
+ <% unless current_organization.deadline_reminders_enabled || current_organization.distribution_reminders_enabled %>
+ <%= render 'shared/reminders_disabled_notice', reminder_type: :all %>
+ <% end %>
<%= f.input :quota, label: "Quota", wrapper: :input_group do %>
diff --git a/app/views/shared/_reminders_disabled_notice.html.erb b/app/views/shared/_reminders_disabled_notice.html.erb
new file mode 100644
index 0000000000..114b41fa18
--- /dev/null
+++ b/app/views/shared/_reminders_disabled_notice.html.erb
@@ -0,0 +1,27 @@
+<%#
+ Shown in place of reminder-configuration fields when the organization has
+ turned the relevant reminder emails off on its settings page.
+
+ Locals:
+ reminder_type - :deadline, :distribution, or :all
+%>
+
+
+ <% case reminder_type %>
+ <% when :distribution %>
+ Day-before distribution reminder emails are turned off
+ <% when :all %>
+ Reminder emails are turned off for your organization
+ <% else %>
+ Monthly deadline reminder emails are turned off
+ <% end %>
+
+
+ Your organization has turned this off, so these emails will not be sent.
+ <% if can_administrate? %>
+ <%= link_to "Change this in your organization settings", edit_organization_path %>.
+ <% else %>
+ Contact your organization administrator to change this.
+ <% end %>
+
+
diff --git a/db/migrate/20260829112930_add_reminder_toggles_to_organizations.rb b/db/migrate/20260829112930_add_reminder_toggles_to_organizations.rb
new file mode 100644
index 0000000000..0d028c9ed9
--- /dev/null
+++ b/db/migrate/20260829112930_add_reminder_toggles_to_organizations.rb
@@ -0,0 +1,15 @@
+class AddReminderTogglesToOrganizations < ActiveRecord::Migration[8.1]
+ def up
+ add_column :organizations, :deadline_reminders_enabled, :boolean, null: false, default: false
+ add_column :organizations, :distribution_reminders_enabled, :boolean, null: false, default: false
+
+ # Set the existing orgs value to true.
+ # This preserves current behavior since emails only get sent if the org already has reminder settings.
+ Organization.update_all(deadline_reminders_enabled: true, distribution_reminders_enabled: true)
+ end
+
+ def down
+ remove_column :organizations, :distribution_reminders_enabled
+ remove_column :organizations, :deadline_reminders_enabled
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index dafb084222..60e9900e35 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.1].define(version: 2026_08_28_150820) do
+ActiveRecord::Schema[8.1].define(version: 2026_08_29_112930) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"
@@ -469,8 +469,10 @@
t.string "city"
t.datetime "created_at", precision: nil, null: false
t.integer "deadline_day"
+ t.boolean "deadline_reminders_enabled", default: false, null: false
t.integer "default_storage_location"
t.boolean "distribute_monthly", default: false, null: false
+ t.boolean "distribution_reminders_enabled", default: false, null: false
t.string "email"
t.boolean "enable_child_based_requests", default: true, null: false
t.boolean "enable_individual_requests", default: true, null: false
diff --git a/spec/controllers/distributions_controller_spec.rb b/spec/controllers/distributions_controller_spec.rb
index 4a6ae7723e..2e6afafd49 100644
--- a/spec/controllers/distributions_controller_spec.rb
+++ b/spec/controllers/distributions_controller_spec.rb
@@ -240,6 +240,7 @@
context "when partner has enabled send_reminders" do
before(:each) do
partner.send_reminders = true
+ organization.update!(distribution_reminders_enabled: true)
end
it "should schedule the reminder email" do
subject
@@ -261,6 +262,18 @@
expect(enqueued_jobs.size).to eq(0)
end
end
+
+ context "when the partner has send_reminders enabled but the organization has disabled distribution reminders" do
+ before do
+ partner.update!(send_reminders: true)
+ organization.update!(distribution_reminders_enabled: false)
+ end
+
+ it "should not schedule an email reminder" do
+ subject
+ expect(enqueued_jobs.map { |job| job["arguments"][1] }).not_to include("reminder_email")
+ end
+ end
end
end
@@ -416,6 +429,7 @@
context "when partner has enabled send_reminders" do
before(:each) do
partner.send_reminders = true
+ organization.update!(distribution_reminders_enabled: true)
end
it "should schedule the reminder email" do
subject
diff --git a/spec/factories/organizations.rb b/spec/factories/organizations.rb
index fa35788030..bcfe8fe8c2 100644
--- a/spec/factories/organizations.rb
+++ b/spec/factories/organizations.rb
@@ -6,8 +6,10 @@
# bank_is_set_up :boolean default(FALSE), not null
# city :string
# deadline_day :integer
+# deadline_reminders_enabled :boolean default(FALSE), not null
# default_storage_location :integer
# distribute_monthly :boolean default(FALSE), not null
+# distribution_reminders_enabled :boolean default(FALSE), not null
# email :string
# enable_child_based_requests :boolean default(TRUE), not null
# enable_individual_requests :boolean default(TRUE), not null
diff --git a/spec/mailers/distribution_mailer_spec.rb b/spec/mailers/distribution_mailer_spec.rb
index 12e7ac147a..7e15fd9ad2 100644
--- a/spec/mailers/distribution_mailer_spec.rb
+++ b/spec/mailers/distribution_mailer_spec.rb
@@ -143,6 +143,9 @@
describe "#reminder_email" do
let(:mail) { DistributionMailer.reminder_email(distribution.id) }
+ # New organizations have reminders disabled by default; opt in for these specs.
+ before { organization.update!(distribution_reminders_enabled: true) }
+
context 'HTML format' do
it "renders the body with organization's email text" do
html = html_body(mail)
@@ -180,5 +183,17 @@
expect(mail.body.encoded).to match("delivery")
end
end
+
+ context "when the organization has disabled distribution reminders" do
+ before do
+ partner.update!(send_reminders: true)
+ organization.update!(distribution_reminders_enabled: false)
+ end
+
+ it "does not send the reminder even though the partner has send_reminders enabled" do
+ expect(mail.body).to be_blank
+ expect(mail.to).to be_nil
+ end
+ end
end
end
diff --git a/spec/mailers/reminder_deadline_mailer_spec.rb b/spec/mailers/reminder_deadline_mailer_spec.rb
index 5752407de2..d4f2c01d59 100644
--- a/spec/mailers/reminder_deadline_mailer_spec.rb
+++ b/spec/mailers/reminder_deadline_mailer_spec.rb
@@ -6,11 +6,23 @@
let(:partner) { create(:partner, organization: organization) }
before(:each) do
organization.reminder_email_text = "Custom reminder message"
- organization.update!(deadline_day: 1)
+ organization.update!(deadline_day: 1, deadline_reminders_enabled: true)
end
subject { described_class.notify_deadline(partner) }
+ context 'when the organization has disabled monthly deadline reminders' do
+ before do
+ partner.update!(send_reminders: true)
+ organization.update!(deadline_reminders_enabled: false)
+ end
+
+ it 'does not send even though the partner has reminders enabled' do
+ expect(subject.body).to be_blank
+ expect(subject.to).to be_nil
+ end
+ end
+
it 'renders the subject' do
expect(subject.subject).to eq("#{organization.name} Deadline Reminder")
end
diff --git a/spec/models/organization_spec.rb b/spec/models/organization_spec.rb
index 854469d18f..cd2e2aaf1d 100644
--- a/spec/models/organization_spec.rb
+++ b/spec/models/organization_spec.rb
@@ -6,8 +6,10 @@
# bank_is_set_up :boolean default(FALSE), not null
# city :string
# deadline_day :integer
+# deadline_reminders_enabled :boolean default(FALSE), not null
# default_storage_location :integer
# distribute_monthly :boolean default(FALSE), not null
+# distribution_reminders_enabled :boolean default(FALSE), not null
# email :string
# enable_child_based_requests :boolean default(TRUE), not null
# enable_individual_requests :boolean default(TRUE), not null
@@ -100,6 +102,20 @@
end
end
+ describe "reminder toggles" do
+ it "defaults deadline_reminders_enabled to false for a new organization" do
+ expect(Organization.new.deadline_reminders_enabled).to be false
+ expect(build(:organization).deadline_reminders_enabled).to be false
+ expect(create(:organization).reload.deadline_reminders_enabled).to be false
+ end
+
+ it "defaults distribution_reminders_enabled to false for a new organization" do
+ expect(Organization.new.distribution_reminders_enabled).to be false
+ expect(build(:organization).distribution_reminders_enabled).to be false
+ expect(create(:organization).reload.distribution_reminders_enabled).to be false
+ end
+ end
+
context "Associations >" do
it { should have_many(:item_categories) }
it { should have_many(:product_drive_tags) }
diff --git a/spec/requests/organization_requests_spec.rb b/spec/requests/organization_requests_spec.rb
index 70cc04d0c1..0ce747b79e 100644
--- a/spec/requests/organization_requests_spec.rb
+++ b/spec/requests/organization_requests_spec.rb
@@ -152,7 +152,7 @@
every_nth_month: 1,
day_of_month: 20
}).to_ical
- organization.update(reminder_schedule_definition: valid_reminder_schedule)
+ organization.update(reminder_schedule_definition: valid_reminder_schedule, deadline_reminders_enabled: true)
end
it "reports the next date a reminder email will be sent" do
@@ -167,6 +167,19 @@
expect(response.body).to include "Your next reminder date is Tue Oct 20 2020."
expect(response.body).to include "The deadline on your next reminder email will be Sun Oct 25 2020."
end
+
+ context "but monthly deadline reminders are disabled" do
+ before do
+ organization.update!(deadline_day: 25, deadline_reminders_enabled: false)
+ get organization_path
+ end
+
+ it "hides the reminder schedule details" do
+ expect(response.body).not_to include "Reminder emails are sent"
+ expect(response.body).not_to include "Deadline day in reminder email"
+ expect(response.body).not_to include "Additional text for reminder email"
+ end
+ end
end
it "cannot see 'Demote to User' button for admins" do
@@ -315,6 +328,19 @@
end
end
+ context "toggles the reminder email settings" do
+ let(:update_param) do
+ { organization: { deadline_reminders_enabled: true, distribution_reminders_enabled: false } }
+ end
+
+ it "persists both flags" do
+ subject
+ organization.reload
+ expect(organization.deadline_reminders_enabled).to be true
+ expect(organization.distribution_reminders_enabled).to be false
+ end
+ end
+
context "updates repackage essentials setting" do
let(:update_param) { { organization: { repackage_essentials: true } } }
it "works" do
diff --git a/spec/services/partners/fetch_partners_to_remind_now_service_spec.rb b/spec/services/partners/fetch_partners_to_remind_now_service_spec.rb
index cba44f2bfc..34b387eaee 100644
--- a/spec/services/partners/fetch_partners_to_remind_now_service_spec.rb
+++ b/spec/services/partners/fetch_partners_to_remind_now_service_spec.rb
@@ -6,6 +6,9 @@
context "when there is a partner" do
let!(:partner) { create(:partner) }
+ # New organizations have reminders disabled by default; these specs exercise
+ # the scheduling logic, so opt the partner's organization in.
+ before { partner.organization.update(deadline_reminders_enabled: true) }
context "that has an organization with a global reminder & deadline" do
context "that is for today" do
before do
@@ -57,6 +60,17 @@
expect(subject).not_to include(partner)
end
end
+
+ context "but the organization has disabled deadline reminders" do
+ before do
+ partner.update!(send_reminders: true)
+ partner.organization.update(deadline_reminders_enabled: false)
+ end
+
+ it "should NOT include that partner even though the partner has send_reminders enabled" do
+ expect(subject).not_to include(partner)
+ end
+ end
end
context "that is not for today" do
@@ -154,6 +168,16 @@
expect(subject).to include(partner)
end
end
+
+ context "but the organization has disabled deadline reminders" do
+ before do
+ partner.organization.update(deadline_reminders_enabled: false)
+ end
+
+ it "should NOT include that partner" do
+ expect(subject).not_to include(partner)
+ end
+ end
end
context "that is not for today" do
diff --git a/spec/system/admin/organizations_system_spec.rb b/spec/system/admin/organizations_system_spec.rb
index e6797eb84e..44f95ed47a 100644
--- a/spec/system/admin/organizations_system_spec.rb
+++ b/spec/system/admin/organizations_system_spec.rb
@@ -138,6 +138,26 @@
expect(page).to have_content("invited")
end
+ it "can create an organization with reminders left disabled" do
+ visit new_admin_organization_path
+ admin_user_params = attributes_for(:organization_admin)
+ within "form#new_organization" do
+ fill_in "organization_name", with: "No Reminders Org"
+ fill_in "organization_email", with: "no-reminders@example.com"
+ fill_in "organization_user_name", with: admin_user_params[:name]
+ fill_in "organization_user_email", with: admin_user_params[:email]
+
+ choose('organization[deadline_reminders_enabled]', option: false)
+ choose('organization[distribution_reminders_enabled]', option: false)
+ click_on "Save"
+ end
+
+ expect(page).to have_content("All Human Essentials Organizations")
+ org = Organization.find_by(name: "No Reminders Org")
+ expect(org.deadline_reminders_enabled).to be false
+ expect(org.distribution_reminders_enabled).to be false
+ end
+
it "can view organization details", :aggregate_failures do
visit admin_organizations_path
@@ -160,6 +180,7 @@
visit new_admin_organization_path
within "form#new_organization" do
fill_in "organization_name", with: "aaa" # So the new org will be on the first page
+ choose('organization[deadline_reminders_enabled]', option: true)
end
end
diff --git a/spec/system/distribution_system_spec.rb b/spec/system/distribution_system_spec.rb
index b88bb87828..76b2ff589b 100644
--- a/spec/system/distribution_system_spec.rb
+++ b/spec/system/distribution_system_spec.rb
@@ -320,6 +320,27 @@
visit new_distribution_path
expect(page).to have_no_content "Inactive R Us"
end
+
+ context "when the organization has disabled day-before distribution reminders" do
+ before { organization.update!(distribution_reminders_enabled: false) }
+
+ it "replaces the reminder checkbox with a notice, linking an admin to settings" do
+ sign_in(organization_admin)
+ visit new_distribution_path
+
+ expect(page).not_to have_field("Send email reminder the day before?")
+ expect(page).to have_content("Day-before distribution reminder emails are turned off")
+ expect(page).to have_link("Change this in your organization settings", href: edit_organization_path)
+ end
+
+ it "tells a non-admin user to contact their administrator" do
+ visit new_distribution_path
+
+ expect(page).not_to have_field("Send email reminder the day before?")
+ expect(page).to have_content("Contact your organization administrator")
+ expect(page).not_to have_link("Change this in your organization settings")
+ end
+ end
end
it "errors if user does not fill storage_location" do
@@ -365,6 +386,7 @@
end
it "sends an email if reminders are enabled" do
+ user.organization.update!(distribution_reminders_enabled: true)
job = double('fake_job')
allow(DistributionMailer).to receive(:reminder_email).and_return(job)
allow(job).to receive(:deliver_later)
diff --git a/spec/system/organization_system_spec.rb b/spec/system/organization_system_spec.rb
index d3c351e1c9..18eded1311 100644
--- a/spec/system/organization_system_spec.rb
+++ b/spec/system/organization_system_spec.rb
@@ -71,37 +71,62 @@ def post_form_submit
expect(page.find(".alert")).to have_content "Updated your organization!"
end
- it_behaves_like "deadline and reminder form", "organization", "Save", :post_form_submit
+ # The reminder-schedule fields only render when monthly deadline reminders
+ # are enabled, and the page re-hides them after a refresh, so re-select Yes.
+ def choose_deadline_reminders_yes
+ choose('organization[deadline_reminders_enabled]', option: true)
+ end
- it "the deadline day form's reminder and deadline dates are consistent with the dates calculated by the FetchPartnersToRemindNowService and DeadlineService" do
- travel_to Time.zone.local(2025, 9, 30)
- refresh
- choose "Day of Month"
- fill_in "organization_reminder_schedule_service_day_of_month", with: safe_add_days(Time.zone.now, 1).day
- fill_in "Deadline day in reminder email", with: safe_add_days(Time.zone.now, 2).day
+ it "shows the reminder schedule fields only when monthly deadline reminders are enabled" do
+ expect(page).to have_content("Send monthly deadline reminder emails to partners?")
+ expect(page).not_to have_field("Deadline day in reminder email", visible: :visible)
+ expect(page).not_to have_content("Additional text for reminder email")
- reminder_text = find('small[data-deadline-day-target="reminderText"]').text
- reminder_text.slice!("Your next reminder date is ")
- reminder_text.slice!(".")
- shown_recurrence_date = Time.zone.strptime(reminder_text, "%a %b %d %Y")
+ choose_deadline_reminders_yes
+ expect(page).to have_field("Deadline day in reminder email", visible: :visible)
+ expect(page).to have_content("Additional text for reminder email")
- deadline_text = find('small[data-deadline-day-target="deadlineText"]').text
- deadline_text.slice!("The deadline on your next reminder email will be ")
- deadline_text.slice!(".")
- shown_deadline_date = Time.zone.strptime(deadline_text, "%a %b %d %Y")
+ choose('organization[deadline_reminders_enabled]', option: false)
+ expect(page).not_to have_field("Deadline day in reminder email", visible: :visible)
+ expect(page).not_to have_content("Additional text for reminder email")
+ end
- click_on "Save"
- organization.reload
+ context "with monthly deadline reminders enabled" do
+ before { choose_deadline_reminders_yes }
+
+ it_behaves_like "deadline and reminder form", "organization", "Save", :post_form_submit, :choose_deadline_reminders_yes
+
+ it "the deadline day form's reminder and deadline dates are consistent with the dates calculated by the FetchPartnersToRemindNowService and DeadlineService" do
+ travel_to Time.zone.local(2025, 9, 30)
+ refresh
+ choose_deadline_reminders_yes
+ choose "Day of Month"
+ fill_in "organization_reminder_schedule_service_day_of_month", with: safe_add_days(Time.zone.now, 1).day
+ fill_in "Deadline day in reminder email", with: safe_add_days(Time.zone.now, 2).day
+
+ reminder_text = find('small[data-deadline-day-target="reminderText"]').text
+ reminder_text.slice!("Your next reminder date is ")
+ reminder_text.slice!(".")
+ shown_recurrence_date = Time.zone.strptime(reminder_text, "%a %b %d %Y")
- expect(Partners::FetchPartnersToRemindNowService.new.fetch).to_not include(partner)
+ deadline_text = find('small[data-deadline-day-target="deadlineText"]').text
+ deadline_text.slice!("The deadline on your next reminder email will be ")
+ deadline_text.slice!(".")
+ shown_deadline_date = Time.zone.strptime(deadline_text, "%a %b %d %Y")
- travel_to shown_recurrence_date
+ click_on "Save"
+ organization.reload
- expect(Partners::FetchPartnersToRemindNowService.new.fetch).to include(partner)
- expect(DeadlineService.new(deadline_day: DeadlineService.get_deadline_for_partner(partner)).next_deadline.in_time_zone(Time.zone)).to be_within(1.second).of shown_deadline_date
+ expect(Partners::FetchPartnersToRemindNowService.new.fetch).to_not include(partner)
- expect(page).to have_content("Your next reminder date is #{reminder_text}.")
- expect(page).to have_content("The deadline on your next reminder email will be #{deadline_text}.")
+ travel_to shown_recurrence_date
+
+ expect(Partners::FetchPartnersToRemindNowService.new.fetch).to include(partner)
+ expect(DeadlineService.new(deadline_day: DeadlineService.get_deadline_for_partner(partner)).next_deadline.in_time_zone(Time.zone)).to be_within(1.second).of shown_deadline_date
+
+ expect(page).to have_content("Your next reminder date is #{reminder_text}.")
+ expect(page).to have_content("The deadline on your next reminder email will be #{deadline_text}.")
+ end
end
it 'can select if the org repackages essentials' do
@@ -125,6 +150,16 @@ def post_form_submit
expect(page).to have_content("No")
end
+ it 'can toggle the monthly deadline and day-before distribution reminder emails' do
+ choose('organization[deadline_reminders_enabled]', option: true)
+ choose('organization[distribution_reminders_enabled]', option: true)
+
+ click_on "Save"
+ expect(page).to have_content("Updated your organization!")
+ expect(organization.reload.deadline_reminders_enabled).to be true
+ expect(organization.distribution_reminders_enabled).to be true
+ end
+
it 'can set a default storage location on the organization' do
select(storage_location.name, from: 'Default Storage Location')
diff --git a/spec/system/partner_system_spec.rb b/spec/system/partner_system_spec.rb
index d4c9d1d9fe..1f318b4cfc 100644
--- a/spec/system/partner_system_spec.rb
+++ b/spec/system/partner_system_spec.rb
@@ -701,6 +701,7 @@
describe 'creating a new partner group' do
it 'should allow creating a new partner group with item categories' do
+ organization.update!(deadline_reminders_enabled: true)
travel_to Time.zone.local(2020, 10, 10)
visit partners_path
@@ -727,6 +728,28 @@
end
end
+ describe 'when the organization has disabled monthly deadline reminders' do
+ before { organization.update!(deadline_reminders_enabled: false) }
+
+ it 'replaces the reminder schedule fields with a notice linking an admin to settings' do
+ sign_in(organization_admin)
+ visit new_partner_group_path
+
+ expect(page).to have_content('Do you want to send deadline reminders to them every month?')
+ expect(page).not_to have_field('partner_group_reminder_schedule_service_day_of_month')
+ expect(page).to have_content('Monthly deadline reminder emails are turned off')
+ expect(page).to have_link('Change this in your organization settings', href: edit_organization_path)
+ end
+
+ it 'tells a non-admin user to contact their administrator' do
+ visit new_partner_group_path
+
+ expect(page).to have_content('Monthly deadline reminder emails are turned off')
+ expect(page).to have_content('Contact your organization administrator')
+ expect(page).not_to have_link('Change this in your organization settings')
+ end
+ end
+
describe 'editing a existing partner group' do
let!(:existing_partner_group) { create(:partner_group, organization: organization) }
before do
@@ -762,6 +785,7 @@ def post_refresh
end
before do
+ organization.update!(deadline_reminders_enabled: true)
partner.update!(partner_group: existing_partner_group)
visit partners_path