Track dossier operations with author and subject
This commit is contained in:
parent
3c4380251d
commit
dba8d65137
7 changed files with 124 additions and 24 deletions
|
@ -288,7 +288,7 @@ class Dossier < ApplicationRecord
|
|||
def passer_automatiquement_en_instruction!
|
||||
en_instruction!
|
||||
|
||||
log_dossier_operation(nil, :passer_en_instruction, automatic_operation: true)
|
||||
log_automatic_dossier_operation(:passer_en_instruction)
|
||||
end
|
||||
|
||||
def repasser_en_construction!(gestionnaire)
|
||||
|
@ -311,7 +311,7 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
NotificationMailer.send_closed_notification(self).deliver_later
|
||||
log_dossier_operation(gestionnaire, :accepter)
|
||||
log_dossier_operation(gestionnaire, :accepter, self)
|
||||
end
|
||||
|
||||
def accepter_automatiquement!
|
||||
|
@ -324,14 +324,14 @@ class Dossier < ApplicationRecord
|
|||
end
|
||||
|
||||
NotificationMailer.send_closed_notification(self).deliver_later
|
||||
log_dossier_operation(nil, :accepter, automatic_operation: true)
|
||||
log_automatic_dossier_operation(:accepter, self)
|
||||
end
|
||||
|
||||
def hide!(administration)
|
||||
update(hidden_at: Time.zone.now)
|
||||
|
||||
log_administration_dossier_operation(administration, :supprimer)
|
||||
DeletedDossier.create_from_dossier(self)
|
||||
log_dossier_operation(administration, :supprimer, self)
|
||||
end
|
||||
|
||||
def refuser!(gestionnaire, motivation, justificatif = nil)
|
||||
|
@ -343,7 +343,7 @@ class Dossier < ApplicationRecord
|
|||
refuse!
|
||||
|
||||
NotificationMailer.send_refused_notification(self).deliver_later
|
||||
log_dossier_operation(gestionnaire, :refuser)
|
||||
log_dossier_operation(gestionnaire, :refuser, self)
|
||||
end
|
||||
|
||||
def classer_sans_suite!(gestionnaire, motivation, justificatif = nil)
|
||||
|
@ -355,7 +355,7 @@ class Dossier < ApplicationRecord
|
|||
sans_suite!
|
||||
|
||||
NotificationMailer.send_without_continuation_notification(self).deliver_later
|
||||
log_dossier_operation(gestionnaire, :classer_sans_suite)
|
||||
log_dossier_operation(gestionnaire, :classer_sans_suite, self)
|
||||
end
|
||||
|
||||
def check_mandatory_champs
|
||||
|
@ -368,18 +368,21 @@ class Dossier < ApplicationRecord
|
|||
|
||||
private
|
||||
|
||||
def log_dossier_operation(gestionnaire, operation, automatic_operation: false)
|
||||
dossier_operation_logs.create(
|
||||
gestionnaire: gestionnaire,
|
||||
def log_dossier_operation(author, operation, subject = nil)
|
||||
DossierOperationLog.create_and_serialize(
|
||||
dossier: self,
|
||||
operation: DossierOperationLog.operations.fetch(operation),
|
||||
automatic_operation: automatic_operation
|
||||
author: author,
|
||||
subject: subject
|
||||
)
|
||||
end
|
||||
|
||||
def log_administration_dossier_operation(administration, operation)
|
||||
dossier_operation_logs.create(
|
||||
administration: administration,
|
||||
operation: DossierOperationLog.operations.fetch(operation)
|
||||
def log_automatic_dossier_operation(operation, subject = nil)
|
||||
DossierOperationLog.create_and_serialize(
|
||||
dossier: self,
|
||||
operation: DossierOperationLog.operations.fetch(operation),
|
||||
automatic_operation: true,
|
||||
subject: subject
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -9,6 +9,57 @@ class DossierOperationLog < ApplicationRecord
|
|||
}
|
||||
|
||||
belongs_to :dossier
|
||||
belongs_to :gestionnaire
|
||||
belongs_to :administration
|
||||
has_one_attached :serialized
|
||||
|
||||
def self.create_and_serialize(params)
|
||||
dossier = params.fetch(:dossier)
|
||||
|
||||
operation_log = new(operation: params.fetch(:operation),
|
||||
dossier_id: dossier.id,
|
||||
keep_until: dossier.procedure.keep_until,
|
||||
executed_at: Time.zone.now,
|
||||
automatic_operation: !!params[:automatic_operation])
|
||||
|
||||
serialized = {
|
||||
operation: operation_log.operation,
|
||||
dossier_id: operation_log.dossier_id,
|
||||
author: self.serialize_author(params[:author]),
|
||||
subject: self.serialize_subject(params[:subject]),
|
||||
automatic_operation: operation_log.automatic_operation?,
|
||||
executed_at: operation_log.executed_at.iso8601
|
||||
}.compact.to_json
|
||||
|
||||
operation_log.digest = Digest::SHA256.hexdigest(serialized)
|
||||
|
||||
operation_log.serialized.attach(
|
||||
io: StringIO.new(serialized),
|
||||
filename: "operation-#{operation_log.digest}.json",
|
||||
content_type: 'application/json',
|
||||
# we don't want to run virus scanner on this file
|
||||
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE }
|
||||
)
|
||||
|
||||
operation_log.save!
|
||||
end
|
||||
|
||||
def self.serialize_author(author)
|
||||
if author.nil?
|
||||
nil
|
||||
else
|
||||
OperationAuthorSerializer.new(author).as_json
|
||||
end
|
||||
end
|
||||
|
||||
def self.serialize_subject(subject)
|
||||
if subject.nil?
|
||||
nil
|
||||
elsif !Flipflop.operation_log_serialize_subject?
|
||||
{ id: subject.id }
|
||||
else
|
||||
case subject
|
||||
when Dossier
|
||||
DossierSerializer.new(subject).as_json
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
18
app/serializers/operation_author_serializer.rb
Normal file
18
app/serializers/operation_author_serializer.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
class OperationAuthorSerializer < ActiveModel::Serializer
|
||||
attributes :id, :email
|
||||
|
||||
def id
|
||||
case object
|
||||
when User
|
||||
"Usager##{object.id}"
|
||||
when Gestionnaire
|
||||
"Instructeur##{object.id}"
|
||||
when Administrateur
|
||||
"Administrateur##{object.id}"
|
||||
when Administration
|
||||
"Manager##{object.id}"
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
|
@ -17,6 +17,8 @@ Flipflop.configure do
|
|||
feature :enable_email_login_token
|
||||
feature :new_champs_editor
|
||||
|
||||
feature :operation_log_serialize_subject
|
||||
|
||||
group :production do
|
||||
feature :remote_storage,
|
||||
default: ENV['FOG_ENABLED'] == 'enabled'
|
||||
|
|
|
@ -27,6 +27,7 @@ RSpec.describe AutoArchiveProcedureJob, type: :job do
|
|||
let!(:dossier7) { create(:dossier, procedure: procedure_hier, state: Dossier.states.fetch(:refuse), archived: false) }
|
||||
let!(:dossier8) { create(:dossier, procedure: procedure_hier, state: Dossier.states.fetch(:sans_suite), archived: false) }
|
||||
let!(:dossier9) { create(:dossier, procedure: procedure_aujourdhui, state: Dossier.states.fetch(:en_construction), archived: false) }
|
||||
let(:last_operation) { dossier2.dossier_operation_logs.last }
|
||||
|
||||
before do
|
||||
subject
|
||||
|
@ -40,7 +41,8 @@ RSpec.describe AutoArchiveProcedureJob, type: :job do
|
|||
it {
|
||||
expect(dossier1.state).to eq Dossier.states.fetch(:brouillon)
|
||||
expect(dossier2.state).to eq Dossier.states.fetch(:en_instruction)
|
||||
expect(dossier2.dossier_operation_logs.pluck(:gestionnaire_id, :operation, :automatic_operation)).to match([[nil, 'passer_en_instruction', true]])
|
||||
expect(last_operation.operation).to eq('passer_en_instruction')
|
||||
expect(last_operation.automatic_operation?).to be_truthy
|
||||
expect(dossier3.state).to eq Dossier.states.fetch(:en_instruction)
|
||||
expect(dossier4.state).to eq Dossier.states.fetch(:en_instruction)
|
||||
expect(dossier5.state).to eq Dossier.states.fetch(:en_instruction)
|
||||
|
|
|
@ -31,11 +31,13 @@ RSpec.describe AutoReceiveDossiersForProcedureJob, type: :job do
|
|||
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 {
|
||||
expect(nouveau_dossier1.en_instruction?).to be true
|
||||
expect(nouveau_dossier1.en_instruction_at).to eq(date)
|
||||
expect(nouveau_dossier1.dossier_operation_logs.pluck(:gestionnaire_id, :operation, :automatic_operation)).to match([[nil, 'passer_en_instruction', true]])
|
||||
expect(last_operation.operation).to eq('passer_en_instruction')
|
||||
expect(last_operation.automatic_operation?).to be_truthy
|
||||
|
||||
expect(nouveau_dossier2.en_instruction?).to be true
|
||||
expect(nouveau_dossier2.en_instruction_at).to eq(date)
|
||||
|
@ -50,13 +52,15 @@ RSpec.describe AutoReceiveDossiersForProcedureJob, type: :job do
|
|||
|
||||
context "accepte" do
|
||||
let(:state) { Dossier.states.fetch(:accepte) }
|
||||
let(:last_operation) { nouveau_dossier1.dossier_operation_logs.last }
|
||||
|
||||
it {
|
||||
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(nouveau_dossier1.dossier_operation_logs.pluck(:gestionnaire_id, :operation, :automatic_operation)).to match([[nil, 'accepter', true]])
|
||||
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)
|
||||
|
|
|
@ -764,6 +764,8 @@ describe Dossier do
|
|||
|
||||
describe '#accepter!' do
|
||||
let(:dossier) { create(:dossier) }
|
||||
let(:last_operation) { dossier.dossier_operation_logs.last }
|
||||
let(:operation_serialized) { JSON.parse(last_operation.serialized.download) }
|
||||
let!(:gestionnaire) { create(:gestionnaire) }
|
||||
let!(:now) { Time.zone.parse('01/01/2100') }
|
||||
let(:attestation) { Attestation.new }
|
||||
|
@ -783,13 +785,18 @@ describe Dossier do
|
|||
it { expect(dossier.en_instruction_at).to eq(now) }
|
||||
it { expect(dossier.processed_at).to eq(now) }
|
||||
it { expect(dossier.state).to eq('accepte') }
|
||||
it { expect(dossier.dossier_operation_logs.pluck(:gestionnaire_id, :operation, :automatic_operation)).to match([[gestionnaire.id, 'accepter', false]]) }
|
||||
it { expect(last_operation.operation).to eq('accepter') }
|
||||
it { expect(last_operation.automatic_operation?).to be_falsey }
|
||||
it { expect(operation_serialized['operation']).to eq('accepter') }
|
||||
it { expect(operation_serialized['dossier_id']).to eq(dossier.id) }
|
||||
it { expect(operation_serialized['executed_at']).to eq(last_operation.executed_at.iso8601) }
|
||||
it { expect(NotificationMailer).to have_received(:send_closed_notification).with(dossier) }
|
||||
it { expect(dossier.attestation).to eq(attestation) }
|
||||
end
|
||||
|
||||
describe '#accepter_automatiquement!' do
|
||||
let(:dossier) { create(:dossier) }
|
||||
let(:last_operation) { dossier.dossier_operation_logs.last }
|
||||
let!(:now) { Time.zone.parse('01/01/2100') }
|
||||
let(:attestation) { Attestation.new }
|
||||
|
||||
|
@ -808,30 +815,43 @@ describe Dossier do
|
|||
it { expect(dossier.en_instruction_at).to eq(now) }
|
||||
it { expect(dossier.processed_at).to eq(now) }
|
||||
it { expect(dossier.state).to eq('accepte') }
|
||||
it { expect(dossier.dossier_operation_logs.pluck(:gestionnaire_id, :operation, :automatic_operation)).to match([[nil, 'accepter', true]]) }
|
||||
it { expect(last_operation.operation).to eq('accepter') }
|
||||
it { expect(last_operation.automatic_operation?).to be_truthy }
|
||||
it { expect(NotificationMailer).to have_received(:send_closed_notification).with(dossier) }
|
||||
it { expect(dossier.attestation).to eq(attestation) }
|
||||
end
|
||||
|
||||
describe '#passer_en_instruction!' do
|
||||
let(:dossier) { create(:dossier) }
|
||||
let(:last_operation) { dossier.dossier_operation_logs.last }
|
||||
let(:operation_serialized) { JSON.parse(last_operation.serialized.download) }
|
||||
let(:gestionnaire) { create(:gestionnaire) }
|
||||
|
||||
before { dossier.passer_en_instruction!(gestionnaire) }
|
||||
|
||||
it { expect(dossier.state).to eq('en_instruction') }
|
||||
it { expect(dossier.followers_gestionnaires).to include(gestionnaire) }
|
||||
it { expect(dossier.dossier_operation_logs.pluck(:gestionnaire_id, :operation)).to match([[gestionnaire.id, 'passer_en_instruction']]) }
|
||||
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') }
|
||||
it { expect(operation_serialized['dossier_id']).to eq(dossier.id) }
|
||||
it { expect(operation_serialized['executed_at']).to eq(last_operation.executed_at.iso8601) }
|
||||
end
|
||||
|
||||
describe '#passer_automatiquement_en_instruction!' do
|
||||
let(:dossier) { create(:dossier) }
|
||||
let(:last_operation) { dossier.dossier_operation_logs.last }
|
||||
let(:operation_serialized) { JSON.parse(last_operation.serialized.download) }
|
||||
let(:gestionnaire) { create(:gestionnaire) }
|
||||
|
||||
before { dossier.passer_automatiquement_en_instruction! }
|
||||
|
||||
it { expect(dossier.followers_gestionnaires).not_to include(gestionnaire) }
|
||||
it { expect(dossier.dossier_operation_logs.pluck(:gestionnaire_id, :operation, :automatic_operation)).to match([[nil, 'passer_en_instruction', true]]) }
|
||||
it { expect(last_operation.operation).to eq('passer_en_instruction') }
|
||||
it { expect(last_operation.automatic_operation?).to be_truthy }
|
||||
it { expect(operation_serialized['operation']).to eq('passer_en_instruction') }
|
||||
it { expect(operation_serialized['dossier_id']).to eq(dossier.id) }
|
||||
it { expect(operation_serialized['executed_at']).to eq(last_operation.executed_at.iso8601) }
|
||||
end
|
||||
|
||||
describe "#check_mandatory_champs" do
|
||||
|
@ -934,6 +954,6 @@ describe Dossier do
|
|||
|
||||
it { expect(dossier.hidden_at).to eq(Time.zone.now) }
|
||||
it { expect(last_operation.operation).to eq('supprimer') }
|
||||
it { expect(last_operation.administration).to eq(administration) }
|
||||
it { expect(last_operation.automatic_operation?).to be_falsey }
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue