diff --git a/app/controllers/instructeurs/dossiers_controller.rb b/app/controllers/instructeurs/dossiers_controller.rb index 7d89b7a0c..a148e129a 100644 --- a/app/controllers/instructeurs/dossiers_controller.rb +++ b/app/controllers/instructeurs/dossiers_controller.rb @@ -151,15 +151,15 @@ module Instructeurs case params[:process_action] when "refuser" target_state = :refuse - dossier.refuser!(current_instructeur, motivation, justificatif) + dossier.refuser!(current_instructeur, motivation, justificatif: justificatif) flash.notice = "Dossier considéré comme refusé." when "classer_sans_suite" target_state = :sans_suite - dossier.classer_sans_suite!(current_instructeur, motivation, justificatif) + dossier.classer_sans_suite!(current_instructeur, motivation, justificatif: justificatif) flash.notice = "Dossier considéré comme sans suite." when "accepter" target_state = :accepte - dossier.accepter!(current_instructeur, motivation, justificatif) + dossier.accepter!(current_instructeur, motivation, justificatif: justificatif) flash.notice = "Dossier traité avec succès." end rescue AASM::InvalidTransition => e diff --git a/app/graphql/mutations/dossier_accepter.rb b/app/graphql/mutations/dossier_accepter.rb index bbb5bd9f2..89be39e6d 100644 --- a/app/graphql/mutations/dossier_accepter.rb +++ b/app/graphql/mutations/dossier_accepter.rb @@ -8,12 +8,13 @@ module Mutations argument :instructeur_id, ID, "Instructeur qui prend la décision sur le dossier.", required: true, loads: Types::ProfileType argument :motivation, String, required: false argument :justificatif, ID, required: false + argument :disable_notification, Boolean, "Désactiver l’envoi de l’email de notification après l’opération", required: false, default_value: false field :dossier, Types::DossierType, null: true field :errors, [Types::ValidationErrorType], null: true - def resolve(dossier:, instructeur:, motivation: nil, justificatif: nil) - dossier.accepter!(instructeur, motivation, justificatif) + def resolve(dossier:, instructeur:, motivation: nil, justificatif: nil, disable_notification:) + dossier.accepter!(instructeur, motivation, justificatif: justificatif, disable_notification: disable_notification) { dossier: dossier } end diff --git a/app/graphql/mutations/dossier_classer_sans_suite.rb b/app/graphql/mutations/dossier_classer_sans_suite.rb index bf66add79..b18337bd3 100644 --- a/app/graphql/mutations/dossier_classer_sans_suite.rb +++ b/app/graphql/mutations/dossier_classer_sans_suite.rb @@ -8,12 +8,13 @@ module Mutations argument :instructeur_id, ID, "Instructeur qui prend la décision sur le dossier.", required: true, loads: Types::ProfileType argument :motivation, String, required: true argument :justificatif, ID, required: false + argument :disable_notification, Boolean, "Désactiver l’envoi de l’email de notification après l’opération", required: false, default_value: false field :dossier, Types::DossierType, null: true field :errors, [Types::ValidationErrorType], null: true - def resolve(dossier:, instructeur:, motivation:, justificatif: nil) - dossier.classer_sans_suite!(instructeur, motivation, justificatif) + def resolve(dossier:, instructeur:, motivation:, justificatif: nil, disable_notification:) + dossier.classer_sans_suite!(instructeur, motivation, justificatif: justificatif, disable_notification: disable_notification) { dossier: dossier } end diff --git a/app/graphql/mutations/dossier_passer_en_instruction.rb b/app/graphql/mutations/dossier_passer_en_instruction.rb index fa34ead70..42e569471 100644 --- a/app/graphql/mutations/dossier_passer_en_instruction.rb +++ b/app/graphql/mutations/dossier_passer_en_instruction.rb @@ -6,17 +6,18 @@ module Mutations argument :dossier_id, ID, "Dossier ID", required: true, loads: Types::DossierType argument :instructeur_id, ID, "Instructeur qui prend la décision sur le dossier.", required: true, loads: Types::ProfileType + argument :disable_notification, Boolean, "Désactiver l’envoi de l’email de notification après l’opération", required: false, default_value: false field :dossier, Types::DossierType, null: true field :errors, [Types::ValidationErrorType], null: true - def resolve(dossier:, instructeur:) - dossier.passer_en_instruction!(instructeur) + def resolve(dossier:, instructeur:, disable_notification:) + dossier.passer_en_instruction!(instructeur, disable_notification: disable_notification) { dossier: dossier } end - def authorized?(dossier:, instructeur:) + def authorized?(dossier:, instructeur:, **args) if !dossier.en_construction? return false, { errors: ["Le dossier est déjà #{dossier_display_state(dossier, lower: true)}"] } end diff --git a/app/graphql/mutations/dossier_refuser.rb b/app/graphql/mutations/dossier_refuser.rb index 18abc2a7a..53ad7618b 100644 --- a/app/graphql/mutations/dossier_refuser.rb +++ b/app/graphql/mutations/dossier_refuser.rb @@ -8,12 +8,13 @@ module Mutations argument :instructeur_id, ID, "Instructeur qui prend la décision sur le dossier.", required: true, loads: Types::ProfileType argument :motivation, String, required: true argument :justificatif, ID, required: false + argument :disable_notification, Boolean, "Désactiver l’envoi de l’email de notification après l’opération", required: false, default_value: false field :dossier, Types::DossierType, null: true field :errors, [Types::ValidationErrorType], null: true - def resolve(dossier:, instructeur:, motivation:, justificatif: nil) - dossier.refuser!(instructeur, motivation, justificatif) + def resolve(dossier:, instructeur:, motivation:, justificatif: nil, disable_notification:) + dossier.refuser!(instructeur, motivation, justificatif: justificatif, disable_notification: disable_notification) { dossier: dossier } end diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index c1620765c..5f73dbc34 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -748,6 +748,11 @@ input DossierAccepterInput { """ clientMutationId: String + """ + Désactiver l’envoi de l’email de notification après l’opération + """ + disableNotification: Boolean = false + """ Dossier ID """ @@ -846,6 +851,11 @@ input DossierClasserSansSuiteInput { """ clientMutationId: String + """ + Désactiver l’envoi de l’email de notification après l’opération + """ + disableNotification: Boolean = false + """ Dossier ID """ @@ -1164,6 +1174,11 @@ input DossierPasserEnInstructionInput { """ clientMutationId: String + """ + Désactiver l’envoi de l’email de notification après l’opération + """ + disableNotification: Boolean = false + """ Dossier ID """ @@ -1196,6 +1211,11 @@ input DossierRefuserInput { """ clientMutationId: String + """ + Désactiver l’envoi de l’email de notification après l’opération + """ + disableNotification: Boolean = false + """ Dossier ID """ diff --git a/app/models/bill_signature.rb b/app/models/bill_signature.rb index 8b0e66cf5..d2c1cfd7a 100644 --- a/app/models/bill_signature.rb +++ b/app/models/bill_signature.rb @@ -30,6 +30,7 @@ class BillSignature < ApplicationRecord io: StringIO.new(operations_bill_json), filename: "demarches-simplifiees-operations-#{day.to_date.iso8601}.json", content_type: 'application/json', + # we don't want to run virus scanner on this file metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } ) @@ -52,7 +53,9 @@ class BillSignature < ApplicationRecord self.signature.attach( io: StringIO.new(signature), filename: "demarches-simplifiees-signature-#{day.to_date.iso8601}.der", - content_type: 'application/x-x509-ca-cert' + content_type: 'application/x-x509-ca-cert', + # we don't want to run virus scanner on this file + metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } ) end diff --git a/app/models/champs/pays_champ.rb b/app/models/champs/pays_champ.rb index 04110d82f..fdb025886 100644 --- a/app/models/champs/pays_champ.rb +++ b/app/models/champs/pays_champ.rb @@ -20,7 +20,7 @@ class Champs::PaysChamp < Champs::TextChamp def localized_value if external_id - I18nData.countries(I18n.locale)[external_id] + I18nData.countries(I18n.locale)[external_id].to_s else value.present? ? value.to_s : '' end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index e02866a3a..fce3f381f 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -374,7 +374,6 @@ class Dossier < ApplicationRecord before_save :build_default_champs, if: Proc.new { revision_id_was.nil? } before_save :update_search_terms - after_save :send_dossier_en_instruction after_save :send_web_hook after_create_commit :send_draft_notification_email @@ -676,10 +675,13 @@ class Dossier < ApplicationRecord update!(en_construction_at: Time.zone.now) if self.en_construction_at.nil? end - def after_passer_en_instruction(instructeur) + def after_passer_en_instruction(instructeur, disable_notification: false) instructeur.follow(self) update!(en_instruction_at: Time.zone.now) if self.en_instruction_at.nil? + if !procedure.declarative_accepte? && !disable_notification + NotificationMailer.send_en_instruction_notification(self).deliver_later + end log_dossier_operation(instructeur, :passer_en_instruction) end @@ -695,17 +697,19 @@ class Dossier < ApplicationRecord log_dossier_operation(instructeur, :repasser_en_construction) end - def after_repasser_en_instruction(instructeur) + def after_repasser_en_instruction(instructeur, disable_notification: false) self.archived = false self.en_instruction_at = Time.zone.now attestation&.destroy save! - DossierMailer.notify_revert_to_instruction(self).deliver_later + if !disable_notification + DossierMailer.notify_revert_to_instruction(self).deliver_later + end log_dossier_operation(instructeur, :repasser_en_instruction) end - def after_accepter(instructeur, motivation, justificatif = nil) + def after_accepter(instructeur, motivation, justificatif: nil, disable_notification: false) self.traitements.accepter(motivation: motivation, instructeur: instructeur) if justificatif @@ -718,7 +722,9 @@ class Dossier < ApplicationRecord save! remove_titres_identite! - NotificationMailer.send_accepte_notification(self).deliver_later + if !disable_notification + NotificationMailer.send_accepte_notification(self).deliver_later + end send_dossier_decision_to_experts(self) log_dossier_operation(instructeur, :accepter, self) end @@ -738,7 +744,7 @@ class Dossier < ApplicationRecord log_automatic_dossier_operation(:accepter, self) end - def after_refuser(instructeur, motivation, justificatif = nil) + def after_refuser(instructeur, motivation, justificatif: nil, disable_notification: false) self.traitements.refuser(motivation: motivation, instructeur: instructeur) if justificatif @@ -747,12 +753,14 @@ class Dossier < ApplicationRecord save! remove_titres_identite! - NotificationMailer.send_refuse_notification(self).deliver_later + if !disable_notification + NotificationMailer.send_refuse_notification(self).deliver_later + end send_dossier_decision_to_experts(self) log_dossier_operation(instructeur, :refuser, self) end - def after_classer_sans_suite(instructeur, motivation, justificatif = nil) + def after_classer_sans_suite(instructeur, motivation, justificatif: nil, disable_notification: false) self.traitements.classer_sans_suite(motivation: motivation, instructeur: instructeur) if justificatif @@ -761,7 +769,9 @@ class Dossier < ApplicationRecord save! remove_titres_identite! - NotificationMailer.send_sans_suite_notification(self).deliver_later + if !disable_notification + NotificationMailer.send_sans_suite_notification(self).deliver_later + end send_dossier_decision_to_experts(self) log_dossier_operation(instructeur, :classer_sans_suite, self) end @@ -968,12 +978,6 @@ class Dossier < ApplicationRecord end end - def send_dossier_en_instruction - if saved_change_to_state? && en_instruction? && !procedure.declarative_accepte? - NotificationMailer.send_en_instruction_notification(self).deliver_later - end - end - def send_draft_notification_email if brouillon? && !procedure.declarative? DossierMailer.notify_new_draft(self).deliver_later diff --git a/app/models/etablissement.rb b/app/models/etablissement.rb index 3cf259d2d..25b3969cc 100644 --- a/app/models/etablissement.rb +++ b/app/models/etablissement.rb @@ -176,6 +176,7 @@ class Etablissement < ApplicationRecord attestation.attach( io: StringIO.new(response.body), filename: filename, + # we don't want to run virus scanner on this file metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } ) end diff --git a/app/services/procedure_archive_service.rb b/app/services/procedure_archive_service.rb index bf3b4d320..5a4d54256 100644 --- a/app/services/procedure_archive_service.rb +++ b/app/services/procedure_archive_service.rb @@ -40,7 +40,12 @@ class ProcedureArchiveService end end - archive.file.attach(io: File.open(tmp_file), filename: archive.filename(@procedure)) + archive.file.attach( + io: File.open(tmp_file), + filename: archive.filename(@procedure), + # we don't want to run virus scanner on this file + metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } + ) tmp_file.delete archive.make_available! InstructeurMailer.send_archive(instructeur, @procedure, archive).deliver_later diff --git a/spec/controllers/api/v2/graphql_controller_spec.rb b/spec/controllers/api/v2/graphql_controller_spec.rb index 831786fb5..dffe42533 100644 --- a/spec/controllers/api/v2/graphql_controller_spec.rb +++ b/spec/controllers/api/v2/graphql_controller_spec.rb @@ -34,7 +34,9 @@ describe API::V2::GraphqlController do filename: file.original_filename, byte_size: file.size, checksum: compute_checksum_in_chunks(file), - content_type: file.content_type + content_type: file.content_type, + # we don't want to run virus scanner on this file + metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } } end let(:blob) do @@ -902,11 +904,13 @@ describe API::V2::GraphqlController do describe 'dossierPasserEnInstruction' do let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) } let(:instructeur_id) { instructeur.to_typed_id } + let(:disable_notification) { false } let(:query) do "mutation { dossierPasserEnInstruction(input: { dossierId: \"#{dossier.to_typed_id}\", - instructeurId: \"#{instructeur_id}\" + instructeurId: \"#{instructeur_id}\", + disableNotification: #{disable_notification} }) { dossier { id @@ -932,6 +936,9 @@ describe API::V2::GraphqlController do }, errors: nil }) + + perform_enqueued_jobs + expect(ActionMailer::Base.deliveries.size).to eq(4) end end @@ -958,6 +965,25 @@ describe API::V2::GraphqlController do }) end end + + context 'disable notification' do + let(:disable_notification) { true } + it "should passer en instruction dossier without notification" do + expect(gql_errors).to eq(nil) + + expect(gql_data).to eq(dossierPasserEnInstruction: { + dossier: { + id: dossier.to_typed_id, + state: "en_instruction", + motivation: nil + }, + errors: nil + }) + + perform_enqueued_jobs + expect(ActionMailer::Base.deliveries.size).to eq(3) + end + end end describe 'dossierClasserSansSuite' do diff --git a/spec/factories/champ.rb b/spec/factories/champ.rb index f2cc90961..03a36d695 100644 --- a/spec/factories/champ.rb +++ b/spec/factories/champ.rb @@ -11,7 +11,13 @@ FactoryBot.define do trait :with_piece_justificative_file do after(:build) do |champ, _evaluator| - champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain") + champ.piece_justificative_file.attach( + io: StringIO.new("toto"), + filename: "toto.txt", + content_type: "text/plain", + # we don't want to run virus scanner on this file + metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } + ) end end @@ -143,7 +149,13 @@ FactoryBot.define do end after(:build) do |champ, evaluator| - champ.piece_justificative_file.attach(io: StringIO.new("x" * evaluator.size), filename: "toto.txt", content_type: "text/plain") + champ.piece_justificative_file.attach( + io: StringIO.new("x" * evaluator.size), + filename: "toto.txt", + content_type: "text/plain", + # we don't want to run virus scanner on this file + metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } + ) end end @@ -151,7 +163,13 @@ FactoryBot.define do type_de_champ { association :type_de_champ_titre_identite, procedure: dossier.procedure } after(:build) do |champ, _evaluator| - champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.png", content_type: "image/png") + champ.piece_justificative_file.attach( + io: StringIO.new("toto"), + filename: "toto.png", + content_type: "image/png", + # we don't want to run virus scanner on this file + metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } + ) end end diff --git a/spec/factories/dossier.rb b/spec/factories/dossier.rb index 7eb38ce3f..ec747fcc8 100644 --- a/spec/factories/dossier.rb +++ b/spec/factories/dossier.rb @@ -205,7 +205,9 @@ FactoryBot.define do after(:create) do |dossier, _evaluator| dossier.justificatif_motivation.attach( io: StringIO.new('Hello World'), - filename: 'hello.txt' + filename: 'hello.txt', + # we don't want to run virus scanner on this file + metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } ) end end diff --git a/spec/factories/procedure.rb b/spec/factories/procedure.rb index ef670a43b..2651a971e 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -248,7 +248,9 @@ FactoryBot.define do after(:create) do |procedure, _evaluator| procedure.notice.attach( io: StringIO.new('Hello World'), - filename: 'hello.txt' + filename: 'hello.txt', + # we don't want to run virus scanner on this file + metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } ) end end @@ -257,7 +259,9 @@ FactoryBot.define do after(:create) do |procedure, _evaluator| procedure.deliberation.attach( io: StringIO.new('Hello World'), - filename: 'hello.txt' + filename: 'hello.txt', + # we don't want to run virus scanner on this file + metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } ) end end diff --git a/spec/factories/type_de_champ.rb b/spec/factories/type_de_champ.rb index 30711fbe9..ae6fa4cc1 100644 --- a/spec/factories/type_de_champ.rb +++ b/spec/factories/type_de_champ.rb @@ -130,7 +130,13 @@ FactoryBot.define do type_champ { TypeDeChamp.type_champs.fetch(:piece_justificative) } after(:build) do |type_de_champ, _evaluator| - type_de_champ.piece_justificative_template.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain") + type_de_champ.piece_justificative_template.attach( + io: StringIO.new("toto"), + filename: "toto.txt", + content_type: "text/plain", + # we don't want to run virus scanner on this file + metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } + ) end end factory :type_de_champ_titre_identite do diff --git a/spec/models/concern/tags_substitution_concern_spec.rb b/spec/models/concern/tags_substitution_concern_spec.rb index a8854c45c..8ab94ca3e 100644 --- a/spec/models/concern/tags_substitution_concern_spec.rb +++ b/spec/models/concern/tags_substitution_concern_spec.rb @@ -320,7 +320,7 @@ describe TagsSubstitutionConcern, type: :model do Timecop.freeze(Time.zone.local(2004, 5, 6)) dossier.passer_en_instruction!(instructeur) Timecop.freeze(Time.zone.local(2007, 8, 9)) - dossier.accepter!(instructeur, nil, nil) + dossier.accepter!(instructeur, nil) Timecop.return end diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 213cef8b0..8886cc206 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -239,7 +239,7 @@ describe Dossier do Timecop.freeze(date2) d.passer_en_instruction!(instructeur) Timecop.freeze(date3) - d.accepter!(instructeur, "Motivation", nil) + d.accepter!(instructeur, "Motivation") Timecop.return d end @@ -422,7 +422,7 @@ describe Dossier do let(:dossier) { create(:dossier, :en_instruction, :with_individual) } before do - dossier.accepter!(instructeur, nil, nil) + dossier.accepter!(instructeur, nil) dossier.reload end @@ -435,7 +435,7 @@ describe Dossier do let(:dossier) { create(:dossier, :en_instruction, :with_individual) } before do - dossier.refuser!(instructeur, nil, nil) + dossier.refuser!(instructeur, nil) dossier.reload end @@ -447,7 +447,7 @@ describe Dossier do let(:dossier) { create(:dossier, :en_instruction, :with_individual) } before do - dossier.classer_sans_suite!(instructeur, nil, nil) + dossier.classer_sans_suite!(instructeur, nil) dossier.reload end diff --git a/spec/services/dossier_projection_service_spec.rb b/spec/services/dossier_projection_service_spec.rb index 243f81084..096ac0e07 100644 --- a/spec/services/dossier_projection_service_spec.rb +++ b/spec/services/dossier_projection_service_spec.rb @@ -184,6 +184,33 @@ describe DossierProjectionService do it { is_expected.to eq('18 a la bonne rue') } end + + context 'for type_de_champ table: type_de_champ pays which needs external_id field' do + let(:table) { 'type_de_champ' } + let(:procedure) { create(:procedure, types_de_champ: [build(:type_de_champ_pays)]) } + let(:dossier) { create(:dossier, procedure: procedure) } + let(:column) { dossier.procedure.types_de_champ.first.stable_id.to_s } + let!(:previous_locale) { I18n.locale } + + before { I18n.locale = :fr } + after { I18n.locale = previous_locale } + + context 'when external id is set' do + before do + dossier.champs.first.update(external_id: 'GB') + end + + it { is_expected.to eq('Royaume-Uni') } + end + + context 'when no external id is set' do + before do + dossier.champs.first.update(value: "qu'il est beau mon pays") + end + + it { is_expected.to eq("qu'il est beau mon pays") } + end + end end end end