2019-06-17-01 (#3959)

2019-06-17-01
This commit is contained in:
Pierre de La Morinerie 2019-06-17 11:24:37 +02:00 committed by GitHub
commit f13eed9123
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 294 additions and 124 deletions

View file

@ -139,7 +139,7 @@ GEM
carrierwave-i18n (0.2.0)
case_transform (0.2)
activesupport
chartkick (3.0.2)
chartkick (3.2.0)
childprocess (0.9.0)
ffi (~> 1.0, >= 1.0.11)
chunky_png (1.3.11)

View file

@ -37,6 +37,8 @@ module Gestionnaires
def personnes_impliquees
@following_instructeurs_emails = dossier.followers_gestionnaires.pluck(:email)
previous_followers = dossier.previous_followers_gestionnaires - dossier.followers_gestionnaires
@previous_following_instructeurs_emails = previous_followers.pluck(:email)
@avis_emails = dossier.avis.includes(:gestionnaire).map(&:email_to_display)
@invites_emails = dossier.invites.map(&:email)
@potential_recipients = procedure.gestionnaires.reject { |g| g == current_gestionnaire }

View file

@ -112,4 +112,12 @@ module ApplicationHelper
root_path
end
end
def try_format_date(date)
date.present? ? I18n.l(date) : ''
end
def try_format_datetime(datetime)
datetime.present? ? I18n.l(datetime) : ''
end
end

View file

@ -34,7 +34,7 @@ class ApiEntreprise::API
if response.success?
JSON.parse(response.body, symbolize_names: true)
elsif response.code == 404 || response.code == 422
elsif response.code&.between?(401, 499)
raise RestClient::ResourceNotFound
else
raise RestClient::RequestFailed

View file

@ -6,7 +6,7 @@ class Champs::DateChamp < Champ
end
def to_s
value.present? ? Date.parse(value).strftime('%d/%m/%Y') : ""
value.present? ? I18n.l(Date.parse(value)) : ""
end
private

View file

@ -5,6 +5,10 @@ class Champs::DatetimeChamp < Champ
# Text search is pretty useless for datetimes so were not including these champs
end
def to_s
value.present? ? I18n.l(Time.zone.parse(value)) : ""
end
private
def format_before_save

View file

@ -26,8 +26,10 @@ class Dossier < ApplicationRecord
has_many :champs_private, -> { root.private_only.ordered }, class_name: 'Champ', dependent: :destroy
has_many :commentaires, dependent: :destroy
has_many :invites, dependent: :destroy
has_many :follows
has_many :follows, -> { active }
has_many :previous_follows, -> { inactive }, class_name: 'Follow'
has_many :followers_gestionnaires, through: :follows, source: :gestionnaire
has_many :previous_followers_gestionnaires, -> { distinct }, through: :previous_follows, source: :gestionnaire
has_many :avis, dependent: :destroy
has_many :dossier_operation_logs, dependent: :destroy

View file

@ -2,10 +2,13 @@ class Follow < ApplicationRecord
belongs_to :gestionnaire
belongs_to :dossier
validates :gestionnaire_id, uniqueness: { scope: :dossier_id }
validates :gestionnaire_id, uniqueness: { scope: [:dossier_id, :unfollowed_at] }
before_create :set_default_date
scope :active, -> { where(unfollowed_at: nil) }
scope :inactive, -> { where.not(unfollowed_at: nil) }
private
def set_default_date

View file

@ -16,8 +16,10 @@ class Gestionnaire < ApplicationRecord
has_many :procedures_with_email_notifications, through: :assign_to_with_email_notifications, source: :procedure
has_many :dossiers, -> { state_not_brouillon }, through: :procedures
has_many :follows
has_many :follows, -> { active }
has_many :previous_follows, -> { inactive }, class_name: 'Follow'
has_many :followed_dossiers, through: :follows, source: :dossier
has_many :previously_followed_dossiers, -> { distinct }, through: :previous_follows, source: :dossier
has_many :avis
has_many :dossiers_from_avis, through: :avis, source: :dossier
has_many :trusted_device_tokens
@ -27,26 +29,23 @@ class Gestionnaire < ApplicationRecord
end
def follow(dossier)
if follow?(dossier)
return
end
begin
followed_dossiers << dossier
# If the user tries to follow a dossier she already follows,
# we just fail silently: it means the goal is already reached.
rescue ActiveRecord::RecordNotUnique
# Altough we checked before the insertion that the gestionnaire wasn't
# already following this dossier, this was done at the Rails level:
# at the database level, the dossier was already followed, and a
# "invalid constraint" exception is raised.
#
# We can ignore this safely, as it means the goal is already reached:
# the gestionnaire follows the dossier.
return
# Database uniqueness constraint
rescue ActiveRecord::RecordInvalid => e
# ActiveRecord validation
raise unless e.record.errors.details.dig(:gestionnaire_id, 0, :error) == :taken
end
end
def unfollow(dossier)
followed_dossiers.delete(dossier)
f = follows.find_by(dossier: dossier)
if f.present?
f.update(unfollowed_at: Time.zone.now)
end
end
def follow?(dossier)
@ -94,26 +93,20 @@ class Gestionnaire < ApplicationRecord
.find_by(gestionnaire: self, dossier: dossier)
if follow.present?
# retirer le seen_at.present? une fois la contrainte de presence en base (et les migrations ad hoc)
champs_publiques = follow.demande_seen_at.present? &&
follow.dossier.champs.updated_since?(follow.demande_seen_at).any?
champs_publiques = follow.dossier.champs.updated_since?(follow.demande_seen_at).any?
pieces_justificatives = follow.demande_seen_at.present? &&
follow.dossier.pieces_justificatives.updated_since?(follow.demande_seen_at).any?
pieces_justificatives = follow.dossier.pieces_justificatives.updated_since?(follow.demande_seen_at).any?
demande = champs_publiques || pieces_justificatives
annotations_privees = follow.annotations_privees_seen_at.present? &&
follow.dossier.champs_private.updated_since?(follow.annotations_privees_seen_at).any?
annotations_privees = follow.dossier.champs_private.updated_since?(follow.annotations_privees_seen_at).any?
avis_notif = follow.avis_seen_at.present? &&
follow.dossier.avis.updated_since?(follow.avis_seen_at).any?
avis_notif = follow.dossier.avis.updated_since?(follow.avis_seen_at).any?
messagerie = follow.messagerie_seen_at.present? &&
dossier.commentaires
.where.not(email: OLD_CONTACT_EMAIL)
.where.not(email: CONTACT_EMAIL)
.updated_since?(follow.messagerie_seen_at).any?
messagerie = dossier.commentaires
.where.not(email: OLD_CONTACT_EMAIL)
.where.not(email: CONTACT_EMAIL)
.updated_since?(follow.messagerie_seen_at).any?
annotations_hash(demande, annotations_privees, avis_notif, messagerie)
else

View file

@ -25,9 +25,16 @@ class PieceJustificativeToChampPieceJointeMigrationService
populate_champs_pjs!(procedure, types_de_champ_pj, &progress)
# Only destroy the old types PJ once everything has been safely migrated to
# champs PJs. Destroying the types PJ will cascade and destroy the PJs,
# and delete the linked objects from remote storage. This means that no other
# cleanup action is required.
# champs PJs.
# First destroy the individual PJ champs on all dossiers.
# It will cascade and destroy the PJs, and delete the linked objects from remote storage.
procedure.dossiers.unscope(where: :hidden_at).includes(:champs).find_each do |dossier|
destroy_pieces_justificatives(dossier)
end
# Now we can destroy the type de champ themselves,
# without cascading the timestamp update on all attached dossiers.
procedure.types_de_piece_justificative.destroy_all
end
@ -40,32 +47,7 @@ class PieceJustificativeToChampPieceJointeMigrationService
# Unscope to make sure all dossiers are migrated, even the soft-deleted ones
procedure.dossiers.unscope(where: :hidden_at).includes(:champs).find_each do |dossier|
# Add the new pieces justificatives champs to the dossier
champs_pj = types_de_champ_pj.map(&:build_champ)
dossier.champs += champs_pj
# Copy the dossier old pieces jointes to the new champs
# (even if the champs already existed, so that we ensure a clean state)
champs_pj.each do |champ|
type_pj_id = champ.type_de_champ.old_pj&.fetch('stable_id', nil)
pj = dossier.retrieve_last_piece_justificative_by_type(type_pj_id)
if pj.present?
convert_pj_to_champ!(pj, champ)
champ.update(
updated_at: pj.updated_at,
created_at: pj.created_at
)
else
champ.update(
updated_at: dossier.updated_at,
created_at: dossier.created_at
)
end
yield if block_given?
end
migrate_dossier!(dossier, types_de_champ_pj, &progress)
end
rescue StandardError, SignalException
@ -74,8 +56,11 @@ class PieceJustificativeToChampPieceJointeMigrationService
rake_puts "Error received. Rolling back migration of procedure #{procedure.id}"
types_de_champ_pj.each do |type_champ|
type_champ.champ.each { |c| c.piece_justificative_file.purge }
type_champ.destroy
# First destroy all the individual champs on dossiers
type_champ.champ.each { |c| destroy_champ_pj(c.dossier.reload, c) }
# Now we can destroy the type de champ itself,
# without cascading the timestamp update on all attached dossiers.
type_champ.reload.destroy
end
rake_puts "Migration of procedure #{procedure.id} rolled back."
@ -84,6 +69,39 @@ class PieceJustificativeToChampPieceJointeMigrationService
raise
end
def migrate_dossier!(dossier, types_de_champ_pj)
# Add the new pieces justificatives champs to the dossier
champs_pj = types_de_champ_pj.map(&:build_champ)
preserving_updated_at(dossier) do
dossier.champs += champs_pj
end
# Copy the dossier old pieces jointes to the new champs
# (even if the champs already existed, so that we ensure a clean state)
champs_pj.each do |champ|
type_pj_id = champ.type_de_champ.old_pj&.fetch('stable_id', nil)
pj = dossier.retrieve_last_piece_justificative_by_type(type_pj_id)
if pj.present?
preserving_updated_at(dossier) do
convert_pj_to_champ!(pj, champ)
end
champ.update_columns(
updated_at: pj.updated_at,
created_at: pj.created_at
)
else
champ.update_columns(
updated_at: dossier.updated_at,
created_at: dossier.created_at
)
end
yield if block_given?
end
end
def convert_pj_to_champ!(pj, champ)
actual_file_exists = pj.content.file.send(:file)
if actual_file_exists
@ -133,4 +151,26 @@ class PieceJustificativeToChampPieceJointeMigrationService
def make_empty_blob(pj)
storage_service.make_empty_blob(pj.content, pj.updated_at.iso8601, filename: pj.original_filename)
end
def preserving_updated_at(model)
original_modification_date = model.updated_at
begin
yield
ensure
model.update_column(:updated_at, original_modification_date)
end
end
def destroy_pieces_justificatives(dossier)
preserving_updated_at(dossier) do
dossier.pieces_justificatives.destroy_all
end
end
def destroy_champ_pj(dossier, champ)
preserving_updated_at(dossier) do
champ.piece_justificative_file.purge
champ.destroy
end
end
end

View file

@ -20,9 +20,9 @@
- if procedure.publiee?
%td.procedure-lien= link_to(procedure_lien(procedure), procedure_lien(procedure))
- if procedure.publiee_ou_archivee?
%td= link_to(procedure.published_at.present? ? procedure.published_at.strftime('%d/%m/%Y %H:%M') : "", admin_procedure_href)
%td= link_to(procedure.published_at.present? ? try_format_datetime(procedure.published_at) : "", admin_procedure_href)
- else
%td= link_to(procedure.created_at.strftime('%d/%m/%Y %H:%M'), admin_procedure_href)
%td= link_to(try_format_datetime(procedure.created_at), admin_procedure_href)
%td
= link_to('Cloner', admin_procedure_clone_path(procedure.id), data: { method: :put }, class: 'btn-sm btn-primary clone-btn')
- if !procedure.publiee_ou_archivee?

View file

@ -5,7 +5,7 @@
%p
Vous avez fait la demande dun compte administrateur sur demarches-simplifiees.fr.
Votre compte a été créé mais reste inactif, il arrivera à expiration le #{@expiration_date.strftime("%d/%m/%Y")}
Votre compte a été créé mais reste inactif, il arrivera à expiration le #{try_format_date(@expiration_date)}
%p
Afin dactiver votre compte, veuillez cliquer sur le lien ci-dessous :

View file

@ -5,7 +5,10 @@
.personnes-impliquees.container
= render partial: 'gestionnaires/dossiers/envoyer_dossier_block', locals: { dossier: @dossier, potential_recipients: @potential_recipients }
= render partial: 'gestionnaires/dossiers/personnes_impliquees_block', locals: { emails_collection: @following_instructeurs_emails, title: "Instructeurs qui suivent le dossier", blank: "Aucun instructeur ne suit ce dossier" }
= render partial: 'gestionnaires/dossiers/personnes_impliquees_block', locals: { emails_collection: @following_instructeurs_emails, title: "Instructeurs qui suivent actuellement le dossier", blank: "Aucun instructeur ne suit ce dossier" }
- if @previous_following_instructeurs_emails.present?
= render partial: 'gestionnaires/dossiers/personnes_impliquees_block', locals: { emails_collection: @previous_following_instructeurs_emails, title: "Instructeurs ayant précédemment suivi le dossier", blank: " " }
= render partial: 'gestionnaires/dossiers/personnes_impliquees_block', locals: { emails_collection: @avis_emails, title: "Personnes à qui un avis a été demandé", blank: "Aucun avis n'a été demandé" }

View file

@ -0,0 +1,9 @@
= form_for procedure.administrateurs.new,
url: { controller: 'procedure_administrateurs' },
html: { class: 'form', id: "procedure-#{procedure.id}-new_administrateur" } ,
remote: true do |f|
= f.label :email do
Ajouter un administrateur
%span.notice= "Renseignez lemail dun administrateur déjà enregistré sur demarches-simplifiees.fr pour lui permettre de modifier « #{procedure.libelle} »."
= f.email_field :email, placeholder: 'marie.dupont@exemple.fr', required: true
= f.submit 'Ajouter comme administrateur', class: 'button primary send'

View file

@ -1,6 +1,6 @@
%tr{ id: "procedure-#{@procedure.id}-administrateur-#{administrateur.id}" }
%td= administrateur.email
%td= administrateur.created_at.strftime('%d/%m/%Y %H:%M')
%td= try_format_datetime(administrateur.created_at)
%td= administrateur.registration_state
%td
- if administrateur == current_administrateur

View file

@ -3,4 +3,7 @@
= append_to_element("#procedure-#{@procedure.id}-administrateurs",
partial: 'administrateur',
locals: { administrateur: @administrateur })
document.getElementById('procedure-#{@procedure.id}-new_administrateur').reset()
= render_to_element("#procedure-#{@procedure.id}-new_administrateur",
partial: 'add_admin_form',
outer: true,
locals: { procedure: @procedure })

View file

@ -15,12 +15,4 @@
%tfoot
%tr
%th{ colspan: 4 }
= form_for @procedure.administrateurs.new,
url: { controller: 'procedure_administrateurs' },
html: { class: 'form', id: "procedure-#{@procedure.id}-new_administrateur" } ,
remote: true do |f|
= f.label :email do
Ajouter un administrateur
%span.notice= "Renseignez lemail dun administrateur déjà enregistré sur demarches-simplifiees.fr pour lui permettre de modifier « #{@procedure.libelle} »."
= f.email_field :email, placeholder: 'marie.dupont@exemple.fr', required: true
= f.submit 'Ajouter comme administrateur', class: 'button primary send'
= render 'add_admin_form', procedure: @procedure

View file

@ -30,6 +30,10 @@
= render partial: "shared/champs/siret/show", locals: { champ: c, profile: profile }
- when TypeDeChamp.type_champs.fetch(:textarea)
= render partial: "shared/champs/textarea/show", locals: { champ: c }
- when TypeDeChamp.type_champs.fetch(:date)
= c.to_s
- when TypeDeChamp.type_champs.fetch(:datetime)
= c.to_s
- else
= sanitize(c.to_s)
@ -37,4 +41,4 @@
%td.updated-at
%span{ class: highlight_if_unseen_class(demande_seen_at, c.updated_at) }
modifié le
= c.updated_at.strftime("%d/%m/%Y à %H:%M")
= try_format_datetime(c.updated_at)

View file

@ -11,10 +11,12 @@
= form_for dossier, form_options do |f|
.prologue
.mandatory-explanation
%p.mandatory-explanation
Les champs avec un astérisque (
%span.mandatory> *
) sont obligatoires. Pour enregistrer les informations saisies, cliquez sur le bouton « enregistrer le brouillon » en bas à gauche du formulaire.
) sont obligatoires.
%p.mandatory-explanation
Pour enregistrer votre dossier et le reprendre plus tard, cliquez sur le bouton « Enregistrer le brouillon » en bas à gauche du formulaire.
- if notice_url(dossier.procedure).present?
= link_to notice_url(dossier.procedure), target: '_blank', rel: 'noopener', class: 'button notice', title: "Pour vous aider à remplir votre dossier, vous pouvez consulter le guide de cette démarche." do

View file

@ -22,7 +22,7 @@
%td= etablissement.naf
%tr
%th.libelle Date de création :
%td= etablissement.entreprise.date_creation&.strftime("%d/%m/%Y")
%td= try_format_date(etablissement.entreprise.date_creation)
%tr
%th.libelle Effectif de l'organisation :
%td= effectif(etablissement)
@ -64,10 +64,10 @@
%td= etablissement.association_objet
%tr
%th.libelle Date de création :
%td= etablissement.association_date_creation&.strftime("%d/%m/%Y")
%td= try_format_date(etablissement.association_date_creation)
%tr
%th.libelle Date de publication :
%td= etablissement.association_date_publication&.strftime("%d/%m/%Y")
%td= try_format_date(etablissement.association_date_publication)
%tr
%th.libelle Date de déclaration :
%td= etablissement.association_date_declaration&.strftime("%d/%m/%Y")
%td= try_format_date(etablissement.association_date_declaration)

View file

@ -12,4 +12,4 @@
- if individual.birthdate.present?
%tr
%th.libelle Date de naissance :
%td= individual.birthdate&.strftime("%d/%m/%Y")
%td= try_format_date(individual.birthdate)

View file

@ -23,11 +23,11 @@
= link_to pj.content_url, { target: :blank, rel: :noopener } do
%span.filename= display_pj_filename(pj)
%span
ajoutée le #{pj.created_at.strftime('%d/%m/%Y à %H:%M')}
ajoutée le #{try_format_datetime(pj.created_at)}
- else
%td Pièce non fournie
%td.updated-at
- if pj
%span{ class: highlight_if_unseen_class(demande_seen_at, pj.updated_at) }
modifié le
= pj.updated_at.strftime("%d/%m/%Y à %H:%M")
= try_format_datetime(pj.updated_at)

