From b458f03e43d257ade3684a8f881078f6e73caf0a Mon Sep 17 00:00:00 2001 From: Nicolas Florentin Date: Tue, 5 Sep 2023 11:15:12 +0200 Subject: [PATCH] add a notification to remind users to upload their supporting documents --- CHANGELOG.md | 2 + .../javascript/models/notification-type.ts | 1 + app/models/user.rb | 1 + app/services/members/members_service.rb | 1 + ...supporting_document_reminder.json.jbuilder | 4 + ...user_supporting_document_reminder.html.erb | 5 ++ .../supporting_documents_reminder_worker.rb | 27 +++++++ config/locales/en.yml | 2 + config/locales/fr.yml | 2 + config/locales/mails.en.yml | 4 + config/schedule.yml | 5 ++ ...ing_documents_reminder_sent_at_to_users.rb | 5 ++ db/seeds/notification_types.rb | 1 + db/structure.sql | 17 ++++- test/fixtures/notification_types.yml | 8 ++ ...pporting_documents_reminder_worker_test.rb | 73 +++++++++++++++++++ 16 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 app/views/api/notifications/_notify_user_supporting_document_reminder.json.jbuilder create mode 100644 app/views/notifications_mailer/notify_user_supporting_document_reminder.html.erb create mode 100644 app/workers/supporting_documents_reminder_worker.rb create mode 100644 db/migrate/20230901090637_add_supporting_documents_reminder_sent_at_to_users.rb create mode 100644 test/workers/supporting_documents_reminder_worker_test.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index a40ad652b..34a863cb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,9 @@ - Fix a bug: for project categories, if there is no category : do not show categories panel in show view, do not show categories input field in edit view - Fix a bug: unable to update status to paid for latest payment schedule item - Fix a bug: unable to generate statistic +- Improvement : add a notification to remind users to upload their supporting documents - [TODO DEPLOY] `rails fablab:maintenance:regenerate_statistics[2014,1]` +- [TODO DEPLOY] `rails db:seed` ## v6.0.13 2023 August 28 diff --git a/app/frontend/src/javascript/models/notification-type.ts b/app/frontend/src/javascript/models/notification-type.ts index 906320abc..4089b03a1 100644 --- a/app/frontend/src/javascript/models/notification-type.ts +++ b/app/frontend/src/javascript/models/notification-type.ts @@ -74,6 +74,7 @@ export const notificationTypeNames = [ 'notify_user_is_validated', 'notify_user_is_invalidated', 'notify_user_supporting_document_refusal', + 'notify_user_supporting_document_reminder', 'notify_admin_user_supporting_document_refusal', 'notify_user_order_is_ready', 'notify_user_order_is_canceled', diff --git a/app/models/user.rb b/app/models/user.rb index 8568a444a..c8bd3135c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -89,6 +89,7 @@ class User < ApplicationRecord scope :with_subscription, -> { joins(statistic_profile: [:subscriptions]) } scope :not_confirmed, -> { where(confirmed_at: nil) } scope :inactive_for_3_years, -> { where('users.last_sign_in_at < ?', 3.years.ago) } + scope :supporting_documents_reminder_not_sent, -> { where(supporting_documents_reminder_sent_at: nil) } def to_json(*) ApplicationController.new.view_context.render( diff --git a/app/services/members/members_service.rb b/app/services/members/members_service.rb index 46c8f06da..e84966e12 100644 --- a/app/services/members/members_service.rb +++ b/app/services/members/members_service.rb @@ -27,6 +27,7 @@ class Members::MembersService if @member.validated_at? && !(new_types - current_types).empty? validated_at_changed = true @member.validated_at = nil + @member.supporting_documents_reminder_sent_at = nil end end diff --git a/app/views/api/notifications/_notify_user_supporting_document_reminder.json.jbuilder b/app/views/api/notifications/_notify_user_supporting_document_reminder.json.jbuilder new file mode 100644 index 000000000..279b0fe0a --- /dev/null +++ b/app/views/api/notifications/_notify_user_supporting_document_reminder.json.jbuilder @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +json.title notification.notification_type +json.description t('.reminder_message') diff --git a/app/views/notifications_mailer/notify_user_supporting_document_reminder.html.erb b/app/views/notifications_mailer/notify_user_supporting_document_reminder.html.erb new file mode 100644 index 000000000..dfa3ed300 --- /dev/null +++ b/app/views/notifications_mailer/notify_user_supporting_document_reminder.html.erb @@ -0,0 +1,5 @@ +<%= render 'notifications_mailer/shared/hello', recipient: @recipient %> + +

+ <%= t('.body.user_supporting_document_reminder') %> +

