From 7c3ca54e188275bba29c7edc7774c7954e15adf1 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 1 Aug 2023 10:58:41 +0200 Subject: [PATCH 01/15] feat(demarche): expose public logo url --- app/controllers/procedures_controller.rb | 17 +++++++++++++++++ config/routes.rb | 2 ++ 2 files changed, 19 insertions(+) create mode 100644 app/controllers/procedures_controller.rb diff --git a/app/controllers/procedures_controller.rb b/app/controllers/procedures_controller.rb new file mode 100644 index 00000000000..8ac9aa7307b --- /dev/null +++ b/app/controllers/procedures_controller.rb @@ -0,0 +1,17 @@ +class ProceduresController < ApplicationController + before_action :retrieve_procedure + + def logo + if @procedure.logo.attached? + redirect_to url_for(@procedure.logo.variant(:email)) + else + redirect_to image_url(PROCEDURE_DEFAULT_LOGO_SRC) + end + end + + private + + def retrieve_procedure + @procedure = Procedure.find(params[:id]) + end +end diff --git a/config/routes.rb b/config/routes.rb index eb74b18a690..de19a964ea0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -351,6 +351,8 @@ delete 'france_connect_information' => 'profil#destroy_fci' end + get 'procedures/:id/logo', to: 'procedures#logo', as: :procedure_logo + # # Expert # From ca4066939c53558f95805e2a455c85797ac85631 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 8 Aug 2023 10:44:36 +0200 Subject: [PATCH 02/15] chore(pipedrive): remove pipedrive --- .../manager/demandes_controller.rb | 62 -------------- app/dashboards/demande_dashboard.rb | 4 - app/helpers/demande_helper.rb | 21 ----- app/jobs/pipedrive_accepts_deals_job.rb | 5 -- app/jobs/pipedrive_refuses_deals_job.rb | 5 -- app/lib/biz_dev.rb | 4 - app/lib/pipedrive/api.rb | 82 ------------------- app/lib/pipedrive/deal_adapter.rb | 63 -------------- app/lib/pipedrive/organization_adapter.rb | 13 --- app/lib/pipedrive/person_adapter.rb | 52 ------------ app/services/pipedrive_service.rb | 25 ------ app/views/manager/demandes/index.html.erb | 63 -------------- config/initializers/02_urls.rb | 1 - config/routes.rb | 5 -- config/secrets.yml | 4 - .../manager/demandes_controller_spec.rb | 20 ----- spec/lib/biz_dev_spec.rb | 16 ---- spec/lib/pipedrive/deal_adapter_spec.rb | 25 ------ 18 files changed, 470 deletions(-) delete mode 100644 app/controllers/manager/demandes_controller.rb delete mode 100644 app/dashboards/demande_dashboard.rb delete mode 100644 app/helpers/demande_helper.rb delete mode 100644 app/jobs/pipedrive_accepts_deals_job.rb delete mode 100644 app/jobs/pipedrive_refuses_deals_job.rb delete mode 100644 app/lib/pipedrive/api.rb delete mode 100644 app/lib/pipedrive/deal_adapter.rb delete mode 100644 app/lib/pipedrive/organization_adapter.rb delete mode 100644 app/lib/pipedrive/person_adapter.rb delete mode 100644 app/services/pipedrive_service.rb delete mode 100644 app/views/manager/demandes/index.html.erb delete mode 100644 spec/controllers/manager/demandes_controller_spec.rb delete mode 100644 spec/lib/pipedrive/deal_adapter_spec.rb diff --git a/app/controllers/manager/demandes_controller.rb b/app/controllers/manager/demandes_controller.rb deleted file mode 100644 index 06ae1838989..00000000000 --- a/app/controllers/manager/demandes_controller.rb +++ /dev/null @@ -1,62 +0,0 @@ -module Manager - class DemandesController < Manager::ApplicationController - def index - @pending_demandes = pending_demandes - end - - def create_administrateur - administrateur = current_super_admin.invite_admin(create_administrateur_params[:email]) - - if administrateur.errors.empty? - PipedriveAcceptsDealsJob.perform_later( - create_administrateur_params[:person_id], - current_super_admin.id, - create_administrateur_params[:stage_id] - ) - - flash.notice = "Administrateur créé" - redirect_to manager_demandes_path - else - flash.now.alert = administrateur.errors.full_messages.to_sentence - @pending_demandes = pending_demandes - render :index - end - end - - def refuse_administrateur - PipedriveRefusesDealsJob.perform_later( - refuse_administrateur_params[:person_id], - current_super_admin.id - ) - - AdministrationMailer - .refuse_admin(refuse_administrateur_params[:email]) - .deliver_later - - flash.notice = "La demande de #{refuse_administrateur_params[:email]} va être refusée" - redirect_to manager_demandes_path - end - - private - - def create_administrateur_params - params.permit(:email, :person_id, :stage_id) - end - - def refuse_administrateur_params - params.permit(:email, :person_id) - end - - def pending_demandes - already_approved_emails = Administrateur.eager_load(:user) - .where(users: { email: demandes.map { |d| d[:email] } }) - .map(&:email) - - demandes.reject { |demande| already_approved_emails.include?(demande[:email]) } - end - - def demandes - @demandes ||= PipedriveService.get_demandes - end - end -end diff --git a/app/dashboards/demande_dashboard.rb b/app/dashboards/demande_dashboard.rb deleted file mode 100644 index fbe72530395..00000000000 --- a/app/dashboards/demande_dashboard.rb +++ /dev/null @@ -1,4 +0,0 @@ -require "administrate/base_dashboard" - -class DemandeDashboard < Administrate::BaseDashboard -end diff --git a/app/helpers/demande_helper.rb b/app/helpers/demande_helper.rb deleted file mode 100644 index 26266b204e3..00000000000 --- a/app/helpers/demande_helper.rb +++ /dev/null @@ -1,21 +0,0 @@ -module DemandeHelper - def nb_of_procedures_options - { - 'je ne sais pas' => Pipedrive::DealAdapter::PIPEDRIVE_NB_OF_PROCEDURES_DO_NOT_KNOW_VALUE, - '1' => Pipedrive::DealAdapter::PIPEDRIVE_NB_OF_PROCEDURES_1_VALUE, - '1 à 10' => Pipedrive::DealAdapter::PIPEDRIVE_NB_OF_PROCEDURES_1_TO_10_VALUE, - '10 à 100 ' => Pipedrive::DealAdapter::PIPEDRIVE_NB_OF_PROCEDURES_10_TO_100_VALUE, - 'plus de 100' => Pipedrive::DealAdapter::PIPEDRIVE_NB_OF_PROCEDURES_ABOVE_100_VALUE - } - end - - def deadline_options - { - 'le plus vite possible' => Pipedrive::DealAdapter::PIPEDRIVE_DEADLINE_ASAP_VALUE, - 'dans les 3 prochains mois' => Pipedrive::DealAdapter::PIPEDRIVE_DEADLINE_NEXT_3_MONTHS_VALUE, - 'dans les 6 prochains mois' => Pipedrive::DealAdapter::PIPEDRIVE_DEADLINE_NEXT_6_MONTHS_VALUE, - 'dans les 12 prochains mois' => Pipedrive::DealAdapter::PIPEDRIVE_DEADLINE_NEXT_12_MONTHS_VALUE, - 'pas de date' => Pipedrive::DealAdapter::PIPEDRIVE_DEADLINE_NO_DATE_VALUE - } - end -end diff --git a/app/jobs/pipedrive_accepts_deals_job.rb b/app/jobs/pipedrive_accepts_deals_job.rb deleted file mode 100644 index 05ce05a1cd8..00000000000 --- a/app/jobs/pipedrive_accepts_deals_job.rb +++ /dev/null @@ -1,5 +0,0 @@ -class PipedriveAcceptsDealsJob < ApplicationJob - def perform(person_id, administration_id, stage_id) - PipedriveService.accept_demande_from_person(person_id, administration_id, stage_id) - end -end diff --git a/app/jobs/pipedrive_refuses_deals_job.rb b/app/jobs/pipedrive_refuses_deals_job.rb deleted file mode 100644 index 610a4cd5188..00000000000 --- a/app/jobs/pipedrive_refuses_deals_job.rb +++ /dev/null @@ -1,5 +0,0 @@ -class PipedriveRefusesDealsJob < ApplicationJob - def perform(person_id, administration_id) - PipedriveService.refuse_demande_from_person(person_id, administration_id) - end -end diff --git a/app/lib/biz_dev.rb b/app/lib/biz_dev.rb index 5543436ca6b..426687b0e04 100644 --- a/app/lib/biz_dev.rb +++ b/app/lib/biz_dev.rb @@ -5,8 +5,4 @@ module BizDev def self.full_name(administration_id) NAME end - - def self.pipedrive_id(administration_id) - PIPEDRIVE_ID - end end diff --git a/app/lib/pipedrive/api.rb b/app/lib/pipedrive/api.rb deleted file mode 100644 index 2c4201e7f5a..00000000000 --- a/app/lib/pipedrive/api.rb +++ /dev/null @@ -1,82 +0,0 @@ -class Pipedrive::API - PIPEDRIVE_ALL_NOT_DELETED_DEALS = 'all_not_deleted' - PIPEDRIVE_DEALS_URL = [PIPEDRIVE_API_URL, 'deals'].join("/") - PIPEDRIVE_PEOPLE_URL = [PIPEDRIVE_API_URL, 'persons'].join("/") - PIPEDRIVE_ORGANIZATIONS_URL = [PIPEDRIVE_API_URL, 'organizations'].join("/") - - def self.get_persons_owned_by_user(user_id) - url = PIPEDRIVE_PEOPLE_URL - params = { user_id: user_id } - - self.get(url, params) - end - - def self.get_deals_for_person(person_id) - url = [PIPEDRIVE_PEOPLE_URL, person_id, "deals"].join('/') - params = { status: PIPEDRIVE_ALL_NOT_DELETED_DEALS } - - self.get(url, params) - end - - def self.put_deal(deal_id, params) - url = [PIPEDRIVE_DEALS_URL, deal_id].join("/") - - self.put(url, params) - end - - def self.post_deal(params) - self.post(PIPEDRIVE_DEALS_URL, params) - end - - def self.put_person(person_id, params) - url = [PIPEDRIVE_PEOPLE_URL, person_id].join("/") - - self.put(url, params) - end - - def self.post_person(params) - self.post(PIPEDRIVE_PEOPLE_URL, params) - end - - def self.post_organization(params) - self.post(PIPEDRIVE_ORGANIZATIONS_URL, params) - end - - private - - def self.get(url, params) - params.merge!({ - start: 0, - limit: 500, - api_token: token - }) - - response = Typhoeus.get(url, params: params) - - if response.success? - JSON.parse(response.body)['data'] - end - end - - def self.put(url, params) - Typhoeus.put( - url, - params: { api_token: token }, - body: params.to_json, - headers: { 'content-type' => 'application/json' } - ) - end - - def self.post(url, params) - Typhoeus.post( - url, - params: { api_token: token }, - body: params.to_json, - headers: { 'content-type' => 'application/json' } - ) - end - - def self.token - Rails.application.secrets.pipedrive[:key] - end -end diff --git a/app/lib/pipedrive/deal_adapter.rb b/app/lib/pipedrive/deal_adapter.rb deleted file mode 100644 index 5e8cd77eff6..00000000000 --- a/app/lib/pipedrive/deal_adapter.rb +++ /dev/null @@ -1,63 +0,0 @@ -class Pipedrive::DealAdapter - PIPEDRIVE_ADMIN_CENTRAL_STOCK_STAGE_ID = 35 - PIPEDRIVE_SERVICE_DECO_REGIONAL_STOCK_STAGE_ID = 24 - PIPEDRIVE_SERVICE_DECO_DEPARTEMENTAL_STOCK_STAGE_ID = 20 - PIPEDRIVE_COLLECTIVITE_DEP_OU_REG_STOCK_STAGE_ID = 30 - PIPEDRIVE_COLLECTIVITE_LOCALE_STOCK_STAGE_ID = 40 - PIPEDRIVE_ORGANISMES_STOCK_STAGE_ID = 1 - PIPEDRIVE_ORGANISMES_REFUSES_STOCK_STAGE_ID = 45 - PIPEDRIVE_SUSPECTS_COMPTE_CREE_STAGE_ID = 48 - - PIPEDRIVE_LOST_STATUS = "lost" - PIPEDRIVE_LOST_REASON = "refusé depuis DS" - - PIPEDRIVE_NB_OF_PROCEDURES_ATTRIBUTE_ID = "b22f8710352a7fb548623c062bf82ed6d1b6b704" - PIPEDRIVE_NB_OF_PROCEDURES_DO_NOT_KNOW_VALUE = "Je ne sais pas" - PIPEDRIVE_NB_OF_PROCEDURES_1_VALUE = "juste 1" - PIPEDRIVE_NB_OF_PROCEDURES_1_TO_10_VALUE = "de 1 a 10" - PIPEDRIVE_NB_OF_PROCEDURES_10_TO_100_VALUE = "de 10 a 100" - PIPEDRIVE_NB_OF_PROCEDURES_ABOVE_100_VALUE = "Plus de 100" - - PIPEDRIVE_DEADLINE_ATTRIBUTE_ID = "36a72c82af9d9f0d476b041ede8876844a249bf2" - PIPEDRIVE_DEADLINE_ASAP_VALUE = "Le plus vite possible" - PIPEDRIVE_DEADLINE_NEXT_3_MONTHS_VALUE = "Dans les 3 prochain mois" - PIPEDRIVE_DEADLINE_NEXT_6_MONTHS_VALUE = "Dans les 6 prochain mois" - PIPEDRIVE_DEADLINE_NEXT_12_MONTHS_VALUE = "Dans les 12 prochain mois" - PIPEDRIVE_DEADLINE_NO_DATE_VALUE = "Pas de date" - - def self.refuse_deal(deal_id, owner_id) - params = { - user_id: owner_id, - stage_id: PIPEDRIVE_ORGANISMES_REFUSES_STOCK_STAGE_ID, - status: PIPEDRIVE_LOST_STATUS, - lost_reason: PIPEDRIVE_LOST_REASON - } - - Pipedrive::API.put_deal(deal_id, params) - end - - def self.get_deals_ids_for_person(person_id) - deals = Pipedrive::API.get_deals_for_person(person_id) || [] - deals.map { |datum| datum['id'] } - end - - def self.update_deal_owner_and_stage(deal_id, owner_id, stage_id) - params = { user_id: owner_id, stage_id: stage_id } - - Pipedrive::API.put_deal(deal_id, params) - end - - def self.add_deal(organisation_id, person_id, title, nb_of_procedures, nb_of_dossiers, deadline) - params = { - org_id: organisation_id, - person_id: person_id, - title: title, - user_id: Pipedrive::PersonAdapter::PIPEDRIVE_ROBOT_ID, - "#{PIPEDRIVE_NB_OF_PROCEDURES_ATTRIBUTE_ID}": nb_of_procedures, - value: nb_of_dossiers, - "#{PIPEDRIVE_DEADLINE_ATTRIBUTE_ID}": deadline - } - - Pipedrive::API.post_deal(params) - end -end diff --git a/app/lib/pipedrive/organization_adapter.rb b/app/lib/pipedrive/organization_adapter.rb deleted file mode 100644 index b4fb88ea151..00000000000 --- a/app/lib/pipedrive/organization_adapter.rb +++ /dev/null @@ -1,13 +0,0 @@ -class Pipedrive::OrganizationAdapter - def self.add_organization(name, address) - params = { - name: name, - owner_id: Pipedrive::PersonAdapter::PIPEDRIVE_ROBOT_ID, - address: address - } - - response = Pipedrive::API.post_organization(params) - - JSON.parse(response.body)['data']['id'] - end -end diff --git a/app/lib/pipedrive/person_adapter.rb b/app/lib/pipedrive/person_adapter.rb deleted file mode 100644 index 270dd223f82..00000000000 --- a/app/lib/pipedrive/person_adapter.rb +++ /dev/null @@ -1,52 +0,0 @@ -class Pipedrive::PersonAdapter - PIPEDRIVE_POSTE_ATTRIBUTE_ID = '33a790746f1713d712fe97bcce9ac1ca6374a4d6' - PIPEDRIVE_SOURCE_ATTRIBUTE_ID = '2fa7864f467ffa97721cbcd08df5a3d591b15f50' - PIPEDRIVE_NB_DOSSIERS_ATTRIBUTE_ID = '2734a3ff19f4b88bd0d7b4cf02c47c7545617207' - PIPEDRIVE_DEADLINE_ATTRIBUTE_ID = 'ef766dd14de7da246fb5fc1704f45d1f1830f6c9' - PIPEDRIVE_ROBOT_ID = '11381160' - - def self.get_demandes_from_persons_owned_by_robot - demandes = Pipedrive::API.get_persons_owned_by_user(PIPEDRIVE_ROBOT_ID) - - if demandes.present? - demandes.map do |datum| - { - person_id: datum['id'], - nom: datum['name'], - poste: datum[PIPEDRIVE_POSTE_ATTRIBUTE_ID], - email: datum.dig('email', 0, 'value'), - tel: datum.dig('phone', 0, 'value'), - organisation: datum['org_name'], - nb_dossiers: datum[PIPEDRIVE_NB_DOSSIERS_ATTRIBUTE_ID], - deadline: datum[PIPEDRIVE_DEADLINE_ATTRIBUTE_ID] - } - end - else - [] - end - end - - def self.update_person_owner(person_id, owner_id) - params = { owner_id: owner_id } - - Pipedrive::API.put_person(person_id, params) - end - - def self.add_person(email, phone, name, organization_id, poste, source, nb_of_dossiers, deadline) - params = { - email: email, - phone: phone, - name: name, - org_id: organization_id, - owner_id: PIPEDRIVE_ROBOT_ID, - "#{PIPEDRIVE_POSTE_ATTRIBUTE_ID}": poste, - "#{PIPEDRIVE_SOURCE_ATTRIBUTE_ID}": source, - "#{PIPEDRIVE_NB_DOSSIERS_ATTRIBUTE_ID}": nb_of_dossiers, - "#{PIPEDRIVE_DEADLINE_ATTRIBUTE_ID}": deadline - } - - response = Pipedrive::API.post_person(params) - - JSON.parse(response.body)['data']['id'] - end -end diff --git a/app/services/pipedrive_service.rb b/app/services/pipedrive_service.rb deleted file mode 100644 index 33b80f68eef..00000000000 --- a/app/services/pipedrive_service.rb +++ /dev/null @@ -1,25 +0,0 @@ -class PipedriveService - def self.accept_demande_from_person(person_id, administration_id, stage_id) - owner_id = BizDev.pipedrive_id(administration_id) - person_deals_ids = Pipedrive::DealAdapter.get_deals_ids_for_person(person_id) - person_deals_ids.each { |deal_id| Pipedrive::DealAdapter.update_deal_owner_and_stage(deal_id, owner_id, stage_id) } - Pipedrive::PersonAdapter.update_person_owner(person_id, owner_id) - end - - def self.refuse_demande_from_person(person_id, administration_id) - owner_id = BizDev.pipedrive_id(administration_id) - person_deals_ids = Pipedrive::DealAdapter.get_deals_ids_for_person(person_id) - person_deals_ids.each { |deal_id| Pipedrive::DealAdapter.refuse_deal(deal_id, owner_id) } - Pipedrive::PersonAdapter.update_person_owner(person_id, owner_id) - end - - def self.get_demandes - Pipedrive::PersonAdapter.get_demandes_from_persons_owned_by_robot - end - - def self.add_demande(email, phone, name, poste, source, organization_name, address, nb_of_procedures, nb_of_dossiers, deadline) - organization_id = Pipedrive::OrganizationAdapter.add_organization(organization_name, address) - person_id = Pipedrive::PersonAdapter.add_person(email, phone, name, organization_id, poste, source, nb_of_dossiers, deadline) - Pipedrive::DealAdapter.add_deal(organization_id, person_id, organization_name, nb_of_procedures, nb_of_dossiers, deadline) - end -end diff --git a/app/views/manager/demandes/index.html.erb b/app/views/manager/demandes/index.html.erb deleted file mode 100644 index 6d4fb534ab9..00000000000 --- a/app/views/manager/demandes/index.html.erb +++ /dev/null @@ -1,63 +0,0 @@ -<% content_for(:title) do %> - <%= display_resource_name('Demandes') %> -<% end %> - - -

