Merge pull request #10190 from demarches-simplifiees/add-AR-feature-ldu

ETQ Admin je peux configurer une démarche avec accusé de lecture
This commit is contained in:
Lisa Durand 2024-04-16 09:58:13 +00:00 committed by GitHub
commit 7a80574afc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
47 changed files with 491 additions and 96 deletions

View file

@ -0,0 +1,5 @@
class Dossiers::AccuseLectureComponent < ApplicationComponent
def initialize(dossier:)
@dossier = dossier
end
end

View file

@ -0,0 +1,4 @@
---
en:
text_accuse_lecture: This procedure is subject to a reading acknowledgment. By requesting the display of the decision taken on your file, you accept the reading acknowledgment and thereby the triggering of the legal deadline in the event of an appeal.
btn_accuse_lecture: I would like to display the decision

View file

@ -0,0 +1,4 @@
---
fr:
text_accuse_lecture: Cette démarche est soumise à un accusé de lecture. En demandant laffichage de la décision prise sur votre dossier, vous acceptez laccusé de lecture et par là même le démarrage du délai légal en cas de recours.
btn_accuse_lecture: Je souhaite afficher la décision

View file

@ -0,0 +1,10 @@
= render Dsfr::CalloutComponent.new(title: nil) do |c|
- c.with_body do
= t('.text_accuse_lecture')
= form_for @dossier,
method: :get,
url: set_accuse_lecture_agreement_at_dossier_path(@dossier),
data: { controller: 'autosubmit', turbo: 'true' } do |f|
= f.submit t('.btn_accuse_lecture'), class: "fr-btn fr-mt-2w"

View file

@ -0,0 +1,5 @@
class Procedure::Card::AccuseLectureComponent < ApplicationComponent
def initialize(procedure:)
@procedure = procedure
end
end

View file

@ -0,0 +1,4 @@
---
fr:
title: Accusé de lecture
subtitle: Pour les démarches avec voies de recours

View file

@ -0,0 +1,11 @@
.fr-col-6.fr-col-md-4.fr-col-lg-3
= link_to accuse_lecture_admin_procedure_path(@procedure), class: 'fr-tile fr-enlarge-link' do
.fr-tile__body.flex.column.align-center.justify-between
- if @procedure.accuse_lecture.present?
%p.fr-badge.fr-badge--success Activé
- else
%p.fr-badge.fr-badge--info Désactivé
%div
%h3.fr-h6.fr-mt-10v= t('.title')
%p.fr-tile-subtitle= t('.subtitle')
%p.fr-btn.fr-btn--tertiary= t('views.shared.actions.edit')

View file

@ -3,7 +3,7 @@ module Administrateurs
layout 'all', only: [:all, :administrateurs]
respond_to :html, :xlsx
before_action :retrieve_procedure, only: [:champs, :annotations, :modifications, :edit, :zones, :monavis, :update_monavis, :jeton, :update_jeton, :publication, :publish, :transfert, :close, :confirmation, :allow_expert_review, :allow_expert_messaging, :experts_require_administrateur_invitation, :reset_draft, :publish_revision, :check_path]
before_action :retrieve_procedure, only: [:champs, :annotations, :modifications, :edit, :zones, :monavis, :update_monavis, :accuse_lecture, :update_accuse_lecture, :jeton, :update_jeton, :publication, :publish, :transfert, :close, :confirmation, :allow_expert_review, :allow_expert_messaging, :experts_require_administrateur_invitation, :reset_draft, :publish_revision, :check_path]
before_action :draft_valid?, only: [:apercu]
after_action :reset_procedure, only: [:update]
@ -276,6 +276,13 @@ module Administrateurs
render 'monavis'
end
def accuse_lecture
end
def update_accuse_lecture
@procedure.update!(procedure_params)
end
def jeton
end
@ -515,6 +522,7 @@ module Administrateurs
:logo,
:auto_archive_on,
:monavis_embed,
:accuse_lecture,
:api_entreprise_token,
:duree_conservation_dossiers_dans_ds,
{ zone_ids: [] },

View file

@ -124,6 +124,13 @@ module Users
@dossier = dossier
end
def set_accuse_lecture_agreement_at
@dossier = dossier
@dossier.update!(accuse_lecture_agreement_at: Time.zone.now)
flash.notice = 'Accusé de lecture accepté'
redirect_back(fallback_location: demande_dossier_path(@dossier))
end
def identite
@dossier = dossier
@user = current_user

View file

