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