-Plus de 1000 dossiers et "le plus vite possible" 👉 c'est un VIP ❤️. Appelez-le 📞 pour répondre à ses questions. -

-<% if @pending_demandes.present? %> -
- - - <% keys = @pending_demandes.first.keys %> - - <% keys.each do |key| %> - - <% end %> - - - - <% @pending_demandes.each do |demande| %> - - <% keys.each do |key| %> - - <% end %> - - - - <% end %> - -
- <%= key %> - -
- <%= demande[key] %> - - <%= form_tag(manager_demandes_create_administrateur_path) do -%> - <%= select_tag "stage_id", - options_for_select({ - "suspect" => Pipedrive::DealAdapter::PIPEDRIVE_SUSPECTS_COMPTE_CREE_STAGE_ID - - }), - style: 'margin-bottom: 20px; width: inherit;' %> - - <%= hidden_field_tag 'email', demande[:email] %> - <%= hidden_field_tag 'person_id', demande[:person_id] %> - - <%= submit_tag 'Créer' %> - <% end -%> - - <%= button_to('Refuser', - manager_demandes_refuse_administrateur_path, - params: { person_id: demande[:person_id], email: demande[:email] }, - style: 'background-color: #FFFFFF; color: #293f54; border: 1px solid #dfe0e1') %> -
-
-<% else %> -

Aucune demande

