From f631acd11802a8fb3bd70ef8d94e0b25c14ab30a Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Wed, 1 Jul 2020 17:31:08 +0200 Subject: [PATCH 01/11] update state date in aasm after callbacks call aasm event methods, not state methods directly --- app/controllers/users/dossiers_controller.rb | 2 +- app/models/dossier.rb | 26 ++++----- .../instructeurs/dossiers_controller_spec.rb | 6 +- .../users/dossiers_controller_spec.rb | 2 +- spec/models/dossier_spec.rb | 57 ++++++++++++------- 5 files changed, 52 insertions(+), 41 deletions(-) diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 4d9c75b04..531a30db1 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -149,7 +149,7 @@ module Users errors = update_dossier_and_compute_errors if passage_en_construction? && errors.blank? - @dossier.en_construction! + @dossier.passer_en_construction! NotificationMailer.send_initiated_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 diff --git a/app/models/dossier.rb b/app/models/dossier.rb index b1898d2e2..5cd6cc178 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -282,7 +282,6 @@ class Dossier < ApplicationRecord delegate :types_de_champ, to: :procedure delegate :france_connect_information, to: :user - before_validation :update_state_dates, if: -> { state_changed? } before_save :build_default_champs, if: Proc.new { groupe_instructeur_id_was.nil? } before_save :update_search_terms @@ -508,20 +507,23 @@ class Dossier < ApplicationRecord end end + def after_passer_en_construction + update!(en_construction_at: Time.zone.now) if self.en_construction_at.nil? + end + def after_passer_en_instruction(instructeur) instructeur.follow(self) + update!(en_instruction_at: Time.zone.now) if self.en_instruction_at.nil? log_dossier_operation(instructeur, :passer_en_instruction) end def after_passer_automatiquement_en_instruction + update!(en_instruction_at: Time.zone.now) if self.en_instruction_at.nil? log_automatic_dossier_operation(:passer_en_instruction) end def after_repasser_en_construction(instructeur) - self.en_instruction_at = nil - - save! log_dossier_operation(instructeur, :repasser_en_construction) end @@ -529,6 +531,7 @@ class Dossier < ApplicationRecord self.archived = false self.processed_at = nil self.motivation = nil + self.en_instruction_at = Time.zone.now attestation&.destroy save! @@ -543,6 +546,8 @@ class Dossier < ApplicationRecord self.justificatif_motivation.attach(justificatif) end + self.processed_at = Time.zone.now + if attestation.nil? self.attestation = build_attestation end @@ -559,6 +564,7 @@ class Dossier < ApplicationRecord self.attestation = build_attestation end + self.processed_at = Time.zone.now save! NotificationMailer.send_closed_notification(self).deliver_later log_automatic_dossier_operation(:accepter, self) @@ -571,6 +577,7 @@ class Dossier < ApplicationRecord self.justificatif_motivation.attach(justificatif) end + self.processed_at = Time.zone.now save! NotificationMailer.send_refused_notification(self).deliver_later log_dossier_operation(instructeur, :refuser, self) @@ -583,6 +590,7 @@ class Dossier < ApplicationRecord self.justificatif_motivation.attach(justificatif) end + self.processed_at = Time.zone.now save! NotificationMailer.send_without_continuation_notification(self).deliver_later log_dossier_operation(instructeur, :classer_sans_suite, self) @@ -766,16 +774,6 @@ class Dossier < ApplicationRecord end end - def update_state_dates - if en_construction? && !self.en_construction_at - self.en_construction_at = Time.zone.now - elsif en_instruction? && !self.en_instruction_at - self.en_instruction_at = Time.zone.now - elsif TERMINE.include?(state) && !self.processed_at - self.processed_at = Time.zone.now - end - end - def send_dossier_received if saved_change_to_state? && en_instruction? && !procedure.declarative_accepte? NotificationMailer.send_dossier_received(self).deliver_later diff --git a/spec/controllers/instructeurs/dossiers_controller_spec.rb b/spec/controllers/instructeurs/dossiers_controller_spec.rb index 88a228c27..ec2d68386 100644 --- a/spec/controllers/instructeurs/dossiers_controller_spec.rb +++ b/spec/controllers/instructeurs/dossiers_controller_spec.rb @@ -194,7 +194,7 @@ describe Instructeurs::DossiersController, type: :controller do describe '#terminer' do context "with refuser" do before do - dossier.en_instruction! + dossier.passer_en_instruction!(instructeur) sign_in(instructeur.user) end @@ -235,7 +235,7 @@ describe Instructeurs::DossiersController, type: :controller do context "with classer_sans_suite" do before do - dossier.en_instruction! + dossier.passer_en_instruction!(instructeur) sign_in(instructeur.user) end context 'without attachment' do @@ -277,7 +277,7 @@ describe Instructeurs::DossiersController, type: :controller do context "with accepter" do before do - dossier.en_instruction! + dossier.passer_en_instruction!(instructeur) sign_in(instructeur.user) expect(NotificationMailer).to receive(:send_closed_notification) diff --git a/spec/controllers/users/dossiers_controller_spec.rb b/spec/controllers/users/dossiers_controller_spec.rb index b98d4a090..9dd0cdfd5 100644 --- a/spec/controllers/users/dossiers_controller_spec.rb +++ b/spec/controllers/users/dossiers_controller_spec.rb @@ -657,7 +657,7 @@ describe Users::DossiersController, type: :controller do let!(:invite) { create(:invite, dossier: dossier, user: user) } before do - dossier.en_construction! + dossier.passer_en_construction! subject end diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 4af2276c8..6cbf0503a 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -346,13 +346,14 @@ describe Dossier do let(:state) { Dossier.states.fetch(:brouillon) } let(:dossier) { create(:dossier, state: state) } let(:beginning_of_day) { Time.zone.now.beginning_of_day } + let(:instructeur) { create(:instructeur) } before { Timecop.freeze(beginning_of_day) } after { Timecop.return } context 'when dossier is en_construction' do before do - dossier.en_construction! + dossier.passer_en_construction! dossier.reload end @@ -361,8 +362,8 @@ describe Dossier do it 'should keep first en_construction_at date' do Timecop.return - dossier.en_instruction! - dossier.en_construction! + dossier.passer_en_instruction!(instructeur) + dossier.repasser_en_construction!(instructeur) expect(dossier.en_construction_at).to eq(beginning_of_day) end @@ -370,9 +371,10 @@ describe Dossier do context 'when dossier is en_instruction' do let(:state) { Dossier.states.fetch(:en_construction) } + let(:instructeur) { create(:instructeur) } before do - dossier.en_instruction! + dossier.passer_en_instruction!(instructeur) dossier.reload end @@ -381,39 +383,48 @@ describe Dossier do it 'should keep first en_instruction_at date if dossier is set to en_construction again' do Timecop.return - dossier.en_construction! - dossier.en_instruction! + dossier.repasser_en_construction!(instructeur) + dossier.passer_en_instruction!(instructeur) expect(dossier.en_instruction_at).to eq(beginning_of_day) end end - shared_examples 'dossier is processed' do |new_state| - before do - dossier.update(state: new_state) - dossier.reload - end - - it { expect(dossier.state).to eq(new_state) } - it { expect(dossier.processed_at).to eq(beginning_of_day) } - end - context 'when dossier is accepte' do let(:state) { Dossier.states.fetch(:en_instruction) } - it_behaves_like 'dossier is processed', Dossier.states.fetch(:accepte) + before do + dossier.accepter!(instructeur, nil, nil) + dossier.reload + end + + it { expect(dossier.state).to eq(Dossier.states.fetch(:accepte)) } + it { expect(dossier.processed_at).to eq(beginning_of_day) } + end context 'when dossier is refuse' do let(:state) { Dossier.states.fetch(:en_instruction) } - it_behaves_like 'dossier is processed', Dossier.states.fetch(:refuse) + before do + dossier.refuser!(instructeur, nil, nil) + dossier.reload + end + + it { expect(dossier.state).to eq(Dossier.states.fetch(:refuse)) } + it { expect(dossier.processed_at).to eq(beginning_of_day) } end context 'when dossier is sans_suite' do let(:state) { Dossier.states.fetch(:en_instruction) } - it_behaves_like 'dossier is processed', Dossier.states.fetch(:sans_suite) + before do + dossier.classer_sans_suite!(instructeur, nil, nil) + dossier.reload + end + + it { expect(dossier.state).to eq(Dossier.states.fetch(:sans_suite)) } + it { expect(dossier.processed_at).to eq(beginning_of_day) } end end @@ -478,13 +489,14 @@ describe Dossier do describe "#send_dossier_received" do let(:procedure) { create(:procedure) } let(:dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction)) } + let(:instructeur) { create(:instructeur) } before do allow(NotificationMailer).to receive(:send_dossier_received).and_return(double(deliver_later: nil)) end it "sends an email when the dossier becomes en_instruction" do - dossier.en_instruction! + dossier.passer_en_instruction!(instructeur) expect(NotificationMailer).to have_received(:send_dossier_received).with(dossier) end @@ -777,6 +789,7 @@ describe Dossier do describe 'webhook' do let(:dossier) { create(:dossier) } + let(:instructeur) { create(:instructeur) } it 'should not call webhook' do expect { @@ -792,7 +805,7 @@ describe Dossier do }.to_not have_enqueued_job(WebHookJob) expect { - dossier.en_construction! + dossier.passer_en_construction! }.to have_enqueued_job(WebHookJob) expect { @@ -800,7 +813,7 @@ describe Dossier do }.to_not have_enqueued_job(WebHookJob) expect { - dossier.en_instruction! + dossier.passer_en_instruction!(instructeur) }.to have_enqueued_job(WebHookJob) end end From a072d3521151b696f79017d3bbde429b13db6b85 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Thu, 2 Jul 2020 11:02:50 +0200 Subject: [PATCH 02/11] use traitement model when a dossier is terminated (accepte, refuse or classe_sans_suite), we store now `processed_at` and `motivation` in a traitement instance --- .rubocop.yml | 1 + app/models/dossier.rb | 25 +++++++++++-------- app/models/instructeur.rb | 2 +- app/models/traitement.rb | 4 +++ app/views/dossiers/show.pdf.prawn | 2 +- .../20200630140356_create_traitements.rb | 11 ++++++++ db/schema.rb | 14 ++++++++++- ...0154829_add_traitements_from_dossiers.rake | 18 +++++++++++++ spec/factories/dossier.rb | 15 +++++++---- .../concern/tags_substitution_concern_spec.rb | 13 +++++++--- spec/models/dossier_spec.rb | 22 ++++++++++++---- spec/models/instructeur_spec.rb | 2 +- spec/models/traitement_spec.rb | 5 ++++ spec/services/notification_service_spec.rb | 2 +- 14 files changed, 107 insertions(+), 29 deletions(-) create mode 100644 app/models/traitement.rb create mode 100644 db/migrate/20200630140356_create_traitements.rb create mode 100644 lib/tasks/deployment/20200630154829_add_traitements_from_dossiers.rake create mode 100644 spec/models/traitement_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml index bdeea2d8f..1ab77533c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -689,6 +689,7 @@ Rails/CreateTableWithTimestamps: - db/migrate/2016*.rb - db/migrate/2017*.rb - db/migrate/2018*.rb + - db/migrate/20200630140356_create_traitements.rb Rails/Date: Enabled: false diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 5cd6cc178..c15644939 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -42,6 +42,7 @@ class Dossier < ApplicationRecord has_many :followers_instructeurs, through: :follows, source: :instructeur has_many :previous_followers_instructeurs, -> { distinct }, through: :previous_follows, source: :instructeur has_many :avis, inverse_of: :dossier, dependent: :destroy + has_many :traitements, -> { order(:processed_at) }, inverse_of: :dossier, dependent: :destroy has_many :dossier_operation_logs, -> { order(:created_at) }, dependent: :nullify, inverse_of: :dossier @@ -293,6 +294,16 @@ class Dossier < ApplicationRecord validates :individual, presence: true, if: -> { procedure.for_individual? } validates :groupe_instructeur, presence: true + def motivation + return nil if !termine? + traitements.any? ? traitements.last.motivation : read_attribute(:motivation) + end + + def processed_at + return nil if !termine? + traitements.any? ? traitements.last.processed_at : read_attribute(:processed_at) + end + def update_search_terms self.search_terms = [ user&.email, @@ -529,8 +540,6 @@ class Dossier < ApplicationRecord def after_repasser_en_instruction(instructeur) self.archived = false - self.processed_at = nil - self.motivation = nil self.en_instruction_at = Time.zone.now attestation&.destroy @@ -540,14 +549,12 @@ class Dossier < ApplicationRecord end def after_accepter(instructeur, motivation, justificatif = nil) - self.motivation = motivation + self.traitements.build(state: Dossier.states.fetch(:accepte), instructeur: instructeur, motivation: motivation, processed_at: Time.zone.now) if justificatif self.justificatif_motivation.attach(justificatif) end - self.processed_at = Time.zone.now - if attestation.nil? self.attestation = build_attestation end @@ -558,39 +565,37 @@ class Dossier < ApplicationRecord end def after_accepter_automatiquement + self.traitements.build(state: Dossier.states.fetch(:accepte), instructeur: nil, motivation: nil, processed_at: Time.zone.now) self.en_instruction_at ||= Time.zone.now if attestation.nil? self.attestation = build_attestation end - self.processed_at = Time.zone.now save! NotificationMailer.send_closed_notification(self).deliver_later log_automatic_dossier_operation(:accepter, self) end def after_refuser(instructeur, motivation, justificatif = nil) - self.motivation = motivation + self.traitements.build(state: Dossier.states.fetch(:refuse), instructeur: instructeur, motivation: motivation, processed_at: Time.zone.now) if justificatif self.justificatif_motivation.attach(justificatif) end - self.processed_at = Time.zone.now save! NotificationMailer.send_refused_notification(self).deliver_later log_dossier_operation(instructeur, :refuser, self) end def after_classer_sans_suite(instructeur, motivation, justificatif = nil) - self.motivation = motivation + self.traitements.build(state: Dossier.states.fetch(:sans_suite), instructeur: instructeur, motivation: motivation, processed_at: Time.zone.now) if justificatif self.justificatif_motivation.attach(justificatif) end - self.processed_at = Time.zone.now save! NotificationMailer.send_without_continuation_notification(self).deliver_later log_dossier_operation(instructeur, :classer_sans_suite, self) diff --git a/app/models/instructeur.rb b/app/models/instructeur.rb index 27fc5223e..b1e6db2a1 100644 --- a/app/models/instructeur.rb +++ b/app/models/instructeur.rb @@ -161,7 +161,7 @@ class Instructeur < ApplicationRecord h = { nb_en_construction: groupe.dossiers.en_construction.count, nb_en_instruction: groupe.dossiers.en_instruction.count, - nb_accepted: groupe.dossiers.accepte.where(processed_at: Time.zone.yesterday.beginning_of_day..Time.zone.yesterday.end_of_day).count, + nb_accepted: Traitement.where(dossier: groupe.dossiers.accepte, processed_at: Time.zone.yesterday.beginning_of_day..Time.zone.yesterday.end_of_day).count, nb_notification: notifications_for_procedure(procedure, :not_archived).count } diff --git a/app/models/traitement.rb b/app/models/traitement.rb new file mode 100644 index 000000000..f0a414f59 --- /dev/null +++ b/app/models/traitement.rb @@ -0,0 +1,4 @@ +class Traitement < ApplicationRecord + belongs_to :dossier + belongs_to :instructeur +end diff --git a/app/views/dossiers/show.pdf.prawn b/app/views/dossiers/show.pdf.prawn index 475bf1274..36c1c6329 100644 --- a/app/views/dossiers/show.pdf.prawn +++ b/app/views/dossiers/show.pdf.prawn @@ -156,7 +156,7 @@ def add_etats_dossier(pdf, dossier) if dossier.en_instruction_at.present? format_in_2_columns(pdf, "En instruction le", try_format_date(dossier.en_instruction_at)) end - if dossier.processed_at?.present? + if dossier.processed_at.present? format_in_2_columns(pdf, "Décision le", try_format_date(dossier.processed_at)) end diff --git a/db/migrate/20200630140356_create_traitements.rb b/db/migrate/20200630140356_create_traitements.rb new file mode 100644 index 000000000..d2dc10748 --- /dev/null +++ b/db/migrate/20200630140356_create_traitements.rb @@ -0,0 +1,11 @@ +class CreateTraitements < ActiveRecord::Migration[5.2] + def change + create_table :traitements do |t| + t.references :dossier, foreign_key: true + t.references :instructeur, foreign_key: true + t.string :motivation + t.string :state + t.timestamp :processed_at + end + end +end diff --git a/db/schema.rb b/db/schema.rb index a3aaf999f..dd3a06913 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_06_11_122406) do +ActiveRecord::Schema.define(version: 2020_06_30_140356) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -560,6 +560,16 @@ ActiveRecord::Schema.define(version: 2020_06_11_122406) do t.string "version", null: false end + create_table "traitements", force: :cascade do |t| + t.bigint "dossier_id" + t.bigint "instructeur_id" + t.string "motivation" + t.string "state" + t.datetime "processed_at" + t.index ["dossier_id"], name: "index_traitements_on_dossier_id" + t.index ["instructeur_id"], name: "index_traitements_on_instructeur_id" + end + create_table "trusted_device_tokens", force: :cascade do |t| t.string "token", null: false t.bigint "instructeur_id" @@ -660,6 +670,8 @@ ActiveRecord::Schema.define(version: 2020_06_11_122406) do add_foreign_key "received_mails", "procedures" add_foreign_key "refused_mails", "procedures" add_foreign_key "services", "administrateurs" + add_foreign_key "traitements", "dossiers" + add_foreign_key "traitements", "instructeurs" add_foreign_key "trusted_device_tokens", "instructeurs" add_foreign_key "types_de_champ", "types_de_champ", column: "parent_id" add_foreign_key "users", "administrateurs" diff --git a/lib/tasks/deployment/20200630154829_add_traitements_from_dossiers.rake b/lib/tasks/deployment/20200630154829_add_traitements_from_dossiers.rake new file mode 100644 index 000000000..9940c9843 --- /dev/null +++ b/lib/tasks/deployment/20200630154829_add_traitements_from_dossiers.rake @@ -0,0 +1,18 @@ +namespace :after_party do + desc 'Deployment task: add_traitements_from_dossiers' + task add_traitements_from_dossiers: :environment do + puts "Running deploy task 'add_traitements_from_dossiers'" + + dossiers_termines = Dossier.state_termine + progress = ProgressReport.new(dossiers_termines.count) + dossiers_termines.find_each do |dossier| + dossier.traitements.create!(state: dossier.state, motivation: dossier.motivation, processed_at: dossier.processed_at) + progress.inc + end + progress.finish + + # Update task as completed. If you remove the line below, the task will + # run with every deploy (or every time you call after_party:run). + AfterParty::TaskRecord.create version: '20200630154829' + end +end diff --git a/spec/factories/dossier.rb b/spec/factories/dossier.rb index 222f87b1c..7abd373ea 100644 --- a/spec/factories/dossier.rb +++ b/spec/factories/dossier.rb @@ -127,11 +127,15 @@ FactoryBot.define do end trait :accepte do - after(:create) do |dossier, _evaluator| + transient do + motivation { nil } + end + + after(:create) do |dossier, evaluator| dossier.state = Dossier.states.fetch(:accepte) dossier.en_construction_at ||= dossier.created_at + 1.minute dossier.en_instruction_at ||= dossier.en_construction_at + 1.minute - dossier.processed_at ||= dossier.en_instruction_at + 1.minute + dossier.traitements.build(state: Dossier.states.fetch(:accepte), processed_at: dossier.en_instruction_at + 1.minute, motivation: evaluator.motivation) dossier.save! end end @@ -141,7 +145,7 @@ FactoryBot.define do dossier.state = Dossier.states.fetch(:refuse) dossier.en_construction_at ||= dossier.created_at + 1.minute dossier.en_instruction_at ||= dossier.en_construction_at + 1.minute - dossier.processed_at ||= dossier.en_instruction_at + 1.minute + dossier.traitements.build(state: Dossier.states.fetch(:refuse), processed_at: dossier.en_instruction_at + 1.minute) dossier.save! end end @@ -151,14 +155,14 @@ FactoryBot.define do dossier.state = Dossier.states.fetch(:sans_suite) dossier.en_construction_at ||= dossier.created_at + 1.minute dossier.en_instruction_at ||= dossier.en_construction_at + 1.minute - dossier.processed_at ||= dossier.en_instruction_at + 1.minute + dossier.traitements.build(state: Dossier.states.fetch(:sans_suite), processed_at: dossier.en_instruction_at + 1.minute) dossier.save! end end trait :with_motivation do after(:create) do |dossier, _evaluator| - dossier.motivation = case dossier.state + motivation = case dossier.state when Dossier.states.fetch(:refuse) 'L’entreprise concernée n’est pas agréée.' when Dossier.states.fetch(:sans_suite) @@ -166,6 +170,7 @@ FactoryBot.define do else 'Vous avez validé les conditions.' end + dossier.traitements.last.update!(motivation: motivation) end end diff --git a/spec/models/concern/tags_substitution_concern_spec.rb b/spec/models/concern/tags_substitution_concern_spec.rb index 9488976fd..37acae977 100644 --- a/spec/models/concern/tags_substitution_concern_spec.rb +++ b/spec/models/concern/tags_substitution_concern_spec.rb @@ -35,6 +35,7 @@ describe TagsSubstitutionConcern, type: :model do let(:individual) { nil } let(:etablissement) { create(:etablissement) } let!(:dossier) { create(:dossier, procedure: procedure, individual: individual, etablissement: etablissement) } + let(:instructeur) { create(:instructeur) } before { Timecop.freeze(Time.zone.now) } @@ -242,7 +243,7 @@ describe TagsSubstitutionConcern, type: :model do end context 'when the dossier has a motivation' do - let(:dossier) { create(:dossier, motivation: 'motivation') } + let(:dossier) { create(:dossier, :accepte, motivation: 'motivation') } context 'and the template has some dossier tags' do let(:template) { '--motivation-- --numéro du dossier--' } @@ -318,9 +319,13 @@ describe TagsSubstitutionConcern, type: :model do context "when using a date tag" do before do - dossier.en_construction_at = Time.zone.local(2001, 2, 3) - dossier.en_instruction_at = Time.zone.local(2004, 5, 6) - dossier.processed_at = Time.zone.local(2007, 8, 9) + Timecop.freeze(Time.zone.local(2001, 2, 3)) + dossier.passer_en_construction! + Timecop.freeze(Time.zone.local(2004, 5, 6)) + dossier.passer_en_instruction!(instructeur) + Timecop.freeze(Time.zone.local(2007, 8, 9)) + dossier.accepter!(instructeur, nil, nil) + Timecop.return end context "with date de dépôt" do diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 6cbf0503a..7a01740c6 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -209,8 +209,17 @@ describe Dossier do let(:date1) { 1.day.ago } let(:date2) { 1.hour.ago } let(:date3) { 1.minute.ago } - let(:dossier) { create(:dossier, :with_entreprise, user: user, procedure: procedure, en_construction_at: date1, en_instruction_at: date2, processed_at: date3, motivation: "Motivation") } - let!(:follow) { create(:follow, instructeur: instructeur, dossier: dossier) } + let(:dossier) do + d = create(:dossier, :with_entreprise, user: user, procedure: procedure) + Timecop.freeze(date1) + d.passer_en_construction! + Timecop.freeze(date2) + d.passer_en_instruction!(instructeur) + Timecop.freeze(date3) + d.accepter!(instructeur, "Motivation", nil) + Timecop.return + d + end describe "followers_instructeurs" do let(:non_following_instructeur) { create(:instructeur) } @@ -399,8 +408,8 @@ describe Dossier do end it { expect(dossier.state).to eq(Dossier.states.fetch(:accepte)) } + it { expect(dossier.traitements.last.processed_at).to eq(beginning_of_day) } it { expect(dossier.processed_at).to eq(beginning_of_day) } - end context 'when dossier is refuse' do @@ -801,7 +810,7 @@ describe Dossier do dossier.procedure.update_column(:web_hook_url, '/webhook.json') expect { - dossier.update_column(:motivation, 'bonjour') + dossier.update_column(:search_terms, 'bonjour') }.to_not have_enqueued_job(WebHookJob) expect { @@ -809,7 +818,7 @@ describe Dossier do }.to have_enqueued_job(WebHookJob) expect { - dossier.update_column(:motivation, 'bonjour2') + dossier.update_column(:search_terms, 'bonjour2') }.to_not have_enqueued_job(WebHookJob) expect { @@ -904,8 +913,11 @@ describe Dossier do after { Timecop.return } + it { expect(dossier.traitements.last.motivation).to eq('motivation') } it { expect(dossier.motivation).to eq('motivation') } + it { expect(dossier.traitements.last.instructeur).to eq(instructeur) } it { expect(dossier.en_instruction_at).to eq(dossier.en_instruction_at) } + it { expect(dossier.traitements.last.processed_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') } diff --git a/spec/models/instructeur_spec.rb b/spec/models/instructeur_spec.rb index cdef3e0d9..2e69ce40c 100644 --- a/spec/models/instructeur_spec.rb +++ b/spec/models/instructeur_spec.rb @@ -478,7 +478,7 @@ describe Instructeur, type: :model do before do procedure_to_assign.update(declarative_with_state: "accepte") DeclarativeProceduresJob.new.perform - dossier.update(processed_at: Time.zone.yesterday.beginning_of_day) + dossier.traitements.last.update(processed_at: Time.zone.yesterday.beginning_of_day) dossier.reload end diff --git a/spec/models/traitement_spec.rb b/spec/models/traitement_spec.rb new file mode 100644 index 000000000..86f9dcc7a --- /dev/null +++ b/spec/models/traitement_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Traitement, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 9cb1952ef..59266eab2 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -66,7 +66,7 @@ describe NotificationService do before do procedure.update(declarative_with_state: "accepte") DeclarativeProceduresJob.new.perform - dossier.update(processed_at: Time.zone.yesterday.beginning_of_day) + dossier.traitements.last.update!(processed_at: Time.zone.yesterday.beginning_of_day) dossier.reload end From 0be4b50ade99a5528f5ffd13c6e0b146f706ced6 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Mon, 6 Jul 2020 16:58:08 +0200 Subject: [PATCH 03/11] update usual_traitement_time with traitement model --- app/models/procedure.rb | 22 +++++++++------------- spec/factories/dossier.rb | 14 +++++++++++--- spec/models/procedure_spec.rb | 3 +-- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 5da6426ad..126062de2 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -438,7 +438,15 @@ class Procedure < ApplicationRecord end def usual_traitement_time - percentile_time(:en_construction_at, :processed_at, 90) + times = Traitement.includes(:dossier) + .where(state: Dossier::TERMINE) + .where(processed_at: 1.month.ago..Time.zone.now) + .pluck('dossiers.en_construction_at', :processed_at) + .map { |(en_construction_at, processed_at)| processed_at - en_construction_at } + + if times.present? + times.percentile(90).ceil + end end def populate_champ_stable_ids @@ -610,18 +618,6 @@ class Procedure < ApplicationRecord end end - def percentile_time(start_attribute, end_attribute, p) - times = dossiers - .where.not(start_attribute => nil, end_attribute => nil) - .where(end_attribute => 1.month.ago..Time.zone.now) - .pluck(start_attribute, end_attribute) - .map { |(start_date, end_date)| end_date - start_date } - - if times.present? - times.percentile(p).ceil - end - end - def ensure_path_exists if self.path.blank? self.path = SecureRandom.uuid diff --git a/spec/factories/dossier.rb b/spec/factories/dossier.rb index 7abd373ea..51c2995e7 100644 --- a/spec/factories/dossier.rb +++ b/spec/factories/dossier.rb @@ -129,13 +129,21 @@ FactoryBot.define do trait :accepte do transient do motivation { nil } + processed_at { nil } end after(:create) do |dossier, evaluator| dossier.state = Dossier.states.fetch(:accepte) - dossier.en_construction_at ||= dossier.created_at + 1.minute - dossier.en_instruction_at ||= dossier.en_construction_at + 1.minute - dossier.traitements.build(state: Dossier.states.fetch(:accepte), processed_at: dossier.en_instruction_at + 1.minute, motivation: evaluator.motivation) + processed_at = evaluator.processed_at + if processed_at.present? + dossier.en_construction_at ||= processed_at - 2.minutes + dossier.en_instruction_at ||= processed_at - 1.minute + dossier.traitements.build(state: Dossier.states.fetch(:accepte), processed_at: processed_at, motivation: evaluator.motivation) + else + dossier.en_construction_at ||= dossier.created_at + 1.minute + dossier.en_instruction_at ||= dossier.en_construction_at + 1.minute + dossier.traitements.build(state: Dossier.states.fetch(:accepte), processed_at: dossier.en_instruction_at + 1.minute, motivation: evaluator.motivation) + end dossier.save! end end diff --git a/spec/models/procedure_spec.rb b/spec/models/procedure_spec.rb index 73c96c84b..6d4b5149f 100644 --- a/spec/models/procedure_spec.rb +++ b/spec/models/procedure_spec.rb @@ -958,8 +958,7 @@ describe Procedure do let(:procedure) { create(:procedure) } def create_dossier(construction_date:, instruction_date:, processed_date:) - dossier = create(:dossier, :accepte, procedure: procedure) - dossier.update!(en_construction_at: construction_date, en_instruction_at: instruction_date, processed_at: processed_date) + dossier = create(:dossier, :accepte, procedure: procedure, en_construction_at: construction_date, en_instruction_at: instruction_date, processed_at: processed_date) end before do From cea170e4a67e9c9f409403f31dca64a231d217c2 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Mon, 6 Jul 2020 18:42:21 +0200 Subject: [PATCH 04/11] update stats with traitement model --- app/controllers/stats_controller.rb | 14 +++++++++----- spec/controllers/stats_controller_spec.rb | 22 ++++++++-------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb index 9e3934c35..d676b5b40 100644 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -249,9 +249,11 @@ class StatsController < ApplicationController min_date = 11.months.ago max_date = Time.zone.now.to_date - processed_dossiers = dossiers + processed_dossiers = Traitement.includes(:dossier) + .where(dossier_id: dossiers) + .where('dossiers.state' => Dossier::TERMINE) .where(:processed_at => min_date..max_date) - .pluck(:groupe_instructeur_id, :en_construction_at, :processed_at) + .pluck('dossiers.groupe_instructeur_id', 'dossiers.en_construction_at', :processed_at) # Group dossiers by month processed_dossiers_by_month = processed_dossiers @@ -290,11 +292,13 @@ class StatsController < ApplicationController min_date = 11.months.ago max_date = Time.zone.now.to_date - processed_dossiers = dossiers + processed_dossiers = Traitement.includes(:dossier) + .where(dossier: dossiers) + .where('dossiers.state' => Dossier::TERMINE) .where(:processed_at => min_date..max_date) .pluck( - :groupe_instructeur_id, - Arel.sql('EXTRACT(EPOCH FROM (en_construction_at - created_at)) / 60 AS processing_time'), + 'dossiers.groupe_instructeur_id', + Arel.sql('EXTRACT(EPOCH FROM (dossiers.en_construction_at - dossiers.created_at)) / 60 AS processing_time'), :processed_at ) diff --git a/spec/controllers/stats_controller_spec.rb b/spec/controllers/stats_controller_spec.rb index d4547e81c..a16a9281d 100644 --- a/spec/controllers/stats_controller_spec.rb +++ b/spec/controllers/stats_controller_spec.rb @@ -106,26 +106,23 @@ describe StatsController, type: :controller do before do procedure_1 = FactoryBot.create(:procedure) procedure_2 = FactoryBot.create(:procedure) - dossier_p1_a = FactoryBot.create(:dossier, + dossier_p1_a = FactoryBot.create(:dossier, :accepte, :procedure => procedure_1, :en_construction_at => 2.months.ago.beginning_of_month, :processed_at => 2.months.ago.beginning_of_month + 3.days) - dossier_p1_b = FactoryBot.create(:dossier, + dossier_p1_b = FactoryBot.create(:dossier, :accepte, :procedure => procedure_1, :en_construction_at => 2.months.ago.beginning_of_month, :processed_at => 2.months.ago.beginning_of_month + 1.day) - dossier_p1_c = FactoryBot.create(:dossier, + dossier_p1_c = FactoryBot.create(:dossier, :accepte, :procedure => procedure_1, :en_construction_at => 1.month.ago.beginning_of_month, :processed_at => 1.month.ago.beginning_of_month + 5.days) - dossier_p2_a = FactoryBot.create(:dossier, + dossier_p2_a = FactoryBot.create(:dossier, :accepte, :procedure => procedure_2, :en_construction_at => 2.months.ago.beginning_of_month, :processed_at => 2.months.ago.beginning_of_month + 4.days) - # Write directly in the DB to avoid the before_validation hook - Dossier.update_all(state: Dossier.states.fetch(:accepte)) - @expected_hash = { (2.months.ago.beginning_of_month).to_s => 3.0, (1.month.ago.beginning_of_month).to_s => 5.0 @@ -154,30 +151,27 @@ describe StatsController, type: :controller do before do procedure_1 = FactoryBot.create(:procedure, :with_type_de_champ, :types_de_champ_count => 24) procedure_2 = FactoryBot.create(:procedure, :with_type_de_champ, :types_de_champ_count => 48) - dossier_p1_a = FactoryBot.create(:dossier, + dossier_p1_a = FactoryBot.create(:dossier, :accepte, :procedure => procedure_1, :created_at => 2.months.ago.beginning_of_month, :en_construction_at => 2.months.ago.beginning_of_month + 30.minutes, :processed_at => 2.months.ago.beginning_of_month + 1.day) - dossier_p1_b = FactoryBot.create(:dossier, + dossier_p1_b = FactoryBot.create(:dossier, :accepte, :procedure => procedure_1, :created_at => 2.months.ago.beginning_of_month, :en_construction_at => 2.months.ago.beginning_of_month + 10.minutes, :processed_at => 2.months.ago.beginning_of_month + 1.day) - dossier_p1_c = FactoryBot.create(:dossier, + dossier_p1_c = FactoryBot.create(:dossier, :accepte, :procedure => procedure_1, :created_at => 1.month.ago.beginning_of_month, :en_construction_at => 1.month.ago.beginning_of_month + 50.minutes, :processed_at => 1.month.ago.beginning_of_month + 1.day) - dossier_p2_a = FactoryBot.create(:dossier, + dossier_p2_a = FactoryBot.create(:dossier, :accepte, :procedure => procedure_2, :created_at => 2.months.ago.beginning_of_month, :en_construction_at => 2.months.ago.beginning_of_month + 80.minutes, :processed_at => 2.months.ago.beginning_of_month + 1.day) - # Write directly in the DB to avoid the before_validation hook - Dossier.update_all(state: Dossier.states.fetch(:accepte)) - @expected_hash = { (2.months.ago.beginning_of_month).to_s => 30.0, (1.month.ago.beginning_of_month).to_s => 50.0 From 8f9d6d287131cb12617538dee8644ec227e01d20 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Mon, 6 Jul 2020 09:30:07 +0200 Subject: [PATCH 05/11] show which instructeur took decision and when MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and also replace "sans suite" by "classé sans suite" --- .../instructeurs/dossiers/personnes_impliquees.html.haml | 3 +++ config/locales/models/dossier/fr.yml | 7 +++++-- spec/controllers/api/v2/graphql_controller_spec.rb | 2 +- spec/helpers/dossier_helper_spec.rb | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/views/instructeurs/dossiers/personnes_impliquees.html.haml b/app/views/instructeurs/dossiers/personnes_impliquees.html.haml index 7a5ac4cde..f3ab891d4 100644 --- a/app/views/instructeurs/dossiers/personnes_impliquees.html.haml +++ b/app/views/instructeurs/dossiers/personnes_impliquees.html.haml @@ -5,6 +5,9 @@ .personnes-impliquees.container = render partial: 'instructeurs/dossiers/envoyer_dossier_block', locals: { dossier: @dossier, potential_recipients: @potential_recipients } + - @dossier.traitements.each do |traitement| + = render partial: 'instructeurs/dossiers/personnes_impliquees_block', locals: { emails_collection: [traitement.instructeur.email], title: "Instructeur qui a #{t(traitement.state, scope: 'activerecord.attributes.traitement.state').downcase} le dossier (#{l(traitement.processed_at)})" } + = render partial: 'instructeurs/dossiers/personnes_impliquees_block', locals: { emails_collection: @following_instructeurs_emails, title: "Instructeurs qui suivent actuellement le dossier", blank: "Aucun instructeur ne suit ce dossier" } - if @previous_following_instructeurs_emails.present? diff --git a/config/locales/models/dossier/fr.yml b/config/locales/models/dossier/fr.yml index 5ed0cb43d..d79072067 100644 --- a/config/locales/models/dossier/fr.yml +++ b/config/locales/models/dossier/fr.yml @@ -9,13 +9,13 @@ fr: montant_projet: 'Le montant du projet' montant_aide_demande: "Le montant d’aide demandée" date_previsionnelle: "La date de début prévisionnelle" - state: + state: &state brouillon: "Brouillon" en_construction: "En construction" en_instruction: "En instruction" accepte: "Accepté" refuse: "Refusé" - sans_suite: "Sans suite" + sans_suite: "Classé sans suite" autorisation_donnees: Acceptation des CGU state/brouillon: Brouillon state/en_construction: En construction @@ -23,3 +23,6 @@ fr: state/accepte: Accepté state/refuse: Refusé state/sans_suite: Sans suite + traitement: + state: + <<: *state diff --git a/spec/controllers/api/v2/graphql_controller_spec.rb b/spec/controllers/api/v2/graphql_controller_spec.rb index 8f80a0019..67fa3d713 100644 --- a/spec/controllers/api/v2/graphql_controller_spec.rb +++ b/spec/controllers/api/v2/graphql_controller_spec.rb @@ -596,7 +596,7 @@ describe API::V2::GraphqlController do it "should fail" do expect(gql_errors).to eq(nil) expect(gql_data).to eq(dossierRefuser: { - errors: [{ message: "Le dossier est déjà sans suite" }], + errors: [{ message: "Le dossier est déjà classé sans suite" }], dossier: nil }) end diff --git a/spec/helpers/dossier_helper_spec.rb b/spec/helpers/dossier_helper_spec.rb index 7e90e8807..77ed77905 100644 --- a/spec/helpers/dossier_helper_spec.rb +++ b/spec/helpers/dossier_helper_spec.rb @@ -156,7 +156,7 @@ RSpec.describe DossierHelper, type: :helper do it 'sans_suite is traité' do dossier.sans_suite! - expect(subject).to eq('Sans suite') + expect(subject).to eq('Classé sans suite') end it 'refuse is traité' do From 9b6c5fd7fd965a5535426f1ae63b34e7f4bdf9eb Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Mon, 6 Jul 2020 22:13:00 +0200 Subject: [PATCH 06/11] update termine_close_to_expiration with traitement --- app/models/dossier.rb | 7 +++---- app/models/traitement.rb | 7 +++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/models/dossier.rb b/app/models/dossier.rb index c15644939..08bc0864c 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -199,10 +199,9 @@ class Dossier < ApplicationRecord .joins(:procedure) .where("dossiers.en_instruction_at + (duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: INTERVAL_BEFORE_EXPIRATION }) end - scope :termine_close_to_expiration, -> do - state_termine - .joins(:procedure) - .where("dossiers.processed_at + (duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: INTERVAL_BEFORE_EXPIRATION }) + def self.termine_close_to_expiration + dossier_ids = Traitement.termine_close_to_expiration.pluck(:dossier_id).uniq + Dossier.where(id: dossier_ids) end scope :brouillon_expired, -> do diff --git a/app/models/traitement.rb b/app/models/traitement.rb index f0a414f59..88e36eb45 100644 --- a/app/models/traitement.rb +++ b/app/models/traitement.rb @@ -1,4 +1,11 @@ class Traitement < ApplicationRecord belongs_to :dossier belongs_to :instructeur + + scope :termine_close_to_expiration, -> do + joins(dossier: :procedure) + .where(state: Dossier::TERMINE) + .where('dossiers.state' => Dossier::TERMINE) + .where("traitements.processed_at + (procedures.duree_conservation_dossiers_dans_ds * INTERVAL '1 month') - INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: Dossier::INTERVAL_BEFORE_EXPIRATION }) + end end From 1253bb59949f1e0f3f3b8e9650cfd77418895647 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Tue, 7 Jul 2020 10:43:07 +0200 Subject: [PATCH 07/11] store instructeur_email in traitement --- app/models/dossier.rb | 8 ++++---- app/models/traitement.rb | 1 - .../instructeurs/dossiers/personnes_impliquees.html.haml | 2 +- ...ructeur_id_and_add_instructeur_email_to_traitements.rb | 6 ++++++ db/schema.rb | 6 ++---- spec/models/dossier_spec.rb | 2 +- 6 files changed, 14 insertions(+), 11 deletions(-) create mode 100644 db/migrate/20200707082256_remove_instructeur_id_and_add_instructeur_email_to_traitements.rb diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 08bc0864c..a29673af4 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -548,7 +548,7 @@ class Dossier < ApplicationRecord end def after_accepter(instructeur, motivation, justificatif = nil) - self.traitements.build(state: Dossier.states.fetch(:accepte), instructeur: instructeur, motivation: motivation, processed_at: Time.zone.now) + self.traitements.build(state: Dossier.states.fetch(:accepte), instructeur_email: instructeur.email, motivation: motivation, processed_at: Time.zone.now) if justificatif self.justificatif_motivation.attach(justificatif) @@ -564,7 +564,7 @@ class Dossier < ApplicationRecord end def after_accepter_automatiquement - self.traitements.build(state: Dossier.states.fetch(:accepte), instructeur: nil, motivation: nil, processed_at: Time.zone.now) + self.traitements.build(state: Dossier.states.fetch(:accepte), instructeur_email: nil, motivation: nil, processed_at: Time.zone.now) self.en_instruction_at ||= Time.zone.now if attestation.nil? @@ -577,7 +577,7 @@ class Dossier < ApplicationRecord end def after_refuser(instructeur, motivation, justificatif = nil) - self.traitements.build(state: Dossier.states.fetch(:refuse), instructeur: instructeur, motivation: motivation, processed_at: Time.zone.now) + self.traitements.build(state: Dossier.states.fetch(:refuse), instructeur_email: instructeur.email, motivation: motivation, processed_at: Time.zone.now) if justificatif self.justificatif_motivation.attach(justificatif) @@ -589,7 +589,7 @@ class Dossier < ApplicationRecord end def after_classer_sans_suite(instructeur, motivation, justificatif = nil) - self.traitements.build(state: Dossier.states.fetch(:sans_suite), instructeur: instructeur, motivation: motivation, processed_at: Time.zone.now) + self.traitements.build(state: Dossier.states.fetch(:sans_suite), instructeur_email: instructeur.email, motivation: motivation, processed_at: Time.zone.now) if justificatif self.justificatif_motivation.attach(justificatif) diff --git a/app/models/traitement.rb b/app/models/traitement.rb index 88e36eb45..cbbdd2464 100644 --- a/app/models/traitement.rb +++ b/app/models/traitement.rb @@ -1,6 +1,5 @@ class Traitement < ApplicationRecord belongs_to :dossier - belongs_to :instructeur scope :termine_close_to_expiration, -> do joins(dossier: :procedure) diff --git a/app/views/instructeurs/dossiers/personnes_impliquees.html.haml b/app/views/instructeurs/dossiers/personnes_impliquees.html.haml index f3ab891d4..685f15143 100644 --- a/app/views/instructeurs/dossiers/personnes_impliquees.html.haml +++ b/app/views/instructeurs/dossiers/personnes_impliquees.html.haml @@ -6,7 +6,7 @@ = render partial: 'instructeurs/dossiers/envoyer_dossier_block', locals: { dossier: @dossier, potential_recipients: @potential_recipients } - @dossier.traitements.each do |traitement| - = render partial: 'instructeurs/dossiers/personnes_impliquees_block', locals: { emails_collection: [traitement.instructeur.email], title: "Instructeur qui a #{t(traitement.state, scope: 'activerecord.attributes.traitement.state').downcase} le dossier (#{l(traitement.processed_at)})" } + = render partial: 'instructeurs/dossiers/personnes_impliquees_block', locals: { emails_collection: [traitement.instructeur_email], title: "Instructeur qui a #{t(traitement.state, scope: 'activerecord.attributes.traitement.state').downcase} le dossier (#{l(traitement.processed_at)})" } = render partial: 'instructeurs/dossiers/personnes_impliquees_block', locals: { emails_collection: @following_instructeurs_emails, title: "Instructeurs qui suivent actuellement le dossier", blank: "Aucun instructeur ne suit ce dossier" } diff --git a/db/migrate/20200707082256_remove_instructeur_id_and_add_instructeur_email_to_traitements.rb b/db/migrate/20200707082256_remove_instructeur_id_and_add_instructeur_email_to_traitements.rb new file mode 100644 index 000000000..33f954f60 --- /dev/null +++ b/db/migrate/20200707082256_remove_instructeur_id_and_add_instructeur_email_to_traitements.rb @@ -0,0 +1,6 @@ +class RemoveInstructeurIdAndAddInstructeurEmailToTraitements < ActiveRecord::Migration[5.2] + def change + add_column :traitements, :instructeur_email, :string + remove_column :traitements, :instructeur_id + end +end diff --git a/db/schema.rb b/db/schema.rb index dd3a06913..5e8ef8db9 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_06_30_140356) do +ActiveRecord::Schema.define(version: 2020_07_07_082256) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -562,12 +562,11 @@ ActiveRecord::Schema.define(version: 2020_06_30_140356) do create_table "traitements", force: :cascade do |t| t.bigint "dossier_id" - t.bigint "instructeur_id" t.string "motivation" t.string "state" t.datetime "processed_at" + t.string "instructeur_email" t.index ["dossier_id"], name: "index_traitements_on_dossier_id" - t.index ["instructeur_id"], name: "index_traitements_on_instructeur_id" end create_table "trusted_device_tokens", force: :cascade do |t| @@ -671,7 +670,6 @@ ActiveRecord::Schema.define(version: 2020_06_30_140356) do add_foreign_key "refused_mails", "procedures" add_foreign_key "services", "administrateurs" add_foreign_key "traitements", "dossiers" - add_foreign_key "traitements", "instructeurs" add_foreign_key "trusted_device_tokens", "instructeurs" add_foreign_key "types_de_champ", "types_de_champ", column: "parent_id" add_foreign_key "users", "administrateurs" diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 7a01740c6..732209a4b 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -915,7 +915,7 @@ describe Dossier do it { expect(dossier.traitements.last.motivation).to eq('motivation') } it { expect(dossier.motivation).to eq('motivation') } - it { expect(dossier.traitements.last.instructeur).to eq(instructeur) } + it { expect(dossier.traitements.last.instructeur_email).to eq(instructeur.email) } it { expect(dossier.en_instruction_at).to eq(dossier.en_instruction_at) } it { expect(dossier.traitements.last.processed_at).to eq(now) } it { expect(dossier.processed_at).to eq(now) } From f0e0554da98ce22f5fe059088fb559938074b6ee Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Tue, 7 Jul 2020 10:44:09 +0200 Subject: [PATCH 08/11] display instructeur_email who took decision only if email is present --- app/views/instructeurs/dossiers/personnes_impliquees.html.haml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/instructeurs/dossiers/personnes_impliquees.html.haml b/app/views/instructeurs/dossiers/personnes_impliquees.html.haml index 685f15143..6380a980e 100644 --- a/app/views/instructeurs/dossiers/personnes_impliquees.html.haml +++ b/app/views/instructeurs/dossiers/personnes_impliquees.html.haml @@ -6,7 +6,8 @@ = render partial: 'instructeurs/dossiers/envoyer_dossier_block', locals: { dossier: @dossier, potential_recipients: @potential_recipients } - @dossier.traitements.each do |traitement| - = render partial: 'instructeurs/dossiers/personnes_impliquees_block', locals: { emails_collection: [traitement.instructeur_email], title: "Instructeur qui a #{t(traitement.state, scope: 'activerecord.attributes.traitement.state').downcase} le dossier (#{l(traitement.processed_at)})" } + - if traitement.instructeur_email.present? + = render partial: 'instructeurs/dossiers/personnes_impliquees_block', locals: { emails_collection: [traitement.instructeur_email], title: "Instructeur qui a #{t(traitement.state, scope: 'activerecord.attributes.traitement.state').downcase} le dossier (#{l(traitement.processed_at)})" } = render partial: 'instructeurs/dossiers/personnes_impliquees_block', locals: { emails_collection: @following_instructeurs_emails, title: "Instructeurs qui suivent actuellement le dossier", blank: "Aucun instructeur ne suit ce dossier" } From dfc97d8d872124d1a29f65b1556dbf875b512286 Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Tue, 7 Jul 2020 18:16:01 +0200 Subject: [PATCH 09/11] =?UTF-8?q?all=C3=A8ge=20l'affichage=20des=20d=C3=A9?= =?UTF-8?q?cisions=20rendues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../_decisions_rendues_block.html.haml | 19 +++++++++++++++++++ .../dossiers/personnes_impliquees.html.haml | 6 ++---- 2 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 app/views/instructeurs/dossiers/_decisions_rendues_block.html.haml diff --git a/app/views/instructeurs/dossiers/_decisions_rendues_block.html.haml b/app/views/instructeurs/dossiers/_decisions_rendues_block.html.haml new file mode 100644 index 000000000..fd329cf0a --- /dev/null +++ b/app/views/instructeurs/dossiers/_decisions_rendues_block.html.haml @@ -0,0 +1,19 @@ +.tab-title Décisions rendues +- if traitements.any? + %ul.tab-list + - traitements.each do |traitement| + - if traitement.instructeur_email.present? + %li + = "Le #{l(traitement.processed_at, format: '%d %B %Y à %R')}, " + = traitement.instructeur_email + a + %strong= t(traitement.state, scope: 'activerecord.attributes.traitement.state').downcase + ce dossier + - else + %li + = "Le #{l(traitement.processed_at, format: '%d %B %Y à %R')}, " + ce dossier a été + %strong= t(traitement.state, scope: 'activerecord.attributes.traitement.state').downcase +- else + %p.tab-paragraph Aucune décision n'a été rendue + diff --git a/app/views/instructeurs/dossiers/personnes_impliquees.html.haml b/app/views/instructeurs/dossiers/personnes_impliquees.html.haml index 6380a980e..e30a3e70e 100644 --- a/app/views/instructeurs/dossiers/personnes_impliquees.html.haml +++ b/app/views/instructeurs/dossiers/personnes_impliquees.html.haml @@ -5,10 +5,6 @@ .personnes-impliquees.container = render partial: 'instructeurs/dossiers/envoyer_dossier_block', locals: { dossier: @dossier, potential_recipients: @potential_recipients } - - @dossier.traitements.each do |traitement| - - if traitement.instructeur_email.present? - = render partial: 'instructeurs/dossiers/personnes_impliquees_block', locals: { emails_collection: [traitement.instructeur_email], title: "Instructeur qui a #{t(traitement.state, scope: 'activerecord.attributes.traitement.state').downcase} le dossier (#{l(traitement.processed_at)})" } - = render partial: 'instructeurs/dossiers/personnes_impliquees_block', locals: { emails_collection: @following_instructeurs_emails, title: "Instructeurs qui suivent actuellement le dossier", blank: "Aucun instructeur ne suit ce dossier" } - if @previous_following_instructeurs_emails.present? @@ -17,3 +13,5 @@ = render partial: 'instructeurs/dossiers/personnes_impliquees_block', locals: { emails_collection: @avis_emails, title: "Personnes à qui un avis a été demandé", blank: "Aucun avis n'a été demandé" } = render partial: 'instructeurs/dossiers/personnes_impliquees_block', locals: { emails_collection: @invites_emails, title: "Personnes invitées à consulter ce dossier", blank: "Aucune personne n'a été invitée à consulter ce dossier" } + + = render partial: 'instructeurs/dossiers/decisions_rendues_block', locals: { traitements: @dossier.traitements } From f38e16e371e350a2448f78ebf47dfc88816a0d7d Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Wed, 8 Jul 2020 11:49:28 +0200 Subject: [PATCH 10/11] avoid n+1 queries --- app/models/dossier.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/dossier.rb b/app/models/dossier.rb index a29673af4..987d8fa32 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -129,6 +129,7 @@ class Dossier < ApplicationRecord :individual, :followers_instructeurs, :avis, + :traitements, etablissement: :champ, champs: { etablissement: :champ, @@ -173,6 +174,7 @@ class Dossier < ApplicationRecord justificatif_motivation_attachment: :blob, attestation: [], avis: { piece_justificative_file_attachment: :blob }, + traitements: [], etablissement: [], individual: [], user: []) @@ -249,7 +251,7 @@ class Dossier < ApplicationRecord end scope :for_procedure, -> (procedure) { includes(:user, :groupe_instructeur).where(groupe_instructeurs: { procedure: procedure }) } - scope :for_api_v2, -> { includes(procedure: [:administrateurs], etablissement: [], individual: []) } + scope :for_api_v2, -> { includes(procedure: [:administrateurs], etablissement: [], individual: [], traitements: []) } scope :with_notifications, -> do # This scope is meant to be composed, typically with Instructeur.followed_dossiers, which means that the :follows table is already INNER JOINed; From 7fd4d46e8e79792722073bebe9bb36eb0aad4f6a Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Wed, 8 Jul 2020 17:53:42 +0200 Subject: [PATCH 11/11] remove empty spec file --- spec/models/traitement_spec.rb | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 spec/models/traitement_spec.rb diff --git a/spec/models/traitement_spec.rb b/spec/models/traitement_spec.rb deleted file mode 100644 index 86f9dcc7a..000000000 --- a/spec/models/traitement_spec.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'rails_helper' - -RSpec.describe Traitement, type: :model do - pending "add some examples to (or delete) #{__FILE__}" -end