Merge pull request #129 from sgmap/accompagnateur_invite_non_tps

Accompagnateur invite
This commit is contained in:
LeSim 2017-05-29 11:52:45 +02:00 committed by GitHub
commit 31eab83f7a
40 changed files with 941 additions and 53 deletions

View file

@ -0,0 +1,92 @@
@import "typography";
@import "colors";
.avis-sign-up {
display: flex;
.left,
.right {
width: 50%;
padding: 60px 86px;
}
.left {
p {
margin: auto;
max-width: 410px;
text-align: center;
}
.description {
font-size: 30px;
line-height: 1.3;
}
.dossier {
font-size: 18px;
font-weight: bold;
margin-top: 15px;
}
}
.right {
background-color: $light-grey;
h1 {
font-size: 36px;
font-weight: bold;
margin-bottom: 60px;
}
form {
max-width: 420px;
}
label,
input {
display: block;
width: 100%;
}
label {
font-size: 14px;
line-height: 1.57;
margin: 24px 0 8px;
}
input {
border: solid 1px $border-grey;
border-radius: 4px;
height: 56px;
padding: 0 15px;
font-family: Muli;
font-size: 14px;
&:disabled {
background-color: $border-grey;
}
}
button {
display: inline-block;
height: 60px;
line-height: 60px;
border: none;
border-radius: 60px;
background-color: $blue;
color: #FFFFFF;
font-size: 16px;
font-weight: bold;
text-align: center;
width: 100%;
margin: 55px 0;
&:hover {
color: #FFFFFF;
text-decoration: none;
background-color: $light-blue;
cursor: pointer;
}
}
}
}

View file

@ -0,0 +1,95 @@
class Backoffice::AvisController < ApplicationController
before_action :authenticate_gestionnaire!, except: [:sign_up, :create_gestionnaire]
before_action :redirect_if_no_sign_up_needed, only: [:sign_up]
before_action :check_avis_exists_and_email_belongs_to_avis, only: [:sign_up, :create_gestionnaire]
def create
avis = Avis.new(create_params.merge(claimant: current_gestionnaire))
avis.dossier = dossier
email = create_params[:email]
gestionnaire = Gestionnaire.find_by(email: email)
if gestionnaire
avis.gestionnaire = gestionnaire
avis.email = nil
end
if avis.save
flash[:notice] = "Votre demande d'avis a bien été envoyée à #{email}"
end
redirect_to backoffice_dossier_path(dossier)
end
def update
if avis.update(update_params)
NotificationService.new('avis', params[:dossier_id]).notify
flash[:notice] = 'Merci, votre avis a été enregistré.'
end
redirect_to backoffice_dossier_path(avis.dossier_id)
end
def sign_up
@email = params[:email]
@dossier = Avis.includes(:dossier).find(params[:id]).dossier
render layout: 'new_application'
end
def create_gestionnaire
email = params[:email]
password = params['gestionnaire']['password']
gestionnaire = Gestionnaire.new(email: email, password: password)
if gestionnaire.save
sign_in(gestionnaire, scope: :gestionnaire)
Avis.link_avis_to_gestionnaire(gestionnaire)
avis = Avis.find(params[:id])
redirect_to url_for(backoffice_dossier_path(avis.dossier_id))
else
flash[:alert] = gestionnaire.errors.full_messages.join('<br>')
redirect_to url_for(avis_sign_up_path(params[:id], email))
end
end
private
def dossier
current_gestionnaire.dossiers.find(params[:dossier_id])
end
def avis
current_gestionnaire.avis.find(params[:id])
end
def create_params
params.require(:avis).permit(:email, :introduction)
end
def update_params
params.require(:avis).permit(:answer)
end
def redirect_if_no_sign_up_needed
avis = Avis.find(params[:id])
if current_gestionnaire.present?
# a gestionnaire is authenticated ... lets see if it can view the dossier
redirect_to backoffice_dossier_url(avis.dossier)
elsif avis.gestionnaire.present? && avis.gestionnaire.email == params[:email]
# the avis gestionnaire has already signed up and it sould sign in
redirect_to new_gestionnaire_session_url
end
end
def check_avis_exists_and_email_belongs_to_avis
if !Avis.avis_exists_and_email_belongs_to_avis?(params[:id], params[:email])
redirect_to url_for(root_path)
end
end
end

View file

@ -22,4 +22,5 @@ class Backoffice::Dossiers::ProcedureController < Backoffice::DossiersListContro
def retrieve_procedure
current_gestionnaire.procedures.find params[:id]
end
end

View file

