Merge pull request #6131 from betagouv/more_instructeur_show_love

ameloration des perfs instructeur show procedure
This commit is contained in:
LeSim 2021-04-29 11:55:55 +02:00 committed by GitHub
commit f45a7a83fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 201 additions and 161 deletions

View file

@ -85,10 +85,10 @@ module Instructeurs
@archived_dossiers
end
@has_en_cours_notifications = current_instructeur.notifications_for_procedure(@procedure, :en_cours).exists?
@has_termine_notifications = current_instructeur.notifications_for_procedure(@procedure, :termine).exists?
@not_archived_notifications_dossier_ids = current_instructeur.notifications_for_procedure(@procedure, :not_archived).pluck(:id)
notifications = current_instructeur.notifications_for_groupe_instructeurs(groupe_instructeur_ids)
@has_en_cours_notifications = notifications[:en_cours].present?
@has_termine_notifications = notifications[:termines].present?
@not_archived_notifications_dossier_ids = notifications[:en_cours] + notifications[:termines]
sorted_ids = procedure_presentation.sorted_ids(@dossiers, current_instructeur)
@ -101,18 +101,12 @@ module Instructeurs
page = params[:page].presence || 1
filtered_sorted_paginated_ids = Kaminari
@filtered_sorted_paginated_ids = Kaminari
.paginate_array(filtered_sorted_ids)
.page(page)
.per(ITEMS_PER_PAGE)
@dossiers = @dossiers.where(id: filtered_sorted_paginated_ids)
@dossiers = @dossiers.sort_by { |d| filtered_sorted_paginated_ids.index(d.id) }
@projected_dossiers = DossierProjectionService.project(filtered_sorted_paginated_ids, procedure_presentation.displayed_fields)
kaminarize(page, filtered_sorted_ids.count)
@projected_dossiers = DossierProjectionService.project(@filtered_sorted_paginated_ids, procedure_presentation.displayed_fields)
assign_exports
end
@ -156,6 +150,11 @@ module Instructeurs
.groupe_instructeurs
.where(procedure: procedure)
@dossier_count = current_instructeur
.dossiers_count_summary(groupe_instructeur_ids)
.fetch_values('tous', 'archives')
.sum
export = Export.find_or_create_export(export_format, groupe_instructeurs)
if export.ready? && export.old? && params[:force_export]
@ -220,10 +219,7 @@ module Instructeurs
end
def assign_exports
groupe_instructeurs_for_procedure = current_instructeur.groupe_instructeurs.where(procedure: procedure)
@xlsx_export = Export.find_for_format_and_groupe_instructeurs(:xlsx, groupe_instructeurs_for_procedure)
@csv_export = Export.find_for_format_and_groupe_instructeurs(:csv, groupe_instructeurs_for_procedure)
@ods_export = Export.find_for_format_and_groupe_instructeurs(:ods, groupe_instructeurs_for_procedure)
@xlsx_export, @csv_export, @ods_export = Export.find_for_groupe_instructeurs(groupe_instructeur_ids)
end
def assign_to
@ -251,7 +247,9 @@ module Instructeurs
end
def procedure
Procedure.find(procedure_id)
Procedure
.with_attached_logo
.find(procedure_id)
end
def ensure_ownership!
@ -282,25 +280,5 @@ module Instructeurs
def current_filters
@current_filters ||= procedure_presentation.filters[statut]
end
def kaminarize(current_page, total)
@dossiers.instance_eval <<-EVAL
def current_page
#{current_page}
end
def total_pages
(#{total} / #{ITEMS_PER_PAGE}.to_f).ceil
end
def limit_value
#{ITEMS_PER_PAGE}
end
def first_page?
current_page == 1
end
def last_page?
current_page == total_pages
end
EVAL
end
end
end

View file

@ -21,4 +21,17 @@ class Expert < ApplicationRecord
def self.by_email(email)
Expert.eager_load(:user).find_by(users: { email: email })
end
def avis_summary
if @avis_summary.present?
@avis_summary
else
query = <<~EOF
COUNT(*) FILTER (where answer IS NULL) AS unanswered,
COUNT(*) AS total
EOF
result = avis.select(query)[0]
@avis_summary = { unanswered: result.unanswered, total: result.total }
end
end
end

View file

@ -51,15 +51,18 @@ class Export < ApplicationRecord
def self.find_or_create_export(format, groupe_instructeurs)
create_with(groupe_instructeurs: groupe_instructeurs)
.create_or_find_by(format: format, key: generate_cache_key(groupe_instructeurs))
.create_or_find_by(format: format, key: generate_cache_key(groupe_instructeurs.map(&:id)))
end
def self.find_for_format_and_groupe_instructeurs(format, groupe_instructeurs)
find_by(format: format, key: generate_cache_key(groupe_instructeurs))
def self.find_for_groupe_instructeurs(groupe_instructeurs_ids)
exports = where(key: generate_cache_key(groupe_instructeurs_ids))
['xlsx', 'csv', 'ods']
.map { |format| exports.find { |export| export.format == format } }
end
def self.generate_cache_key(groupe_instructeurs)
groupe_instructeurs.map(&:id).sort.join('-')
def self.generate_cache_key(groupe_instructeurs_ids)
groupe_instructeurs_ids.sort.join('-')
end
private

View file

@ -134,14 +134,21 @@ class Instructeur < ApplicationRecord
end
end
def notifications_for_procedure(procedure, scope)
target_groupes = groupe_instructeurs.where(procedure: procedure)
def notifications_for_groupe_instructeurs(groupe_instructeurs)
Dossier
.where(groupe_instructeur: target_groupes)
.send(scope) # :en_cours or :termine or :not_archived (or any other Dossier scope)
.not_archived
.where(groupe_instructeur: groupe_instructeurs)
.merge(followed_dossiers)
.with_notifications
.pluck(:state, :id)
.reduce({ termines: [], en_cours: [] }) do |acc, e|
if Dossier::TERMINE.include?(e[0])
acc[:termines] << e[1]
elsif Dossier::EN_CONSTRUCTION_OU_INSTRUCTION.include?(e[0])
acc[:en_cours] << e[1]
end
acc
end
end
def procedure_ids_with_notifications(scope)
@ -165,11 +172,14 @@ class Instructeur < ApplicationRecord
.reduce([]) do |acc, groupe|
procedure = groupe.procedure
notifications = notifications_for_groupe_instructeurs([groupe.id])
nb_notification = notifications[:en_cours].count + notifications[:termines].count
h = {
nb_en_construction: groupe.dossiers.en_construction.count,
nb_en_instruction: groupe.dossiers.en_instruction.count,
nb_accepted: Traitement.where(dossier: groupe.dossiers.accepte, processed_at: Time.zone.yesterday.beginning_of_day..Time.zone.yesterday.end_of_day).count,
nb_notification: notifications_for_procedure(procedure, :not_archived).count
nb_notification: nb_notification
}
if h[:nb_en_construction] > 0 || h[:nb_notification] > 0

View file

@ -1,5 +1,5 @@
class DossierProjectionService
class DossierProjection < Struct.new(:dossier, :columns)
class DossierProjection < Struct.new(:dossier_id, :state, :archived, :columns)
end
TABLE = 'table'
@ -18,57 +18,64 @@ class DossierProjectionService
# - the order of the intermediary query results are unknown
# - some values can be missing (if a revision added or removed them)
def self.project(dossiers_ids, fields)
champ_fields, other_fields = fields
.partition { |f| ['type_de_champ', 'type_de_champ_private'].include?(f[TABLE]) }
state_field = { TABLE => 'self', COLUMN => 'state' }
archived_field = { TABLE => 'self', COLUMN => 'archived' }
if champ_fields.present?
Champ
.includes(:type_de_champ)
.where(
# as querying the champs table is costly
# we fetch all the requested champs at once
types_de_champ: { stable_id: champ_fields.map { |f| f[COLUMN] } },
dossier_id: dossiers_ids
)
.select(:dossier_id, :value, :type_de_champ_id, :stable_id) # we cannot pluck :value, as we need the champ.to_s method
.group_by(&:stable_id) # the champs are redispatched to their respective fields
.map do |stable_id, champs|
field = champ_fields.find { |f| f[COLUMN] == stable_id.to_s }
field[:id_value_h] = champs.to_h { |c| [c.dossier_id, c.to_s] }
end
end
other_fields.each do |field|
field[:id_value_h] = case field[TABLE]
([state_field, archived_field] + fields) # the view needs state and archived dossier attributes
.each { |f| f[:id_value_h] = {} }
.group_by { |f| f[TABLE] } # one query per table
.each do |table, fields|
case table
when 'type_de_champ', 'type_de_champ_private'
Champ
.includes(:type_de_champ)
.where(
types_de_champ: { stable_id: fields.map { |f| f[COLUMN] } },
dossier_id: dossiers_ids
)
.select(:dossier_id, :value, :type_de_champ_id, :stable_id) # we cannot pluck :value, as we need the champ.to_s method
.group_by(&:stable_id) # the champs are redispatched to their respective fields
.map do |stable_id, champs|
field = fields.find { |f| f[COLUMN] == stable_id.to_s }
field[:id_value_h] = champs.to_h { |c| [c.dossier_id, c.to_s] }
end
when 'self'
Dossier
.where(id: dossiers_ids)
.pluck(:id, field[COLUMN].to_sym)
.to_h { |id, col| [id, col&.strftime('%d/%m/%Y')] }
.pluck(:id, *fields.map { |f| f[COLUMN].to_sym })
.each do |id, *columns|
fields.zip(columns).each do |field, value|
if [state_field, archived_field].include?(field)
field[:id_value_h][id] = value
else
field[:id_value_h][id] = value&.strftime('%d/%m/%Y') # other fields are datetime
end
end
end
when 'individual'
Individual
.where(dossier_id: dossiers_ids)
.pluck(:dossier_id, *fields.map { |f| f[COLUMN].to_sym })
.each { |id, *columns| fields.zip(columns).each { |field, value| field[:id_value_h][id] = value } }
when 'etablissement'
Etablissement
.where(dossier_id: dossiers_ids)
.pluck(:dossier_id, *fields.map { |f| f[COLUMN].to_sym })
.each { |id, *columns| fields.zip(columns).each { |field, value| field[:id_value_h][id] = value } }
when 'user'
Dossier
fields[0][:id_value_h] = Dossier # there is only one field available for user table
.joins(:user)
.where(id: dossiers_ids)
.pluck('dossiers.id, users.email')
.to_h
when 'individual'
Individual
.where(dossier_id: dossiers_ids)
.pluck(:dossier_id, field[COLUMN].to_sym)
.to_h
when 'etablissement'
Etablissement
.where(dossier_id: dossiers_ids)
.pluck(:dossier_id, field[COLUMN].to_sym)
.to_h
when 'groupe_instructeur'
Dossier
fields[0][:id_value_h] = Dossier
.joins(:groupe_instructeur)
.where(id: dossiers_ids)
.pluck('dossiers.id, groupe_instructeurs.label')
.to_h
when 'followers_instructeurs'
Follow
fields[0][:id_value_h] = Follow
.active
.joins(instructeur: :user)
.where(dossier_id: dossiers_ids)
@ -78,11 +85,13 @@ class DossierProjectionService
end
end
Dossier
.select(:id, :state, :archived) # the dossier object is needed in the view
.find(dossiers_ids) # keeps dossiers_ids order and raise exception if one is missing
.map do |dossier|
DossierProjection.new(dossier, fields.map { |f| f[:id_value_h][dossier.id] })
dossiers_ids.map do |dossier_id|
DossierProjection.new(
dossier_id,
state_field[:id_value_h][dossier_id],
archived_field[:id_value_h][dossier_id],
fields.map { |f| f[:id_value_h][dossier_id] }
)
end
end
end

View file

@ -23,7 +23,13 @@
- else
%p.menu-item Le téléchargement des pièces jointes est désactivé pour les dossiers de plus de #{number_to_human_size Dossier::TAILLE_MAX_ZIP}.
= render partial: "instructeurs/procedures/dossier_actions", locals: { procedure: dossier.procedure, dossier: dossier, dossier_is_followed: current_instructeur&.follow?(dossier) }
= render partial: "instructeurs/procedures/dossier_actions",
locals: { procedure_id: dossier.procedure.id,
dossier_id: dossier.id,
state: dossier.state,
archived: dossier.archived,
dossier_is_followed: current_instructeur&.follow?(dossier) }
.state-button
= render partial: "state_button", locals: { dossier: dossier }

View file

@ -1,19 +1,19 @@
- if dossier.en_construction_ou_instruction?
- if Dossier::EN_CONSTRUCTION_OU_INSTRUCTION.include?(state)
- if dossier_is_followed
= link_to unfollow_instructeur_dossier_path(procedure, dossier), method: :patch, class: 'button' do
= link_to unfollow_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'button' do
%span.icon.unfollow>
Ne plus suivre
- else
= link_to follow_instructeur_dossier_path(procedure, dossier), method: :patch, class: 'button' do
= link_to follow_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'button' do
%span.icon.follow>
Suivre le dossier
- elsif dossier.termine?
- if dossier.archived
= link_to unarchive_instructeur_dossier_path(procedure, dossier), method: :patch, class: 'button' do
- elsif Dossier::TERMINE.include?(state)
- if archived
= link_to unarchive_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'button' do
%span.icon.unarchive>
Désarchiver le dossier
- else
= link_to archive_instructeur_dossier_path(procedure, dossier), method: :patch, class: 'button' do
= link_to archive_instructeur_dossier_path(procedure_id, dossier_id), method: :patch, class: 'button' do
%span.icon.archive>
Archiver le dossier

View file

@ -1,4 +1,4 @@
- if procedure.dossiers.state_not_brouillon.any?
- if dossier_count > 0
%span.dropdown
%button.button.dropdown-button{ 'aria-expanded' => 'false', 'aria-controls' => 'download-menu' }
Télécharger tous les dossiers

View file

@ -3,6 +3,6 @@
= field['label']
- if @procedure_presentation.sort['table'] == field['table'] && @procedure_presentation.sort['column'] == field['column']
- if @procedure_presentation.sort['order'] == 'asc'
%img.caret-icon{ src: image_url("table/up_caret.svg") }
%img.caret-icon{ src: image_url("table/up_caret.svg"), width: 10, height: 6, loading: 'lazy' }
- else
%img.caret-icon{ src: image_url("table/down_caret.svg") }
%img.caret-icon{ src: image_url("table/down_caret.svg"), width: 10, height: 6, loading: 'lazy' }

View file

@ -1,5 +1,5 @@
<%= render_to_element('.procedure-actions', partial: "download_dossiers",
locals: { procedure: @procedure, xlsx_export: @xlsx_export, csv_export: @csv_export, ods_export: @ods_export }) %>
locals: { procedure: @procedure, xlsx_export: @xlsx_export, csv_export: @csv_export, ods_export: @ods_export, dossier_count: @dossier_count }) %>
<% [[@xlsx_export, :xlsx], [@csv_export, :csv], [@ods_export, :ods]].each do |(export, format)| %>
<% if export && !export.ready? %>

View file

@ -50,7 +50,7 @@
.procedure-actions
= render partial: "download_dossiers",
locals: { procedure: @procedure, xlsx_export: @xlsx_export, csv_export: @csv_export, ods_export: @ods_export }
locals: { procedure: @procedure, xlsx_export: @xlsx_export, csv_export: @csv_export, ods_export: @ods_export, dossier_count: @tous_count + @archives_count }
.container
- if @statut == 'a-suivre'
@ -75,8 +75,9 @@
%span.icon.delete
Afficher les dossiers supprimés
- if @dossiers.present? || @current_filters.count > 0
= paginate @dossiers
- if @filtered_sorted_paginated_ids.present? || @current_filters.count > 0
- pagination = paginate @filtered_sorted_paginated_ids
= pagination
%span.dropdown
%button.button.dropdown-button{ 'aria-expanded' => 'false', 'aria-controls' => 'filter-menu' }
Filtrer
@ -130,27 +131,32 @@
%tbody
- @projected_dossiers.each do |p|
- dossier = p.dossier
- path = instructeur_dossier_path(@procedure, dossier.id)
- path = instructeur_dossier_path(@procedure, p.dossier_id)
%tr
%td.folder-col
%a.cell-link{ href: path }
%span.icon.folder
- if @not_archived_notifications_dossier_ids.include?(dossier.id)
- if @not_archived_notifications_dossier_ids.include?(p.dossier_id)
%span.notifications{ 'aria-label': 'notifications' }
%td.number-col
%a.cell-link{ href: path }= dossier.id
%a.cell-link{ href: path }= p.dossier_id
- p.columns.each do |column|
%td
%a.cell-link{ href: path }= column
%td.status-col
%a.cell-link{ href: path }= status_badge(dossier.state)
%a.cell-link{ href: path }= status_badge(p.state)
%td.action-col.follow-col= render partial: 'dossier_actions', locals: { procedure: @procedure, dossier: dossier, dossier_is_followed: @followed_dossiers_id.include?(dossier.id) }
= paginate @dossiers
%td.action-col.follow-col= render partial: 'dossier_actions',
locals: { procedure_id: @procedure.id,
dossier_id: p.dossier_id,
state: p.state,
archived: p.archived,
dossier_is_followed: @followed_dossiers_id.include?(p.dossier_id) }
= pagination
- else
%h2.empty-text Aucun dossier