-<% end %> diff --git a/config/initializers/02_urls.rb b/config/initializers/02_urls.rb index 4c7b1a256ef..f47dfcfc7fa 100644 --- a/config/initializers/02_urls.rb +++ b/config/initializers/02_urls.rb @@ -8,7 +8,6 @@ API_TCHAP_URL = ENV.fetch("API_TCHAP_URL", "https://matrix.agent.tchap.gouv.fr/_matrix/identity/api/v1") API_COJO_URL = ENV.fetch("API_COJO_URL", nil) HELPSCOUT_API_URL = ENV.fetch("HELPSCOUT_API_URL", "https://api.helpscout.net/v2") -PIPEDRIVE_API_URL = ENV.fetch("PIPEDRIVE_API_URL", "https://api.pipedrive.com/v1") SENDINBLUE_API_URL = ENV.fetch("SENDINBLUE_API_URL", "https://in-automate.sendinblue.com/api/v2") SENDINBLUE_API_V3_URL = ENV.fetch("SENDINBLUE_API_V3_URL", "https://api.sendinblue.com/v3") UNIVERSIGN_API_URL = ENV.fetch("UNIVERSIGN_API_URL", "https://ws.universign.eu/tsa/post/") diff --git a/config/routes.rb b/config/routes.rb index eb74b18a690..ccfa538df9f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -56,8 +56,6 @@ resources :dossiers, only: [:show] - resources :demandes, only: [:index] - resources :bill_signatures, only: [:index] resources :exports, only: [:index, :show] @@ -80,9 +78,6 @@ end resources :safe_mailers, only: [:index, :edit, :update, :destroy, :new, :create, :show] - post 'demandes/create_administrateur' - post 'demandes/refuse_administrateur' - authenticate :super_admin do mount Flipper::UI.app(-> { Flipper.instance }) => "/features", as: :flipper match "/delayed_job" => DelayedJobWeb, :anchor => false, :via => [:get, :post] diff --git a/config/secrets.yml b/config/secrets.yml index cf527bafec5..866fa61598f 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -45,8 +45,6 @@ defaults: &defaults api_key: <%= ENV['DOLIST_API_KEY'] %> api_entreprise: key: <%= ENV['API_ENTREPRISE_KEY'] %> - pipedrive: - key: <%= ENV['PIPEDRIVE_KEY'] %> mailtrap: username: <%= ENV['MAILTRAP_USERNAME'] %> password: <%= ENV['MAILTRAP_PASSWORD'] %> @@ -111,8 +109,6 @@ test: key_derivation_salt: test-yyMmzM9cTSD1rs3Fq3hwt3hMNg4 # ggignore api_entreprise: key: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik9oIHllYWgiLCJpYXQiOjE1MTYyMzkwMjJ9.f06sBo3q2Yxnw_TYPFUEs0CozBmcV-XniH_DeKNWzKE" # ggignore - pipedrive: - key: pipedrive_test_key france_connect_particulier: identifier: france_connect_test_identifier secret: france_connect_test_secret diff --git a/spec/controllers/manager/demandes_controller_spec.rb b/spec/controllers/manager/demandes_controller_spec.rb deleted file mode 100644 index 33498bfb5f8..00000000000 --- a/spec/controllers/manager/demandes_controller_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -describe Manager::DemandesController, type: :controller do - let(:super_admin) { create(:super_admin) } - - describe 'GET #index' do - before do - sign_in super_admin - end - - it "display pending demandes" do - approved_administrateur = create(:administrateur, email: "approved@example.com") - pending_demande = { email: 'pending@example.com' } - demandes = [{ email: approved_administrateur.email }, pending_demande] - allow(PipedriveService).to receive(:get_demandes).and_return(demandes) - - get :index - - expect(assigns(:pending_demandes)).to eq([pending_demande]) - end - end -end diff --git a/spec/lib/biz_dev_spec.rb b/spec/lib/biz_dev_spec.rb index 0d22c87a22f..76497baa0f4 100644 --- a/spec/lib/biz_dev_spec.rb +++ b/spec/lib/biz_dev_spec.rb @@ -19,20 +19,4 @@ it { is_expected.not_to be_empty } end end - - describe '#pipedrive_id' do - subject { described_class.pipedrive_id(administration_id) } - - context 'when administration is a business developer' do - let(:administration_id) { first_biz_dev_id } - - it { is_expected.to be > 0 } - end - - context 'when administration is not a business developer' do - let(:administration_id) { non_biz_dev_id } - - it { is_expected.to be > 0 } - end - end end diff --git a/spec/lib/pipedrive/deal_adapter_spec.rb b/spec/lib/pipedrive/deal_adapter_spec.rb deleted file mode 100644 index 5d758367327..00000000000 --- a/spec/lib/pipedrive/deal_adapter_spec.rb +++ /dev/null @@ -1,25 +0,0 @@ -describe Pipedrive::DealAdapter do - let(:url) { PIPEDRIVE_API_URL } - let(:status) { 200 } - let(:body) { '{}' } - - before do - stub_request(:get, url) - .to_return(status: status, body: body) - end - - describe ".get_deals_ids_for_person" do - let(:url) { %r{/persons/1/deals\?*} } - subject { Pipedrive::DealAdapter.get_deals_ids_for_person('1') } - - context "with valid data" do - let(:body) { '{ "success": true, "data": [ { "id": 34 }, { "id": 35 } ] }' } - it { is_expected.to eq [34, 35] } - end - - context "when no data are returned" do - let(:body) { '{ "success": true, "data": null }' } - it { is_expected.to eq [] } - end - end -end From 4b8626b126dc1b29d186646f8a68759136c4f9dc Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Thu, 10 Aug 2023 15:33:21 +0200 Subject: [PATCH 03/15] fix(after_party): this job timouts in prod. Drop it for now. --- ...0230721145042_purge_invalid_geo_areas.rake | 22 ------------------- 1 file changed, 22 deletions(-) delete mode 100644 lib/tasks/deployment/20230721145042_purge_invalid_geo_areas.rake diff --git a/lib/tasks/deployment/20230721145042_purge_invalid_geo_areas.rake b/lib/tasks/deployment/20230721145042_purge_invalid_geo_areas.rake deleted file mode 100644 index 0ab8d473016..00000000000 --- a/lib/tasks/deployment/20230721145042_purge_invalid_geo_areas.rake +++ /dev/null @@ -1,22 +0,0 @@ -namespace :after_party do - desc 'Deployment task: purge_invalid_geo_areas' - task purge_invalid_geo_areas: :environment do - puts "Running deploy task 'purge_invalid_geo_areas'" - - geo_areas = GeoArea.selections_utilisateur - progress = ProgressReport.new(geo_areas.count) - - geo_areas.find_each do |geo_area| - if !geo_area.valid? - geo_area.destroy - end - progress.inc - end - progress.finish - - # Update task as completed. If you remove the line below, the task will - # run with every deploy (or every time you call after_party:run). - AfterParty::TaskRecord - .create version: AfterParty::TaskRecorder.new(__FILE__).timestamp - end -end From 1e820b124e9abf467095f72e827545ed63d0a0e9 Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 13 Jul 2023 17:54:28 +0200 Subject: [PATCH 04/15] dsfr(checkbox): ajoute un composant pour les checkboxes DSFR --- app/components/dsfr/checkbox_component.rb | 22 +++++++++++++++++++ .../checkbox_component.html.haml | 20 +++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 app/components/dsfr/checkbox_component.rb create mode 100644 app/components/dsfr/checkbox_component/checkbox_component.html.haml diff --git a/app/components/dsfr/checkbox_component.rb b/app/components/dsfr/checkbox_component.rb new file mode 100644 index 00000000000..a516a2838d1 --- /dev/null +++ b/app/components/dsfr/checkbox_component.rb @@ -0,0 +1,22 @@ +class Dsfr::CheckboxComponent < ApplicationComponent + attr_reader :error + + def initialize(form:, target:, checkboxes:, error: nil) + @form = form + @target = target + @checkboxes = checkboxes + @error = error + end + + def error? + # TODO: mettre correctement le aria-labelled-by avec l'id du div qui contient les erreurs + # https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/bouton-radio/ + @error.present? + end + + def each_checkboxes + @checkboxes.each do |button| + yield(*button.values_at(:label, :checked_value, :unchecked_value, :hint), button.except(:label, :checked_value, :unchecked_value, :hint)) + end + end +end diff --git a/app/components/dsfr/checkbox_component/checkbox_component.html.haml b/app/components/dsfr/checkbox_component/checkbox_component.html.haml new file mode 100644 index 00000000000..5e53f15b078 --- /dev/null +++ b/app/components/dsfr/checkbox_component/checkbox_component.html.haml @@ -0,0 +1,20 @@ +%fieldset{ class: class_names("fr-fieldset": true, "fr-fieldset--error": error?), 'aria-labelledby': 'radio-hint-element-legend radio-hint-element-messages', role: error? ? :group : nil } + %legend.fr-fieldset__legend--regular.fr-fieldset__legend + = content + + - each_checkboxes do |label, checked_value, unchecked_value, hint, button_options| + .fr-fieldset__element + .fr-checkbox-group + = @form.check_box @target, button_options.merge(id: checked_value.to_s.parameterize, multiple: true), checked_value, unchecked_value + = @form.label @target, for: checked_value.to_s.parameterize, class: 'fr-label' do + - capture do + = label + + = button_options[:after_label] if button_options[:after_label] + + %span.fr-hint-text= hint if hint + + .fr-messages-group{ 'aria-live': 'assertive' } + - if error? + %p.fr-message.fr-message--error= error + From 05687e63ca8712f36d53ec44f09b10f7d09952f4 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 18 Jul 2023 11:29:44 +0200 Subject: [PATCH 05/15] amelioration(dossier.routage): assigne le groupe d'instructeur au moment de passer_en_construction! un dossier et ignore ce concept de pre-setter le groupe d'instructeur sur tout le reste de l'app --- .../api/public/v1/dossiers_controller.rb | 1 - app/controllers/users/commencer_controller.rb | 1 - app/controllers/users/dossiers_controller.rb | 2 - app/models/concerns/dossier_clone_concern.rb | 4 +- app/models/dossier.rb | 7 +- app/models/procedure.rb | 6 -- app/models/procedure_revision.rb | 2 +- .../concern/dossier_clone_concern_spec.rb | 12 +++- spec/models/dossier_spec.rb | 70 ++++++++++++++----- 9 files changed, 71 insertions(+), 34 deletions(-) diff --git a/app/controllers/api/public/v1/dossiers_controller.rb b/app/controllers/api/public/v1/dossiers_controller.rb index 718036d6e2e..3503a2def8f 100644 --- a/app/controllers/api/public/v1/dossiers_controller.rb +++ b/app/controllers/api/public/v1/dossiers_controller.rb @@ -4,7 +4,6 @@ class API::Public::V1::DossiersController < API::Public::V1::BaseController def create dossier = Dossier.new( revision: @procedure.active_revision, - groupe_instructeur: @procedure.defaut_groupe_instructeur_for_new_dossier, state: Dossier.states.fetch(:brouillon), prefilled: true ) diff --git a/app/controllers/users/commencer_controller.rb b/app/controllers/users/commencer_controller.rb index 5ae8a0a36f9..9f007c92b65 100644 --- a/app/controllers/users/commencer_controller.rb +++ b/app/controllers/users/commencer_controller.rb @@ -96,7 +96,6 @@ def retrieve_procedure_with_closed def build_prefilled_dossier @prefilled_dossier = Dossier.new( revision: @revision, - groupe_instructeur: @procedure.defaut_groupe_instructeur_for_new_dossier, state: Dossier.states.fetch(:brouillon), prefilled: true ) diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 4a28191b91e..92e9c25b526 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -375,7 +375,6 @@ def new dossier = Dossier.new( revision: params[:brouillon] ? procedure.draft_revision : procedure.active_revision, - groupe_instructeur: procedure.defaut_groupe_instructeur_for_new_dossier, user: current_user, state: Dossier.states.fetch(:brouillon) ) @@ -559,7 +558,6 @@ def submit_dossier_and_compute_errors @dossier.valid?(**submit_validation_options) errors += format_errors(errors: @dossier.errors) errors += format_errors(errors: @dossier.check_mandatory_and_visible_champs) - errors end diff --git a/app/models/concerns/dossier_clone_concern.rb b/app/models/concerns/dossier_clone_concern.rb index fc056c8bd06..914c6fc0383 100644 --- a/app/models/concerns/dossier_clone_concern.rb +++ b/app/models/concerns/dossier_clone_concern.rb @@ -76,7 +76,8 @@ def merge_fork(editing_fork) end def clone(user: nil, fork: false) - dossier_attributes = [:autorisation_donnees, :revision_id, :groupe_instructeur_id] + dossier_attributes = [:autorisation_donnees, :revision_id] + dossier_attributes += [:groupe_instructeur_id] if fork relationships = [:individual, :etablissement] cloned_champs = champs @@ -95,7 +96,6 @@ def clone(user: nil, fork: false) kopy.user = user || original.user kopy.state = Dossier.states.fetch(:brouillon) - kopy.champs = cloned_champs.values.map do |(_, champ)| champ.dossier = kopy champ.parent = cloned_champs[champ.parent_id].second if champ.child? diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 693e4fea9b2..12050c35aae 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -135,7 +135,7 @@ def classer_sans_suite(motivation: nil, instructeur: nil, processed_at: Time.zon aasm whiny_persistence: true, column: :state, enum: true do state :brouillon, initial: true - state :en_construction + state :en_construction, before_enter: :compute_routing state :en_instruction state :accepte state :refuse @@ -852,7 +852,6 @@ def after_passer_en_construction .passer_en_construction .processed_at save! - MailTemplatePresenterService.create_commentaire_for_state(self) NotificationMailer.send_en_construction_notification(self).deliver_later procedure.compute_dossiers_count @@ -871,6 +870,10 @@ def submit_en_construction!(pending_correction_confirm: false) end end + def compute_routing + RoutingEngine.compute(self) + end + def after_passer_en_instruction(h) instructeur = h[:instructeur] disable_notification = h.fetch(:disable_notification, false) diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 482a0fd30bc..84f7d9f8517 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -666,12 +666,6 @@ def instructeurs_self_management? routing_enabled? || instructeurs_self_management_enabled? end - def defaut_groupe_instructeur_for_new_dossier - if !routing_enabled? || feature_enabled?(:procedure_routage_api) - defaut_groupe_instructeur - end - end - def groupe_instructeurs_but_defaut groupe_instructeurs - [defaut_groupe_instructeur] end diff --git a/app/models/procedure_revision.rb b/app/models/procedure_revision.rb index 8f065d4ee80..a38706b9351 100644 --- a/app/models/procedure_revision.rb +++ b/app/models/procedure_revision.rb @@ -150,7 +150,7 @@ def new_dossier def dossier_for_preview(user) dossier = Dossier - .create_with(groupe_instructeur: procedure.defaut_groupe_instructeur_for_new_dossier, autorisation_donnees: true) + .create_with(autorisation_donnees: true) .find_or_initialize_by(revision: self, user: user, for_procedure_preview: true, state: Dossier.states.fetch(:brouillon)) if dossier.new_record? diff --git a/spec/models/concern/dossier_clone_concern_spec.rb b/spec/models/concern/dossier_clone_concern_spec.rb index aa8b31481ce..fbd2501a295 100644 --- a/spec/models/concern/dossier_clone_concern_spec.rb +++ b/spec/models/concern/dossier_clone_concern_spec.rb @@ -15,7 +15,8 @@ describe '#clone' do let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private) } let(:dossier) { create(:dossier, procedure: procedure) } - let(:new_dossier) { dossier.clone } + let(:fork) { false } + let(:new_dossier) { dossier.clone(fork:) } context 'reset most attributes' do it { expect(new_dossier.id).not_to eq(dossier.id) } @@ -49,7 +50,14 @@ end context 'copies some attributes' do - it { expect(new_dossier.groupe_instructeur).to eq(dossier.groupe_instructeur) } + context 'when fork' do + let(:fork) { true } + it { expect(new_dossier.groupe_instructeur).to eq(dossier.groupe_instructeur) } + end + + context 'when not forked' do + it { expect(new_dossier.groupe_instructeur).to be_nil } + end it { expect(new_dossier.autorisation_donnees).to eq(dossier.autorisation_donnees) } it { expect(new_dossier.revision_id).to eq(dossier.revision_id) } it { expect(new_dossier.user_id).to eq(dossier.user_id) } diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 3d7af58f850..9080e54d3f3 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -466,27 +466,63 @@ after { Timecop.return } context 'when dossier is en_construction' do - before do - dossier.passer_en_construction! - dossier.reload + context 'when the procedure.routing_enabled? is false' do + before do + dossier.passer_en_construction! + dossier.reload + end + + it { expect(dossier.state).to eq(Dossier.states.fetch(:en_construction)) } + it { expect(dossier.en_construction_at).to eq(beginning_of_day) } + it { expect(dossier.depose_at).to eq(beginning_of_day) } + it { expect(dossier.traitement.state).to eq(Dossier.states.fetch(:en_construction)) } + it { expect(dossier.traitement.processed_at).to eq(beginning_of_day) } + + it 'should keep first en_construction_at date' do + Timecop.return + dossier.passer_en_instruction!(instructeur: instructeur) + dossier.repasser_en_construction!(instructeur: instructeur) + + expect(dossier.traitements.size).to eq(3) + expect(dossier.traitements.first.processed_at).to eq(beginning_of_day) + expect(dossier.traitement.processed_at.round).to eq(dossier.en_construction_at.round) + expect(dossier.depose_at).to eq(beginning_of_day) + expect(dossier.en_construction_at).to be > beginning_of_day + end end - it { expect(dossier.state).to eq(Dossier.states.fetch(:en_construction)) } - it { expect(dossier.en_construction_at).to eq(beginning_of_day) } - it { expect(dossier.depose_at).to eq(beginning_of_day) } - it { expect(dossier.traitement.state).to eq(Dossier.states.fetch(:en_construction)) } - it { expect(dossier.traitement.processed_at).to eq(beginning_of_day) } + context 'when the procedure.routing_enabled? is true' do + include Logic + let(:gi_libelle) { 'Paris' } + let!(:procedure) do + create(:procedure, + types_de_champ_public: [ + { type: :drop_down_list, libelle: 'Votre ville', options: [gi_libelle, 'Lyon', 'Marseille'] }, + { type: :text, libelle: 'Un champ texte' } + ]) + end + let!(:drop_down_tdc) { procedure.draft_revision.types_de_champ.first } + let(:dossier) { create(:dossier, :brouillon, user:, procedure:, groupe_instructeur: nil) } + let(:gi) do + create(:groupe_instructeur, + routing_rule: ds_eq(champ_value(drop_down_tdc.stable_id), + constant(gi_libelle))) + end - it 'should keep first en_construction_at date' do - Timecop.return - dossier.passer_en_instruction!(instructeur: instructeur) - dossier.repasser_en_construction!(instructeur: instructeur) + before do + procedure.groupe_instructeurs = [gi] + procedure.defaut_groupe_instructeur = gi + procedure.save! + procedure.toggle_routing + dossier.champs.first.value = gi_libelle + dossier.save! + dossier.passer_en_construction! + dossier.reload + end - expect(dossier.traitements.size).to eq(3) - expect(dossier.traitements.first.processed_at).to eq(beginning_of_day) - expect(dossier.traitement.processed_at.round).to eq(dossier.en_construction_at.round) - expect(dossier.depose_at).to eq(beginning_of_day) - expect(dossier.en_construction_at).to be > beginning_of_day + it 'RoutingEngine.compute' do + expect(dossier.groupe_instructeur).not_to be_nil + end end end From fc3a5496eb0612859c1c711cfd4da9f0a321b889 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 18 Jul 2023 14:49:30 +0200 Subject: [PATCH 06/15] =?UTF-8?q?amelioration(email=5Fusagers):=20autorise?= =?UTF-8?q?=20uniquement=20les=20instructeurs=20pr=C3=A9sent=20dans=20tous?= =?UTF-8?q?=20les=20groupes=20d'instructeur=20a=20envoyer=20un=20message?= =?UTF-8?q?=20de=20masse=20aux=20usagers=20ayant=20un=20dossier=20en=20bro?= =?UTF-8?q?uillon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/helpers/procedure_helper.rb | 8 ++++---- spec/helpers/procedure_helper_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/app/helpers/procedure_helper.rb b/app/helpers/procedure_helper.rb index 2f9e5b76fd0..d7904a06860 100644 --- a/app/helpers/procedure_helper.rb +++ b/app/helpers/procedure_helper.rb @@ -39,10 +39,10 @@ def can_manage_groupe_instructeurs?(procedure) end def can_send_groupe_message?(procedure) - procedure.dossiers - .state_brouillon - .includes(:groupe_instructeur) - .exists?(groupe_instructeur: current_instructeur.groupe_instructeurs) + total_groupe_instructeur_on_procedure = procedure.groupe_instructeurs.active.count + total_groupe_instructeur_on_instructeur = current_instructeur.groupe_instructeurs.active.where(procedure: procedure).count + + total_groupe_instructeur_on_procedure == total_groupe_instructeur_on_instructeur end def url_or_email_to_lien_dpo(procedure) diff --git a/spec/helpers/procedure_helper_spec.rb b/spec/helpers/procedure_helper_spec.rb index 6a2a920202a..7423b251e38 100644 --- a/spec/helpers/procedure_helper_spec.rb +++ b/spec/helpers/procedure_helper_spec.rb @@ -10,6 +10,26 @@ end end + describe 'can_send_groupe_message?' do + let(:procedure) { create(:procedure, groupe_instructeurs: [gi1, gi2]) } + let(:current_instructeur) { create(:instructeur) } + subject { can_send_groupe_message?(procedure) } + + context 'when current_instructeur is in all procedure.groupes_instructeur' do + let(:gi1) { create(:groupe_instructeur, instructeurs: [current_instructeur]) } + let(:gi2) { create(:groupe_instructeur, instructeurs: [current_instructeur]) } + it { is_expected.to be_truthy } + end + + context 'when current_instructeur is in all procedure.groupes_instructeur' do + let(:instructeur2) { create(:instructeur) } + let(:gi1) { create(:groupe_instructeur, instructeurs: [current_instructeur]) } + let(:gi2) { create(:groupe_instructeur, instructeurs: [instructeur2]) } + + it { is_expected.to be_falsy } + end + end + describe '#estimated_fill_duration_minutes' do subject { estimated_fill_duration_minutes(procedure.reload) } From 764f19d11a035a008df9ed082eb4a75838a24ff6 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 17 Jul 2023 09:32:14 +0200 Subject: [PATCH 07/15] tech(clean): utilise les composant DSFR pour le formulaire des commentaires --- .../shared/dossiers/messages/_form.html.haml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/app/views/shared/dossiers/messages/_form.html.haml b/app/views/shared/dossiers/messages/_form.html.haml index 5e616301cf0..7a26eb66ff8 100644 --- a/app/views/shared/dossiers/messages/_form.html.haml +++ b/app/views/shared/dossiers/messages/_form.html.haml @@ -1,20 +1,15 @@ = render NestedForms::FormOwnerComponent.new -= form_for(commentaire, url: form_url, html: { multipart: true, data: { controller: 'persisted-form', persisted_form_key_value: @dossier.present? ? dom_id(@dossier) : dom_id(@procedure, :bulk_message) } }) do |f| += form_for(commentaire, url: form_url, html: { multipart: true, data: { controller: 'persisted-form', persisted_form_key_value: dom_id(@dossier) } }) do |f| - dossier = commentaire.dossier - placeholder = t('views.shared.dossiers.messages.form.write_message_to_administration_placeholder') - if instructeur_signed_in? || administrateur_signed_in? || expert_signed_in? - placeholder = t('views.shared.dossiers.messages.form.write_message_placeholder') %p.mandatory-explanation= t('asterisk_html', scope: [:utils]) - .fr-input-group - = label_tag :commentaire_body, class: 'fr-label' do - = t('message', scope: [:utils]) - %span.mandatory * - = f.text_area :body, rows: 5, placeholder: placeholder, title: placeholder, required: true, class: 'fr-input message-textarea' - - disable_piece_jointe = defined?(disable_piece_jointe) ? disable_piece_jointe : false - - if !disable_piece_jointe - .fr-mt-3w - = render Attachment::EditComponent.new(attached_file: commentaire.piece_jointe) + = render Dsfr::InputComponent.new(form: f, attribute: :body, input_type: :text_area, opts: { rows: 5, placeholder: placeholder, title: placeholder, class: 'fr-input message-textarea'}) + + .fr-mt-3w + = render Attachment::EditComponent.new(attached_file: commentaire.piece_jointe) .fr-mt-3w = f.submit t('views.shared.dossiers.messages.form.send_message'), class: 'fr-btn', data: { disable: true } From 425d8867be4e9db06ca8ff838a77ac7f07435d0b Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 13 Jul 2023 17:58:11 +0200 Subject: [PATCH 08/15] amelioration(bulk_messages): ETQ instructeur, je peux envoyer un message aux usagers ayant un dossier en brouillon --- .../instructeurs/procedures_controller.rb | 54 +++----- app/controllers/users/dossiers_controller.rb | 1 + app/models/bulk_message.rb | 1 - app/models/commentaire.rb | 2 - app/models/dossier.rb | 2 +- app/models/groupe_instructeur.rb | 2 +- .../procedures/email_usagers.html.haml | 49 ++++---- config/locales/models/bulk_message/en.yml | 9 ++ config/locales/models/bulk_message/fr.yml | 9 ++ .../views/instructeurs/procedures/en.yml | 7 +- .../views/instructeurs/procedures/fr.yml | 5 +- .../procedures_controller_spec.rb | 115 +++++++++++------- 12 files changed, 145 insertions(+), 111 deletions(-) create mode 100644 config/locales/models/bulk_message/en.yml create mode 100644 config/locales/models/bulk_message/fr.yml diff --git a/app/controllers/instructeurs/procedures_controller.rb b/app/controllers/instructeurs/procedures_controller.rb index 5642d1fdacd..02640933eef 100644 --- a/app/controllers/instructeurs/procedures_controller.rb +++ b/app/controllers/instructeurs/procedures_controller.rb @@ -224,19 +224,18 @@ def stats def email_usagers @procedure = procedure - @commentaire = Commentaire.new - @email_usagers_dossiers = email_usagers_dossiers - @dossiers_count = @email_usagers_dossiers.count - @groupe_instructeurs = email_usagers_groupe_instructeurs_label - @bulk_messages = BulkMessage.includes(:groupe_instructeurs).where(groupe_instructeurs: { id: current_instructeur.groupe_instructeur_ids, procedure: procedure }) + @bulk_messages = BulkMessage.includes(:groupe_instructeurs).where(groupe_instructeurs: { procedure: procedure }) + @bulk_message = current_instructeur.bulk_messages.build + @dossiers_without_groupe_count = procedure.dossiers.state_brouillon.for_groupe_instructeur(nil).count end def create_multiple_commentaire @procedure = procedure errors = [] - - email_usagers_dossiers.each do |dossier| - commentaire = CommentaireService.create(current_instructeur, dossier, commentaire_params) + bulk_message = current_instructeur.bulk_messages.build(bulk_message_params) + dossiers = procedure.dossiers.state_brouillon.for_groupe_instructeur(nil) + dossiers.each do |dossier| + commentaire = CommentaireService.create(current_instructeur, dossier, bulk_message_params.except(:targets)) if commentaire.errors.empty? commentaire.dossier.update!(last_commentaire_updated_at: Time.zone.now) else @@ -244,8 +243,15 @@ def create_multiple_commentaire end end - valid_dossiers_count = email_usagers_dossiers.count - errors.count - create_bulk_message_mail(valid_dossiers_count, Dossier.states.fetch(:brouillon)) + valid_dossiers_count = dossiers.count - errors.count + bulk_message.assign_attributes( + dossier_count: valid_dossiers_count, + dossier_state: Dossier.states.fetch(:brouillon), + sent_at: Time.zone.now, + instructeur_id: current_instructeur.id, + groupe_instructeurs: GroupeInstructeur.for_dossiers(dossiers) + ) + bulk_message.save! if errors.empty? flash[:notice] = "Tous les messages ont été envoyés avec succès" @@ -262,18 +268,6 @@ def administrateurs private - def create_bulk_message_mail(dossier_count, dossier_state) - BulkMessage.create( - dossier_count: dossier_count, - dossier_state: dossier_state, - body: commentaire_params[:body], - sent_at: Time.zone.now, - instructeur_id: current_instructeur.id, - piece_jointe: commentaire_params[:piece_jointe], - groupe_instructeurs: email_usagers_groupe_instructeurs - ) - end - def assign_to_params params.require(:assign_to) .permit(:instant_expert_avis_email_notifications_enabled, :instant_email_dossier_notifications_enabled, :instant_email_message_notifications_enabled, :daily_email_notifications_enabled, :weekly_email_notifications_enabled) @@ -355,20 +349,8 @@ def current_filters @current_filters ||= procedure_presentation.filters.fetch(statut, []) end - def email_usagers_dossiers - procedure.dossiers.state_brouillon.where(groupe_instructeur: current_instructeur.groupe_instructeur_ids).includes(:groupe_instructeur) - end - - def email_usagers_groupe_instructeurs_label - email_usagers_dossiers.map(&:groupe_instructeur).uniq.map(&:label) - end - - def email_usagers_groupe_instructeurs - email_usagers_dossiers.map(&:groupe_instructeur).uniq - end - - def commentaire_params - params.require(:commentaire).permit(:body, :piece_jointe) + def bulk_message_params + params.require(:bulk_message).permit(:body) end end end diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 92e9c25b526..91419ad23fa 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -541,6 +541,7 @@ def set_dossier_as_editing_fork def update_dossier_and_compute_errors errors = [] + @dossier.assign_attributes(champs_public_params) if @dossier.champs_public_all.any?(&:changed_for_autosave?) @dossier.last_champ_updated_at = Time.zone.now diff --git a/app/models/bulk_message.rb b/app/models/bulk_message.rb index 6de6905a30c..8f54de120c1 100644 --- a/app/models/bulk_message.rb +++ b/app/models/bulk_message.rb @@ -1,5 +1,4 @@ class BulkMessage < ApplicationRecord belongs_to :instructeur has_and_belongs_to_many :groupe_instructeurs, -> { order(:label) } - has_one_attached :piece_jointe end diff --git a/app/models/commentaire.rb b/app/models/commentaire.rb index 4cac74bf128..4edbb0110a1 100644 --- a/app/models/commentaire.rb +++ b/app/models/commentaire.rb @@ -1,8 +1,6 @@ class Commentaire < ApplicationRecord include Discard::Model - belongs_to :dossier, inverse_of: :commentaires, touch: true, optional: false - belongs_to :instructeur, inverse_of: :commentaires, optional: true belongs_to :expert, inverse_of: :commentaires, optional: true has_one :dossier_correction, inverse_of: :commentaire, dependent: :nullify diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 12050c35aae..d55bf99e2fd 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -215,7 +215,7 @@ def classer_sans_suite(motivation: nil, instructeur: nil, processed_at: Time.zon } scope :for_procedure_preview, -> { where(for_procedure_preview: true) } scope :for_editing_fork, -> { where.not(editing_fork_origin_id: nil) } - + scope :for_groupe_instructeur, -> (groupe_instructeurs) { where(groupe_instructeur: groupe_instructeurs) } scope :order_by_updated_at, -> (order = :desc) { order(updated_at: order) } scope :order_by_created_at, -> (order = :asc) { order(depose_at: order, created_at: order, id: order) } scope :updated_since, -> (since) { where('dossiers.updated_at >= ?', since) } diff --git a/app/models/groupe_instructeur.rb b/app/models/groupe_instructeur.rb index 87224cf7b09..cad3b263858 100644 --- a/app/models/groupe_instructeur.rb +++ b/app/models/groupe_instructeur.rb @@ -30,7 +30,7 @@ class GroupeInstructeur < ApplicationRecord scope :for_api_v2, -> { includes(procedure: [:administrateurs]) } scope :active, -> { where(closed: false) } scope :closed, -> { where(closed: true) } - + scope :for_dossiers, -> (dossiers) { joins(:dossiers).where(dossiers: dossiers).distinct(:id) } def add(instructeur) return if instructeur.nil? return if in?(instructeur.groupe_instructeurs) diff --git a/app/views/instructeurs/procedures/email_usagers.html.haml b/app/views/instructeurs/procedures/email_usagers.html.haml index 115a47a3308..61af9344425 100644 --- a/app/views/instructeurs/procedures/email_usagers.html.haml +++ b/app/views/instructeurs/procedures/email_usagers.html.haml @@ -3,30 +3,35 @@ = render partial: 'administrateurs/breadcrumbs', locals: { steps: [[@procedure.libelle.truncate_words(10), instructeur_procedure_path(@procedure)], [t('.contact_users')]] } -.messagerie.container - - if @email_usagers_dossiers.present? - %p.notice.mb-2.mt-4 - = t('.notice', dossiers_count: pluralize(@dossiers_count, 'personne'), groupe_instructeurs: @groupe_instructeurs.join(', ')) +.messagerie.fr-container + %h1 Contacter les usagers + %p.fr-highlight + = t('.hint', count: @dossiers_without_groupe_count).html_safe - = render partial: 'shared/dossiers/messages/form', locals: { commentaire: @commentaire, form_url: create_multiple_commentaire_instructeur_procedure_path(@procedure), disable_piece_jointe: true } + - if @dossiers_without_groupe_count.positive? + = form_for(@bulk_message, url: create_multiple_commentaire_instructeur_procedure_path, html: { data: { controller: 'persisted-form', persisted_form_key_value: dom_id(@procedure, :bulk_message) } }) do |f| - - if @bulk_messages.present? - %section.list-avis.mt-8 - %h1.tab-title - Messages envoyés précédemment - %span.fr-badge= @bulk_messages.count + %p.mandatory-explanation= t('asterisk_html', scope: [:utils]) - %ul - - @bulk_messages.each do |message| - %li.one-avis.flex.align-start - .width-100 - %h2.claimant - %span.email= message.instructeur.email - %span.date message envoyé à #{@dossiers_count} usagers le #{message.sent_at.strftime('%d/%m/%y à %H:%M')} - %p= message.body - .answer.flex.align-start - - if message.piece_jointe.present? - = render Attachment::ShowComponent.new(attachment: message.piece_jointe.attachment) + = render Dsfr::InputComponent.new(form: f, attribute: :body, input_type: :text_area, opts: { rows: 5, placeholder: t('views.shared.dossiers.messages.form.write_message_placeholder'), title: t('views.shared.dossiers.messages.form.write_message_placeholder'), class: 'fr-input message-textarea'}) + + .fr-mt-3w + = f.submit t('views.shared.dossiers.messages.form.send_message'), class: 'fr-btn', data: { disable: true } - else .page-title.center - %h2 Il n’y a aucun dossier en brouillon dans vos groupes d’instructeurs + %h2 Il n’y a aucun dossier en brouillon + + - if @bulk_messages.present? + %section.list-avis.mt-8 + %h1.tab-title + Messages envoyés précédemment + %span.fr-badge= @bulk_messages.count + + %ul + - @bulk_messages.each do |message| + %li.one-avis.flex.align-start + .width-100 + %h2.claimant + %span.email= message.instructeur.email + %span.date message envoyé à #{@dossiers_count} usagers le #{message.sent_at.strftime('%d/%m/%y à %H:%M')} + %p= message.body diff --git a/config/locales/models/bulk_message/en.yml b/config/locales/models/bulk_message/en.yml new file mode 100644 index 00000000000..c1a59b743f9 --- /dev/null +++ b/config/locales/models/bulk_message/en.yml @@ -0,0 +1,9 @@ +en: + activerecord: + models: + bulk_message: + one: "Bulk message to users" + other: "Bulk message to users" + attributes: + bulk_message: + body: Content diff --git a/config/locales/models/bulk_message/fr.yml b/config/locales/models/bulk_message/fr.yml new file mode 100644 index 00000000000..04c830d1fd2 --- /dev/null +++ b/config/locales/models/bulk_message/fr.yml @@ -0,0 +1,9 @@ +fr: + activerecord: + models: + bulk_message: + one: "Message aux usagers" + other: "Messages aux usagers" + attributes: + bulk_message: + body: "Message envoyé aux destinataires :" diff --git a/config/locales/views/instructeurs/procedures/en.yml b/config/locales/views/instructeurs/procedures/en.yml index dab0dce1169..e8d267ca7b5 100644 --- a/config/locales/views/instructeurs/procedures/en.yml +++ b/config/locales/views/instructeurs/procedures/en.yml @@ -12,8 +12,11 @@ en: copy_link_button: Copy the procedure link to clipboard email_usagers: contact_users: Contact users (draft) - notice: "You will send a message to %{dossiers_count} whose files are in draft, in the instructor groups : %{groupe_instructeurs}." + hint: + zero: "There is no user with a draft." + one: "You will send a message to 1 user." + other: "You will send a message to %{count} users." administrators_list: title: "%{procedure_libelle} - n°%{procedure_id} - administrators" - stats: + stats: title: Statistics diff --git a/config/locales/views/instructeurs/procedures/fr.yml b/config/locales/views/instructeurs/procedures/fr.yml index e2d6433943c..886464b85d4 100644 --- a/config/locales/views/instructeurs/procedures/fr.yml +++ b/config/locales/views/instructeurs/procedures/fr.yml @@ -12,7 +12,10 @@ fr: copy_link_button: Copier le lien de la démarche dans le presse-papiers email_usagers: contact_users: Contacter les usagers (brouillon) - notice: "Vous allez envoyer un message à %{dossiers_count} dont les dossiers sont en brouillon, dans les groupes instructeurs : %{groupe_instructeurs}." + hint: + zero: "Aucun usager n'a de dossier en brouillon." + one: "Vous allez envoyer un message à 1 usager ayant un dossier en brouillon." + other: "Vous allez envoyer un message à %{count} usagers ayant un dossier en brouillon." administrators_list: title: "%{procedure_libelle} - n°%{procedure_id} - administrateurs" stats: diff --git a/spec/controllers/instructeurs/procedures_controller_spec.rb b/spec/controllers/instructeurs/procedures_controller_spec.rb index a6b7614f0c7..ee7b4c19f00 100644 --- a/spec/controllers/instructeurs/procedures_controller_spec.rb +++ b/spec/controllers/instructeurs/procedures_controller_spec.rb @@ -486,6 +486,76 @@ end end + describe '#email_usagers' do + let(:instructeur) { create(:instructeur) } + let(:procedure) { create(:procedure) } + let!(:gi_1) { create(:groupe_instructeur, label: 'gi_1', procedure: procedure, instructeurs: [instructeur]) } + let!(:dossier_without_groupe) { create(:dossier, :brouillon, procedure: procedure, groupe_instructeur: nil) } + + subject do + get :email_usagers, params: { procedure_id: procedure.id } + end + + it { is_expected.to redirect_to(new_user_session_path) } + + context 'when authenticated' do + before { sign_in(instructeur.user) } + it 'lists dossier brouillon in groupe_instructeur as well as dossiers_brouillon outside groupe_instructeur' do + is_expected.to have_http_status(200) + expect(assigns(:dossiers_without_groupe_count)).to eq(1) + end + end + end + + describe '#create_multiple_commentaire' do + let(:instructeur) { create(:instructeur) } + let!(:gi_p1_1) { create(:groupe_instructeur, label: '1', procedure: procedure, instructeurs: [instructeur]) } + let!(:gi_p1_2) { create(:groupe_instructeur, label: '2', procedure: procedure) } + let(:body) { "avant\napres" } + let(:bulk_message) { BulkMessage.first } + let!(:dossier) { create(:dossier, state: "brouillon", procedure: procedure, groupe_instructeur: gi_p1_1) } + let!(:dossier_2) { create(:dossier, state: "brouillon", procedure: procedure, groupe_instructeur: gi_p1_1) } + let!(:dossier_3) { create(:dossier, state: "brouillon", procedure: procedure, groupe_instructeur: gi_p1_2) } + let!(:procedure) { create(:procedure, :published, instructeurs: [instructeur]) } + + before do + sign_in(instructeur.user) + procedure + end + + let!(:dossier_4) { create(:dossier, state: "brouillon", procedure: procedure, groupe_instructeur: nil) } + before do + post :create_multiple_commentaire, + params: { + procedure_id: procedure.id, + bulk_message: { body: body } + } + end + + it "creates a commentaire for 1 dossiers" do + expect(Commentaire.count).to eq(1) + expect(dossier.commentaires).to eq([]) + expect(dossier_2.commentaires).to eq([]) + expect(dossier_3.commentaires).to eq([]) + expect(dossier_4.commentaires.first.body).to eq("avant\napres") + end + + it "creates a Bulk Message for 2 groupes instructeurs" do + expect(BulkMessage.count).to eq(1) + expect(bulk_message.body).to eq("avant\napres") + expect(bulk_message.groupe_instructeurs).to be_empty + end + + it "creates a flash notice" do + expect(flash.notice).to be_present + expect(flash.notice).to eq("Tous les messages ont été envoyés avec succès") + end + + it "redirect to instructeur_procedure_path" do + expect(response).to redirect_to instructeur_procedure_path(procedure) + end + end + describe '#download_export' do let(:instructeur) { create(:instructeur) } let!(:procedure) { create(:procedure) } @@ -563,49 +633,4 @@ it { is_expected.to have_http_status(:forbidden) } end end - - describe '#create_multiple_commentaire' do - let(:instructeur) { create(:instructeur) } - let!(:gi_p1_1) { create(:groupe_instructeur, label: '1', procedure: procedure) } - let!(:gi_p1_2) { create(:groupe_instructeur, label: '2', procedure: procedure) } - let(:body) { "avant\napres" } - let(:bulk_message) { BulkMessage.first } - let!(:dossier) { create(:dossier, state: "brouillon", procedure: procedure, groupe_instructeur: gi_p1_1) } - let!(:dossier_2) { create(:dossier, state: "brouillon", procedure: procedure, groupe_instructeur: gi_p1_1) } - let!(:dossier_3) { create(:dossier, state: "brouillon", procedure: procedure, groupe_instructeur: gi_p1_2) } - let!(:procedure) { create(:procedure, :published, instructeurs: [instructeur]) } - - before do - sign_in(instructeur.user) - instructeur.groupe_instructeurs << gi_p1_1 - procedure - post :create_multiple_commentaire, - params: { - procedure_id: procedure.id, - commentaire: { body: body } - } - end - - it "creates a commentaire for 2 dossiers" do - expect(Commentaire.count).to eq(2) - expect(dossier.commentaires.first.body).to eq("avant\napres") - expect(dossier_2.commentaires.first.body).to eq("avant\napres") - expect(dossier_3.commentaires).to eq([]) - end - - it "creates a Bulk Message for 2 groupes instructeurs" do - expect(BulkMessage.count).to eq(1) - expect(bulk_message.body).to eq("avant\napres") - expect(bulk_message.groupe_instructeurs).to match([gi_p1_1]) - end - - it "creates a flash notice" do - expect(flash.notice).to be_present - expect(flash.notice).to eq("Tous les messages ont été envoyés avec succès") - end - - it "redirect to instructeur_procedure_path" do - expect(response).to redirect_to instructeur_procedure_path(procedure) - end - end end From 367a557aafb5d2494ab3765e43513b882bf3e027 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 18 Jul 2023 16:44:03 +0200 Subject: [PATCH 09/15] amelioration(data): pour les dossiers en brouillon ayant un groupe_instructeur_id, supprime cette information --- app/components/dsfr/checkbox_component.rb | 22 ------------------- .../checkbox_component.html.haml | 20 ----------------- app/models/dossier.rb | 6 +---- ...ssier_brouillon_groupe_instructeur_id.rake | 21 ++++++++++++++++++ 4 files changed, 22 insertions(+), 47 deletions(-) delete mode 100644 app/components/dsfr/checkbox_component.rb delete mode 100644 app/components/dsfr/checkbox_component/checkbox_component.html.haml create mode 100644 lib/tasks/deployment/20230718143350_reset_dossier_brouillon_groupe_instructeur_id.rake diff --git a/app/components/dsfr/checkbox_component.rb b/app/components/dsfr/checkbox_component.rb deleted file mode 100644 index a516a2838d1..00000000000 --- a/app/components/dsfr/checkbox_component.rb +++ /dev/null @@ -1,22 +0,0 @@ -class Dsfr::CheckboxComponent < ApplicationComponent - attr_reader :error - - def initialize(form:, target:, checkboxes:, error: nil) - @form = form - @target = target - @checkboxes = checkboxes - @error = error - end - - def error? - # TODO: mettre correctement le aria-labelled-by avec l'id du div qui contient les erreurs - # https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/bouton-radio/ - @error.present? - end - - def each_checkboxes - @checkboxes.each do |button| - yield(*button.values_at(:label, :checked_value, :unchecked_value, :hint), button.except(:label, :checked_value, :unchecked_value, :hint)) - end - end -end diff --git a/app/components/dsfr/checkbox_component/checkbox_component.html.haml b/app/components/dsfr/checkbox_component/checkbox_component.html.haml deleted file mode 100644 index 5e53f15b078..00000000000 --- a/app/components/dsfr/checkbox_component/checkbox_component.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -%fieldset{ class: class_names("fr-fieldset": true, "fr-fieldset--error": error?), 'aria-labelledby': 'radio-hint-element-legend radio-hint-element-messages', role: error? ? :group : nil } - %legend.fr-fieldset__legend--regular.fr-fieldset__legend - = content - - - each_checkboxes do |label, checked_value, unchecked_value, hint, button_options| - .fr-fieldset__element - .fr-checkbox-group - = @form.check_box @target, button_options.merge(id: checked_value.to_s.parameterize, multiple: true), checked_value, unchecked_value - = @form.label @target, for: checked_value.to_s.parameterize, class: 'fr-label' do - - capture do - = label - - = button_options[:after_label] if button_options[:after_label] - - %span.fr-hint-text= hint if hint - - .fr-messages-group{ 'aria-live': 'assertive' } - - if error? - %p.fr-message.fr-message--error= error - diff --git a/app/models/dossier.rb b/app/models/dossier.rb index d55bf99e2fd..ad918b7436f 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -135,7 +135,7 @@ def classer_sans_suite(motivation: nil, instructeur: nil, processed_at: Time.zon aasm whiny_persistence: true, column: :state, enum: true do state :brouillon, initial: true - state :en_construction, before_enter: :compute_routing + state :en_construction state :en_instruction state :accepte state :refuse @@ -870,10 +870,6 @@ def submit_en_construction!(pending_correction_confirm: false) end end - def compute_routing - RoutingEngine.compute(self) - end - def after_passer_en_instruction(h) instructeur = h[:instructeur] disable_notification = h.fetch(:disable_notification, false) diff --git a/lib/tasks/deployment/20230718143350_reset_dossier_brouillon_groupe_instructeur_id.rake b/lib/tasks/deployment/20230718143350_reset_dossier_brouillon_groupe_instructeur_id.rake new file mode 100644 index 00000000000..a76286bd969 --- /dev/null +++ b/lib/tasks/deployment/20230718143350_reset_dossier_brouillon_groupe_instructeur_id.rake @@ -0,0 +1,21 @@ +namespace :after_party do + desc 'Deployment task: reset_dossier_brouillon_groupe_instructeur_id' + task reset_dossier_brouillon_groupe_instructeur_id: :environment do + puts "Running deploy task 'reset_dossier_brouillon_groupe_instructeur_id'" + + dossier_brouillon = Dossier.state_brouillon.where.not(groupe_instructeur_id: nil) + progress = ProgressReport.new(dossier_brouillon.count) + + # Put your task implementation HERE. + dossier_brouillon.in_batches do |relation| + progress.inc(relation.count) + relation.update_all(groupe_instructeur_id: nil) + end + + progress.finish + # Update task as completed. If you remove the line below, the task will + # run with every deploy (or every time you call after_party:run). + AfterParty::TaskRecord + .create version: AfterParty::TaskRecorder.new(__FILE__).timestamp + end +end From a3c6a2c46fb1a3cce4ad3767ccf8d0a56a08ab1e Mon Sep 17 00:00:00 2001 From: Martin Date: Thu, 27 Jul 2023 11:44:14 +0200 Subject: [PATCH 10/15] =?UTF-8?q?correctif(dolist):=20utilise=20des=20lien?= =?UTF-8?q?s=20vers=20les=20logos=20des=20procedures=20plut=C3=B4t=20que?= =?UTF-8?q?=20des=20attachements.inlined?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile | 1 + Gemfile.lock | 1 + app/mailers/application_mailer.rb | 14 -------------- app/mailers/dossier_mailer.rb | 8 ++++---- app/mailers/notification_mailer.rb | 2 +- app/models/procedure.rb | 12 +++++++++++- config/initializers/new_framework_defaults_7_0.rb | 2 +- spec/models/procedure_spec.rb | 14 ++++++++++++++ 8 files changed, 33 insertions(+), 21 deletions(-) diff --git a/Gemfile b/Gemfile index ea24566ab21..35dd58cd459 100644 --- a/Gemfile +++ b/Gemfile @@ -59,6 +59,7 @@ gem 'lograge' gem 'logstash-event' gem 'mailjet', require: false gem 'matrix' # needed by prawn and not default in ruby 3.1 +gem 'mini_magick' gem 'net-imap', require: false # See https://github.com/mikel/mail/pull/1439 gem 'net-pop', require: false # same gem 'net-smtp', require: false # same diff --git a/Gemfile.lock b/Gemfile.lock index cc46335aa10..b0785402f53 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -868,6 +868,7 @@ DEPENDENCIES matrix memory_profiler mina + mini_magick net-imap net-pop net-smtp diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 4577daf46d4..adb90e82ddd 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -8,18 +8,4 @@ class ApplicationMailer < ActionMailer::Base layout 'mailer' before_action -> { Sentry.set_tags(mailer: mailer_name, action: action_name) } - - # Attach the procedure logo to the email (if any). - # Returns the attachment url. - def attach_logo(procedure) - if procedure.logo.attached? - logo_filename = procedure.logo.filename.to_s - attachments.inline[logo_filename] = procedure.logo.download - attachments[logo_filename].url - end - rescue StandardError => e - # A problem occured when reading logo, maybe the logo is missing and we should clean the procedure to remove logo reference ? - Sentry.capture_exception(e, extra: { procedure_id: procedure.id }) - nil - end end diff --git a/app/mailers/dossier_mailer.rb b/app/mailers/dossier_mailer.rb index 27da3c9d7de..88d469252ba 100644 --- a/app/mailers/dossier_mailer.rb +++ b/app/mailers/dossier_mailer.rb @@ -12,7 +12,7 @@ def notify_new_draft @dossier = params[:dossier] I18n.with_locale(@dossier.user_locale) do @service = @dossier.procedure.service - @logo_url = attach_logo(@dossier.procedure) + @logo_url = @dossier.procedure.email_logo_url @subject = default_i18n_subject(libelle_demarche: @dossier.procedure.libelle) mail(to: @dossier.user_email_for(:notification), subject: @subject) do |format| @@ -27,7 +27,7 @@ def notify_new_answer I18n.with_locale(dossier.user_locale) do @dossier = dossier @service = dossier.procedure.service - @logo_url = attach_logo(dossier.procedure) + @logo_url = @dossier.procedure.email_logo_url @body = commentaire.body @subject = default_i18n_subject(dossier_id: dossier.id, libelle_demarche: dossier.procedure.libelle) @@ -52,7 +52,7 @@ def notify_pending_correction I18n.with_locale(dossier.user_locale) do @dossier = dossier @service = dossier.procedure.service - @logo_url = attach_logo(dossier.procedure) + @logo_url = @dossier.procedure.email_logo_url @correction = commentaire.dossier_correction @subject = default_i18n_subject(dossier_id: dossier.id, libelle_demarche: dossier.procedure.libelle) @@ -85,7 +85,7 @@ def notify_revert_to_instruction(dossier) I18n.with_locale(dossier.user_locale) do @dossier = dossier @service = dossier.procedure.service - @logo_url = attach_logo(dossier.procedure) + @logo_url = @dossier.procedure.email_logo_url @subject = default_i18n_subject(dossier_id: dossier.id, libelle_demarche: dossier.procedure.libelle) mail(to: dossier.user_email_for(:notification), subject: @subject) do |format| diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb index d969295b8b4..0a66929d26d 100644 --- a/app/mailers/notification_mailer.rb +++ b/app/mailers/notification_mailer.rb @@ -17,7 +17,7 @@ class NotificationMailer < ApplicationMailer def send_notification @service = @dossier.procedure.service - @logo_url = attach_logo(@dossier.procedure) + @logo_url = @dossier.procedure.email_logo_url attachments[@attachment[:filename]] = @attachment[:content] if @attachment.present? I18n.with_locale(@dossier.user_locale) do mail(subject: @subject, to: @email, template_name: 'send_notification') diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 84f7d9f8517..adbf0033dde 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -158,7 +158,9 @@ def revisions_with_pending_dossiers belongs_to :defaut_groupe_instructeur, class_name: 'GroupeInstructeur', inverse_of: false, optional: true - has_one_attached :logo + has_one_attached :logo do |attachable| + attachable.variant :email, resize_to_limit: [150, 150] + end has_one_attached :notice has_one_attached :deliberation @@ -633,6 +635,14 @@ def missing_steps result end + def email_logo_url + if logo.attached? + logo.variant(:email).processed.blob.url(expires_in: 1.year) + else + ActionController::Base.helpers.image_url(PROCEDURE_DEFAULT_LOGO_SRC) + end + end + def logo_url if logo.attached? Rails.application.routes.url_helpers.url_for(logo) diff --git a/config/initializers/new_framework_defaults_7_0.rb b/config/initializers/new_framework_defaults_7_0.rb index b90fb14e4bf..14380b98a2d 100644 --- a/config/initializers/new_framework_defaults_7_0.rb +++ b/config/initializers/new_framework_defaults_7_0.rb @@ -73,7 +73,7 @@ # generate variants to use image processing macros and ruby-vips # operations. See the upgrading guide for detail on the changes required. # The `:mini_magick` option is not deprecated; it's fine to keep using it. -Rails.application.config.active_storage.variant_processor = :vips +Rails.application.config.active_storage.variant_processor = :mini_magick # Enable parameter wrapping for JSON. # Previously this was set in an initializer. It's fine to keep using that initializer if you've customized it. diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 9291e15cdc7..ff777509865 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -1446,6 +1446,20 @@ end end + describe '#email_logo_url' do + context 'with logo' do + let!(:procedure) { create(:procedure, :with_logo) } + it 'creates a blob synchronously one time' do + expect { procedure.email_logo_url }.to change { ActiveStorage::Blob.count }.by(1) + end + + it 'creates the blob only the first time' do + procedure.email_logo_url + expect { procedure.email_logo_url }.not_to change { ActiveStorage::Blob.count } + end + end + end + describe '#average_dossier_weight' do let(:procedure) { create(:procedure, :published) } From bf6b5ea9632e8f610c075f89f14bbb5729345ed6 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 14 Aug 2023 11:09:00 +0200 Subject: [PATCH 11/15] amelioration(mailer.logo): utilise procedure_logo_url pour afficher le logo dans les mails --- app/mailers/dossier_mailer.rb | 8 ++++---- app/mailers/notification_mailer.rb | 2 +- app/models/procedure.rb | 8 -------- spec/models/procedure_spec.rb | 14 -------------- 4 files changed, 5 insertions(+), 27 deletions(-) diff --git a/app/mailers/dossier_mailer.rb b/app/mailers/dossier_mailer.rb index 88d469252ba..d9081708ee6 100644 --- a/app/mailers/dossier_mailer.rb +++ b/app/mailers/dossier_mailer.rb @@ -12,7 +12,7 @@ def notify_new_draft @dossier = params[:dossier] I18n.with_locale(@dossier.user_locale) do @service = @dossier.procedure.service - @logo_url = @dossier.procedure.email_logo_url + @logo_url = procedure_logo_url(@dossier.procedure) @subject = default_i18n_subject(libelle_demarche: @dossier.procedure.libelle) mail(to: @dossier.user_email_for(:notification), subject: @subject) do |format| @@ -27,7 +27,7 @@ def notify_new_answer I18n.with_locale(dossier.user_locale) do @dossier = dossier @service = dossier.procedure.service - @logo_url = @dossier.procedure.email_logo_url + @logo_url = procedure_logo_url(@dossier.procedure) @body = commentaire.body @subject = default_i18n_subject(dossier_id: dossier.id, libelle_demarche: dossier.procedure.libelle) @@ -52,7 +52,7 @@ def notify_pending_correction I18n.with_locale(dossier.user_locale) do @dossier = dossier @service = dossier.procedure.service - @logo_url = @dossier.procedure.email_logo_url + @logo_url = procedure_logo_url(@dossier.procedure) @correction = commentaire.dossier_correction @subject = default_i18n_subject(dossier_id: dossier.id, libelle_demarche: dossier.procedure.libelle) @@ -85,7 +85,7 @@ def notify_revert_to_instruction(dossier) I18n.with_locale(dossier.user_locale) do @dossier = dossier @service = dossier.procedure.service - @logo_url = @dossier.procedure.email_logo_url + @logo_url = procedure_logo_url(@dossier.procedure) @subject = default_i18n_subject(dossier_id: dossier.id, libelle_demarche: dossier.procedure.libelle) mail(to: dossier.user_email_for(:notification), subject: @subject) do |format| diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb index 0a66929d26d..23b9a605711 100644 --- a/app/mailers/notification_mailer.rb +++ b/app/mailers/notification_mailer.rb @@ -17,7 +17,7 @@ class NotificationMailer < ApplicationMailer def send_notification @service = @dossier.procedure.service - @logo_url = @dossier.procedure.email_logo_url + @logo_url = procedure_logo_url(@dossier.procedure) attachments[@attachment[:filename]] = @attachment[:content] if @attachment.present? I18n.with_locale(@dossier.user_locale) do mail(subject: @subject, to: @email, template_name: 'send_notification') diff --git a/app/models/procedure.rb b/app/models/procedure.rb index adbf0033dde..3436e11fef8 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -635,14 +635,6 @@ def missing_steps result end - def email_logo_url - if logo.attached? - logo.variant(:email).processed.blob.url(expires_in: 1.year) - else - ActionController::Base.helpers.image_url(PROCEDURE_DEFAULT_LOGO_SRC) - end - end - def logo_url if logo.attached? Rails.application.routes.url_helpers.url_for(logo) diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index ff777509865..9291e15cdc7 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -1446,20 +1446,6 @@ end end - describe '#email_logo_url' do - context 'with logo' do - let!(:procedure) { create(:procedure, :with_logo) } - it 'creates a blob synchronously one time' do - expect { procedure.email_logo_url }.to change { ActiveStorage::Blob.count }.by(1) - end - - it 'creates the blob only the first time' do - procedure.email_logo_url - expect { procedure.email_logo_url }.not_to change { ActiveStorage::Blob.count } - end - end - end - describe '#average_dossier_weight' do let(:procedure) { create(:procedure, :published) } From 9445c12b4d029324cb0f0d2f5f55ebe667105e9f Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Wed, 2 Aug 2023 10:32:49 +0200 Subject: [PATCH 12/15] refacto upload archive --- app/services/archive_uploader.rb | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/app/services/archive_uploader.rb b/app/services/archive_uploader.rb index 7da5ff246a5..38750d21141 100644 --- a/app/services/archive_uploader.rb +++ b/app/services/archive_uploader.rb @@ -6,19 +6,7 @@ class ArchiveUploader def upload(archive) uploaded_blob = create_and_upload_blob - begin - archive.file.purge if archive.file.attached? - rescue ActiveStorage::FileNotFoundError - archive.file.destroy - archive.file.detach - end - archive.reload - ActiveStorage::Attachment.create( - name: 'file', - record_type: 'Archive', - record_id: archive.id, - blob_id: uploaded_blob.id - ) + archive.file.attach(uploaded_blob) end def blob From c6e7c6fa9b3221c6c127c6209eee1a6ca2449884 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 18 Aug 2023 15:51:11 +0200 Subject: [PATCH 13/15] =?UTF-8?q?correct(archive=5Fuploader.rb):=20consid?= =?UTF-8?q?=C3=A8re=20les=20archives=20g=C3=A9n=C3=A9r=C3=A9es=20par=20le?= =?UTF-8?q?=20DSProxy=20comme=20=C3=A9tant=20d=C3=A9j=C3=A0=20analyz=C3=A9?= =?UTF-8?q?es.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/services/archive_uploader.rb | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/app/services/archive_uploader.rb b/app/services/archive_uploader.rb index 38750d21141..682cd2072ad 100644 --- a/app/services/archive_uploader.rb +++ b/app/services/archive_uploader.rb @@ -6,7 +6,20 @@ class ArchiveUploader def upload(archive) uploaded_blob = create_and_upload_blob - archive.file.attach(uploaded_blob) + begin + archive.file.purge if archive.file.attached? + rescue ActiveStorage::FileNotFoundError + archive.file.destroy + archive.file.detach + end + archive.reload + uploaded_blob.reload + ActiveStorage::Attachment.create( + name: 'file', + record_type: 'Archive', + record_id: archive.id, + blob_id: uploaded_blob.id + ) end def blob @@ -56,7 +69,7 @@ def blob_default_params(filepath) key: namespaced_object_key, filename: filename, content_type: 'application/zip', - metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE } + metadata: { analyzed: true, virus_scan_result: ActiveStorage::VirusScanner::SAFE } } end From d8e5ec3b6d825db10ee9cdf2a09aeb4e355145e1 Mon Sep 17 00:00:00 2001 From: Martin Date: Fri, 18 Aug 2023 16:32:16 +0200 Subject: [PATCH 14/15] =?UTF-8?q?tech(gemfile):=20met=20=C3=A0=20jour=20ac?= =?UTF-8?q?tivestorage-openstack=201.5.1=20vers=201.6.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Gemfile.lock | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index b0785402f53..4445cab47c9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -77,8 +77,8 @@ GEM activesupport (= 7.0.5.1) marcel (~> 1.0) mini_mime (>= 1.1.0) - activestorage-openstack (1.5.1) - fog-openstack (~> 1.0) + activestorage-openstack (1.6.0) + fog-openstack (>= 1.0.9) marcel rails (>= 5.2.2) activesupport (7.0.5.1) @@ -236,7 +236,7 @@ GEM tzinfo ethon (0.15.0) ffi (>= 1.15.0) - excon (0.79.0) + excon (0.100.0) factory_bot (6.1.0) activesupport (>= 5.0.0) ffi (1.15.5) @@ -254,19 +254,18 @@ GEM rack (>= 1.4, < 3) rack-protection (>= 1.5.3, <= 4.0.0) sanitize (< 7) - fog-core (2.2.3) + fog-core (2.3.0) builder excon (~> 0.71) - formatador (~> 0.2) + formatador (>= 0.2, < 2.0) mime-types fog-json (1.2.0) fog-core multi_json (~> 1.10) - fog-openstack (1.0.11) + fog-openstack (1.1.0) fog-core (~> 2.1) fog-json (>= 1.0) - ipaddress (>= 0.8) - formatador (0.2.5) + formatador (1.1.0) fugit (1.4.2) et-orbi (~> 1.1, >= 1.1.8) raabro (~> 1.4) @@ -349,7 +348,6 @@ GEM ruby-vips (>= 2.0.17, < 3) invisible_captcha (2.0.0) rails (>= 5.0) - ipaddress (0.8.3) jquery-rails (4.5.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) From 3f33c450e904364c70c44e99949ff4764963b49a Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Mon, 21 Aug 2023 15:33:34 +0200 Subject: [PATCH 15/15] =?UTF-8?q?Revert=20"tech(gemfile):=20met=20=C3=A0?= =?UTF-8?q?=20jour=20activestorage-openstack=201.5.1=20vers=201.6.0"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit d8e5ec3b6d825db10ee9cdf2a09aeb4e355145e1. La version 1.6.0 passe la méthode `service.client` en privé ce qui pose souci. --- Gemfile.lock | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4445cab47c9..b0785402f53 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -77,8 +77,8 @@ GEM activesupport (= 7.0.5.1) marcel (~> 1.0) mini_mime (>= 1.1.0) - activestorage-openstack (1.6.0) - fog-openstack (>= 1.0.9) + activestorage-openstack (1.5.1) + fog-openstack (~> 1.0) marcel rails (>= 5.2.2) activesupport (7.0.5.1) @@ -236,7 +236,7 @@ GEM tzinfo ethon (0.15.0) ffi (>= 1.15.0) - excon (0.100.0) + excon (0.79.0) factory_bot (6.1.0) activesupport (>= 5.0.0) ffi (1.15.5) @@ -254,18 +254,19 @@ GEM rack (>= 1.4, < 3) rack-protection (>= 1.5.3, <= 4.0.0) sanitize (< 7) - fog-core (2.3.0) + fog-core (2.2.3) builder excon (~> 0.71) - formatador (>= 0.2, < 2.0) + formatador (~> 0.2) mime-types fog-json (1.2.0) fog-core multi_json (~> 1.10) - fog-openstack (1.1.0) + fog-openstack (1.0.11) fog-core (~> 2.1) fog-json (>= 1.0) - formatador (1.1.0) + ipaddress (>= 0.8) + formatador (0.2.5) fugit (1.4.2) et-orbi (~> 1.1, >= 1.1.8) raabro (~> 1.4) @@ -348,6 +349,7 @@ GEM ruby-vips (>= 2.0.17, < 3) invisible_captcha (2.0.0) rails (>= 5.0) + ipaddress (0.8.3) jquery-rails (4.5.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0)