@ -4,10 +4,13 @@ class Backoffice::DossiersController < Backoffice::DossiersListController
before_action :ensure_gestionnaire_is_authorized, only: :show
def index
return redirect_to backoffice_invitations_path if current_gestionnaire.avis.any?
procedure = current_gestionnaire.procedure_filter
if procedure.nil?
procedure_list = dossiers_list_facade.gestionnaire_procedures_name_and_id_list
if procedure_list.count == 0
flash.alert = "Vous n'avez aucune procédure d'affectée."
return redirect_to root_path
@ -20,7 +23,8 @@ class Backoffice::DossiersController < Backoffice::DossiersListController
end
def show
create_dossier_facade params[:id]
dossier_id = params[:id]
create_dossier_facade dossier_id
unless @facade.nil?
@champs_private = @facade.champs_private
@ -28,7 +32,13 @@ class Backoffice::DossiersController < Backoffice::DossiersListController
@headers_private = @champs_private.select { |champ| champ.type_champ == 'header_section' }
end
Notification.where(dossier_id: params[:id].to_i).update_all already_read: true
# if the current_gestionnaire does not own the dossier, it is here to give an advice
# and it should not remove the notifications
if current_gestionnaire.dossiers.find_by(id: dossier_id).present?
Notification.where(dossier_id: dossier_id).update_all(already_read: true)
end
@new_avis = Avis.new(introduction: "Bonjour, merci de me donner votre avis sur ce dossier.")
end
def filter
@ -185,11 +195,10 @@ class Backoffice::DossiersController < Backoffice::DossiersListController
private
def ensure_gestionnaire_is_authorized
current_gestionnaire.dossiers.find(params[:id])
rescue ActiveRecord::RecordNotFound
flash.alert = t('errors.messages.dossier_not_found')
redirect_to url_for(controller: '/backoffice')
unless current_gestionnaire.can_view_dossier?(params[:id])
flash.alert = t('errors.messages.dossier_not_found')
redirect_to url_for(controller: '/backoffice')
end
end
def create_dossier_facade dossier_id

View file

@ -1,4 +1,8 @@
class BackofficeController < ApplicationController
include SmartListing::Helper::ControllerExtensions
helper SmartListing::Helper
before_action :authenticate_gestionnaire!, only: [:invitations]
def index
if !gestionnaire_signed_in?
@ -7,4 +11,19 @@ class BackofficeController < ApplicationController
redirect_to(:backoffice_dossiers)
end
end
def invitations
pending_avis = current_gestionnaire.avis.without_answer.includes(dossier: [:procedure]).by_latest
@pending_avis = smart_listing_create :pending_avis,
pending_avis,
partial: 'backoffice/dossiers/list_invitations',
array: true
avis_with_answer = current_gestionnaire.avis.with_answer.includes(dossier: [:procedure]).by_latest
@avis_with_answer = smart_listing_create :avis_with_answer,
avis_with_answer,
partial: 'backoffice/dossiers/list_invitations',
array: true
end
end

View file

@ -14,6 +14,8 @@ class RootController < ApplicationController
return redirect_to admin_procedures_path
elsif gestionnaire_signed_in?
return redirect_to backoffice_invitations_path if current_gestionnaire.avis.any?
procedure_id = current_gestionnaire.procedure_filter
if procedure_id.nil?
procedure_list = current_gestionnaire.procedures

View file

@ -0,0 +1,9 @@
class AvisMailer < ApplicationMailer
def you_are_invited_on_dossier(avis)
@avis = avis
email = @avis.gestionnaire.try(:email) || @avis.email
mail(to: email, subject: "Donnez votre avis sur le dossier nº #{@avis.dossier.id} (#{@avis.dossier.procedure.libelle})")
end
end

29
app/models/avis.rb Normal file
View file

@ -0,0 +1,29 @@
class Avis < ApplicationRecord
belongs_to :dossier
belongs_to :gestionnaire
belongs_to :claimant, class_name: 'Gestionnaire'
after_create :notify_gestionnaire
scope :with_answer, -> { where.not(answer: nil) }
scope :without_answer, -> { where(answer: nil) }
scope :for_dossier, ->(dossier_id) { where(dossier_id: dossier_id) }
scope :by_latest, -> { order(updated_at: :desc) }
def email_to_display
gestionnaire.try(:email) || email
end
def notify_gestionnaire
AvisMailer.you_are_invited_on_dossier(self).deliver_now
end
def self.link_avis_to_gestionnaire(gestionnaire)
Avis.where(email: gestionnaire.email).update_all(email: nil, gestionnaire_id: gestionnaire.id)
end
def self.avis_exists_and_email_belongs_to_avis?(avis_id, email)
avis = Avis.find_by(id: avis_id)
avis.present? && avis.email == email
end
end

View file

@ -25,6 +25,7 @@ class Dossier < ActiveRecord::Base
has_many :invites_gestionnaires, class_name: 'InviteGestionnaire', dependent: :destroy
has_many :follows
has_many :notifications, dependent: :destroy
has_many :avis, dependent: :destroy
belongs_to :procedure
belongs_to :user

View file

@ -12,6 +12,7 @@ class Gestionnaire < ActiveRecord::Base
has_many :followed_dossiers, through: :follows, source: :dossier
has_many :follows
has_many :preference_list_dossiers
has_many :avis
after_create :build_default_preferences_list_dossier
after_create :build_default_preferences_smart_listing_page
@ -24,6 +25,11 @@ class Gestionnaire < ActiveRecord::Base
self[:procedure_filter]
end
def can_view_dossier?(dossier_id)
avis.where(dossier_id: dossier_id).any? ||
dossiers.where(id: dossier_id).any?
end
def toggle_follow_dossier dossier_id
dossier = dossier_id
dossier = Dossier.find(dossier_id) unless dossier_id.class == Dossier
@ -41,6 +47,10 @@ class Gestionnaire < ActiveRecord::Base
Follow.where(gestionnaire_id: id, dossier_id: dossier_id).any?
end
def assigned_on_procedure?(procedure_id)
procedures.find_by(id: procedure_id).present?
end
def build_default_preferences_list_dossier procedure_id=nil
PreferenceListDossier.available_columns_for(procedure_id).each do |table|