\ No newline at end of file diff --git a/app/workers/supporting_documents_reminder_worker.rb b/app/workers/supporting_documents_reminder_worker.rb new file mode 100644 index 000000000..91a54f2b5 --- /dev/null +++ b/app/workers/supporting_documents_reminder_worker.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +# Asynchronously export the accounting data (AccountingLines) to an external accounting software +class SupportingDocumentsReminderWorker + include Sidekiq::Worker + + def perform + users_to_notify = User.members + .supporting_documents_reminder_not_sent + .where("users.created_at < ?", 2.days.ago) + .left_outer_joins(supporting_document_files: { supporting_document_type: :groups }) + .where("groups.id = users.group_id OR groups.id IS NULL") + .select("users.*, count(supporting_document_files.id)") + .group("users.id") + .having("(count(supporting_document_files.id)) < (SELECT count(supporting_document_types.id) "\ + "FROM supporting_document_types "\ + "INNER JOIN supporting_document_types_groups "\ + "ON supporting_document_types_groups.supporting_document_type_id = supporting_document_types.id "\ + "WHERE supporting_document_types_groups.group_id = users.group_id)") + users_to_notify.each do |user| + NotificationCenter.call type: 'notify_user_supporting_document_reminder', + receiver: user, + attached_object: user + user.update_column(:supporting_documents_reminder_sent_at, DateTime.current) + end + end +end \ No newline at end of file diff --git a/config/locales/en.yml b/config/locales/en.yml index f78c2203c..bc4aa012d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -446,6 +446,8 @@ en: account_invalidated: "Your account is invalid." notify_user_supporting_document_refusal: refusal: "Your supporting documents were refused" + notify_user_supporting_document_reminder: + reminder_message: "This is a reminder for you to upload your supporting documents." notify_admin_user_supporting_document_refusal: refusal: "Member's supporting document %{NAME} was refused." notify_user_order_is_ready: diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 6722847d0..d62dda346 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -446,6 +446,8 @@ fr: account_invalidated: "Votre compte est invalide." notify_user_supporting_document_refusal: refusal: "Vos pièces justificatives ont été refusées" + notify_user_supporting_document_reminder: + reminder_message: "Ceci est un message de rappel pour vous inviter à uploader vos pièces justificatives." notify_admin_user_supporting_document_refusal: refusal: "Le justificatif du membre %{NAME} a été refusé." notify_user_order_is_ready: diff --git a/config/locales/mails.en.yml b/config/locales/mails.en.yml index 6151da3c6..ee299f0ab 100644 --- a/config/locales/mails.en.yml +++ b/config/locales/mails.en.yml @@ -402,6 +402,10 @@ en: body: user_supporting_document_files_refusal: "Your supporting documents were refused:" action: "Please re-upload some new supporting documents." + notify_user_supporting_document_reminder: + subject: "Reminder to upload your supporting documents" + body: + user_supporting_document_reminder: "This is a reminder for you to upload your supporting documents." notify_admin_user_supporting_document_refusal: subject: "A member's supporting documents were refused" body: diff --git a/config/schedule.yml b/config/schedule.yml index 31e0c7f81..953a374e5 100644 --- a/config/schedule.yml +++ b/config/schedule.yml @@ -62,4 +62,9 @@ auto_cancel_authorizations: class: TrainingAuthorizationWorker queue: default +supporting_documents_reminder_worker: + cron: "0 8 * * *" # every day, at 8 + class: SupportingDocumentsReminderWorker + queue: default + <%= PluginRegistry.insert_code('yml.schedule') %> diff --git a/db/migrate/20230901090637_add_supporting_documents_reminder_sent_at_to_users.rb b/db/migrate/20230901090637_add_supporting_documents_reminder_sent_at_to_users.rb new file mode 100644 index 000000000..d7842242e --- /dev/null +++ b/db/migrate/20230901090637_add_supporting_documents_reminder_sent_at_to_users.rb @@ -0,0 +1,5 @@ +class AddSupportingDocumentsReminderSentAtToUsers < ActiveRecord::Migration[7.0] + def change + add_column :users, :supporting_documents_reminder_sent_at, :datetime + end +end diff --git a/db/seeds/notification_types.rb b/db/seeds/notification_types.rb index a181bd344..b93ad7b87 100644 --- a/db/seeds/notification_types.rb +++ b/db/seeds/notification_types.rb @@ -69,6 +69,7 @@ NOTIFICATIONS_TYPES = [ { name: 'notify_user_is_validated', category: 'users_accounts', is_configurable: false }, { name: 'notify_user_is_invalidated', category: 'users_accounts', is_configurable: false }, { name: 'notify_user_supporting_document_refusal', category: 'supporting_documents', is_configurable: false }, + { name: 'notify_user_supporting_document_reminder', category: 'supporting_documents', is_configurable: false }, { name: 'notify_admin_user_supporting_document_refusal', category: 'supporting_documents', is_configurable: true }, { name: 'notify_user_order_is_ready', category: 'shop', is_configurable: false }, { name: 'notify_user_order_is_canceled', category: 'shop', is_configurable: false }, diff --git a/db/structure.sql b/db/structure.sql index 7ef685d2d..c9683cb21 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -1726,7 +1726,8 @@ CREATE TABLE public.notification_types ( category character varying NOT NULL, is_configurable boolean NOT NULL, created_at timestamp without time zone NOT NULL, - updated_at timestamp without time zone NOT NULL + updated_at timestamp without time zone NOT NULL, + roles character varying[] DEFAULT '{}'::character varying[] ); @@ -4193,7 +4194,8 @@ CREATE TABLE public.users ( current_sign_in_ip inet, last_sign_in_ip inet, mapped_from_sso character varying, - validated_at timestamp without time zone + validated_at timestamp without time zone, + supporting_documents_reminder_sent_at timestamp(6) without time zone ); @@ -6651,6 +6653,13 @@ CREATE UNIQUE INDEX index_notification_preferences_on_user_and_notification_type CREATE UNIQUE INDEX index_notification_types_on_name ON public.notification_types USING btree (name); +-- +-- Name: index_notification_types_on_roles; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_notification_types_on_roles ON public.notification_types USING btree (roles); + + -- -- Name: index_notifications_on_notification_type_id; Type: INDEX; Schema: public; Owner: - -- @@ -8900,6 +8909,8 @@ INSERT INTO "schema_migrations" (version) VALUES ('20230626122947'), ('20230718133636'), ('20230718134350'), -('20230720085857'); +('20230720085857'), +('20230828073428'), +('20230901090637'); diff --git a/test/fixtures/notification_types.yml b/test/fixtures/notification_types.yml index 579c9b9d5..4324e3748 100644 --- a/test/fixtures/notification_types.yml +++ b/test/fixtures/notification_types.yml @@ -573,3 +573,11 @@ notification_type_72: is_configurable: true created_at: 2023-02-16 10:42:39.143888000 Z updated_at: 2023-02-16 10:42:39.143888000 Z + +notification_type_73: + id: 73 + name: notify_user_supporting_document_reminder + category: supporting_documents + is_configurable: false + created_at: 2023-02-02 08:25:33.439078000 Z + updated_at: 2023-02-02 08:25:33.439078000 Z \ No newline at end of file diff --git a/test/workers/supporting_documents_reminder_worker_test.rb b/test/workers/supporting_documents_reminder_worker_test.rb new file mode 100644 index 000000000..34f64cbd0 --- /dev/null +++ b/test/workers/supporting_documents_reminder_worker_test.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require 'test_helper' +require 'minitest/autorun' +#require 'sidekiq/testing' + +class SupportingDocumentsReminderWorkerTest < ActiveSupport::TestCase + include ActionMailer::TestHelper + setup do + # Sidekiq::Testing.inline! + + @worker = SupportingDocumentsReminderWorker.new + + group = groups(:group_1) + @users = User.where(group_id: group.id).members + @supporting_document_type_1 = SupportingDocumentType.create!(name: "doc1", groups: [group]) + @supporting_document_type_2 = SupportingDocumentType.create!(name: "doc2", groups: [group]) + end + + teardown do + # Sidekiq::Testing.fake! + end + + test 'notify every users who did not upload supporting document files' do + @users.each do |user| + assert_nil user.supporting_documents_reminder_sent_at + end + + assert_enqueued_emails @users.length do + @worker.perform + end + + @users.reload.each do |user| + assert user.supporting_documents_reminder_sent_at + end + + assert_enqueued_emails 0 do + @worker.perform + end + end + + test 'notify users even if they have uploaded 1 document of the 2' do + @users.each do |user| + user.supporting_document_files.create!(supporting_document_type: @supporting_document_type_1, + attachment: fixture_file_upload('document.pdf')) + end + + assert_enqueued_emails @users.length do + @worker.perform + end + end + + test 'do not notify users if they have uploaded all documents' do + @users.each do |user| + user.supporting_document_files.create!(supporting_document_type: @supporting_document_type_1, + attachment: fixture_file_upload('document.pdf')) + user.supporting_document_files.create!(supporting_document_type: @supporting_document_type_2, + attachment: fixture_file_upload('document.pdf')) + end + + assert_enqueued_emails 0 do + @worker.perform + end + end + + test 'do not notify users if they were created too recently' do + @users.update_all(created_at: 2.minutes.ago) + + assert_enqueued_emails 0 do + @worker.perform + end + end +end