View file

@ -3,6 +3,6 @@
.card-title
Le dépôt de dossier est fermé
- if dossier.procedure.archived_at.present?
Il n'est plus possible de déposer de dossier pour cette démarche en ligne depuis le #{dossier.procedure.archived_at.strftime("%d/%m/%Y")}.
Il n'est plus possible de déposer de dossier pour cette démarche en ligne depuis le #{try_format_date(dossier.procedure.archived_at)}.
- else
Il n'est plus possible de déposer de dossier pour cette démarche en ligne.

View file

@ -5,7 +5,7 @@
- if champ.updated_at.present? && seen_at.present?
%span.updated-at{ class: highlight_if_unseen_class(seen_at, champ.updated_at) }
= "modifié le #{champ.updated_at.strftime('%d/%m/%Y à %H:%M')}"
= "modifié le #{try_format_datetime(champ.updated_at)}"
- if champ.description.present?
%span.notice= string_to_html(champ.description)

View file

@ -13,12 +13,12 @@
%li
Date de création :
= etablissement.association_date_creation&.strftime('%d/%m/%Y')
= try_format_date(etablissement.association_date_creation)
%li
Date de déclaration :
= etablissement.association_date_declaration&.strftime('%d/%m/%Y')
= try_format_date(etablissement.association_date_declaration)
%li
Date de publication :
= etablissement.association_date_publication&.strftime('%d/%m/%Y')
= try_format_date(etablissement.association_date_publication)

