diff --git a/app/controllers/concerns/create_avis_concern.rb b/app/controllers/concerns/create_avis_concern.rb
index 87143a3ce..7f9514509 100644
--- a/app/controllers/concerns/create_avis_concern.rb
+++ b/app/controllers/concerns/create_avis_concern.rb
@@ -29,6 +29,9 @@ module CreateAvisConcern
if persisted.any?
sent_emails_addresses = persisted.map(&:email_to_display).join(", ")
flash.notice = "Une demande d'avis a été envoyée à #{sent_emails_addresses}"
+ persisted.each do |avis|
+ dossier.demander_un_avis!(avis)
+ end
end
if failed.any?
diff --git a/app/controllers/gestionnaires/dossiers_controller.rb b/app/controllers/gestionnaires/dossiers_controller.rb
index a96616bfe..d7f7d06fa 100644
--- a/app/controllers/gestionnaires/dossiers_controller.rb
+++ b/app/controllers/gestionnaires/dossiers_controller.rb
@@ -136,8 +136,8 @@ module Gestionnaires
def update_annotations
dossier = current_gestionnaire.dossiers.includes(champs_private: :type_de_champ).find(params[:dossier_id])
- # FIXME: add attachements validation, cf. Champ#piece_justificative_file_errors
dossier.update(champs_private_params)
+ dossier.modifier_annotations!(current_gestionnaire)
redirect_to annotations_privees_gestionnaire_dossier_path(procedure, dossier)
end
diff --git a/app/javascript/components/TypesDeChampEditor/components/TypeDeChamp.js b/app/javascript/components/TypesDeChampEditor/components/TypeDeChamp.js
index 9038a1424..751f7702b 100644
--- a/app/javascript/components/TypesDeChampEditor/components/TypeDeChamp.js
+++ b/app/javascript/components/TypesDeChampEditor/components/TypeDeChamp.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { sortableElement, sortableHandle } from 'react-sortable-hoc';
+import { useInView } from 'react-intersection-observer';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import DescriptionInput from './DescriptionInput';
@@ -29,6 +30,10 @@ const TypeDeChamp = sortableElement(
const canBeMandatory =
!isHeaderSection && !isExplication && !state.isAnnotation;
+ const [ref, inView] = useInView({
+ threshold: [0.6]
+ });
+
const updateHandlers = createUpdateHandlers(
dispatch,
typeDeChamp,
@@ -42,8 +47,10 @@ const TypeDeChamp = sortableElement(
return (
{
+ .then(async () => {
+ if (insertAfter) {
+ // Move the champ to the correct position server-side
+ await moveTypeDeChampOperation(
+ typeDeChamp,
+ insertAfter.index,
+ state.queue
+ );
+ }
state.flash.success();
done();
- if (state.lastTypeDeChampRef) {
- scrollToComponent(state.lastTypeDeChampRef.current);
+ if (insertAfter) {
+ scrollToComponent(insertAfter.target.nextElementSibling);
}
})
.catch(message => state.flash.error(message));
+ let newTypeDeChamps = [...typeDeChamps, typeDeChamp];
+ if (insertAfter) {
+ // Move the champ to the correct position client-side
+ newTypeDeChamps = arrayMove(
+ newTypeDeChamps,
+ typeDeChamps.length,
+ insertAfter.index
+ );
+ }
+
return {
...state,
- typeDeChamps: [...typeDeChamps, typeDeChamp]
+ typeDeChamps: newTypeDeChamps
};
}
+function addNewTypeDeChamp(state, typeDeChamps, done) {
+ return addTypeDeChamp(state, typeDeChamps, findItemToInsertAfter(), done);
+}
+
function addNewRepetitionTypeDeChamp(state, typeDeChamps, typeDeChamp, done) {
- return addNewTypeDeChamp(
+ return addTypeDeChamp(
{
...state,
defaultTypeDeChampAttributes: {
@@ -69,6 +91,7 @@ function addNewRepetitionTypeDeChamp(state, typeDeChamps, typeDeChamp, done) {
}
},
typeDeChamps,
+ null,
done
);
}
@@ -182,3 +205,23 @@ function getUpdateHandler(typeDeChamp, { queue, flash }) {
}
return handler;
}
+
+function findItemToInsertAfter() {
+ const target = getFirstTarget();
+
+ return {
+ target,
+ index: parseInt(target.dataset.index) + 1
+ };
+}
+
+function getFirstTarget() {
+ const [target] = document.querySelectorAll('[data-in-view]');
+ if (target) {
+ const parentTarget = target.closest('[data-repetition]');
+ if (parentTarget) {
+ return parentTarget;
+ }
+ return target;
+ }
+}
diff --git a/app/javascript/shared/polyfills.js b/app/javascript/shared/polyfills.js
index a19bbef8c..91124f495 100644
--- a/app/javascript/shared/polyfills.js
+++ b/app/javascript/shared/polyfills.js
@@ -5,3 +5,7 @@ import '@babel/polyfill';
import 'dom4';
import './polyfills/insertAdjacentElement';
import './polyfills/dataset';
+
+if (typeof window.IntersectionObserver === 'undefined') {
+ import('intersection-observer');
+}
diff --git a/app/models/dossier.rb b/app/models/dossier.rb
index 39dc2c7ae..abcfd983f 100644
--- a/app/models/dossier.rb
+++ b/app/models/dossier.rb
@@ -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
@@ -366,20 +366,33 @@ class Dossier < ApplicationRecord
end
end
+ def modifier_annotations!(gestionnaire)
+ champs_private.select(&:value_previously_changed?).each do |champ|
+ log_dossier_operation(gestionnaire, :modifier_annotation, champ)
+ end
+ end
+
+ def demander_un_avis!(avis)
+ log_dossier_operation(avis.claimant, :demander_un_avis, avis)
+ end
+
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
diff --git a/app/models/dossier_operation_log.rb b/app/models/dossier_operation_log.rb
index 6dced86b6..e517dcb36 100644
--- a/app/models/dossier_operation_log.rb
+++ b/app/models/dossier_operation_log.rb
@@ -5,10 +5,76 @@ class DossierOperationLog < ApplicationRecord
accepter: 'accepter',
refuser: 'refuser',
classer_sans_suite: 'classer_sans_suite',
- supprimer: 'supprimer'
+ supprimer: 'supprimer',
+ modifier_annotation: 'modifier_annotation',
+ demander_un_avis: 'demander_un_avis'
}
belongs_to :dossier
- belongs_to :gestionnaire
- belongs_to :administration
+ has_one_attached :serialized
+
+ def self.create_and_serialize(params)
+ dossier = params.fetch(:dossier)
+
+ duree_conservation_dossiers = dossier.procedure.duree_conservation_dossiers_dans_ds
+ keep_until = if duree_conservation_dossiers.present?
+ if dossier.en_instruction_at
+ dossier.en_instruction_at + duree_conservation_dossiers.months
+ else
+ dossier.created_at + duree_conservation_dossiers.months
+ end
+ end
+
+ operation_log = new(operation: params.fetch(:operation),
+ dossier_id: dossier.id,
+ keep_until: 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
+ when Champ
+ ChampSerializer.new(subject).as_json
+ when Avis
+ AvisSerializer.new(subject).as_json
+ end
+ end
+ end
end
diff --git a/app/serializers/avis_serializer.rb b/app/serializers/avis_serializer.rb
new file mode 100644
index 000000000..7811c1152
--- /dev/null
+++ b/app/serializers/avis_serializer.rb
@@ -0,0 +1,19 @@
+class AvisSerializer < ActiveModel::Serializer
+ attributes :email,
+ :answer,
+ :introduction,
+ :created_at,
+ :answered_at
+
+ def email
+ object.email_to_display
+ end
+
+ def created_at
+ object.created_at&.in_time_zone('UTC')
+ end
+
+ def answered_at
+ object.updated_at&.in_time_zone('UTC')
+ end
+end
diff --git a/app/serializers/operation_author_serializer.rb b/app/serializers/operation_author_serializer.rb
new file mode 100644
index 000000000..0856a1e90
--- /dev/null
+++ b/app/serializers/operation_author_serializer.rb
@@ -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
diff --git a/app/views/admin/procedures/_informations.html.haml b/app/views/admin/procedures/_informations.html.haml
index c93d70c6b..2bf4511e8 100644
--- a/app/views/admin/procedures/_informations.html.haml
+++ b/app/views/admin/procedures/_informations.html.haml
@@ -30,7 +30,7 @@
%li Texte de loi (loi, décret, circulaire, arrêté,…)
%li Texte juridique (statuts, délibération, décision du conseil d'administration…)
%li
- = link_to("En savoir plus", CADRE_JURIDIQUE_URL, target: "_blank", rel: "noopener")
+ = link_to("En savoir plus avec cette vidéo de 5 minutes", CADRE_JURIDIQUE_URL, target: "_blank", rel: "noopener")
%p.help-block
%i.fa.fa-info-circle
diff --git a/app/views/admin/procedures/_modal_publish.html.haml b/app/views/admin/procedures/_modal_publish.html.haml
index 686da7c15..202f1f6bd 100644
--- a/app/views/admin/procedures/_modal_publish.html.haml
+++ b/app/views/admin/procedures/_modal_publish.html.haml
@@ -31,6 +31,13 @@
%br
.alert.alert-info
Attention, diffusez toujours le
lien complet affiché ci-dessus, et non pas un lien générique vers demarches-simplifiees.fr. Ne dites pas non plus aux usagers de se rendre sur le site générique demarches-simplifiees.fr, donnez-leur toujours le lien complet.
+ %br
+ %br
+ Prenez quelques minutes pour savoir comment établir une bonne relation avec les usagers de votre démarche :
+ = link_to "Regarder la vidéo de 5 minutes",
+ "https://vimeo.com/334463514",
+ target: "_blank"
+
#path-messages
#path_is_mine.text-warning.center.message
Ce lien est déjà utilisé par une de vos démarche.
diff --git a/app/views/admin/procedures/new_from_existing.html.haml b/app/views/admin/procedures/new_from_existing.html.haml
index c13262c8a..2800c40b4 100644
--- a/app/views/admin/procedures/new_from_existing.html.haml
+++ b/app/views/admin/procedures/new_from_existing.html.haml
@@ -2,9 +2,35 @@
- if current_administrateur.procedures.brouillons.count == 0
.card.feedback
.card-title
- Bienvenue,
+ Bienvenue,
+ %br
vous allez pouvoir créer une première démarche de test.
Celle-ci sera visible uniquement par vous et ne sera publiée nulle part, alors pas de crainte à avoir.
+ %br
+ %br
+ Besoin d'aide ?
+ %br
+ > Nous proposons des ateliers en ligne pour vous aider à créer votre 1er formulaire et répondre à vos questions :
+ = link_to "inscrivez-vous ici",
+ "https://vimeo.com/334463514",
+ target: "_blank"
+ %br
+ > Vous pouvez
+ = link_to "visionner cette vidéo",
+ "https://vimeo.com/261478872",
+ target: "_blank"
+ %br
+ > Vous pouvez lire notre
+ = link_to "documentation en ligne",
+ "https://doc.demarches-simplifiees.fr/tutoriels/tutoriel-administrateur",
+ target: "_blank"
+
+ %br
+ > Vous pouvez enfin
+ = link_to "prendre un rendez-vous téléphonique avec nous",
+ "https://calendly.com/demarches-simplifiees/accompagnement-administrateur-demarches-simplifiees-fr",
+ target: "_blank"
+
.form
.send-wrapper
diff --git a/app/views/admin/procedures/show.html.haml b/app/views/admin/procedures/show.html.haml
index 84a5a260e..ea2c586b4 100644
--- a/app/views/admin/procedures/show.html.haml
+++ b/app/views/admin/procedures/show.html.haml
@@ -81,6 +81,11 @@
%p
Une fois que vous êtes prêt à publier définitivement votre démarche, cliquez sur le bouton "Publier" pour choisir le lien définitif de votre démarche, les modifications sur la démarches ne seront alors plus possibles.
+ %br
+ %h4 Prenez quelques minutes pour savoir comment établir une bonne relation avec les usagers de votre démarche:
+ %p.center
+ %br
+ %iframe{ :src =>"https://player.vimeo.com/video/334463514?color=0069CC",:width =>"640",:height =>"360",:frameborder => "0" }
- else
.alert.alert-info
Pour pouvoir tester cette démarche, vous devez d’abord lui affecter
diff --git a/app/views/layouts/_footer.html.haml b/app/views/layouts/_footer.html.haml
index 4ef07e77c..4310d5bd8 100644
--- a/app/views/layouts/_footer.html.haml
+++ b/app/views/layouts/_footer.html.haml
@@ -13,4 +13,6 @@
\-
= link_to 'Documentation', DOC_URL
\-
- = link_to 'Aide', FAQ_URL
+ = link_to 'FAQ', FAQ_ADMIN_URL
+ \-
+ = link_to 'Inscription ateliers en ligne', WEBINAIRE_URL
diff --git a/app/views/layouts/_navbar.html.haml b/app/views/layouts/_navbar.html.haml
index 7b0fc7741..2fe826508 100644
--- a/app/views/layouts/_navbar.html.haml
+++ b/app/views/layouts/_navbar.html.haml
@@ -10,7 +10,7 @@
#navbar-body
.row
%div{ style: "vertical-align: middle;float:left;position:absolute;line-height: 60px;z-index:2;" }
- Besoin d'aide ?
#{CONTACT_PHONE} ou
par email
+ Besoin d'aide?
#{CONTACT_PHONE} ou
email ou
prenez rendez-vous avec nous
-# BEST WTF EVER
-# this begin rescue hides potentials bugs by displaying another navbar
- begin
diff --git a/app/views/manager/application/_javascript.html.erb b/app/views/manager/application/_javascript.html.erb
index 3b7d1a319..353b1888f 100644
--- a/app/views/manager/application/_javascript.html.erb
+++ b/app/views/manager/application/_javascript.html.erb
@@ -11,7 +11,7 @@ by providing a `content_for(:javascript)` block.
<%= javascript_include_tag js_path %>
<% end %>
-<%= javascript_pack_tag 'manager' %>
+<%= javascript_packs_with_chunks_tag 'manager' %>
<%= yield :javascript %>
diff --git a/config/features.rb b/config/features.rb
index f42abcf32..13f6d592a 100644
--- a/config/features.rb
+++ b/config/features.rb
@@ -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'
diff --git a/config/initializers/urls.rb b/config/initializers/urls.rb
index 2a06cbed5..92a27a505 100644
--- a/config/initializers/urls.rb
+++ b/config/initializers/urls.rb
@@ -15,13 +15,14 @@ FOG_BASE_URL = "https://static.demarches-simplifiees.fr"
DOC_URL = "https://doc.demarches-simplifiees.fr"
ADMINISTRATEUR_TUTORIAL_URL = [DOC_URL, "tutoriels", "tutoriel-administrateur"].join("/")
INSTRUCTEUR_TUTORIAL_URL = [DOC_URL, "tutoriels", "tutoriel-accompagnateur"].join("/")
-CADRE_JURIDIQUE_URL = [ADMINISTRATEUR_TUTORIAL_URL, "cadre-juridique"].join("#")
-WEBINAIRE_URL = [DOC_URL, "pour-aller-plus-loin", "webinaires"].join("/")
+CADRE_JURIDIQUE_URL = [DOC_URL, "tutoriels/video-le-cadre-juridique"].join("/")
+WEBINAIRE_URL = "https://app.livestorm.co/demarches-simplifiees"
LISTE_DES_DEMARCHES_URL = [DOC_URL, "listes-des-demarches"].join("/")
CGU_URL = [DOC_URL, "cgu"].join("/")
MENTIONS_LEGALES_URL = [CGU_URL, "4-mentions-legales"].join("#")
API_DOC_URL = [DOC_URL, "pour-aller-plus-loin", "api"].join("/")
WEBHOOK_DOC_URL = [DOC_URL, "pour-aller-plus-loin", "webhook"].join("/")
FAQ_URL = "https://faq.demarches-simplifiees.fr"
+FAQ_ADMIN_URL = "https://faq.demarches-simplifiees.fr/collection/1-administrateur"
STATUS_PAGE_URL = "https://status.demarches-simplifiees.fr"
MATOMO_IFRAME_URL = "https://stats.data.gouv.fr/index.php?module=CoreAdminHome&action=optOut&language=fr&&fontColor=333333&fontSize=16px&fontFamily=Muli"
diff --git a/db/migrate/20190327102360_add_digest_and_timestamps_to_dossier_operation_logs.rb b/db/migrate/20190327102360_add_digest_and_timestamps_to_dossier_operation_logs.rb
new file mode 100644
index 000000000..235e78f78
--- /dev/null
+++ b/db/migrate/20190327102360_add_digest_and_timestamps_to_dossier_operation_logs.rb
@@ -0,0 +1,8 @@
+class AddDigestAndTimestampsToDossierOperationLogs < ActiveRecord::Migration[5.2]
+ def change
+ add_column :dossier_operation_logs, :keep_until, :datetime
+ add_column :dossier_operation_logs, :executed_at, :datetime
+ add_column :dossier_operation_logs, :digest, :text
+ add_index :dossier_operation_logs, :keep_until
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index aeceeaf2f..c0f9b1a41 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: 2019_03_27_102357) do
+ActiveRecord::Schema.define(version: 2019_03_27_102360) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -221,9 +221,13 @@ ActiveRecord::Schema.define(version: 2019_03_27_102357) do
t.datetime "updated_at", null: false
t.boolean "automatic_operation", default: false, null: false
t.bigint "administration_id"
+ t.datetime "keep_until"
+ t.datetime "executed_at"
+ t.text "digest"
t.index ["administration_id"], name: "index_dossier_operation_logs_on_administration_id"
t.index ["dossier_id"], name: "index_dossier_operation_logs_on_dossier_id"
t.index ["gestionnaire_id"], name: "index_dossier_operation_logs_on_gestionnaire_id"
+ t.index ["keep_until"], name: "index_dossier_operation_logs_on_keep_until"
end
create_table "dossiers", id: :serial, force: :cascade do |t|
diff --git a/package.json b/package.json
index 61fbe0f21..22dbdf814 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"debounce": "^1.2.0",
"dom4": "^2.1.4",
"highcharts": "^6.1.2",
+ "intersection-observer": "^0.6.0",
"jquery": "^3.4.1",
"leaflet": "^1.4.0",
"leaflet-freedraw": "^2.10.0",
@@ -23,6 +24,7 @@
"ramda": "=0.24.1",
"react": "^16.8.6",
"react-dom": "^16.8.6",
+ "react-intersection-observer": "^8.23.0",
"react-scroll-to-component": "^1.0.2",
"react-sortable-hoc": "^1.7.1",
"react_ujs": "^2.5.0",
diff --git a/spec/jobs/auto_archive_procedure_job_spec.rb b/spec/jobs/auto_archive_procedure_job_spec.rb
index 2103f8a8a..8e0225357 100644
--- a/spec/jobs/auto_archive_procedure_job_spec.rb
+++ b/spec/jobs/auto_archive_procedure_job_spec.rb
@@ -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)
diff --git a/spec/jobs/auto_receive_dossiers_for_procedure_job_spec.rb b/spec/jobs/auto_receive_dossiers_for_procedure_job_spec.rb
index a2ab0a799..517a6dde9 100644
--- a/spec/jobs/auto_receive_dossiers_for_procedure_job_spec.rb
+++ b/spec/jobs/auto_receive_dossiers_for_procedure_job_spec.rb
@@ -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)
diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb
index ea85160af..a39b96117 100644
--- a/spec/models/dossier_spec.rb
+++ b/spec/models/dossier_spec.rb
@@ -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
diff --git a/yarn.lock b/yarn.lock
index 1b9ccd07f..c54641d3f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -688,6 +688,13 @@
"@babel/plugin-transform-react-jsx-self" "^7.0.0"
"@babel/plugin-transform-react-jsx-source" "^7.0.0"
+"@babel/runtime@^7.0.0":
+ version "7.4.4"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.4.tgz#dc2e34982eb236803aa27a07fea6857af1b9171d"
+ integrity sha512-w0+uT71b6Yi7i5SE0co4NioIpSYS6lLiXvCzWzGSKvpK5vdQtCbICHMj+gbAKAOtxiV6HsVh/MBdaF9EQ6faSg==
+ dependencies:
+ regenerator-runtime "^0.13.2"
+
"@babel/runtime@^7.2.0", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.2":
version "7.4.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.3.tgz#79888e452034223ad9609187a0ad1fe0d2ad4bdc"
@@ -4166,9 +4173,14 @@ internal-ip@^4.2.0:
ipaddr.js "^1.9.0"
interpret@^1.1.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296"
- integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
+ integrity sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=
+
+intersection-observer@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/intersection-observer/-/intersection-observer-0.6.0.tgz#d64aae04211b4cec051537168f5fa670a4acc770"
+ integrity sha512-WUVAqGJr08yh73XKe1JhylQ9BiBIytrkt8SH5Knu7Uy44ij5cICi6PbVLIbV/D2eIx9LJVkGBo9WF80R4VXJ+w==
invariant@^2.2.2, invariant@^2.2.4:
version "2.2.4"
@@ -6737,6 +6749,14 @@ react-dom@^16.8.6:
prop-types "^15.6.2"
scheduler "^0.13.6"
+react-intersection-observer@^8.23.0:
+ version "8.23.0"
+ resolved "https://registry.yarnpkg.com/react-intersection-observer/-/react-intersection-observer-8.23.0.tgz#1533aaf39cc70300ff8ca37e6551d2d68a589cd0"
+ integrity sha512-kHXfxhGKvVDNkrvmh9CKCnAWvJBigyB7oSDzMXL54weFDwwI4WfTr58YauZ0RRPkGzoD/hYEuzfS1wipXn23fA==
+ dependencies:
+ "@babel/runtime" "^7.0.0"
+ invariant "^2.2.4"
+
react-is@^16.8.1:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"