feat(svr): refuses automatically a dossier

This commit is contained in:
Colin Darie 2023-07-31 13:50:22 +02:00
parent 69e673e47e
commit e8000adecf
No known key found for this signature in database
GPG key ID: 8C76CADD40253590
5 changed files with 182 additions and 2 deletions

View file

@ -96,6 +96,12 @@ class Dossier < ApplicationRecord
processed_at: processed_at) processed_at: processed_at)
end end
def refuser_automatiquement(processed_at: Time.zone.now, motivation:)
build(state: Dossier.states.fetch(:refuse),
motivation: motivation,
processed_at: processed_at)
end
def classer_sans_suite(motivation: nil, instructeur: nil, processed_at: Time.zone.now) def classer_sans_suite(motivation: nil, instructeur: nil, processed_at: Time.zone.now)
build(state: Dossier.states.fetch(:sans_suite), build(state: Dossier.states.fetch(:sans_suite),
instructeur_email: instructeur&.email, instructeur_email: instructeur&.email,
@ -177,6 +183,10 @@ class Dossier < ApplicationRecord
transitions from: :en_instruction, to: :refuse, guard: :can_terminer? transitions from: :en_instruction, to: :refuse, guard: :can_terminer?
end end
event :refuser_automatiquement, after: :after_refuser_automatiquement do
transitions from: :en_instruction, to: :refuse, guard: :can_refuser_automatiquement?
end
event :classer_sans_suite, after: :after_classer_sans_suite do event :classer_sans_suite, after: :after_classer_sans_suite do
transitions from: :en_instruction, to: :sans_suite, guard: :can_terminer? transitions from: :en_instruction, to: :sans_suite, guard: :can_terminer?
end end
@ -526,7 +536,14 @@ class Dossier < ApplicationRecord
def can_accepter_automatiquement? def can_accepter_automatiquement?
return false unless can_terminer? return false unless can_terminer?
return true if declarative_triggered_at.nil? && procedure.declarative_accepte? && en_construction? 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?) return true if procedure.sva? && can_terminer_automatiquement_by_sva_svr?
false
end
def can_refuser_automatiquement?
return false unless can_terminer?
return true if procedure.svr? && can_terminer_automatiquement_by_sva_svr?
false false
end end
@ -559,6 +576,10 @@ class Dossier < ApplicationRecord
termine? || reason == :procedure_removed termine? || reason == :procedure_removed
end end
def can_terminer_automatiquement_by_sva_svr?
sva_svr_decision_triggered_at.nil? && !pending_correction? && (sva_svr_decision_on.today? || sva_svr_decision_on.past?)
end
def any_etablissement_as_degraded_mode? def any_etablissement_as_degraded_mode?
return true if etablissement&.as_degraded_mode? return true if etablissement&.as_degraded_mode?
return true if champs_public_all.any? { _1.etablissement&.as_degraded_mode? } return true if champs_public_all.any? { _1.etablissement&.as_degraded_mode? }
@ -996,7 +1017,7 @@ class Dossier < ApplicationRecord
if procedure.declarative_accepte? if procedure.declarative_accepte?
self.en_instruction_at = self.processed_at self.en_instruction_at = self.processed_at
self.declarative_triggered_at = self.processed_at self.declarative_triggered_at = self.processed_at
elsif procedure.sva_svr_enabled? elsif procedure.sva?
self.sva_svr_decision_triggered_at = self.processed_at self.sva_svr_decision_triggered_at = self.processed_at
end end
@ -1040,6 +1061,23 @@ class Dossier < ApplicationRecord
log_dossier_operation(instructeur, :refuser, self) log_dossier_operation(instructeur, :refuser, self)
end end
def after_refuser_automatiquement
# Only SVR can refuse automatically
I18n.with_locale(user.locale || I18n.default_locale) do
self.motivation = I18n.t("shared.dossiers.motivation.refused_by_svr")
end
self.processed_at = traitements.refuser_automatiquement(motivation:).processed_at
self.sva_svr_decision_triggered_at = self.processed_at
save!
remove_titres_identite!
MailTemplatePresenterService.create_commentaire_for_state(self)
NotificationMailer.send_refuse_notification(self).deliver_later
log_automatic_dossier_operation(:refuser, self)
end
def after_classer_sans_suite(h) def after_classer_sans_suite(h)
instructeur = h[:instructeur] instructeur = h[:instructeur]
motivation = h[:motivation] motivation = h[:motivation]
@ -1090,6 +1128,8 @@ class Dossier < ApplicationRecord
passer_automatiquement_en_instruction! passer_automatiquement_en_instruction!
elsif en_instruction? && procedure.sva? && may_accepter_automatiquement? elsif en_instruction? && procedure.sva? && may_accepter_automatiquement?
accepter_automatiquement! accepter_automatiquement!
elsif en_instruction? && procedure.svr? && may_refuser_automatiquement?
refuser_automatiquement!
elsif will_save_change_to_sva_svr_decision_on? 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 save! # we always want the most up to date decision when there is a pending correction
end end

View file

@ -5,6 +5,8 @@ en:
details_no_name: "The file was submitted by a FranceConnect account." details_no_name: "The file was submitted by a FranceConnect account."
details: "The file was submitted by the account of %{name}." details: "The file was submitted by the account of %{name}."
details_updated: "The file was submitted by the account of %{name}, authenticated by FranceConnect on %{date}." details_updated: "The file was submitted by the account of %{name}, authenticated by FranceConnect on %{date}."
motivation:
refused_by_svr: "The service handling your file was unable to process it within the time limit set by the Silence Vaut Rejet law."
header: header:
expires_at: expires_at:
brouillon: "Expires on %{date} (%{duree_conservation_totale} months after this file was created)" brouillon: "Expires on %{date} (%{duree_conservation_totale} months after this file was created)"

View file

@ -5,6 +5,8 @@ fr:
details_no_name: "Le dossier a été déposé par un compte FranceConnect." details_no_name: "Le dossier a été déposé par un compte FranceConnect."
details: "Le dossier a été déposé par le compte de %{name}." details: "Le dossier a été déposé par le compte de %{name}."
details_updated: "Le dossier a été déposé par le compte de %{name}, authentifié par FranceConnect le %{date}." details_updated: "Le dossier a été déposé par le compte de %{name}, authentifié par FranceConnect le %{date}."
motivation:
refused_by_svr: "Le service traitant na pas été en mesure de traiter votre demande dans le délai imparti par la règle du Silence Vaut Rejet."
header: header:
expires_at: expires_at:
brouillon: "Expirera le %{date} (%{duree_conservation_totale} mois après la création du dossier)" brouillon: "Expirera le %{date} (%{duree_conservation_totale} mois après la création du dossier)"

View file

@ -48,6 +48,42 @@ RSpec.describe ProcedureSVASVRProcessDossierJob, type: :job do
end end
end end
context 'when procedure is SVR' do
let(:procedure) { create(:procedure, :published, :svr, :for_individual) }
it 'should refuse dossier' do
expect(subject.sva_svr_decision_on).to eq(Date.current)
expect(subject).to be_refuse
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 refuses 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 refuses 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 context 'when dossier was submitted before sva was enabled' do
let!(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure:, depose_at: 2.months.ago) } let!(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure:, depose_at: 2.months.ago) }

