refactor(demarche): make declarative demarche processing syncroneous

This commit is contained in:
Paul Chavard 2022-12-02 18:15:43 +01:00
parent 78c85ee8c4
commit ed4d5cb36a
7 changed files with 42 additions and 181 deletions

View file

@ -165,6 +165,7 @@ module Users
if errors.blank? if errors.blank?
@dossier.passer_en_construction! @dossier.passer_en_construction!
@dossier.process_declarative!
NotificationMailer.send_en_construction_notification(@dossier).deliver_later NotificationMailer.send_en_construction_notification(@dossier).deliver_later
@dossier.groupe_instructeur.instructeurs.with_instant_email_dossier_notifications.each do |instructeur| @dossier.groupe_instructeur.instructeurs.with_instant_email_dossier_notifications.each do |instructeur|
DossierMailer.notify_new_dossier_depose_to_instructeur(@dossier, instructeur.email).deliver_later DossierMailer.notify_new_dossier_depose_to_instructeur(@dossier, instructeur.email).deliver_later

View file

@ -1,13 +0,0 @@
class Cron::DeclarativeProceduresJob < Cron::CronJob
self.schedule_expression = "every 1 minute"
def perform(*args)
Procedure.declarative.find_each do |procedure|
begin
procedure.process_dossiers!
rescue => e
Sentry.capture_exception(e)
end
end
end
end

View file

@ -169,7 +169,7 @@ class Dossier < ApplicationRecord
end end
event :passer_automatiquement_en_instruction, after: :after_passer_automatiquement_en_instruction do event :passer_automatiquement_en_instruction, after: :after_passer_automatiquement_en_instruction do
transitions from: :en_construction, to: :en_instruction transitions from: :en_construction, to: :en_instruction, guard: :can_passer_automatiquement_en_instruction?
end end
event :repasser_en_construction, after: :after_repasser_en_construction do event :repasser_en_construction, after: :after_repasser_en_construction do
@ -181,7 +181,7 @@ class Dossier < ApplicationRecord
end end
event :accepter_automatiquement, after: :after_accepter_automatiquement do event :accepter_automatiquement, after: :after_accepter_automatiquement do
transitions from: :en_construction, to: :accepte, guard: :can_terminer? transitions from: :en_construction, to: :accepte, guard: :can_accepter_automatiquement?
end end
event :refuser, after: :after_refuser do event :refuser, after: :after_refuser do
@ -526,6 +526,14 @@ class Dossier < ApplicationRecord
true true
end end
def can_accepter_automatiquement?
declarative_triggered_at.nil? && can_terminer?
end
def can_passer_automatiquement_en_instruction?
declarative_triggered_at.nil?
end
def can_repasser_en_instruction? def can_repasser_en_instruction?
termine? && !user_deleted? termine? && !user_deleted?
end end
@ -978,6 +986,14 @@ class Dossier < ApplicationRecord
log_dossier_operation(instructeur, :classer_sans_suite, self) log_dossier_operation(instructeur, :classer_sans_suite, self)
end end
def process_declarative!
if procedure.declarative_accepte? && may_accepter_automatiquement?
accepter_automatiquement!
elsif procedure.declarative_en_instruction? && may_passer_automatiquement_en_instruction?
passer_automatiquement_en_instruction!
end
end
def remove_titres_identite! def remove_titres_identite!
champs_public.filter(&:titre_identite?).map(&:piece_justificative_file).each(&:purge_later) champs_public.filter(&:titre_identite?).map(&:piece_justificative_file).each(&:purge_later)
end end

View file

@ -447,6 +447,10 @@ class Procedure < ApplicationRecord
declarative_with_state == Procedure.declarative_with_states.fetch(:accepte) declarative_with_state == Procedure.declarative_with_states.fetch(:accepte)
end end
def declarative_en_instruction?
declarative_with_state == Procedure.declarative_with_states.fetch(:en_instruction)
end
def self.declarative_attributes_for_select def self.declarative_attributes_for_select
declarative_with_states.map do |state, _| declarative_with_states.map do |state, _|
[I18n.t("activerecord.attributes.#{model_name.i18n_key}.declarative_with_state/#{state}"), state] [I18n.t("activerecord.attributes.#{model_name.i18n_key}.declarative_with_state/#{state}"), state]
@ -635,23 +639,6 @@ class Procedure < ApplicationRecord
result result
end end
def process_dossiers!
case declarative_with_state
when Procedure.declarative_with_states.fetch(:en_instruction)
dossiers
.state_en_construction
.where(declarative_triggered_at: nil)
.find_each(&:passer_automatiquement_en_instruction!)
when Procedure.declarative_with_states.fetch(:accepte)
dossiers
.state_en_construction
.where(declarative_triggered_at: nil)
.find_each do |dossier|
dossier.accepter_automatiquement! if dossier.may_accepter_automatiquement?
end
end
end
def logo_url def logo_url
if logo.attached? if logo.attached?
Rails.application.routes.url_helpers.url_for(logo) Rails.application.routes.url_helpers.url_for(logo)