@ -78,6 +78,14 @@ module DossierHelper
end
end
def status_badge_user(dossier, alignment_class = '')
if dossier.hide_info_with_accuse_lecture?
tag.span 'traité', role: 'status', class: "fr-badge fr-badge--sm fr-badge--no-icon #{alignment_class}"
else
status_badge(dossier.state, alignment_class)
end
end
def status_badge(state, alignment_class = '')
status_text = dossier_display_state(state, lower: true)
tag.span status_text, role: 'status', class: class_names(

View file

@ -6,7 +6,7 @@
# The subject and body of a Notification can be customized by each demarche.
#
class NotificationMailer < ApplicationMailer
before_action :set_dossier, except: [:send_notification_for_tiers]
before_action :set_dossier, except: [:send_notification_for_tiers, :send_accuse_lecture_notification]
before_action :set_services_publics_plus, only: :send_notification
helper ServiceHelper
@ -42,6 +42,16 @@ class NotificationMailer < ApplicationMailer
mail(subject: @subject, to: @email, template_name: 'send_notification_for_tiers')
end
def send_accuse_lecture_notification(dossier)
@dossier = dossier
@subject = "La décision a été rendue pour votre démarche #{@dossier.procedure.libelle.truncate_words(50)}"
@email = @dossier.user_email_for(:notification)
@logo_url = procedure_logo_url(@dossier.procedure)
mail(subject: @subject, to: @email, template_name: 'send_accuse_lecture_notification')
end
def self.send_en_construction_notification(dossier)
with(dossier: dossier, state: Dossier.states.fetch(:en_construction)).send_notification
end

View file

@ -120,7 +120,11 @@ module DossierStateConcern
disable_notification = h.fetch(:disable_notification, false)
if !disable_notification
NotificationMailer.send_accepte_notification(self).deliver_later
if procedure.accuse_lecture?
NotificationMailer.send_accuse_lecture_notification(self).deliver_later
else
NotificationMailer.send_accepte_notification(self).deliver_later
end
NotificationMailer.send_notification_for_tiers(self).deliver_later if self.for_tiers?
end
@ -152,7 +156,11 @@ module DossierStateConcern
end
def after_commit_accepter_automatiquement
NotificationMailer.send_accepte_notification(self).deliver_later
if procedure.accuse_lecture?
NotificationMailer.send_accuse_lecture_notification(self).deliver_later
else
NotificationMailer.send_accepte_notification(self).deliver_later
end
NotificationMailer.send_notification_for_tiers(self).deliver_later if self.for_tiers?
send_dossier_decision_to_experts(self)
@ -184,7 +192,11 @@ module DossierStateConcern
disable_notification = h.fetch(:disable_notification, false)
if !disable_notification
NotificationMailer.send_refuse_notification(self).deliver_later
if procedure.accuse_lecture?
NotificationMailer.send_accuse_lecture_notification(self).deliver_later
else
NotificationMailer.send_refuse_notification(self).deliver_later
end
NotificationMailer.send_notification_for_tiers(self).deliver_later if self.for_tiers?
end
@ -209,7 +221,11 @@ module DossierStateConcern
end
def after_commit_refuser_automatiquement
NotificationMailer.send_refuse_notification(self).deliver_later
if procedure.accuse_lecture?
NotificationMailer.send_accuse_lecture_notification(self).deliver_later
else
NotificationMailer.send_refuse_notification(self).deliver_later
end
NotificationMailer.send_notification_for_tiers(self).deliver_later if self.for_tiers?
send_dossier_decision_to_experts(self)
@ -241,7 +257,11 @@ module DossierStateConcern
disable_notification = h.fetch(:disable_notification, false)
if !disable_notification
NotificationMailer.send_sans_suite_notification(self).deliver_later
if procedure.accuse_lecture?
NotificationMailer.send_accuse_lecture_notification(self).deliver_later
else
NotificationMailer.send_sans_suite_notification(self).deliver_later
end
NotificationMailer.send_notification_for_tiers(self).deliver_later if self.for_tiers?
end

View file

@ -1180,6 +1180,14 @@ class Dossier < ApplicationRecord
end
end
def hide_info_with_accuse_lecture?
procedure.accuse_lecture? && termine? && accuse_lecture_agreement_at.blank?
end
def termine_and_accuse_lecture?
procedure.accuse_lecture? && termine?
end
private
def champs_by_public_id

View file

@ -23,6 +23,7 @@ class DossiersFilter
dossiers_result = dossiers
dossiers_result = dossiers_result.where(state: state) if state.present? && state != Dossier::A_CORRIGER
dossiers_result = dossiers_result.with_pending_corrections if state.present? && state == Dossier::A_CORRIGER
dossiers_result = exclude_accuse_lecture(dossiers_result) if state.present? && Dossier::TERMINE.include?(state)
dossiers_result = dossiers_result.where('dossiers.created_at >= ?', from_created_at_date) if from_created_at_date.present?
dossiers_result = dossiers_result.where('dossiers.depose_at >= ?', from_depose_at_date) if from_depose_at_date.present?
dossiers_result
@ -47,4 +48,8 @@ class DossiersFilter
rescue Date::Error
nil
end
def exclude_accuse_lecture(dossiers)
dossiers.joins(:procedure).where.not('dossiers.accuse_lecture_agreement_at IS NULL AND procedures.accuse_lecture = TRUE ')
end
end

View file

@ -3,9 +3,13 @@ class MailTemplatePresenterService
include ActionView::Helpers::TextHelper
def self.create_commentaire_for_state(dossier, state)
service = new(dossier, state)
body = ["<p>[#{service.safe_subject}]</p>", service.safe_body].join('')
CommentaireService.create!(CONTACT_EMAIL, dossier, body: body)
if dossier.procedure.accuse_lecture? && Dossier::TERMINE.include?(state)
CommentaireService.create!(CONTACT_EMAIL, dossier, body: I18n.t('layouts.mailers.accuse_lecture.commentaire_html', service: dossier.procedure.service.nom))
else
service = new(dossier, state)
body = ["<p>[#{service.safe_subject}]</p>", service.safe_body].join('')
CommentaireService.create!(CONTACT_EMAIL, dossier, body: body)
end
end
def safe_body

View file

@ -0,0 +1,43 @@
= render partial: 'administrateurs/breadcrumbs',
locals: { steps: [['Démarches', admin_procedures_back_path(@procedure)],
[@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)],
['Accusé de lecture']] }
.fr-container
.fr-grid-row
.fr-col-12.fr-col-offset-md-2.fr-col-md-8
%h1.page-title
Accusé de lecture
= render Dsfr::CalloutComponent.new(title: nil) do |c|
- c.with_body do
%p
Pour les démarches avec voies de recours, il est possible dactiver laccusé de lecture.
%br
%p
Cette fonctionnalité permet à linstructeur de connaître la date de lecture de la décision finale par lusager.
%br
%p
Lusager na plus accès à la décision finale par mail, mais il doit se connecter sur la plateforme #{Current.application_name} pour en prendre connaissance et en accuser lecture.
%ul.fr-toggle__list
%li
= form_for @procedure,
method: :patch,
url: update_accuse_lecture_admin_procedure_path(@procedure),
data: { controller: 'autosubmit', turbo: 'true' } do |f|
= render Dsfr::ToggleComponent.new(form: f,
target: :accuse_lecture,
title: "Accusé de lecture de la démarche",
hint: "Laccusé de lecture est à activer uniquement pour les démarches avec voies de recours car il complexifie laccès à la décision finale pour les usagers",
opt: {"checked" => @procedure.accuse_lecture})
.padded-fixed-footer
.fixed-footer
.fr-container
.fr-grid-row
.fr-col-12.fr-col-offset-md-2.fr-col-md-8
%ul.fr-btns-group.fr-btns-group--inline-md
%li
= link_to 'Enregistrer et revenir à la page de suivi', admin_procedure_path(id: @procedure), class: 'fr-btn'

View file

@ -89,3 +89,4 @@
= render Procedure::Card::MonAvisComponent.new(procedure: @procedure)
= render Procedure::Card::DossierSubmittedMessageComponent.new(procedure: @procedure)
= render Procedure::Card::ChorusComponent.new(procedure: @procedure)
= render Procedure::Card::AccuseLectureComponent.new(procedure: @procedure)

View file

@ -0,0 +1,23 @@
- content_for :procedure_logo do
= render 'layouts/mailers/logo', url: @logo_url
%p
= t("layouts.mailers.accuse_lecture.good_morning")
%p
= t("layouts.mailers.accuse_lecture.first_part",
dossier_id: number_with_delimiter(@dossier.id))
%span{ :style => "font-weight: bold;" }
= @dossier.procedure.libelle
= t("layouts.mailers.accuse_lecture.second_part")
%p
= t("layouts.mailers.accuse_lecture.third_part")
= link_to APPLICATION_NAME, dossier_url(@dossier)
%p
= t(:best_regards, scope: [:views, :shared, :greetings])
%br
= t('layouts.mailers.signature.team')
= APPLICATION_NAME

View file

@ -15,8 +15,10 @@
- if @repasser_en_instruction
= t("layouts.mailers.for_tiers.repasser_en_instruction")
- elsif @dossier.hide_info_with_accuse_lecture?
= t("layouts.mailers.for_tiers.accuse_lecture", processed_at: l(@dossier.updated_at.to_date))
- else
= t("layouts.mailers.for_tiers.#{@dossier.state}", processed_at: l(@dossier.updated_at.to_date) )
= t("layouts.mailers.for_tiers.#{@dossier.state}", processed_at: l(@dossier.updated_at.to_date))
%p
= t("layouts.mailers.for_tiers.second_part")

