diff --git a/app/controllers/admin/accompagnateurs_controller.rb b/app/controllers/admin/accompagnateurs_controller.rb index 83f9a6c94..ef47687a8 100644 --- a/app/controllers/admin/accompagnateurs_controller.rb +++ b/app/controllers/admin/accompagnateurs_controller.rb @@ -23,7 +23,13 @@ class Admin::AccompagnateursController < AdminController end def update - AccompagnateurService.change_assignement! Gestionnaire.find(params[:accompagnateur_id]), Procedure.find(params[:procedure_id]), params[:to] + gestionnaire = Gestionnaire.find(params[:accompagnateur_id]) + procedure = Procedure.find(params[:procedure_id]) + to = params[:to] + + + AccompagnateurService.change_assignement! gestionnaire, procedure, to + AccompagnateurService.build_default_column gestionnaire, procedure, to flash.notice = "Assignement effectué" redirect_to admin_procedure_accompagnateurs_path, procedure_id: params[:procedure_id] diff --git a/app/controllers/backoffice/dossiers/procedure_controller.rb b/app/controllers/backoffice/dossiers/procedure_controller.rb new file mode 100644 index 000000000..bb43d4584 --- /dev/null +++ b/app/controllers/backoffice/dossiers/procedure_controller.rb @@ -0,0 +1,35 @@ +class Backoffice::Dossiers::ProcedureController < ApplicationController + include SmartListing::Helper::ControllerExtensions + helper SmartListing::Helper + + before_action :authenticate_gestionnaire! + + def show + cookies[:liste] = params[:liste] || cookies[:liste] || 'a_traiter' + smartlisting_dossier cookies[:liste] + + render 'backoffice/dossiers/index' + rescue ActiveRecord::RecordNotFound + flash[:alert] = "Cette procédure n'existe pas ou vous n'y avez pas accès." + redirect_to backoffice_dossiers_path + end + + private + + def smartlisting_dossier liste + create_dossiers_list_facade liste + + @dossiers = smart_listing_create :dossiers, + @dossiers_list_facade.dossiers_to_display, + partial: "backoffice/dossiers/list", + array: true + end + + def create_dossiers_list_facade liste='a_traiter' + @dossiers_list_facade = DossiersListFacades.new current_gestionnaire, liste, retrieve_procedure + end + + def retrieve_procedure + current_gestionnaire.procedures.find params[:id] + end +end diff --git a/app/controllers/backoffice/dossiers_controller.rb b/app/controllers/backoffice/dossiers_controller.rb index dde387679..2497f1199 100644 --- a/app/controllers/backoffice/dossiers_controller.rb +++ b/app/controllers/backoffice/dossiers_controller.rb @@ -5,7 +5,8 @@ class Backoffice::DossiersController < ApplicationController before_action :authenticate_gestionnaire! def index - smartlisting_dossier (params[:liste] || 'a_traiter') + cookies[:liste] = params[:liste] || cookies[:liste] || 'a_traiter' + smartlisting_dossier (cookies[:liste]) end def show @@ -95,6 +96,8 @@ class Backoffice::DossiersController < ApplicationController def follow follow = current_gestionnaire.toggle_follow_dossier params[:dossier_id] + current_gestionnaire.dossiers.find(params[:dossier_id]).next_step! 'gestionnaire', 'follow' + flash.notice = (follow.class == Follow ? 'Dossier suivi' : 'Dossier relaché') redirect_to request.referer end @@ -123,7 +126,7 @@ class Backoffice::DossiersController < ApplicationController end def create_dossiers_list_facade liste='a_traiter' - @dossiers_list_facade = DossiersListFacades.new current_gestionnaire, liste + @dossiers_list_facade = DossiersListFacades.new current_gestionnaire, liste, retrieve_procedure end def create_dossier_facade dossier_id @@ -133,4 +136,10 @@ class Backoffice::DossiersController < ApplicationController flash.alert = t('errors.messages.dossier_not_found') redirect_to url_for(controller: '/backoffice') end + + + def retrieve_procedure + return if params[:procedure_id].blank? + current_gestionnaire.procedures.find params[:procedure_id] + end end diff --git a/app/controllers/backoffice/preference_list_dossier_controller.rb b/app/controllers/backoffice/preference_list_dossier_controller.rb index 034ff1fb1..0b9e564b3 100644 --- a/app/controllers/backoffice/preference_list_dossier_controller.rb +++ b/app/controllers/backoffice/preference_list_dossier_controller.rb @@ -3,6 +3,7 @@ class Backoffice::PreferenceListDossierController < ApplicationController helper SmartListing::Helper before_action :authenticate_gestionnaire! + before_action :params_procedure_id def add PreferenceListDossier.create( @@ -13,14 +14,17 @@ class Backoffice::PreferenceListDossierController < ApplicationController bootstrap_lg: params[:bootstrap_lg], order: nil, filter: nil, - gestionnaire: current_gestionnaire + gestionnaire: current_gestionnaire, + procedure_id: params_procedure_id ) render partial: 'backoffice/dossiers/pref_list', formats: :js end def reload_pref_list - render partial: 'backoffice/dossiers/pref_list' + @dossiers_list_facade = DossiersListFacades.new current_gestionnaire, '', retrieve_procedure + + render partial: 'backoffice/dossiers/pref_list', id: params_procedure_id end def delete @@ -28,4 +32,15 @@ class Backoffice::PreferenceListDossierController < ApplicationController render partial: 'backoffice/dossiers/pref_list', formats: :js end + + private + + def params_procedure_id + @procedure_id ||= params[:procedure_id] + end + + def retrieve_procedure + return if params[:procedure_id].blank? + current_gestionnaire.procedures.find params_procedure_id + end end diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 4193859c9..891bf600b 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -10,7 +10,8 @@ class Users::DossiersController < UsersController end def index - @dossiers_list_facade = DossiersListFacades.new current_user, (params[:liste] || 'a_traiter') + cookies[:liste] = params[:liste] || cookies[:liste] || 'a_traiter' + @dossiers_list_facade = DossiersListFacades.new current_user, cookies[:liste] @dossiers = smart_listing_create :dossiers, @dossiers_list_facade.dossiers_to_display, diff --git a/app/decorators/france_connect_information_decorator.rb b/app/decorators/france_connect_information_decorator.rb index 206361fe8..27e69987d 100644 --- a/app/decorators/france_connect_information_decorator.rb +++ b/app/decorators/france_connect_information_decorator.rb @@ -1,4 +1,7 @@ class FranceConnectInformationDecorator < Draper::Decorator delegate_all - + + def gender_fr + gender == 'female' ? 'Mme' : 'Mr' + end end diff --git a/app/facades/dossiers_list_facades.rb b/app/facades/dossiers_list_facades.rb index 576e4d55a..87a72b22f 100644 --- a/app/facades/dossiers_list_facades.rb +++ b/app/facades/dossiers_list_facades.rb @@ -1,12 +1,15 @@ class DossiersListFacades - def initialize current_devise_profil, liste + include Rails.application.routes.url_helpers + + def initialize current_devise_profil, liste, procedure = nil @current_devise_profil = current_devise_profil @liste = liste + @procedure = procedure end def service if gestionnaire? - @service ||= DossiersListGestionnaireService.new @current_devise_profil, @liste + @service ||= DossiersListGestionnaireService.new @current_devise_profil, @liste, @procedure elsif user? @service ||= DossiersListUserService.new @current_devise_profil, @liste end @@ -16,10 +19,22 @@ class DossiersListFacades @liste end + def gestionnaire_procedures_name_and_id_list + @current_devise_profil.procedures.order('libelle ASC').inject([]) { |acc, procedure| acc.push({id: procedure.id, libelle: procedure.libelle})} + end + + def procedure_id + @procedure.nil? ? nil : @procedure.id + end + def dossiers_to_display service.dossiers_to_display end + def preference_list_dossiers_filter + @list_table_columns ||= @current_devise_profil.preference_list_dossiers.where(procedure: @procedure).order(:id) + end + def nouveaux_class (@liste == 'nouveaux' ? 'active' : '') end @@ -101,6 +116,30 @@ class DossiersListFacades def invite_total service.invite.count end + + def nouveaux_url + base_url 'nouveaux' + end + + def a_traiter_url + base_url 'a_traiter' + end + + def en_attente_url + base_url 'en_attente' + end + + def deposes_url + base_url 'deposes' + end + + def a_instruire_url + base_url 'a_instruire' + end + + def termine_url + base_url 'termine' + end private @@ -111,4 +150,8 @@ class DossiersListFacades def user? @current_devise_profil.class == User end + + def base_url liste + @procedure.nil? ? backoffice_dossiers_path(liste: liste) : backoffice_dossiers_procedure_path(id: @procedure.id, liste: liste) + end end \ No newline at end of file diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 623cb9515..e94ffa375 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -97,7 +97,7 @@ class Dossier < ActiveRecord::Base end def next_step! role, action - unless %w(initiate update comment valid submit receive refuse without_continuation close).include?(action) + unless %w(initiate follow update comment valid submit receive refuse without_continuation close).include?(action) fail 'action is not valid' end @@ -132,6 +132,10 @@ class Dossier < ActiveRecord::Base elsif initiated? replied! end + when 'follow' + if initiated? + updated! + end when 'valid' if updated? validated! diff --git a/app/models/gestionnaire.rb b/app/models/gestionnaire.rb index 68d8f30ee..9ed8990fa 100644 --- a/app/models/gestionnaire.rb +++ b/app/models/gestionnaire.rb @@ -35,7 +35,7 @@ class Gestionnaire < ActiveRecord::Base def build_default_preferences_list_dossier - PreferenceListDossier.available_columns.each do |table| + PreferenceListDossier.available_columns_for.each do |table| table.second.each do |column| if valid_couple_table_attr? table.first, column.first diff --git a/app/models/preference_list_dossier.rb b/app/models/preference_list_dossier.rb index a6a57f985..c5af12636 100644 --- a/app/models/preference_list_dossier.rb +++ b/app/models/preference_list_dossier.rb @@ -1,19 +1,27 @@ class PreferenceListDossier < ActiveRecord::Base belongs_to :gestionnaire + belongs_to :procedure def table_attr return self.attr if table.nil? || table.empty? table + '.' + attr end - def self.available_columns - { + def self.available_columns_for procedure_id = nil + columns = { dossier: columns_dossier, procedure: columns_procedure, entreprise: columns_entreprise, etablissement: columns_etablissement, - user: columns_user + user: columns_user, + france_connect: columns_france_connect } + + columns = columns.merge({ + champs: columns_champs_procedure(procedure_id) + }) unless procedure_id.nil? + + columns end private @@ -64,7 +72,6 @@ class PreferenceListDossier < ActiveRecord::Base def self.columns_user table = 'user' - { email: create_column('Email', table, 'email', 'email', 2) } @@ -74,12 +81,23 @@ class PreferenceListDossier < ActiveRecord::Base table = 'france_connect_information' { - gender: create_column('Civilité', table, 'gender', 'gender', 1), - given_name: create_column('Prénom', table, 'given_name', 'given_name', 2), - family_name: create_column('Nom', table, 'family_name', 'family_name', 2) + gender: create_column('Civilité (FC)', table, 'gender', 'gender_fr', 1), + given_name: create_column('Prénom (FC)', table, 'given_name', 'given_name', 2), + family_name: create_column('Nom (FC)', table, 'family_name', 'family_name', 2) } end + def self.columns_champs_procedure procedure_id + table = 'champs' + + Procedure.find(procedure_id).types_de_champ.inject({}) do |acc, type_de_champ| + acc = acc.merge({ + "type_de_champ_#{type_de_champ.id}" => create_column(type_de_champ.libelle, table, type_de_champ.id, 'value', 2) + }) if type_de_champ.field_for_list? + acc + end + end + def self.create_column libelle, table, attr, attr_decorate, bootstrap_lg { libelle: libelle, diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 317f70f44..1e8d79bf9 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -15,6 +15,8 @@ class Procedure < ActiveRecord::Base has_many :assign_to, dependent: :destroy has_many :gestionnaires, through: :assign_to + has_many :preference_list_dossiers + delegate :use_api_carto, to: :module_api_carto accepts_nested_attributes_for :types_de_champ, :reject_if => proc { |attributes| attributes['libelle'].blank? }, :allow_destroy => true @@ -87,7 +89,7 @@ class Procedure < ActiveRecord::Base end def clone - procedure = self.deep_clone(include: [:types_de_piece_justificative, :types_de_champ, :module_api_carto, :mail_templates]) + procedure = self.deep_clone(include: [:types_de_piece_justificative, :types_de_champ, :types_de_champ_private, :module_api_carto, :mail_templates]) procedure.archived = false procedure.published = false procedure.logo_secure_token = nil diff --git a/app/models/type_de_champ.rb b/app/models/type_de_champ.rb index db33a1a9c..3950d37ea 100644 --- a/app/models/type_de_champ.rb +++ b/app/models/type_de_champ.rb @@ -22,7 +22,6 @@ class TypeDeChamp < ActiveRecord::Base accepts_nested_attributes_for :drop_down_list - validates :libelle, presence: true, allow_blank: false, allow_nil: false validates :type_champ, presence: true, allow_blank: false, allow_nil: false # validates :order_place, presence: true, allow_blank: false, allow_nil: false @@ -30,4 +29,8 @@ class TypeDeChamp < ActiveRecord::Base def self.type_de_champs_list_fr type_champs.map { |champ| [ I18n.t("activerecord.attributes.type_de_champ.type_champs.#{champ.last}"), champ.first ] } end + + def field_for_list? + !(type_champ == 'textarea' || type_champ == 'header_section') + end end \ No newline at end of file diff --git a/app/services/accompagnateur_service.rb b/app/services/accompagnateur_service.rb index 119560418..8a254e06f 100644 --- a/app/services/accompagnateur_service.rb +++ b/app/services/accompagnateur_service.rb @@ -9,4 +9,16 @@ class AccompagnateurService AssignTo.delete_all(gestionnaire: accompagnateur, procedure: procedure) end end + + def self.build_default_column accompagnateur, procedure, to + return unless to == ASSIGN + return unless PreferenceListDossier.where(gestionnaire: accompagnateur, procedure: procedure).empty? + + accompagnateur.preference_list_dossiers.each do |pref| + clone = pref.dup + + clone.procedure = procedure + clone.save + end + end end \ No newline at end of file diff --git a/app/services/dossiers_list_gestionnaire_service.rb b/app/services/dossiers_list_gestionnaire_service.rb index 2ef534620..6e3cda519 100644 --- a/app/services/dossiers_list_gestionnaire_service.rb +++ b/app/services/dossiers_list_gestionnaire_service.rb @@ -1,7 +1,8 @@ class DossiersListGestionnaireService - def initialize current_devise_profil, liste + def initialize current_devise_profil, liste, procedure = nil @current_devise_profil = current_devise_profil @liste = liste + @procedure = procedure end def dossiers_to_display @@ -10,35 +11,34 @@ class DossiersListGestionnaireService 'en_attente' => waiting_for_user, 'deposes' => deposes, 'a_instruire' => a_instruire, - 'termine' => termine, - 'suivi' => suivi}[@liste] + 'termine' => termine}[@liste] end def nouveaux - @nouveaux ||= @current_devise_profil.dossiers.nouveaux + @nouveaux ||= filter_dossiers.nouveaux end def waiting_for_gestionnaire - @waiting_for_gestionnaire ||= @current_devise_profil.dossiers.waiting_for_gestionnaire + @waiting_for_gestionnaire ||= filter_dossiers.waiting_for_gestionnaire end def waiting_for_user - @waiting_for_user ||= @current_devise_profil.dossiers.waiting_for_user + @waiting_for_user ||= filter_dossiers.waiting_for_user end def deposes - @deposes ||= @current_devise_profil.dossiers.deposes + @deposes ||= filter_dossiers.deposes end def a_instruire - @a_instruire ||= @current_devise_profil.dossiers.a_instruire + @a_instruire ||= filter_dossiers.a_instruire end def termine - @termine ||= @current_devise_profil.dossiers.termine + @termine ||= filter_dossiers.termine end - def suivi - @suivi ||= @current_devise_profil.dossiers_follow + def filter_dossiers + @filter_dossiers ||= @procedure.nil? ? @current_devise_profil.dossiers : @procedure.dossiers end end \ No newline at end of file diff --git a/app/views/backoffice/dossiers/_list.html.haml b/app/views/backoffice/dossiers/_list.html.haml index 8add886a6..f9fa09309 100644 --- a/app/views/backoffice/dossiers/_list.html.haml +++ b/app/views/backoffice/dossiers/_list.html.haml @@ -1,19 +1,29 @@ - unless smart_listing.empty? %table.table %thead - - current_gestionnaire.preference_list_dossiers.order(:id).each do |preference| - %th{class: "col-md-#{preference.bootstrap_lg} col-lg-#{preference.bootstrap_lg}"}= smart_listing.sortable preference.libelle, preference.table_attr + - @dossiers_list_facade.preference_list_dossiers_filter.each do |preference| + %th{class: "col-md-#{preference.bootstrap_lg} col-lg-#{preference.bootstrap_lg}"} + - if preference.table == 'champs' + = preference.libelle + -else + = smart_listing.sortable preference.libelle, preference.table_attr + %th.col-md-1.col-lg-1.center Actions %th.col-md-1.col-lg-1.center Abonnés - @dossiers.each do |dossier| %tr - - current_gestionnaire.preference_list_dossiers.order(:id).each_with_index do |preference, index| + - @dossiers_list_facade.preference_list_dossiers_filter.each_with_index do |preference, index| %td - if preference.table.nil? || preference.table.empty? - value = dossier.decorate.public_send(preference.attr_decorate) + - elsif preference.table == 'champs' + - value = dossier.champs.find_by_type_de_champ_id(preference.attr).value - else - - value = dossier.public_send(preference.table).decorate.public_send(preference.attr_decorate) + - begin + - value = dossier.public_send(preference.table).decorate.public_send(preference.attr_decorate) + - rescue NoMethodError + - value = '' - if index == 0 = link_to value, backoffice_dossier_path(id: dossier.id) diff --git a/app/views/backoffice/dossiers/_onglets.html.haml b/app/views/backoffice/dossiers/_onglets.html.haml index 2d88d5b7b..cb69ef779 100644 --- a/app/views/backoffice/dossiers/_onglets.html.haml +++ b/app/views/backoffice/dossiers/_onglets.html.haml @@ -1,46 +1,52 @@ =link_to 'Tous mes dossiers en CSV', backoffice_download_dossiers_tps_path, {class: 'btn btn-success btn-sm', style: 'float: right; margin-right: 4%; margin-top: 7px'} %h1 Gestion des dossiers -%br + +#filter_by_procedure{style:'margin-left: 5%'} + %select{onchange: 'location = this.value', style:'margin-top: 10px; margin-bottom: 10px', id: 'filter_by_procedure_select'} + %option{value: backoffice_dossiers_path} + - @dossiers_list_facade.gestionnaire_procedures_name_and_id_list.each do |procedure| + %option{value: backoffice_dossiers_procedure_path(procedure[:id]), ('selected' if procedure[:id] == params[:id].to_i) => '' } + = truncate(procedure[:libelle], {length: 50}) #onglets %ul.nav.nav-tabs %li{ class: (@dossiers_list_facade.nouveaux_class)} - %a{:href => "#{url_for backoffice_dossiers_path(liste: 'nouveaux')}", 'data-toggle' => :tooltip, title: 'Les nouveaux dossiers non ouverts.'} + %a{:href => "#{url_for @dossiers_list_facade.nouveaux_url}", 'data-toggle' => :tooltip, title: 'Les nouveaux dossiers non ouverts.'} %h5.text-info = "Nouveaux " .badge.progress-bar-info =@dossiers_list_facade.nouveaux_total %li{ class: (@dossiers_list_facade.a_traiter_class) } - %a{:href => "#{url_for backoffice_dossiers_path(liste: 'a_traiter')}", 'data-toggle' => :tooltip, title: 'Les dossiers qui requièrent une action de votre part.'} + %a{:href => "#{url_for @dossiers_list_facade.a_traiter_url}", 'data-toggle' => :tooltip, title: 'Les dossiers qui requièrent une action de votre part.'} %h5.text-danger = "Action requise" .badge.progress-bar-danger =@dossiers_list_facade.a_traiter_total %li{ class: (@dossiers_list_facade.en_attente_class) } - %a{:href => "#{url_for backoffice_dossiers_path(liste: 'en_attente')}", 'data-toggle' => :tooltip, title: 'Les dossiers en attentes d\'une action de la part de l\'usager.'} + %a{:href => "#{url_for @dossiers_list_facade.en_attente_url}", 'data-toggle' => :tooltip, title: 'Les dossiers en attentes d\'une action de la part de l\'usager.'} %h5.text-default ="Attente usager " .badge.progress-bar-default =@dossiers_list_facade.en_attente_total %li{ class: (@dossiers_list_facade.deposes_class) } - %a{:href => "#{url_for backoffice_dossiers_path(liste: 'deposes')}", 'data-toggle' => :tooltip, title: 'Les dossiers qui ont été validés et déposés par les usager qui attendent une réponse de bonne réception avant instruction.'} + %a{:href => "#{url_for @dossiers_list_facade.deposes_url}", 'data-toggle' => :tooltip, title: 'Les dossiers qui ont été validés et déposés par les usager qui attendent une réponse de bonne réception avant instruction.'} %h5.text-purple ="À réceptionner" .badge.progress-bar-purple =@dossiers_list_facade.deposes_total %li{ class: (@dossiers_list_facade.a_instruire_class) } - %a{:href => "#{url_for backoffice_dossiers_path(liste: 'a_instruire')}", 'data-toggle' => :tooltip, title: 'Les dossiers qui ont été notifiés comme bien réceptionnés et qui attendent un verdict final.'} + %a{:href => "#{url_for @dossiers_list_facade.a_instruire_url}", 'data-toggle' => :tooltip, title: 'Les dossiers qui ont été notifiés comme bien réceptionnés et qui attendent un verdict final.'} %h5.text-warning = "À instruire" .badge.progress-bar-warning =@dossiers_list_facade.a_instruire_total %li{ class: (@dossiers_list_facade.termine_class) } - %a{:href => "#{url_for backoffice_dossiers_path(liste: 'termine')}",'data-toggle' => :tooltip, title: 'Tous les dossiers qui ont été traité avec un statut "Validé", "Refusé" ou "Sans suite "'} + %a{:href => "#{url_for @dossiers_list_facade.termine_url}",'data-toggle' => :tooltip, title: 'Tous les dossiers qui ont été traité avec un statut "Validé", "Refusé" ou "Sans suite "'} %h5.text-success = "Terminé" .badge.progress-bar-success diff --git a/app/views/backoffice/dossiers/_pref_list.html.haml b/app/views/backoffice/dossiers/_pref_list.html.haml index e802bb638..45825b606 100644 --- a/app/views/backoffice/dossiers/_pref_list.html.haml +++ b/app/views/backoffice/dossiers/_pref_list.html.haml @@ -9,10 +9,11 @@ Actuelles %ul - - current_gestionnaire.preference_list_dossiers.order(:id).each_with_index do |preference, index| + - @dossiers_list_facade.preference_list_dossiers_filter.each_with_index do |preference, index| %li = form_tag backoffice_preference_list_dossier_delete_path, method: :delete, remote: true do = hidden_field_tag :pref_id, preference.id + = hidden_field_tag :procedure_id, preference.procedure_id = preference.libelle %button.btn.btn-default.btn-xs{type: :submit, id: "delete_pref_list_#{preference[:table]}_#{preference[:attr]}"} %i.fa.fa-minus @@ -21,11 +22,11 @@ Disponibles %table - - PreferenceListDossier.available_columns.each_with_index do |tables, index| - - if index%2 == 0 + - PreferenceListDossier.available_columns_for(@dossiers_list_facade.procedure_id).each_with_index do |tables, index| + - if index%2 == 0 || tables.first == :champs %tr - %td.col-sm-5.col-md-5.col-lg-5{style: 'vertical-align: top'} + %td.col-sm-5.col-md-5.col-lg-5{style: 'vertical-align: top', colspan: (tables.first == :champs ? 2 : 1)} %h5= tables.first.to_s.gsub('_', ' ').capitalize %ul - tables.second.each do |columns| @@ -36,6 +37,7 @@ = hidden_field_tag :attr, columns.second[:attr] = hidden_field_tag :attr_decorate, columns.second[:attr_decorate] = hidden_field_tag :bootstrap_lg, columns.second[:bootstrap_lg] + = hidden_field_tag :procedure_id, @dossiers_list_facade.procedure_id = columns.second[:libelle] %button.btn.btn-default.btn-xs{type: :submit, id: "add_pref_list_#{columns.second[:table]}_#{columns.second[:attr]}"} diff --git a/app/views/backoffice/dossiers/_pref_list.js.erb b/app/views/backoffice/dossiers/_pref_list.js.erb index 2d421e10f..e15e3f068 100644 --- a/app/views/backoffice/dossiers/_pref_list.js.erb +++ b/app/views/backoffice/dossiers/_pref_list.js.erb @@ -1,12 +1,12 @@ $.ajax({ method: 'get', - url: '/backoffice/preference_list_dossier/reload_smartlisting', + url: '/backoffice/preference_list_dossier/reload_smartlisting?procedure_id=<%= @procedure_id %>', async: true }); $.ajax({ methd: 'get', - url: '/backoffice/preference_list_dossier/reload_pref_list', + url: '/backoffice/preference_list_dossier/reload_pref_list?procedure_id=<%= @procedure_id %>', async: true }).done(function (data) { $("#pref_list_menu").html(data); diff --git a/app/views/backoffice/dossiers/index.html.haml b/app/views/backoffice/dossiers/index.html.haml index c4fab90de..b6473abf4 100644 --- a/app/views/backoffice/dossiers/index.html.haml +++ b/app/views/backoffice/dossiers/index.html.haml @@ -1,10 +1,10 @@ #backoffice_index #pref_list_menu - = render partial: 'pref_list' - = render partial: 'onglets' + = render partial: 'backoffice/dossiers/pref_list' + = render partial: 'backoffice/dossiers/onglets' = smart_listing_render :dossiers %br %br - = render partial: 'state_description', locals: {dossiers_list_facade: @dossiers_list_facade} + = render partial: 'backoffice/dossiers/state_description', locals: {dossiers_list_facade: @dossiers_list_facade} diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index ee376238c..16112041c 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -150,7 +150,7 @@ Devise.setup do |config| # ==> Configuration for :timeoutable # The time you want to timeout the user session without activity. After this # time the user will be asked for credentials again. Default is 30 minutes. - # config.timeout_in = 30.minutes + config.timeout_in = 24.hours # If true, expires auth token on session timeout. # config.expire_auth_token_on_timeout = false diff --git a/config/routes.rb b/config/routes.rb index 673bdbb14..13e67732a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -37,6 +37,12 @@ Rails.application.routes.draw do root 'root#index' get 'cgu' => 'cgu#index' + get 'demo' => 'demo#index' + get 'users' => 'users#index' + get 'admin' => 'admin#index' + get 'backoffice' => 'backoffice#index' + + resources :administrations namespace :france_connect do get 'particulier' => 'particulier#login' @@ -47,9 +53,6 @@ Rails.application.routes.draw do post 'particulier/check_email' => 'particulier#check_email' end - get 'demo' => 'demo#index' - get 'users' => 'users#index' - namespace :users do namespace :dossiers do resources :invites, only: [:index, :show] @@ -85,8 +88,6 @@ Rails.application.routes.draw do resource :dossiers end - get 'admin' => 'admin#index' - namespace :admin do get 'sign_in' => '/administrateurs/sessions#new' get 'procedures/archived' => 'procedures#archived' @@ -139,25 +140,13 @@ Rails.application.routes.draw do get 'address_point' => 'search#get_address_point' end - get 'backoffice' => 'backoffice#index' - namespace :backoffice do get 'sign_in' => '/gestionnaires/sessions#new' - get 'dossiers/search' => 'dossiers#search' + get 'download_dossiers_tps' => 'dossiers#download_dossiers_tps' resource :private_formulaire - namespace :preference_list_dossier do - post 'add' - delete 'delete' - - get 'reload_smartlisting' => '/backoffice/dossiers#reload_smartlisting' - get 'reload_pref_list' - end - - get 'download_dossiers_tps' => 'dossiers#download_dossiers_tps' - resources :dossiers do post 'valid' => 'dossiers#valid' post 'receive' => 'dossiers#receive' @@ -170,10 +159,20 @@ Rails.application.routes.draw do put 'follow' => 'dossiers#follow' end - resources :commentaires, only: [:create] - end + namespace :dossiers do + resources :procedure, only: [:show] + end - resources :administrations + resources :commentaires, only: [:create] + + namespace :preference_list_dossier do + post 'add' + delete 'delete' + + get 'reload_smartlisting' => '/backoffice/dossiers#reload_smartlisting' + get 'reload_pref_list' + end + end namespace :api do namespace :v1 do diff --git a/db/migrate/20160906123255_add_has_procedure_to_pref_list_dossier.rb b/db/migrate/20160906123255_add_has_procedure_to_pref_list_dossier.rb new file mode 100644 index 000000000..e80588537 --- /dev/null +++ b/db/migrate/20160906123255_add_has_procedure_to_pref_list_dossier.rb @@ -0,0 +1,5 @@ +class AddHasProcedureToPrefListDossier < ActiveRecord::Migration + def change + add_belongs_to :preference_list_dossiers, :procedure + end +end diff --git a/db/migrate/20160906134155_build_default_pref_list_dossier_procedure.rb b/db/migrate/20160906134155_build_default_pref_list_dossier_procedure.rb new file mode 100644 index 000000000..bb4545d69 --- /dev/null +++ b/db/migrate/20160906134155_build_default_pref_list_dossier_procedure.rb @@ -0,0 +1,44 @@ +class BuildDefaultPrefListDossierProcedure < ActiveRecord::Migration + class Gestionnaire < ActiveRecord::Base + has_many :assign_to, dependent: :destroy + has_many :procedures, through: :assign_to + has_many :preference_list_dossiers + end + + class PreferenceListDossier < ActiveRecord::Base + belongs_to :gestionnaire + belongs_to :procedure + end + + class AssignTo < ActiveRecord::Base + belongs_to :procedure + belongs_to :gestionnaire + end + + class Procedure < ActiveRecord::Base + has_many :gestionnaires, through: :assign_to + has_many :preference_list_dossiers + end + + def up + Gestionnaire.all.each do |gestionnaire| + gestionnaire.procedures.each do |procedure| + gestionnaire.preference_list_dossiers.where(procedure: nil).each do |preference| + clone = preference.dup + + clone.procedure = procedure + clone.save + end + + base_object = gestionnaire.preference_list_dossiers.where(procedure: nil).size + created_object = gestionnaire.preference_list_dossiers.where(procedure: procedure).size + + raise "ERROR nb object (#{base_object} != #{created_object})" unless created_object == base_object + end + end + end + + def down + PreferenceListDossier.where('procedure_id IS NOT NULL') + end +end diff --git a/db/schema.rb b/db/schema.rb index 3b12345df..7533be06e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160901082824) do +ActiveRecord::Schema.define(version: 20160906134155) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -264,6 +264,7 @@ ActiveRecord::Schema.define(version: 20160901082824) do t.string "order" t.string "filter" t.integer "gestionnaire_id" + t.integer "procedure_id" end create_table "procedure_paths", force: :cascade do |t| diff --git a/spec/controllers/admin/accompagnateurs_controller_spec.rb b/spec/controllers/admin/accompagnateurs_controller_spec.rb index 42889eb1f..285b43feb 100644 --- a/spec/controllers/admin/accompagnateurs_controller_spec.rb +++ b/spec/controllers/admin/accompagnateurs_controller_spec.rb @@ -15,7 +15,7 @@ describe Admin::AccompagnateursController, type: :controller do end describe 'PUT #update' do - subject { put :update, accompagnateur_id: gestionnaire.id ,procedure_id: procedure.id } + subject { put :update, accompagnateur_id: gestionnaire.id ,procedure_id: procedure.id, to: 'assign' } it { expect(subject).to redirect_to admin_procedure_accompagnateurs_path(procedure_id: procedure.id) } @@ -25,6 +25,10 @@ describe Admin::AccompagnateursController, type: :controller do end it { expect(flash[:notice]).to be_present } + + it 'default pref list dossier procedure columns are created' do + expect(procedure.preference_list_dossiers.size).to eq gestionnaire.preference_list_dossiers.where('procedure_id IS NULL').size + end end end end \ No newline at end of file diff --git a/spec/controllers/backoffice/dossiers_controller_spec.rb b/spec/controllers/backoffice/dossiers_controller_spec.rb index 7769cc888..bcc763f42 100644 --- a/spec/controllers/backoffice/dossiers_controller_spec.rb +++ b/spec/controllers/backoffice/dossiers_controller_spec.rb @@ -4,14 +4,19 @@ describe Backoffice::DossiersController, type: :controller do before do @request.env['HTTP_REFERER'] = TPS::Application::URL end + let(:procedure) { create :procedure } - let(:dossier) { create(:dossier, :with_entreprise) } + let(:dossier) { create(:dossier, :with_entreprise, procedure: procedure) } let(:dossier_archived) { create(:dossier, :with_entreprise, archived: true) } let(:dossier_id) { dossier.id } let(:bad_dossier_id) { Dossier.count + 10 } let(:gestionnaire) { create(:gestionnaire, administrateurs: [create(:administrateur)]) } + before do + create :assign_to, procedure: procedure, gestionnaire: gestionnaire + end + describe 'GET #show' do context 'gestionnaire is connected' do before do @@ -223,6 +228,20 @@ describe Backoffice::DossiersController, type: :controller do it { expect(subject.status).to eq 302 } + context 'when dossier is at state initiated' do + let(:dossier) { create(:dossier, :with_entreprise, procedure: procedure, state: 'initiated') } + + before do + subject + dossier.reload + end + + it 'change state for updated' do + expect(dossier.state).to eq 'updated' + end + + end + describe 'flash alert' do context 'when dossier is not follow by gestionnaire' do before do diff --git a/spec/controllers/backoffice/preference_list_dossier_controller_spec.rb b/spec/controllers/backoffice/preference_list_dossier_controller_spec.rb index ecf6c25f0..06ccf9b55 100644 --- a/spec/controllers/backoffice/preference_list_dossier_controller_spec.rb +++ b/spec/controllers/backoffice/preference_list_dossier_controller_spec.rb @@ -36,6 +36,7 @@ describe Backoffice::PreferenceListDossierController, type: :controller do it { expect(last.bootstrap_lg).to eq bootstrap_lg } it { expect(last.order).to be_nil } it { expect(last.filter).to be_nil } + it { expect(last.procedure_id).to be_nil } it { expect(last.gestionnaire).to eq gestionnaire } end end diff --git a/spec/facades/dossiers_list_facades_spec.rb b/spec/facades/dossiers_list_facades_spec.rb new file mode 100644 index 000000000..625643930 --- /dev/null +++ b/spec/facades/dossiers_list_facades_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +describe DossiersListFacades do + + let(:gestionnaire) { create :gestionnaire } + let(:procedure) { create :procedure } + let(:procedure_2) { create :procedure, libelle: 'plop' } + + before do + create :assign_to, procedure: procedure, gestionnaire: gestionnaire + create :assign_to, procedure: procedure_2, gestionnaire: gestionnaire + + create :preference_list_dossier, + gestionnaire: gestionnaire, + table: '', + attr: 'state', + attr_decorate: 'display_state' + + create :preference_list_dossier, + gestionnaire: gestionnaire, + table: '', + attr: 'state', + attr_decorate: 'display_state', + procedure_id: procedure.id + end + + describe '#preference_list_dossiers_filter' do + + subject { facade.preference_list_dossiers_filter } + + context 'when procedure is not pasted at the facade' do + let(:facade) { described_class.new gestionnaire, 'nouveaux' } + + it { expect(subject.size).to eq 6 } + end + + context 'when procedure is pasted at the facade' do + let(:facade) { described_class.new gestionnaire, 'nouveaux', procedure } + + it { expect(subject.size).to eq 1 } + end + end + + describe '#gestionnaire_procedures_name_and_id_list' do + let(:facade) { described_class.new gestionnaire, 'nouveaux' } + + subject { facade.gestionnaire_procedures_name_and_id_list } + + it { expect(subject.size).to eq 2 } + + it { expect(subject.first[:id]).to eq procedure.id } + it { expect(subject.first[:libelle]).to eq procedure.libelle } + + it { expect(subject.last[:id]).to eq procedure_2.id } + it { expect(subject.last[:libelle]).to eq procedure_2.libelle } + + end +end \ No newline at end of file diff --git a/spec/features/backoffice/lateral_page_pref_list_dossier_by_procedure_backoffice_spec.rb b/spec/features/backoffice/lateral_page_pref_list_dossier_by_procedure_backoffice_spec.rb new file mode 100644 index 000000000..d9b97d581 --- /dev/null +++ b/spec/features/backoffice/lateral_page_pref_list_dossier_by_procedure_backoffice_spec.rb @@ -0,0 +1,87 @@ +require 'spec_helper' + +feature 'usage of pref list dossier lateral panel by procedure', js: true do + + let(:administrateur) { create(:administrateur) } + let(:gestionnaire) { create(:gestionnaire, administrateurs: [administrateur]) } + let(:procedure) { create(:procedure, :with_type_de_champ, administrateur: administrateur) } + + before do + create(:dossier, :with_entreprise, procedure: procedure, state: 'updated') + create :assign_to, procedure: procedure, gestionnaire: gestionnaire + visit backoffice_path + end + + scenario 'he is redirected to /gestionnaires/sign_id' do + expect(page).to have_css('#gestionnaire_login') + end + + context 'when user enter good credentials' do + before do + page.find_by_id(:gestionnaire_email).set gestionnaire.email + page.find_by_id(:gestionnaire_password).set gestionnaire.password + page.click_on 'Se connecter' + end + + scenario 'he is redirected to /backoffice/dossiers/' do + expect(page).to have_css('#backoffice_index') + end + + describe 'user navigate to dossiers list by procedure' do + before do + visit backoffice_dossiers_procedure_path(procedure.id) + end + + scenario 'lateral panel is masked' do + expect(page).to have_css('#pref_list_menu', visible: false) + end + + context 'when on click on pref list button' do + before do + page.click_on 'pref_list_dossier_open_action' + end + + scenario 'lateral panel is appeared' do + expect(page).to have_css('#pref_list_menu') + end + + context 'when on click on add attribut specific at the procedure button' do + before do + page.click_on 'add_pref_list_champs_'+procedure.types_de_champ.first.id.to_s + end + + scenario 'preference list panel is brought up to date' do + wait_for_ajax + expect(page).to have_css('#delete_pref_list_champs_'+procedure.types_de_champ.first.id.to_s) + end + + context 'when on click on delete attribut button' do + before do + page.click_on 'delete_pref_list_champs_'+procedure.types_de_champ.first.id.to_s + end + + scenario 'preference list panel is brought up to date' do + wait_for_ajax + expect(page).not_to have_css('#delete_pref_list_champs_'+procedure.types_de_champ.first.id.to_s) + end + + scenario 'dossier is brought up to date' do + wait_for_ajax + expect(page).not_to have_selector("a.sortable[data-attr='entreprise.siren']") + end + + context 'when on click on close pref list button' do + before do + page.click_on 'pref_list_dossier_close_action' + end + + scenario 'lateral panel is masked' do + expect(page).to have_css('#pref_list_menu', visible: false) + end + end + end + end + end + end + end +end \ No newline at end of file diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 583a47410..21ac74bd0 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -219,6 +219,12 @@ describe Dossier do it { is_expected.to eq('replied') } end + context 'when is follow' do + let(:action) { 'follow' } + + it { is_expected.to eq 'updated' } + end + context 'when is validated the dossier' do let(:action) { 'valid' } @@ -244,10 +250,7 @@ describe Dossier do context 'when is updated dossier informations' do let(:action) { 'update' } - it { - - is_expected.to eq('updated') - } + it { is_expected.to eq('updated') } end end @@ -260,6 +263,12 @@ describe Dossier do it { is_expected.to eq('replied') } end + context 'when is follow' do + let(:action) { 'follow' } + + it { is_expected.to eq 'replied' } + end + context 'when is validated the dossier' do let(:action) { 'valid' } @@ -453,7 +462,6 @@ describe Dossier do end end - context 'when dossier is at state closed' do before do dossier.closed! diff --git a/spec/models/preference_list_dossier_spec.rb b/spec/models/preference_list_dossier_spec.rb index e65b377fb..095d1083d 100644 --- a/spec/models/preference_list_dossier_spec.rb +++ b/spec/models/preference_list_dossier_spec.rb @@ -11,9 +11,12 @@ describe PreferenceListDossier do it { is_expected.to have_db_column(:gestionnaire_id) } it { is_expected.to belong_to(:gestionnaire) } + it { is_expected.to belong_to(:procedure) } - describe '.available_columns' do - subject { PreferenceListDossier.available_columns } + describe '.available_columns_for' do + let(:procedure_id) { nil } + + subject { PreferenceListDossier.available_columns_for procedure_id } describe 'dossier' do subject { super()[:dossier] } @@ -249,5 +252,70 @@ describe PreferenceListDossier do it { expect(subject[:filter]).to be_nil } end end + + describe 'france_connect' do + subject { super()[:france_connect] } + + it { expect(subject.size).to eq 3 } + + describe 'gender' do + subject { super()[:gender] } + + it { expect(subject[:libelle]).to eq 'Civilité (FC)' } + it { expect(subject[:table]).to eq 'france_connect_information' } + it { expect(subject[:attr]).to eq 'gender' } + it { expect(subject[:attr_decorate]).to eq 'gender_fr' } + it { expect(subject[:bootstrap_lg]).to eq 1 } + it { expect(subject[:order]).to be_nil } + it { expect(subject[:filter]).to be_nil } + end + + describe 'family_name' do + subject { super()[:family_name] } + + it { expect(subject[:libelle]).to eq 'Nom (FC)' } + it { expect(subject[:table]).to eq 'france_connect_information' } + it { expect(subject[:attr]).to eq 'family_name' } + it { expect(subject[:attr_decorate]).to eq 'family_name' } + it { expect(subject[:bootstrap_lg]).to eq 2 } + it { expect(subject[:order]).to be_nil } + it { expect(subject[:filter]).to be_nil } + end + + describe 'gender' do + subject { super()[:given_name] } + + it { expect(subject[:libelle]).to eq 'Prénom (FC)' } + it { expect(subject[:table]).to eq 'france_connect_information' } + it { expect(subject[:attr]).to eq 'given_name' } + it { expect(subject[:attr_decorate]).to eq 'given_name' } + it { expect(subject[:bootstrap_lg]).to eq 2 } + it { expect(subject[:order]).to be_nil } + it { expect(subject[:filter]).to be_nil } + end + end + + context 'when a procedure ID is pasted' do + let(:procedure) { (create :procedure, :with_type_de_champ) } + let(:procedure_id) { procedure.id } + + describe 'champs' do + subject { super()[:champs] } + + it { expect(subject.size).to eq 1 } + + describe 'first champs' do + subject { super()["type_de_champ_#{procedure.types_de_champ.first.id}"] } + + it { expect(subject[:libelle]).to eq 'Description' } + it { expect(subject[:table]).to eq 'champs' } + it { expect(subject[:attr]).to eq procedure.types_de_champ.first.id } + it { expect(subject[:attr_decorate]).to eq 'value' } + it { expect(subject[:bootstrap_lg]).to eq 2 } + it { expect(subject[:order]).to be_nil } + it { expect(subject[:filter]).to be_nil } + end + end + end end end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index bd720b25a..47356f165 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -9,6 +9,7 @@ describe Procedure do it { is_expected.to have_one(:mail_received) } it { is_expected.to have_one(:module_api_carto) } it { is_expected.to belong_to(:administrateur) } + it { is_expected.to have_many(:preference_list_dossiers) } end describe 'attributes' do @@ -162,6 +163,8 @@ describe Procedure do let(:procedure) { create(:procedure, archived: archived, published: published) } let!(:type_de_champ_0) { create(:type_de_champ_public, procedure: procedure, order_place: 0) } let!(:type_de_champ_1) { create(:type_de_champ_public, procedure: procedure, order_place: 1) } + let!(:type_de_champ_private_0) { create(:type_de_champ_private, procedure: procedure, order_place: 0) } + let!(:type_de_champ_private_1) { create(:type_de_champ_private, procedure: procedure, order_place: 1) } let!(:piece_justificative_0) { create(:type_de_piece_justificative, procedure: procedure, order_place: 0) } let!(:piece_justificative_1) { create(:type_de_piece_justificative, procedure: procedure, order_place: 1) } @@ -176,11 +179,19 @@ describe Procedure do expect(subject).to have_same_attributes_as(procedure) expect(subject.module_api_carto).to have_same_attributes_as(procedure.module_api_carto) + expect(subject.types_de_piece_justificative.size).to eq procedure.types_de_piece_justificative.size + expect(subject.types_de_champ.size).to eq procedure.types_de_champ.size + expect(subject.types_de_champ_private.size).to eq procedure.types_de_champ_private.size + expect(subject.mail_templates.size).to eq procedure.mail_templates.size subject.types_de_champ.zip(procedure.types_de_champ).each do |stc, ptc| expect(stc).to have_same_attributes_as(ptc) end + subject.types_de_champ_private.zip(procedure.types_de_champ_private).each do |stc, ptc| + expect(stc).to have_same_attributes_as(ptc) + end + subject.types_de_piece_justificative.zip(procedure.types_de_piece_justificative).each do |stc, ptc| expect(stc).to have_same_attributes_as(ptc) end diff --git a/spec/models/type_de_champ_shared_example.rb b/spec/models/type_de_champ_shared_example.rb index 5ce94d7d3..e4ca86e0f 100644 --- a/spec/models/type_de_champ_shared_example.rb +++ b/spec/models/type_de_champ_shared_example.rb @@ -41,4 +41,15 @@ shared_examples 'type_de_champ_spec' do it { is_expected.to allow_value('blabla').for(:description) } end end + + describe 'field_for_list?' do + + let(:type_de_champ_yes) { create :type_de_champ_public, type_champ: 'text' } + let(:type_de_champ_no_1) { create :type_de_champ_public, type_champ: 'textarea' } + let(:type_de_champ_no_2) { create :type_de_champ_public, type_champ: 'header_section' } + + it { expect(type_de_champ_yes.field_for_list?).to be_truthy } + it { expect(type_de_champ_no_1.field_for_list?).to be_falsey } + it { expect(type_de_champ_no_2.field_for_list?).to be_falsey } + end end \ No newline at end of file diff --git a/spec/services/accompagnateur_service_spec.rb b/spec/services/accompagnateur_service_spec.rb index 1c0dcf9ab..c939ecf27 100644 --- a/spec/services/accompagnateur_service_spec.rb +++ b/spec/services/accompagnateur_service_spec.rb @@ -1,10 +1,11 @@ require 'spec_helper' describe AccompagnateurService do - describe '#change_assignement!' do - let(:procedure) { create :procedure } - let(:accompagnateur) { create :gestionnaire } + let(:procedure) { create :procedure } + let(:accompagnateur) { create :gestionnaire } + + describe '#change_assignement!' do subject { AccompagnateurService.change_assignement! accompagnateur, procedure, to } @@ -29,4 +30,35 @@ describe AccompagnateurService do it { expect(accompagnateur.procedures).not_to include procedure } end end + + describe '#build_default_column' do + + subject { AccompagnateurService.build_default_column accompagnateur, procedure, to } + + context 'when to is not assign' do + let(:to) { AccompagnateurService::NOT_ASSIGN } + + it { is_expected.to be_nil } + end + + context 'when to is assign' do + let(:to) { AccompagnateurService::ASSIGN } + + context 'when gestionnaire has already preference for this procedure' do + before do + create :preference_list_dossier, gestionnaire: accompagnateur, procedure: procedure + end + + it { is_expected.to be_nil } + end + + context 'when gestionnaire has not preference for this procedure' do + before do + subject + end + + it { expect(accompagnateur.preference_list_dossiers.where('procedure_id IS NULL').size).to eq procedure.preference_list_dossiers.size } + end + end + end end \ No newline at end of file