Merge pull request #10918 from demarches-simplifiees/gallery_v2_add_origins

ETQ instructeur je vois dans la galerie les pjs issues des avis, annotations privées et justificatifs de décision
This commit is contained in:
Eric Leroy-Terquem 2024-11-05 16:01:32 +00:00 committed by GitHub
commit 3f43e3433a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 221 additions and 22 deletions

View file

@ -17,17 +17,35 @@ class Attachment::GalleryItemComponent < ApplicationComponent
def gallery_demande? = @gallery_demande
def libelle
from_dossier? ? attachment.record.libelle : 'Pièce jointe au message'
if from_champ?
attachment.record.libelle
elsif from_messagerie?
'Pièce jointe au message'
elsif from_avis_externe?
'Pièce jointe à lavis'
elsif from_justificatif_motivation?
'Pièce jointe à la décision'
end
end
def origin
case
when from_dossier?
when from_public_champ?
'Dossier usager'
when from_private_champ?
'Annotation privée'
when from_messagerie_expert?
'Messagerie (expert)'
when from_messagerie_instructeur?
'Messagerie (instructeur)'
when from_messagerie_usager?
'Messagerie (usager)'
when from_avis_externe_instructeur?
'Avis externe (instructeur)'
when from_avis_externe_expert?
'Avis externe (expert)'
when from_justificatif_motivation?
'Justificatif de décision'
end
end
@ -52,7 +70,7 @@ class Attachment::GalleryItemComponent < ApplicationComponent
end
def updated?
from_dossier? && updated_at > attachment.record.dossier.depose_at
from_public_champ? && updated_at > attachment.record.dossier.depose_at
end
def updated_at
@ -68,10 +86,18 @@ class Attachment::GalleryItemComponent < ApplicationComponent
private
def from_dossier?
def from_champ?
attachment.record.class.in?([Champs::PieceJustificativeChamp, Champs::TitreIdentiteChamp])
end
def from_public_champ?
from_champ? && !attachment.record.private?
end
def from_private_champ?
from_champ? && attachment.record.private?
end
def from_messagerie?
attachment.record.is_a?(Commentaire)
end
@ -80,7 +106,27 @@ class Attachment::GalleryItemComponent < ApplicationComponent
from_messagerie? && attachment.record.instructeur.present?
end
def from_messagerie_expert?
from_messagerie? && attachment.record.expert.present?
end
def from_messagerie_usager?
from_messagerie? && attachment.record.instructeur.nil?
from_messagerie? && attachment.record.instructeur.nil? && attachment.record.expert.nil?
end
def from_avis_externe?
attachment.record.is_a?(Avis)
end
def from_avis_externe_instructeur?
from_avis_externe? && attachment.name == 'introduction_file'
end
def from_avis_externe_expert?
from_avis_externe? && attachment.name == 'piece_justificative_file'
end
def from_justificatif_motivation?
attachment.name == 'justificatif_motivation'
end
end

View file

@ -9,7 +9,7 @@
Visualiser
- if !gallery_demande?
.fr-text--sm.fr-mt-2v.fr-mb-1v
= libelle.truncate(25)
= libelle.truncate(30)
= render Attachment::ShowComponent.new(attachment:, truncate: true, new_tab: gallery_demande?)
- if !gallery_demande?
.fr-mt-2v.fr-mb-2v{ class: badge_updated_class }
@ -19,7 +19,7 @@
= image_tag('apercu-indisponible.png')
- if !gallery_demande?
.fr-text--sm.fr-mt-2v.fr-mb-1v
= libelle.truncate(25)
= libelle.truncate(30)
= render Attachment::ShowComponent.new(attachment:, truncate: true, new_tab: gallery_demande?)
- if !gallery_demande?
.fr-mt-2v.fr-mb-2v{ class: badge_updated_class }

View file

@ -104,7 +104,12 @@ module Experts
updated_recently = @avis.updated_recently?
if @avis.update(avis_params)
flash.notice = 'Votre réponse est enregistrée.'
@avis.dossier.update!(last_avis_updated_at: Time.zone.now)
timestamps = [:last_avis_updated_at, :updated_at]
timestamps << :last_avis_piece_jointe_updated_at if @avis.piece_justificative_file.attached?
@avis.dossier.touch(*timestamps)
if !updated_recently
@avis.dossier.followers_instructeurs
.with_instant_expert_avis_email_notifications_enabled
@ -162,7 +167,11 @@ module Experts
@commentaire = CommentaireService.create(current_expert, avis.dossier, commentaire_params)
if @commentaire.errors.empty?
@commentaire.dossier.update!(last_commentaire_updated_at: Time.zone.now)
timestamps = [:last_commentaire_updated_at, :updated_at]
timestamps << :last_commentaire_piece_jointe_updated_at if @commentaire.piece_jointe.attached?
@commentaire.dossier.touch(*timestamps)
flash.notice = "Message envoyé"
redirect_to messagerie_expert_avis_path(avis.procedure, avis)
else

View file

@ -513,11 +513,21 @@ module Instructeurs
.commentaires
.includes(piece_jointe_attachments: :blob)
.map(&:piece_jointe)
.map(&:attachments)
.flatten
.flat_map(&:attachments)
.map(&:id)
champs_attachments_ids + commentaires_attachments_ids
avis_attachments_ids = dossier
.avis.flat_map { [_1.introduction_file, _1.piece_justificative_file] }
.flat_map(&:attachments)
.compact
.map(&:id)
justificatif_motivation_id = dossier
.justificatif_motivation
&.attachment
&.id
champs_attachments_ids + commentaires_attachments_ids + avis_attachments_ids + [justificatif_motivation_id]
end
@gallery_attachments = ActiveStorage::Attachment.where(id: gallery_attachments_ids)
end

View file

@ -10,7 +10,7 @@ module BlobImageProcessorConcern
end
def representation_required?
from_champ? || from_messagerie? || logo? || from_action_text?
from_champ? || from_messagerie? || logo? || from_action_text? || from_avis? || from_justificatif_motivation?
end
private
@ -31,7 +31,15 @@ module BlobImageProcessorConcern
attachments.any? { _1.record.class == ActionText::RichText }
end
def from_avis?
attachments.any? { _1.record.class == Avis }
end
def watermark_required?
attachments.any? { _1.record.class == Champs::TitreIdentiteChamp }
end
def from_justificatif_motivation?
attachments.any? { _1.name == 'justificatif_motivation' }
end
end

View file

@ -378,7 +378,8 @@ class Dossier < ApplicationRecord
' OR last_avis_updated_at > follows.avis_seen_at' \
' OR last_commentaire_updated_at > follows.messagerie_seen_at' \
' OR last_commentaire_piece_jointe_updated_at > follows.pieces_jointes_seen_at' \
' OR last_champ_piece_jointe_updated_at > follows.pieces_jointes_seen_at')
' OR last_champ_piece_jointe_updated_at > follows.pieces_jointes_seen_at' \
' OR last_avis_piece_jointe_updated_at > follows.pieces_jointes_seen_at')
.distinct
end

View file

@ -125,7 +125,7 @@ class Instructeur < ApplicationRecord
annotations_privees = dossier.last_champ_private_updated_at&.>(follow.annotations_privees_seen_at) || false
avis_notif = dossier.last_avis_updated_at&.>(follow.avis_seen_at) || false
messagerie = dossier.last_commentaire_updated_at&.>(follow.messagerie_seen_at) || false
pieces_jointes = dossier.last_champ_piece_jointe_updated_at&.>(follow.pieces_jointes_seen_at) || dossier.last_commentaire_piece_jointe_updated_at&.>(follow.pieces_jointes_seen_at) || false
pieces_jointes = dossier.last_champ_piece_jointe_updated_at&.>(follow.pieces_jointes_seen_at) || dossier.last_commentaire_piece_jointe_updated_at&.>(follow.pieces_jointes_seen_at) || dossier.last_avis_piece_jointe_updated_at&.>(follow.pieces_jointes_seen_at) || false
annotations_hash(demande, annotations_privees, avis_notif, messagerie, pieces_jointes)
else

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddLastAvisPieceJointeUpdatedAt < ActiveRecord::Migration[7.0]
def change
add_column :dossiers, :last_avis_piece_jointe_updated_at, :datetime
end
end