View file

@ -31,6 +31,12 @@
%td.status-col
= link_to(dossier_linked_path(current_instructeur, dossier), class: 'cell-link') do
= status_badge(dossier.state)
%td.action-col.follow-col= render partial: 'instructeurs/procedures/dossier_actions', locals: { procedure: dossier.procedure, dossier: dossier, dossier_is_followed: @followed_dossiers_id.include?(dossier.id) }
%td.action-col.follow-col= render partial: "instructeurs/procedures/dossier_actions",
locals: { procedure_id: dossier.procedure.id,
dossier_id: dossier.id,
state: dossier.state,
archived: dossier.archived,
dossier_is_followed: @followed_dossiers_id.include?(dossier.id) }
- else
%h2 Aucun dossier correspondant à votre recherche n'a été trouvé

View file

@ -1,7 +1,7 @@
.dropdown.header-menu-opener
%button.button.dropdown-button.icon-only.header-menu-button{ title: "Mon compte", 'aria-expanded' => 'false', 'aria-controls' => 'mon_compte_menu' }
.hidden Mon compte
= image_tag "icons/account-circle.svg", alt: 'Mon compte'
= image_tag "icons/account-circle.svg", alt: 'Mon compte', width: 24, height: 24, loading: 'lazy'
%ul.header-menu.dropdown-content#mon_compte_menu
%li
.menu-item{ title: current_email }

