Merge pull request #6612 from betagouv/main

2021-11-04-01
This commit is contained in:
LeSim 2021-11-04 10:45:57 +01:00 committed by GitHub
commit 2e421429e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 397 additions and 257 deletions

View file

@ -18,7 +18,7 @@ GIT
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
aasm (5.1.1) aasm (5.2.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
acsv (0.0.1) acsv (0.0.1)
actioncable (6.1.4.1) actioncable (6.1.4.1)

View file

@ -18,6 +18,47 @@ class API::V2::GraphqlController < API::V2::BaseController
private private
def append_info_to_payload(payload)
super
payload.merge!({
graphql_operation: operation_log(params[:query], params[:operationName], params[:variables])
})
end
def operation_log(query, operation_name, variables)
return "NoQuery" if query.nil?
operation = GraphQL.parse(query).children.find do |node|
if node.is_a?(GraphQL::Language::Nodes::OperationDefinition)
node.name == operation_name
end
end
return "InvalidQuery" if operation.nil?
return "IntrospectionQuery" if operation.name == "IntrospectionQuery"
message = operation.operation_type
if operation.name
message += ": #{operation.name} { "
end
message += operation.selections.map(&:name).join(', ')
message += " }"
if variables.present?
message += " "
message += variables.to_unsafe_h.flat_map do |(name, value)|
if name == "input"
value.map do |(name, value)|
"#{name}: \"#{value.to_s.truncate(10)}\""
end
else
"#{name}: \"#{value.to_s.truncate(10)}\""
end
end.join(', ')
end
message
end
def process_action(*args) def process_action(*args)
super super
rescue ActionDispatch::Http::Parameters::ParseError => exception rescue ActionDispatch::Http::Parameters::ParseError => exception

View file

@ -63,7 +63,7 @@ module NewAdministrateur
end end
def destroy def destroy
if !groupe_instructeur.dossiers.empty? if !groupe_instructeur.dossiers.with_discarded.empty?
flash[:alert] = "Impossible de supprimer un groupe avec des dossiers. Il faut le réaffecter avant" flash[:alert] = "Impossible de supprimer un groupe avec des dossiers. Il faut le réaffecter avant"
elsif procedure.groupe_instructeurs.one? elsif procedure.groupe_instructeurs.one?
flash[:alert] = "Suppression impossible : il doit y avoir au moins un groupe instructeur sur chaque procédure" flash[:alert] = "Suppression impossible : il doit y avoir au moins un groupe instructeur sur chaque procédure"

View file

@ -50,6 +50,7 @@ class Avis < ApplicationRecord
scope :by_latest, -> { order(updated_at: :desc) } scope :by_latest, -> { order(updated_at: :desc) }
scope :updated_since?, -> (date) { where('avis.updated_at > ?', date) } scope :updated_since?, -> (date) { where('avis.updated_at > ?', date) }
scope :discarded_termine_expired, -> { unscope(:joins).where(dossier: Dossier.discarded_termine_expired) } scope :discarded_termine_expired, -> { unscope(:joins).where(dossier: Dossier.discarded_termine_expired) }
scope :discarded_en_construction_expired, -> { unscope(:joins).where(dossier: Dossier.discarded_en_construction_expired) }
# The form allows subtmitting avis requests to several emails at once, # The form allows subtmitting avis requests to several emails at once,
# hence this virtual attribute. # hence this virtual attribute.

View file

@ -21,6 +21,7 @@
# #
class Champs::RepetitionChamp < Champ class Champs::RepetitionChamp < Champ
accepts_nested_attributes_for :champs, allow_destroy: true accepts_nested_attributes_for :champs, allow_destroy: true
delegate :libelle_for_export, to: :type_de_champ
def rows def rows
champs.group_by(&:row).values champs.group_by(&:row).values
@ -57,13 +58,6 @@ class Champs::RepetitionChamp < Champ
end end
end end
# We have to truncate the label here as spreadsheets have a (30 char) limit on length.
def libelle_for_export
str = "(#{stable_id}) #{libelle}"
# /\*?[] are invalid Excel worksheet characters
ActiveStorage::Filename.new(str.delete('[]*?')).sanitized
end
class Row < Hashie::Dash class Row < Hashie::Dash
property :index property :index
property :dossier_id property :dossier_id
@ -73,19 +67,11 @@ class Champs::RepetitionChamp < Champ
self[attribute] self[attribute]
end end
def spreadsheet_columns def spreadsheet_columns(types_de_champ)
[ [
['Dossier ID', :dossier_id], ['Dossier ID', :dossier_id],
['Ligne', :index] ['Ligne', :index]
] + exported_champs ] + Dossier.champs_for_export(champs, types_de_champ)
end
private
def exported_champs
champs.reject(&:exclude_from_export?).map do |champ|
[champ.libelle, champ.for_export]
end
end end
end end
end end

View file

@ -17,8 +17,6 @@
class DeletedDossier < ApplicationRecord class DeletedDossier < ApplicationRecord
belongs_to :procedure, -> { with_discarded }, inverse_of: :deleted_dossiers, optional: false belongs_to :procedure, -> { with_discarded }, inverse_of: :deleted_dossiers, optional: false
validates :dossier_id, uniqueness: true
scope :order_by_updated_at, -> (order = :desc) { order(created_at: order) } scope :order_by_updated_at, -> (order = :desc) { order(created_at: order) }
scope :deleted_since, -> (since) { where('deleted_dossiers.deleted_at >= ?', since) } scope :deleted_since, -> (since) { where('deleted_dossiers.deleted_at >= ?', since) }
@ -32,16 +30,17 @@ class DeletedDossier < ApplicationRecord
} }
def self.create_from_dossier(dossier, reason) def self.create_from_dossier(dossier, reason)
create!( # We have some bad data because of partially deleted dossiers in the past.
# For now use find_or_create_by! to avoid errors.
create_with(
reason: reasons.fetch(reason), reason: reasons.fetch(reason),
dossier_id: dossier.id,
groupe_instructeur_id: dossier.groupe_instructeur_id, groupe_instructeur_id: dossier.groupe_instructeur_id,
revision_id: dossier.revision_id, revision_id: dossier.revision_id,
user_id: dossier.user_id, user_id: dossier.user_id,
procedure: dossier.procedure, procedure: dossier.procedure,
state: dossier.state, state: dossier.state,
deleted_at: Time.zone.now deleted_at: Time.zone.now
) ).create_or_find_by!(dossier_id: dossier.id)
end end
def procedure_removed? def procedure_removed?

View file