View file

@ -5,7 +5,8 @@ class Notification < ActiveRecord::Base
cerfa: 'cerfa',
piece_justificative: 'piece_justificative',
champs: 'champs',
submitted: 'submitted'
submitted: 'submitted',
avis: 'avis'
}
scope :unread, -> { where(already_read: false) }
end

View file

@ -3,7 +3,6 @@ class Procedure < ActiveRecord::Base
has_many :types_de_champ, class_name: 'TypeDeChampPublic', dependent: :destroy
has_many :types_de_champ_private, dependent: :destroy
has_many :dossiers
has_many :notifications, through: :dossiers
has_one :procedure_path, dependent: :destroy

View file

@ -35,6 +35,8 @@ class NotificationService
attribut
when 'submitted'
"Le dossier nº #{@dossier_id} a été déposé."
when 'avis'
'Un nouvel avis a été rendu'
else
'Notification par défaut'
end

View file

@ -0,0 +1,28 @@
%html
%body
%p
Bonjour,
%br
= "Vous avez été invité par #{@avis.claimant.email} à donner votre avis sur le dossier nº #{@avis.dossier.id} de la procédure : #{@avis.dossier.procedure.libelle}."
%br
Message de votre interlocuteur :
%p{ style: 'border: 1px solid grey' }
= @avis.introduction
- if @avis.gestionnaire.present?
%p
= link_to "Connectez-vous pour donner votre avis", new_gestionnaire_session_url
- else
%p
= link_to "Inscrivez-vous pour donner votre avis", avis_sign_up_url(@avis.id, @avis.email)
Bonne journée,
%br
%br
L'équipe Téléprocédures Simplifiées
%br
%br
%hr
%br
Merci de ne pas répondre à cet email. Postez directement vos questions dans votre dossier sur la plateforme.

View file

@ -0,0 +1,15 @@
.avis-sign-up
.left
%p.description= @dossier.procedure.libelle
%p.dossier Dossier n°#{@dossier.id}
.right
%h1 Créez-vous un compte
= form_for(Gestionnaire.new, url: { controller: 'backoffice/avis', action: :create_gestionnaire }, method: :post) do |f|
= f.label :email, 'Email'
= f.email_field :email, value: @email, disabled: true
= f.label :password, 'Mot de passe'
= f.password_field :password, autofocus: true, required: true, placeholder: '8 caractères minimum'
%button Créer un compte

View file

@ -0,0 +1,20 @@
- if smart_listing.collection.any?
%table#dossiers-list.table
%thead
%th
%th
Procédure
%th
Invité le
%tbody
- smart_listing.collection.each do |avis|
%tr.dossier-row{ id: "tr_dossier_#{avis.dossier.id}", 'data-dossier_url' => backoffice_dossier_url(id: avis.dossier.id) }
%td= avis.dossier.id
%td= avis.dossier.procedure.libelle
%td= avis.created_at.strftime('%d/%m/%Y %H:%M')
= smart_listing.paginate
- else
.center{ colspan: 2 }
%em Aucun dossier

View file

@ -0,0 +1,22 @@
.col-md-12
.default-data-block.default_visible
.row.show-block
.header
.title
.carret-right
.carret-down
= "#{@pending_avis.count} avis à rendre"
.body
= smart_listing_render :pending_avis
%br
.default-data-block
.row.show-block
.header
.title
.carret-right
.carret-down
= "#{@avis_with_answer.count} avis #{"rendu".pluralize(@avis_with_answer.count)}"
.body
= smart_listing_render :avis_with_answer

View file

@ -0,0 +1,4 @@
<%= smart_listing_update :pending_avis %>
<%= smart_listing_update :avis_with_answer %>
link_init();

View file

@ -0,0 +1,52 @@
- if current_gestionnaire && current_gestionnaire.assigned_on_procedure?(@facade.dossier.procedure_id)
.default-data-block.default_visible
.row.show-block.infos
.header
.col-xs-12.title
.carret-right
.carret-down
AVIS EXTERNES
.body
.display-block-on-print
- dossier_facade.dossier.avis.by_latest.each do |avis|
- if avis.answer
.panel.panel-success
.panel-heading
%strong= avis.email_to_display
a donné son avis le
= avis.updated_at.localtime.strftime('%d/%m/%Y à %H:%M')
.panel-body
%strong Vous :
= avis.introduction
%hr
%strong= "#{avis.email_to_display} :"
= avis.answer
- else
.panel.panel-info
.panel-heading
Avis demandé à
%strong= avis.email_to_display
le
= avis.created_at.localtime.strftime('%d/%m/%Y à %H:%M')
.panel-body
%strong Vous :
= avis.introduction
%hr
.center
%em Avis en attente
.hidden-print
.panel.panel-default
.panel-heading
Demander un avis externe
.panel-body
.help-block
Invitez une personne externe à consulter le dossier et à vous donner un avis sur celui ci.
%br
Cette personne pourra également contribuer au fil de messagerie, mais ne pourra pas modifier le dossier.
= simple_form_for @new_avis, url: backoffice_dossier_avis_index_path(dossier_facade.dossier.object.id) do |f|
= f.input 'email', label: "Email de la personne qui doit donner un avis"
= f.input 'introduction', label: "Message"
= f.submit "Envoyer la demande d'avis", class: 'btn btn-default'

