diff --git a/Gemfile b/Gemfile index d629ba148..a03b04aab 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,7 @@ gem 'actiontext', git: 'https://github.com/kobaltz/actiontext.git', branch: 'arc gem 'active_link_to' # Automatically set a class on active links gem 'active_model_serializers' gem 'active_storage_validations' -gem 'activestorage-openstack' +gem 'activestorage-openstack', git: 'https://github.com/tchak/activestorage-openstack.git', branch: 'fix-activestorage-filename-wrap' gem 'administrate' gem 'after_party' gem 'anchored' diff --git a/Gemfile.lock b/Gemfile.lock index 460ed512d..292ab7bf7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -15,30 +15,41 @@ GIT open4 (~> 1.3.4) rake +GIT + remote: https://github.com/tchak/activestorage-openstack.git + revision: 907582f14d785fb965baebe9524c0162b1bd57c9 + branch: fix-activestorage-filename-wrap + specs: + activestorage-openstack (1.3.0) + fog-openstack (~> 1.0) + marcel + mime-types + rails (>= 5.2.2) + GEM remote: https://rubygems.org/ specs: aasm (5.0.1) concurrent-ruby (~> 1.0) - actioncable (5.2.3) - actionpack (= 5.2.3) + actioncable (5.2.4.1) + actionpack (= 5.2.4.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailer (5.2.3) - actionpack (= 5.2.3) - actionview (= 5.2.3) - activejob (= 5.2.3) + actionmailer (5.2.4.1) + actionpack (= 5.2.4.1) + actionview (= 5.2.4.1) + activejob (= 5.2.4.1) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.2.3) - actionview (= 5.2.3) - activesupport (= 5.2.3) - rack (~> 2.0) + actionpack (5.2.4.1) + actionview (= 5.2.4.1) + activesupport (= 5.2.4.1) + rack (~> 2.0, >= 2.0.8) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.2.3) - activesupport (= 5.2.3) + actionview (5.2.4.1) + activesupport (= 5.2.4.1) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) @@ -46,32 +57,27 @@ GEM active_link_to (1.0.5) actionpack addressable - active_model_serializers (0.10.8) - actionpack (>= 4.1, < 6) - activemodel (>= 4.1, < 6) + active_model_serializers (0.10.10) + actionpack (>= 4.1, < 6.1) + activemodel (>= 4.1, < 6.1) case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) active_storage_validations (0.8.7) rails (>= 5.2.0) - activejob (5.2.3) - activesupport (= 5.2.3) + activejob (5.2.4.1) + activesupport (= 5.2.4.1) globalid (>= 0.3.6) - activemodel (5.2.3) - activesupport (= 5.2.3) - activerecord (5.2.3) - activemodel (= 5.2.3) - activesupport (= 5.2.3) + activemodel (5.2.4.1) + activesupport (= 5.2.4.1) + activerecord (5.2.4.1) + activemodel (= 5.2.4.1) + activesupport (= 5.2.4.1) arel (>= 9.0) - activestorage (5.2.3) - actionpack (= 5.2.3) - activerecord (= 5.2.3) + activestorage (5.2.4.1) + actionpack (= 5.2.4.1) + activerecord (= 5.2.4.1) marcel (~> 0.3.1) - activestorage-openstack (1.2.0) - fog-openstack (~> 1.0) - marcel - mime-types - rails (>= 5.2.2) - activesupport (5.2.3) + activesupport (5.2.4.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -205,7 +211,7 @@ GEM ethon (0.11.0) ffi (>= 1.3.0) eventmachine (1.2.7) - excon (0.71.0) + excon (0.72.0) execjs (2.7.0) factory_bot (4.11.1) activesupport (>= 3.0.0) @@ -221,9 +227,9 @@ GEM flipper (~> 0.17.2) rack (>= 1.4, < 3) rack-protection (>= 1.5.3, < 2.1.0) - fog-core (2.1.2) + fog-core (2.2.0) builder - excon (~> 0.58) + excon (~> 0.71) formatador (~> 0.2) mime-types fog-json (1.2.0) @@ -239,8 +245,9 @@ GEM geocoder (1.6.0) globalid (0.4.2) activesupport (>= 4.2.0) - gon (6.2.1) - actionpack (>= 3.0) + gon (6.3.2) + actionpack (>= 3.0.20) + i18n (>= 0.7) multi_json request_store (>= 1.0) graphiql-rails (1.7.0) @@ -322,25 +329,25 @@ GEM activesupport (>= 4.2) aes_key_wrap bindata - jsonapi-renderer (0.2.0) + jsonapi-renderer (0.2.2) jwt (2.1.0) - kaminari (1.1.1) + kaminari (1.2.0) activesupport (>= 4.1.0) - kaminari-actionview (= 1.1.1) - kaminari-activerecord (= 1.1.1) - kaminari-core (= 1.1.1) - kaminari-actionview (1.1.1) + kaminari-actionview (= 1.2.0) + kaminari-activerecord (= 1.2.0) + kaminari-core (= 1.2.0) + kaminari-actionview (1.2.0) actionview - kaminari-core (= 1.1.1) - kaminari-activerecord (1.1.1) + kaminari-core (= 1.2.0) + kaminari-activerecord (1.2.0) activerecord - kaminari-core (= 1.1.1) - kaminari-core (1.1.1) + kaminari-core (= 1.2.0) + kaminari-core (1.2.0) launchy (2.4.3) addressable (~> 2.3) letter_opener (1.7.0) launchy (~> 2.2) - letter_opener_web (1.3.4) + letter_opener_web (1.4.0) actionmailer (>= 3.2) letter_opener (~> 1.0) railties (>= 3.2) @@ -367,10 +374,10 @@ GEM marcel (0.3.3) mimemagic (~> 0.3.2) method_source (0.9.2) - mime-types (3.3) + mime-types (3.3.1) mime-types-data (~> 3.2015) mime-types-data (3.2019.1009) - mimemagic (0.3.3) + mimemagic (0.3.4) mini_mime (1.0.2) mini_portile2 (2.4.0) minitest (5.14.0) @@ -450,7 +457,7 @@ GEM pry-byebug (3.6.0) byebug (~> 10.0) pry (~> 0.10) - public_suffix (4.0.1) + public_suffix (4.0.3) puma (3.12.2) pundit (2.0.1) activesupport (>= 3.0.0) @@ -471,18 +478,18 @@ GEM rack rack-test (1.1.0) rack (>= 1.0, < 3) - rails (5.2.3) - actioncable (= 5.2.3) - actionmailer (= 5.2.3) - actionpack (= 5.2.3) - actionview (= 5.2.3) - activejob (= 5.2.3) - activemodel (= 5.2.3) - activerecord (= 5.2.3) - activestorage (= 5.2.3) - activesupport (= 5.2.3) + rails (5.2.4.1) + actioncable (= 5.2.4.1) + actionmailer (= 5.2.4.1) + actionpack (= 5.2.4.1) + actionview (= 5.2.4.1) + activejob (= 5.2.4.1) + activemodel (= 5.2.4.1) + activerecord (= 5.2.4.1) + activestorage (= 5.2.4.1) + activesupport (= 5.2.4.1) bundler (>= 1.3.0) - railties (= 5.2.3) + railties (= 5.2.4.1) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.4) actionpack (>= 5.0.1.x) @@ -496,9 +503,9 @@ GEM rails-i18n (5.1.2) i18n (>= 0.7, < 2) railties (>= 5.0, < 6) - railties (5.2.3) - actionpack (= 5.2.3) - activesupport (= 5.2.3) + railties (5.2.4.1) + actionpack (= 5.2.4.1) + activesupport (= 5.2.4.1) method_source rake (>= 0.8.7) thor (>= 0.19.0, < 2.0) @@ -515,7 +522,7 @@ GEM railties (>= 3.2) tilt regexp_parser (1.6.0) - request_store (1.4.1) + request_store (1.5.0) rack (>= 1.4) responders (3.0.0) actionpack (>= 5.0) @@ -716,7 +723,7 @@ DEPENDENCIES active_link_to active_model_serializers active_storage_validations - activestorage-openstack + activestorage-openstack! administrate after_party anchored diff --git a/app/controllers/admin/attestation_templates_controller.rb b/app/controllers/admin/attestation_templates_controller.rb index d1eff3ba5..4306de5a9 100644 --- a/app/controllers/admin/attestation_templates_controller.rb +++ b/app/controllers/admin/attestation_templates_controller.rb @@ -48,7 +48,7 @@ class Admin::AttestationTemplatesController < AdminController render 'admin/attestation_templates/show', formats: [:pdf] else - flash.alert = attestation_template.errors.full_messages.join('
') + flash.alert = attestation.errors.full_messages.join('
') end end diff --git a/app/controllers/new_administrateur/groupe_instructeurs_controller.rb b/app/controllers/new_administrateur/groupe_instructeurs_controller.rb index cf024df3d..8732b2bb1 100644 --- a/app/controllers/new_administrateur/groupe_instructeurs_controller.rb +++ b/app/controllers/new_administrateur/groupe_instructeurs_controller.rb @@ -70,7 +70,11 @@ module NewAdministrateur def reaffecter target_group = procedure.groupe_instructeurs.find(params[:target_group]) - groupe_instructeur.dossiers.update_all(groupe_instructeur_id: target_group.id) + + groupe_instructeur.dossiers.find_each do |dossier| + dossier.assign_to_groupe_instructeur(target_group, current_administrateur) + end + flash[:notice] = "Les dossiers du groupe « #{groupe_instructeur.label} » ont été réaffectés au groupe « #{target_group.label} »." redirect_to procedure_groupe_instructeurs_path(procedure) end diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index fac36c1c7..e66aaf44b 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -297,14 +297,13 @@ module Users end # FIXME: require(:dossier) when all the champs are united - def champs_and_groupe_instructeurs_params - params.permit(dossier: [ - :groupe_instructeur_id, + def champs_params + params.permit(dossier: { champs_attributes: [ :id, :value, :primary_value, :secondary_value, :piece_justificative_file, value: [], champs_attributes: [:id, :_destroy, :value, :primary_value, :secondary_value, :piece_justificative_file, value: []] ] - ]) + }) end def dossier @@ -315,11 +314,20 @@ module Users Dossier.with_champs.find(params[:id]) end + def change_groupe_instructeur? + params[:dossier][:groupe_instructeur_id].present? && @dossier.groupe_instructeur_id != params[:dossier][:groupe_instructeur_id].to_i + end + def update_dossier_and_compute_errors errors = [] - if champs_and_groupe_instructeurs_params[:dossier] && !@dossier.update(champs_and_groupe_instructeurs_params[:dossier]) - errors += @dossier.errors.full_messages + if champs_params[:dossier] + if !@dossier.update(champs_params[:dossier]) + errors += @dossier.errors.full_messages + elsif change_groupe_instructeur? + groupe_instructeur = @dossier.procedure.groupe_instructeurs.find(params[:dossier][:groupe_instructeur_id]) + @dossier.assign_to_groupe_instructeur(groupe_instructeur) + end end if !save_draft? diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index 9e1661174..15cdb6803 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -385,6 +385,7 @@ type Dossier { """ dateTraitement: ISO8601DateTime demandeur: Demandeur! + groupeInstructeur: GroupeInstructeur! id: ID! instructeurs: [Profile!]! messages: [Message!]! diff --git a/app/graphql/types/dossier_type.rb b/app/graphql/types/dossier_type.rb index 5cfe64b81..77026ef92 100644 --- a/app/graphql/types/dossier_type.rb +++ b/app/graphql/types/dossier_type.rb @@ -35,6 +35,8 @@ module Types field :messages, [Types::MessageType], null: false field :avis, [Types::AvisType], null: false + field :groupe_instructeur, Types::GroupeInstructeurType, null: false + def state object.state end @@ -47,6 +49,10 @@ module Types Loaders::Association.for(object.class, :followers_instructeurs).load(object) end + def groupe_instructeur + Loaders::Record.for(GroupeInstructeur).load(object.groupe_instructeur_id) + end + def messages Loaders::Association.for(object.class, commentaires: [:instructeur, :user]).load(object) end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 9c25ec40f..ebee64ca8 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -213,7 +213,6 @@ class Dossier < ApplicationRecord before_save :build_default_champs, if: Proc.new { groupe_instructeur_id_was.nil? } before_save :update_search_terms - after_save :unfollow_stale_instructeurs after_save :send_dossier_received after_save :send_web_hook after_create :send_draft_notification_email @@ -300,6 +299,22 @@ class Dossier < ApplicationRecord instruction_commencee? && retention_end_date <= Time.zone.now end + def assign_to_groupe_instructeur(groupe_instructeur, author = nil) + if groupe_instructeur.procedure == procedure && groupe_instructeur != self.groupe_instructeur + if update(groupe_instructeur: groupe_instructeur) + unfollow_stale_instructeurs + + if author.present? + log_dossier_operation(author, :changer_groupe_instructeur, self) + end + + true + end + else + false + end + end + def text_summary if brouillon? parts = [ @@ -647,14 +662,11 @@ class Dossier < ApplicationRecord end def unfollow_stale_instructeurs - if saved_change_to_groupe_instructeur_id? && saved_change_to_groupe_instructeur_id[0].present? - followers_instructeurs.each do |instructeur| - if instructeur.groupe_instructeurs.exclude?(groupe_instructeur) - instructeur.unfollow(self) - DossierMailer.notify_groupe_instructeur_changed(instructeur, self).deliver_later - end + followers_instructeurs.each do |instructeur| + if instructeur.groupe_instructeurs.exclude?(groupe_instructeur) + instructeur.unfollow(self) + DossierMailer.notify_groupe_instructeur_changed(instructeur, self).deliver_later end - log_dossier_operation(user, :changer_groupe_instructeur, self) end end diff --git a/spec/controllers/api/v2/graphql_controller_spec.rb b/spec/controllers/api/v2/graphql_controller_spec.rb index eaab82e2f..feab5a789 100644 --- a/spec/controllers/api/v2/graphql_controller_spec.rb +++ b/spec/controllers/api/v2/graphql_controller_spec.rb @@ -201,6 +201,10 @@ describe API::V2::GraphqlController do id email } + groupeInstructeur { + id + label + } messages { email body @@ -255,6 +259,10 @@ describe API::V2::GraphqlController do email: instructeur.email } ], + groupeInstructeur: { + id: dossier.groupe_instructeur.to_typed_id, + label: dossier.groupe_instructeur.label + }, demandeur: { id: dossier.individual.to_typed_id, nom: dossier.individual.nom, diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index dbdc9ac69..f1cad93d2 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -398,12 +398,29 @@ describe Dossier do it { is_expected.to match([dossier3, dossier4, dossier2]) } end + describe "#assign_to_groupe_instructeur" do + let(:procedure) { create(:procedure) } + let(:new_groupe_instructeur_new_procedure) { create(:groupe_instructeur) } + let(:new_groupe_instructeur) { create(:groupe_instructeur, procedure: procedure) } + let(:dossier) { create(:dossier, :en_construction, procedure: procedure) } + + it "can change groupe instructeur" do + expect(dossier.assign_to_groupe_instructeur(new_groupe_instructeur_new_procedure)).to be_falsey + expect(dossier.groupe_instructeur).not_to eq(new_groupe_instructeur_new_procedure) + end + + it "can not change groupe instructeur if new groupe is from another procedure" do + expect(dossier.assign_to_groupe_instructeur(new_groupe_instructeur)).to be_truthy + expect(dossier.groupe_instructeur).to eq(new_groupe_instructeur) + end + end + describe "#unfollow_stale_instructeurs" do let(:procedure) { create(:procedure) } let(:instructeur) { create(:instructeur) } - let(:new_groupe_instructeur) { create(:groupe_instructeur) } + let(:new_groupe_instructeur) { create(:groupe_instructeur, procedure: procedure) } let(:instructeur2) { create(:instructeur, groupe_instructeurs: [procedure.defaut_groupe_instructeur, new_groupe_instructeur]) } - let(:dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction)) } + let(:dossier) { create(:dossier, :en_construction, procedure: procedure) } let(:last_operation) { DossierOperationLog.last } before do @@ -413,7 +430,7 @@ describe Dossier do it "unfollows stale instructeurs when groupe instructeur change" do instructeur.follow(dossier) instructeur2.follow(dossier) - dossier.reload.update(groupe_instructeur: new_groupe_instructeur) + dossier.reload.assign_to_groupe_instructeur(new_groupe_instructeur, procedure.administrateurs.first) expect(dossier.reload.followers_instructeurs).not_to include(instructeur) expect(dossier.reload.followers_instructeurs).to include(instructeur2) diff --git a/spec/models/procedure_presentation_spec.rb b/spec/models/procedure_presentation_spec.rb index ce6a06349..d3985de74 100644 --- a/spec/models/procedure_presentation_spec.rb +++ b/spec/models/procedure_presentation_spec.rb @@ -215,7 +215,10 @@ describe ProcedurePresentation do let!(:follow1) { create(:follow, dossier: dossier, instructeur: create(:instructeur, email: 'user1@host')) } let!(:follow2) { create(:follow, dossier: dossier, instructeur: create(:instructeur, email: 'user2@host')) } - it { is_expected.to eq('user1@host, user2@host') } + it "return emails of followers instructeurs" do + emails_to_display = procedure_presentation.displayed_field_values(dossier).first.split(', ').sort + expect(emails_to_display).to eq ["user1@host", "user2@host"] + end end context 'for type_de_champ table' do