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"