View file

@ -1,3 +1,5 @@
= render partial: 'dossiers/edit_avis', locals: { dossier_facade: @facade }
= render partial: 'dossiers/messagerie', locals: { dossier_facade: @facade }
- if @facade.procedure.individual_with_siret
@ -51,8 +53,7 @@
= render partial: '/users/carte/map', locals: { dossier: @facade.dossier }
= render partial: 'users/carte/init_carto', locals: { dossier: @facade.dossier }
- if @current_gestionnaire && gestionnaire_signed_in? && @champs_private.count > 0
- if @current_gestionnaire && gestionnaire_signed_in? && current_gestionnaire.assigned_on_procedure?(@facade.dossier.procedure_id) && @champs_private.count > 0
.default-data-block.default_visible
.row.show-block#private-fields
.header
@ -65,3 +66,5 @@
= (private_fields_count == 1) ? "1 champ" : "#{private_fields_count} champs"
.body
= render partial: '/dossiers/infos_private_fields'
= render partial: 'dossiers/avis', locals: { dossier_facade: @facade }

View file

@ -0,0 +1,13 @@
- if current_gestionnaire
- avis_for_dossier = current_gestionnaire.avis.for_dossier(dossier_facade.dossier.id).by_latest
- if avis_for_dossier.any?
.panel.panel-default
.panel-body
%h4 Votre avis est sollicité sur le dossier :
- avis_for_dossier.each do |avis|
%hr
%p= avis.introduction
= simple_form_for avis, url: backoffice_dossier_avis_path(dossier_facade.dossier, avis) do |f|
= f.input 'answer', label: "Votre avis"
- submit_label = if avis.answer then "Modifier votre avis" else "Enregistrer votre avis" end
= f.submit submit_label, class: 'btn btn-default'

View file

@ -20,6 +20,10 @@
#infos-block
.split-hr-left
#procedure-list
- if current_gestionnaire.avis.any?
= link_to backoffice_invitations_path do
.procedure-list-element{ class: ('active' if request.path == backoffice_invitations_path) }
Invitations
- current_gestionnaire.procedures.by_libelle.each do |procedure|
= link_to backoffice_dossiers_procedure_path(procedure.id), { title: procedure.libelle } do
.procedure-list-element{ class: ('active' if procedure.id.to_s == params[:id]) }

View file

@ -2,8 +2,8 @@
.infos
#dossier_id= t('dynamics.dossiers.numéro') + @facade.dossier.id.to_s
#action-block
- if gestionnaire_signed_in?
- if current_gestionnaire && current_gestionnaire.assigned_on_procedure?(@facade.dossier.procedure_id)
#action-block
- if !@facade.dossier.read_only? || @facade.dossier.initiated?
= link_to 'Passer en instruction', backoffice_dossier_receive_path(@facade.dossier), method: :post, class: 'btn btn-danger btn-block', data: { confirm: "Confirmer vous le passage en instruction de ce dossier ?" }

View file

@ -0,0 +1 @@
= render partial: 'layouts/left_panels/left_panel_backoffice_dossierscontroller_index'

View file