View file

@ -1,124 +0,0 @@
RSpec.describe Cron::DeclarativeProceduresJob, type: :job do
describe "perform" do
let(:date) { Time.utc(2017, 9, 1, 10, 5, 0) }
let(:instruction_date) { date + 120 }
let(:state) { nil }
let(:procedure) { create(:procedure, :published, :for_individual, :with_instructeur, declarative_with_state: state) }
let(:nouveau_dossier1) { create(:dossier, :en_construction, :with_individual, :with_attestation, procedure: procedure) }
let(:nouveau_dossier2) { create(:dossier, :en_construction, :with_individual, :with_attestation, procedure: procedure) }
let(:dossier_recu) { create(:dossier, :en_instruction, :with_individual, procedure: procedure) }
let(:dossier_brouillon) { create(:dossier, procedure: procedure) }
let(:dossier_repasse_en_construction) { create(:dossier, :en_construction, :with_individual, procedure: procedure) }
before do
Timecop.freeze(date)
dossier_repasse_en_construction&.touch(:declarative_triggered_at)
end
subject(:perform_job) do
dossiers = [
nouveau_dossier1,
nouveau_dossier2,
dossier_recu,
dossier_brouillon,
dossier_repasse_en_construction
].compact
Cron::DeclarativeProceduresJob.new.perform
dossiers.each(&:reload)
end
after { Timecop.return }
context "with some dossiers" do
context "en_construction" do
let(:state) { Dossier.states.fetch(:en_instruction) }
let(:last_operation) { nouveau_dossier1.dossier_operation_logs.last }
it {
perform_job
expect(nouveau_dossier1.en_instruction?).to be_truthy
expect(nouveau_dossier1.en_instruction_at).to eq(date)
expect(last_operation.operation).to eq('passer_en_instruction')
expect(last_operation.automatic_operation?).to be_truthy
expect(nouveau_dossier2.en_instruction?).to be_truthy
expect(nouveau_dossier2.en_instruction_at).to eq(date)
expect(dossier_recu.en_instruction?).to be_truthy
expect(dossier_recu.en_instruction_at).to eq(instruction_date)
expect(dossier_brouillon.brouillon?).to be_truthy
expect(dossier_brouillon.en_instruction_at).to eq(nil)
expect(dossier_repasse_en_construction.en_construction?).to be_truthy
}
end
context "accepte" do
let(:state) { Dossier.states.fetch(:accepte) }
let(:last_operation) { nouveau_dossier1.dossier_operation_logs.last }
it {
perform_job
expect(nouveau_dossier1.accepte?).to be true
expect(nouveau_dossier1.en_instruction_at).to eq(date)
expect(nouveau_dossier1.processed_at).to eq(date)
expect(nouveau_dossier1.attestation).to be_present
expect(last_operation.operation).to eq('accepter')
expect(last_operation.automatic_operation?).to be_truthy
expect(nouveau_dossier2.accepte?).to be true
expect(nouveau_dossier2.en_instruction_at).to eq(date)
expect(nouveau_dossier2.processed_at).to eq(date)
expect(nouveau_dossier2.attestation).to be_present
expect(dossier_recu.en_instruction?).to be true
expect(dossier_recu.en_instruction_at).to eq(instruction_date)
expect(dossier_recu.processed_at).to eq(nil)
expect(dossier_brouillon.brouillon?).to be true
expect(dossier_brouillon.en_instruction_at).to eq(nil)
expect(dossier_brouillon.processed_at).to eq(nil)
}
context "having etablissement in degraded_mode" do
let(:procedure) { create(:procedure, :published, :with_instructeur, for_individual: false, declarative_with_state: state) }
let(:nouveau_dossier1) { create(:dossier, :en_construction, :with_entreprise, :with_attestation, procedure: procedure, as_degraded_mode: false) }
let(:nouveau_dossier2) { create(:dossier, :en_construction, :with_entreprise, :with_attestation, procedure: procedure, as_degraded_mode: true) }
let(:dossier_recu) { nil }
let(:dossier_repasse_en_construction) { nil }
before do
expect(nouveau_dossier2).to_not receive(:accepter_automatiquement)
expect(Sentry).to_not receive(:capture_exception)
end
it {
perform_job
expect(nouveau_dossier1).to be_accepte
expect(nouveau_dossier2).to be_en_construction
}
end
end
end
end
describe 'safer perform' do
let(:state) { Dossier.states.fetch(:en_instruction) }
it 'works no matter if one raise' do
procedure_1 = instance_double("Procedure")
expect(procedure_1).to receive(:process_dossiers!)
procedure_2 = instance_double("Procedure")
expect(procedure_2).to receive(:process_dossiers!).and_raise("boom")
procedure_3 = double(process_dossiers!: true)
expect(procedure_3).to receive(:process_dossiers!)
expect(Procedure).to receive_message_chain(:declarative, :find_each).and_yield(procedure_1).and_yield(procedure_2).and_yield(procedure_3)
Cron::DeclarativeProceduresJob.perform_now
end
end
end