View file

@ -495,6 +495,7 @@ ActiveRecord::Schema[7.0].define(version: 2024_10_14_084333) do
t.string "hidden_by_reason"
t.datetime "hidden_by_user_at", precision: nil
t.datetime "identity_updated_at", precision: nil
t.datetime "last_avis_piece_jointe_updated_at"
t.datetime "last_avis_updated_at", precision: nil
t.datetime "last_champ_piece_jointe_updated_at"
t.datetime "last_champ_private_updated_at", precision: nil

View file

@ -4,9 +4,10 @@ require 'rails_helper'
RSpec.describe Attachment::GalleryItemComponent, type: :component do
let(:instructeur) { create(:instructeur) }
let(:procedure) { create(:procedure, :published, types_de_champ_public:) }
let(:procedure) { create(:procedure, :published, types_de_champ_public:, types_de_champ_private:) }
let(:types_de_champ_public) { [{ type: :piece_justificative }] }
let(:dossier) { create(:dossier, :with_populated_champs, :en_construction, procedure:) }
let(:types_de_champ_private) { [{ type: :piece_justificative }] }
let(:dossier) { create(:dossier, :with_populated_champs, :with_populated_annotations, :en_construction, procedure:) }
let(:filename) { attachment.blob.filename.to_s }
let(:gallery_demande) { false }
let(:seen_at) { nil }
@ -16,8 +17,10 @@ RSpec.describe Attachment::GalleryItemComponent, type: :component do
subject { render_inline(component).to_html }
context "when attachment is from a piece justificative champ" do
let(:champ) { dossier.champs.first }
context "when attachment is from a public piece justificative champ" do
let(:champ) do
dossier.champs.where(private: false).first
end
let(:libelle) { champ.libelle }
let(:attachment) { champ.piece_justificative_file.attachments.first }
@ -56,6 +59,38 @@ RSpec.describe Attachment::GalleryItemComponent, type: :component do
end
end
context "when attachment is from a private piece justificative champ" do
let(:annotation) do
dossier.champs.where(private: true).first
end
let(:libelle) { annotation.libelle }
let(:attachment) { annotation.piece_justificative_file.attachments.first }
# Correspond au cas standard où le blob est créé avant le dépôt du dossier
before { dossier.touch(:depose_at) }
it "displays libelle, link, tag and renders title" do
expect(subject).to have_text(libelle)
expect(subject).to have_link(filename)
expect(subject).to have_text('Annotation privée')
expect(component.title).to eq("#{libelle} -- #{filename}")
end
it "displays when gallery item has been added" do
expect(subject).to have_text('Ajoutée le')
expect(subject).not_to have_css('.highlighted')
expect(subject).to have_text(component.helpers.try_format_datetime(attachment.record.created_at, format: :veryshort))
end
context "when gallery item is in page Demande" do
let(:gallery_demande) { true }
it "does not display libelle" do
expect(subject).not_to have_text(libelle)
end
end
end
context "when attachment is from a commentaire" do
let(:commentaire) { create(:commentaire, :with_file, dossier: dossier) }
let(:attachment) { commentaire.piece_jointe.first }
@ -100,5 +135,75 @@ RSpec.describe Attachment::GalleryItemComponent, type: :component do
expect(subject).to have_text('Messagerie (instructeur)')
end
end
context 'from an expert' do
let(:expert) { create(:expert) }
before { commentaire.update!(expert:) }
it "displays the right tag" do
expect(subject).to have_text('Messagerie (expert)')
end
end
end
context "when attachment is from a justificatif motivation" do
let(:fake_justificatif) { fixture_file_upload('spec/fixtures/files/piece_justificative_0.pdf', 'application/pdf') }
let(:attachment) { dossier.justificatif_motivation.attachment }
before { dossier.update!(justificatif_motivation: fake_justificatif) }
it "displays a generic libelle, link, tag and renders title" do
expect(subject).to have_text('Justificatif de décision')
expect(subject).to have_link(filename)
expect(subject).to have_text('Pièce jointe à la décision')
expect(component.title).to eq("Pièce jointe à la décision -- #{filename}")
end
end
context "when attachment is from an avis" do
context 'from an instructeur' do
let(:avis) { create(:avis, :with_introduction, dossier: dossier) }
let(:attachment) { avis.introduction_file.attachment }
it "displays a generic libelle, link, tag and renders title" do
expect(subject).to have_text('Pièce jointe à lavis')
expect(subject).to have_link(filename)
expect(subject).to have_text('Avis externe (instructeur)')
expect(component.title).to eq("Pièce jointe à lavis -- #{filename}")
end
context "when instructeur has not seen it yet" do
let(:seen_at) { now - 1.day }
before do
attachment.blob.update(created_at: now)
end
it 'displays datetime in the right style' do
expect(subject).to have_css('.highlighted')
end
end
context "when instructeur has already seen it" do
let!(:seen_at) { now }
before do
freeze_time
attachment.blob.touch(:created_at)
end
it 'displays datetime in the right style' do
expect(subject).not_to have_css('.highlighted')
end
end
end
context 'from an expert' do
let(:avis) { create(:avis, :with_piece_justificative, dossier: dossier) }
let(:attachment) { avis.piece_justificative_file.attachment }
it "displays the right tag" do
expect(subject).to have_text('Avis externe (expert)')
end
end
end
end