@ -1,40 +1,43 @@
.col-xs-7.main-info
= @facade.dossier.procedure.libelle
.col-xs-3.options
.row
.col-xs-12
- if current_gestionnaire.follow?(@facade.dossier.id)
= link_to backoffice_dossier_follow_path(dossier_id: @facade.dossier.id), "data-method" => :put, class: "button-navbar-action", id: "suivre_dossier_#{@facade.dossier.id}" do
%i.fa.fa-user-times
Ne plus suivre
- else
= link_to backoffice_dossier_follow_path(dossier_id: @facade.dossier.id), 'data-method' => :put, class: 'button-navbar-action', id: "suivre_dossier_#{@facade.dossier.id}" do
%i.fa.fa-user-plus
Suivre le dossier
.row
.col-xs-12
#invitations.dropdown-toggle{ 'data-toggle' => 'dropdown', 'aria-haspopup' => true, 'aria-expanded' => false }
%i.fa.fa-user
= t('utils.involved')
.badge.progress-bar-info
= @facade.dossier.invites.count
.dropdown-menu.dropdown-menu-right.dropdown-pannel
%h4= t('dynamics.dossiers.followers.title')
%ul
- unless @facade.followers.empty?
- @facade.followers.each do |follower|
%li= follower.email
- else
= t('dynamics.dossiers.followers.empty')
%h4= t('dynamics.dossiers.invites.title')
%ul
- unless @facade.invites.empty?
- @facade.invites.each do |invite|
%li= invite.email
- else
= t('dynamics.dossiers.invites.empty')
- if current_gestionnaire.assigned_on_procedure?(@facade.dossier.procedure_id)
.row
.col-xs-12
- if current_gestionnaire.follow?(@facade.dossier.id)
= link_to backoffice_dossier_follow_path(dossier_id: @facade.dossier.id), "data-method" => :put, class: "button-navbar-action", id: "suivre_dossier_#{@facade.dossier.id}" do
%i.fa.fa-user-times
Ne plus suivre
- else
= link_to backoffice_dossier_follow_path(dossier_id: @facade.dossier.id), 'data-method' => :put, class: 'button-navbar-action', id: "suivre_dossier_#{@facade.dossier.id}" do
%i.fa.fa-user-plus
Suivre le dossier
.row
.col-xs-12
#invitations.dropdown-toggle{ 'data-toggle' => 'dropdown', 'aria-haspopup' => true, 'aria-expanded' => false }
%i.fa.fa-user
= t('utils.involved')
.badge.progress-bar-info
= @facade.dossier.invites.count
.dropdown-menu.dropdown-menu-right.dropdown-pannel
%h4= t('dynamics.dossiers.followers.title')
%ul
- unless @facade.followers.empty?
- @facade.followers.each do |follower|
%li= follower.email
- else
= t('dynamics.dossiers.followers.empty')
%h4= t('dynamics.dossiers.invites.title')
%p
%b Attention, les invitations sur les dossiers vont disparaître en faveur des avis externes situés en bas de la page
%ul
- unless @facade.invites.empty?
- @facade.invites.each do |invite|
%li= invite.email
- else
= t('dynamics.dossiers.invites.empty')
%li
= form_tag invites_dossier_path(dossier_id: @facade.dossier.id), method: :post, class: 'form-inline', id: 'send-invitation' do
= text_field_tag :email, '', class: 'form-control', placeholder: 'Envoyer une invitation', id: 'invitation-email'
= submit_tag 'Ajouter', class: 'btn btn-success', data: { confirm: "Envoyer l'invitation ?" }
%li
= form_tag invites_dossier_path(dossier_id: @facade.dossier.id), method: :post, class: 'form-inline', id: 'send-invitation' do
= text_field_tag :email, '', class: 'form-control', placeholder: 'Envoyer une invitation', id: 'invitation-email'
= submit_tag 'Ajouter', class: 'btn btn-success', data: { confirm: "Envoyer l'invitation ?" }

View file

@ -0,0 +1,2 @@
.col-xs-10.main-info
INVITATIONS

View file

@ -15,6 +15,7 @@ ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.irregular 'type_de_champ', 'types_de_champ'
inflect.irregular 'type_de_champ_private', 'types_de_champ_private'
inflect.irregular 'assign_to', 'assign_tos'
inflect.irregular('avis', 'avis')
end
# These inflection rules are supported but not enabled by default:
@ -24,4 +25,5 @@ end
ActiveSupport::Inflector.inflections(:fr) do |inflect|
inflect.plural(/$/, 's')
inflect.plural(/(hib|ch|bij|caill|p|gen|jouj)ou$/i, '\1oux')
inflect.irregular('avis', 'avis')
end

View file

@ -30,6 +30,9 @@ Rails.application.routes.draw do
put '/gestionnaires' => 'gestionnaires/registrations#update', :as => 'gestionnaires_registration'
end
get 'avis/:id/sign_up/email/:email' => 'backoffice/avis#sign_up', constraints: { email: /.*/ }, as: 'avis_sign_up'
post 'avis/:id/sign_up/email/:email' => 'backoffice/avis#create_gestionnaire', constraints: { email: /.*/ }
devise_scope :administrateur do
get '/administrateurs/sign_in/demo' => 'administrateurs/sessions#demo'
end
@ -167,6 +170,8 @@ Rails.application.routes.draw do
resource :private_formulaire
get 'invitations'
resources :dossiers do
post 'receive' => 'dossiers#receive'
post 'refuse' => 'dossiers#refuse'
@ -179,6 +184,7 @@ Rails.application.routes.draw do
post 'reopen' => 'dossiers#reopen'
put 'follow' => 'dossiers#follow'
resources :commentaires, only: [:index]
resources :avis, only: [:create, :update]
end
namespace :dossiers do

View file

@ -0,0 +1,13 @@
class CreateAvis < ActiveRecord::Migration[5.0]
def change
create_table :avis do |t|
t.string :email
t.text :introduction
t.text :answer
t.references :gestionnaire
t.references :dossier
t.timestamps
end
end
end

View file

@ -0,0 +1,5 @@
class AddClaimantToAvis < ActiveRecord::Migration[5.0]
def change
add_reference :avis, :claimant, foreign_key: { to_table: :gestionnaires }, null: false
end
end

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170523081220) do
ActiveRecord::Schema.define(version: 20170523092900) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -71,6 +71,20 @@ ActiveRecord::Schema.define(version: 20170523081220) do
t.index ["procedure_id"], name: "index_assign_tos_on_procedure_id", using: :btree
end
create_table "avis", force: :cascade do |t|
t.string "email"
t.text "introduction"
t.text "answer"
t.integer "gestionnaire_id"
t.integer "dossier_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "claimant_id", null: false
t.index ["claimant_id"], name: "index_avis_on_claimant_id", using: :btree
t.index ["dossier_id"], name: "index_avis_on_dossier_id", using: :btree
t.index ["gestionnaire_id"], name: "index_avis_on_gestionnaire_id", using: :btree
end
create_table "cadastres", force: :cascade do |t|
t.string "surface_intersection"
t.float "surface_parcelle"
@ -434,6 +448,7 @@ ActiveRecord::Schema.define(version: 20170523081220) do
t.index ["procedure_id"], name: "index_without_continuation_mails_on_procedure_id", using: :btree
end
add_foreign_key "avis", "gestionnaires", column: "claimant_id"
add_foreign_key "cerfas", "dossiers"
add_foreign_key "closed_mails", "procedures"
add_foreign_key "commentaires", "dossiers"