View file

@ -5,11 +5,20 @@
.fr-container.counter-start-header-section.dossier-show{ class: class_names("dossier-show-instructeur" => profile =="instructeur") }
.fr-grid-row.fr-grid-row--center
.fr-col-12.fr-col-xl-8
- if profile == 'instructeur' && dossier.termine_and_accuse_lecture?
= render Dsfr::CalloutComponent.new(title: nil) do |c|
- c.with_html_body do
= t('views.shared.dossiers.demande.accuse_lecture')
- if dossier.accuse_lecture_agreement_at.present?
= t('views.shared.dossiers.demande.accuse_lecture_with_agreement', agreement: l(dossier.accuse_lecture_agreement_at, format: :long))
- else
= t('views.shared.dossiers.demande.accuse_lecture_without_agreement')
%h2.fr-h6.fr-background-alt--grey.fr-mb-0
.flex-grow.fr-py-3v.fr-px-2w= t('views.shared.dossiers.demande.en_construction')
- if dossier.depose_at.present?
= render partial: "shared/dossiers/infos_generales", locals: { dossier: dossier }
= render partial: "shared/dossiers/infos_generales", locals: { dossier: dossier, profile: profile }
- if dossier.for_tiers?

View file

@ -1,6 +1,6 @@
%h1
= procedure_libelle(dossier.procedure)
= status_badge(dossier.state, 'super')
= status_badge_user(dossier, 'super')
%h2
= t('views.users.dossiers.show.header.dossier_number', dossier_id: dossier.id)
= t('views.users.dossiers.show.header.created_date', date_du_dossier: I18n.l(dossier.created_at))

View file

@ -9,18 +9,22 @@
.fr-highlight
%p.fr-text--sm.fr-text-mention--grey Sauf mention contraire, les champs ont été saisis à la date du dépôt du dossier.
- if dossier.justificatif_motivation.attached?
= render Dossiers::RowShowComponent.new(label: "Justificatif") do |c|
- c.with_value do
.action
= render Attachment::ShowComponent.new(attachment: dossier.justificatif_motivation.attachment)
- if profile == 'usager' && dossier.hide_info_with_accuse_lecture?
= render Dossiers::AccuseLectureComponent.new(dossier: dossier)
- if dossier.motivation.present?
= render Dossiers::RowShowComponent.new(label: "Motivation") do |c|
- c.with_value do
= simple_format dossier.motivation
- else
- if dossier.justificatif_motivation.attached?
= render Dossiers::RowShowComponent.new(label: "Justificatif") do |c|
- c.with_value do
.action
= render Attachment::ShowComponent.new(attachment: dossier.justificatif_motivation.attachment)
- if dossier.attestation.present? && dossier.attestation.pdf.attached?
= render Dossiers::RowShowComponent.new(label: "Attestation") do |c|
- c.with_value do
= render Dsfr::DownloadComponent.new(attachment: dossier.attestation.pdf, name: t(:download_attestation, scope: [:views, :shared, :dossiers, :form]))
- if dossier.motivation.present?
= render Dossiers::RowShowComponent.new(label: "Motivation") do |c|
- c.with_value do
= simple_format dossier.motivation
- if dossier.attestation.present? && dossier.attestation.pdf.attached?
= render Dossiers::RowShowComponent.new(label: "Attestation") do |c|
- c.with_value do
= render Dsfr::DownloadComponent.new(attachment: dossier.attestation.pdf, name: t(:download_attestation, scope: [:views, :shared, :dossiers, :form]))

View file

@ -44,7 +44,7 @@
%span.fr-badge.fr-badge--sm.fr-badge--warning
= t('views.users.dossiers.dossiers_list.deleted_badge')
- else
= status_badge(dossier.state, 'fr-mb-1w')
= status_badge_user(dossier, 'fr-mb-1w')
- if dossier.pending_correction?
%br