View file

@ -21,30 +21,28 @@
- if nav_bar_profile == :instructeur && instructeur_signed_in?
- current_url = request.path_info
%ul.header-tabs
- if current_instructeur.procedures.count > 0
- if current_instructeur.procedures.any?
%li
= active_link_to "Démarches", instructeur_procedures_path, active: ['dossiers','procedures'].include?(controller_name), class: 'tab-link'
- if current_instructeur.user.expert && current_expert.avis.count > 0
- if current_instructeur.user.expert && current_expert.avis_summary[:total] > 0
%li
= active_link_to expert_all_avis_path, active: controller_name == 'avis', class: 'tab-link' do
Avis
- avis_counter = current_expert.avis.without_answer.count
- if avis_counter > 0
%span.badge.warning= avis_counter
- if current_expert.avis_summary[:unanswered] > 0
%span.badge.warning= current_expert.avis_summary[:unanswered]
- if nav_bar_profile == :expert && expert_signed_in?
%ul.header-tabs
- if current_expert.user.instructeur && current_instructeur.procedures.count > 0
- if current_expert.user.instructeur && current_instructeur.procedures.any?
%li
= active_link_to "Démarches", instructeur_procedures_path, active: ['dossiers','procedures'].include?(controller_name), class: 'tab-link'
- if current_expert.avis.count > 0
- if current_expert.avis_summary[:total] > 0
%li
= active_link_to expert_all_avis_path, active: controller_name == 'avis', class: 'tab-link' do
Avis
- avis_counter = current_expert.avis.without_answer.count
- if avis_counter > 0
%span.badge.warning= avis_counter
- if current_expert.avis_summary[:unanswered] > 0
%span.badge.warning= current_expert.avis_summary[:unanswered]
- if nav_bar_profile == :user
%ul.header-tabs

View file

@ -3,4 +3,4 @@
= label_tag :q, "Numéro de dossier", class: 'hidden'
= text_field_tag "q", "#{@search_terms if @search_terms.present?}", placeholder: "Rechercher un dossier"
%button{ title: "Rechercher" }
= image_tag "icons/search-blue.svg", alt: 'Rechercher', 'aria-hidden':'true'
= image_tag "icons/search-blue.svg", alt: 'Rechercher', 'aria-hidden':'true', width: 24, height: 24, loading: 'lazy'

View file

@ -4,7 +4,7 @@ FactoryBot.define do
groupe_instructeurs { [association(:groupe_instructeur)] }
after(:build) do |export, _evaluator|
export.key = Export.generate_cache_key(export.groupe_instructeurs)
export.key = Export.generate_cache_key(export.groupe_instructeurs.map(&:id))
end
end
end

View file

@ -48,9 +48,9 @@ RSpec.describe Export, type: :model do
context 'when an export is made for one groupe instructeur' do
let!(:export) { create(:export, groupe_instructeurs: [gi_1, gi_2]) }
it { expect(Export.find_for_format_and_groupe_instructeurs(:csv, [gi_1])).to eq(nil) }
it { expect(Export.find_for_format_and_groupe_instructeurs(:csv, [gi_2, gi_1])).to eq(export) }
it { expect(Export.find_for_format_and_groupe_instructeurs(:csv, [gi_1, gi_2, gi_3])).to eq(nil) }
it { expect(Export.find_for_groupe_instructeurs([gi_1.id])[1]).to eq(nil) }
it { expect(Export.find_for_groupe_instructeurs([gi_2.id, gi_1.id])[1]).to eq(export) }
it { expect(Export.find_for_groupe_instructeurs([gi_1.id, gi_2.id, gi_3.id])[1]).to eq(nil) }
end
end
end