View file

@ -0,0 +1,186 @@
require 'spec_helper'
describe Backoffice::AvisController, type: :controller do
describe '#POST create' do
let(:gestionnaire){ create(:gestionnaire) }
let!(:dossier){ create(:dossier, state: 'received') }
let!(:assign_to){ create(:assign_to, gestionnaire: gestionnaire, procedure: dossier.procedure )}
subject { post :create, params: { dossier_id: dossier.id, avis: { email: gestionnaire.email, introduction: "Bonjour, regardez ce joli dossier." } } }
context 'when gestionnaire is not authenticated' do
it { is_expected.to redirect_to new_user_session_path }
it { expect{ subject }.to_not change(Avis, :count) }
end
context 'when gestionnaire is authenticated' do
before do
sign_in gestionnaire
end
context 'When gestionnaire is known' do
it { is_expected.to redirect_to backoffice_dossier_path(dossier.id) }
it { expect{ subject }.to change(Avis, :count).by(1) }
it do
subject
expect(gestionnaire.avis.last).to_not eq(nil)
expect(gestionnaire.avis.last.email).to eq(nil)
expect(gestionnaire.avis.last.dossier_id).to eq(dossier.id)
end
end
end
end
describe '#POST update' do
let(:gestionnaire){ create(:gestionnaire) }
let(:dossier){ create(:dossier, state: 'received') }
let(:avis){ create(:avis, dossier: dossier, gestionnaire: gestionnaire )}
subject { post :update, params: { dossier_id: dossier.id, id: avis.id, avis: { answer: "Ok ce dossier est valide." } } }
before :each do
notification = double('notification', notify: true)
allow(NotificationService).to receive(:new).and_return(notification)
end
context 'when gestionnaire is not authenticated' do
it { is_expected.to redirect_to new_user_session_path }
it { expect(avis.answer).to be_nil }
end
context 'when gestionnaire is authenticated' do
before do
sign_in gestionnaire
end
context 'and is invited on dossier' do
it { is_expected.to redirect_to backoffice_dossier_path(dossier.id) }
it do
subject
expect(avis.reload.answer).to eq("Ok ce dossier est valide.")
expect(NotificationService).to have_received(:new).at_least(:once)
end
end
context 'but is not invited on dossier' do
let(:gestionnaire2) { create(:gestionnaire) }
let(:avis){ create(:avis, dossier: dossier, gestionnaire: gestionnaire2 )}
it { expect{ subject }.to raise_error(ActiveRecord::RecordNotFound) }
end
end
end
describe '.sign_up' do
let(:invited_email) { 'invited@avis.com' }
let(:dossier) { create(:dossier) }
let!(:avis) { create(:avis, email: invited_email, dossier: dossier) }
let(:invitations_email) { true }
context 'when the new gestionnaire has never signed up' do
before do
expect(Avis).to receive(:avis_exists_and_email_belongs_to_avis?)
.with(avis.id.to_s, invited_email)
.and_return(invitations_email)
get :sign_up, params: { id: avis.id, email: invited_email }
end
context 'when the email belongs to the invitation' do
it { expect(subject.status).to eq(200) }
it { expect(assigns(:email)).to eq(invited_email) }
it { expect(assigns(:dossier)).to eq(dossier) }
end
context 'when the email does not belong to the invitation' do
let(:invitations_email) { false }
it { is_expected.to redirect_to root_path }
end
end
context 'when the gestionnaire has already signed up and belongs to the invitation' do
let(:gestionnaire) { create(:gestionnaire, email: invited_email) }
let!(:avis) { create(:avis, dossier: dossier, gestionnaire: gestionnaire) }
context 'when the gestionnaire is authenticated' do
before do
sign_in gestionnaire
get :sign_up, params: { id: avis.id, email: invited_email }
end
it { is_expected.to redirect_to backoffice_dossier_url(avis.dossier) }
end
context 'when the gestionnaire is not authenticated' do
before do
get :sign_up, params: { id: avis.id, email: invited_email }
end
it { is_expected.to redirect_to new_gestionnaire_session_url }
end
end
context 'when the gestionnaire has already signed up / is authenticated and does not belong to the invitation' do
let(:gestionnaire) { create(:gestionnaire, email: 'other@gmail.com') }
let!(:avis) { create(:avis, email: invited_email, dossier: dossier) }
before do
sign_in gestionnaire
get :sign_up, params: { id: avis.id, email: invited_email }
end
# redirected to dossier but then the gestionnaire gonna be banished !
it { is_expected.to redirect_to backoffice_dossier_url(avis.dossier) }
end
end
describe '.create_gestionnaire' do
let(:invited_email) { 'invited@avis.com' }
let(:dossier) { create(:dossier) }
let!(:avis) { create(:avis, email: invited_email, dossier: dossier) }
let(:avis_id) { avis.id }
let(:password) { '12345678' }
let(:created_gestionnaire) { Gestionnaire.find_by(email: invited_email) }
let(:invitations_email) { true }
before do
allow(Avis).to receive(:link_avis_to_gestionnaire)
expect(Avis).to receive(:avis_exists_and_email_belongs_to_avis?)
.with(avis_id.to_s, invited_email)
.and_return(invitations_email)
post :create_gestionnaire, params: { id: avis_id,
email: invited_email,
gestionnaire: {
password: password
} }
end
context 'when the email does not belong to the invitation' do
let(:invitations_email) { false }
it { is_expected.to redirect_to root_path }
end
context 'when the email belongs to the invitation' do
context 'when the gestionnaire creation succeeds' do
it { expect(created_gestionnaire).to be_present }
it { expect(created_gestionnaire.valid_password?(password)).to be true }
it { expect(Avis).to have_received(:link_avis_to_gestionnaire) }
it { expect(subject.current_gestionnaire).to eq(created_gestionnaire) }
it { is_expected.to redirect_to backoffice_dossier_path(dossier) }
end
context 'when the gestionnaire creation fails' do
let(:password) { '' }
it { expect(created_gestionnaire).to be_nil }
it { is_expected.to redirect_to avis_sign_up_path(avis_id, invited_email) }
it { expect(flash.alert).to eq('Password : Le mot de passe est vide') }
end
end
end
end