View file

@ -186,7 +186,7 @@ describe Instructeur, type: :model do
end end
describe '#notifications_for_dossier' do describe '#notifications_for_dossier' do
let!(:dossier) { create(:dossier, :followed, state: Dossier.states.fetch(:en_construction)) } let!(:dossier) { create(:dossier, :en_construction, :followed) }
let(:instructeur) { dossier.follows.first.instructeur } let(:instructeur) { dossier.follows.first.instructeur }
subject { instructeur.notifications_for_dossier(dossier) } subject { instructeur.notifications_for_dossier(dossier) }
@ -245,12 +245,12 @@ describe Instructeur, type: :model do
# a procedure, one group, 2 instructeurs # a procedure, one group, 2 instructeurs
let(:procedure) { create(:simple_procedure, :routee, :with_type_de_champ_private, :for_individual) } let(:procedure) { create(:simple_procedure, :routee, :with_type_de_champ_private, :for_individual) }
let(:gi_p1) { procedure.groupe_instructeurs.last } let(:gi_p1) { procedure.groupe_instructeurs.last }
let!(:dossier) { create(:dossier, :with_individual, :followed, procedure: procedure, groupe_instructeur: gi_p1, state: Dossier.states.fetch(:en_construction)) } let!(:dossier) { create(:dossier, :en_construction, :with_individual, :followed, procedure: procedure, groupe_instructeur: gi_p1) }
let(:instructeur) { dossier.follows.first.instructeur } let(:instructeur) { dossier.follows.first.instructeur }
let!(:instructeur_2) { create(:instructeur, groupe_instructeurs: [gi_p1]) } let!(:instructeur_2) { create(:instructeur, groupe_instructeurs: [gi_p1]) }
# another procedure, dossier followed by a third instructeur # another procedure, dossier followed by a third instructeur
let!(:dossier_on_procedure_2) { create(:dossier, :followed, state: Dossier.states.fetch(:en_construction)) } let!(:dossier_on_procedure_2) { create(:dossier, :en_construction, :followed) }
let!(:instructeur_on_procedure_2) { dossier_on_procedure_2.follows.first.instructeur } let!(:instructeur_on_procedure_2) { dossier_on_procedure_2.follows.first.instructeur }
let(:gi_p2) { dossier.groupe_instructeur } let(:gi_p2) { dossier.groupe_instructeur }
@ -355,7 +355,7 @@ describe Instructeur, type: :model do
end end
describe '#procedure_ids_with_notifications' do describe '#procedure_ids_with_notifications' do
let!(:dossier) { create(:dossier, :followed, state: Dossier.states.fetch(:en_construction)) } let!(:dossier) { create(:dossier, :en_construction, :followed) }
let(:instructeur) { dossier.follows.first.instructeur } let(:instructeur) { dossier.follows.first.instructeur }
let(:procedure) { dossier.procedure } let(:procedure) { dossier.procedure }
@ -369,7 +369,7 @@ describe Instructeur, type: :model do
end end
describe '#mark_tab_as_seen' do describe '#mark_tab_as_seen' do
let!(:dossier) { create(:dossier, :followed, state: Dossier.states.fetch(:en_construction)) } let!(:dossier) { create(:dossier, :en_construction, :followed) }
let(:instructeur) { dossier.follows.first.instructeur } let(:instructeur) { dossier.follows.first.instructeur }
let(:freeze_date) { Time.zone.parse('12/12/2012') } let(:freeze_date) { Time.zone.parse('12/12/2012') }
@ -416,7 +416,7 @@ describe Instructeur, type: :model do
end end
context 'when a dossier in construction exists' do context 'when a dossier in construction exists' do
let!(:dossier) { create(:dossier, procedure: procedure_to_assign, state: Dossier.states.fetch(:en_construction)) } let!(:dossier) { create(:dossier, :en_construction, procedure: procedure_to_assign) }
it do it do
expect(instructeur.email_notification_data).to eq([ expect(instructeur.email_notification_data).to eq([
@ -454,12 +454,11 @@ describe Instructeur, type: :model do
end end
context 'when a declarated dossier in instruction exists' do context 'when a declarated dossier in instruction exists' do
let!(:dossier) { create(:dossier, procedure: procedure_to_assign, state: Dossier.states.fetch(:en_construction)) } let(:dossier) { create(:dossier, :en_construction, procedure: procedure_to_assign) }
before do before do
procedure_to_assign.update(declarative_with_state: "en_instruction") procedure_to_assign.update!(declarative_with_state: "en_instruction")
Cron::DeclarativeProceduresJob.new.perform dossier.process_declarative!
dossier.reload
end end
it { expect(procedure_to_assign.declarative_with_state).to eq("en_instruction") } it { expect(procedure_to_assign.declarative_with_state).to eq("en_instruction") }
@ -479,12 +478,11 @@ describe Instructeur, type: :model do
end end
context 'when a declarated dossier in accepte processed at today exists' do context 'when a declarated dossier in accepte processed at today exists' do
let!(:dossier) { create(:dossier, procedure: procedure_to_assign, state: Dossier.states.fetch(:en_construction)) } let(:dossier) { create(:dossier, :en_construction, procedure: procedure_to_assign) }
before do before do
procedure_to_assign.update(declarative_with_state: "accepte") procedure_to_assign.update(declarative_with_state: "accepte")
Cron::DeclarativeProceduresJob.new.perform dossier.process_declarative!
dossier.reload
end end
it { expect(procedure_to_assign.declarative_with_state).to eq("accepte") } it { expect(procedure_to_assign.declarative_with_state).to eq("accepte") }
@ -496,13 +494,12 @@ describe Instructeur, type: :model do
end end
context 'when a declarated dossier in accepte processed at yesterday exists' do context 'when a declarated dossier in accepte processed at yesterday exists' do
let!(:dossier) { create(:dossier, procedure: procedure_to_assign, state: Dossier.states.fetch(:en_construction)) } let(:dossier) { create(:dossier, :en_construction, procedure: procedure_to_assign) }
before do before do
procedure_to_assign.update(declarative_with_state: "accepte") procedure_to_assign.update(declarative_with_state: "accepte")
Cron::DeclarativeProceduresJob.new.perform dossier.process_declarative!
dossier.traitements.last.update(processed_at: Time.zone.yesterday.beginning_of_day) dossier.traitements.last.update(processed_at: Time.zone.yesterday.beginning_of_day)
dossier.reload
end end
it { expect(procedure_to_assign.declarative_with_state).to eq("accepte") } it { expect(procedure_to_assign.declarative_with_state).to eq("accepte") }

View file

@ -48,11 +48,10 @@ describe NotificationService do
end end
context 'when a declarative dossier in instruction exists on this procedure' do context 'when a declarative dossier in instruction exists on this procedure' do
let!(:dossier) { create(:dossier, :en_construction, procedure: procedure) } let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
before do before do
procedure.update(declarative_with_state: "en_instruction") procedure.update(declarative_with_state: "en_instruction")
Cron::DeclarativeProceduresJob.new.perform dossier.process_declarative!
dossier.reload
end end
it do it do
@ -62,12 +61,11 @@ describe NotificationService do
end end
context 'when a declarative dossier in accepte on yesterday exists on this procedure' do context 'when a declarative dossier in accepte on yesterday exists on this procedure' do
let!(:dossier) { create(:dossier, :en_construction, procedure: procedure) } let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
before do before do
procedure.update(declarative_with_state: "accepte") procedure.update(declarative_with_state: "accepte")
Cron::DeclarativeProceduresJob.new.perform dossier.process_declarative!
dossier.traitements.last.update!(processed_at: Time.zone.yesterday.beginning_of_day) dossier.traitements.last.update!(processed_at: Time.zone.yesterday.beginning_of_day)
dossier.reload
end end
it do it do
@ -77,11 +75,10 @@ describe NotificationService do
end end
context 'when a declarative dossier in accepte on today exists on this procedure' do context 'when a declarative dossier in accepte on today exists on this procedure' do
let!(:dossier) { create(:dossier, :en_construction, procedure: procedure) } let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
before do before do
procedure.update(declarative_with_state: "accepte") procedure.update(declarative_with_state: "accepte")
Cron::DeclarativeProceduresJob.new.perform dossier.process_declarative!
dossier.reload
end end
it do it do