commit
9500bbf7c2
7 changed files with 136 additions and 41 deletions
|
@ -83,6 +83,18 @@ class Dossier < ApplicationRecord
|
|||
has_many :avis, inverse_of: :dossier, dependent: :destroy
|
||||
has_many :experts, through: :avis
|
||||
has_many :traitements, -> { order(:processed_at) }, inverse_of: :dossier, dependent: :destroy do
|
||||
def passer_en_construction(processed_at: Time.zone.now)
|
||||
build(state: Dossier.states.fetch(:en_construction),
|
||||
process_expired: false,
|
||||
processed_at: processed_at)
|
||||
end
|
||||
|
||||
def passer_en_instruction(processed_at: Time.zone.now)
|
||||
build(state: Dossier.states.fetch(:en_instruction),
|
||||
process_expired: false,
|
||||
processed_at: processed_at)
|
||||
end
|
||||
|
||||
def accepter_automatiquement(processed_at: Time.zone.now)
|
||||
build(state: Dossier.states.fetch(:accepte),
|
||||
process_expired: proxy_association.owner.procedure.feature_enabled?(:procedure_process_expired_dossiers_termine),
|
||||
|
@ -113,6 +125,7 @@ class Dossier < ApplicationRecord
|
|||
processed_at: processed_at)
|
||||
end
|
||||
end
|
||||
has_one :traitement, -> { order(processed_at: :desc) }, inverse_of: false
|
||||
|
||||
has_many :dossier_operation_logs, -> { order(:created_at) }, inverse_of: :dossier
|
||||
|
||||
|
@ -221,7 +234,7 @@ class Dossier < ApplicationRecord
|
|||
:user,
|
||||
:individual,
|
||||
:followers_instructeurs,
|
||||
:traitements,
|
||||
:traitement,
|
||||
:groupe_instructeur,
|
||||
procedure: [
|
||||
:groupe_instructeurs,
|
||||
|
@ -258,7 +271,7 @@ class Dossier < ApplicationRecord
|
|||
justificatif_motivation_attachment: :blob,
|
||||
attestation: [],
|
||||
avis: { piece_justificative_file_attachment: :blob },
|
||||
traitements: [],
|
||||
traitement: [],
|
||||
etablissement: [],
|
||||
individual: [],
|
||||
user: [])
|
||||
|
@ -342,7 +355,7 @@ class Dossier < ApplicationRecord
|
|||
.where.not(user: users_who_submitted)
|
||||
end
|
||||
|
||||
scope :for_api_v2, -> { includes(procedure: [:administrateurs, :attestation_template], etablissement: [], individual: [], traitements: []) }
|
||||
scope :for_api_v2, -> { includes(procedure: [:administrateurs, :attestation_template], etablissement: [], individual: [], traitement: []) }
|
||||
|
||||
scope :with_notifications, -> do
|
||||
joins(:follows)
|
||||
|
@ -423,12 +436,12 @@ class Dossier < ApplicationRecord
|
|||
|
||||
def motivation
|
||||
return nil if !termine?
|
||||
traitements.any? ? traitements.last.motivation : read_attribute(:motivation)
|
||||
traitement&.motivation || read_attribute(:motivation)
|
||||
end
|
||||
|
||||
def processed_at
|
||||
return nil if !termine?
|
||||
traitements.any? ? traitements.last.processed_at : read_attribute(:processed_at)
|
||||
traitement&.processed_at || read_attribute(:processed_at)
|
||||
end
|
||||
|
||||
def update_search_terms
|
||||
|
@ -649,6 +662,7 @@ class Dossier < ApplicationRecord
|
|||
transaction do
|
||||
if keep_track_on_deletion?
|
||||
DeletedDossier.create_from_dossier(self, :expired)
|
||||
dossier_operation_logs.destroy_all
|
||||
log_automatic_dossier_operation(:supprimer, self)
|
||||
end
|
||||
destroy!
|
||||
|
@ -708,14 +722,23 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
def after_passer_en_construction
|
||||
update!(conservation_extension: 0.days)
|
||||
update!(en_construction_at: Time.zone.now) if self.en_construction_at.nil?
|
||||
self.conservation_extension = 0.days
|
||||
self.en_construction_at = self.traitements
|
||||
.passer_en_construction
|
||||
.processed_at
|
||||
save!
|
||||
end
|
||||
|
||||
def after_passer_en_instruction(instructeur, disable_notification: false)
|
||||
instructeur.follow(self)
|
||||
|
||||
update!(en_instruction_at: Time.zone.now) if self.en_instruction_at.nil?
|
||||
self.en_construction_close_to_expiration_notice_sent_at = nil
|
||||
self.conservation_extension = 0.days
|
||||
self.en_instruction_at = self.traitements
|
||||
.passer_en_instruction
|
||||
.processed_at
|
||||
save!
|
||||
|
||||
if !procedure.declarative_accepte? && !disable_notification
|
||||
NotificationMailer.send_en_instruction_notification(self).deliver_later
|
||||
end
|
||||
|
@ -723,20 +746,32 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
def after_passer_automatiquement_en_instruction
|
||||
self.en_instruction_at ||= Time.zone.now
|
||||
self.declarative_triggered_at = Time.zone.now
|
||||
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
|
||||
save!
|
||||
log_automatic_dossier_operation(:passer_en_instruction)
|
||||
end
|
||||
|
||||
def after_repasser_en_construction(instructeur)
|
||||
update!(conservation_extension: 0.days)
|
||||
self.en_construction_close_to_expiration_notice_sent_at = nil
|
||||
self.conservation_extension = 0.days
|
||||
self.en_construction_at = self.traitements
|
||||
.passer_en_construction
|
||||
.processed_at
|
||||
save!
|
||||
log_dossier_operation(instructeur, :repasser_en_construction)
|
||||
end
|
||||
|
||||
def after_repasser_en_instruction(instructeur, disable_notification: false)
|
||||
self.archived = false
|
||||
self.en_instruction_at = Time.zone.now
|
||||
self.termine_close_to_expiration_notice_sent_at = nil
|
||||
self.conservation_extension = 0.days
|
||||
self.en_instruction_at = self.traitements
|
||||
.passer_en_instruction
|
||||
.processed_at
|
||||
attestation&.destroy
|
||||
|
||||
save!
|
||||
|
@ -747,7 +782,10 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
def after_accepter(instructeur, motivation, justificatif: nil, disable_notification: false)
|
||||
self.traitements.accepter(motivation: motivation, instructeur: instructeur)
|
||||
self.processed_at = self.traitements
|
||||
.accepter(motivation: motivation, instructeur: instructeur)
|
||||
.processed_at
|
||||
save!
|
||||
|
||||
if justificatif
|
||||
self.justificatif_motivation.attach(justificatif)
|
||||
|
@ -767,9 +805,10 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
def after_accepter_automatiquement
|
||||
self.traitements.accepter_automatiquement
|
||||
self.en_instruction_at ||= Time.zone.now
|
||||
self.declarative_triggered_at = Time.zone.now
|
||||
self.processed_at = self.en_instruction_at = self.declarative_triggered_at = self.traitements
|
||||
.accepter_automatiquement
|
||||
.processed_at
|
||||
save!
|
||||
|
||||
if attestation.nil?
|
||||
self.attestation = build_attestation
|
||||
|
@ -782,7 +821,10 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
def after_refuser(instructeur, motivation, justificatif: nil, disable_notification: false)
|
||||
self.traitements.refuser(motivation: motivation, instructeur: instructeur)
|
||||
self.processed_at = self.traitements
|
||||
.refuser(motivation: motivation, instructeur: instructeur)
|
||||
.processed_at
|
||||
save!
|
||||
|
||||
if justificatif
|
||||
self.justificatif_motivation.attach(justificatif)
|
||||
|
@ -798,7 +840,10 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
def after_classer_sans_suite(instructeur, motivation, justificatif: nil, disable_notification: false)
|
||||
self.traitements.classer_sans_suite(motivation: motivation, instructeur: instructeur)
|
||||
self.processed_at = self.traitements
|
||||
.classer_sans_suite(motivation: motivation, instructeur: instructeur)
|
||||
.processed_at
|
||||
save!
|
||||
|
||||
if justificatif
|
||||
self.justificatif_motivation.attach(justificatif)
|
||||
|
|
|
@ -13,9 +13,13 @@
|
|||
class Traitement < ApplicationRecord
|
||||
belongs_to :dossier, optional: false
|
||||
|
||||
scope :en_construction, -> { where(state: Dossier.states.fetch(:en_construction)) }
|
||||
scope :en_instruction, -> { where(state: Dossier.states.fetch(:en_instruction)) }
|
||||
scope :termine, -> { where(state: Dossier::TERMINE) }
|
||||
|
||||
scope :termine_close_to_expiration, -> do
|
||||
joins(dossier: :procedure)
|
||||
.where(state: Dossier::TERMINE)
|
||||
.termine
|
||||
.where(process_expired: true)
|
||||
.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 })
|
||||
|
@ -23,6 +27,7 @@ class Traitement < ApplicationRecord
|
|||
|
||||
scope :for_traitement_time_stats, -> (procedure) do
|
||||
includes(:dossier)
|
||||
.termine
|
||||
.where(dossier: procedure.dossiers)
|
||||
.where.not('dossiers.en_construction_at' => nil, :processed_at => nil)
|
||||
.order(:processed_at)
|
||||
|
@ -31,6 +36,7 @@ class Traitement < ApplicationRecord
|
|||
def self.count_dossiers_termines_by_month(groupe_instructeurs)
|
||||
last_traitements_per_dossier = Traitement
|
||||
.select('max(traitements.processed_at) as processed_at')
|
||||
.termine
|
||||
.where(dossier: Dossier.state_termine.where(groupe_instructeur: groupe_instructeurs))
|
||||
.group(:dossier_id)
|
||||
.to_sql
|
||||
|
|
|
@ -297,13 +297,12 @@ class TypeDeChamp < ApplicationRecord
|
|||
# otherwise return all types_de_champ in their latest state
|
||||
types_de_champ = TypeDeChamp
|
||||
.fillable
|
||||
.joins(:parent)
|
||||
.where(parent: { stable_id: stable_id })
|
||||
.joins(parent: :revision_types_de_champ)
|
||||
.where(parent: { stable_id: stable_id }, revision_types_de_champ: { revision_id: revision })
|
||||
|
||||
TypeDeChamp
|
||||
.where(id: types_de_champ.group(:stable_id).select('MAX(types_de_champ.id)'))
|
||||
.joins(parent: :revision_types_de_champ)
|
||||
.order(:order_place, 'procedure_revision_types_de_champ.revision_id': :desc)
|
||||
.order(:order_place, id: :desc)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: set_dossiers_processed_at'
|
||||
task set_dossiers_processed_at: :environment do
|
||||
puts "Running deploy task 'set_dossiers_processed_at'"
|
||||
|
||||
dossiers = Dossier.termine.includes(:traitement)
|
||||
progress = ProgressReport.new(dossiers.count)
|
||||
|
||||
dossiers.find_each do |dossier|
|
||||
if dossier.processed_at != dossier.traitement.processed_at
|
||||
dossier.update_column(:processed_at, dossier.traitement.processed_at)
|
||||
end
|
||||
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: AfterParty::TaskRecorder.new(__FILE__).timestamp
|
||||
end
|
||||
end
|
|
@ -340,15 +340,11 @@ describe Instructeurs::DossiersController, type: :controller do
|
|||
end
|
||||
|
||||
before do
|
||||
Timecop.freeze(Time.zone.now)
|
||||
|
||||
expect_any_instance_of(AttestationTemplate)
|
||||
.to receive(:attestation_for)
|
||||
.with(have_attributes(motivation: "Yallah", processed_at: Time.zone.now))
|
||||
.with(have_attributes(motivation: "Yallah"))
|
||||
end
|
||||
|
||||
after { Timecop.return }
|
||||
|
||||
it { subject }
|
||||
end
|
||||
|
||||
|
|
|
@ -387,13 +387,18 @@ describe Dossier do
|
|||
|
||||
it { expect(dossier.state).to eq(Dossier.states.fetch(:en_construction)) }
|
||||
it { expect(dossier.en_construction_at).to eq(beginning_of_day) }
|
||||
it { expect(dossier.traitement.state).to eq(Dossier.states.fetch(:en_construction)) }
|
||||
it { expect(dossier.traitement.processed_at).to eq(beginning_of_day) }
|
||||
|
||||
it 'should keep first en_construction_at date' do
|
||||
Timecop.return
|
||||
dossier.passer_en_instruction!(instructeur)
|
||||
dossier.repasser_en_construction!(instructeur)
|
||||
|
||||
expect(dossier.en_construction_at).to eq(beginning_of_day)
|
||||
expect(dossier.traitements.size).to eq(3)
|
||||
expect(dossier.traitements.first.processed_at).to eq(beginning_of_day)
|
||||
expect(dossier.traitement.processed_at.round).to eq(dossier.en_construction_at.round)
|
||||
expect(dossier.en_construction_at).to be > beginning_of_day
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -408,13 +413,18 @@ describe Dossier do
|
|||
|
||||
it { expect(dossier.state).to eq(Dossier.states.fetch(:en_instruction)) }
|
||||
it { expect(dossier.en_instruction_at).to eq(beginning_of_day) }
|
||||
it { expect(dossier.traitement.state).to eq(Dossier.states.fetch(:en_instruction)) }
|
||||
it { expect(dossier.traitement.processed_at).to eq(beginning_of_day) }
|
||||
|
||||
it 'should keep first en_instruction_at date if dossier is set to en_construction again' do
|
||||
Timecop.return
|
||||
dossier.repasser_en_construction!(instructeur)
|
||||
dossier.passer_en_instruction!(instructeur)
|
||||
|
||||
expect(dossier.en_instruction_at).to eq(beginning_of_day)
|
||||
expect(dossier.traitements.size).to eq(3)
|
||||
expect(dossier.traitements.first.processed_at).to eq(beginning_of_day)
|
||||
expect(dossier.traitement.processed_at.round).to eq(dossier.en_instruction_at.round)
|
||||
expect(dossier.en_instruction_at).to be > beginning_of_day
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -427,8 +437,9 @@ 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) }
|
||||
it { expect(dossier.traitement.state).to eq(Dossier.states.fetch(:accepte)) }
|
||||
it { expect(dossier.traitement.processed_at).to eq(beginning_of_day) }
|
||||
end
|
||||
|
||||
context 'when dossier is refuse' do
|
||||
|
@ -441,6 +452,8 @@ describe Dossier do
|
|||
|
||||
it { expect(dossier.state).to eq(Dossier.states.fetch(:refuse)) }
|
||||
it { expect(dossier.processed_at).to eq(beginning_of_day) }
|
||||
it { expect(dossier.traitement.state).to eq(Dossier.states.fetch(:refuse)) }
|
||||
it { expect(dossier.traitement.processed_at).to eq(beginning_of_day) }
|
||||
end
|
||||
|
||||
context 'when dossier is sans_suite' do
|
||||
|
@ -453,6 +466,8 @@ describe Dossier do
|
|||
|
||||
it { expect(dossier.state).to eq(Dossier.states.fetch(:sans_suite)) }
|
||||
it { expect(dossier.processed_at).to eq(beginning_of_day) }
|
||||
it { expect(dossier.traitement.state).to eq(Dossier.states.fetch(:sans_suite)) }
|
||||
it { expect(dossier.traitement.processed_at).to eq(beginning_of_day) }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -997,7 +1012,7 @@ describe Dossier do
|
|||
end
|
||||
|
||||
describe '#passer_en_instruction!' do
|
||||
let(:dossier) { create(:dossier, :en_construction) }
|
||||
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(:operation_serialized) { JSON.parse(last_operation.serialized.download) }
|
||||
let(:instructeur) { create(:instructeur) }
|
||||
|
@ -1006,6 +1021,7 @@ describe Dossier do
|
|||
|
||||
it { expect(dossier.state).to eq('en_instruction') }
|
||||
it { expect(dossier.followers_instructeurs).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_falsey }
|
||||
it { expect(operation_serialized['operation']).to eq('passer_en_instruction') }
|
||||
|
@ -1014,7 +1030,7 @@ describe Dossier do
|
|||
end
|
||||
|
||||
describe '#passer_automatiquement_en_instruction!' do
|
||||
let(:dossier) { create(:dossier, :en_construction) }
|
||||
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(:operation_serialized) { JSON.parse(last_operation.serialized.download) }
|
||||
let(:instructeur) { create(:instructeur) }
|
||||
|
@ -1022,6 +1038,7 @@ describe Dossier do
|
|||
before { dossier.passer_automatiquement_en_instruction! }
|
||||
|
||||
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') }
|
||||
|
@ -1119,7 +1136,7 @@ describe Dossier do
|
|||
end
|
||||
|
||||
describe '#repasser_en_instruction!' do
|
||||
let(:dossier) { create(:dossier, :refuse, :with_attestation, archived: true) }
|
||||
let(:dossier) { create(:dossier, :refuse, :with_attestation, archived: true, termine_close_to_expiration_notice_sent_at: Time.zone.now) }
|
||||
let!(:instructeur) { create(:instructeur) }
|
||||
let(:last_operation) { dossier.dossier_operation_logs.last }
|
||||
|
||||
|
@ -1136,6 +1153,7 @@ describe Dossier do
|
|||
it { expect(dossier.processed_at).to be_nil }
|
||||
it { expect(dossier.motivation).to be_nil }
|
||||
it { expect(dossier.attestation).to be_nil }
|
||||
it { expect(dossier.termine_close_to_expiration_notice_sent_at).to be_nil }
|
||||
it { expect(last_operation.operation).to eq('repasser_en_instruction') }
|
||||
it { expect(JSON.parse(last_operation.serialized.download)['author']['email']).to eq(instructeur.email) }
|
||||
it { expect(DossierMailer).to have_received(:notify_revert_to_instruction).with(dossier) }
|
||||
|
|
|
@ -342,13 +342,22 @@ describe ProcedureExportService do
|
|||
expect(subject.sheets.map(&:name)).to eq(['Dossiers', 'Etablissements', 'Avis', champ_repetition.libelle_for_export])
|
||||
end
|
||||
|
||||
it 'should have headers' do
|
||||
expect(repetition_sheet.headers).to eq([
|
||||
"Dossier ID",
|
||||
"Ligne",
|
||||
"Nom",
|
||||
"Age"
|
||||
])
|
||||
context 'with cloned procedure' do
|
||||
let(:other_parent) { create(:type_de_champ_repetition, stable_id: champ_repetition.stable_id) }
|
||||
|
||||
before do
|
||||
create(:procedure_revision_type_de_champ, type_de_champ: other_parent, revision: create(:procedure).active_revision)
|
||||
create(:type_de_champ, parent: other_parent)
|
||||
end
|
||||
|
||||
it 'should have headers' do
|
||||
expect(repetition_sheet.headers).to eq([
|
||||
"Dossier ID",
|
||||
"Ligne",
|
||||
"Nom",
|
||||
"Age"
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
it 'should have data' do
|
||||
|
|
Loading…
Reference in a new issue