@ -606,7 +606,7 @@ class Dossier < ApplicationRecord
end end
def keep_track_on_deletion? def keep_track_on_deletion?
!procedure.brouillon? !procedure.brouillon? && !brouillon?
end end
def expose_legacy_carto_api? def expose_legacy_carto_api?
@ -645,56 +645,68 @@ class Dossier < ApplicationRecord
end end
end end
def expired_keep_track! def expired_keep_track_and_destroy!
if keep_track_on_deletion? transaction do
DeletedDossier.create_from_dossier(self, :expired) if keep_track_on_deletion?
log_automatic_dossier_operation(:supprimer, self) DeletedDossier.create_from_dossier(self, :expired)
log_automatic_dossier_operation(:supprimer, self)
end
destroy!
end end
true
rescue
false
end end
def discard_and_keep_track!(author, reason) def discard_and_keep_track!(author, reason)
if keep_track_on_deletion? user_email = user_deleted? ? nil : user_email_for(:notification)
if en_construction? deleted_dossier = nil
deleted_dossier = DeletedDossier.create_from_dossier(self, reason)
transaction do
if keep_track_on_deletion?
log_dossier_operation(author, :supprimer, self)
deleted_dossier = DeletedDossier.create_from_dossier(self, reason)
end
update!(dossier_transfer_id: nil)
discard!
end
if deleted_dossier.present?
if en_construction?
administration_emails = followers_instructeurs.present? ? followers_instructeurs.map(&:email) : procedure.administrateurs.map(&:email) administration_emails = followers_instructeurs.present? ? followers_instructeurs.map(&:email) : procedure.administrateurs.map(&:email)
administration_emails.each do |email| administration_emails.each do |email|
DossierMailer.notify_deletion_to_administration(deleted_dossier, email).deliver_later DossierMailer.notify_deletion_to_administration(deleted_dossier, email).deliver_later
end end
end
if !user_deleted? if user_email.present?
DossierMailer.notify_deletion_to_user(deleted_dossier, user_email_for(:notification)).deliver_later if reason == :user_request
DossierMailer.notify_deletion_to_user(deleted_dossier, user_email).deliver_later
else
DossierMailer.notify_instructeur_deletion_to_user(deleted_dossier, user_email).deliver_later
end end
log_dossier_operation(author, :supprimer, self)
elsif termine?
deleted_dossier = DeletedDossier.create_from_dossier(self, reason)
if !user_deleted?
DossierMailer.notify_instructeur_deletion_to_user(deleted_dossier, user_email_for(:notification)).deliver_later
end
log_dossier_operation(author, :supprimer, self)
end end
end end
update!(dossier_transfer_id: nil)
discard!
end end
def restore(author, only_discarded_with_procedure = false) def restore(author)
if discarded? if discarded?
deleted_dossier = DeletedDossier.find_by(dossier_id: id) transaction do
if undiscard && keep_track_on_deletion?
if !only_discarded_with_procedure || deleted_dossier&.procedure_removed? deleted_dossier&.destroy!
if undiscard && keep_track_on_deletion? && en_construction?
deleted_dossier&.destroy
log_dossier_operation(author, :restaurer, self) log_dossier_operation(author, :restaurer, self)
end end
end end
end end
end end
def restore_if_discarded_with_procedure(author)
if deleted_dossier&.procedure_removed?
restore(author)
end
end
def after_passer_en_construction def after_passer_en_construction
update!(conservation_extension: 0.days) update!(conservation_extension: 0.days)
update!(en_construction_at: Time.zone.now) if self.en_construction_at.nil? update!(en_construction_at: Time.zone.now) if self.en_construction_at.nil?
@ -906,12 +918,12 @@ class Dossier < ApplicationRecord
columns << ['Groupe instructeur', groupe_instructeur.label] columns << ['Groupe instructeur', groupe_instructeur.label]
end end
columns + champs_for_export(types_de_champ) columns + self.class.champs_for_export(champs + champs_private, types_de_champ)
end end
def champs_for_export(types_de_champ) def self.champs_for_export(champs, types_de_champ)
# Index values by stable_id # Index values by stable_id
values = (champs + champs_private).reject(&:exclude_from_export?) values = champs.reject(&:exclude_from_export?)
.index_by(&:stable_id) .index_by(&:stable_id)
.transform_values(&:for_export) .transform_values(&:for_export)
@ -965,6 +977,7 @@ class Dossier < ApplicationRecord
transaction do transaction do
DossierOperationLog.discarded_en_construction_expired.destroy_all DossierOperationLog.discarded_en_construction_expired.destroy_all
Avis.discarded_en_construction_expired.destroy_all
discarded_en_construction_expired.destroy_all discarded_en_construction_expired.destroy_all
end end
@ -977,6 +990,10 @@ class Dossier < ApplicationRecord
private private
def deleted_dossier
@deleted_dossier ||= DeletedDossier.find_by(dossier_id: id)
end
def defaut_groupe_instructeur? def defaut_groupe_instructeur?
groupe_instructeur == procedure.defaut_groupe_instructeur groupe_instructeur == procedure.defaut_groupe_instructeur
end end

View file

@ -99,20 +99,16 @@ class Procedure < ApplicationRecord
end end
def types_de_champ_for_procedure_presentation def types_de_champ_for_procedure_presentation
explanatory_types_de_champ = [:header_section, :explication, :repetition].map { |k| TypeDeChamp.type_champs.fetch(k) }
if brouillon? if brouillon?
TypeDeChamp TypeDeChamp.fillable
.joins(:revisions) .joins(:revision_types_de_champ)
.where.not(type_champ: explanatory_types_de_champ) .where(revision_types_de_champ: { revision: draft_revision })
.where(procedure_revisions: { id: draft_revision_id })
.order(:private, :position) .order(:private, :position)
else else
# fetch all type_de_champ.stable_id for all the revisions expect draft # fetch all type_de_champ.stable_id for all the revisions expect draft
# and for each stable_id take the bigger (more recent) type_de_champ.id # and for each stable_id take the bigger (more recent) type_de_champ.id
recent_ids = TypeDeChamp recent_ids = TypeDeChamp.fillable
.joins(:revisions) .joins(:revisions)
.where.not(type_champ: explanatory_types_de_champ)
.where(procedure_revisions: { procedure_id: id }) .where(procedure_revisions: { procedure_id: id })
.where.not(procedure_revisions: { id: draft_revision_id }) .where.not(procedure_revisions: { id: draft_revision_id })
.group(:stable_id) .group(:stable_id)
@ -139,6 +135,7 @@ class Procedure < ApplicationRecord
else else
TypeDeChamp.root TypeDeChamp.root
.public_only .public_only
.fillable
.where(revision: revisions - [draft_revision]) .where(revision: revisions - [draft_revision])
.order(:created_at) .order(:created_at)
.uniq .uniq
@ -151,6 +148,7 @@ class Procedure < ApplicationRecord
else else
TypeDeChamp.root TypeDeChamp.root
.private_only .private_only
.fillable
.where(revision: revisions - [draft_revision]) .where(revision: revisions - [draft_revision])
.order(:created_at) .order(:created_at)
.uniq .uniq
@ -672,7 +670,7 @@ class Procedure < ApplicationRecord
def restore(author) def restore(author)
if discarded? && undiscard if discarded? && undiscard
dossiers.with_discarded.discarded.find_each do |dossier| dossiers.with_discarded.discarded.find_each do |dossier|
dossier.restore(author, true) dossier.restore_if_discarded_with_procedure(author)
end end
end end
end end

View file

@ -86,6 +86,8 @@ class TypeDeChamp < ApplicationRecord
scope :ordered, -> { order(order_place: :asc) } scope :ordered, -> { order(order_place: :asc) }
scope :root, -> { where(parent_id: nil) } scope :root, -> { where(parent_id: nil) }
scope :repetition, -> { where(type_champ: type_champs.fetch(:repetition)) } scope :repetition, -> { where(type_champ: type_champs.fetch(:repetition)) }
scope :not_repetition, -> { where.not(type_champ: type_champs.fetch(:repetition)) }
scope :fillable, -> { where.not(type_champ: [type_champs.fetch(:header_section), type_champs.fetch(:explication)]) }
has_many :champ, inverse_of: :type_de_champ, dependent: :destroy do has_many :champ, inverse_of: :type_de_champ, dependent: :destroy do
def build(params = {}) def build(params = {})
@ -287,6 +289,24 @@ class TypeDeChamp < ApplicationRecord
options.slice(*TypesDeChamp::CarteTypeDeChamp::LAYERS) options.slice(*TypesDeChamp::CarteTypeDeChamp::LAYERS)
end end
def types_de_champ_for_revision(revision)
if revision.draft?
# if we are asking for children on a draft revision, just use current child types_de_champ
types_de_champ.fillable
else
# otherwise return all types_de_champ in their latest state
types_de_champ = TypeDeChamp
.fillable
.joins(:parent)
.where(parent: { stable_id: stable_id })
TypeDeChamp
.where(id: types_de_champ.group(:stable_id).select('MAX(types_de_champ.id)'))
.joins(parent: :revision_types_de_champ)
.order(:order_place, 'procedure_revision_types_de_champ.revision_id': :desc)
end
end
FEATURE_FLAGS = {} FEATURE_FLAGS = {}
def self.type_de_champ_types_for(procedure, user) def self.type_de_champ_types_for(procedure, user)

View file

@ -4,4 +4,11 @@ class TypesDeChamp::RepetitionTypeDeChamp < TypesDeChamp::TypeDeChampBase
champ.add_row champ.add_row
champ champ
end end
# We have to truncate the label here as spreadsheets have a (30 char) limit on length.
def libelle_for_export(index = 0)
str = "(#{stable_id}) #{libelle}"
# /\*?[] are invalid Excel worksheet characters
ActiveStorage::Filename.new(str.delete('[]*?')).sanitized
end
end end

View file

@ -1,14 +1,14 @@
class TypesDeChamp::TypeDeChampBase class TypesDeChamp::TypeDeChampBase
include ActiveModel::Validations include ActiveModel::Validations
delegate :description, :libelle, to: :@type_de_champ delegate :description, :libelle, :stable_id, to: :@type_de_champ
def initialize(type_de_champ) def initialize(type_de_champ)
@type_de_champ = type_de_champ @type_de_champ = type_de_champ
end end
def tags_for_template def tags_for_template
stable_id = @type_de_champ.stable_id stable_id = self.stable_id
[ [
{ {
libelle: libelle, libelle: libelle,
@ -21,7 +21,7 @@ class TypesDeChamp::TypeDeChampBase
] ]
end end
def libelle_for_export(index) def libelle_for_export(index = 0)
libelle libelle
end end

View file

@ -19,22 +19,16 @@ class ExpiredDossiersDeletionService
.brouillon_close_to_expiration .brouillon_close_to_expiration
.without_brouillon_expiration_notice_sent .without_brouillon_expiration_notice_sent
dossiers_close_to_expiration user_notifications = group_by_user_email(dossiers_close_to_expiration)
.with_notifiable_procedure
.includes(:user, :procedure)
.group_by(&:user)
.each do |(user, dossiers)|
DossierMailer.notify_brouillon_near_deletion(
dossiers,
user.email
).deliver_later
# mark as sent dossiers from current notification
Dossier.where(id: dossiers).update_all(brouillon_close_to_expiration_notice_sent_at: Time.zone.now)
end
# mark as sent dossiers without notification
dossiers_close_to_expiration.update_all(brouillon_close_to_expiration_notice_sent_at: Time.zone.now) dossiers_close_to_expiration.update_all(brouillon_close_to_expiration_notice_sent_at: Time.zone.now)
user_notifications.each do |(email, dossiers)|
DossierMailer.notify_brouillon_near_deletion(
dossiers,
email
).deliver_later
end
end end
def self.send_en_construction_expiration_notices def self.send_en_construction_expiration_notices
@ -52,24 +46,17 @@ class ExpiredDossiersDeletionService
end end
def self.delete_expired_brouillons_and_notify def self.delete_expired_brouillons_and_notify
dossiers_to_remove = Dossier.brouillon_expired user_notifications = group_by_user_email(Dossier.brouillon_expired)
.map { |(email, dossiers)| [email, dossiers.map(&:hash_for_deletion_mail)] }
dossiers_to_remove Dossier.brouillon_expired.destroy_all
.with_notifiable_procedure
.includes(:user, :procedure)
.group_by(&:user)
.each do |(user, dossiers)|
DossierMailer.notify_brouillon_deletion(
dossiers.map(&:hash_for_deletion_mail),
user.email
).deliver_later
# destroy dossiers from current notification user_notifications.each do |(email, dossiers_hash)|
Dossier.where(id: dossiers).destroy_all DossierMailer.notify_brouillon_deletion(
end dossiers_hash,
email
# destroy dossiers without notification ).deliver_later
dossiers_to_remove.destroy_all end
end end
def self.delete_expired_en_construction_and_notify def self.delete_expired_en_construction_and_notify
@ -83,57 +70,58 @@ class ExpiredDossiersDeletionService
private private
def self.send_expiration_notices(dossiers_close_to_expiration, close_to_expiration_flag) def self.send_expiration_notices(dossiers_close_to_expiration, close_to_expiration_flag)
dossiers_close_to_expiration user_notifications = group_by_user_email(dossiers_close_to_expiration)
.with_notifiable_procedure administration_notifications = group_by_fonctionnaire_email(dossiers_close_to_expiration)
.includes(:user)
.group_by(&:user)
.each do |(user, dossiers)|
DossierMailer.notify_near_deletion_to_user(
dossiers,
user.email
).deliver_later
end
group_by_fonctionnaire_email(dossiers_close_to_expiration).each do |(email, dossiers)|
DossierMailer.notify_near_deletion_to_administration(
dossiers.to_a,
email
).deliver_later
# mark as sent dossiers from current notification
Dossier.where(id: dossiers.to_a).update_all(close_to_expiration_flag => Time.zone.now)
end
# mark as sent dossiers without notification
dossiers_close_to_expiration.update_all(close_to_expiration_flag => Time.zone.now) dossiers_close_to_expiration.update_all(close_to_expiration_flag => Time.zone.now)
user_notifications.each do |(email, dossiers)|
DossierMailer.notify_near_deletion_to_user(dossiers, email).deliver_later
end
administration_notifications.each do |(email, dossiers)|
DossierMailer.notify_near_deletion_to_administration(dossiers, email).deliver_later
end
end end
def self.delete_expired_and_notify(dossiers_to_remove, notify_on_closed_procedures_to_user: false) def self.delete_expired_and_notify(dossiers_to_remove, notify_on_closed_procedures_to_user: false)
dossiers_to_remove.each(&:expired_keep_track!) user_notifications = group_by_user_email(dossiers_to_remove, notify_on_closed_procedures_to_user: notify_on_closed_procedures_to_user)
.map { |(email, dossiers)| [email, dossiers.map(&:id)] }
administration_notifications = group_by_fonctionnaire_email(dossiers_to_remove)
.map { |(email, dossiers)| [email, dossiers.map(&:id)] }
dossiers_to_remove deleted_dossier_ids = []
.with_notifiable_procedure(notify_on_closed: notify_on_closed_procedures_to_user) dossiers_to_remove.find_each do |dossier|
.includes(:user) if dossier.expired_keep_track_and_destroy!
.group_by(&:user) deleted_dossier_ids << dossier.id
.each do |(user, dossiers)|
DossierMailer.notify_automatic_deletion_to_user(
DeletedDossier.where(dossier_id: dossiers.map(&:id)).to_a,
user.email
).deliver_later
end end
self.group_by_fonctionnaire_email(dossiers_to_remove).each do |(email, dossiers)|
DossierMailer.notify_automatic_deletion_to_administration(
DeletedDossier.where(dossier_id: dossiers.map(&:id)).to_a,
email
).deliver_later
# destroy dossiers from current notification
Dossier.where(id: dossiers.to_a).destroy_all
end end
# destroy dossiers without notification user_notifications.each do |(email, dossier_ids)|
dossiers_to_remove.destroy_all dossier_ids = dossier_ids.intersection(deleted_dossier_ids)
if dossier_ids.present?
DossierMailer.notify_automatic_deletion_to_user(
DeletedDossier.where(dossier_id: dossier_ids).to_a,
email
).deliver_later
end
end
administration_notifications.each do |(email, dossier_ids)|
dossier_ids = dossier_ids.intersection(deleted_dossier_ids)
if dossier_ids.present?
DossierMailer.notify_automatic_deletion_to_administration(
DeletedDossier.where(dossier_id: dossier_ids).to_a,
email
).deliver_later
end
end
end
def self.group_by_user_email(dossiers, notify_on_closed_procedures_to_user: false)
dossiers
.with_notifiable_procedure(notify_on_closed: notify_on_closed_procedures_to_user)
.includes(:user, :procedure)
.group_by(&:user)
.map { |(user, dossiers)| [user.email, dossiers] }
end end
def self.group_by_fonctionnaire_email(dossiers) def self.group_by_fonctionnaire_email(dossiers)
@ -143,5 +131,6 @@ class ExpiredDossiersDeletionService
.each_with_object(Hash.new { |h, k| h[k] = Set.new }) do |dossier, h| .each_with_object(Hash.new { |h, k| h[k] = Set.new }) do |dossier, h|
(dossier.followers_instructeurs + dossier.procedure.administrateurs).each { |destinataire| h[destinataire.email] << dossier } (dossier.followers_instructeurs + dossier.procedure.administrateurs).each { |destinataire| h[destinataire.email] << dossier }
end end
.map { |(email, dossiers)| [email, dossiers.to_a] }
end end
end end

View file

@ -39,21 +39,22 @@ class ProcedureExportService
@avis ||= dossiers.flat_map(&:avis) @avis ||= dossiers.flat_map(&:avis)
end end
def champs_repetables
@champs_repetables ||= dossiers.flat_map do |dossier|
[dossier.champs, dossier.champs_private]
.flatten
.filter { |champ| champ.is_a?(Champs::RepetitionChamp) }
end.group_by(&:libelle_for_export)
end
def champs_repetables_options def champs_repetables_options
champs_repetables.map do |libelle, champs| revision = @procedure.active_revision
[ champs_by_stable_id = dossiers
libelle, .flat_map { |dossier| (dossier.champs + dossier.champs_private).filter(&:repetition?) }
champs.flat_map(&:rows_for_export) .group_by(&:stable_id)
]
end @procedure.types_de_champ_for_procedure_presentation.repetition
.map { |type_de_champ_repetition| [type_de_champ_repetition, type_de_champ_repetition.types_de_champ_for_revision(revision).to_a] }
.filter { |(_, types_de_champ)| types_de_champ.present? }
.map do |(type_de_champ_repetition, types_de_champ)|
{
sheet_name: type_de_champ_repetition.libelle_for_export,
instances: champs_by_stable_id.fetch(type_de_champ_repetition.stable_id, []).flat_map(&:rows_for_export),
spreadsheet_columns: Proc.new { |instance| instance.spreadsheet_columns(types_de_champ) }
}
end
end end
DEFAULT_STYLES = { DEFAULT_STYLES = {
@ -69,8 +70,8 @@ class ProcedureExportService
{ instances: etablissements.to_a, sheet_name: 'Etablissements' } { instances: etablissements.to_a, sheet_name: 'Etablissements' }
when :avis when :avis
{ instances: avis.to_a, sheet_name: 'Avis' } { instances: avis.to_a, sheet_name: 'Avis' }
when Array when Hash
{ instances: table.last, sheet_name: table.first } table
end.merge(DEFAULT_STYLES) end.merge(DEFAULT_STYLES)
# transliterate: convert to ASCII characters # transliterate: convert to ASCII characters
@ -84,7 +85,7 @@ class ProcedureExportService
end end
def spreadsheet_columns(format) def spreadsheet_columns(format)
types_de_champ = @procedure.types_de_champ_for_procedure_presentation.to_a types_de_champ = @procedure.types_de_champ_for_procedure_presentation.not_repetition.to_a
Proc.new do |instance| Proc.new do |instance|
instance.send(:"spreadsheet_columns_#{format}", types_de_champ: types_de_champ) instance.send(:"spreadsheet_columns_#{format}", types_de_champ: types_de_champ)

View file

@ -76,4 +76,10 @@ Rails.application.configure do
debounce_delay: 500, debounce_delay: 500,
status_visible_duration: 500 status_visible_duration: 500
} }
# BCrypt is slow by design - but during tests we want to make it faster
# to compute hashes of passwords.
silence_warnings do
BCrypt::Engine::DEFAULT_COST = BCrypt::Engine::MIN_COST
end
end end

View file

@ -0,0 +1,16 @@
namespace :ds do
desc 'DS task: destroy_expired_dossiers_mae'
task destroy_expired_dossiers_mae: :environment do
dossiers = Dossier.state_termine
.where("termine_close_to_expiration_notice_sent_at + INTERVAL :expires_in < :now", { now: Time.zone.now, expires_in: '30 days' })
.joins(:groupe_instructeur)
.where(groupe_instructeur: { procedure_id: [47787, 47844, 47478, 47865] })
progress = ProgressReport.new(dossiers.count)
dossiers.find_each do |dossier|
dossier.expired_keep_track_and_destroy!
progress.inc
end
progress.finish
end
end

View file

@ -256,7 +256,7 @@ describe API::V1::DossiersController do
describe 'repetition' do describe 'repetition' do
let(:procedure) { create(:procedure, :with_repetition, administrateur: admin) } let(:procedure) { create(:procedure, :with_repetition, administrateur: admin) }
let(:dossier) { create(:dossier, :en_construction, :with_all_champs, procedure: procedure) } let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure: procedure) }
subject { super().first[:rows] } subject { super().first[:rows] }

View file

@ -1,21 +1,11 @@
describe API::V2::GraphqlController do describe API::V2::GraphqlController do
let(:admin) { create(:administrateur) } let(:admin) { create(:administrateur) }
let(:token) { admin.renew_api_token } let(:token) { admin.renew_api_token }
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, :with_all_champs, :with_all_annotations, administrateurs: [admin]) } let(:procedure) { create(:procedure, :published, :for_individual, :with_service, administrateurs: [admin]) }
let(:dossier) do let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) }
dossier = create(:dossier,
:en_construction,
:with_all_champs,
:with_all_annotations,
:with_individual,
procedure: procedure)
create(:commentaire, :with_file, dossier: dossier, email: 'test@test.com')
dossier
end
let(:dossier1) { create(:dossier, :en_construction, :with_individual, procedure: procedure, en_construction_at: 1.day.ago) } let(:dossier1) { create(:dossier, :en_construction, :with_individual, procedure: procedure, en_construction_at: 1.day.ago) }
let(:dossier2) { create(:dossier, :en_construction, :with_individual, :archived, procedure: procedure, en_construction_at: 3.days.ago) } let(:dossier2) { create(:dossier, :en_construction, :with_individual, :archived, procedure: procedure, en_construction_at: 3.days.ago) }
let(:dossier_brouillon) { create(:dossier, :with_individual, procedure: procedure) } let(:dossiers) { [dossier] }
let(:dossiers) { [dossier2, dossier1, dossier] }
let(:instructeur) { create(:instructeur, followed_dossiers: dossiers) } let(:instructeur) { create(:instructeur, followed_dossiers: dossiers) }
def compute_checksum_in_chunks(io) def compute_checksum_in_chunks(io)
@ -116,57 +106,63 @@ describe API::V2::GraphqlController do
request.env['HTTP_AUTHORIZATION'] = authorization_header request.env['HTTP_AUTHORIZATION'] = authorization_header
end end
context "demarche" do describe "demarche" do
it "should be returned" do describe "query a demarche" do
expect(gql_errors).to eq(nil) let(:procedure) { create(:procedure, :published, :for_individual, :with_service, :with_all_champs, :with_all_annotations, administrateurs: [admin]) }
expect(gql_data).to eq(demarche: {
id: procedure.to_typed_id, it "returns the demarche" do
number: procedure.id, expect(gql_errors).to eq(nil)
title: procedure.libelle, expect(gql_data).to eq(demarche: {
description: procedure.description, id: procedure.to_typed_id,
state: 'publiee', number: procedure.id,
dateFermeture: nil, title: procedure.libelle,
dateCreation: procedure.created_at.iso8601, description: procedure.description,
dateDerniereModification: procedure.updated_at.iso8601, state: 'publiee',
groupeInstructeurs: [ dateFermeture: nil,
{ dateCreation: procedure.created_at.iso8601,
instructeurs: [{ email: instructeur.email }], dateDerniereModification: procedure.updated_at.iso8601,
label: "défaut" groupeInstructeurs: [
}
],
revisions: procedure.revisions.map { |revision| { id: revision.to_typed_id } },
draftRevision: { id: procedure.draft_revision.to_typed_id },
publishedRevision: {
id: procedure.published_revision.to_typed_id,
champDescriptors: procedure.published_types_de_champ.map do |tdc|
{ {
type: tdc.type_champ instructeurs: [{ email: instructeur.email }],
label: "défaut"
} }
end ],
}, revisions: procedure.revisions.map { |revision| { id: revision.to_typed_id } },
service: { draftRevision: { id: procedure.draft_revision.to_typed_id },
nom: procedure.service.nom, publishedRevision: {
typeOrganisme: procedure.service.type_organisme, id: procedure.published_revision.to_typed_id,
organisme: procedure.service.organisme champDescriptors: procedure.published_types_de_champ.map do |tdc|
}, {
champDescriptors: procedure.types_de_champ.map do |tdc| type: tdc.type_champ
{ }
id: tdc.to_typed_id, end
label: tdc.libelle, },
type: tdc.type_champ, service: {
description: tdc.description, nom: procedure.service.nom,
required: tdc.mandatory?, typeOrganisme: procedure.service.type_organisme,
champDescriptors: tdc.repetition? ? tdc.reload.types_de_champ.map { |tdc| { id: tdc.to_typed_id, type: tdc.type_champ } } : nil, organisme: procedure.service.organisme
options: tdc.drop_down_list? ? tdc.drop_down_list_options.reject(&:empty?) : nil },
champDescriptors: procedure.types_de_champ.map do |tdc|
{
id: tdc.to_typed_id,
label: tdc.libelle,
type: tdc.type_champ,
description: tdc.description,
required: tdc.mandatory?,
champDescriptors: tdc.repetition? ? tdc.reload.types_de_champ.map { |tdc| { id: tdc.to_typed_id, type: tdc.type_champ } } : nil,
options: tdc.drop_down_list? ? tdc.drop_down_list_options.reject(&:empty?) : nil
}
end,
dossiers: {
nodes: dossiers.map { |dossier| { id: dossier.to_typed_id } }
} }
end, })
dossiers: { end
nodes: dossiers.map { |dossier| { id: dossier.to_typed_id } }
}
})
end end
context "filter dossiers" do describe "filter dossiers" do
let(:dossiers) { [dossier, dossier1, dossier2] }
let(:query) do let(:query) do
"{ "{
demarche(number: #{procedure.id}) { demarche(number: #{procedure.id}) {
@ -193,7 +189,8 @@ describe API::V2::GraphqlController do
end end
end end
context "filter archived dossiers" do describe "filter archived dossiers" do
let(:dossiers) { [dossier, dossier1, dossier2] }
let(:query) do let(:query) do
"{ "{
demarche(number: #{procedure.id}) { demarche(number: #{procedure.id}) {
@ -210,7 +207,8 @@ describe API::V2::GraphqlController do
context 'with archived=true' do context 'with archived=true' do
let(:archived_filter) { 'true' } let(:archived_filter) { 'true' }
it "only archived dossiers should be returned" do
it 'returns only archived dossiers' do
expect(gql_errors).to eq(nil) expect(gql_errors).to eq(nil)
expect(gql_data).to eq(demarche: { expect(gql_data).to eq(demarche: {
id: procedure.to_typed_id, id: procedure.to_typed_id,
@ -224,7 +222,8 @@ describe API::V2::GraphqlController do
context 'with archived=false' do context 'with archived=false' do
let(:archived_filter) { 'false' } let(:archived_filter) { 'false' }
it "only not archived dossiers should be returned" do
it 'returns only non-archived dossiers' do
expect(gql_errors).to eq(nil) expect(gql_errors).to eq(nil)
expect(gql_data).to eq(demarche: { expect(gql_data).to eq(demarche: {
id: procedure.to_typed_id, id: procedure.to_typed_id,
@ -237,7 +236,7 @@ describe API::V2::GraphqlController do
end end
end end
context "filter by minRevision" do describe "filter by minRevision" do
let(:query) do let(:query) do
"{ "{
demarche(number: #{procedure.id}) { demarche(number: #{procedure.id}) {
@ -266,7 +265,7 @@ describe API::V2::GraphqlController do
end end
end end
context "filter by maxRevision" do describe "filter by maxRevision" do
let(:query) do let(:query) do
"{ "{
demarche(number: #{procedure.id}) { demarche(number: #{procedure.id}) {
@ -296,8 +295,20 @@ describe API::V2::GraphqlController do
end end
end end
context "dossier" do describe "dossier" do
context "with individual" do let(:dossier) do
dossier = create(:dossier,
:en_construction,
:with_populated_champs,
:with_populated_annotations,
:with_individual,
procedure: procedure)
create(:commentaire, :with_file, dossier: dossier, email: 'test@test.com')
dossier
end
context "for individual" do
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, :with_all_champs, :with_all_annotations, administrateurs: [admin]) }
let(:query) do let(:query) do
"{ "{
dossier(number: #{dossier.id}) { dossier(number: #{dossier.id}) {
@ -449,7 +460,7 @@ describe API::V2::GraphqlController do
end end
end end
context "with entreprise" do context "for entreprise" do
let(:procedure_for_entreprise) { create(:procedure, :published, administrateurs: [admin]) } let(:procedure_for_entreprise) { create(:procedure, :published, administrateurs: [admin]) }
let(:dossier) { create(:dossier, :en_construction, :with_entreprise, procedure: procedure_for_entreprise) } let(:dossier) { create(:dossier, :en_construction, :with_entreprise, procedure: procedure_for_entreprise) }
@ -661,7 +672,7 @@ describe API::V2::GraphqlController do
end end
end end
context "deletedDossiers" do describe "deletedDossiers" do
let(:query) do let(:query) do
"{ "{
demarche(number: #{procedure.id}) { demarche(number: #{procedure.id}) {
@ -699,7 +710,7 @@ describe API::V2::GraphqlController do
end end
end end
context "champ" do describe "champ" do
let(:champ) { create(:champ_piece_justificative, dossier: dossier) } let(:champ) { create(:champ_piece_justificative, dossier: dossier) }
let(:byte_size) { 2712286911 } let(:byte_size) { 2712286911 }
@ -722,7 +733,7 @@ describe API::V2::GraphqlController do
} }
end end
context "when file is really big" do context "when the file is really big" do
before do before do
champ.piece_justificative_file.blob.update(byte_size: byte_size) champ.piece_justificative_file.blob.update(byte_size: byte_size)
end end
@ -766,7 +777,7 @@ describe API::V2::GraphqlController do
end end
end end
context "groupeInstructeur" do describe "groupeInstructeur" do
let(:groupe_instructeur) { procedure.groupe_instructeurs.first } let(:groupe_instructeur) { procedure.groupe_instructeurs.first }
let(:query) do let(:query) do
"{ "{
@ -796,7 +807,7 @@ describe API::V2::GraphqlController do
end end
end end
context "mutations" do describe "mutations" do
describe 'dossierEnvoyerMessage' do describe 'dossierEnvoyerMessage' do
context 'success' do context 'success' do
let(:query) do let(:query) do
@ -902,6 +913,7 @@ describe API::V2::GraphqlController do
end end
describe 'dossierPasserEnInstruction' do describe 'dossierPasserEnInstruction' do
let(:dossiers) { [dossier2, dossier1, dossier] }
let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) } let(:dossier) { create(:dossier, :en_construction, :with_individual, procedure: procedure) }
let(:instructeur_id) { instructeur.to_typed_id } let(:instructeur_id) { instructeur.to_typed_id }
let(:disable_notification) { false } let(:disable_notification) { false }
@ -1322,6 +1334,8 @@ describe API::V2::GraphqlController do
end end
describe 'dossierModifierAnnotation' do describe 'dossierModifierAnnotation' do
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, :with_all_annotations, administrateurs: [admin]) }
describe 'text' do describe 'text' do
let(:query) do let(:query) do
"mutation { "mutation {
@ -1563,7 +1577,7 @@ describe API::V2::GraphqlController do
expect(gql_errors).not_to eq(nil) expect(gql_errors).not_to eq(nil)
end end
context "dossier" do describe "dossier" do
let(:query) { "{ dossier(number: #{dossier.id}) { id number usager { email } } }" } let(:query) { "{ dossier(number: #{dossier.id}) { id number usager { email } } }" }
it "should return error" do it "should return error" do
@ -1572,7 +1586,7 @@ describe API::V2::GraphqlController do
end end
end end
context "mutation" do describe "mutation" do
let(:query) do let(:query) do
"mutation { "mutation {
dossierEnvoyerMessage(input: { dossierEnvoyerMessage(input: {

View file

@ -560,8 +560,8 @@ describe Instructeurs::DossiersController, type: :controller do
let(:dossier) do let(:dossier) do
create(:dossier, create(:dossier,
:accepte, :accepte,
:with_all_champs, :with_populated_champs,
:with_all_annotations, :with_populated_annotations,
:with_motivation, :with_motivation,
:with_entreprise, :with_entreprise,
:with_commentaires, procedure: procedure) :with_commentaires, procedure: procedure)
@ -593,7 +593,7 @@ describe Instructeurs::DossiersController, type: :controller do
build(:type_de_champ_repetition, :with_types_de_champ, position: 3) build(:type_de_champ_repetition, :with_types_de_champ, position: 3)
], instructeurs: instructeurs) ], instructeurs: instructeurs)
end end
let(:dossier) { create(:dossier, :en_construction, :with_all_annotations, procedure: procedure) } let(:dossier) { create(:dossier, :en_construction, :with_populated_annotations, procedure: procedure) }
let(:another_instructeur) { create(:instructeur) } let(:another_instructeur) { create(:instructeur) }
let(:now) { Time.zone.parse('01/01/2100') } let(:now) { Time.zone.parse('01/01/2100') }

View file

@ -1,10 +1,10 @@
describe RechercheController, type: :controller do describe RechercheController, type: :controller do
let(:dossier) { create(:dossier, :en_construction, :with_all_annotations) } let(:dossier) { create(:dossier, :en_construction, :with_populated_annotations) }
let(:dossier2) { create(:dossier, :en_construction, procedure: dossier.procedure) } let(:dossier2) { create(:dossier, :en_construction, procedure: dossier.procedure) }
let(:instructeur) { create(:instructeur) } let(:instructeur) { create(:instructeur) }
let(:dossier_with_expert) { avis.dossier } let(:dossier_with_expert) { avis.dossier }
let(:avis) { create(:avis, dossier: create(:dossier, :en_construction, :with_all_annotations)) } let(:avis) { create(:avis, dossier: create(:dossier, :en_construction, :with_populated_annotations)) }
let(:user) { instructeur.user } let(:user) { instructeur.user }

View file

@ -879,7 +879,7 @@ describe Users::DossiersController, type: :controller do
let(:dossier) do let(:dossier) do
create(:dossier, create(:dossier,
:accepte, :accepte,
:with_all_champs, :with_populated_champs,
:with_motivation, :with_motivation,
:with_commentaires, :with_commentaires,
procedure: procedure, procedure: procedure,

View file

@ -212,7 +212,7 @@ FactoryBot.define do
end end
end end
trait :with_all_champs do trait :with_populated_champs do
after(:create) do |dossier, _evaluator| after(:create) do |dossier, _evaluator|
dossier.champs = dossier.types_de_champ.map do |type_de_champ| dossier.champs = dossier.types_de_champ.map do |type_de_champ|
build(:"champ_#{type_de_champ.type_champ}", dossier: dossier, type_de_champ: type_de_champ) build(:"champ_#{type_de_champ.type_champ}", dossier: dossier, type_de_champ: type_de_champ)
@ -221,7 +221,7 @@ FactoryBot.define do
end end
end end
trait :with_all_annotations do trait :with_populated_annotations do
after(:create) do |dossier, _evaluator| after(:create) do |dossier, _evaluator|
dossier.champs_private = dossier.types_de_champ_private.map do |type_de_champ| dossier.champs_private = dossier.types_de_champ_private.map do |type_de_champ|
build(:"champ_#{type_de_champ.type_champ}", private: true, dossier: dossier, type_de_champ: type_de_champ) build(:"champ_#{type_de_champ.type_champ}", private: true, dossier: dossier, type_de_champ: type_de_champ)

View file

@ -1341,14 +1341,20 @@ describe Dossier do
end end
describe "champs_for_export" do describe "champs_for_export" do
let(:procedure) { create(:procedure, :with_type_de_champ, :with_datetime, :with_yes_no, :with_explication, :with_commune) } let(:procedure) { create(:procedure, :with_type_de_champ, :with_datetime, :with_yes_no, :with_explication, :with_commune, :with_repetition) }
let(:text_type_de_champ) { procedure.types_de_champ.find { |type_de_champ| type_de_champ.type_champ == TypeDeChamp.type_champs.fetch(:text) } } let(:text_type_de_champ) { procedure.types_de_champ.find { |type_de_champ| type_de_champ.type_champ == TypeDeChamp.type_champs.fetch(:text) } }
let(:yes_no_type_de_champ) { procedure.types_de_champ.find { |type_de_champ| type_de_champ.type_champ == TypeDeChamp.type_champs.fetch(:yes_no) } } let(:yes_no_type_de_champ) { procedure.types_de_champ.find { |type_de_champ| type_de_champ.type_champ == TypeDeChamp.type_champs.fetch(:yes_no) } }
let(:datetime_type_de_champ) { procedure.types_de_champ.find { |type_de_champ| type_de_champ.type_champ == TypeDeChamp.type_champs.fetch(:datetime) } } let(:datetime_type_de_champ) { procedure.types_de_champ.find { |type_de_champ| type_de_champ.type_champ == TypeDeChamp.type_champs.fetch(:datetime) } }
let(:explication_type_de_champ) { procedure.types_de_champ.find { |type_de_champ| type_de_champ.type_champ == TypeDeChamp.type_champs.fetch(:explication) } } let(:explication_type_de_champ) { procedure.types_de_champ.find { |type_de_champ| type_de_champ.type_champ == TypeDeChamp.type_champs.fetch(:explication) } }
let(:commune_type_de_champ) { procedure.types_de_champ.find { |type_de_champ| type_de_champ.type_champ == TypeDeChamp.type_champs.fetch(:communes) } } let(:commune_type_de_champ) { procedure.types_de_champ.find { |type_de_champ| type_de_champ.type_champ == TypeDeChamp.type_champs.fetch(:communes) } }
let(:repetition_type_de_champ) { procedure.types_de_champ.find { |type_de_champ| type_de_champ.type_champ == TypeDeChamp.type_champs.fetch(:repetition) } }
let(:repetition_champ) { dossier.champs.find(&:repetition?) }
let(:repetition_second_revision_champ) { dossier_second_revision.champs.find(&:repetition?) }
let(:dossier) { create(:dossier, procedure: procedure) } let(:dossier) { create(:dossier, procedure: procedure) }
let(:dossier_second_revision) { create(:dossier, procedure: procedure) } let(:dossier_second_revision) { create(:dossier, procedure: procedure) }
let(:dossier_champs_for_export) { Dossier.champs_for_export(dossier.champs, procedure.types_de_champ_for_procedure_presentation.not_repetition) }
let(:dossier_second_revision_champs_for_export) { Dossier.champs_for_export(dossier_second_revision.champs, procedure.types_de_champ_for_procedure_presentation.not_repetition) }
let(:repetition_second_revision_champs_for_export) { Dossier.champs_for_export(repetition_second_revision_champ.champs, procedure.types_de_champ_for_procedure_presentation.repetition) }
context "when procedure published" do context "when procedure published" do
before do before do
@ -1358,16 +1364,19 @@ describe Dossier do
procedure.draft_revision.add_type_de_champ(type_champ: TypeDeChamp.type_champs.fetch(:text), libelle: 'New text field') procedure.draft_revision.add_type_de_champ(type_champ: TypeDeChamp.type_champs.fetch(:text), libelle: 'New text field')
procedure.draft_revision.find_or_clone_type_de_champ(yes_no_type_de_champ.stable_id).update(libelle: 'Updated yes/no') procedure.draft_revision.find_or_clone_type_de_champ(yes_no_type_de_champ.stable_id).update(libelle: 'Updated yes/no')
procedure.draft_revision.find_or_clone_type_de_champ(commune_type_de_champ.stable_id).update(libelle: 'Commune de naissance') procedure.draft_revision.find_or_clone_type_de_champ(commune_type_de_champ.stable_id).update(libelle: 'Commune de naissance')
procedure.draft_revision.find_or_clone_type_de_champ(repetition_type_de_champ.stable_id).update(libelle: 'Repetition')
procedure.update(published_revision: procedure.draft_revision, draft_revision: procedure.create_new_revision) procedure.update(published_revision: procedure.draft_revision, draft_revision: procedure.create_new_revision)
dossier.reload dossier.reload
procedure.reload procedure.reload
end end
it "should have champs from all revisions" do it "should have champs from all revisions" do
expect(dossier.types_de_champ.map(&:libelle)).to eq([text_type_de_champ.libelle, datetime_type_de_champ.libelle, "Yes/no", explication_type_de_champ.libelle, commune_type_de_champ.libelle]) expect(dossier.types_de_champ.map(&:libelle)).to eq([text_type_de_champ.libelle, datetime_type_de_champ.libelle, "Yes/no", explication_type_de_champ.libelle, commune_type_de_champ.libelle, repetition_type_de_champ.libelle])
expect(dossier_second_revision.types_de_champ.map(&:libelle)).to eq([datetime_type_de_champ.libelle, "Updated yes/no", explication_type_de_champ.libelle, 'Commune de naissance', "New text field"]) expect(dossier_second_revision.types_de_champ.map(&:libelle)).to eq([datetime_type_de_champ.libelle, "Updated yes/no", explication_type_de_champ.libelle, 'Commune de naissance', "Repetition", "New text field"])
expect(dossier.champs_for_export(dossier.procedure.types_de_champ_for_procedure_presentation).map { |(libelle)| libelle }).to eq([text_type_de_champ.libelle, datetime_type_de_champ.libelle, "Updated yes/no", "Commune de naissance", "Commune de naissance (Code insee)", "New text field"]) expect(dossier_champs_for_export.map { |(libelle)| libelle }).to eq([text_type_de_champ.libelle, datetime_type_de_champ.libelle, "Updated yes/no", "Commune de naissance", "Commune de naissance (Code insee)", "New text field"])
expect(dossier.champs_for_export(dossier.procedure.types_de_champ_for_procedure_presentation)).to eq(dossier_second_revision.champs_for_export(dossier_second_revision.procedure.types_de_champ_for_procedure_presentation)) expect(dossier_champs_for_export).to eq(dossier_second_revision_champs_for_export)
expect(repetition_second_revision_champs_for_export.map { |(libelle)| libelle }).to eq(procedure.types_de_champ_for_procedure_presentation.repetition.map(&:libelle_for_export))
expect(repetition_second_revision_champs_for_export.first.size).to eq(2)
end end
end end
@ -1375,7 +1384,7 @@ describe Dossier do
let(:procedure) { create(:procedure, :with_type_de_champ, :with_explication) } let(:procedure) { create(:procedure, :with_type_de_champ, :with_explication) }
it "should not contain non-exportable types de champ" do it "should not contain non-exportable types de champ" do
expect(dossier.champs_for_export(dossier.procedure.types_de_champ_for_procedure_presentation).map { |(libelle)| libelle }).to eq([text_type_de_champ.libelle]) expect(dossier_champs_for_export.map { |(libelle)| libelle }).to eq([text_type_de_champ.libelle])
end end
end end
end end
@ -1452,6 +1461,42 @@ describe Dossier do
expect(dossier.destroy).to be_truthy expect(dossier.destroy).to be_truthy
expect(transfer.reload).not_to be_nil expect(transfer.reload).not_to be_nil
end end
context 'discarded' do
context 'en_construction' do
let(:dossier) { create(:dossier, :en_construction) }
before do
create(:avis, dossier: dossier)
Timecop.travel(2.weeks.ago) do
dossier.discard!
end
dossier.reload
end
it "can destroy dossier with avis" do
Avis.discarded_en_construction_expired.destroy_all
expect(dossier.destroy).to be_truthy
end
end
context 'termine' do
let(:dossier) { create(:dossier, :accepte) }
before do
create(:avis, dossier: dossier)
Timecop.travel(2.weeks.ago) do
dossier.discard!
end
dossier.reload
end
it "can destroy dossier with avis" do
Avis.discarded_termine_expired.destroy_all
expect(dossier.destroy).to be_truthy
end
end
end
end end
describe "#spreadsheet_columns" do describe "#spreadsheet_columns" do

View file

@ -1,6 +1,6 @@
describe ProcedurePresentation do describe ProcedurePresentation do
describe "#types_de_champ_for_procedure_presentation" do describe "#types_de_champ_for_procedure_presentation" do
subject { procedure.types_de_champ_for_procedure_presentation.pluck(:libelle) } subject { procedure.types_de_champ_for_procedure_presentation.not_repetition.pluck(:libelle) }
context 'for a draft procedure' do context 'for a draft procedure' do
let(:procedure) { create(:procedure) } let(:procedure) { create(:procedure) }

View file

@ -56,7 +56,7 @@ describe ProcedureRevision do
revision.reload revision.reload
expect(revision.types_de_champ.index(type_de_champ)).to eq(2) expect(revision.types_de_champ.index(type_de_champ)).to eq(2)
expect(revision.procedure.types_de_champ.index(type_de_champ)).to eq(2) expect(revision.procedure.types_de_champ.index(type_de_champ)).to eq(2)
expect(revision.procedure.types_de_champ_for_procedure_presentation.index(type_de_champ)).to eq(2) expect(revision.procedure.types_de_champ_for_procedure_presentation.not_repetition.index(type_de_champ)).to eq(2)
end end
it 'move up' do it 'move up' do
@ -66,7 +66,7 @@ describe ProcedureRevision do
revision.reload revision.reload
expect(revision.types_de_champ.index(last_type_de_champ)).to eq(0) expect(revision.types_de_champ.index(last_type_de_champ)).to eq(0)
expect(revision.procedure.types_de_champ.index(last_type_de_champ)).to eq(0) expect(revision.procedure.types_de_champ.index(last_type_de_champ)).to eq(0)
expect(revision.procedure.types_de_champ_for_procedure_presentation.index(last_type_de_champ)).to eq(0) expect(revision.procedure.types_de_champ_for_procedure_presentation.not_repetition.index(last_type_de_champ)).to eq(0)
end end
context 'repetition' do context 'repetition' do

View file

@ -32,7 +32,7 @@ describe ProcedureExportService do
end end
describe 'Dossiers sheet' do describe 'Dossiers sheet' do
let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :with_individual, procedure: procedure) } let!(:dossier) { create(:dossier, :en_instruction, :with_populated_champs, :with_individual, procedure: procedure) }
let(:nominal_headers) do let(:nominal_headers) do
[ [
@ -119,7 +119,7 @@ describe ProcedureExportService do
describe 'Etablissement sheet' do describe 'Etablissement sheet' do
let(:procedure) { create(:procedure, :published, :with_all_champs) } let(:procedure) { create(:procedure, :published, :with_all_champs) }
let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :with_entreprise, procedure: procedure) } let!(:dossier) { create(:dossier, :en_instruction, :with_populated_champs, :with_entreprise, procedure: procedure) }
let(:dossier_etablissement) { etablissements_sheet.data[1] } let(:dossier_etablissement) { etablissements_sheet.data[1] }
let(:champ_etablissement) { etablissements_sheet.data[0] } let(:champ_etablissement) { etablissements_sheet.data[0] }
@ -309,7 +309,7 @@ describe ProcedureExportService do
end end
describe 'Avis sheet' do describe 'Avis sheet' do
let!(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :with_individual, procedure: procedure) } let!(:dossier) { create(:dossier, :en_instruction, :with_populated_champs, :with_individual, procedure: procedure) }
let!(:avis) { create(:avis, :with_answer, dossier: dossier) } let!(:avis) { create(:avis, :with_answer, dossier: dossier) }
it 'should have headers' do it 'should have headers' do
@ -332,8 +332,8 @@ describe ProcedureExportService do
describe 'Repetitions sheet' do describe 'Repetitions sheet' do
let!(:dossiers) do let!(:dossiers) do
[ [
create(:dossier, :en_instruction, :with_all_champs, :with_individual, procedure: procedure), create(:dossier, :en_instruction, :with_populated_champs, :with_individual, procedure: procedure),
create(:dossier, :en_instruction, :with_all_champs, :with_individual, procedure: procedure) create(:dossier, :en_instruction, :with_populated_champs, :with_individual, procedure: procedure)
] ]
end end
let(:champ_repetition) { dossiers.first.champs.find { |champ| champ.type_champ == 'repetition' } } let(:champ_repetition) { dossiers.first.champs.find { |champ| champ.type_champ == 'repetition' } }
@ -381,7 +381,7 @@ describe ProcedureExportService do
end end
context 'with non unique labels' do context 'with non unique labels' do
let(:dossier) { create(:dossier, :en_instruction, :with_all_champs, :with_individual, procedure: procedure) } let(:dossier) { create(:dossier, :en_instruction, :with_populated_champs, :with_individual, procedure: procedure) }
let(:champ_repetition) { dossier.champs.find { |champ| champ.type_champ == 'repetition' } } let(:champ_repetition) { dossier.champs.find { |champ| champ.type_champ == 'repetition' } }
let(:type_de_champ_repetition) { create(:type_de_champ_repetition, procedure: procedure, libelle: champ_repetition.libelle) } let(:type_de_champ_repetition) { create(:type_de_champ_repetition, procedure: procedure, libelle: champ_repetition.libelle) }
let!(:another_champ_repetition) { create(:champ_repetition, type_de_champ: type_de_champ_repetition, dossier: dossier) } let!(:another_champ_repetition) { create(:champ_repetition, type_de_champ: type_de_champ_repetition, dossier: dossier) }

View file

@ -1,7 +1,7 @@
describe 'user access to the list of their dossiers' do describe 'user access to the list of their dossiers' do
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:dossier_brouillon) { create(:dossier, user: user) } let!(:dossier_brouillon) { create(:dossier, user: user) }
let!(:dossier_en_construction) { create(:dossier, :with_all_champs, :en_construction, user: user) } let!(:dossier_en_construction) { create(:dossier, :with_populated_champs, :en_construction, user: user) }
let!(:dossier_en_instruction) { create(:dossier, :en_instruction, user: user) } let!(:dossier_en_instruction) { create(:dossier, :en_instruction, user: user) }
let!(:dossier_archived) { create(:dossier, :en_instruction, :archived, user: user) } let!(:dossier_archived) { create(:dossier, :en_instruction, :archived, user: user) }
let(:dossiers_per_page) { 25 } let(:dossiers_per_page) { 25 }
@ -121,7 +121,7 @@ describe 'user access to the list of their dossiers' do
end end
context "when user search for something inside the dossier" do context "when user search for something inside the dossier" do
let(:dossier_en_construction2) { create(:dossier, :with_all_champs, :en_construction, user: user) } let(:dossier_en_construction2) { create(:dossier, :with_populated_champs, :en_construction, user: user) }
before do before do
page.find_by_id('q').set(dossier_en_construction.champs.first.value) page.find_by_id('q').set(dossier_en_construction.champs.first.value)
end end