View file

@ -2,7 +2,7 @@
.fr-container
%h1
= dossier.procedure.libelle
= status_badge(dossier.state, 'super')
= status_badge_user(dossier, 'super')
= pending_correction_badge(:for_user) if dossier.pending_correction?
%h2
= t('views.users.dossiers.show.header.dossier_number', dossier_id: dossier.id)

View file

@ -54,46 +54,50 @@
%p
= t('views.users.dossiers.show.status_overview.use_mailbox_for_questions_html', mailbox_url: messagerie_dossier_url(dossier))
- elsif dossier.accepte?
.accepte
%p.decision{ role: 'status' }
= dsfr_icon('fr-icon-checkbox-circle-fill fr-text-default--success')
= t('views.users.dossiers.show.status_overview.acceptee_html')
- elsif dossier.termine?
- if dossier.hide_info_with_accuse_lecture?
= render Dossiers::AccuseLectureComponent.new(dossier: dossier)
- if dossier.motivation.present?
%h3= t('views.users.dossiers.show.status_overview.accepte_motivation')
%blockquote= simple_format(dossier.motivation)
- elsif dossier.accepte?
.accepte
%p.decision{ role: 'status' }
= dsfr_icon('fr-icon-checkbox-circle-fill fr-text-default--success')
= t('views.users.dossiers.show.status_overview.acceptee_html')
= render partial: 'users/dossiers/show/download_justificatif', locals: { dossier: dossier }
- if dossier.motivation.present?
%h3= t('views.users.dossiers.show.status_overview.accepte_motivation')
%blockquote= simple_format(dossier.motivation)
- if dossier.attestation.present?
= render partial: 'users/dossiers/show/download_justificatif', locals: { dossier: dossier }
- if dossier.attestation.present?
.action
= link_to attestation_dossier_path(dossier), class: "fr-btn fr-icon-download-line fr-btn--icon-left", **external_link_attributes do
= t('views.users.dossiers.show.status_overview.accepte_attestation')
- elsif dossier.refuse?
.refuse
%p.decision{ role: 'status' }
= dsfr_icon('fr-icon-close-circle-fill fr-text-default--error')
= t('views.users.dossiers.show.status_overview.refuse_html')
- if dossier.motivation.present?
%h3= t('views.users.dossiers.show.status_overview.refuse_motivation')
%blockquote= simple_format(dossier.motivation)
= render partial: 'users/dossiers/show/download_justificatif', locals: { dossier: dossier }
.action
= link_to attestation_dossier_path(dossier), class: "fr-btn fr-icon-download-line fr-btn--icon-left", target: '_blank', rel: 'noopener' do
= t('views.users.dossiers.show.status_overview.accepte_attestation')
= link_to t('views.users.dossiers.show.status_overview.refuse_reply'), messagerie_dossier_url(dossier, anchor: 'new_commentaire'), class: 'fr-link'
- elsif dossier.sans_suite?
.sans-suite
%p.decision{ role: 'status' }
= dsfr_icon('fr-icon-intermediate-circle-fill')
= t('views.users.dossiers.show.status_overview.sans_suite_html')
- elsif dossier.refuse?
.refuse
%p.decision{ role: 'status' }
= dsfr_icon('fr-icon-close-circle-fill fr-text-default--error')
= t('views.users.dossiers.show.status_overview.refuse_html')
= render partial: 'users/dossiers/show/download_justificatif', locals: { dossier: dossier }
- if dossier.motivation.present?
%h3= t('views.users.dossiers.show.status_overview.refuse_motivation')
%blockquote= simple_format(dossier.motivation)
= render partial: 'users/dossiers/show/download_justificatif', locals: { dossier: dossier }
.action
= link_to t('views.users.dossiers.show.status_overview.refuse_reply'), messagerie_dossier_url(dossier, anchor: 'new_commentaire'), class: 'fr-link'
- elsif dossier.sans_suite?
.sans-suite
%p.decision{ role: 'status' }
= dsfr_icon('fr-icon-intermediate-circle-fill')
= t('views.users.dossiers.show.status_overview.sans_suite_html')
= render partial: 'users/dossiers/show/download_justificatif', locals: { dossier: dossier }
- if dossier.motivation.present?
%h3= t('views.users.dossiers.show.status_overview.sans_suite_motivation')
%blockquote= simple_format(dossier.motivation)
- if dossier.motivation.present?
%h3= t('views.users.dossiers.show.status_overview.sans_suite_motivation')
%blockquote= simple_format(dossier.motivation)