Merge pull request #9104 from colinux/sva
ETQ admin je peux configurer ma démarche en SVA/SVR
This commit is contained in:
commit
0d106cdf4b
84 changed files with 1737 additions and 80 deletions
|
@ -29,3 +29,15 @@
|
|||
vertical-align: baseline;
|
||||
}
|
||||
}
|
||||
|
||||
.badge-group {
|
||||
display: flex;
|
||||
|
||||
.fr-badge {
|
||||
margin-right: $default-spacer;
|
||||
}
|
||||
|
||||
.fr-badge:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,22 @@
|
|||
class Dsfr::RadioButtonListComponent < ApplicationComponent
|
||||
def initialize(form:, target:, buttons:)
|
||||
attr_reader :error
|
||||
|
||||
def initialize(form:, target:, buttons:, error: nil)
|
||||
@form = form
|
||||
@target = target
|
||||
@buttons = buttons
|
||||
@error = error
|
||||
end
|
||||
|
||||
def error?
|
||||
# TODO: mettre correctement le aria-labelled-by avec l'id du div qui contient les erreurs
|
||||
# https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/bouton-radio/
|
||||
@error.present?
|
||||
end
|
||||
|
||||
def each_button
|
||||
@buttons.each do |button|
|
||||
yield(*button.values_at(:label, :value, :hint), **button.except(:label, :value, :hint))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
%fieldset.fr-fieldset{ 'aria-labelledby': 'radio-hint-element-legend radio-hint-element-messages' }
|
||||
%fieldset{ class: class_names("fr-fieldset": true, "fr-fieldset--error": error?), 'aria-labelledby': 'radio-hint-element-legend radio-hint-element-messages', role: error? ? :group : nil }
|
||||
%legend.fr-fieldset__legend--regular.fr-fieldset__legend
|
||||
= content
|
||||
|
||||
- @buttons.map { _1.values_at(:label, :value, :hint) }.each do |label, value, hint|
|
||||
- each_button do |label, value, hint, **button_options|
|
||||
.fr-fieldset__element
|
||||
.fr-radio-group
|
||||
= @form.radio_button @target, value
|
||||
= @form.radio_button @target, value, **button_options
|
||||
= @form.label @target, value: value, class: 'fr-label' do
|
||||
- capture do
|
||||
= label
|
||||
%span.fr-hint-text= hint
|
||||
.fr-messages-group{ 'aria-live': 'assertive' }
|
||||
|
||||
= button_options[:after_label] if button_options[:after_label]
|
||||
|
||||
%span.fr-hint-text= hint if hint
|
||||
|
||||
.fr-messages-group{ 'aria-live': 'assertive' }
|
||||
- if error?
|
||||
%p.fr-message.fr-message--error= error
|
||||
|
|
|
@ -15,10 +15,18 @@ class Instructeurs::EnConstructionMenuComponent < ApplicationComponent
|
|||
end
|
||||
|
||||
def menu_label
|
||||
if dossier.en_construction?
|
||||
if !dossier.may_repasser_en_construction?
|
||||
t('.request_correction')
|
||||
else
|
||||
t(".revert_en_construction")
|
||||
end
|
||||
end
|
||||
|
||||
def sva?
|
||||
dossier.procedure.sva?
|
||||
end
|
||||
|
||||
def sva_resume_method
|
||||
dossier.procedure.sva_svr_configuration.resume
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
en:
|
||||
revert_en_construction: Revert to in progress
|
||||
request_correction: Request a correction
|
||||
request_completion: Request to complete
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
fr:
|
||||
revert_en_construction: Repasser en construction
|
||||
request_correction: Demander une correction
|
||||
request_completion: Demander à compléter
|
||||
|
|
|
@ -16,7 +16,13 @@
|
|||
|
||||
.dropdown-description
|
||||
%h4= t('.request_correction')
|
||||
L’usager sera informé que des modifications sont attendues
|
||||
L’usager sera informé que des modifications sont attendues.
|
||||
|
||||
- if sva?
|
||||
- if sva_resume_method == :reset
|
||||
Le délai du SVA sera réinitialisé lorqu’il déclarera avoir complété le dossier.
|
||||
- else
|
||||
Le délai du SVA reprendra lorsqu’il déclarera avoir corrigé le dossier.
|
||||
|
||||
- menu.with_item(class: "inactive form-inside fr-pt-1v") do
|
||||
= render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier:,
|
||||
|
@ -29,3 +35,24 @@
|
|||
process_action: nil,
|
||||
title: 'Marquer en attente de corrections',
|
||||
confirm: 'Envoyer la demande de corrections ?'}
|
||||
|
||||
- if sva?
|
||||
- menu.with_item do
|
||||
= link_to('#', onclick: "DS.showMotivation(event, 'pending_completion');", role: 'menuitem') do
|
||||
%span.fr-icon.fr-icon-error-warning-line.fr-text-default--warning.fr-mt-1v{ "aria-hidden": "true" }
|
||||
|
||||
.dropdown-description
|
||||
%h4= t('.request_completion')
|
||||
L’usager sera informé que son dossier est incomplet. Le délai du SVA sera réinitialisé lorque il déclarera avoir complété le dossier.
|
||||
|
||||
- menu.with_item(class: "inactive form-inside fr-pt-1v") do
|
||||
= render partial: 'instructeurs/dossiers/instruction_button_motivation', locals: { dossier:,
|
||||
visible: false,
|
||||
form_path: pending_correction_instructeur_dossier_path(dossier.procedure, dossier, kind: :incomplete),
|
||||
placeholder: 'Expliquez au demandeur comment compléter son dossier',
|
||||
popup_class: 'pending_completion',
|
||||
button_justificatif_label: "Ajouter une pièce jointe (facultatif)",
|
||||
process_button: dossier.en_construction? ? 'Valider' : 'Valider et repasser en construction',
|
||||
process_action: nil,
|
||||
title: 'Marquer le dossier comme incomplet',
|
||||
confirm: 'Envoyer la demande de complétion ?'}
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Instructeurs::SVASVRDecisionBadgeComponent < ApplicationComponent
|
||||
attr_reader :object
|
||||
attr_reader :procedure
|
||||
attr_reader :with_label
|
||||
|
||||
def initialize(projection_or_dossier:, procedure:, with_label: false)
|
||||
@object = projection_or_dossier
|
||||
@procedure = procedure
|
||||
@decision = procedure.sva_svr_configuration.decision.to_sym
|
||||
@with_label = with_label
|
||||
end
|
||||
|
||||
def render?
|
||||
return false unless procedure.sva_svr_enabled?
|
||||
|
||||
[:en_construction, :en_instruction].include? object.state.to_sym
|
||||
end
|
||||
|
||||
def without_date?
|
||||
object.sva_svr_decision_on.nil?
|
||||
end
|
||||
|
||||
def classes
|
||||
class_names(
|
||||
'fr-badge fr-badge--sm': true,
|
||||
'fr-badge--warning': soon?,
|
||||
'fr-badge--info': !soon?
|
||||
)
|
||||
end
|
||||
|
||||
def soon?
|
||||
object.sva_svr_decision_on < 7.days.from_now.to_date
|
||||
end
|
||||
|
||||
def pending_correction?
|
||||
object.pending_correction?
|
||||
end
|
||||
|
||||
def days_count
|
||||
(object.sva_svr_decision_on - Date.current).to_i
|
||||
end
|
||||
|
||||
def sva?
|
||||
@decision == :sva
|
||||
end
|
||||
|
||||
def svr?
|
||||
@decision == :svr
|
||||
end
|
||||
|
||||
def label_for_badge
|
||||
sva? ? "SVA :" : "SVR :"
|
||||
end
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
en:
|
||||
no_sva: Submitted before SVA
|
||||
no_svr: Submitted before SVR
|
||||
in_days:
|
||||
zero: Today
|
||||
one: Tomorrow
|
||||
other: in %{count} days
|
||||
remaining_days_after_correction:
|
||||
other: "%{count} d. after correction"
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
fr:
|
||||
no_sva: Déposé avant SVA
|
||||
no_svr: Déposé avant SVR
|
||||
in_days:
|
||||
zero: Aujourd’hui
|
||||
one: Demain
|
||||
other: dans %{count} jours
|
||||
remaining_days_after_correction:
|
||||
other: "%{count} j. après correction"
|
|
@ -0,0 +1,11 @@
|
|||
- if without_date?
|
||||
%span.fr-badge.fr-badge--sm
|
||||
= t(sva? ? '.no_sva' : '.no_svr')
|
||||
- else
|
||||
%span{ class: classes }
|
||||
- if with_label.present?
|
||||
= label_for_badge
|
||||
- if pending_correction?
|
||||
= t('.remaining_days_after_correction', count: days_count)
|
||||
- else
|
||||
= t('.in_days', count: days_count)
|
7
app/components/procedure/card/sva_svr_component.rb
Normal file
7
app/components/procedure/card/sva_svr_component.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Procedure::Card::SVASVRComponent < ApplicationComponent
|
||||
def initialize(procedure:)
|
||||
@procedure = procedure
|
||||
end
|
||||
end
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
en:
|
||||
ready: "Configuré"
|
||||
needs_configuration: "À configurer"
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
fr:
|
||||
title: "Silence Vaut Accord"
|
||||
subtitle: "Accepter ou Refuser un dossier après un délai"
|
||||
ready: "Configuré"
|
||||
needs_configuration: "À configurer"
|
|
@ -0,0 +1,14 @@
|
|||
.fr-col-6.fr-col-md-4.fr-col-lg-3
|
||||
= link_to edit_admin_procedure_sva_svr_path(@procedure), class: 'fr-tile fr-enlarge-link', id: 'sva' do
|
||||
.fr-tile__body.flex.justify-between
|
||||
- if @procedure.sva_svr_enabled?
|
||||
%div
|
||||
%span.icon.accept
|
||||
%p.fr-tile-status-accept= t('.ready')
|
||||
- else
|
||||
%div
|
||||
%span.icon.clock
|
||||
%p.fr-tile-status-todo= t('.needs_configuration')
|
||||
%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')
|
43
app/components/procedure/sva_svr_form_component.rb
Normal file
43
app/components/procedure/sva_svr_form_component.rb
Normal file
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Procedure::SVASVRFormComponent < ApplicationComponent
|
||||
attr_reader :procedure, :configuration
|
||||
|
||||
def initialize(procedure:, configuration:)
|
||||
@procedure = procedure
|
||||
@configuration = configuration
|
||||
end
|
||||
|
||||
def form_disabled?
|
||||
return false if procedure.brouillon?
|
||||
|
||||
procedure.sva_svr_enabled?
|
||||
end
|
||||
|
||||
def decision_buttons
|
||||
scope = ".decision_buttons"
|
||||
|
||||
[
|
||||
{ label: t("disabled", scope:), value: "disabled", disabled: form_disabled? },
|
||||
{ label: t("sva", scope:), value: "sva", hint: t("sva_hint", scope:) },
|
||||
{ label: t("svr", scope:), value: "svr", hint: t("svr_hint", scope:), disabled: true, after_label: tag.span("Disponible prochainement", class: "fr-badge fr-badge--sm fr-ml-1w") }
|
||||
]
|
||||
end
|
||||
|
||||
def resume_buttons
|
||||
scope = ".resume_buttons"
|
||||
|
||||
[
|
||||
{
|
||||
value: "continue",
|
||||
label: t("continue_label", scope: scope),
|
||||
hint: t("continue_hint", scope: scope)
|
||||
},
|
||||
{
|
||||
value: "reset",
|
||||
label: t("reset_label", scope: scope),
|
||||
hint: t("reset_hint", scope: scope)
|
||||
}
|
||||
]
|
||||
end
|
||||
end
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
en:
|
||||
rule: Rule to apply
|
||||
delay: Configuration of the delay before decision
|
||||
unit_labels:
|
||||
months: months
|
||||
weeks: weeks
|
||||
days: days
|
||||
resume_method: How to calculate the delay when the applicant resubmits their corrected file?
|
||||
resume_intro: |
|
||||
When an instructor asks for a file to be corrected, the countdown of the delay is interrupted.
|
||||
The delay resumes when the applicant resubmits their file stating that they have made the requested corrections.
|
||||
If the file has been declared incomplete, the delay will be reset, regardless of the configuration below.
|
||||
submit: Apply SVA/SVR configuration
|
||||
cancel: Cancel
|
||||
decision_buttons:
|
||||
disabled: "Disabled"
|
||||
sva: "Silence Equals Acceptation (SVA)"
|
||||
sva_hint: "A file is automatically accepted if no instructor has pronounced before the allotted time"
|
||||
svr: "Silence Equals Rejection (SVR)"
|
||||
svr_hint: "A file is automatically rejected if no instructor has pronounced before the allotted time"
|
||||
resume_buttons:
|
||||
continue_label: "Resume countdown from where it stopped"
|
||||
continue_hint: "Example: if the instructor requests corrections to a complete file with 10 days to go before the automatic decision, and the file is resubmitted on April 15, it will be automatically accepted on April 25, unless the instructor makes a decision by then or requests corrections again. On the other hand, if the inspector asks for the file to be completed, the deadline will be reset."
|
||||
reset_label: "Reset the delay"
|
||||
reset_hint: "Example: if the file is resubmitted on April 15 and the delay is 2 months, the decision will be automatically made on June 15, unless the instructor pronounces in the meantime or asks for corrections again."
|
||||
notice_new_files_only: "Information: if you activate this rule, only the newly submitted files will be subject to it."
|
||||
notice_edit_denied: "Warning: SVA/SVR cannot be changed or disabled."
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
fr:
|
||||
rule: Règle à appliquer
|
||||
delay: Configuration du délai avant décision
|
||||
unit_labels:
|
||||
months: mois
|
||||
weeks: semaines
|
||||
days: jours
|
||||
resume_method: Comment calculer le délai lorsque le demandeur re-dépose son dossier corrigé ?
|
||||
resume_intro: |
|
||||
Lorsqu’un instructeur demande de corriger un dossier, le décompte du délai est interrompu.
|
||||
Le délai reprend lorsque le demandeur redépose son dossier en déclarant avoir effectué les corrections demandées.
|
||||
Si le dossier avait été déclaré incomplet, le délai sera réinitialisé, quelle que soit la configuration ci-dessous.
|
||||
submit: Appliquer la configuration SVA/SVR
|
||||
cancel: Annuler
|
||||
decision_buttons:
|
||||
disabled: "Désactivé"
|
||||
sva: "Silence Vaut Accord"
|
||||
sva_hint: "Un dossier est automatiquement accepté si aucun n’instructeur ne s’est prononcé avant le délai imparti"
|
||||
svr: "Silence Vaut Rejet"
|
||||
svr_hint: "Un dossier est automatiquement refusé si aucun n’instructeur ne s’est prononcé avant le délai imparti"
|
||||
resume_buttons:
|
||||
continue_label: "Reprendre le décompte depuis le moment où il s’était arrêté"
|
||||
continue_hint: "Exemple: si l’instructeur demande des corrections d’un dossier complet alors qu’il reste 10 jours avant la décision automatique, et que le dossier est re-déposé le 15 avril, il sera automatiquement accepté le 25 avril, sauf à ce que l’instructeur se prononce d’ici là ou demande à nouveau des corrections. En revanche si l’instructeur demande à compléter le dossier, le délai sera réinitialisé."
|
||||
reset_label: "Réinitialiser le délai"
|
||||
reset_hint: "Exemple: si le dossier est re-déposé le 15 avril et que le délai est de 2 mois, la décision sera automatiquement prise le 15 juin, sauf à ce que l’instructeur se prononce d’ici là ou demande à nouveau des corrections."
|
||||
notice_new_files_only: "Information : si vous activez cette règle, seuls les nouveaux dossiers déposés y seront soumis."
|
||||
notice_edit_denied: "Avertissement : le changement ou la désactivation du SVA/SVR est impossible."
|
|
@ -0,0 +1,33 @@
|
|||
= form_for [procedure, configuration], url: admin_procedure_sva_svr_path(procedure), method: :put do |f|
|
||||
- if procedure.publiee? && !procedure.sva_svr_enabled?
|
||||
.fr-alert.fr-alert--info.fr-alert--sm.fr-mb-4w
|
||||
%p= t('.notice_new_files_only')
|
||||
|
||||
- if procedure.publiee? && procedure.sva_svr_enabled?
|
||||
.fr-alert.fr-alert--warning.fr-alert--sm.fr-mb-4w
|
||||
%p= t('.notice_edit_denied')
|
||||
|
||||
%fieldset.fr-fieldset
|
||||
%legend.fr-fieldset__legend= t(".rule")
|
||||
= render Dsfr::RadioButtonListComponent.new(form: f, target: :decision, buttons: decision_buttons, error: configuration.errors[:decision].first)
|
||||
|
||||
%fieldset.fr-fieldset
|
||||
%legend.fr-fieldset__legend= t(".delay")
|
||||
.fr-fieldset__element.fr-fieldset__element--inline
|
||||
.fr-input-group
|
||||
= f.number_field :period, class: 'fr-input', disabled: form_disabled?
|
||||
.fr-fieldset__element.fr-fieldset__element--inline
|
||||
.fr-select-group
|
||||
= f.select :unit, options_for_select(SVASVRConfiguration.unit_options.map { [t(_1, scope: ".unit_labels"), _1] }, selected: configuration.unit), {}, class: 'fr-select', disabled: form_disabled?
|
||||
|
||||
%fieldset.fr-fieldset
|
||||
%legend.fr-fieldset__legend
|
||||
= t(".resume_method")
|
||||
|
||||
%span.fr-hint-text
|
||||
= t(".resume_intro")
|
||||
|
||||
= render Dsfr::RadioButtonListComponent.new(form: f, target: :resume, buttons: resume_buttons)
|
||||
|
||||
= f.submit t(".submit"), class: "fr-btn", disabled: form_disabled?
|
||||
= link_to t(".cancel"), admin_procedure_path(procedure.id), class: "fr-btn fr-btn--secondary fr-ml-2w"
|
34
app/controllers/administrateurs/sva_svr_controller.rb
Normal file
34
app/controllers/administrateurs/sva_svr_controller.rb
Normal file
|
@ -0,0 +1,34 @@
|
|||
module Administrateurs
|
||||
class SVASVRController < AdministrateurController
|
||||
before_action :retrieve_procedure
|
||||
|
||||
def show
|
||||
redirect_to edit_admin_procedure_sva_svr_path(@procedure.id)
|
||||
end
|
||||
|
||||
def edit
|
||||
@configuration = @procedure.sva_svr_configuration
|
||||
end
|
||||
|
||||
def update
|
||||
@configuration = @procedure.sva_svr_configuration
|
||||
@configuration.assign_attributes(configuration_params)
|
||||
|
||||
if @configuration.valid?
|
||||
@procedure.update!(sva_svr: @configuration.attributes)
|
||||
|
||||
flash.notice = "La configuration SVA/SVR a été mise à jour et prend immédiatement effet pour les nouveaux dossiers."
|
||||
redirect_to admin_procedure_path(@procedure)
|
||||
else
|
||||
flash.now.alert = "Des erreurs empêchent la validation du SVA/SVR. Corrigez les erreurs"
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def configuration_params
|
||||
params.require(:sva_svr_configuration).permit(:decision, :period, :unit, :resume)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -234,7 +234,7 @@ module Instructeurs
|
|||
commentaire = CommentaireService.build(current_instructeur, dossier, { body: message, piece_jointe: })
|
||||
|
||||
if commentaire.valid?
|
||||
dossier.flag_as_pending_correction!(commentaire)
|
||||
dossier.flag_as_pending_correction!(commentaire, params[:kind].presence)
|
||||
dossier.update!(last_commentaire_updated_at: Time.zone.now)
|
||||
current_instructeur.follow(dossier)
|
||||
|
||||
|
|
|
@ -185,6 +185,7 @@ module Users
|
|||
if errors.blank?
|
||||
@dossier.passer_en_construction!
|
||||
@dossier.process_declarative!
|
||||
@dossier.process_sva_svr!
|
||||
NotificationMailer.send_en_construction_notification(@dossier).deliver_later
|
||||
@dossier.groupe_instructeur.instructeurs.with_instant_email_dossier_notifications.each do |instructeur|
|
||||
DossierMailer.notify_new_dossier_depose_to_instructeur(@dossier, instructeur.email).deliver_later
|
||||
|
@ -233,6 +234,7 @@ module Users
|
|||
|
||||
if cast_bool(params.dig(:dossier, :pending_correction_confirm))
|
||||
editing_fork_origin.resolve_pending_correction!
|
||||
editing_fork_origin.process_sva_svr!
|
||||
end
|
||||
|
||||
redirect_to dossier_path(editing_fork_origin)
|
||||
|
|
|
@ -8,7 +8,7 @@ module ProcedureHelper
|
|||
def procedure_badge(procedure)
|
||||
return nil unless procedure.brouillon?
|
||||
|
||||
tag.span(t('helpers.procedure.testing_procedure'), class: 'fr-badge')
|
||||
tag.span(t('helpers.procedure.testing_procedure'), class: 'fr-badge fr-badge--sm')
|
||||
end
|
||||
|
||||
def procedure_publish_label(procedure, key)
|
||||
|
|
11
app/jobs/cron/procedure_process_sva_svr_job.rb
Normal file
11
app/jobs/cron/procedure_process_sva_svr_job.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
class Cron::ProcedureProcessSVASVRJob < Cron::CronJob
|
||||
self.schedule_expression = "every day at 1:00"
|
||||
|
||||
def perform
|
||||
Procedure.sva_svr.find_each do |procedure|
|
||||
procedure.dossiers.state_en_construction_ou_instruction.find_each do |dossier|
|
||||
ProcedureSVASVRProcessDossierJob.perform_later(dossier)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
7
app/jobs/procedure_sva_svr_process_dossier_job.rb
Normal file
7
app/jobs/procedure_sva_svr_process_dossier_job.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
class ProcedureSVASVRProcessDossierJob < ApplicationJob
|
||||
queue_as :sva
|
||||
|
||||
def perform(dossier)
|
||||
dossier.process_sva_svr!
|
||||
end
|
||||
end
|
|
@ -53,6 +53,8 @@ class DossierMailer < ApplicationMailer
|
|||
@dossier = dossier
|
||||
@service = dossier.procedure.service
|
||||
@logo_url = attach_logo(dossier.procedure)
|
||||
@correction = commentaire.dossier_correction
|
||||
|
||||
@subject = default_i18n_subject(dossier_id: dossier.id, libelle_demarche: dossier.procedure.libelle)
|
||||
|
||||
mail(to: dossier.user_email_for(:notification), subject: @subject) do |format|
|
||||
|
|
|
@ -8,20 +8,24 @@ module DossierCorrectableConcern
|
|||
|
||||
scope :with_pending_corrections, -> { joins(:corrections).where(corrections: { resolved_at: nil }) }
|
||||
|
||||
def flag_as_pending_correction!(commentaire)
|
||||
def flag_as_pending_correction!(commentaire, kind = nil)
|
||||
return unless may_flag_as_pending_correction?
|
||||
|
||||
corrections.create!(commentaire:)
|
||||
kind ||= :correction
|
||||
|
||||
corrections.create!(commentaire:, kind:)
|
||||
|
||||
log_pending_correction_operation(commentaire, kind) if procedure.sva_svr_enabled?
|
||||
|
||||
return if en_construction?
|
||||
|
||||
repasser_en_construction!(instructeur: commentaire.instructeur)
|
||||
repasser_en_construction_with_pending_correction!(instructeur: commentaire.instructeur)
|
||||
end
|
||||
|
||||
def may_flag_as_pending_correction?
|
||||
return false if pending_corrections.exists?
|
||||
|
||||
en_construction? || may_repasser_en_construction?
|
||||
en_construction? || may_repasser_en_construction_with_pending_correction?
|
||||
end
|
||||
|
||||
def pending_correction?
|
||||
|
@ -39,6 +43,20 @@ module DossierCorrectableConcern
|
|||
|
||||
def resolve_pending_correction!
|
||||
pending_corrections.update!(resolved_at: Time.current)
|
||||
pending_corrections.reset
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def log_pending_correction_operation(commentaire, kind)
|
||||
operation = case kind.to_sym
|
||||
when :correction
|
||||
"demander_une_correction"
|
||||
when :incomplete
|
||||
"demander_a_completer"
|
||||
end
|
||||
|
||||
log_dossier_operation(commentaire.instructeur, operation, commentaire)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,8 +12,10 @@ module DossierFilteringConcern
|
|||
scope :filter_by_datetimes, lambda { |column, dates|
|
||||
if dates.present?
|
||||
case column
|
||||
when 'sva_svr_decision_before'
|
||||
state_not_termine.where("dossiers.sva_svr_decision_on": ..dates.sort.first)
|
||||
when *DATE_SINCE_MAPPING.keys
|
||||
where("dossiers.#{DATE_SINCE_MAPPING.fetch(column)} >= ?", dates.sort.first)
|
||||
where("dossiers.#{DATE_SINCE_MAPPING.fetch(column)}": dates.sort.first..)
|
||||
else
|
||||
dates
|
||||
.map { |date| self.where(column => date..(date + 1.day)) }
|
||||
|
|
54
app/models/concerns/procedure_sva_svr_concern.rb
Normal file
54
app/models/concerns/procedure_sva_svr_concern.rb
Normal file
|
@ -0,0 +1,54 @@
|
|||
module ProcedureSVASVRConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
scope :sva_svr, -> { where("sva_svr ->> 'decision' IN (?)", ['sva', 'svr']) }
|
||||
validate :sva_svr_immutable_on_published, if: :will_save_change_to_sva_svr?
|
||||
validate :validates_sva_svr_compatible
|
||||
|
||||
def sva_svr_enabled?
|
||||
sva? || svr?
|
||||
end
|
||||
|
||||
def sva?
|
||||
decision == :sva
|
||||
end
|
||||
|
||||
def svr?
|
||||
decision == :svr
|
||||
end
|
||||
|
||||
def sva_svr_configuration
|
||||
@sva_svr_configuration ||= SVASVRConfiguration.new(sva_svr)
|
||||
end
|
||||
|
||||
def sva_svr_decision
|
||||
decision
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def decision
|
||||
sva_svr.fetch("decision", nil)&.to_sym
|
||||
end
|
||||
|
||||
def decision_was
|
||||
sva_svr_was.fetch("decision", nil)&.to_sym
|
||||
end
|
||||
|
||||
def sva_svr_immutable_on_published
|
||||
return if brouillon?
|
||||
return if [:sva, :svr].exclude?(decision_was)
|
||||
|
||||
errors.add(:sva_svr, :immutable)
|
||||
end
|
||||
|
||||
def validates_sva_svr_compatible
|
||||
return if !sva_svr_enabled?
|
||||
|
||||
if declarative_with_state.present?
|
||||
errors.add(:sva_svr, :declarative_incompatible)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -36,6 +36,8 @@
|
|||
# processed_at :datetime
|
||||
# search_terms :string
|
||||
# state :string
|
||||
# sva_svr_decision_on :date
|
||||
# sva_svr_decision_triggered_at :datetime
|
||||
# termine_close_to_expiration_notice_sent_at :datetime
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
|
@ -199,6 +201,10 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
event :repasser_en_construction, after: :after_repasser_en_construction do
|
||||
transitions from: :en_instruction, to: :en_construction, guard: :can_repasser_en_construction?
|
||||
end
|
||||
|
||||
event :repasser_en_construction_with_pending_correction, after: :after_repasser_en_construction do
|
||||
transitions from: :en_instruction, to: :en_construction
|
||||
end
|
||||
|
||||
|
@ -208,6 +214,7 @@ class Dossier < ApplicationRecord
|
|||
|
||||
event :accepter_automatiquement, after: :after_accepter_automatiquement do
|
||||
transitions from: :en_construction, to: :accepte, guard: :can_accepter_automatiquement?
|
||||
transitions from: :en_instruction, to: :accepte, guard: :can_accepter_automatiquement?
|
||||
end
|
||||
|
||||
event :refuser, after: :after_refuser do
|
||||
|
@ -563,11 +570,23 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
def can_accepter_automatiquement?
|
||||
declarative_triggered_at.nil? && procedure.declarative_accepte? && can_terminer?
|
||||
return false unless can_terminer?
|
||||
return true if declarative_triggered_at.nil? && procedure.declarative_accepte? && en_construction?
|
||||
return true if procedure.sva? && sva_svr_decision_triggered_at.nil? && !pending_correction? && (sva_svr_decision_on.today? || sva_svr_decision_on.past?)
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def can_passer_automatiquement_en_instruction?
|
||||
(declarative_triggered_at.nil? && procedure.declarative_en_instruction?) || procedure.auto_archive_on&.then { _1 <= Time.zone.today }
|
||||
return true if declarative_triggered_at.nil? && procedure.declarative_en_instruction?
|
||||
return true if procedure.auto_archive_on? && !procedure.auto_archive_on.future?
|
||||
return true if procedure.sva_svr_enabled? && sva_svr_decision_triggered_at.nil? && !pending_correction?
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def can_repasser_en_construction?
|
||||
!procedure.sva_svr_enabled?
|
||||
end
|
||||
|
||||
def can_repasser_en_instruction?
|
||||
|
@ -900,13 +919,22 @@ class Dossier < ApplicationRecord
|
|||
def after_passer_automatiquement_en_instruction
|
||||
self.en_construction_close_to_expiration_notice_sent_at = nil
|
||||
self.conservation_extension = 0.days
|
||||
self.en_instruction_at = self.declarative_triggered_at = self.traitements
|
||||
.passer_en_instruction
|
||||
.processed_at
|
||||
self.en_instruction_at = traitements.passer_en_instruction.processed_at
|
||||
|
||||
if procedure.declarative_en_instruction?
|
||||
self.declarative_triggered_at = en_instruction_at
|
||||
end
|
||||
|
||||
save!
|
||||
|
||||
NotificationMailer.send_en_instruction_notification(self).deliver_later
|
||||
log_automatic_dossier_operation(:passer_en_instruction)
|
||||
|
||||
if procedure.sva_svr_enabled?
|
||||
# TODO: handle serialization errors when SIRET demandeur was not completed
|
||||
log_automatic_dossier_operation(:passer_en_instruction, self)
|
||||
else
|
||||
log_automatic_dossier_operation(:passer_en_instruction)
|
||||
end
|
||||
end
|
||||
|
||||
def after_repasser_en_construction(h)
|
||||
|
@ -975,9 +1003,15 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
def after_accepter_automatiquement
|
||||
self.processed_at = self.en_instruction_at = self.declarative_triggered_at = self.traitements
|
||||
.accepter_automatiquement
|
||||
.processed_at
|
||||
self.processed_at = traitements.accepter_automatiquement.processed_at
|
||||
|
||||
if procedure.declarative_accepte?
|
||||
self.en_instruction_at = self.processed_at
|
||||
self.declarative_triggered_at = self.processed_at
|
||||
elsif procedure.sva_svr_enabled?
|
||||
self.sva_svr_decision_triggered_at = self.processed_at
|
||||
end
|
||||
|
||||
save!
|
||||
|
||||
if attestation.nil?
|
||||
|
@ -1046,6 +1080,26 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def process_sva_svr!
|
||||
return unless procedure.sva_svr_enabled?
|
||||
return if sva_svr_decision_triggered_at.present?
|
||||
|
||||
# set or recompute sva date, except for dossiers submitted before sva was enabled
|
||||
if depose_at.today? || sva_svr_decision_on.present?
|
||||
self.sva_svr_decision_on = SVASVRDecisionDateCalculatorService.new(self, procedure).decision_date
|
||||
end
|
||||
|
||||
return if sva_svr_decision_on.nil?
|
||||
|
||||
if en_construction? && may_passer_automatiquement_en_instruction?
|
||||
passer_automatiquement_en_instruction!
|
||||
elsif en_instruction? && procedure.sva? && may_accepter_automatiquement?
|
||||
accepter_automatiquement!
|
||||
elsif will_save_change_to_sva_svr_decision_on?
|
||||
save! # we always want the most up to date decision when there is a pending correction
|
||||
end
|
||||
end
|
||||
|
||||
def remove_titres_identite!
|
||||
champs_public.filter(&:titre_identite?).map(&:piece_justificative_file).each(&:purge_later)
|
||||
end
|
||||
|
@ -1134,10 +1188,11 @@ class Dossier < ApplicationRecord
|
|||
['Dernière mise à jour le', :updated_at],
|
||||
['Déposé le', :depose_at],
|
||||
['Passé en instruction le', :en_instruction_at],
|
||||
procedure.sva_svr_enabled? ? ["Date #{procedure.sva_svr_configuration.human_decision}", :sva_svr_decision_on] : nil,
|
||||
['Traité le', :processed_at],
|
||||
['Motivation de la décision', :motivation],
|
||||
['Instructeurs', followers_instructeurs.map(&:email).join(' ')]
|
||||
]
|
||||
].compact
|
||||
|
||||
if procedure.routing_enabled?
|
||||
columns << ['Groupe instructeur', groupe_instructeur.label]
|
||||
|
@ -1233,6 +1288,10 @@ class Dossier < ApplicationRecord
|
|||
false
|
||||
end
|
||||
|
||||
def sva_svr_decision_in_days
|
||||
(sva_svr_decision_on - Date.current).to_i
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create_missing_traitemets
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# Table name: dossier_corrections
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# kind :string default("correction"), not null
|
||||
# resolved_at :datetime
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
|
@ -17,6 +18,8 @@ class DossierCorrection < ApplicationRecord
|
|||
|
||||
scope :pending, -> { where(resolved_at: nil) }
|
||||
|
||||
enum kind: { correction: 'correction', incomplete: 'incomplete' }
|
||||
|
||||
def resolved?
|
||||
resolved_at.present?
|
||||
end
|
||||
|
|
|
@ -19,6 +19,8 @@ class DossierOperationLog < ApplicationRecord
|
|||
changer_groupe_instructeur: 'changer_groupe_instructeur',
|
||||
passer_en_instruction: 'passer_en_instruction',
|
||||
repasser_en_construction: 'repasser_en_construction',
|
||||
demander_une_correction: 'demander_une_correction',
|
||||
demander_a_completer: 'demander_a_completer',
|
||||
repasser_en_instruction: 'repasser_en_instruction',
|
||||
accepter: 'accepter',
|
||||
refuser: 'refuser',
|
||||
|
@ -134,6 +136,8 @@ class DossierOperationLog < ApplicationRecord
|
|||
SerializerService.champ(subject)
|
||||
when Avis
|
||||
SerializerService.avis(subject)
|
||||
when Commentaire
|
||||
SerializerService.message(subject)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
# published_at :datetime
|
||||
# routing_criteria_name :text default("Votre ville")
|
||||
# routing_enabled :boolean
|
||||
# sva_svr :jsonb not null
|
||||
# tags :text default([]), is an Array
|
||||
# unpublished_at :datetime
|
||||
# web_hook_url :string
|
||||
|
@ -68,6 +69,7 @@ class Procedure < ApplicationRecord
|
|||
include EncryptableConcern
|
||||
include InitiationProcedureConcern
|
||||
include ProcedureGroupeInstructeurAPIHackConcern
|
||||
include ProcedureSVASVRConcern
|
||||
|
||||
include Discard::Model
|
||||
self.discard_column = :hidden_at
|
||||
|
|
|
@ -38,6 +38,8 @@ class ProcedurePresentation < ApplicationRecord
|
|||
validate :check_filters_max_length
|
||||
|
||||
def self_fields
|
||||
sva_svr_enabled = procedure.sva_svr_enabled?
|
||||
|
||||
[
|
||||
field_hash('self', 'created_at', type: :date),
|
||||
field_hash('self', 'updated_at', type: :date),
|
||||
|
@ -45,13 +47,15 @@ class ProcedurePresentation < ApplicationRecord
|
|||
field_hash('self', 'en_construction_at', type: :date),
|
||||
field_hash('self', 'en_instruction_at', type: :date),
|
||||
field_hash('self', 'processed_at', type: :date),
|
||||
sva_svr_enabled && field_hash('self', 'sva_svr_decision_on', type: :date),
|
||||
sva_svr_enabled && field_hash('self', 'sva_svr_decision_before', type: :date, virtual: true),
|
||||
field_hash('self', 'updated_since', type: :date, virtual: true),
|
||||
field_hash('self', 'depose_since', type: :date, virtual: true),
|
||||
field_hash('self', 'en_construction_since', type: :date, virtual: true),
|
||||
field_hash('self', 'en_instruction_since', type: :date, virtual: true),
|
||||
field_hash('self', 'processed_since', type: :date, virtual: true),
|
||||
field_hash('self', 'state', type: :enum, scope: 'instructeurs.dossiers.filterable_state', virtual: true)
|
||||
]
|
||||
].compact_blank
|
||||
end
|
||||
|
||||
def fields
|
||||
|
@ -118,11 +122,15 @@ class ProcedurePresentation < ApplicationRecord
|
|||
end
|
||||
|
||||
def displayed_fields_for_headers
|
||||
[
|
||||
array = [
|
||||
field_hash('self', 'id', classname: 'number-col'),
|
||||
*displayed_fields,
|
||||
field_hash('self', 'state', classname: 'state-col')
|
||||
]
|
||||
|
||||
array << field_hash('self', 'sva_svr_decision_on', classname: 'sva-col') if procedure.sva_svr_enabled?
|
||||
|
||||
array
|
||||
end
|
||||
|
||||
def sorted_ids(dossiers, count)
|
||||
|
|
34
app/models/sva_svr_configuration.rb
Normal file
34
app/models/sva_svr_configuration.rb
Normal file
|
@ -0,0 +1,34 @@
|
|||
class SVASVRConfiguration
|
||||
include ActiveModel::Model
|
||||
include ActiveModel::Attributes
|
||||
|
||||
attribute :decision, default: 'disabled'
|
||||
attribute :period, default: 2
|
||||
attribute :unit, default: 'months'
|
||||
attribute :resume, default: 'continue'
|
||||
|
||||
DECISION_OPTIONS = ['disabled', 'sva', 'svr']
|
||||
UNIT_OPTIONS = ['days', 'weeks', 'months']
|
||||
RESUME_OPTIONS = ['continue', 'reset']
|
||||
|
||||
validates :decision, inclusion: { in: DECISION_OPTIONS.without('svr') }
|
||||
validates :period, presence: true, numericality: { only_integer: true }, if: -> { enabled? }
|
||||
validates :unit, presence: true, inclusion: { in: UNIT_OPTIONS }, if: -> { enabled? }
|
||||
validates :resume, presence: true, inclusion: { in: RESUME_OPTIONS }, if: -> { enabled? }
|
||||
|
||||
def self.unit_options
|
||||
UNIT_OPTIONS
|
||||
end
|
||||
|
||||
def human_decision
|
||||
return if decision == 'disabled'
|
||||
|
||||
decision.upcase
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def enabled?
|
||||
decision != 'disabled'
|
||||
end
|
||||
end
|
|
@ -1,5 +1,5 @@
|
|||
class DossierProjectionService
|
||||
class DossierProjection < Struct.new(:dossier_id, :state, :archived, :hidden_by_user_at, :hidden_by_administration_at, :batch_operation_id, :corrections, :columns) do
|
||||
class DossierProjection < Struct.new(:dossier_id, :state, :archived, :hidden_by_user_at, :hidden_by_administration_at, :batch_operation_id, :sva_svr_decision_on, :corrections, :columns) do
|
||||
def pending_correction?
|
||||
return false if corrections.blank?
|
||||
|
||||
|
@ -29,8 +29,9 @@ class DossierProjectionService
|
|||
batch_operation_field = { TABLE => 'self', COLUMN => 'batch_operation_id' }
|
||||
hidden_by_user_at_field = { TABLE => 'self', COLUMN => 'hidden_by_user_at' }
|
||||
hidden_by_administration_at_field = { TABLE => 'self', COLUMN => 'hidden_by_administration_at' }
|
||||
sva_svr_decision_on_field = { TABLE => 'self', COLUMN => 'sva_svr_decision_on' }
|
||||
dossier_corrections = { TABLE => 'dossier_corrections', COLUMN => 'resolved_at' }
|
||||
([state_field, archived_field, hidden_by_user_at_field, hidden_by_administration_at_field, batch_operation_field, dossier_corrections] + fields) # the view needs state and archived dossier attributes
|
||||
([state_field, archived_field, sva_svr_decision_on_field, hidden_by_user_at_field, hidden_by_administration_at_field, batch_operation_field, dossier_corrections] + 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|
|
||||
|
@ -54,7 +55,7 @@ class DossierProjectionService
|
|||
.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, hidden_by_user_at_field, hidden_by_administration_at_field, batch_operation_field].include?(field)
|
||||
if [state_field, archived_field, hidden_by_user_at_field, hidden_by_administration_at_field, batch_operation_field, sva_svr_decision_on_field].include?(field)
|
||||
field[:id_value_h][id] = value
|
||||
else
|
||||
field[:id_value_h][id] = value&.strftime('%d/%m/%Y') # other fields are datetime
|
||||
|
@ -130,6 +131,7 @@ class DossierProjectionService
|
|||
hidden_by_user_at_field[:id_value_h][dossier_id],
|
||||
hidden_by_administration_at_field[:id_value_h][dossier_id],
|
||||
batch_operation_field[:id_value_h][dossier_id],
|
||||
sva_svr_decision_on_field[:id_value_h][dossier_id],
|
||||
dossier_corrections[:id_value_h][dossier_id],
|
||||
fields.map { |f| f[:id_value_h][dossier_id] }
|
||||
)
|
||||
|
|
|
@ -41,6 +41,15 @@ class SerializerService
|
|||
end
|
||||
end
|
||||
|
||||
def self.message(commentaire)
|
||||
Sentry.with_scope do |scope|
|
||||
scope.set_tags(dossier_id: commentaire.dossier_id)
|
||||
|
||||
data = execute_query('serializeMessage', { number: commentaire.dossier_id, id: commentaire.to_typed_id })
|
||||
data && data['dossier']["messages"].first
|
||||
end
|
||||
end
|
||||
|
||||
def self.execute_query(operation_name, variables)
|
||||
result = API::V2::Schema.execute(QUERY,
|
||||
variables: variables,
|
||||
|
@ -113,6 +122,14 @@ class SerializerService
|
|||
}
|
||||
}
|
||||
|
||||
query serializeMessage($number: Int!, $id: ID!) {
|
||||
dossier(number: $number) {
|
||||
messages(id: $id) {
|
||||
...MessageFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment DossierFragment on Dossier {
|
||||
id
|
||||
number
|
||||
|
@ -359,5 +376,15 @@ class SerializerService
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment MessageFragment on Message {
|
||||
id
|
||||
email
|
||||
body
|
||||
createdAt
|
||||
attachments {
|
||||
...FileFragment
|
||||
}
|
||||
}
|
||||
GRAPHQL
|
||||
end
|
||||
|
|
77
app/services/sva_svr_decision_date_calculator_service.rb
Normal file
77
app/services/sva_svr_decision_date_calculator_service.rb
Normal file
|
@ -0,0 +1,77 @@
|
|||
class SVASVRDecisionDateCalculatorService
|
||||
attr_reader :dossier, :procedure, :unit, :period, :resume_method
|
||||
|
||||
EMPTY_DOSSIER = Struct.new(:depose_at) do
|
||||
def corrections
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def self.decision_date_from_today(procedure)
|
||||
dossier = EMPTY_DOSSIER.new(Date.current)
|
||||
new(dossier, procedure).decision_date
|
||||
end
|
||||
|
||||
def initialize(dossier, procedure)
|
||||
@dossier = dossier
|
||||
@procedure = procedure
|
||||
|
||||
config = procedure.sva_svr_configuration
|
||||
@unit = config.unit.to_sym
|
||||
@period = config.period.to_i
|
||||
@resume_method = config.resume.to_sym
|
||||
end
|
||||
|
||||
def decision_date
|
||||
duration = calculate_duration
|
||||
|
||||
start_date = determine_start_date + 1.day
|
||||
correction_delay = calculate_correction_delay(start_date)
|
||||
|
||||
start_date + correction_delay + duration
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def calculate_duration
|
||||
case unit
|
||||
when :days
|
||||
period.days
|
||||
when :weeks
|
||||
period.weeks
|
||||
when :months
|
||||
period.months
|
||||
end
|
||||
end
|
||||
|
||||
def determine_start_date
|
||||
return dossier.depose_at.to_date if dossier.corrections.empty?
|
||||
return latest_correction_date if resume_method == :reset
|
||||
return latest_incomplete_correction_date if dossier.corrections.any?(&:incomplete?)
|
||||
|
||||
dossier.depose_at.to_date
|
||||
end
|
||||
|
||||
def latest_incomplete_correction_date
|
||||
correction_date dossier.corrections.filter(&:incomplete?).max_by(&:resolved_at)
|
||||
end
|
||||
|
||||
def latest_correction_date
|
||||
correction_date dossier.corrections.max_by(&:resolved_at)
|
||||
end
|
||||
|
||||
def calculate_correction_delay(start_date)
|
||||
dossier.corrections.sum do |correction|
|
||||
resolved_date = correction_date(correction)
|
||||
next 0 unless resolved_date > start_date
|
||||
|
||||
(resolved_date + 1.day - correction.created_at.to_date).days # restart from next day after resolution
|
||||
end
|
||||
end
|
||||
|
||||
def correction_date(correction)
|
||||
# NOTE: when correction is not resolved, assume it could be done today
|
||||
# so interfaces could show how many days are remaining after correction
|
||||
correction.resolved_at&.to_date || Date.current
|
||||
end
|
||||
end
|
|
@ -63,5 +63,6 @@
|
|||
= render Procedure::Card::AnnotationsComponent.new(procedure: @procedure)
|
||||
= render Procedure::Card::APIEntrepriseComponent.new(procedure: @procedure)
|
||||
= render Procedure::Card::APIParticulierComponent.new(procedure: @procedure)
|
||||
= render Procedure::Card::SVASVRComponent.new(procedure: @procedure) if @procedure.sva_svr_enabled? || @procedure.feature_enabled?(:sva)
|
||||
= render Procedure::Card::MonAvisComponent.new(procedure: @procedure)
|
||||
= render Procedure::Card::DossierSubmittedMessageComponent.new(procedure: @procedure)
|
||||
|
|
43
app/views/administrateurs/sva_svr/edit.html.haml
Normal file
43
app/views/administrateurs/sva_svr/edit.html.haml
Normal file
|
@ -0,0 +1,43 @@
|
|||
= render partial: 'administrateurs/breadcrumbs',
|
||||
locals: { steps: [['Démarches', admin_procedures_path],
|
||||
["#{@procedure.libelle.truncate_words(10)}", admin_procedure_path(@procedure)],
|
||||
["Configuration SVA/SVR"]] }
|
||||
|
||||
.fr-container.fr-my-5w
|
||||
%h1.fr-h1 Règle du Silence Vaut Accord ou Silence Vaut Rejet
|
||||
|
||||
= render Dsfr::CalloutComponent.new(title: "Fonctionnement du SVA/SVR") do |c|
|
||||
- c.with_body do
|
||||
%p.fr-callout__text
|
||||
Le SVA “Silence Vaut Accord” ou SVR “Silence Vaut Rejet” est un principe législatif qui définit le comportement d’une demande en cas de silence de l'administration : soit la demande est automatiquement acceptée (SVA), soit elle est automatiquement rejetée (SVR).
|
||||
%strong= APPLICATION_NAME
|
||||
permet l’application de ce principe pour les démarches concernées.
|
||||
|
||||
%p.fr-callout__text.fr-mt-2w
|
||||
Concrètement, le silence est l’absence d’instruction d’un dossier par un instructeur à l’issue du délai imparti. À son dépôt, un dossier passe immédiatement “en instruction” (l’usager ne peut plus le modifier) et le calcul du délai démarrera le lendemain. À l’issue du délai, si le dossier n’a pas été instruit par un instructeur, il sera automatiquement accepté ou refusé, comme si un instructeur l’avait fait manuellement. Toutes les autres fonctionnalités restent inchangées.
|
||||
|
||||
%p.fr-callout__text.fr-mt-2w
|
||||
L’écoulement du délai est suspendu quand un instructeur
|
||||
%strong demande des corrections
|
||||
ou
|
||||
déclare le
|
||||
%strong dossier incomplet
|
||||
(actions
|
||||
%em Demander une correction
|
||||
et
|
||||
%em Demander à compléter
|
||||
). Le dossier repasse alors en construction.
|
||||
Une fois les corrections effectuées, l’usager re-dépose son dossier. En fonction de la démarche et de la complétude du dossier, l’écoulement du délai reprend, ou est réinitialisé à 0.
|
||||
|
||||
%p.fr-callout__text.fr-mt-2w
|
||||
Cet écran permet le réglage des paramètres nécessaires au fonctionnement du SVA ou SVR.
|
||||
%br
|
||||
%strong Il sera ensuite impossible de modifier ou désactiver cette fonctionnalité sur une démarche publiée.
|
||||
|
||||
- c.with_bottom do
|
||||
%p.fr-mt-2w
|
||||
= link_to("Texte sur LegiFrance", "https://www.legifrance.gouv.fr/contenu/menu/autour-de-la-loi/sva-silence-vaut-accord", class: "fr-link fr-mr-1w", title: new_tab_suffix("En savoir plus sur le LegiFrance"), **external_link_attributes)
|
||||
= link_to("Liste des démarches encadrées par ce principe", "https://www.service-public.fr/demarches-silence-vaut-accord", class: "fr-link", title: new_tab_suffix("Rechercher les démarches avec SVA sur service-public.fr"), **external_link_attributes)
|
||||
|
||||
|
||||
= render Procedure::SVASVRFormComponent.new(procedure: @procedure, configuration: @configuration)
|
|
@ -3,10 +3,13 @@
|
|||
|
||||
%p= t(:hello, scope: [:views, :shared, :greetings])
|
||||
|
||||
%p= t('.explanation_html', dossier_id: @dossier.id, libelle_demarche: @dossier.procedure.libelle)
|
||||
%p= t(".#{@correction.kind}.explanation_html", dossier_id: @dossier.id, libelle_demarche: @dossier.procedure.libelle)
|
||||
%p= t('.link')
|
||||
= round_button(t('.access_message'), messagerie_dossier_url(@dossier), :primary)
|
||||
|
||||
- if @dossier.sva_svr_decision_on.present?
|
||||
%p= t(".#{@correction.kind}.sva_svr", rule_name: t(@dossier.procedure.sva? ? :sva : :svr, scope: 'shared.procedures.sva_svr_rule_name'))
|
||||
|
||||
= render 'layouts/mailers/signature', service: @service
|
||||
|
||||
- content_for :footer do
|
||||
|
|
|
@ -241,6 +241,18 @@ def add_etats_dossier(pdf, dossier)
|
|||
format_in_2_columns(pdf, "En instruction le", try_format_date(dossier.en_instruction_at))
|
||||
end
|
||||
|
||||
if dossier.sva_svr_decision_triggered_at.present?
|
||||
format_in_2_columns(pdf, "Décision #{dossier.procedure.sva_svr_configuration.human_decision} prise le", try_format_date(dossier.sva_svr_decision_triggered_at))
|
||||
elsif dossier.sva_svr_decision_on.present?
|
||||
value = if dossier.pending_correction?
|
||||
"#{dossier.sva_svr_decision_in_days} jours après la correction"
|
||||
else
|
||||
try_format_date(dossier.sva_svr_decision_on)
|
||||
end
|
||||
|
||||
format_in_2_columns(pdf, "Date prévisionnelle #{dossier.procedure.sva_svr_configuration.human_decision}", value)
|
||||
end
|
||||
|
||||
if dossier.processed_at.present?
|
||||
format_in_2_columns(pdf, "Décision le", try_format_date(dossier.processed_at))
|
||||
end
|
||||
|
|
|
@ -4,11 +4,15 @@
|
|||
%h1.fr-h3.fr-mb-1w
|
||||
= "Dossier nº #{dossier.id}"
|
||||
|
||||
= status_badge(dossier.state, 'super')
|
||||
= pending_correction_badge(:for_instructeur) if dossier.pending_correction?
|
||||
|
||||
= link_to dossier.procedure.libelle.truncate_words(10), instructeur_procedure_path(dossier.procedure), title: dossier.procedure.libelle, class: "fr-link"
|
||||
= procedure_badge(dossier.procedure)
|
||||
.fr-mt-2w.badge-group
|
||||
= procedure_badge(dossier.procedure)
|
||||
|
||||
= status_badge(dossier.state)
|
||||
= pending_correction_badge(:for_instructeur) if dossier.pending_correction?
|
||||
= render Instructeurs::SVASVRDecisionBadgeComponent.new(projection_or_dossier: dossier, procedure: dossier.procedure, with_label: true)
|
||||
|
||||
|
||||
|
||||
.header-actions.fr-ml-auto
|
||||
= render partial: 'instructeurs/dossiers/header_actions', locals: { dossier: }
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
%li{ 'data-turbo': turbo ? 'true' : 'false' }
|
||||
= button_to passer_en_instruction_instructeur_dossier_path(procedure_id, dossier_id), method: :post, class: 'fr-btn fr-icon-edit-line' do
|
||||
Passer en instruction
|
||||
- elsif Dossier.states[:en_instruction] == state && !with_menu
|
||||
- elsif Dossier.states[:en_instruction] == state && !with_menu && !sva_svr
|
||||
%li{ 'data-turbo': turbo ? 'true' : 'false' }
|
||||
= button_to repasser_en_construction_instructeur_dossier_path(procedure_id, dossier_id), method: :post, class: 'fr-btn fr-btn--secondary fr-icon-draft-line' do
|
||||
Repasser en construction
|
||||
|
|
|
@ -174,6 +174,11 @@
|
|||
- status << pending_correction_badge(:for_instructeur, html_class: "fr-mt-1v") if p.pending_correction?
|
||||
= link_to_if(p.hidden_by_administration_at.blank?, safe_join(status), path, class: class_names("cell-link": true, "fr-py-0": status.many?))
|
||||
|
||||
- if @procedure.sva_svr_enabled?
|
||||
%td
|
||||
%span.cell-link
|
||||
= link_to_if p.hidden_by_administration_at.blank?, render(Instructeurs::SVASVRDecisionBadgeComponent.new(projection_or_dossier: p, procedure: @procedure), path)
|
||||
|
||||
%td.action-col.follow-col
|
||||
%ul.inline.fr-btns-group.fr-btns-group--sm.fr-btns-group--inline.fr-btns-group--icon-right
|
||||
= render partial: 'instructeurs/procedures/dossier_actions', locals: { procedure_id: @procedure.id,
|
||||
|
@ -183,6 +188,7 @@
|
|||
dossier_is_followed: @followed_dossiers_id.include?(p.dossier_id),
|
||||
close_to_expiration: @statut == 'expirant',
|
||||
hidden_by_administration: @statut == 'supprimes_recemment',
|
||||
sva_svr: @procedure.sva_svr_enabled?,
|
||||
turbo: false,
|
||||
with_menu: false }
|
||||
%tfoot
|
||||
|
|
|
@ -101,6 +101,7 @@
|
|||
dossier_is_followed: @followed_dossiers_id.include?(p.dossier_id),
|
||||
close_to_expiration: nil,
|
||||
hidden_by_administration: nil,
|
||||
sva_svr: p.sva_svr_decision_on.present?,
|
||||
turbo: false,
|
||||
with_menu: false }
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
= yield
|
||||
|
||||
- unless @no_description
|
||||
.fr-accordions-group
|
||||
.fr-accordions-group.fr-mb-3w
|
||||
%section.fr-accordion
|
||||
%h2.fr-accordion__title
|
||||
%button.fr-accordion__btn{ "aria-controls" => "accordion-114", "aria-expanded" => "true" }
|
||||
|
@ -77,5 +77,16 @@
|
|||
#accordion-117.fr-collapse
|
||||
= t('shared.procedure_description.estimated_fill_duration_detail', estimated_minutes: estimated_fill_duration_minutes(procedure))
|
||||
|
||||
.fr-my-3w
|
||||
= render Procedure::NoticeComponent.new(procedure:)
|
||||
.fr-my-3w
|
||||
= render Procedure::NoticeComponent.new(procedure:)
|
||||
|
||||
- if procedure.sva_svr_enabled?
|
||||
= render Dsfr::CalloutComponent.new(title: t('shared.procedure_description.sva_svr_title', rule: t(procedure.sva_svr_decision, scope: 'shared.procedures.sva_svr_rule_name')), icon: "fr-fi-information-line", extra_class_names: "fr-my-6w") do |c|
|
||||
- c.with_body do
|
||||
%p
|
||||
= t("#{procedure.sva_svr_decision}_text_html", scope: 'shared.procedure_description') # i18n-tasks-use: t('shared.procedure_description.sva_text_html') t('shared.procedure_description.svr_text_html')
|
||||
%p.fr-mt-1w
|
||||
= t('shared.procedure_description.sva_svr_prevision_date',
|
||||
delay: t("x_#{procedure.sva_svr_configuration.unit}", count: procedure.sva_svr_configuration.period.to_i, scope: 'datetime.distance_in_words'),
|
||||
date: l(SVASVRDecisionDateCalculatorService.decision_date_from_today(procedure), format: :long).gsub(' ', " "))
|
||||
|
||||
|
|
|
@ -43,6 +43,13 @@
|
|||
%p{ role: 'status' }
|
||||
= t('views.users.dossiers.show.status_overview.admin_review')
|
||||
|
||||
- if dossier.sva_svr_decision_on.present?
|
||||
-# i18n-tasks-use t('views.users.dossiers.show.status_overview.delay_title.sva'), t('views.users.dossiers.show.status_overview.delay_title.svr')
|
||||
= render Dsfr::CalloutComponent.new(title: t(dossier.procedure.sva_svr_configuration.decision, scope: "views.users.dossiers.show.status_overview.delay_title")) do |c|
|
||||
- c.with_body do
|
||||
%p
|
||||
= t('views.users.dossiers.show.status_overview.delay_text_sva_svr', date: l(dossier.sva_svr_decision_on, format: :long))
|
||||
|
||||
= render partial: 'users/dossiers/show/estimated_delay', locals: { procedure: dossier.procedure }
|
||||
|
||||
%p
|
||||
|
|
|
@ -19,7 +19,8 @@ features = [
|
|||
:procedure_routage_api,
|
||||
:groupe_instructeur_api_hack,
|
||||
:rerouting,
|
||||
:cojo_type_de_champ
|
||||
:cojo_type_de_champ,
|
||||
:sva
|
||||
]
|
||||
|
||||
def database_exists?
|
||||
|
|
|
@ -15,6 +15,8 @@ ActiveSupport::Inflector.inflections(:en) do |inflect|
|
|||
inflect.acronym 'JSON'
|
||||
inflect.acronym 'RNA'
|
||||
inflect.acronym 'URL'
|
||||
inflect.acronym 'SVA'
|
||||
inflect.acronym 'SVR'
|
||||
inflect.irregular 'type_de_champ', 'types_de_champ'
|
||||
inflect.irregular 'type_de_champ_private', 'types_de_champ_private'
|
||||
inflect.irregular 'procedure_revision_type_de_champ', 'procedure_revision_types_de_champ'
|
||||
|
|
|
@ -432,6 +432,10 @@ en:
|
|||
en_construction_html: Your file is in progress. It means that <strong>you can still edit it</strong>. You will no longer be able to edit the file when the administration will switch it to "review".
|
||||
status_review: undergoing review
|
||||
admin_review: The administration is reviewing your file. You are no longer able to edit it.
|
||||
delay_title:
|
||||
sva: "Your file is subject to the legislative framework « Silence Vaut Accord »"
|
||||
svr: "Your file is subject to the legislative framework « Silence Vaut Rejet »"
|
||||
delay_text_sva_svr: "You will receive a reply from the administration no later than %{date}."
|
||||
status_completed: completed
|
||||
use_mailbox_for_questions_html: "<strong>You have a question?</strong> Use the mailbox to <a href=\"%{mailbox_url}\">contact the administration directly</a>."
|
||||
acceptee_html: "Your file had been <strong>accepted</strong>."
|
||||
|
@ -833,9 +837,19 @@ en:
|
|||
dossiers_count: "Nb files"
|
||||
weekly_distribution: "Weekly distribution"
|
||||
weekly_distribution_details: "in the last 6 months"
|
||||
sva_svr_rule_name:
|
||||
sva: "Silence Vaut Accord"
|
||||
svr: "Silence Vaut Rejet"
|
||||
procedure_description:
|
||||
estimated_fill_duration: "Estimated fill time: %{estimated_minutes} mn"
|
||||
estimated_fill_duration_title: What is the procedure estimated fill time ?
|
||||
estimated_fill_duration_detail: "The fill time is etimated to %{estimated_minutes} min. This period may vary depending on the options you choose"
|
||||
pieces_jointes : What are the required attachments ?
|
||||
pieces_jointes_conditionnal_list_title : Attachments list according to your situation
|
||||
sva_svr_title: "This procedure applies the « %{rule} »"
|
||||
sva_text_html: "SVA, or « <strong>Silence Vaut Rejet</strong> » is a legislative principle that defines the behavior of a request in the event of silence from the administration: the request is automatically accepted after a certain period if your file is complete."
|
||||
svr_text_html: "SVR, or « <strong>Silence Vaut Rejet</strong> » is a legislative principle that defines the behavior of a request in the event of silence from the administration: the request is automatically refused after a certain period if your file is complete."
|
||||
sva_svr_prevision_date:
|
||||
Thus, you will receive an answer to your request within %{delay} of submitting your complete file.
|
||||
For example, if you submit your application today, you will receive a reply no later than %{date}.
|
||||
If your file is incomplete, this date may be postponed until submitting a completed file.
|
||||
|
|
|
@ -434,6 +434,10 @@ fr:
|
|||
en_construction_html: "Votre dossier est en construction. Cela signifie que <strong>vous pouvez encore le modifier</strong>. Vous ne pourrez plus modifier votre dossier lorsque l’administration le passera « en instruction »."
|
||||
status_review: en instruction
|
||||
admin_review: Votre dossier est en cours d’instruction par l’administration. Vous ne pouvez plus le modifier.
|
||||
delay_title:
|
||||
sva: "Votre dossier est soumis au cadre législatif « Silence Vaut Accord »"
|
||||
svr: "Votre dossier est soumis au cadre législatif « Silence Vaut Rejet »"
|
||||
delay_text_sva_svr: "Vous aurez un retour de l'administration au plus tard le %{date}."
|
||||
status_completed: terminé
|
||||
use_mailbox_for_questions_html: "<strong>Vous avez une question ?</strong> Utilisez la messagerie pour <a href=\"%{mailbox_url}\">contacter l’administration directement</a>."
|
||||
acceptee_html: "Votre dossier a été <strong>accepté</strong>."
|
||||
|
@ -887,9 +891,19 @@ fr:
|
|||
dossiers_count: "Nb dossiers"
|
||||
weekly_distribution: "Répartition par semaine"
|
||||
weekly_distribution_details: "au cours des 6 derniers mois"
|
||||
sva_svr_rule_name:
|
||||
sva: "Silence Vaut Accord"
|
||||
svr: "Silence Vaut Rejet"
|
||||
procedure_description:
|
||||
estimated_fill_duration: "Temps de remplissage estimé : %{estimated_minutes} mn"
|
||||
estimated_fill_duration_title: Quelle est la durée de remplissage de la démarche ?
|
||||
estimated_fill_duration_detail: "La durée de remplissage est estimée à %{estimated_minutes} min. Ce délai peut varier selon les options que vous choisirez."
|
||||
pieces_jointes : Quelles sont les pièces justificatives à fournir ?
|
||||
pieces_jointes_conditionnal_list_title : Liste des pièces en fonction de votre situation
|
||||
sva_svr_title: "Cette démarche applique le « %{rule} »"
|
||||
sva_text_html: "Le SVA, ou « <strong>Silence Vaut Accord</strong> », est un principe législatif qui définit le comportement d’une demande en cas d’absence de réponse de l’administration : la demande sera automatiquement acceptée à l’issue d’un délai si votre dossier est complet."
|
||||
svr_text_html: "Le SVR, ou « <strong>Silence Vaut Rejet</strong> », est un principe législatif qui définit le comportement d’une demande en cas d’absence de réponse de l’administration : la demande sera automatiquement refusée à l’issue d’un délai si votre dossier est complet."
|
||||
sva_svr_prevision_date:
|
||||
Ainsi, vous recevrez une réponse à votre demande dans les %{delay} après avoir déposé votre dossier complet.
|
||||
Par exemple, si vous déposez votre dossier aujourd'hui, vous aurez une réponse au plus tard le %{date}.
|
||||
Si votre dossier est incomplet, cette date pourrait être repoussée jusqu’à la soumission d’un dossier complété.
|
||||
|
|
|
@ -57,3 +57,6 @@ en:
|
|||
format: 'Private field %{message}'
|
||||
lien_dpo:
|
||||
invalid_uri_or_email: "Fill in with an email or a link"
|
||||
sva_svr:
|
||||
immutable: "SVA/SVR configuration can no longer be modified"
|
||||
declarative_incompatible: "SVA/SVR is incompatible with a declarative procedure"
|
||||
|
|
|
@ -65,3 +65,6 @@ fr:
|
|||
invalid_uri_or_email: "Veuillez saisir un mail ou un lien"
|
||||
auto_archive_on:
|
||||
future: doit être dans le futur
|
||||
sva_svr:
|
||||
immutable: "La configuration SVA/SVR ne peut plus être modifiée"
|
||||
declarative_incompatible: "Le SVA/SVR est incompatible avec une démarche déclarative"
|
||||
|
|
|
@ -17,6 +17,8 @@ en:
|
|||
en_construction_since: Submitted since
|
||||
en_instruction_since: Instructed since
|
||||
processed_since: Finished since
|
||||
sva_svr_decision_on: SVA decision date
|
||||
sva_svr_decision_before: SVA decision date before
|
||||
user:
|
||||
email: Requester
|
||||
followers_instructeurs:
|
||||
|
|
|
@ -17,6 +17,8 @@ fr:
|
|||
en_construction_since: En construction depuis
|
||||
en_instruction_since: En instruction depuis
|
||||
processed_since: Terminé depuis
|
||||
sva_svr_decision_on: Date décision SVA
|
||||
sva_svr_decision_before: Date décision SVA avant
|
||||
user:
|
||||
email: Demandeur
|
||||
followers_instructeurs:
|
||||
|
|
|
@ -6,6 +6,15 @@
|
|||
:message_date_with_year => lambda { |time, _| "%B #{time.day.ordinalize} %Y at %H:%M" },
|
||||
:message_date_without_time => lambda { |_time, _| "%Y/%m/%d" }
|
||||
}
|
||||
},
|
||||
|
||||
datetime: {
|
||||
distance_in_words: {
|
||||
x_weeks: {
|
||||
one: "1 week",
|
||||
other: "%{count} weeks"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,15 @@
|
|||
:message_date_with_year => lambda { |time, _| "le #{time.day == 1 ? '1er' : time.day} %B %Y à %H h %M" },
|
||||
:message_date_without_time => lambda { |_time, _| "%d/%m/%Y" }
|
||||
}
|
||||
},
|
||||
|
||||
datetime: {
|
||||
distance_in_words: {
|
||||
x_weeks: {
|
||||
one: "1 semaine",
|
||||
other: "%{count} semaines"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,16 @@ en:
|
|||
dossier_mailer:
|
||||
notify_pending_correction:
|
||||
subject: You need to modify your file no. %{dossier_id} « %{libelle_demarche} »
|
||||
explanation_html:
|
||||
In order to continue its instruction, <strong>an instructor requested you to edit information</strong> to your file no. %{dossier_id} of the « %{libelle_demarche} » procedure.
|
||||
correction:
|
||||
explanation_html:
|
||||
In order to continue its instruction, <strong>an instructor requested you to edit information</strong> to your file no. %{dossier_id} of the « %{libelle_demarche} » procedure.
|
||||
sva_svr:
|
||||
As part of the « %{rule_name} » principle, the instruction period is suspended until you submit the requested corrections.
|
||||
incomplete:
|
||||
explanation_html:
|
||||
In order to continue its instruction, <strong>an instructor requested you to complete</strong> your file no. %{dossier_id} of the « %{libelle_demarche} » procedure.
|
||||
sva_svr:
|
||||
As part of the « %{rule_name} » principle, the instruction period will be reset upon receipt of your complete file.
|
||||
link:
|
||||
Check your file's mailbox to see what changes need to be made, then edit the file directly on the website.
|
||||
access_message: Open the mailbox
|
||||
|
|
|
@ -3,8 +3,16 @@ fr:
|
|||
dossier_mailer:
|
||||
notify_pending_correction:
|
||||
subject: Vous devez corriger votre dossier nº %{dossier_id} « %{libelle_demarche} »
|
||||
explanation_html:
|
||||
Afin de poursuivre son instruction, <strong>un instructeur vous demande d’apporter des corrections</strong> à votre dossier nº %{dossier_id} de la démarche « %{libelle_demarche} ».
|
||||
correction:
|
||||
explanation_html:
|
||||
Afin de poursuivre son instruction, <strong>un instructeur vous demande d’apporter des corrections</strong> à votre dossier nº %{dossier_id} de la démarche « %{libelle_demarche} ».
|
||||
sva_svr:
|
||||
Dans le cadre du principe du « %{rule_name} », le délai d’instruction est suspendu jusqu’à ce que vous déposiez les corrections demandées.
|
||||
incomplete:
|
||||
explanation_html:
|
||||
Afin de commencer son instruction, <strong>un instructeur vous demande de compléter</strong> votre dossier nº %{dossier_id} de la démarche « %{libelle_demarche} ».
|
||||
sva_svr:
|
||||
Dans le cadre du principe du « %{rule_name} », le délai d’instruction sera réinitialisé à réception de votre dossier complet.
|
||||
link:
|
||||
Consultez la messagerie de votre dossier pour prendre connaissance des modifications à effectuer,
|
||||
puis modifiez le dossier directement sur le site.
|
||||
|
|
|
@ -573,6 +573,8 @@ Rails.application.routes.draw do
|
|||
resource :dossier_submitted_message, only: [:edit, :update, :create]
|
||||
# ADDED TO ACCESS IT FROM THE IFRAME
|
||||
get 'attestation_template/preview' => 'attestation_templates#preview'
|
||||
|
||||
resource :sva_svr, only: [:show, :edit, :update], controller: 'sva_svr'
|
||||
end
|
||||
|
||||
resources :services, except: [:show] do
|
||||
|
|
5
db/migrate/20230525130349_add_sva_svr_to_procedures.rb
Normal file
5
db/migrate/20230525130349_add_sva_svr_to_procedures.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
class AddSVASVRToProcedures < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :procedures, :sva_svr, :jsonb, default: {}, null: false
|
||||
end
|
||||
end
|
|
@ -0,0 +1,6 @@
|
|||
class AddSVASVRDecisionOnToDossiers < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :dossiers, :sva_svr_decision_on, :date, default: nil
|
||||
add_column :dossiers, :sva_svr_decision_triggered_at, :datetime, default: nil
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
class AddKindToDossierCorrections < ActiveRecord::Migration[7.0]
|
||||
def change
|
||||
add_column :dossier_corrections, :kind, :string, default: 'correction', null: false
|
||||
end
|
||||
end
|
|
@ -323,6 +323,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_29_102031) do
|
|||
t.bigint "dossier_id", null: false
|
||||
t.datetime "resolved_at", precision: 6
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.string "kind", default: "correction", null: false
|
||||
t.index ["commentaire_id"], name: "index_dossier_corrections_on_commentaire_id"
|
||||
t.index ["dossier_id"], name: "index_dossier_corrections_on_dossier_id"
|
||||
t.index ["resolved_at"], name: "index_dossier_corrections_on_resolved_at", where: "((resolved_at IS NULL) OR (resolved_at IS NOT NULL))"
|
||||
|
@ -409,6 +410,8 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_29_102031) do
|
|||
t.string "state"
|
||||
t.datetime "termine_close_to_expiration_notice_sent_at", precision: 6
|
||||
t.datetime "updated_at", precision: 6
|
||||
t.date "sva_svr_decision_on"
|
||||
t.datetime "sva_svr_decision_triggered_at", precision: 6
|
||||
t.integer "user_id"
|
||||
t.index ["archived"], name: "index_dossiers_on_archived"
|
||||
t.index ["batch_operation_id"], name: "index_dossiers_on_batch_operation_id"
|
||||
|
@ -762,6 +765,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_06_29_102031) do
|
|||
t.text "routing_criteria_name", default: "Votre ville"
|
||||
t.boolean "routing_enabled"
|
||||
t.bigint "service_id"
|
||||
t.jsonb "sva_svr", default: {}, null: false
|
||||
t.text "tags", default: [], array: true
|
||||
t.datetime "test_started_at", precision: 6
|
||||
t.datetime "unpublished_at", precision: 6
|
||||
|
|
|
@ -49,5 +49,16 @@ RSpec.describe Instructeurs::EnConstructionMenuComponent, type: :component do
|
|||
expect(subject).to have_dropdown_item('Repasser en construction')
|
||||
expect(subject).to have_dropdown_items(count: 3)
|
||||
end
|
||||
|
||||
context 'when procedure is sva' do
|
||||
let(:dossier) { create(:dossier, :en_instruction, procedure: create(:procedure, :sva)) }
|
||||
|
||||
it 'renders a dropdown' do
|
||||
expect(subject).to have_dropdown_title('Demander une correction')
|
||||
expect(subject).to have_dropdown_item('Demander une correction')
|
||||
expect(subject).to have_dropdown_item('Demander à compléter')
|
||||
expect(subject).to have_dropdown_items(count: 4)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -499,11 +499,13 @@ describe Instructeurs::DossiersController, type: :controller do
|
|||
describe '#pending_correction' do
|
||||
let(:message) { 'do that' }
|
||||
let(:justificatif) { nil }
|
||||
let(:kind) { nil }
|
||||
|
||||
subject do
|
||||
post :pending_correction, params: {
|
||||
procedure_id: procedure.id, dossier_id: dossier.id,
|
||||
dossier: { motivation: message, justificatif_motivation: justificatif }
|
||||
dossier: { motivation: message, justificatif_motivation: justificatif },
|
||||
kind:
|
||||
}, format: :turbo_stream
|
||||
end
|
||||
|
||||
|
@ -529,6 +531,7 @@ describe Instructeurs::DossiersController, type: :controller do
|
|||
|
||||
expect(dossier.reload).to be_en_construction
|
||||
expect(dossier).to be_pending_correction
|
||||
expect(dossier.corrections.last).to be_correction
|
||||
end
|
||||
|
||||
it 'create a comment with text body' do
|
||||
|
@ -536,6 +539,14 @@ describe Instructeurs::DossiersController, type: :controller do
|
|||
expect(dossier.commentaires.last).to be_flagged_pending_correction
|
||||
end
|
||||
|
||||
context 'flagged as incomplete' do
|
||||
let(:kind) { 'incomplete' }
|
||||
|
||||
it 'create a correction of incomplete kind' do
|
||||
expect(dossier.corrections.last).to be_incomplete
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an attachment' do
|
||||
let(:justificatif) { fake_justificatif }
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
describe Users::DossiersController, type: :controller do
|
||||
include ActiveSupport::Testing::TimeHelpers
|
||||
|
||||
let(:user) { create(:user) }
|
||||
|
||||
describe 'before_actions' do
|
||||
|
@ -351,9 +353,8 @@ describe Users::DossiersController, type: :controller do
|
|||
let(:payload) { { id: dossier.id } }
|
||||
|
||||
subject do
|
||||
Timecop.freeze(now) do
|
||||
post :submit_brouillon, params: payload
|
||||
end
|
||||
travel_to now
|
||||
post :submit_brouillon, params: payload
|
||||
end
|
||||
|
||||
context 'when the dossier cannot be updated by the user' do
|
||||
|
@ -433,6 +434,24 @@ describe Users::DossiersController, type: :controller do
|
|||
it { expect(flash.alert).to eq("Vous n’avez pas accès à ce dossier") }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when procedure has sva enabled' do
|
||||
let(:procedure) { create(:procedure, :sva) }
|
||||
let!(:dossier) { create(:dossier, :brouillon, procedure:, user:) }
|
||||
|
||||
it 'passe automatiquement en instruction' do
|
||||
delivery = double.tap { expect(_1).to receive(:deliver_later).with(no_args).twice }
|
||||
expect(NotificationMailer).to receive(:send_en_construction_notification).and_return(delivery)
|
||||
expect(NotificationMailer).to receive(:send_en_instruction_notification).and_return(delivery)
|
||||
|
||||
subject
|
||||
dossier.reload
|
||||
|
||||
expect(dossier).to be_en_instruction
|
||||
expect(dossier.pending_correction?).to be_falsey
|
||||
expect(dossier.en_instruction_at).to within(5.seconds).of(Time.current)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#submit_en_construction' do
|
||||
|
@ -522,6 +541,47 @@ describe Users::DossiersController, type: :controller do
|
|||
it "resolve correction" do
|
||||
expect { subject }.to change { correction.reload.resolved_at }.to be_truthy
|
||||
end
|
||||
|
||||
context 'when procedure has sva enabled' do
|
||||
let(:procedure) { create(:procedure, :sva) }
|
||||
let!(:dossier) { create(:dossier, :en_construction, procedure:, user:) }
|
||||
|
||||
it 'passe automatiquement en instruction' do
|
||||
expect(dossier.pending_correction?).to be_truthy
|
||||
|
||||
subject
|
||||
dossier.reload
|
||||
|
||||
expect(dossier).to be_en_instruction
|
||||
expect(dossier.pending_correction?).to be_falsey
|
||||
expect(dossier.en_instruction_at).to within(5.seconds).of(Time.current)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is sva without confirming correction' do
|
||||
let!(:correction) { create(:dossier_correction, dossier: dossier) }
|
||||
|
||||
subject { post :submit_en_construction, params: { id: dossier.id } }
|
||||
|
||||
it "does not resolve correction" do
|
||||
expect { subject }.not_to change { correction.reload.resolved_at }
|
||||
end
|
||||
|
||||
context 'when procedure has sva enabled' do
|
||||
let(:procedure) { create(:procedure, :sva) }
|
||||
let!(:dossier) { create(:dossier, :en_construction, procedure:, user:) }
|
||||
|
||||
it 'does not passe automatiquement en instruction' do
|
||||
expect(dossier.pending_correction?).to be_truthy
|
||||
|
||||
subject
|
||||
dossier.reload
|
||||
|
||||
expect(dossier).to be_en_construction
|
||||
expect(dossier.pending_correction?).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
FactoryBot.define do
|
||||
factory :commentaire do
|
||||
association :dossier, :en_construction
|
||||
email { generate(:user_email) }
|
||||
|
||||
body { 'plop' }
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ FactoryBot.define do
|
|||
factory :dossier_correction do
|
||||
dossier
|
||||
commentaire
|
||||
kind { :correction }
|
||||
resolved_at { nil }
|
||||
|
||||
trait :resolved do
|
||||
|
|
|
@ -13,6 +13,8 @@ FactoryBot.define do
|
|||
ask_birthday { false }
|
||||
lien_site_web { "https://mon-site.gouv" }
|
||||
path { SecureRandom.uuid }
|
||||
declarative_with_state { nil }
|
||||
sva_svr { {} }
|
||||
|
||||
groupe_instructeurs { [association(:groupe_instructeur, :default, procedure: instance, strategy: :build)] }
|
||||
administrateurs { administrateur.present? ? [administrateur] : [association(:administrateur)] }
|
||||
|
@ -411,6 +413,14 @@ FactoryBot.define do
|
|||
build(:dossier_submitted_message, revisions: [procedure.active_revision])
|
||||
end
|
||||
end
|
||||
|
||||
trait :sva do
|
||||
sva_svr { SVASVRConfiguration.new(decision: :sva).attributes }
|
||||
end
|
||||
|
||||
trait :svr do
|
||||
sva_svr { SVASVRConfiguration.new(decision: :svr).attributes }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
33
spec/jobs/cron/procedure_process_sva_svr_job_spec.rb
Normal file
33
spec/jobs/cron/procedure_process_sva_svr_job_spec.rb
Normal file
|
@ -0,0 +1,33 @@
|
|||
RSpec.describe Cron::ProcedureProcessSVASVRJob, type: :job do
|
||||
let!(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure:, depose_at: 2.months.ago, sva_svr_decision_on: Date.current) }
|
||||
let!(:dossier_in_future) { create(:dossier, :en_instruction, :with_individual, procedure:, depose_at: 1.day.ago, sva_svr_decision_on: Date.yesterday + 2.months) }
|
||||
let!(:dossier_en_construction) { create(:dossier, :en_construction, :with_individual, procedure:, depose_at: 2.months.ago, sva_svr_decision_on: Date.current) }
|
||||
let!(:dossier_en_brouillon) { create(:dossier, :brouillon, :with_individual, procedure:) }
|
||||
|
||||
subject(:perform_job) { described_class.perform_now }
|
||||
before { perform_job }
|
||||
|
||||
context 'when procedure is published' do
|
||||
let(:procedure) { create(:procedure, :published, :sva, :for_individual) }
|
||||
|
||||
it 'queues ProcedureSVASVRProcessDossierJob for published sva procedure' do
|
||||
expect(ProcedureSVASVRProcessDossierJob).to have_been_enqueued.with(dossier)
|
||||
expect(ProcedureSVASVRProcessDossierJob).to have_been_enqueued.with(dossier_en_construction)
|
||||
expect(ProcedureSVASVRProcessDossierJob).to have_been_enqueued.with(dossier_in_future)
|
||||
expect(ProcedureSVASVRProcessDossierJob).not_to have_been_enqueued.with(dossier_en_brouillon)
|
||||
expect(ProcedureSVASVRProcessDossierJob).to have_been_enqueued.exactly(3).times
|
||||
end
|
||||
end
|
||||
|
||||
context 'when procedure is closed' do
|
||||
let(:procedure) { create(:procedure, :closed, :sva, :for_individual) }
|
||||
|
||||
it { expect(ProcedureSVASVRProcessDossierJob).to have_been_enqueued.with(dossier) }
|
||||
end
|
||||
|
||||
context 'when procedure is not sva' do
|
||||
let(:procedure) { create(:procedure, :published, :for_individual) }
|
||||
|
||||
it { expect(ProcedureSVASVRProcessDossierJob).not_to have_been_enqueued }
|
||||
end
|
||||
end
|
56
spec/jobs/procedure_sva_svr_process_dossier_job_spec.rb
Normal file
56
spec/jobs/procedure_sva_svr_process_dossier_job_spec.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
RSpec.describe ProcedureSVASVRProcessDossierJob, type: :job do
|
||||
include ActiveJob::TestHelper
|
||||
include ActiveSupport::Testing::TimeHelpers
|
||||
|
||||
let(:procedure) { create(:procedure, :published, :sva, :for_individual) }
|
||||
let!(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure:, depose_at: 2.months.ago - 1.day, sva_svr_decision_on: Date.current) }
|
||||
|
||||
subject do
|
||||
described_class.perform_now(dossier)
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
context 'when procedure is SVA' do
|
||||
it 'should accept dossier' do
|
||||
expect(subject.sva_svr_decision_on).to eq(Date.current)
|
||||
expect(subject).to be_accepte
|
||||
expect(subject.processed_at).to within(1.second).of(Time.current)
|
||||
end
|
||||
|
||||
context 'when decision is scheduled in the future' do
|
||||
let!(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure:, depose_at: 1.day.ago, sva_svr_decision_on: 2.months.from_now.to_date) }
|
||||
|
||||
it 'should not accept dossier' do
|
||||
expect { subject }.not_to change { dossier.reload.updated_at }
|
||||
expect(subject).to be_en_instruction
|
||||
end
|
||||
end
|
||||
|
||||
context 'when dossier has pending correction / is en_construction' do
|
||||
before do
|
||||
travel_to 2.days.ago do # create correction in past so it will be 3 days of delay
|
||||
dossier.flag_as_pending_correction!(build(:commentaire, dossier: dossier))
|
||||
end
|
||||
end
|
||||
|
||||
it 'should not accept dossier' do
|
||||
subject
|
||||
expect(dossier).to be_en_construction
|
||||
end
|
||||
|
||||
it 'should update sva_svr_decision_on with corrections delay' do
|
||||
expect { subject }.to change { dossier.reload.sva_svr_decision_on }.from(Date.current).to(Date.current + 3.days)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when dossier was submitted before sva was enabled' do
|
||||
let!(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure:, depose_at: 2.months.ago) }
|
||||
|
||||
it 'should be noop' do
|
||||
expect(subject.sva_svr_decision_on).to be_nil
|
||||
expect(subject).to be_en_instruction
|
||||
expect(subject.processed_at).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
|
@ -228,4 +228,53 @@ RSpec.describe DossierMailer, type: :mailer do
|
|||
it { expect(subject.body).to include(dossier.procedure.libelle) }
|
||||
it { expect(subject.body).to include("Suite à cette modification, vous ne suivez plus ce dossier.") }
|
||||
end
|
||||
|
||||
describe '.notify_pending_correction' do
|
||||
let(:procedure) { create(:procedure) }
|
||||
let(:dossier) { create(:dossier, :en_construction, procedure:, sva_svr_decision_on:) }
|
||||
let(:sva_svr_decision_on) { nil }
|
||||
let(:kind) { :correction }
|
||||
let(:commentaire) { create(:commentaire, dossier:) }
|
||||
|
||||
subject {
|
||||
dossier.flag_as_pending_correction!(commentaire, kind)
|
||||
described_class.with(commentaire:).notify_pending_correction
|
||||
}
|
||||
|
||||
context 'kind is correction' do
|
||||
it { expect(subject.subject).to eq("Vous devez corriger votre dossier nº #{dossier.id} « #{dossier.procedure.libelle} »") }
|
||||
it { expect(subject.body).to include("apporter des corrections") }
|
||||
it { expect(subject.body).not_to include("Silence") }
|
||||
end
|
||||
|
||||
context 'sva with kind is correction' do
|
||||
let(:sva_svr_decision_on) { Date.tomorrow }
|
||||
let(:procedure) { create(:procedure, :sva) }
|
||||
|
||||
it { expect(subject.subject).to eq("Vous devez corriger votre dossier nº #{dossier.id} « #{dossier.procedure.libelle} »") }
|
||||
it { expect(subject.body).to include("apporter des corrections") }
|
||||
it { expect(subject.body).to include("Silence Vaut Accord") }
|
||||
it { expect(subject.body).to include("suspendu") }
|
||||
end
|
||||
|
||||
context 'sva with kind is incomplete' do
|
||||
let(:sva_svr_decision_on) { Date.tomorrow }
|
||||
let(:kind) { :incomplete }
|
||||
let(:procedure) { create(:procedure, :sva) }
|
||||
|
||||
it { expect(subject.body).to include("compléter") }
|
||||
it { expect(subject.body).to include("Silence Vaut Accord") }
|
||||
it { expect(subject.body).to include("réinitialisé") }
|
||||
end
|
||||
|
||||
context 'svr with kind is incomplete' do
|
||||
let(:sva_svr_decision_on) { Date.tomorrow }
|
||||
let(:kind) { :incomplete }
|
||||
let(:procedure) { create(:procedure, :svr) }
|
||||
|
||||
it { expect(subject.body).to include("compléter") }
|
||||
it { expect(subject.body).to include("Silence Vaut Rejet") }
|
||||
it { expect(subject.body).to include("réinitialisé") }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,7 +9,13 @@ class DossierMailerPreview < ActionMailer::Preview
|
|||
end
|
||||
|
||||
def notify_pending_correction
|
||||
DossierMailer.with(dossier: dossier_en_construction).notify_pending_correction
|
||||
commentaire = commentaire(on: dossier_en_construction(sva_svr_decision: :sva)).tap { _1.build_dossier_correction(kind: :correction) }
|
||||
DossierMailer.with(commentaire:).notify_pending_correction
|
||||
end
|
||||
|
||||
def notify_pending_correction_sva_correction
|
||||
commentaire = commentaire(on: dossier_en_construction(sva_svr_decision: :sva)).tap { _1.build_dossier_correction(kind: :correction) }
|
||||
DossierMailer.with(commentaire:).notify_pending_correction
|
||||
end
|
||||
|
||||
def notify_revert_to_instruction
|
||||
|
@ -99,8 +105,17 @@ class DossierMailerPreview < ActionMailer::Preview
|
|||
Dossier.new(id: 47882, state: :en_instruction, procedure: procedure, user: user)
|
||||
end
|
||||
|
||||
def dossier_en_construction
|
||||
Dossier.new(id: 47882, state: :en_construction, procedure: procedure, user: user)
|
||||
def dossier_en_construction(sva_svr_decision: nil)
|
||||
local_procedure = procedure
|
||||
|
||||
dossier = Dossier.new(id: 47882, state: :en_construction, procedure: local_procedure, user: user)
|
||||
|
||||
if sva_svr_decision
|
||||
local_procedure.sva_svr = { decision: sva_svr_decision, period: 2, unit: :months }
|
||||
dossier.sva_svr_decision_on = 10.days.from_now.to_date
|
||||
end
|
||||
|
||||
dossier
|
||||
end
|
||||
|
||||
def dossier_accepte
|
||||
|
|
|
@ -31,25 +31,33 @@ describe DossierCorrectableConcern do
|
|||
let(:instructeur) { create(:instructeur) }
|
||||
let(:commentaire) { create(:commentaire, dossier:, instructeur:) }
|
||||
|
||||
subject(:flag) { dossier.flag_as_pending_correction!(commentaire) }
|
||||
|
||||
context 'when dossier is en_construction' do
|
||||
it 'creates a correction' do
|
||||
expect { dossier.flag_as_pending_correction!(commentaire) }.to change { dossier.corrections.pending.count }.by(1)
|
||||
expect { flag }.to change { dossier.corrections.pending.count }.by(1)
|
||||
expect(dossier.corrections.last).to be_correction
|
||||
end
|
||||
|
||||
it 'created a correction of incomplete kind' do
|
||||
expect { dossier.flag_as_pending_correction!(commentaire, "incomplete") }.to change { dossier.corrections.pending.count }.by(1)
|
||||
expect(dossier.corrections.last).to be_incomplete
|
||||
end
|
||||
|
||||
it 'does not change dossier state' do
|
||||
expect { dossier.flag_as_pending_correction!(commentaire) }.not_to change { dossier.state }
|
||||
expect { flag }.not_to change { dossier.state }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when dossier is not en_instruction' do
|
||||
context 'when dossier is en_instruction' do
|
||||
let(:dossier) { create(:dossier, :en_instruction) }
|
||||
|
||||
it 'creates a correction' do
|
||||
expect { dossier.flag_as_pending_correction!(commentaire) }.to change { dossier.corrections.pending.count }.by(1)
|
||||
expect { flag }.to change { dossier.corrections.pending.count }.by(1)
|
||||
end
|
||||
|
||||
it 'repasse dossier en_construction' do
|
||||
expect { dossier.flag_as_pending_correction!(commentaire) }.to change { dossier.state }.to('en_construction')
|
||||
expect { flag }.to change { dossier.state }.to('en_construction')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -57,7 +65,7 @@ describe DossierCorrectableConcern do
|
|||
before { create(:dossier_correction, dossier:) }
|
||||
|
||||
it 'does not create a correction' do
|
||||
expect { dossier.flag_as_pending_correction!(commentaire) }.not_to change { dossier.corrections.pending.count }
|
||||
expect { flag }.not_to change { dossier.corrections.pending.count }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -65,7 +73,7 @@ describe DossierCorrectableConcern do
|
|||
before { create(:dossier_correction, :resolved, dossier:) }
|
||||
|
||||
it 'creates a correction' do
|
||||
expect { dossier.flag_as_pending_correction!(commentaire) }.to change { dossier.corrections.pending.count }.by(1)
|
||||
expect { flag }.to change { dossier.corrections.pending.count }.by(1)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -73,7 +81,39 @@ describe DossierCorrectableConcern do
|
|||
let(:dossier) { create(:dossier, :accepte) }
|
||||
|
||||
it 'does not create a correction' do
|
||||
expect { dossier.flag_as_pending_correction!(commentaire) }.not_to change { dossier.corrections.pending.count }
|
||||
expect { flag }.not_to change { dossier.corrections.pending.count }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when procedure is sva' do
|
||||
let(:dossier) { create(:dossier, :en_instruction, procedure: create(:procedure, :published, :sva)) }
|
||||
|
||||
it 'creates a correction' do
|
||||
expect { flag }.to change { dossier.corrections.pending.count }.by(1)
|
||||
end
|
||||
|
||||
it 'repasse dossier en_construction' do
|
||||
expect { flag }.to change { dossier.state }.to('en_construction')
|
||||
end
|
||||
|
||||
it 'creates a log operation' do
|
||||
expect { flag }.to change { dossier.dossier_operation_logs.count }.by(2)
|
||||
|
||||
log_correction, log_construction = dossier.dossier_operation_logs.last(2)
|
||||
expect(log_correction.operation).to eq("demander_une_correction")
|
||||
expect(log_construction.operation).to eq("repasser_en_construction")
|
||||
|
||||
expect(log_correction.data["subject"]["body"]).to eq(commentaire.body)
|
||||
expect(log_correction.data["subject"]["email"]).to eq(commentaire.instructeur.email)
|
||||
end
|
||||
|
||||
it 'creates a log operation of incomplete dossier' do
|
||||
expect { dossier.flag_as_pending_correction!(commentaire, "incomplete") }.to change { dossier.dossier_operation_logs.count }.by(2)
|
||||
|
||||
log_correction, _ = dossier.dossier_operation_logs.last(2)
|
||||
expect(log_correction.operation).to eq("demander_a_completer")
|
||||
expect(log_correction.data["subject"]["body"]).to eq(commentaire.body)
|
||||
expect(log_correction.data["subject"]["email"]).to eq(commentaire.instructeur.email)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1011,7 +1011,6 @@ describe Dossier, type: :model do
|
|||
end
|
||||
|
||||
describe '#accepter_automatiquement!' do
|
||||
let(:dossier) { create(:dossier, :en_construction, :with_individual, :with_declarative_accepte) }
|
||||
let(:last_operation) { dossier.dossier_operation_logs.last }
|
||||
let!(:now) { Time.zone.parse('01/01/2100') }
|
||||
let(:attestation) { Attestation.new }
|
||||
|
@ -1021,20 +1020,49 @@ describe Dossier, type: :model do
|
|||
allow(dossier).to receive(:build_attestation).and_return(attestation)
|
||||
|
||||
Timecop.freeze(now)
|
||||
dossier.accepter_automatiquement!
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
after { Timecop.return }
|
||||
|
||||
it { expect(dossier.motivation).to eq(nil) }
|
||||
it { expect(dossier.en_instruction_at).to eq(now) }
|
||||
it { expect(dossier.processed_at).to eq(now) }
|
||||
it { expect(dossier.state).to eq('accepte') }
|
||||
it { expect(last_operation.operation).to eq('accepter') }
|
||||
it { expect(last_operation.automatic_operation?).to be_truthy }
|
||||
it { expect(NotificationMailer).to have_received(:send_accepte_notification).with(dossier) }
|
||||
it { expect(dossier.attestation).to eq(attestation) }
|
||||
subject {
|
||||
dossier.accepter_automatiquement!
|
||||
dossier.reload
|
||||
}
|
||||
|
||||
context 'as declarative procedure' do
|
||||
let(:dossier) { create(:dossier, :en_construction, :with_individual, :with_declarative_accepte) }
|
||||
|
||||
it 'accepts dossier automatiquement' do
|
||||
expect(subject.motivation).to eq(nil)
|
||||
expect(subject.en_instruction_at).to eq(now)
|
||||
expect(subject.processed_at).to eq(now)
|
||||
expect(subject.declarative_triggered_at).to eq(now)
|
||||
expect(subject.sva_svr_decision_triggered_at).to be_nil
|
||||
expect(subject).to be_accepte
|
||||
expect(last_operation.operation).to eq('accepter')
|
||||
expect(last_operation.automatic_operation?).to be_truthy
|
||||
expect(NotificationMailer).to have_received(:send_accepte_notification).with(dossier)
|
||||
expect(subject.attestation).to eq(attestation)
|
||||
end
|
||||
end
|
||||
|
||||
context 'as sva procedure' do
|
||||
let(:procedure) { create(:procedure, :for_individual, :published, :sva) }
|
||||
let(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure:, sva_svr_decision_on: Date.current, en_instruction_at: DateTime.new(2021, 5, 1, 12)) }
|
||||
|
||||
it 'accepts dossier automatiquement' do
|
||||
expect(subject.motivation).to eq(nil)
|
||||
expect(subject.en_instruction_at).to eq(DateTime.new(2021, 5, 1, 12))
|
||||
expect(subject.processed_at).to eq(now)
|
||||
expect(subject.declarative_triggered_at).to be_nil
|
||||
expect(subject.sva_svr_decision_triggered_at).to eq(now)
|
||||
expect(subject).to be_accepte
|
||||
expect(last_operation.operation).to eq('accepter')
|
||||
expect(last_operation.automatic_operation?).to be_truthy
|
||||
expect(NotificationMailer).to have_received(:send_accepte_notification).with(dossier)
|
||||
expect(subject.attestation).to eq(attestation)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#passer_en_instruction!' do
|
||||
|
@ -1062,20 +1090,70 @@ describe Dossier, type: :model do
|
|||
end
|
||||
|
||||
describe '#passer_automatiquement_en_instruction!' do
|
||||
let(:dossier) { create(:dossier, :en_construction, :with_declarative_en_instruction, en_construction_close_to_expiration_notice_sent_at: Time.zone.now) }
|
||||
let(:last_operation) { dossier.dossier_operation_logs.last }
|
||||
let(:operation_serialized) { last_operation.data }
|
||||
let(:instructeur) { create(:instructeur) }
|
||||
|
||||
before { dossier.passer_automatiquement_en_instruction! }
|
||||
context "via procedure declarative en instruction" do
|
||||
let(:dossier) { create(:dossier, :en_construction, :with_declarative_en_instruction, en_construction_close_to_expiration_notice_sent_at: Time.zone.now) }
|
||||
|
||||
it { expect(dossier.followers_instructeurs).not_to include(instructeur) }
|
||||
it { expect(dossier.en_construction_close_to_expiration_notice_sent_at).to be_nil }
|
||||
it { expect(last_operation.operation).to eq('passer_en_instruction') }
|
||||
it { expect(last_operation.automatic_operation?).to be_truthy }
|
||||
it { expect(operation_serialized['operation']).to eq('passer_en_instruction') }
|
||||
it { expect(operation_serialized['dossier_id']).to eq(dossier.id) }
|
||||
it { expect(operation_serialized['executed_at']).to eq(last_operation.executed_at.iso8601) }
|
||||
subject do
|
||||
dossier.process_declarative!
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
it 'passes dossier en instruction' do
|
||||
expect(subject.followers_instructeurs).not_to include(instructeur)
|
||||
expect(subject.en_construction_close_to_expiration_notice_sent_at).to be_nil
|
||||
expect(subject.declarative_triggered_at).to be_within(1.second).of(Time.current)
|
||||
expect(last_operation.operation).to eq('passer_en_instruction')
|
||||
expect(last_operation.automatic_operation?).to be_truthy
|
||||
expect(operation_serialized['operation']).to eq('passer_en_instruction')
|
||||
expect(operation_serialized['dossier_id']).to eq(dossier.id)
|
||||
expect(operation_serialized['executed_at']).to eq(last_operation.executed_at.iso8601)
|
||||
end
|
||||
end
|
||||
|
||||
context "via procedure sva" do
|
||||
let(:procedure) { create(:procedure, :sva, :published, :for_individual) }
|
||||
let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure:, sva_svr_decision_on: 10.days.from_now) }
|
||||
|
||||
subject do
|
||||
dossier.process_sva_svr!
|
||||
dossier.reload
|
||||
end
|
||||
|
||||
it 'passes dossier en instruction' do
|
||||
expect(subject.state).to eq('en_instruction')
|
||||
expect(subject.followers_instructeurs).not_to include(instructeur)
|
||||
expect(subject.sva_svr_decision_on).to eq(2.months.from_now.to_date + 1.day) # date is updated
|
||||
expect(last_operation.operation).to eq('passer_en_instruction')
|
||||
expect(last_operation.automatic_operation?).to be_truthy
|
||||
expect(operation_serialized['operation']).to eq('passer_en_instruction')
|
||||
expect(operation_serialized['dossier_id']).to eq(dossier.id)
|
||||
expect(operation_serialized['executed_at']).to eq(last_operation.executed_at.iso8601)
|
||||
end
|
||||
|
||||
context 'when dossier was submitted with sva not yet enabled' do
|
||||
let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure:, depose_at: 10.days.ago) }
|
||||
|
||||
it 'leaves dossier en construction' do
|
||||
expect(subject.sva_svr_decision_on).to be_nil
|
||||
expect(subject.state).to eq('en_construction')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#can_repasser_en_construction?' do
|
||||
let(:dossier) { create(:dossier, :en_instruction) }
|
||||
it { expect(dossier.can_repasser_en_construction?).to be_truthy }
|
||||
|
||||
context 'when procedure is sva' do
|
||||
let(:dossier) { create(:dossier, :en_instruction, procedure: create(:procedure, :sva)) }
|
||||
|
||||
it { expect(dossier.can_repasser_en_construction?).to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#can_passer_automatiquement_en_instruction?' do
|
||||
|
@ -1115,10 +1193,24 @@ describe Dossier, type: :model do
|
|||
it { expect(dossier.can_passer_automatiquement_en_instruction?).to be_truthy }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when procedure has sva or svr enabled' do
|
||||
let(:procedure) { create(:procedure, :published, :sva) }
|
||||
let(:dossier) { create(:dossier, :en_construction, procedure:) }
|
||||
|
||||
it { expect(dossier.can_passer_automatiquement_en_instruction?).to be_truthy }
|
||||
|
||||
context 'when dossier was already processed by sva' do
|
||||
let(:dossier) { create(:dossier, :en_construction, procedure:, sva_svr_decision_triggered_at: 1.hour.ago) }
|
||||
|
||||
it { expect(dossier.can_passer_automatiquement_en_instruction?).to be_falsey }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#can_accepter_automatiquement?' do
|
||||
let(:dossier) { create(:dossier, :en_instruction, declarative_triggered_at: declarative_triggered_at) }
|
||||
let(:dossier) { create(:dossier, state: initial_state, declarative_triggered_at: declarative_triggered_at) }
|
||||
let(:initial_state) { :en_construction }
|
||||
let(:declarative_triggered_at) { nil }
|
||||
|
||||
it { expect(dossier.can_accepter_automatiquement?).to be_falsey }
|
||||
|
@ -1136,6 +1228,43 @@ describe Dossier, type: :model do
|
|||
it { expect(dossier.can_accepter_automatiquement?).to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when procedure is sva/svr' do
|
||||
let(:decision) { :sva }
|
||||
let(:initial_state) { :en_instruction }
|
||||
|
||||
before do
|
||||
dossier.procedure.update!(sva_svr: SVASVRConfiguration.new(decision:).attributes)
|
||||
dossier.update!(sva_svr_decision_on: Date.current)
|
||||
end
|
||||
|
||||
it { expect(dossier.can_accepter_automatiquement?).to be_truthy }
|
||||
|
||||
context 'when sva_svr_decision_on is in the future' do
|
||||
before { dossier.update!(sva_svr_decision_on: 1.day.from_now) }
|
||||
|
||||
it { expect(dossier.can_accepter_automatiquement?).to be_falsey }
|
||||
end
|
||||
|
||||
context 'when dossier has pending correction' do
|
||||
let(:dossier) { create(:dossier, :en_construction) }
|
||||
let!(:dossier_correction) { create(:dossier_correction, dossier:) }
|
||||
|
||||
it { expect(dossier.can_accepter_automatiquement?).to be_falsey }
|
||||
end
|
||||
|
||||
context 'when decision is svr' do
|
||||
let(:decision) { :svr }
|
||||
|
||||
it { expect(dossier.can_accepter_automatiquement?).to be_falsey }
|
||||
end
|
||||
|
||||
context 'when dossier was already processed by sva' do
|
||||
before { dossier.update!(sva_svr_decision_triggered_at: 1.hour.ago) }
|
||||
|
||||
it { expect(dossier.can_accepter_automatiquement?).to be_falsey }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "can't transition to terminer when etablissement is in degraded mode" do
|
||||
|
@ -1752,6 +1881,12 @@ describe Dossier, type: :model do
|
|||
let(:dossier) { create(:dossier) }
|
||||
|
||||
it { expect(dossier.spreadsheet_columns(types_de_champ: [])).to include(["État du dossier", "Brouillon"]) }
|
||||
|
||||
context 'procedure sva' do
|
||||
let(:dossier) { create(:dossier, :en_instruction, procedure: create(:procedure, :sva)) }
|
||||
|
||||
it { expect(dossier.spreadsheet_columns(types_de_champ: [])).to include(["Date SVA", :sva_svr_decision_on]) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#processed_in_month' do
|
||||
|
@ -1918,6 +2053,12 @@ describe Dossier, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#sva_svr_decision_in_days' do
|
||||
let(:dossier) { create(:dossier, :en_instruction, sva_svr_decision_on: 10.days.from_now) }
|
||||
|
||||
it { expect(dossier.sva_svr_decision_in_days).to eq 10 }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def count_for_month(processed_by_month, month)
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
describe ProcedurePresentation do
|
||||
include ActiveSupport::Testing::TimeHelpers
|
||||
|
||||
let(:procedure) { create(:procedure, :published, types_de_champ_public: [{}], types_de_champ_private: [{}]) }
|
||||
let(:instructeur) { create(:instructeur) }
|
||||
let(:assign_to) { create(:assign_to, procedure: procedure, instructeur: instructeur) }
|
||||
|
@ -113,6 +115,18 @@ describe ProcedurePresentation do
|
|||
|
||||
it { is_expected.to include(name_field, surname_field, gender_field) }
|
||||
end
|
||||
|
||||
context 'when the procedure is sva/svr' do
|
||||
let(:procedure) { create(:procedure, :for_individual, :sva) }
|
||||
let(:procedure_presentation) { create(:procedure_presentation, assign_to: assign_to) }
|
||||
|
||||
let(:decision_on) { { "label" => "Date décision SVA", "table" => "self", "column" => "sva_svr_decision_on", 'classname' => '', 'virtual' => false, "type" => :date, "scope" => '', "value_column" => :value } }
|
||||
let(:decision_before_field) { { "label" => "Date décision SVA avant", "table" => "self", "column" => "sva_svr_decision_before", 'classname' => '', 'virtual' => true, "type" => :date, "scope" => '', "value_column" => :value } }
|
||||
|
||||
subject { procedure_presentation.fields }
|
||||
|
||||
it { is_expected.to include(decision_on, decision_before_field) }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#displayable_fields_for_select" do
|
||||
|
@ -466,6 +480,23 @@ describe ProcedurePresentation do
|
|||
it { is_expected.to match_array([kept_dossier.id, later_dossier.id]) }
|
||||
end
|
||||
|
||||
context 'for sva_svr_decision_before column' do
|
||||
before do
|
||||
travel_to Time.zone.local(2023, 6, 10, 10)
|
||||
end
|
||||
|
||||
let(:procedure) { create(:procedure, :published, :sva, types_de_champ_public: [{}], types_de_champ_private: [{}]) }
|
||||
let(:filter) { [{ 'table' => 'self', 'column' => 'sva_svr_decision_before', 'value' => '15/06/2023' }] }
|
||||
|
||||
let!(:kept_dossier) { create(:dossier, :en_instruction, procedure:, sva_svr_decision_on: Date.current) }
|
||||
let!(:later_dossier) { create(:dossier, :en_instruction, procedure:, sva_svr_decision_on: Date.current + 2.days) }
|
||||
let!(:discarded_dossier) { create(:dossier, :en_instruction, procedure:, sva_svr_decision_on: Date.current + 10.days) }
|
||||
let!(:en_construction_dossier) { create(:dossier, :en_construction, procedure:, sva_svr_decision_on: Date.current + 2.days) }
|
||||
let!(:accepte_dossier) { create(:dossier, :accepte, procedure:, sva_svr_decision_on: Date.current + 2.days) }
|
||||
|
||||
it { is_expected.to match_array([kept_dossier.id, later_dossier.id, en_construction_dossier.id]) }
|
||||
end
|
||||
|
||||
context 'ignore time of day' do
|
||||
let(:filter) { [{ 'table' => 'self', 'column' => 'en_construction_at', 'value' => '17/10/2018 19:30' }] }
|
||||
|
||||
|
|
|
@ -401,6 +401,55 @@ describe Procedure do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with sva svr' do
|
||||
before {
|
||||
procedure.sva_svr["decision"] = "svr"
|
||||
}
|
||||
|
||||
context 'when procedure is published with sva' do
|
||||
let(:procedure) { create(:procedure, :published, :sva) }
|
||||
|
||||
it 'prevents changes to sva_svr' do
|
||||
expect(procedure).not_to be_valid
|
||||
expect(procedure.errors[:sva_svr].join).to include('ne peut plus être modifiée')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when procedure is published without sva' do
|
||||
let(:procedure) { create(:procedure, :published) }
|
||||
|
||||
it 'allow activation' do
|
||||
expect(procedure).to be_valid
|
||||
end
|
||||
|
||||
it 'allow activation from disabled value' do
|
||||
procedure.sva_svr["decision"] = "disabled"
|
||||
procedure.save!
|
||||
|
||||
procedure.sva_svr["decision"] = "svr"
|
||||
|
||||
expect(procedure).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
context 'brouillon procedure' do
|
||||
let(:procedure) { create(:procedure, :sva) }
|
||||
|
||||
it "can update sva config" do
|
||||
expect(procedure).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
context "with declarative" do
|
||||
let(:procedure) { create(:procedure, declarative_with_state: "accepte") }
|
||||
|
||||
it 'is not valid' do
|
||||
expect(procedure).not_to be_valid
|
||||
expect(procedure.errors[:sva_svr].join).to include('incompatible avec une démarche déclarative')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'opendata' do
|
||||
|
|
75
spec/models/sva_svr_configuration_spec.rb
Normal file
75
spec/models/sva_svr_configuration_spec.rb
Normal file
|
@ -0,0 +1,75 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe SVASVRConfiguration, type: :model do
|
||||
subject(:sva_svr_config) do
|
||||
SVASVRConfiguration.new(
|
||||
decision: decision,
|
||||
period: period,
|
||||
unit: unit,
|
||||
resume: resume
|
||||
)
|
||||
end
|
||||
|
||||
let(:decision) { 'disabled' }
|
||||
let(:period) { 2 }
|
||||
let(:unit) { 'months' }
|
||||
let(:resume) { 'continue' }
|
||||
|
||||
describe 'validations' do
|
||||
context 'when decision is "disabled"' do
|
||||
it 'is valid even if period, unit and resume are nil' do
|
||||
sva_svr_config.period = nil
|
||||
sva_svr_config.unit = nil
|
||||
sva_svr_config.resume = nil
|
||||
|
||||
expect(sva_svr_config).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
context 'when decision is not in DECISION_OPTIONS' do
|
||||
let(:decision) { 'invalid_decision' }
|
||||
|
||||
it 'is not valid' do
|
||||
expect(sva_svr_config).not_to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
context 'when decision is not "disabled"' do
|
||||
let(:decision) { 'sva' }
|
||||
|
||||
it { expect(sva_svr_config).to be_valid }
|
||||
|
||||
it 'is not valid if period is nil' do
|
||||
sva_svr_config.period = nil
|
||||
|
||||
expect(sva_svr_config).not_to be_valid
|
||||
end
|
||||
|
||||
it 'is not valid if unit is nil or not in UNIT_OPTIONS' do
|
||||
sva_svr_config.unit = nil
|
||||
|
||||
expect(sva_svr_config).not_to be_valid
|
||||
|
||||
sva_svr_config.unit = 'years'
|
||||
|
||||
expect(sva_svr_config).not_to be_valid
|
||||
end
|
||||
|
||||
it 'is not valid if resume is nil or not in RESUME_OPTIONS' do
|
||||
sva_svr_config.resume = nil
|
||||
|
||||
expect(sva_svr_config).not_to be_valid
|
||||
|
||||
sva_svr_config.resume = 'pause'
|
||||
|
||||
expect(sva_svr_config).not_to be_valid
|
||||
end
|
||||
|
||||
it 'is not valid if period is not an integer' do
|
||||
sva_svr_config.period = 3.14
|
||||
|
||||
expect(sva_svr_config).not_to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
167
spec/services/sva_svr_decision_date_calculator_service_spec.rb
Normal file
167
spec/services/sva_svr_decision_date_calculator_service_spec.rb
Normal file
|
@ -0,0 +1,167 @@
|
|||
require 'rails_helper'
|
||||
|
||||
describe SVASVRDecisionDateCalculatorService do
|
||||
include ActiveSupport::Testing::TimeHelpers
|
||||
|
||||
let(:procedure) { create(:procedure, sva_svr: config) }
|
||||
let(:dossier) { create(:dossier, :en_instruction, procedure:, depose_at: DateTime.new(2023, 5, 15, 12)) }
|
||||
|
||||
describe '#decision_date' do
|
||||
subject { described_class.new(dossier, procedure).decision_date }
|
||||
|
||||
context 'when sva has a months period' do
|
||||
let(:config) { { decision: :sva, period: 2, unit: :months, resume: :continue } }
|
||||
|
||||
it 'calculates the date based on SVA rules' do
|
||||
expect(subject).to eq(Date.new(2023, 7, 16))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sva has a days period' do
|
||||
let(:config) { { decision: :sva, period: 30, unit: :days, resume: :continue } }
|
||||
|
||||
it 'calculates the date based on SVA rules' do
|
||||
expect(subject).to eq(Date.new(2023, 6, 15))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sva has a weeks period' do
|
||||
let(:config) { { decision: :sva, period: 8, unit: :weeks, resume: :continue } }
|
||||
|
||||
it 'calculates the date based on SVA rules' do
|
||||
expect(subject).to eq(Date.new(2023, 7, 11))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sva resume setting is continue' do
|
||||
let(:config) { { decision: :sva, period: 2, unit: :months, resume: :continue } }
|
||||
|
||||
context 'when a dossier is corrected and resolved' do
|
||||
let!(:correction) do
|
||||
created_at = DateTime.new(2023, 5, 20, 15)
|
||||
resolved_at = DateTime.new(2023, 5, 25, 12)
|
||||
create(:dossier_correction, dossier:, created_at:, resolved_at:)
|
||||
end
|
||||
|
||||
it 'calculates the date based on SVA rules with correction delay' do
|
||||
expect(subject).to eq(Date.new(2023, 7, 22))
|
||||
end
|
||||
|
||||
context 'when there are multiple corrections' do
|
||||
let!(:correction2) do
|
||||
created_at = DateTime.new(2023, 5, 30, 18)
|
||||
resolved_at = DateTime.new(2023, 6, 3, 8)
|
||||
create(:dossier_correction, dossier:, created_at:, resolved_at:)
|
||||
end
|
||||
|
||||
it 'calculates the date based on SVA rules with all correction delays' do
|
||||
expect(subject).to eq(Date.new(2023, 7, 27))
|
||||
end
|
||||
end
|
||||
|
||||
context 'there is a pending correction kind = correct' do
|
||||
before do
|
||||
travel_to DateTime.new(2023, 5, 30, 18) do
|
||||
dossier.flag_as_pending_correction!(build(:commentaire, dossier:))
|
||||
end
|
||||
|
||||
travel_to DateTime.new(2023, 6, 5, 8) # 6 days elapsed, restart 1 day after resolved
|
||||
end
|
||||
|
||||
it 'calculates the date, like if resolution will be today' do
|
||||
expect(subject).to eq(Date.new(2023, 7, 29))
|
||||
end
|
||||
end
|
||||
|
||||
context 'there is a pending correction kind = incomplete' do
|
||||
before do
|
||||
travel_to DateTime.new(2023, 5, 30, 18) do
|
||||
dossier.flag_as_pending_correction!(build(:commentaire, dossier:), :incomplete)
|
||||
end
|
||||
|
||||
travel_to DateTime.new(2023, 6, 5, 8) # 6 days elapsed
|
||||
end
|
||||
|
||||
it 'calculates the date, like if resolution will be today' do
|
||||
expect(subject).to eq(Date.new(2023, 8, 6))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when correction was for an incomplete dossier' do
|
||||
let!(:correction) do
|
||||
created_at = DateTime.new(2023, 5, 20, 15)
|
||||
resolved_at = DateTime.new(2023, 5, 25, 12)
|
||||
create(:dossier_correction, :incomplete, dossier:, created_at:, resolved_at:)
|
||||
end
|
||||
|
||||
it 'calculates the date by resetting delay' do
|
||||
expect(subject).to eq(Date.new(2023, 7, 26))
|
||||
end
|
||||
|
||||
context 'when there are multiple corrections' do
|
||||
let!(:correction2) do
|
||||
created_at = DateTime.new(2023, 5, 30, 18)
|
||||
resolved_at = DateTime.new(2023, 6, 3, 8)
|
||||
create(:dossier_correction, dossier:, created_at:, resolved_at:)
|
||||
end
|
||||
|
||||
it 'calculates the date based on SVA rules with all correction delays' do
|
||||
expect(subject).to eq(Date.new(2023, 7, 31))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sva resume setting is reset' do
|
||||
let(:config) { { decision: :sva, period: 2, unit: :months, resume: :reset } }
|
||||
|
||||
context 'there is no correction' do
|
||||
it 'calculates the date based on deposed_at' do
|
||||
expect(subject).to eq(Date.new(2023, 7, 16))
|
||||
end
|
||||
end
|
||||
|
||||
context 'there are multiple resolved correction' do
|
||||
before do
|
||||
created_at = DateTime.new(2023, 5, 16, 15)
|
||||
resolved_at = DateTime.new(2023, 5, 17, 12)
|
||||
create(:dossier_correction, :incomplete, dossier:, created_at:, resolved_at:)
|
||||
|
||||
created_at = DateTime.new(2023, 5, 20, 15)
|
||||
resolved_at = DateTime.new(2023, 5, 25, 12)
|
||||
create(:dossier_correction, dossier:, created_at:, resolved_at:)
|
||||
end
|
||||
|
||||
it 'calculates the date based on SVA rules from the last resolved date' do
|
||||
expect(subject).to eq(Date.new(2023, 7, 26))
|
||||
end
|
||||
end
|
||||
|
||||
context 'there is a pending correction' do
|
||||
before do
|
||||
travel_to DateTime.new(2023, 5, 30, 18) do
|
||||
dossier.flag_as_pending_correction!(build(:commentaire, dossier:))
|
||||
end
|
||||
|
||||
travel_to DateTime.new(2023, 6, 5, 8)
|
||||
end
|
||||
|
||||
it 'calculates the date, like if resolution will be today and delay restarted' do
|
||||
expect(subject).to eq(Date.new(2023, 8, 6))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#decision_date_from_today' do
|
||||
let(:config) { { decision: :sva, period: 2, unit: :months, resume: :continue } }
|
||||
before { travel_to DateTime.new(2023, 4, 15, 12) }
|
||||
|
||||
subject { described_class.decision_date_from_today(procedure) }
|
||||
|
||||
it 'calculates the date based on today' do
|
||||
expect(subject).to eq(Date.new(2023, 6, 16))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -21,6 +21,15 @@ describe "procedure filters" do
|
|||
end
|
||||
end
|
||||
|
||||
scenario "should display sva by default if procedure has sva enabled" do
|
||||
procedure.update!(sva_svr: SVASVRConfiguration.new(decision: :sva).attributes)
|
||||
visit instructeur_procedure_path(procedure)
|
||||
within ".dossiers-table" do
|
||||
expect(page).to have_link("Date décision SVA")
|
||||
expect(page).to have_link(new_unfollow_dossier.user.email)
|
||||
end
|
||||
end
|
||||
|
||||
scenario "should list all dossiers" do
|
||||
within ".dossiers-table" do
|
||||
expect(page).to have_link(new_unfollow_dossier.id.to_s)
|
||||
|
|
|
@ -31,6 +31,28 @@ describe "procedure sort", js: true do
|
|||
expect(find(".dossiers-table tbody tr:nth-child(3) .number-col a").text).to eq(followed_dossier.id.to_s)
|
||||
end
|
||||
|
||||
scenario "should be able to sort with header with sva date" do
|
||||
procedure.update!(sva_svr: SVASVRConfiguration.new(decision: :sva).attributes)
|
||||
followed_dossier_2.update!(sva_svr_decision_on: Date.tomorrow)
|
||||
followed_dossier.update!(sva_svr_decision_on: Date.today)
|
||||
|
||||
visit instructeur_procedure_path(procedure, statut: "suivis")
|
||||
# sorted by notifications (updated_at desc) by default, filtered by followed
|
||||
expect(all(".dossiers-table tbody tr").count).to eq(3)
|
||||
expect(find(".dossiers-table tbody tr:nth-child(2) .number-col a").text).to eq(followed_dossier.id.to_s)
|
||||
expect(find(".dossiers-table tbody tr:nth-child(3) .number-col a").text).to eq(followed_dossier_2.id.to_s)
|
||||
|
||||
find("thead .sva-col a").click # sort by sva date asc
|
||||
|
||||
expect(find(".dossiers-table tbody tr:nth-child(2) .number-col a").text).to eq(followed_dossier.id.to_s)
|
||||
expect(find(".dossiers-table tbody tr:nth-child(3) .number-col a").text).to eq(followed_dossier_2.id.to_s)
|
||||
|
||||
find("thead .sva-col a").click # reverse order - sort by sva date desc
|
||||
|
||||
expect(find(".dossiers-table tbody tr:nth-child(2) .number-col a").text).to eq(followed_dossier_2.id.to_s)
|
||||
expect(find(".dossiers-table tbody tr:nth-child(3) .number-col a").text).to eq(followed_dossier.id.to_s)
|
||||
end
|
||||
|
||||
scenario "should be able to sort with direct link to notification sort" do
|
||||
# the real input checkbox is hidden - DSFR set a fake checkbox with a label, so we can't use "check/uncheck" methods
|
||||
# but we can assert on the hidden checkbox state
|
||||
|
|
|
@ -11,6 +11,7 @@ describe 'shared/_procedure_description', type: :view do
|
|||
expect(rendered).to have_text(procedure.description)
|
||||
expect(rendered).to have_text('Temps de remplissage estimé')
|
||||
expect(rendered).not_to have_text('Quelles sont les pièces justificatives à fournir')
|
||||
expect(rendered).not_to have_text('Qu’est-ce que le cadre législatif « silence vaut accord » ?')
|
||||
end
|
||||
|
||||
context 'procedure with estimated duration not visible' do
|
||||
|
@ -70,4 +71,28 @@ describe 'shared/_procedure_description', type: :view do
|
|||
expect(rendered).to have_text('une description des pj manuelle')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the procedure is sva' do
|
||||
before { travel_to DateTime.new(2023, 1, 1) }
|
||||
let(:procedure) { create(:procedure, :published, :sva) }
|
||||
|
||||
it 'shows an explanation text' do
|
||||
subject
|
||||
expect(rendered).to have_text('Cette démarche applique le « Silence Vaut Accord »')
|
||||
expect(rendered).to have_text('dans les 2 mois')
|
||||
expect(rendered).to have_text("2 mars 2023")
|
||||
end
|
||||
|
||||
context 'when unit is weeks' do
|
||||
before {
|
||||
procedure.sva_svr["unit"] = "weeks"
|
||||
}
|
||||
|
||||
it 'shows an human period' do
|
||||
subject
|
||||
expect(rendered).to have_text('dans les 2 semaines')
|
||||
expect(rendered).to have_text("16 janvier 2023")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue