diff --git a/app/controllers/users/confirmations_controller.rb b/app/controllers/users/confirmations_controller.rb deleted file mode 100644 index 1992ab9c1..000000000 --- a/app/controllers/users/confirmations_controller.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -class Users::ConfirmationsController < Devise::ConfirmationsController - # GET /resource/confirmation/new - # def new - # super - # end - - # POST /resource/confirmation - # def create - # super - # end - - # GET /resource/confirmation?confirmation_token=abcdef - # def show - # super - # end - - protected - - # The path used after resending confirmation instructions. - # def after_resending_confirmation_instructions_path_for(resource_name) - # super(resource_name) - # end - - # The path used after confirmation. - def after_confirmation_path_for(resource_name, resource) - check_invite!(resource) - - super(resource_name, resource) - end - - private - - def check_invite!(user) - Invite.where(email: user.email).update_all user_id: user.id - end -end diff --git a/app/controllers/users/dossiers/invites_controller.rb b/app/controllers/users/dossiers/invites_controller.rb index 1d451a8ca..f4c4d9dc6 100644 --- a/app/controllers/users/dossiers/invites_controller.rb +++ b/app/controllers/users/dossiers/invites_controller.rb @@ -1,7 +1,10 @@ class Users::Dossiers::InvitesController < UsersController def authenticate_user! session["user_return_to"] = request.fullpath - return redirect_to new_user_registration_path(user_email: params[:email]) if params[:email].present? && User.find_by(email: params[:email]).nil? + + if params[:email].present? && User.find_by(email: params[:email]).nil? + return redirect_to new_user_registration_path(user: { email: params[:email] }) + end super end diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb index 6bbcbdf4a..89af9412d 100644 --- a/app/controllers/users/registrations_controller.rb +++ b/app/controllers/users/registrations_controller.rb @@ -9,9 +9,13 @@ class Users::RegistrationsController < Devise::RegistrationsController # end # GET /resource/sign_up - # def new - # super - # end + def new + # Allow pre-filling the user email from a query parameter + build_resource({ email: sign_up_params[:email] }) + + yield resource if block_given? + respond_with resource + end # POST /resource def create diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 34bb4f150..f83227269 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -326,6 +326,18 @@ class Procedure < ApplicationRecord end end + def mean_traitement_time + mean_time(:en_construction_at, :processed_at) + end + + def mean_verification_time + mean_time(:en_construction_at, :en_instruction_at) + end + + def mean_instruction_time + mean_time(:en_instruction_at, :processed_at) + end + private def can_publish?(path) @@ -388,4 +400,15 @@ class Procedure < ApplicationRecord self.durees_conservation_required ||= duree_conservation_dossiers_hors_ds.present? && duree_conservation_dossiers_dans_ds.present? true end + + def mean_time(start_attribute, end_attribute) + times = dossiers + .state_termine + .pluck(start_attribute, end_attribute) + .map { |times| times[1] - times[0] } + + if times.present? + times.sum.fdiv(times.size).ceil + end + end end diff --git a/app/models/user.rb b/app/models/user.rb index f1287a153..ec69e9b6c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -24,6 +24,11 @@ class User < ApplicationRecord before_validation -> { sanitize_email(:email) } + # Callback provided by Devise + def after_confirmation + link_invites! + end + def self.find_for_france_connect(email, siret) user = User.find_by(email: email) if user.nil? @@ -49,4 +54,10 @@ class User < ApplicationRecord def owns_or_invite?(dossier) owns?(dossier) || invite?(dossier.id) end + + private + + def link_invites! + Invite.where(email: email).update_all(user_id: id) + end end diff --git a/app/views/invite_mailer/invite_guest.html.haml b/app/views/invite_mailer/invite_guest.html.haml index 2bd76f10d..45ef1cd1b 100644 --- a/app/views/invite_mailer/invite_guest.html.haml +++ b/app/views/invite_mailer/invite_guest.html.haml @@ -13,7 +13,7 @@ Afin de répondre à cette invitation, merci de vous inscrire avec l'adresse email = @invite.email sur - = "#{users_dossiers_invite_url(@invite.id)}?email=#{@invite.email}" + = users_dossiers_invite_url(@invite.id, params: { email: @invite.email }) %p Bonne journée, diff --git a/app/views/new_user/dossiers/show/_status_overview.html.haml b/app/views/new_user/dossiers/show/_status_overview.html.haml index d6e1fd3db..97dc76c4b 100644 --- a/app/views/new_user/dossiers/show/_status_overview.html.haml +++ b/app/views/new_user/dossiers/show/_status_overview.html.haml @@ -26,6 +26,11 @@ = succeed '.' do %strong votre dossier passera directement en instruction + - if dossier.procedure.mean_verification_time + - cache(dossier.procedure, expires_in: 1.week) do + %p + Le temps moyen de vérification pour cette démarche est de #{distance_of_time_in_words(dossier.procedure.mean_verification_time)}. + - elsif dossier.en_instruction? .en-instruction %p Votre dossier est complet. Il est en cours d’examen par les instructeur de l’administration. @@ -34,6 +39,10 @@ %strong vous recevrez un email avec le résultat. + - if dossier.procedure.mean_instruction_time + - cache(dossier.procedure, expires_in: 1.week) do + %p + Le temps moyen d’instruction pour cette démarche est de #{distance_of_time_in_words(dossier.procedure.mean_instruction_time)}. - elsif dossier.accepte? .accepte diff --git a/app/views/users/registrations/new.html.haml b/app/views/users/registrations/new.html.haml index 3775c572f..2a11db8ea 100644 --- a/app/views/users/registrations/new.html.haml +++ b/app/views/users/registrations/new.html.haml @@ -11,7 +11,7 @@ .column.auth-form = devise_error_messages! - = form_for User.new, url: user_registration_path, html: { class: "form" } do |f| + = form_for resource, url: user_registration_path, html: { class: "form" } do |f| %h1 Créez-vous un compte = f.label :email, "Email" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index a114f5417..363a2a904 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -47,7 +47,6 @@ fr: submit: publish: Publier reopen: Réactiver - support: info demarche: J'ai un problème lors du remplissage de mon dossier info instruction: J'ai une question sur l'instruction de mon dossier diff --git a/config/routes.rb b/config/routes.rb index 917ff895f..d8c3447a0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -70,8 +70,7 @@ Rails.application.routes.draw do devise_for :users, controllers: { sessions: 'users/sessions', registrations: 'users/registrations', - passwords: 'users/passwords', - confirmations: 'users/confirmations' + passwords: 'users/passwords' } devise_scope :user do diff --git a/spec/controllers/users/confirmations_controller_spec.rb b/spec/controllers/users/confirmations_controller_spec.rb deleted file mode 100644 index 6da736c12..000000000 --- a/spec/controllers/users/confirmations_controller_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -describe Users::ConfirmationsController, type: :controller do - let(:email) { 'mail@beta.gouv.fr' } - let(:user) do - create(:user, - email: email, - password: 'a good password', - confirmation_token: '123', - confirmed_at: nil) - end - - before { @request.env["devise.mapping"] = Devise.mappings[:user] } - - describe '#check_invite!' do - let!(:invite) { create(:invite, email: email) } - let!(:invite2) { create(:invite, email: email) } - - before { get :show, params: { confirmation_token: user.confirmation_token } } - - it 'the new user is connect at his two invite' do - expect(User.last.invites.size).to eq(2) - end - end -end diff --git a/spec/controllers/users/dossiers/invites_controller_spec.rb b/spec/controllers/users/dossiers/invites_controller_spec.rb index c9b0f5e27..ba23279b7 100644 --- a/spec/controllers/users/dossiers/invites_controller_spec.rb +++ b/spec/controllers/users/dossiers/invites_controller_spec.rb @@ -33,7 +33,7 @@ describe Users::Dossiers::InvitesController, type: :controller do context 'when email is not affected at an user' do let(:email) { 'new_user@octo.com' } - it { is_expected.to redirect_to new_user_registration_path(user_email: email) } + it { is_expected.to redirect_to new_user_registration_path(user: { email: email }) } end end end diff --git a/spec/controllers/users/registrations_controller_spec.rb b/spec/controllers/users/registrations_controller_spec.rb index 1c9a5793b..58faecedd 100644 --- a/spec/controllers/users/registrations_controller_spec.rb +++ b/spec/controllers/users/registrations_controller_spec.rb @@ -8,6 +8,22 @@ describe Users::RegistrationsController, type: :controller do @request.env["devise.mapping"] = Devise.mappings[:user] end + describe '#new' do + subject! { get :new } + + it { expect(response).to have_http_status(:ok) } + it { expect(response).to render_template(:new) } + + context 'when an email address is provided' do + render_views true + subject! { get :new, params: { user: { email: 'test@exemple.fr' } } } + + it 'prefills the form with the email address' do + expect(response.body).to include('test@exemple.fr') + end + end + end + describe '#create' do subject do post :create, params: { user: user } diff --git a/spec/factories/dossier.rb b/spec/factories/dossier.rb index ebadac8b2..8c2fe14fa 100644 --- a/spec/factories/dossier.rb +++ b/spec/factories/dossier.rb @@ -97,9 +97,9 @@ FactoryBot.define do trait :accepte do after(:create) do |dossier, _evaluator| dossier.state = Dossier.states.fetch(:accepte) - dossier.processed_at = dossier.created_at + 1.minute - dossier.en_construction_at = dossier.created_at + 2.minutes - dossier.created_at = dossier.created_at + 3.minutes + dossier.processed_at ||= dossier.created_at + 1.minute + dossier.en_construction_at ||= dossier.created_at + 2.minutes + dossier.created_at ||= dossier.created_at + 3.minutes dossier.save! end end diff --git a/spec/features/new_user/dossier_details_spec.rb b/spec/features/new_user/dossier_details_spec.rb index 41575988b..ae3ec8248 100644 --- a/spec/features/new_user/dossier_details_spec.rb +++ b/spec/features/new_user/dossier_details_spec.rb @@ -10,6 +10,10 @@ describe 'Dossier details:' do Flipflop::FeatureSet.current.test!.switch!(:new_dossier_details, true) end + after do + Flipflop::FeatureSet.current.test!.switch!(:new_dossier_details, false) + end + scenario 'the user can see the summary of the dossier status' do visit_dossier dossier @@ -19,6 +23,34 @@ describe 'Dossier details:' do expect(page).to have_text(dossier.commentaires.last.body) end + describe "the user can see the mean time they are expected to wait" do + context "the dossier is in construction" do + before do + other_dossier = create(:dossier, :accepte, :for_individual, procedure: simple_procedure, en_construction_at: 10.days.ago, en_instruction_at: Time.now) + end + + it "show the proper wait time" do + visit_dossier dossier + + expect(page).to have_text("Le temps moyen de vérification pour cette démarche est de 10 jours.") + end + end + + context "the dossier is in instruction" do + let(:dossier) { create(:dossier, :en_instruction, :for_individual, :with_commentaires, user: user, procedure: simple_procedure) } + + before do + other_dossier = create(:dossier, :accepte, :for_individual, procedure: simple_procedure, en_instruction_at: 2.months.ago, processed_at: Time.now) + end + + it "show the proper wait time" do + visit_dossier dossier + + expect(page).to have_text("Le temps moyen d’instruction pour cette démarche est de 2 mois.") + end + end + end + scenario 'the user can see and edit dossier before instruction' do visit_dossier dossier click_on 'Demande' diff --git a/spec/features/new_user/invite_spec.rb b/spec/features/new_user/invite_spec.rb index 41d5f0631..ae9de45d6 100644 --- a/spec/features/new_user/invite_spec.rb +++ b/spec/features/new_user/invite_spec.rb @@ -24,11 +24,37 @@ feature 'Invitations' do expect(page).to have_field('Libelle du champ', with: 'Some edited value') end + context 'when inviting someone without an existing account' do + let(:invite) { create(:invite_user, dossier: dossier, user: nil) } + let(:user_password) { 'l33tus3r' } + + scenario 'an invited user can register using the registration link sent in the invitation email' do + # Click the invitation link + visit users_dossiers_invite_path(invite.id, params: { email: invite.email }) + + # Create the account + expect(page).to have_current_path(new_user_registration_path, ignore_query: true) + expect(page).to have_field('user_email', with: invite.email) + fill_in 'user_password', with: user_password + click_on 'Créer un compte' + + expect(page).to have_content('lien de confirmation') + + # Confirm the email + user = User.find_by(email: invite.email) + visit Rails.application.routes.url_helpers.user_confirmation_path(confirmation_token: user.confirmation_token) + submit_login_form(user.email, user_password) + + # The user should be redirected to the dossier they was invited on + expect(page).to have_current_path(brouillon_dossier_path(dossier)) + end + end + scenario 'an invited user can see and edit the draft', js: true do visit users_dossiers_invite_path(invite) expect(page).to have_current_path(new_user_session_path) - submit_login_form(invited_user) + submit_login_form(invited_user.email, invited_user.password) expect(page).to have_current_path(brouillon_dossier_path(dossier)) expect(page).to have_no_selector('.button.invite-user-action') @@ -42,7 +68,7 @@ feature 'Invitations' do visit users_dossiers_invite_path(invite) expect(page).to have_current_path(new_user_session_path) - submit_login_form(invited_user) + submit_login_form(invited_user.email, invited_user.password) expect(page).to have_current_path(brouillon_dossier_path(dossier)) expect(page).to have_button('Soumettre le dossier', disabled: true) @@ -68,7 +94,7 @@ feature 'Invitations' do visit users_dossiers_invite_path(invite) expect(page).to have_current_path(new_user_session_path) - submit_login_form(invited_user) + submit_login_form(invited_user.email, invited_user.password) expect(page).to have_current_path(users_dossiers_invite_path(invite)) expect(page).to have_no_selector('.button.invite-user-action') expect(page).to have_text("Dossier nº #{dossier.id}") @@ -92,13 +118,13 @@ feature 'Invitations' do def log_in(user) visit '/' click_on 'Connexion' - submit_login_form(user) + submit_login_form(user.email, user.password) expect(page).to have_current_path(dossiers_path) end - def submit_login_form(user) - fill_in 'user_email', with: user.email - fill_in 'user_password', with: user.password + def submit_login_form(email, password) + fill_in 'user_email', with: email + fill_in 'user_password', with: password click_on 'Se connecter' end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 0175811f2..d4217f023 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,6 +1,25 @@ require 'spec_helper' describe User, type: :model do + describe '#after_confirmation' do + let(:email) { 'mail@beta.gouv.fr' } + let!(:invite) { create(:invite, email: email) } + let!(:invite2) { create(:invite, email: email) } + let(:user) do + create(:user, + email: email, + password: 'a good password', + confirmation_token: '123', + confirmed_at: nil) + end + + it 'when confirming a user, it links the pending invitations to this user' do + expect(user.invites.size).to eq(0) + user.confirm + expect(user.reload.invites.size).to eq(2) + end + end + describe '#find_for_france_connect' do let(:siret) { '00000000000000' } context 'when user exist' do