View file

@ -1486,6 +1486,9 @@ describe Instructeurs::DossiersController, type: :controller do
let(:logo_path) { 'spec/fixtures/files/logo_test_procedure.png' }
let(:rib_path) { 'spec/fixtures/files/RIB.pdf' }
let(:commentaire) { create(:commentaire, dossier: dossier) }
let(:expert) { create(:expert) }
let(:experts_procedure) { create(:experts_procedure, expert: expert, procedure: procedure) }
let(:avis) { create(:avis, :with_answer, :with_piece_justificative, dossier: dossier, claimant: expert, experts_procedure: experts_procedure) }
before do
dossier.champs.first.piece_justificative_file.attach(
@ -1502,20 +1505,29 @@ describe Instructeurs::DossiersController, type: :controller do
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE }
)
avis.piece_justificative_file.attach(
io: File.open(rib_path),
filename: "RIB.pdf",
content_type: "application/pdf",
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE }
)
get :pieces_jointes, params: {
procedure_id: procedure.id,
dossier_id: dossier.id
}
end
it 'returns pieces jointes from champs and from messagerie' do
it 'returns pieces jointes from champs, messagerie and avis' do
expect(response.body).to include('Télécharger le fichier toto.txt')
expect(response.body).to include('Télécharger le fichier logo_test_procedure.png')
expect(response.body).to include('Télécharger le fichier RIB.pdf')
expect(response.body).to include('Visualiser')
expect(assigns(:gallery_attachments).count).to eq 3
expect(response.body).to include('Pièce jointe au message')
expect(response.body).to include('Pièce jointe à lavis')
expect(assigns(:gallery_attachments).count).to eq 4
expect(assigns(:gallery_attachments)).to all(be_a(ActiveStorage::Attachment))
expect([Champs::PieceJustificativeChamp, Champs::TitreIdentiteChamp, Commentaire]).to include(*assigns(:gallery_attachments).map { _1.record.class })
expect([Champs::PieceJustificativeChamp, Champs::TitreIdentiteChamp, Commentaire, Avis]).to include(*assigns(:gallery_attachments).map { _1.record.class })
end
end