View file

@ -1103,6 +1103,46 @@ describe Dossier, type: :model do
end end
end end
describe '#refuser_automatiquement' do
context 'as svr procedure' do
let(:last_operation) { dossier.dossier_operation_logs.last }
let(:procedure) { create(:procedure, :for_individual, :published, :svr) }
let(:dossier) { create(:dossier, :en_instruction, :with_individual, procedure:, sva_svr_decision_on: Date.current, en_instruction_at: DateTime.new(2021, 5, 1, 12)) }
before {
freeze_time
allow(NotificationMailer).to receive(:send_refuse_notification).and_return(double(deliver_later: true))
}
subject {
dossier.refuser_automatiquement!
dossier.reload
}
it 'refuses dossier automatiquement' do
expect(subject.en_instruction_at).to eq(DateTime.new(2021, 5, 1, 12))
expect(subject.processed_at).to eq(Time.current)
expect(subject.declarative_triggered_at).to be_nil
expect(subject.sva_svr_decision_triggered_at).to eq(Time.current)
expect(subject.motivation).to include("dans le délai imparti")
expect(subject).to be_refuse
expect(last_operation.operation).to eq('refuser')
expect(last_operation.automatic_operation?).to be_truthy
expect(NotificationMailer).to have_received(:send_refuse_notification).with(dossier)
expect(subject.attestation).to be_nil
expect(dossier.commentaires.count).to eq(1)
end
context 'for an user having english locale' do
before { dossier.user.update!(locale: 'en') }
it 'translates the motivation' do
expect(subject.motivation).to include('within the time limit')
end
end
end
end
describe '#passer_en_instruction!' do describe '#passer_en_instruction!' do
let(:dossier) { create(:dossier, :en_construction, en_construction_close_to_expiration_notice_sent_at: Time.zone.now) } let(:dossier) { create(:dossier, :en_construction, en_construction_close_to_expiration_notice_sent_at: Time.zone.now) }
let(:last_operation) { dossier.dossier_operation_logs.last } let(:last_operation) { dossier.dossier_operation_logs.last }
@ -1324,6 +1364,60 @@ describe Dossier, type: :model do
end end
end end
describe '#can_refuser_automatiquement?' do
let(:dossier) { create(:dossier, state: initial_state) }
let(:initial_state) { :en_instruction }
it { expect(dossier.can_refuser_automatiquement?).to be_falsey }
context 'when procedure is sva/svr' do
let(:decision) { :svr }
before do
dossier.procedure.update!(sva_svr: SVASVRConfiguration.new(decision:).attributes)
dossier.update!(sva_svr_decision_on: Date.current)
end
it { expect(dossier.can_refuser_automatiquement?).to be_truthy }
context 'when procedure is svr' do
let(:decision) { :svr }
before do
dossier.procedure.update!(sva_svr: SVASVRConfiguration.new(decision:).attributes)
dossier.update!(sva_svr_decision_on: Date.current)
end
it { expect(dossier.can_refuser_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_refuser_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_refuser_automatiquement?).to be_falsey }
end
context 'when decision is sva' do
let(:decision) { :sva }
it { expect(dossier.can_refuser_automatiquement?).to be_falsey }
end
context 'when dossier was already processed by svr' do
before { dossier.update!(sva_svr_decision_triggered_at: 1.hour.ago) }
it { expect(dossier.can_refuser_automatiquement?).to be_falsey }
end
end
end
end
describe "can't transition to terminer when etablissement is in degraded mode" do describe "can't transition to terminer when etablissement is in degraded mode" do
let(:instructeur) { create(:instructeur) } let(:instructeur) { create(:instructeur) }
let(:motivation) { 'motivation' } let(:motivation) { 'motivation' }
@ -1957,6 +2051,12 @@ describe Dossier, type: :model do
it { expect(dossier.spreadsheet_columns(types_de_champ: [])).to include(["Date SVA", :sva_svr_decision_on]) } it { expect(dossier.spreadsheet_columns(types_de_champ: [])).to include(["Date SVA", :sva_svr_decision_on]) }
end end
context 'procedure svr' do
let(:dossier) { create(:dossier, :en_instruction, procedure: create(:procedure, :svr)) }
it { expect(dossier.spreadsheet_columns(types_de_champ: [])).to include(["Date SVR", :sva_svr_decision_on]) }
end
end end
describe '#processed_in_month' do describe '#processed_in_month' do