View file

@ -255,29 +255,35 @@ describe Instructeur, type: :model do
end
end
describe '#notifications_for_procedure' do
describe '#notifications_for_groupe_instructeurs' do
# one procedure, one group, 2 instructeurs
let(:procedure) { create(:simple_procedure, :routee, :with_type_de_champ_private) }
let!(:dossier) { create(:dossier, :followed, groupe_instructeur: procedure.groupe_instructeurs.last, state: Dossier.states.fetch(:en_construction)) }
let(:gi_p1) { procedure.groupe_instructeurs.last }
let!(:dossier) { create(:dossier, :followed, groupe_instructeur: gi_p1, state: Dossier.states.fetch(:en_construction)) }
let(:instructeur) { dossier.follows.first.instructeur }
let!(:instructeur_2) { create(:instructeur, groupe_instructeurs: [procedure.groupe_instructeurs.last]) }
let!(:instructeur_2) { create(:instructeur, groupe_instructeurs: [gi_p1]) }
# one other procedure, dossier followed by a third instructeur
let!(:dossier_on_procedure_2) { create(:dossier, :followed, state: Dossier.states.fetch(:en_construction)) }
let!(:instructeur_on_procedure_2) { dossier_on_procedure_2.follows.first.instructeur }
let(:gi_p2) { dossier.groupe_instructeur }
let(:now) { Time.zone.parse("14/09/1867") }
let(:follow) { instructeur.follows.find_by(dossier: dossier) }
let(:follow2) { instructeur_2.follows.find_by(dossier: dossier) }
let(:seen_at_instructeur) { now - 1.hour }
let(:seen_at_instructeur2) { now - 1.hour }
before do
procedure.groupe_instructeurs.last.instructeurs << instructeur
gi_p1.instructeurs << instructeur
instructeur_2.followed_dossiers << dossier
Timecop.freeze(now)
end
after { Timecop.return }
subject { instructeur.notifications_for_procedure(procedure, :en_cours) }
subject { instructeur.notifications_for_groupe_instructeurs(gi_p1)[:en_cours] }
context 'when the instructeur has just followed the dossier' do
it { is_expected.to match([]) }
@ -290,14 +296,14 @@ describe Instructeur, type: :model do
follow2.update_attribute('demande_seen_at', seen_at_instructeur2)
end
it { is_expected.to match([dossier]) }
it { expect(instructeur_2.notifications_for_procedure(procedure, :en_cours)).to match([dossier]) }
it { expect(instructeur_on_procedure_2.notifications_for_procedure(procedure, :en_cours)).to match([]) }
it { is_expected.to match([dossier.id]) }
it { expect(instructeur_2.notifications_for_groupe_instructeurs(gi_p1)[:en_cours]).to match([dossier.id]) }
it { expect(instructeur_on_procedure_2.notifications_for_groupe_instructeurs(gi_p2)[:en_cours]).to match([]) }
context 'and there is a modification on private champs' do
before { dossier.champs_private.first.update_attribute('value', 'toto') }
it { is_expected.to match([dossier]) }
it { is_expected.to match([dossier.id]) }
end
context 'when instructeur update it s public champs last seen' do
@ -305,7 +311,7 @@ describe Instructeur, type: :model do
let(:seen_at_instructeur2) { now - 1.hour }
it { is_expected.to match([]) }
it { expect(instructeur_2.notifications_for_procedure(procedure, :en_cours)).to match([dossier]) }
it { expect(instructeur_2.notifications_for_groupe_instructeurs(gi_p1)[:en_cours]).to match([dossier.id]) }
end
end
@ -321,7 +327,7 @@ describe Instructeur, type: :model do
follow.update_attribute('annotations_privees_seen_at', seen_at_instructeur)
end
it { is_expected.to match([dossier]) }
it { is_expected.to match([dossier.id]) }
end
context 'when there is a modification on avis' do
@ -330,7 +336,7 @@ describe Instructeur, type: :model do
follow.update_attribute('avis_seen_at', seen_at_instructeur)
end
it { is_expected.to match([dossier]) }
it { is_expected.to match([dossier.id]) }
end
context 'the messagerie' do
@ -340,7 +346,7 @@ describe Instructeur, type: :model do
follow.update_attribute('messagerie_seen_at', seen_at_instructeur)
end
it { is_expected.to match([dossier]) }
it { is_expected.to match([dossier.id]) }
end
context 'when there is a new commentaire issued by tps' do
@ -431,9 +437,9 @@ describe Instructeur, type: :model do
context 'when a notification exists' do
before do
allow(instructeur).to receive(:notifications_for_procedure)
.with(procedure_to_assign, :not_archived)
.and_return([1, 2, 3])
allow(instructeur).to receive(:notifications_for_groupe_instructeurs)
.with([procedure_to_assign.groupe_instructeurs.first.id])
.and_return(en_cours: [1, 2, 3], termines: [])
end
it do

View file

@ -5,8 +5,8 @@ describe DossierProjectionService do
context 'with multiple dossier' do
let!(:procedure) { create(:procedure, :with_type_de_champ) }
let!(:dossier_1) { create(:dossier, procedure: procedure) }
let!(:dossier_2) { create(:dossier, procedure: procedure) }
let!(:dossier_3) { create(:dossier, procedure: procedure) }
let!(:dossier_2) { create(:dossier, :en_construction, :archived, procedure: procedure) }
let!(:dossier_3) { create(:dossier, :en_instruction, procedure: procedure) }
let(:dossiers_ids) { [dossier_3.id, dossier_1.id, dossier_2.id] }
let(:fields) do
@ -26,11 +26,20 @@ describe DossierProjectionService do
let(:result) { subject }
it 'respects the dossiers_ids order and returns nil for empty result' do
it 'respects the dossiers_ids order, returns state, archived and nil for empty result' do
expect(result.length).to eq(3)
expect(result[0].dossier.id).to eq(dossier_3.id)
expect(result[1].dossier.id).to eq(dossier_1.id)
expect(result[2].dossier.id).to eq(dossier_2.id)
expect(result[0].dossier_id).to eq(dossier_3.id)
expect(result[1].dossier_id).to eq(dossier_1.id)
expect(result[2].dossier_id).to eq(dossier_2.id)
expect(result[0].state).to eq('en_instruction')
expect(result[1].state).to eq('brouillon')
expect(result[2].state).to eq('en_construction')
expect(result[0].archived).to be false
expect(result[1].archived).to be false
expect(result[2].archived).to be true
expect(result[0].columns[0]).to be nil
expect(result[1].columns[0]).to eq('champ_1')

View file

@ -92,8 +92,8 @@ describe NotificationService do
context 'when there is a notification on this procedure' do
before do
allow_any_instance_of(Instructeur).to receive(:notifications_for_procedure)
.and_return([12])
allow_any_instance_of(Instructeur).to receive(:notifications_for_groupe_instructeurs)
.and_return(en_cours: [12], termines: [])
end
it do

View file

@ -1,20 +1,16 @@
describe 'instructeurs/procedures/_download_dossiers.html.haml', type: :view do
let(:current_instructeur) { create(:instructeur) }
let(:procedure) { create(:procedure) }
let(:dossier_count) { 0 }
subject { render 'instructeurs/procedures/download_dossiers.html.haml', procedure: procedure, xlsx_export: nil, csv_export: nil, ods_export: nil }
subject { render 'instructeurs/procedures/download_dossiers.html.haml', procedure: procedure, dossier_count: dossier_count, xlsx_export: nil, csv_export: nil, ods_export: nil }
context "when procedure has 0 dossier" do
it { is_expected.not_to include("Télécharger tous les dossiers") }
end
context "when procedure has 1 dossier brouillon" do
let!(:dossier) { create(:dossier, procedure: procedure) }
it { is_expected.not_to include("Télécharger tous les dossiers") }
end
context "when procedure has at least 1 dossier en construction" do
let!(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
context "when procedure has at least 1 dossier" do
let(:dossier_count) { 1 }
it { is_expected.to include("Télécharger tous les dossiers") }
context "With zip archive enabled" do