View file

@ -22,7 +22,7 @@
%li
Date de création :
= etablissement.entreprise.date_creation&.strftime('%d/%m/%Y')
= try_format_date(etablissement.entreprise.date_creation)
%li
Effectif organisation :

View file

@ -47,7 +47,7 @@
= render partial: 'shared/dossiers/status_badge', locals: { dossier: dossier }
%td.updated-at-col
= link_to(url_for_dossier(dossier), class: 'cell-link') do
= dossier.updated_at.strftime("%d/%m/%Y")
= try_format_date(dossier.updated_at)
%td.action-col.action-col
= render partial: 'dossier_actions', locals: { dossier: dossier }
= paginate(@dossiers)

View file

@ -6,7 +6,7 @@ Rails.application.config.content_security_policy do |policy|
policy.report_uri "http://#{ENV['APP_HOST']}/csp/" # ne pas notifier report-uri en dev/test
end
# Whitelist image
policy.img_src :self, "*.openstreetmap.org", "static.demarches-simplifiees.fr", "*.cloud.ovh.net", "stats.data.gouv.fr"
policy.img_src :self, "*.openstreetmap.org", "static.demarches-simplifiees.fr", "*.cloud.ovh.net", "stats.data.gouv.fr", "*"
# Whitelist JS: nous, sendinblue et matomo
# miniprofiler et nous avons quelques boutons inline :(
policy.script_src :self, "*.sibautomation.com", "sibautomation.com", "stats.data.gouv.fr", "*.sendinblue.com", :unsafe_eval, :unsafe_inline
@ -16,5 +16,5 @@ Rails.application.config.content_security_policy do |policy|
policy.style_src :self, :unsafe_inline
# Pour tout le reste, par défaut on accepte uniquement ce qui vient de chez nous
# et dans la notification on inclue la source de l'erreur
policy.default_src :self, :data, :report_sample, "fonts.gstatic.com", "in-automate.sendinblue.com", "player.vimeo.com", "app.franceconnect.gouv.fr", "sentry.io", "static.demarches-simplifiees.fr"
policy.default_src :self, :data, :report_sample, "fonts.gstatic.com", "in-automate.sendinblue.com", "player.vimeo.com", "app.franceconnect.gouv.fr", "sentry.io", "static.demarches-simplifiees.fr", "*.crisp.chat", "crisp.chat"
end

View file

@ -249,7 +249,7 @@ fr:
- vendredi
- samedi
formats:
default: "%d/%m/%Y"
default: "%d %B %Y"
short: "%e %b"
long: "%e %B %Y"
datetime:
@ -290,6 +290,9 @@ fr:
x_seconds:
one: 1 seconde
other: "%{count} secondes"
time:
formats:
default: "%d %B %Y %R"
pluralize:
case:
zero: dossier

View file

@ -0,0 +1,8 @@
class EnsureFollowDatesNotNull < ActiveRecord::Migration[5.2]
def change
change_column_null :follows, :demande_seen_at, false
change_column_null :follows, :annotations_privees_seen_at, false
change_column_null :follows, :avis_seen_at, false
change_column_null :follows, :messagerie_seen_at, false
end
end

View file

@ -0,0 +1,17 @@
class AddFollowUnfollowedAt < ActiveRecord::Migration[5.2]
# We need up/down migrations because `remove_index` doesnt allow `unique: true` and cant be properly rolled back.
def up
add_column :follows, :unfollowed_at, :datetime
remove_index :follows, [:gestionnaire_id, :dossier_id]
add_index :follows, [:gestionnaire_id, :dossier_id, :unfollowed_at], unique: true,
name: :uniqueness_index # We need a custom name because the autogenerated name would be too long
end
def down
remove_column :follows, :unfollowed_at
# We dont need to remove the index: dropping the column automatically deletes it.
add_index :follows, [:gestionnaire_id, :dossier_id], unique: true
end
end

View file

@ -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_102360) do
ActiveRecord::Schema.define(version: 2019_06_07_124156) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -327,14 +327,15 @@ ActiveRecord::Schema.define(version: 2019_03_27_102360) do
create_table "follows", id: :serial, force: :cascade do |t|
t.integer "gestionnaire_id", null: false
t.integer "dossier_id", null: false
t.datetime "demande_seen_at"
t.datetime "annotations_privees_seen_at"
t.datetime "avis_seen_at"
t.datetime "messagerie_seen_at"
t.datetime "demande_seen_at", null: false
t.datetime "annotations_privees_seen_at", null: false
t.datetime "avis_seen_at", null: false
t.datetime "messagerie_seen_at", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.datetime "unfollowed_at"
t.index ["dossier_id"], name: "index_follows_on_dossier_id"
t.index ["gestionnaire_id", "dossier_id"], name: "index_follows_on_gestionnaire_id_and_dossier_id", unique: true
t.index ["gestionnaire_id", "dossier_id", "unfollowed_at"], name: "uniqueness_index", unique: true
t.index ["gestionnaire_id"], name: "index_follows_on_gestionnaire_id"
end

View file

@ -0,0 +1,6 @@
{
"errors": [
"Le SIREN ou SIRET est non diffusable"
],
"gateway_error": true
}

View file

@ -17,4 +17,42 @@ describe ApplicationHelper do
it { is_expected.to be_nil }
end
end
describe "#try_format_date" do
subject { try_format_date(date) }
describe 'try formatting a date' do
let(:date) { Date.new(2019, 01, 24) }
it { is_expected.to eq("24 janvier 2019") }
end
describe 'try formatting a blank string' do
let(:date) { "" }
it { is_expected.to eq("") }
end
describe 'try formatting a nil string' do
let(:date) { nil }
it { is_expected.to eq("") }
end
end
describe "#try_format_datetime" do
subject { try_format_datetime(datetime) }
describe 'try formatting 31/01/2019 11:25' do
let(:datetime) { Time.zone.local(2019, 01, 31, 11, 25, 00) }
it { is_expected.to eq("31 janvier 2019 11:25") }
end
describe 'try formatting a blank string' do
let(:datetime) { "" }
it { is_expected.to eq("") }
end
describe 'try formatting a nil string' do
let(:datetime) { nil }
it { is_expected.to eq("") }
end
end
end

View file

@ -31,7 +31,17 @@ describe ApiEntreprise::API do
end
end
context 'when siret exist' do
context 'when siren infos are private' do
let(:siren) { '111111111' }
let(:status) { 403 }
let(:body) { File.read('spec/fixtures/files/api_entreprise/entreprises_private.json') }
it 'raises RestClient::ResourceNotFound' do
expect { subject }.to raise_error(RestClient::ResourceNotFound)
end
end
context 'when siren exist' do
let(:siren) { '418166096' }
let(:status) { 200 }
let(:body) { File.read('spec/fixtures/files/api_entreprise/entreprises.json') }

View file

@ -238,7 +238,7 @@ describe TagsSubstitutionConcern, type: :model do
.update(value: '2017-09-13 09:00')
end
it { is_expected.to eq('15/04/2017 2017-09-13 09:00') }
it { is_expected.to eq('15 avril 2017 13 septembre 2017 09:00') }
end
end
end

View file

@ -67,6 +67,7 @@ describe Gestionnaire, type: :model do
end
it { expect(gestionnaire.follow?(already_followed_dossier)).to be false }
it { expect(gestionnaire.previously_followed_dossiers).to include(already_followed_dossier) }
end
end

View file

@ -26,6 +26,18 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
end
end
def timestamps(dossier)
# Reload dossier because the resolution of in-database timestamps is
# different from the resolution of in-memory timestamps, causing the
# tests to fail on fractional time differences.
dossier.reload
{
created_at: dossier.created_at,
updated_at: dossier.updated_at
}
end
def expect_storage_service_to_convert_object
expect(storage_service).to receive(:make_blob)
expect(storage_service).to receive(:copy_from_carrierwave_to_active_storage!)
@ -64,6 +76,8 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
end
context 'no notifications are sent to instructeurs' do
let!(:initial_dossier_timestamps) { timestamps(dossier) }
context 'when there is a PJ' do
let(:pjs) { make_pjs }
@ -80,24 +94,18 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
dossier.reload
end
it 'the champ has the same created_at as the PJ' do
it 'the champ has the same timestamps as the PJ' do
expect(dossier.champs.last.created_at).to eq(pjs.last.created_at)
expect(dossier.champs.last.updated_at).to eq(pjs.last.updated_at)
end
it 'the champ has the same updated_at as the PJ' do
expect(dossier.champs.last.updated_at).to eq(pjs.last.updated_at)
it 'does not change the dossier timestamps' do
expect(dossier.created_at).to eq(initial_dossier_timestamps[:created_at])
expect(dossier.updated_at).to eq(initial_dossier_timestamps[:updated_at])
end
end
context 'when there is no PJ' do
let!(:expected_updated_at) do
# Reload dossier because the resolution of in-database timestamps is
# different from the resolution of in-memory timestamps, causing the
# tests to fail on fractional time differences.
dossier.reload
dossier.updated_at
end
before do
Timecop.travel(1.hour) { service.convert_procedure_pjs_to_champ_pjs(procedure) }
@ -105,12 +113,14 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
dossier.reload
end
it 'the champ has the same created_at as the dossier' do
expect(dossier.champs.last.created_at).to eq(dossier.created_at)
it 'the champ has the same timestamps as the dossier' do
expect(dossier.champs.last.created_at).to eq(initial_dossier_timestamps[:created_at])
expect(dossier.champs.last.updated_at).to eq(initial_dossier_timestamps[:updated_at])
end
it 'the champ has the same updated_at as the dossier' do
expect(dossier.champs.last.updated_at).to eq(expected_updated_at)
it 'does not change the dossier timestamps' do
expect(dossier.created_at).to eq(initial_dossier_timestamps[:created_at])
expect(dossier.updated_at).to eq(initial_dossier_timestamps[:updated_at])
end
end
end
@ -192,6 +202,9 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
)
end
let!(:initial_dossier_timestamps) { timestamps(dossier) }
let!(:initial_failing_dossier_timestamps) { timestamps(failing_dossier) }
before do
allow(storage_service).to receive(:checksum).and_return('cafe')
allow(storage_service).to receive(:fix_content_type)
@ -204,8 +217,10 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
end
def try_convert(procedure)
service.convert_procedure_pjs_to_champ_pjs(procedure)
Timecop.travel(1.hour) { service.convert_procedure_pjs_to_champ_pjs(procedure) }
rescue StandardError, SignalException => e
dossier.reload
failing_dossier.reload
e
end
@ -234,6 +249,12 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
.not_to change { procedure.types_de_piece_justificative.count }
end
it 'does not change the dossiers timestamps' do
try_convert(procedure)
expect(dossier.updated_at).to eq(initial_dossier_timestamps[:updated_at])
expect(failing_dossier.updated_at).to eq(initial_failing_dossier_timestamps[:updated_at])
end
it 'does not leave stale blobs behind' do
expect { try_convert(procedure) }
.not_to change { ActiveStorage::Blob.count }

View file

@ -38,7 +38,7 @@ describe 'shared/dossiers/demande.html.haml', type: :view do
expect(rendered).to include(individual.gender)
expect(rendered).to include(individual.nom)
expect(rendered).to include(individual.prenom)
expect(rendered).to include(individual.birthdate.strftime("%d/%m/%Y"))
expect(rendered).to include(I18n.l(individual.birthdate))
end
end