View file

@ -97,7 +97,7 @@ describe Backoffice::DossiersController, type: :controller do
describe 'all notifications unread are changed' do
it do
expect(Notification).to receive(:where).with(dossier_id: dossier_id).and_return(Notification::ActiveRecord_Relation)
expect(Notification).to receive(:where).with(dossier_id: dossier_id.to_s).and_return(Notification::ActiveRecord_Relation)
expect(Notification::ActiveRecord_Relation).to receive(:update_all).with(already_read: true).and_return(true)
subject
@ -109,6 +109,32 @@ describe Backoffice::DossiersController, type: :controller do
it { expect(subject).to redirect_to('/backoffice') }
end
describe 'he can invite somebody for avis' do
render_views
it { expect(subject.body).to include("Invitez une personne externe à consulter le dossier et à vous donner un avis sur celui ci.") }
end
context 'and is invited on a dossier' do
let(:dossier_invited){ create(:dossier, procedure: create(:procedure)) }
let!(:avis){ create(:avis, dossier: dossier_invited, gestionnaire: gestionnaire) }
subject { get :show, params: { id: dossier_invited.id } }
render_views
it { expect(subject.status).to eq(200) }
it { expect(subject.body).to include("Votre avis est sollicité sur le dossier") }
it { expect(subject.body).to_not include("Invitez une personne externe à consulter le dossier et à vous donner un avis sur celui ci.") }
describe 'the notifications are not marked as read' do
it do
expect(Notification).not_to receive(:where)
subject
end
end
end
end
context 'gestionnaire does not connected but dossier id is correct' do

View file

@ -2,7 +2,7 @@ require 'spec_helper'
describe BackofficeController, type: :controller do
describe 'GET #index' do
context 'when gestionnaire is not connected'do
context 'when gestionnaire is not connected' do
before do
get :index
end
@ -10,7 +10,7 @@ describe BackofficeController, type: :controller do
it { expect(response).to redirect_to :new_gestionnaire_session }
end
context 'when gestionnaire is connected'do
context 'when gestionnaire is connected' do
before do
sign_in create(:gestionnaire)
get :index
@ -19,4 +19,45 @@ describe BackofficeController, type: :controller do
it { expect(response).to redirect_to :backoffice_dossiers }
end
end
describe 'GET #invitations' do
context 'when gestionnaire is not invited on any dossiers' do
render_views
before do
sign_in create(:gestionnaire)
get :invitations
end
it { expect(response.status).to eq(200) }
it { expect(response.body).to include("INVITATIONS") }
it { expect(response.body).to include("0 avis à rendre") }
it { expect(response.body).to include("0 avis rendus") }
end
context 'when gestionnaire is invited on a dossier' do
let(:dossier){ create(:dossier) }
let(:gestionnaire){ create(:gestionnaire) }
let!(:avis){ create(:avis, dossier: dossier, gestionnaire: gestionnaire) }
render_views
before do
sign_in gestionnaire
get :invitations
end
it { expect(response.status).to eq(200) }
it { expect(response.body).to include("1 avis à rendre") }
it { expect(response.body).to include("0 avis rendus") }
it { expect(response.body).to include(dossier.procedure.libelle) }
context 'when avis is already sent' do
let!(:avis){ create(:avis, dossier: dossier, gestionnaire: gestionnaire, answer: "Voici mon avis.") }
it { expect(response.body).to include("0 avis à rendre") }
it { expect(response.body).to include("1 avis rendu") }
it { expect(response.body).to include(dossier.procedure.libelle) }
end
end
end
end

23
spec/factories/avis.rb Normal file
View file

@ -0,0 +1,23 @@
FactoryGirl.define do
factory :avis do
introduction 'Bonjour, merci de me donner votre avis sur ce dossier'
before(:create) do |avis, _evaluator|
unless avis.gestionnaire
avis.gestionnaire = create :gestionnaire
end
end
before(:create) do |avis, _evaluator|
unless avis.dossier
avis.dossier = create :dossier
end
end
before(:create) do |avis, _evaluator|
unless avis.claimant
avis.claimant = create :gestionnaire
end
end
end
end

View file

@ -0,0 +1,14 @@
require "rails_helper"
RSpec.describe AvisMailer, type: :mailer do
describe '.you_are_invited_on_dossier' do
let(:avis) { create(:avis) }
subject { described_class.you_are_invited_on_dossier(avis) }
it { expect(subject.subject).to eq("Donnez votre avis sur le dossier nº #{avis.dossier.id} (#{avis.dossier.procedure.libelle})") }
it { expect(subject.body).to include("Vous avez été invité par #{avis.claimant.email} à donner votre avis sur le dossier nº #{avis.dossier.id} de la procédure : #{avis.dossier.procedure.libelle}.") }
it { expect(subject.body).to include(avis.introduction) }
end
end

View file

@ -0,0 +1,8 @@
# Preview all emails at http://localhost:3000/rails/mailers/avis_mailer
class AvisMailerPreview < ActionMailer::Preview
def you_are_invited_on_dossier
AvisMailer.you_are_invited_on_dossier(Avis.last)
end
end

89
spec/models/avis_spec.rb Normal file
View file

@ -0,0 +1,89 @@
require 'rails_helper'
RSpec.describe Avis, type: :model do
let(:claimant) { create(:gestionnaire) }
describe '.email_to_display' do
let(:invited_email) { 'invited@avis.com' }
let!(:avis) do
avis = create(:avis, email: invited_email, dossier: create(:dossier))
avis.gestionnaire = nil
avis
end
subject { avis.email_to_display }
context 'when gestionnaire is not known' do
it{ is_expected.to eq(invited_email) }
end
context 'when gestionnaire is known' do
let!(:avis) { create(:avis, email: nil, gestionnaire: create(:gestionnaire), dossier: create(:dossier)) }
it{ is_expected.to eq(avis.gestionnaire.email) }
end
end
describe '.by_latest' do
context 'with 3 avis' do
let!(:avis){ create(:avis) }
let!(:avis2){ create(:avis, updated_at: 4.hours.ago) }
let!(:avis3){ create(:avis, updated_at: 3.hours.ago) }
subject { Avis.by_latest }
it { expect(subject).to eq([avis, avis3, avis2])}
end
end
describe ".link_avis_to_gestionnaire" do
let(:gestionnaire){ create(:gestionnaire) }
subject{ Avis.link_avis_to_gestionnaire(gestionnaire) }
context 'when there are 2 avis linked by email to a gestionnaire' do
let!(:avis){ create(:avis, email: gestionnaire.email, gestionnaire: nil) }
let!(:avis2){ create(:avis, email: gestionnaire.email, gestionnaire: nil) }
before do
subject
avis.reload
avis2.reload
end
it { expect(avis.email).to be_nil }
it { expect(avis.gestionnaire).to eq(gestionnaire) }
it { expect(avis2.email).to be_nil }
it { expect(avis2.gestionnaire).to eq(gestionnaire) }
end
end
describe '.avis_exists_and_email_belongs_to_avis' do
let(:dossier) { create(:dossier) }
let(:invited_email) { 'invited@avis.com' }
let!(:avis) { create(:avis, email: invited_email, dossier: dossier) }
subject { Avis.avis_exists_and_email_belongs_to_avis?(avis_id, email) }
context 'when the avis is unknown' do
let(:avis_id) { 666 }
let(:email) { 'unknown@mystery.com' }
it { is_expected.to be false }
end
context 'when the avis is known' do
let(:avis_id) { avis.id }
context 'when the email belongs to the invitation' do
let(:email) { invited_email }
it { is_expected.to be true }
end
context 'when the email is unknown' do
let(:email) { 'unknown@mystery.com' }
it { is_expected.to be false }
end
end
end
end

View file

@ -395,4 +395,27 @@ describe Gestionnaire, type: :model do
it { is_expected.to eq(nil) }
end
end
describe '.can_view_dossier?' do
subject{ gestionnaire.can_view_dossier?(dossier.id) }
context 'when gestionnaire is assigned on dossier' do
let!(:dossier){ create(:dossier, procedure: procedure, state: 'received') }
it { expect(subject).to be true }
end
context 'when gestionnaire is invited on dossier' do
let(:dossier){ create(:dossier) }
let!(:avis){ create(:avis, dossier: dossier, gestionnaire: gestionnaire) }
it { expect(subject).to be true }
end
context 'when gestionnaire is neither assigned nor invited on dossier' do
let(:dossier){ create(:dossier) }
it { expect(subject).to be false }
end
end
end

View file

@ -6,6 +6,7 @@ describe 'layouts/left_panels/_left_panel_backoffice_dossierscontroller_show.htm
let(:state) { 'draft' }
let(:archived) { false }
let(:gestionnaire) { create(:gestionnaire) }
let!(:assign_to) { create(:assign_to, gestionnaire: gestionnaire, procedure: dossier.procedure) }
before do
sign_in gestionnaire