diff --git a/.ruby-version b/.ruby-version index 2c9b4ef42..75a22a26a 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.3 +3.0.3 diff --git a/Gemfile b/Gemfile index 0890ef679..4dbf8249a 100644 --- a/Gemfile +++ b/Gemfile @@ -67,6 +67,7 @@ gem 'rails' gem 'rails-i18n' # Locales par défaut gem 'rake-progressbar', require: false gem 'react-rails' +gem 'rexml' # add missing gem due to ruby3 (https://github.com/Shopify/bootsnap/issues/325) gem 'rgeo-geojson' gem 'rqrcode' gem 'ruby-saml-idp' diff --git a/Gemfile.lock b/Gemfile.lock index 25da7fb86..9088549ba 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -145,7 +145,7 @@ GEM bcrypt (3.1.16) bindata (2.4.10) bindex (0.8.1) - bootsnap (1.7.2) + bootsnap (1.9.3) msgpack (~> 1.0) brakeman (5.1.1) browser (5.3.1) @@ -858,6 +858,7 @@ DEPENDENCIES rails-i18n rake-progressbar react-rails + rexml rgeo-geojson rqrcode rspec-rails @@ -892,4 +893,4 @@ DEPENDENCIES zxcvbn-ruby BUNDLED WITH - 2.1.4 + 2.2.32 diff --git a/app/controllers/administrateurs/groupe_instructeurs_controller.rb b/app/controllers/administrateurs/groupe_instructeurs_controller.rb index db171b450..58d1cb379 100644 --- a/app/controllers/administrateurs/groupe_instructeurs_controller.rb +++ b/app/controllers/administrateurs/groupe_instructeurs_controller.rb @@ -215,7 +215,7 @@ module Administrateurs else file = group_csv_file.read base_encoding = CharlockHolmes::EncodingDetector.detect(file) - groupes_emails = ACSV::CSV.new(file.encode("UTF-8", base_encoding[:encoding], invalid: :replace, replace: ""), headers: true, header_converters: :downcase) + groupes_emails = ACSV::CSV.new_for_ruby3(file.encode("UTF-8", base_encoding[:encoding], invalid: :replace, replace: ""), headers: true, header_converters: :downcase) .map { |r| r.to_h.slice('groupe', 'email') } groupes_emails_has_keys = groupes_emails.first.has_key?("groupe") && groupes_emails.first.has_key?("email") diff --git a/app/controllers/instructeurs/dossiers_controller.rb b/app/controllers/instructeurs/dossiers_controller.rb index 990f9aea3..2d35d2856 100644 --- a/app/controllers/instructeurs/dossiers_controller.rb +++ b/app/controllers/instructeurs/dossiers_controller.rb @@ -112,7 +112,7 @@ module Instructeurs def passer_en_instruction begin - dossier.passer_en_instruction!(current_instructeur) + dossier.passer_en_instruction!(instructeur: current_instructeur) flash.notice = 'Dossier passé en instruction.' rescue AASM::InvalidTransition => e flash.alert = aasm_error_message(e, target_state: :en_instruction) @@ -138,7 +138,7 @@ module Instructeurs dossier.update!(hidden_by_user_at: nil) end flash.notice = "Le dossier #{dossier.id} a été repassé en instruction." - dossier.repasser_en_instruction!(current_instructeur) + dossier.repasser_en_instruction!(instructeur: current_instructeur) rescue AASM::InvalidTransition => e flash.alert = aasm_error_message(e, target_state: :en_instruction) end @@ -150,19 +150,21 @@ module Instructeurs motivation = params[:dossier] && params[:dossier][:motivation] justificatif = params[:dossier] && params[:dossier][:justificatif_motivation] + h = { instructeur: current_instructeur, motivation: motivation, justificatif: justificatif } + begin case params[:process_action] when "refuser" target_state = :refuse - dossier.refuser!(current_instructeur, motivation, justificatif: justificatif) + dossier.refuser!(h) flash.notice = "Dossier considéré comme refusé." when "classer_sans_suite" target_state = :sans_suite - dossier.classer_sans_suite!(current_instructeur, motivation, justificatif: justificatif) + dossier.classer_sans_suite!(h) flash.notice = "Dossier considéré comme sans suite." when "accepter" target_state = :accepte - dossier.accepter!(current_instructeur, motivation, justificatif: justificatif) + dossier.accepter!(h) flash.notice = "Dossier traité avec succès." end rescue AASM::InvalidTransition => e diff --git a/app/controllers/manager/dossiers_controller.rb b/app/controllers/manager/dossiers_controller.rb index 5c1b342fb..bcc55e81d 100644 --- a/app/controllers/manager/dossiers_controller.rb +++ b/app/controllers/manager/dossiers_controller.rb @@ -41,7 +41,7 @@ module Manager def repasser_en_instruction dossier = Dossier.find(params[:id]) - dossier.repasser_en_instruction(current_super_admin) + dossier.repasser_en_instruction(instructeur: current_super_admin) logger.info("Le dossier #{dossier.id} est repassé en instruction par #{current_super_admin.email}") flash[:notice] = "Le dossier #{dossier.id} est repassé en instruction." diff --git a/app/graphql/mutations/dossier_accepter.rb b/app/graphql/mutations/dossier_accepter.rb index 89be39e6d..83f7c05d9 100644 --- a/app/graphql/mutations/dossier_accepter.rb +++ b/app/graphql/mutations/dossier_accepter.rb @@ -14,7 +14,7 @@ module Mutations field :errors, [Types::ValidationErrorType], null: true def resolve(dossier:, instructeur:, motivation: nil, justificatif: nil, disable_notification:) - dossier.accepter!(instructeur, motivation, justificatif: justificatif, disable_notification: disable_notification) + dossier.accepter!(instructeur: instructeur, motivation: 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 b18337bd3..4d45935cd 100644 --- a/app/graphql/mutations/dossier_classer_sans_suite.rb +++ b/app/graphql/mutations/dossier_classer_sans_suite.rb @@ -14,7 +14,7 @@ module Mutations field :errors, [Types::ValidationErrorType], null: true def resolve(dossier:, instructeur:, motivation:, justificatif: nil, disable_notification:) - dossier.classer_sans_suite!(instructeur, motivation, justificatif: justificatif, disable_notification: disable_notification) + dossier.classer_sans_suite!(instructeur: instructeur, motivation: 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 42e569471..217b0a63b 100644 --- a/app/graphql/mutations/dossier_passer_en_instruction.rb +++ b/app/graphql/mutations/dossier_passer_en_instruction.rb @@ -12,7 +12,7 @@ module Mutations field :errors, [Types::ValidationErrorType], null: true def resolve(dossier:, instructeur:, disable_notification:) - dossier.passer_en_instruction!(instructeur, disable_notification: disable_notification) + dossier.passer_en_instruction!(instructeur: instructeur, disable_notification: disable_notification) { dossier: dossier } end diff --git a/app/graphql/mutations/dossier_refuser.rb b/app/graphql/mutations/dossier_refuser.rb index 53ad7618b..0dfe7e568 100644 --- a/app/graphql/mutations/dossier_refuser.rb +++ b/app/graphql/mutations/dossier_refuser.rb @@ -14,7 +14,7 @@ module Mutations field :errors, [Types::ValidationErrorType], null: true def resolve(dossier:, instructeur:, motivation:, justificatif: nil, disable_notification:) - dossier.refuser!(instructeur, motivation, justificatif: justificatif, disable_notification: disable_notification) + dossier.refuser!(instructeur: instructeur, motivation: motivation, justificatif: justificatif, disable_notification: disable_notification) { dossier: dossier } end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index bf21c7694..aebc6a385 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -787,7 +787,10 @@ class Dossier < ApplicationRecord save! end - def after_passer_en_instruction(instructeur, disable_notification: false) + def after_passer_en_instruction(h) + instructeur = h[:instructeur] + disable_notification = h.fetch(:disable_notification, false) + instructeur.follow(self) self.en_construction_close_to_expiration_notice_sent_at = nil @@ -825,7 +828,10 @@ class Dossier < ApplicationRecord log_dossier_operation(instructeur, :repasser_en_construction) end - def after_repasser_en_instruction(instructeur, disable_notification: false) + def after_repasser_en_instruction(h) + instructeur = h[:instructeur] + disable_notification = h.fetch(:disable_notification, false) + create_missing_traitemets self.archived = false @@ -843,7 +849,12 @@ class Dossier < ApplicationRecord log_dossier_operation(instructeur, :repasser_en_instruction) end - def after_accepter(instructeur, motivation, justificatif: nil, disable_notification: false) + def after_accepter(h) + instructeur = h[:instructeur] + motivation = h[:motivation] + justificatif = h[:justificatif] + disable_notification = h.fetch(:disable_notification, false) + self.processed_at = self.traitements .accepter(motivation: motivation, instructeur: instructeur) .processed_at @@ -882,7 +893,12 @@ class Dossier < ApplicationRecord log_automatic_dossier_operation(:accepter, self) end - def after_refuser(instructeur, motivation, justificatif: nil, disable_notification: false) + def after_refuser(h) + instructeur = h[:instructeur] + motivation = h[:motivation] + justificatif = h[:justificatif] + disable_notification = h.fetch(:disable_notification, false) + self.processed_at = self.traitements .refuser(motivation: motivation, instructeur: instructeur) .processed_at @@ -901,7 +917,12 @@ class Dossier < ApplicationRecord log_dossier_operation(instructeur, :refuser, self) end - def after_classer_sans_suite(instructeur, motivation, justificatif: nil, disable_notification: false) + def after_classer_sans_suite(h) + instructeur = h[:instructeur] + motivation = h[:motivation] + justificatif = h[:justificatif] + disable_notification = h.fetch(:disable_notification, false) + self.processed_at = self.traitements .classer_sans_suite(motivation: motivation, instructeur: instructeur) .processed_at diff --git a/config/initializers/acsv.rb b/config/initializers/acsv.rb new file mode 100644 index 000000000..3a24d2032 --- /dev/null +++ b/config/initializers/acsv.rb @@ -0,0 +1,16 @@ +require 'csv' + +# PR : https://github.com/wvengen/ruby-acsv/pull/3 +module ACSV + class CSV < ::CSV + def self.new_for_ruby3(data, options = {}) + options[:col_sep] ||= ACSV::Detect.separator(data) + # because of the Separation of positional and keyword arguments in Ruby 3.0 + # (https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/) + # instead of + # super(data, options) + # we do + ::CSV.new(data, **options) + end + end +end diff --git a/spec/controllers/instructeurs/dossiers_controller_spec.rb b/spec/controllers/instructeurs/dossiers_controller_spec.rb index 49c51606a..c19ef620f 100644 --- a/spec/controllers/instructeurs/dossiers_controller_spec.rb +++ b/spec/controllers/instructeurs/dossiers_controller_spec.rb @@ -219,7 +219,7 @@ describe Instructeurs::DossiersController, type: :controller do describe '#terminer' do context "with refuser" do before do - dossier.passer_en_instruction!(instructeur) + dossier.passer_en_instruction!(instructeur: instructeur) sign_in(instructeur.user) end @@ -260,7 +260,7 @@ describe Instructeurs::DossiersController, type: :controller do context "with classer_sans_suite" do before do - dossier.passer_en_instruction!(instructeur) + dossier.passer_en_instruction!(instructeur: instructeur) sign_in(instructeur.user) end context 'without attachment' do @@ -302,7 +302,7 @@ describe Instructeurs::DossiersController, type: :controller do context "with accepter" do before do - dossier.passer_en_instruction!(instructeur) + dossier.passer_en_instruction!(instructeur: instructeur) sign_in(instructeur.user) expect(NotificationMailer).to receive(:send_accepte_notification) @@ -749,12 +749,12 @@ describe Instructeurs::DossiersController, type: :controller do end before do - dossier.passer_en_instruction(instructeur) + dossier.passer_en_instruction(instructeur: instructeur) end context 'just before delete the dossier, the operation must be equal to 2' do before do - dossier.accepter!(instructeur, 'le dossier est correct') + dossier.accepter!(instructeur: instructeur, motivation: 'le dossier est correct') end it 'has 2 operations logs before deletion' do @@ -764,7 +764,7 @@ describe Instructeurs::DossiersController, type: :controller do context 'when the instructeur want to delete a dossier with a decision' do before do - dossier.accepter!(instructeur, "le dossier est correct") + dossier.accepter!(instructeur: instructeur, motivation: "le dossier est correct") allow(DossierMailer).to receive(:notify_instructeur_deletion_to_user).and_return(double(deliver_later: nil)) subject end diff --git a/spec/models/concern/tags_substitution_concern_spec.rb b/spec/models/concern/tags_substitution_concern_spec.rb index cf96ef3a1..a15bc1a18 100644 --- a/spec/models/concern/tags_substitution_concern_spec.rb +++ b/spec/models/concern/tags_substitution_concern_spec.rb @@ -318,9 +318,9 @@ describe TagsSubstitutionConcern, type: :model do Timecop.freeze(Time.zone.local(2001, 2, 3)) dossier.passer_en_construction! Timecop.freeze(Time.zone.local(2004, 5, 6)) - dossier.passer_en_instruction!(instructeur) + dossier.passer_en_instruction!(instructeur: instructeur) Timecop.freeze(Time.zone.local(2007, 8, 9)) - dossier.accepter!(instructeur, nil) + dossier.accepter!(instructeur: instructeur) Timecop.return end diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 0649180f1..5a68978bc 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -306,9 +306,9 @@ describe Dossier do Timecop.freeze(date1) d.passer_en_construction! Timecop.freeze(date2) - d.passer_en_instruction!(instructeur) + d.passer_en_instruction!(instructeur: instructeur) Timecop.freeze(date3) - d.accepter!(instructeur, "Motivation") + d.accepter!(instructeur: instructeur, motivation: "Motivation") Timecop.return d end @@ -462,7 +462,7 @@ describe Dossier do it 'should keep first en_construction_at date' do Timecop.return - dossier.passer_en_instruction!(instructeur) + dossier.passer_en_instruction!(instructeur: instructeur) dossier.repasser_en_construction!(instructeur) expect(dossier.traitements.size).to eq(3) @@ -478,7 +478,7 @@ describe Dossier do let(:instructeur) { create(:instructeur) } before do - dossier.passer_en_instruction!(instructeur) + dossier.passer_en_instruction!(instructeur: instructeur) dossier.reload end @@ -490,7 +490,7 @@ describe Dossier do it 'should keep first en_instruction_at date if dossier is set to en_construction again' do Timecop.return dossier.repasser_en_construction!(instructeur) - dossier.passer_en_instruction!(instructeur) + dossier.passer_en_instruction!(instructeur: instructeur) expect(dossier.traitements.size).to eq(4) expect(dossier.traitements.en_construction.first.processed_at).to eq(dossier.depose_at) @@ -504,7 +504,7 @@ describe Dossier do let(:dossier) { create(:dossier, :en_instruction, :with_individual) } before do - dossier.accepter!(instructeur, nil) + dossier.accepter!(instructeur: instructeur) dossier.reload end @@ -518,7 +518,7 @@ describe Dossier do let(:dossier) { create(:dossier, :en_instruction, :with_individual) } before do - dossier.refuser!(instructeur, nil) + dossier.refuser!(instructeur: instructeur) dossier.reload end @@ -532,7 +532,7 @@ describe Dossier do let(:dossier) { create(:dossier, :en_instruction, :with_individual) } before do - dossier.classer_sans_suite!(instructeur, nil) + dossier.classer_sans_suite!(instructeur: instructeur) dossier.reload end @@ -611,7 +611,7 @@ describe Dossier do end it "sends an email when the dossier becomes en_instruction" do - dossier.passer_en_instruction!(instructeur) + dossier.passer_en_instruction!(instructeur: instructeur) expect(NotificationMailer).to have_received(:send_en_instruction_notification).with(dossier) end @@ -949,7 +949,7 @@ describe Dossier do }.to_not have_enqueued_job(WebHookJob) expect { - dossier.passer_en_instruction!(instructeur) + dossier.passer_en_instruction!(instructeur: instructeur) }.to have_enqueued_job(WebHookJob) end end @@ -1034,7 +1034,7 @@ describe Dossier do allow(dossier).to receive(:build_attestation).and_return(attestation) Timecop.freeze(now) - dossier.accepter!(instructeur, 'motivation') + dossier.accepter!(instructeur: instructeur, motivation: 'motivation') dossier.reload end @@ -1089,7 +1089,7 @@ describe Dossier do let(:operation_serialized) { JSON.parse(last_operation.serialized.download) } let(:instructeur) { create(:instructeur) } - before { dossier.passer_en_instruction!(instructeur) } + before { dossier.passer_en_instruction!(instructeur: instructeur) } it { expect(dossier.state).to eq('en_instruction') } it { expect(dossier.followers_instructeurs).to include(instructeur) } @@ -1216,7 +1216,7 @@ describe Dossier do Timecop.freeze allow(DossierMailer).to receive(:notify_revert_to_instruction) .and_return(double(deliver_later: true)) - dossier.repasser_en_instruction!(instructeur) + dossier.repasser_en_instruction!(instructeur: instructeur) dossier.reload end @@ -1495,21 +1495,21 @@ describe Dossier do it "clean up titres identite on accepter" do expect(champ_titre_identite.piece_justificative_file.attached?).to be_truthy expect(champ_titre_identite_vide.piece_justificative_file.attached?).to be_falsey - dossier.accepter!(dossier.followers_instructeurs.first, "yolo!") + dossier.accepter!(instructeur: dossier.followers_instructeurs.first, motivation: "yolo!") expect(champ_titre_identite.piece_justificative_file.attached?).to be_falsey end it "clean up titres identite on refuser" do expect(champ_titre_identite.piece_justificative_file.attached?).to be_truthy expect(champ_titre_identite_vide.piece_justificative_file.attached?).to be_falsey - dossier.refuser!(dossier.followers_instructeurs.first, "yolo!") + dossier.refuser!(instructeur: dossier.followers_instructeurs.first, motivation: "yolo!") expect(champ_titre_identite.piece_justificative_file.attached?).to be_falsey end it "clean up titres identite on classer_sans_suite" do expect(champ_titre_identite.piece_justificative_file.attached?).to be_truthy expect(champ_titre_identite_vide.piece_justificative_file.attached?).to be_falsey - dossier.classer_sans_suite!(dossier.followers_instructeurs.first, "yolo!") + dossier.classer_sans_suite!(instructeur: dossier.followers_instructeurs.first, motivation: "yolo!") expect(champ_titre_identite.piece_justificative_file.attached?).to be_falsey end diff --git a/spec/system/instructeurs/instruction_spec.rb b/spec/system/instructeurs/instruction_spec.rb index 818a2cf8b..312d76dbe 100644 --- a/spec/system/instructeurs/instruction_spec.rb +++ b/spec/system/instructeurs/instruction_spec.rb @@ -181,7 +181,7 @@ describe 'Instructing a dossier:', js: true do let(:commentaire) { create(:commentaire, instructeur: instructeur, dossier: dossier) } before do - dossier.passer_en_instruction!(instructeur) + dossier.passer_en_instruction!(instructeur: instructeur) champ.piece_justificative_file.attach(io: File.open(path), filename: "piece_justificative_0.pdf", content_type: "application/pdf") log_in(instructeur.email, password)