Merge pull request #3597 from betagouv/dev

2019-03-12-01
This commit is contained in:
Mathieu Magnin 2019-03-12 14:49:45 +01:00 committed by GitHub
commit dd7ae8ddb0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
76 changed files with 636 additions and 967 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

View file

@ -23,13 +23,23 @@ $landing-breakpoint: 1040px;
.header-logo { .header-logo {
display: inline-block; display: inline-block;
margin-right: 4 * $default-spacer;
img { .header-logo-wide {
height: 34px; margin-right: 4 * $default-spacer;
}
@media (max-width: $landing-breakpoint) { .header-logo-narrow {
height: 18px; display: none;
}
@media (max-width: $landing-breakpoint) {
.header-logo-wide {
display: none;
}
.header-logo-narrow {
display: inline;
margin-right: 0;
} }
} }
} }
@ -129,7 +139,8 @@ $landing-breakpoint: 1040px;
padding: 9px; padding: 9px;
padding-right: 42px; padding-right: 42px;
float: right; float: right;
width: 300px; max-width: 300px;
min-width: 90px;
margin: 0; margin: 0;
} }

View file

@ -44,7 +44,7 @@ class Admin::ProceduresController < AdminController
def edit def edit
@path = @procedure.path || @procedure.default_path @path = @procedure.path || @procedure.default_path
@availability = @procedure.path_availability(@path) @availability = @procedure.path_availability(current_administrateur, @path)
end end
def destroy def destroy
@ -96,10 +96,10 @@ class Admin::ProceduresController < AdminController
flash.now.alert = @procedure.errors.full_messages flash.now.alert = @procedure.errors.full_messages
@path = procedure_params[:path] @path = procedure_params[:path]
if @path.present? if @path.present?
@availability = @procedure.path_availability(@path) @availability = @procedure.path_availability(current_administrateur, @path)
end end
render 'edit' render 'edit'
elsif Flipflop.publish_draft? && @procedure.brouillon? elsif @procedure.brouillon?
reset_procedure reset_procedure
flash.notice = 'Démarche modifiée. Tous les dossiers de cette démarche ont été supprimés.' flash.notice = 'Démarche modifiée. Tous les dossiers de cette démarche ont été supprimés.'
redirect_to edit_admin_procedure_path(id: @procedure.id) redirect_to edit_admin_procedure_path(id: @procedure.id)
@ -121,7 +121,7 @@ class Admin::ProceduresController < AdminController
procedure.path = nil procedure.path = nil
end end
if procedure.publish_or_reopen!(path) if procedure.publish_or_reopen!(current_administrateur, path)
flash.notice = "Démarche publiée" flash.notice = "Démarche publiée"
redirect_to admin_procedures_path redirect_to admin_procedures_path
else else
@ -195,7 +195,7 @@ class Admin::ProceduresController < AdminController
.pluck('procedures.id') .pluck('procedures.id')
@grouped_procedures = Procedure @grouped_procedures = Procedure
.includes(:administrateur, :service) .includes(:administrateurs, :service)
.where(id: significant_procedure_ids) .where(id: significant_procedure_ids)
.group_by(&:organisation_name) .group_by(&:organisation_name)
.sort_by { |_, procedures| procedures.first.created_at } .sort_by { |_, procedures| procedures.first.created_at }
@ -217,11 +217,10 @@ class Admin::ProceduresController < AdminController
json_path_list = Procedure json_path_list = Procedure
.find_with_path(params[:request]) .find_with_path(params[:request])
.order(:id) .order(:id)
.pluck(:path, :administrateur_id) .map do |procedure|
.map do |path, administrateur_id|
{ {
label: path, label: procedure.path,
mine: administrateur_id == current_administrateur.id mine: current_administrateur.owns?(procedure)
} }
end.to_json end.to_json
@ -234,7 +233,7 @@ class Admin::ProceduresController < AdminController
if procedure_id.present? if procedure_id.present?
procedure = current_administrateur.procedures.find(procedure_id) procedure = current_administrateur.procedures.find(procedure_id)
@availability = procedure.path_availability(path) @availability = procedure.path_availability(current_administrateur, path)
else else
@availability = Procedure.path_availability(current_administrateur, path) @availability = Procedure.path_availability(current_administrateur, path)
end end
@ -273,10 +272,7 @@ class Admin::ProceduresController < AdminController
if @procedure&.locked? if @procedure&.locked?
params.require(:procedure).permit(*editable_params) params.require(:procedure).permit(*editable_params)
else else
if Flipflop.publish_draft? params.require(:procedure).permit(*editable_params, :duree_conservation_dossiers_dans_ds, :duree_conservation_dossiers_hors_ds, :for_individual, :individual_with_siret, :ask_birthday, :path)
editable_params << :path
end
params.require(:procedure).permit(*editable_params, :duree_conservation_dossiers_dans_ds, :duree_conservation_dossiers_hors_ds, :for_individual, :individual_with_siret, :ask_birthday).merge(administrateur_id: current_administrateur.id)
end end
end end
end end

View file

@ -38,7 +38,10 @@ class API::V1::DossiersController < APIController
def fetch_procedure_and_check_token def fetch_procedure_and_check_token
@procedure = Procedure.for_api.find(params[:procedure_id]) @procedure = Procedure.for_api.find(params[:procedure_id])
if !valid_token_for_administrateur?(@procedure.administrateur) administrateur = find_administrateur_for_token(@procedure)
if administrateur
Current.administrateur = administrateur
else
render json: {}, status: :unauthorized render json: {}, status: :unauthorized
end end

View file

@ -10,7 +10,10 @@ class API::V1::ProceduresController < APIController
def fetch_procedure_and_check_token def fetch_procedure_and_check_token
@procedure = Procedure.for_api.find(params[:id]) @procedure = Procedure.for_api.find(params[:id])
if !valid_token_for_administrateur?(@procedure.administrateur) administrateur = find_administrateur_for_token(@procedure)
if administrateur
Current.administrateur = administrateur
else
render json: {}, status: :unauthorized render json: {}, status: :unauthorized
end end

View file

@ -3,8 +3,10 @@ class APIController < ApplicationController
protected protected
def valid_token_for_administrateur?(administrateur) def find_administrateur_for_token(procedure)
administrateur.valid_api_token?(token) procedure.administrateurs.find do |administrateur|
administrateur.valid_api_token?(token)
end
end end
private private

View file

@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception. # Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead. # For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception, if: -> { !Rails.env.test? } protect_from_forgery with: :exception, if: -> { !Rails.env.test? }
before_action :set_current_roles
before_action :load_navbar_left_pannel_partial_url before_action :load_navbar_left_pannel_partial_url
before_action :set_raven_context before_action :set_raven_context
before_action :redirect_if_untrusted before_action :redirect_if_untrusted
@ -79,6 +80,11 @@ class ApplicationController < ActionController::Base
private private
def set_current_roles
Current.administrateur = current_administrateur
Current.gestionnaire = current_gestionnaire
end
def set_active_storage_host def set_active_storage_host
ActiveStorage::Current.host = request.base_url ActiveStorage::Current.host = request.base_url
end end
@ -158,7 +164,7 @@ class ApplicationController < ActionController::Base
def redirect_if_untrusted def redirect_if_untrusted
if gestionnaire_signed_in? && if gestionnaire_signed_in? &&
sensitive_path && sensitive_path &&
current_gestionnaire.feature_enabled?(:enable_email_login_token) && Flipflop.enable_email_login_token? &&
!trusted_device? !trusted_device?
# return at this location # return at this location

View file

@ -24,5 +24,17 @@ module Manager
flash[:notice] = "La démarche a bien été supprimée, en cas d'erreur contactez un développeur." flash[:notice] = "La démarche a bien été supprimée, en cas d'erreur contactez un développeur."
redirect_to manager_procedures_path redirect_to manager_procedures_path
end end
def add_administrateur
procedure = Procedure.find(params[:id])
administrateur = Administrateur.find_by(email: params[:email])
if administrateur
procedure.administrateurs << administrateur
flash[:notice] = "L'administrateur \"#{params[:email]}\" est ajouté à la démarche."
else
flash[:alert] = "L'administrateur \"#{params[:email]}\" est introuvable."
end
redirect_to manager_procedure_path(procedure)
end
end end
end end

View file

@ -81,13 +81,19 @@ module NewAdministrateur
end end
def services def services
current_administrateur.services service_ids = current_administrateur.service_ids
service_ids << maybe_procedure&.service_id
Service.where(id: service_ids.compact.uniq)
end end
def procedure_params def procedure_params
params.require(:procedure).permit(:id, :service_id) params.require(:procedure).permit(:id, :service_id)
end end
def maybe_procedure
current_administrateur.procedures.find_by(id: params[:procedure_id])
end
def procedure def procedure
current_administrateur.procedures.find(params[:procedure_id]) current_administrateur.procedures.find(params[:procedure_id])
end end

View file

@ -87,7 +87,7 @@ module NewGestionnaire
@dossiers = @dossiers.where(id: filtered_sorted_paginated_ids) @dossiers = @dossiers.where(id: filtered_sorted_paginated_ids)
eager_load_displayed_fields @dossiers = procedure_presentation.eager_load_displayed_fields(@dossiers)
@dossiers = @dossiers.sort_by { |d| filtered_sorted_paginated_ids.index(d.id) } @dossiers = @dossiers.sort_by { |d| filtered_sorted_paginated_ids.index(d.id) }
@ -102,17 +102,13 @@ module NewGestionnaire
end end
fields = values.map do |value| fields = values.map do |value|
table, column = value.split("/") find_field(*value.split('/'))
procedure_presentation.fields.find do |field|
field['table'] == table && field['column'] == column
end
end end
procedure_presentation.update(displayed_fields: fields) procedure_presentation.update(displayed_fields: fields)
current_sort = procedure_presentation.sort current_sort = procedure_presentation.sort
if !values.include?("#{current_sort['table']}/#{current_sort['column']}") if !values.include?(field_id(current_sort))
procedure_presentation.update(sort: Procedure.default_sort) procedure_presentation.update(sort: Procedure.default_sort)
end end
@ -145,7 +141,7 @@ module NewGestionnaire
if params[:value].present? if params[:value].present?
filters = procedure_presentation.filters filters = procedure_presentation.filters
table, column = params[:field].split('/') table, column = params[:field].split('/')
label = procedure_presentation.fields.find { |c| c['table'] == table && c['column'] == column }['label'] label = find_field(table, column)['label']
filters[statut] << { filters[statut] << {
'label' => label, 'label' => label,
@ -162,11 +158,9 @@ module NewGestionnaire
def remove_filter def remove_filter
filters = procedure_presentation.filters filters = procedure_presentation.filters
filter_to_remove = current_filters.find do |filter|
filter['table'] == params[:table] && filter['column'] == params[:column]
end
filters[statut] = filters[statut] - [filter_to_remove] to_remove = params.values_at(:table, :column, :value)
filters[statut].reject! { |filter| filter.values_at('table', 'column', 'value') == to_remove }
procedure_presentation.update(filters: filters) procedure_presentation.update(filters: filters)
@ -194,6 +188,14 @@ module NewGestionnaire
private private
def find_field(table, column)
procedure_presentation.fields.find { |c| c['table'] == table && c['column'] == column }
end
def field_id(field)
field.values_at('table', 'column').join('/')
end
def statut def statut
@statut ||= (params[:statut].presence || 'a-suivre') @statut ||= (params[:statut].presence || 'a-suivre')
end end
@ -228,9 +230,7 @@ module NewGestionnaire
end end
def displayed_fields_values def displayed_fields_values
procedure_presentation.displayed_fields.map do |field| procedure_presentation.displayed_fields.map { |field| field_id(field) }
"#{field['table']}/#{field['column']}"
end
end end
def current_filters def current_filters
@ -238,44 +238,7 @@ module NewGestionnaire
end end
def available_fields_to_filters def available_fields_to_filters
current_filters_fields_ids = current_filters.map do |field| procedure_presentation.fields_for_select
"#{field['table']}/#{field['column']}"
end
procedure_presentation.fields_for_select.reject do |field|
current_filters_fields_ids.include?(field[1])
end
end
def eager_load_displayed_fields
procedure_presentation.displayed_fields
.reject { |field| field['table'] == 'self' }
.group_by do |field|
if ['type_de_champ', 'type_de_champ_private'].include?(field['table'])
'type_de_champ_group'
else
field['table']
end
end.each do |group_key, fields|
case group_key
when 'type_de_champ_group'
if fields.any? { |field| field['table'] == 'type_de_champ' }
@dossiers = @dossiers.includes(:champs).references(:champs)
end
if fields.any? { |field| field['table'] == 'type_de_champ_private' }
@dossiers = @dossiers.includes(:champs_private).references(:champs_private)
end
where_conditions = fields.map do |field|
"champs.type_de_champ_id = #{field['column']}"
end.join(" OR ")
@dossiers = @dossiers.where(where_conditions)
else
@dossiers = @dossiers.includes(fields.first['table'])
end
end
end end
def kaminarize(current_page, total) def kaminarize(current_page, total)

View file

@ -186,7 +186,7 @@ module NewUser
end end
def ask_deletion def ask_deletion
dossier = current_user.dossiers.includes(:user, procedure: :administrateur).find(params[:id]) dossier = current_user.dossiers.includes(:user, procedure: :administrateurs).find(params[:id])
if dossier.can_be_deleted_by_user? if dossier.can_be_deleted_by_user?
dossier.delete_and_keep_track dossier.delete_and_keep_track

View file

@ -23,8 +23,6 @@ class StatsController < ApplicationController
@dossiers_cumulative = cumulative_hash(dossiers, :en_construction_at) @dossiers_cumulative = cumulative_hash(dossiers, :en_construction_at)
@dossiers_in_the_last_4_months = last_four_months_hash(dossiers, :en_construction_at) @dossiers_in_the_last_4_months = last_four_months_hash(dossiers, :en_construction_at)
@procedures_count_per_administrateur = procedures_count_per_administrateur(procedures)
if administration_signed_in? if administration_signed_in?
@dossier_instruction_mean_time = Rails.cache.fetch("dossier_instruction_mean_time", expires_in: 1.day) do @dossier_instruction_mean_time = Rails.cache.fetch("dossier_instruction_mean_time", expires_in: 1.day) do
dossier_instruction_mean_time(dossiers) dossier_instruction_mean_time(dossiers)
@ -234,15 +232,6 @@ class StatsController < ApplicationController
.reduce({}, :merge) .reduce({}, :merge)
end end
def procedures_count_per_administrateur(procedures)
count_per_administrateur = procedures.group(:administrateur_id).count.values
{
'Une démarche' => count_per_administrateur.select { |count| count == 1 }.count,
'Entre deux et cinq démarches' => count_per_administrateur.select { |count| count.in?(2..5) }.count,
'Plus de cinq démarches' => count_per_administrateur.select { |count| count > 5 }.count
}
end
def mean(collection) def mean(collection)
(collection.sum.to_f / collection.size).round(2) (collection.sum.to_f / collection.size).round(2)
end end

View file

@ -14,7 +14,7 @@ class ProcedureDashboard < Administrate::BaseDashboard
path: ProcedureLinkField, path: ProcedureLinkField,
dossiers: Field::HasMany, dossiers: Field::HasMany,
gestionnaires: Field::HasMany, gestionnaires: Field::HasMany,
administrateur: Field::BelongsTo, administrateurs: Field::HasMany,
id: Field::Number.with_options(searchable: true), id: Field::Number.with_options(searchable: true),
libelle: Field::String, libelle: Field::String,
description: Field::String, description: Field::String,
@ -58,7 +58,7 @@ class ProcedureDashboard < Administrate::BaseDashboard
SHOW_PAGE_ATTRIBUTES = [ SHOW_PAGE_ATTRIBUTES = [
:id, :id,
:path, :path,
:administrateur, :administrateurs,
:libelle, :libelle,
:description, :description,
:lien_site_web, :lien_site_web,

View file

@ -9,19 +9,24 @@ module Flipflop::Strategies
end end
def enabled?(feature) def enabled?(feature)
# Can only check features if we have the user's session. find_current_administrateur&.feature_enabled?(feature) ||
if request? find_current_gestionnaire&.feature_enabled?(feature)
find_current_administrateur&.feature_enabled?(feature)
end
end end
private private
def find_current_administrateur def find_current_administrateur
if request.session["warden.user.administrateur.key"] administrateur_id = Current.administrateur&.id
administrateur_id = request.session["warden.user.administrateur.key"][0][0] if administrateur_id
Administrateur.find_by(id: administrateur_id) Administrateur.find_by(id: administrateur_id)
end end
end end
def find_current_gestionnaire
gestionnaire_id = Current.gestionnaire&.id
if gestionnaire_id
Gestionnaire.find_by(id: gestionnaire_id)
end
end
end end
end end

View file

@ -7,9 +7,8 @@ class Administrateur < ApplicationRecord
:recoverable, :rememberable, :trackable, :validatable :recoverable, :rememberable, :trackable, :validatable
has_and_belongs_to_many :gestionnaires has_and_belongs_to_many :gestionnaires
has_many :procedures
has_many :administrateurs_procedures has_many :administrateurs_procedures
has_many :admin_procedures, through: :administrateurs_procedures, source: :procedure has_many :procedures, through: :administrateurs_procedures
has_many :services has_many :services
has_many :dossiers, -> { state_not_brouillon }, through: :procedures has_many :dossiers, -> { state_not_brouillon }, through: :procedures
@ -117,7 +116,7 @@ class Administrateur < ApplicationRecord
end end
def owns?(procedure) def owns?(procedure)
id == procedure.administrateur_id procedure.administrateurs.include?(self)
end end
def gestionnaire def gestionnaire

View file

@ -0,0 +1,21 @@
module DossierFilteringConcern
extend ActiveSupport::Concern
included do
scope :filter_by_datetimes, lambda { |column, dates|
if dates.present?
dates
.map { |date| self.where(column => date..(date + 1.day)) }
.reduce(:or)
else
none
end
}
scope :filter_ilike, lambda { |table, column, values|
table_column = ProcedurePresentation.sanitized_column(table, column)
q = Array.new(values.count, "(#{table_column} ILIKE ?)").join(' OR ')
where(q, *(values.map { |value| "%#{value}%" }))
}
end
end

3
app/models/current.rb Normal file
View file

@ -0,0 +1,3 @@
class Current < ActiveSupport::CurrentAttributes
attribute :gestionnaire, :administrateur
end

View file

@ -1,4 +1,6 @@
class Dossier < ApplicationRecord class Dossier < ApplicationRecord
include DossierFilteringConcern
enum state: { enum state: {
brouillon: 'brouillon', brouillon: 'brouillon',
en_construction: 'en_construction', en_construction: 'en_construction',
@ -262,7 +264,7 @@ class Dossier < ApplicationRecord
update(hidden_at: deleted_dossier.deleted_at) update(hidden_at: deleted_dossier.deleted_at)
if en_construction? if en_construction?
administration_emails = followers_gestionnaires.present? ? followers_gestionnaires.pluck(:email) : [procedure.administrateur.email] administration_emails = followers_gestionnaires.present? ? followers_gestionnaires.pluck(:email) : procedure.administrateurs.pluck(: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

View file

@ -1,6 +1,8 @@
require Rails.root.join('lib', 'percentile') require Rails.root.join('lib', 'percentile')
class Procedure < ApplicationRecord class Procedure < ApplicationRecord
self.ignored_columns = [:administrateur_id]
MAX_DUREE_CONSERVATION = 36 MAX_DUREE_CONSERVATION = 36
has_many :types_de_piece_justificative, -> { ordered }, dependent: :destroy has_many :types_de_piece_justificative, -> { ordered }, dependent: :destroy
@ -12,7 +14,6 @@ class Procedure < ApplicationRecord
has_one :module_api_carto, dependent: :destroy has_one :module_api_carto, dependent: :destroy
has_one :attestation_template, dependent: :destroy has_one :attestation_template, dependent: :destroy
belongs_to :administrateur
belongs_to :parent_procedure, class_name: 'Procedure' belongs_to :parent_procedure, class_name: 'Procedure'
belongs_to :service belongs_to :service
@ -48,7 +49,7 @@ class Procedure < ApplicationRecord
scope :for_api, -> { scope :for_api, -> {
includes( includes(
:administrateur, :administrateurs,
:types_de_champ_private, :types_de_champ_private,
:types_de_champ, :types_de_champ,
:types_de_piece_justificative, :types_de_piece_justificative,
@ -101,12 +102,12 @@ class Procedure < ApplicationRecord
end end
end end
def publish_or_reopen!(path) def publish_or_reopen!(administrateur, path)
if archivee? && may_reopen?(path) if archivee? && may_reopen?(administrateur, path)
reopen!(path) reopen!(administrateur, path)
elsif may_publish?(path) elsif may_publish?(administrateur, path)
reset! reset!
publish!(path) publish!(administrateur, path)
end end
end end
@ -124,7 +125,7 @@ class Procedure < ApplicationRecord
# This method is needed for transition. Eventually this will be the same as brouillon?. # This method is needed for transition. Eventually this will be the same as brouillon?.
def brouillon_avec_lien? def brouillon_avec_lien?
Flipflop.publish_draft? && brouillon? && path.present? brouillon? && path.present?
end end
def publiee_ou_archivee? def publiee_ou_archivee?
@ -220,7 +221,6 @@ class Procedure < ApplicationRecord
procedure.administrateurs = administrateurs procedure.administrateurs = administrateurs
end end
procedure.administrateur = admin
procedure.initiated_mail = initiated_mail&.dup procedure.initiated_mail = initiated_mail&.dup
procedure.received_mail = received_mail&.dup procedure.received_mail = received_mail&.dup
procedure.closed_mail = closed_mail&.dup procedure.closed_mail = closed_mail&.dup
@ -340,7 +340,7 @@ class Procedure < ApplicationRecord
PATH_NOT_AVAILABLE_BROUILLON = :not_available_brouillon PATH_NOT_AVAILABLE_BROUILLON = :not_available_brouillon
PATH_CAN_PUBLISH = [PATH_AVAILABLE, PATH_AVAILABLE_PUBLIEE] PATH_CAN_PUBLISH = [PATH_AVAILABLE, PATH_AVAILABLE_PUBLIEE]
def path_availability(path) def path_availability(administrateur, path)
Procedure.path_availability(administrateur, path, id) Procedure.path_availability(administrateur, path, id)
end end
@ -391,7 +391,9 @@ class Procedure < ApplicationRecord
private private
def claim_path_ownership!(path) def claim_path_ownership!(path)
procedure = Procedure.where(administrateur: administrateur).find_by(path: path) procedure = Procedure.joins(:administrateurs)
.where(administrateurs: { id: administrateur_ids })
.find_by(path: path)
if procedure&.publiee? && procedure != self if procedure&.publiee? && procedure != self
procedure.archive! procedure.archive!
@ -400,17 +402,21 @@ class Procedure < ApplicationRecord
update!(path: path) update!(path: path)
end end
def can_publish?(path) def can_publish?(administrateur, path)
path_availability(path).in?(PATH_CAN_PUBLISH) path_availability(administrateur, path).in?(PATH_CAN_PUBLISH)
end end
def after_publish(path) def can_reopen?(administrateur, path)
path_availability(administrateur, path).in?(PATH_CAN_PUBLISH)
end
def after_publish(administrateur, path)
update!(published_at: Time.zone.now) update!(published_at: Time.zone.now)
claim_path_ownership!(path) claim_path_ownership!(path)
end end
def after_reopen(path) def after_reopen(administrateur, path)
update!(published_at: Time.zone.now, archived_at: nil) update!(published_at: Time.zone.now, archived_at: nil)
claim_path_ownership!(path) claim_path_ownership!(path)
@ -472,10 +478,8 @@ class Procedure < ApplicationRecord
end end
def ensure_path_exists def ensure_path_exists
if Flipflop.publish_draft? if self.path.nil?
if self.path.nil? self.path = SecureRandom.uuid
self.path = SecureRandom.uuid
end
end end
end end
end end

View file

@ -74,9 +74,7 @@ class ProcedurePresentation < ApplicationRecord
def sorted_ids(dossiers, gestionnaire) def sorted_ids(dossiers, gestionnaire)
dossiers.each { |dossier| assert_matching_procedure(dossier) } dossiers.each { |dossier| assert_matching_procedure(dossier) }
table = sort['table'] table, column, order = sort.values_at('table', 'column', 'order')
column = sanitized_column(sort)
order = sort['order']
case table case table
when 'notifications' when 'notifications'
@ -88,80 +86,85 @@ class ProcedurePresentation < ApplicationRecord
return (dossiers.order('dossiers.updated_at asc').ids - dossiers_id_with_notification) + return (dossiers.order('dossiers.updated_at asc').ids - dossiers_id_with_notification) +
dossiers_id_with_notification dossiers_id_with_notification
end end
when 'self'
return dossiers
.order("#{column} #{order}")
.pluck(:id)
when 'type_de_champ', 'type_de_champ_private' when 'type_de_champ', 'type_de_champ_private'
return dossiers return dossiers
.includes(table == 'type_de_champ' ? :champs : :champs_private) .includes(table == 'type_de_champ' ? :champs : :champs_private)
.where("champs.type_de_champ_id = #{sort['column'].to_i}") .where("champs.type_de_champ_id = #{column.to_i}")
.order("champs.value #{order}") .order("champs.value #{order}")
.pluck(:id) .pluck(:id)
when 'user', 'individual', 'etablissement' when 'self', 'user', 'individual', 'etablissement'
return dossiers return (table == 'self' ? dossiers : dossiers.includes(table))
.includes(table) .order("#{self.class.sanitized_column(table, column)} #{order}")
.order("#{column} #{order}")
.pluck(:id) .pluck(:id)
end end
end end
def filtered_ids(dossiers, statut) def filtered_ids(dossiers, statut)
dossiers.each { |dossier| assert_matching_procedure(dossier) } dossiers.each { |dossier| assert_matching_procedure(dossier) }
filters[statut].map do |filter| filters[statut].group_by { |filter| filter.values_at('table', 'column') } .map do |(table, column), filters|
table = filter['table'] values = filters.pluck('value')
column = sanitized_column(filter)
case table case table
when 'self' when 'self'
date = Time.zone.parse(filter['value']).beginning_of_day rescue nil dates = values
if date.present? .map { |v| Time.zone.parse(v).beginning_of_day rescue nil }
dossiers.where("#{column} BETWEEN ? AND ?", date, date + 1.day) .compact
else dossiers.filter_by_datetimes(column, dates)
[]
end
when 'type_de_champ', 'type_de_champ_private' when 'type_de_champ', 'type_de_champ_private'
relation = table == 'type_de_champ' ? :champs : :champs_private relation = table == 'type_de_champ' ? :champs : :champs_private
dossiers dossiers
.includes(relation) .includes(relation)
.where("champs.type_de_champ_id = ?", filter['column'].to_i) .where("champs.type_de_champ_id = ?", column.to_i)
.where("champs.value ILIKE ?", "%#{filter['value']}%") .filter_ilike(:champ, :value, values)
when 'etablissement' when 'etablissement'
if filter['column'] == 'entreprise_date_creation' if column == 'entreprise_date_creation'
date = filter['value'].to_date rescue nil dates = values
.map { |v| v.to_date rescue nil }
.compact
dossiers dossiers
.includes(table) .includes(table)
.where("#{column} = ?", date) .where(table.pluralize => { column => dates })
else else
dossiers dossiers
.includes(table) .includes(table)
.where("#{column} ILIKE ?", "%#{filter['value']}%") .filter_ilike(table, column, values)
end end
when 'user', 'individual' when 'user', 'individual'
dossiers dossiers
.includes(table) .includes(table)
.where("#{column} ILIKE ?", "%#{filter['value']}%") .filter_ilike(table, column, values)
end.pluck(:id) end.pluck(:id)
end.reduce(:&) end.reduce(:&)
end end
def eager_load_displayed_fields(dossiers)
relations_to_include = displayed_fields
.pluck('table')
.reject { |table| table == 'self' }
.map do |table|
case table
when 'type_de_champ'
:champs
when 'type_de_champ_private'
:champs_private
else
table
end
end
.uniq
dossiers.includes(relations_to_include)
end
private private
def check_allowed_displayed_fields def check_allowed_displayed_fields
displayed_fields.each do |field| displayed_fields.each do |field|
table = field['table'] check_allowed_field(:displayed_fields, field)
column = field['column']
if !valid_column?(table, column)
errors.add(:filters, "#{table}.#{column} nest pas une colonne permise")
end
end end
end end
def check_allowed_sort_column def check_allowed_sort_column
table = sort['table'] check_allowed_field(:sort, sort, EXTRA_SORT_COLUMNS)
column = sort['column']
if !valid_sort_column?(table, column)
errors.add(:sort, "#{table}.#{column} nest pas une colonne permise")
end
end end
def check_allowed_sort_order def check_allowed_sort_order
@ -174,15 +177,18 @@ class ProcedurePresentation < ApplicationRecord
def check_allowed_filter_columns def check_allowed_filter_columns
filters.each do |_, columns| filters.each do |_, columns|
columns.each do |column| columns.each do |column|
table = column['table'] check_allowed_field(:filters, column)
column = column['column']
if !valid_column?(table, column)
errors.add(:filters, "#{table}.#{column} nest pas une colonne permise")
end
end end
end end
end end
def check_allowed_field(kind, field, extra_columns = {})
table, column = field.values_at('table', 'column')
if !valid_column?(table, column, extra_columns)
errors.add(kind, "#{table}.#{column} nest pas une colonne permise")
end
end
def assert_matching_procedure(dossier) def assert_matching_procedure(dossier)
if dossier.procedure != procedure if dossier.procedure != procedure
raise "Procedure mismatch (expected #{procedure.id}, got #{dossier.procedure.id})" raise "Procedure mismatch (expected #{procedure.id}, got #{dossier.procedure.id})"
@ -210,32 +216,27 @@ class ProcedurePresentation < ApplicationRecord
} }
end end
def valid_column?(table, column) def valid_column?(table, column, extra_columns = {})
valid_columns_for_table(table).include?(column) valid_columns_for_table(table).include?(column) ||
extra_columns[table]&.include?(column)
end end
def valid_columns_for_table(table) def valid_columns_for_table(table)
@column_whitelist ||= fields @column_whitelist ||= fields
.group_by { |field| field['table'] } .group_by { |field| field['table'] }
.map { |table, fields| [table, Set.new(fields.map { |field| field['column'] })] } .map { |table, fields| [table, Set.new(fields.pluck('column'))] }
.to_h .to_h
@column_whitelist[table] || [] @column_whitelist[table] || []
end end
def sanitized_column(field) def self.sanitized_column(table, column)
table = field['table'] [(table == 'self' ? 'dossier' : table.to_s).pluralize, column]
table = ActiveRecord::Base.connection.quote_column_name((table == 'self' ? 'dossier' : table).pluralize) .map { |name| ActiveRecord::Base.connection.quote_column_name(name) }
column = ActiveRecord::Base.connection.quote_column_name(field['column']) .join('.')
table + '.' + column
end end
def dossier_field_service def dossier_field_service
@dossier_field_service ||= DossierFieldService.new @dossier_field_service ||= DossierFieldService.new
end end
def valid_sort_column?(table, column)
valid_column?(table, column) || EXTRA_SORT_COLUMNS[table]&.include?(column)
end
end end

View file

@ -1,7 +1,4 @@
class TypeDeChamp < ApplicationRecord class TypeDeChamp < ApplicationRecord
# TODO drop next line when `type` column has been dropped from `types_de_champ` table
self.inheritance_column = nil
enum type_champs: { enum type_champs: {
text: 'text', text: 'text',
textarea: 'textarea', textarea: 'textarea',

View file

@ -109,7 +109,7 @@ class AdministrateurUsageStatisticsService
end end
def nb_demarches_by_administrateur_id_and_state def nb_demarches_by_administrateur_id_and_state
@nb_demarches_by_administrateur_id_and_state ||= with_default(0, Procedure.group(:administrateur_id, :aasm_state).count) @nb_demarches_by_administrateur_id_and_state ||= with_default(0, Procedure.joins(:administrateurs).group('administrateurs.id', :aasm_state).count)
end end
def nb_services_by_administrateur_id def nb_services_by_administrateur_id
@ -128,9 +128,9 @@ class AdministrateurUsageStatisticsService
result = {} result = {}
Dossier Dossier
.joins(:procedure) .joins(procedure: [:administrateurs])
.group( .group(
:administrateur_id, 'administrateurs.id',
:procedure_id, :procedure_id,
<<~EOSQL <<~EOSQL
CASE CASE

View file

@ -3,10 +3,7 @@
%li{ class: @draft_class } %li{ class: @draft_class }
%a{ :href => "#{url_for :admin_procedures_draft}" } %a{ :href => "#{url_for :admin_procedures_draft}" }
%h5.text-primary %h5.text-primary
- if Flipflop.publish_draft? En test
En test
- else
Brouillons
%li{ class: @active_class } %li{ class: @active_class }
%a{ :href => "#{url_for :admin_procedures}" } %a{ :href => "#{url_for :admin_procedures}" }

View file

@ -6,7 +6,7 @@
= form_for @procedure, url: { controller: 'admin/procedures', action: :create }, multipart: true do |f| = form_for @procedure, url: { controller: 'admin/procedures', action: :create }, multipart: true do |f|
= render partial: 'informations', locals: { f: f } = render partial: 'informations', locals: { f: f }
.text-right .text-right
- if !Flipflop.publish_draft? || @availability.in?(Procedure::PATH_CAN_PUBLISH) - if @availability.in?(Procedure::PATH_CAN_PUBLISH)
= f.button 'Valider', class: 'btn btn-info', id: 'save-procedure' = f.button 'Valider', class: 'btn btn-info', id: 'save-procedure'
- else - else
= f.button 'Valider', class: 'btn btn-info', id: 'save-procedure', disabled: true = f.button 'Valider', class: 'btn btn-info', id: 'save-procedure', disabled: true

View file

@ -27,4 +27,4 @@
%td %td
= link_to('Cloner', admin_procedure_clone_path(procedure.id, from_new_from_existing: true), 'data-method' => :put, class: 'btn-sm btn-primary clone-btn') = link_to('Cloner', admin_procedure_clone_path(procedure.id, from_new_from_existing: true), 'data-method' => :put, class: 'btn-sm btn-primary clone-btn')
%td{ style: 'padding-left: 10px;' } %td{ style: 'padding-left: 10px;' }
= link_to('Contacter', "mailto:#{procedure.administrateur.email}") = link_to('Contacter', "mailto:#{procedure.administrateurs.pluck(:email) * ","}")

View file

@ -13,6 +13,9 @@
Nombre de dossier : #{procedure.dossiers.count} Nombre de dossier : #{procedure.dossiers.count}
%br %br
Admin : Admin :
= link_to "#{procedure.administrateur.email}", "mailto:#{procedure.administrateur.email}" %ul
- procedure.administrateurs.each do |administrateur|
%li
= link_to "#{administrateur.email}", "mailto:#{administrateur.email}"
- else - else
Il n'y a aucune démarche douteuse aujourd'hui Il n'y a aucune démarche douteuse aujourd'hui

View file

@ -11,6 +11,6 @@
\- \-
= contact_link 'Contact' = contact_link 'Contact'
\- \-
= link_to 'FAQ', FAQ_URL
\-
= link_to 'Documentation', DOC_URL = link_to 'Documentation', DOC_URL
\-
= link_to 'Aide', FAQ_URL

View file

@ -6,7 +6,8 @@
.flex.align-center .flex.align-center
= link_to root_path_for_profile(nav_bar_profile), class: "header-logo" do = link_to root_path_for_profile(nav_bar_profile), class: "header-logo" do
%img{ src: image_url("header/logo-ds.svg"), alt: "demarches-simplifiees.fr" } = image_tag "header/logo-ds.svg", alt: "demarches-simplifiees.fr", class: "header-logo-wide"
= image_tag "header/logo-ds-narrow.svg", alt: "demarches-simplifiees.fr", class: "header-logo-narrow"
- if nav_bar_profile == :gestionnaire && gestionnaire_signed_in? - if nav_bar_profile == :gestionnaire && gestionnaire_signed_in?
- current_url = request.path_info - current_url = request.path_info

View file

@ -11,10 +11,7 @@
#procedure-list #procedure-list
%a#draft-procedures{ :href => "#{url_for :admin_procedures_draft}" } %a#draft-procedures{ :href => "#{url_for :admin_procedures_draft}" }
.procedure-list-element{ class: @draft_class } .procedure-list-element{ class: @draft_class }
- if Flipflop.publish_draft? En test
En test
- else
Brouillons
.badge.progress-bar-default .badge.progress-bar-default
= current_administrateur.procedures.brouillons.count = current_administrateur.procedures.brouillons.count

View file

@ -21,7 +21,7 @@
.procedure-list-element{ class: ('active' if active == 'Description') } .procedure-list-element{ class: ('active' if active == 'Description') }
Description Description
%a#onglet-services{ href: current_administrateur.services.present? ? url_for(services_path(procedure_id: @procedure.id)) : url_for(new_service_path(procedure_id: @procedure.id)) } %a#onglet-services{ href: (@procedure.service.present? || current_administrateur.services.present?) ? url_for(services_path(procedure_id: @procedure.id)) : url_for(new_service_path(procedure_id: @procedure.id)) }
.procedure-list-element .procedure-list-element
Service Service
- if @procedure.missing_steps.include?(:service) - if @procedure.missing_steps.include?(:service)

View file

@ -58,8 +58,15 @@ as well as a link to its edit page.
) %> ) %>
</dt> </dt>
<dd class="attribute-data attribute-data--<%=attribute.html_class%>" <dd class="attribute-data attribute-data--<%=attribute.html_class%>">
><%= render_field attribute, page: page %></dd> <%= render_field attribute, page: page %>
<% if attribute.name == 'administrateurs' %>
<%= form_tag(add_administrateur_manager_procedure_path(procedure), style: 'margin-top: 1rem;') do %>
<%= email_field_tag(:email, '', placeholder: 'Email', autocapitalize: 'off', autocorrect: 'off', spellcheck: 'false', style: 'margin-bottom: 1rem;width:24rem;') %>
<button>Ajouter un administrateur</button>
<% end %>
<% end %>
</dd>
<% end %> <% end %>
</dl> </dl>
</section> </section>

View file

@ -9,7 +9,7 @@
= form_for @procedure, url: { controller: "new_administrateur/services", action: :add_to_procedure } , html: { class: 'form' } do |f| = form_for @procedure, url: { controller: "new_administrateur/services", action: :add_to_procedure } , html: { class: 'form' } do |f|
= f.label :service_id, "La démarche #{@procedure.libelle} est affectée au service" = f.label :service_id, "La démarche #{@procedure.libelle} est affectée au service"
= f.select :service_id, = f.select :service_id,
current_administrateur.services.map { |s| [ s.nom, s.id ] }, @services.map { |s| [ s.nom, s.id ] },
{ prompt: 'choisir un service', selected: @procedure.service&.id }, { prompt: 'choisir un service', selected: @procedure.service&.id },
required: true required: true

View file

@ -57,11 +57,16 @@
%br %br
= submit_tag "Ajouter le filtre", class: 'button' = submit_tag "Ajouter le filtre", class: 'button'
- @current_filters.each do |filter| - @current_filters.group_by { |filter| filter['table'] }.each_with_index do |(table, filters), i|
%span.filter - if i > 0
= link_to remove_filter_gestionnaire_procedure_path(@procedure, statut: @statut, table: filter['table'], column: filter['column']) do et
%img.close-icon{ src: image_url("close.svg") } - filters.each_with_index do |filter, i|
= "#{filter['label'].truncate(50)} : #{filter['value']}" - if i > 0
ou
%span.filter
= link_to remove_filter_gestionnaire_procedure_path(@procedure, statut: @statut, table: filter['table'], column: filter['column'], value: filter['value']) do
%img.close-icon{ src: image_url("close.svg") }
= "#{filter['label'].truncate(50)} : #{filter['value']}"
%table.table.dossiers-table.hoverable %table.table.dossiers-table.hoverable
%thead %thead
%tr %tr

View file

@ -4,8 +4,8 @@
= link_to "Mentions légales", MENTIONS_LEGALES_URL, :class => "footer-link", :target => "_blank", rel: "noopener noreferrer" = link_to "Mentions légales", MENTIONS_LEGALES_URL, :class => "footer-link", :target => "_blank", rel: "noopener noreferrer"
= link_to 'FAQ', FAQ_URL
= link_to 'Documentation', DOC_URL = link_to 'Documentation', DOC_URL
= contact_link "Contact technique", class: "footer-link", dossier_id: dossier&.id = contact_link "Contact technique", class: "footer-link", dossier_id: dossier&.id
= link_to 'Aide', FAQ_URL

View file

@ -43,7 +43,7 @@
%ul.numbers %ul.numbers
%li.number %li.number
.number-value .number-value
= number_with_delimiter(Procedure.includes(:administrateur).publiees_ou_archivees.map(&:administrateur).uniq.count, :locale => :fr) = number_with_delimiter(Procedure.includes(:administrateurs).publiees_ou_archivees.flat_map(&:administrateurs).uniq.count, :locale => :fr)
.number-label< .number-label<
administrations administrations
%br<> %br<>

View file

@ -84,15 +84,6 @@
.chart.cumulative-dossiers-chart.hidden .chart.cumulative-dossiers-chart.hidden
= area_chart @dossiers_cumulative = area_chart @dossiers_cumulative
.stat-card.stat-card-half.pull-left
%span.stat-card-title
Nombre d'administrations ayant dématérialisé N démarches
.chart-container
.chart
= pie_chart @procedures_count_per_administrateur,
colors: ["rgba(191, 220, 249, 1)", "rgba(113, 176, 239, 1)", "rgba(61, 149, 236, 1)"]
- if administration_signed_in? - if administration_signed_in?
.stat-card.stat-card-half.pull-left .stat-card.stat-card-half.pull-left
%span.stat-card-title Temps de traitement moyen d'un dossier %span.stat-card-title Temps de traitement moyen d'un dossier

View file

@ -1,85 +1,66 @@
{ {
"ignored_warnings": [ "ignored_warnings": [
{ {
"warning_type": "Cross-Site Scripting", "warning_type": "SQL Injection",
"warning_code": 2, "warning_code": 0,
"fingerprint": "0d61a1267d264f1e61cc2398a2683703ac60878129dc9515542f246a80ad575b", "fingerprint": "bd1df30f95135357b646e21a03d95498874faffa32e3804fc643e9b6b957ee14",
"check_name": "CrossSiteScripting", "check_name": "SQL",
"message": "Unescaped model attribute", "message": "Possible SQL injection",
"file": "app/views/champs/carto/show.js.erb", "file": "app/models/concerns/dossier_filtering_concern.rb",
"line": 5, "line": 18,
"link": "https://brakemanscanner.org/docs/warning_types/cross_site_scripting", "link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "geo_data((Champ.joins(:dossier).where(:dossiers => ({ :user_id => logged_user_ids })).find_by(:id => params.permit(:champ_id)) or CartoChamp.new))", "code": "where(\"#{values.count} OR #{\"(#{ProcedurePresentation.sanitized_column(table, column)} ILIKE ?)\"}\", *values.map do\n \"%#{value}%\"\n end)",
"render_path": [{"type":"controller","class":"Champs::CartoController","method":"show","line":48,"file":"app/controllers/champs/carto_controller.rb"}], "render_path": null,
"location": { "location": {
"type": "template", "type": "method",
"template": "champs/carto/show" "class": "DossierFilteringConcern",
"method": null
}, },
"user_input": "Champ.joins(:dossier).where(:dossiers => ({ :user_id => logged_user_ids }))", "user_input": "values.count",
"confidence": "Weak", "confidence": "Medium",
"note": "Not an injection because logged_user_ids have no user input" "note": "The table and column are escaped, which should make this safe"
}, },
{ {
"warning_type": "SQL Injection", "warning_type": "SQL Injection",
"warning_code": 0, "warning_code": 0,
"fingerprint": "1840f5340630814ea86311e850ebd91b966e6bccd0b6856133528e7745c0695a", "fingerprint": "e6f09095e3d381bcf6280d2f9b06c239946be3e440330136934f34611bc2b2d9",
"check_name": "SQL", "check_name": "SQL",
"message": "Possible SQL injection", "message": "Possible SQL injection",
"file": "app/models/procedure_presentation.rb", "file": "app/models/procedure_presentation.rb",
"line": 90, "line": 97,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/", "link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "dossiers.order(\"#{sanitized_column(sort)} #{sort[\"order\"]}\")", "code": "((\"self\" == \"self\") ? (dossiers) : (dossiers.includes(\"self\"))).order(\"#{self.class.sanitized_column(\"self\", column)} #{order}\")",
"render_path": null, "render_path": null,
"location": { "location": {
"type": "method", "type": "method",
"class": "ProcedurePresentation", "class": "ProcedurePresentation",
"method": "sorted_ids" "method": "sorted_ids"
}, },
"user_input": "sanitized_column(sort)", "user_input": "self.class.sanitized_column(\"self\", column)",
"confidence": "Weak", "confidence": "Weak",
"note": "Not an injection because of `sanitized_column`" "note": "`table`, `column` and `order` come from the model, which is validated to prevent injection attacks. Furthermore, `table` and `column` are escaped."
}, },
{ {
"warning_type": "SQL Injection", "warning_type": "SQL Injection",
"warning_code": 0, "warning_code": 0,
"fingerprint": "b2feda5e5ae668cdbf0653f134c40bcb9e45499c1b607450e43a0166c4098364", "fingerprint": "f85ed20c14a223884f624d744ff99070f6fc0697d918f54a08e7786ad70bb243",
"check_name": "SQL", "check_name": "SQL",
"message": "Possible SQL injection", "message": "Possible SQL injection",
"file": "app/models/procedure_presentation.rb", "file": "app/models/procedure_presentation.rb",
"line": 96, "line": 93,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/", "link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "dossiers.includes(((\"type_de_champ\" == \"type_de_champ\") ? (:champs) : (:champs_private))).where(\"champs.type_de_champ_id = #{sort[\"column\"].to_i}\").order(\"champs.value #{sort[\"order\"]}\")", "code": "dossiers.includes(((\"type_de_champ\" == \"type_de_champ\") ? (:champs) : (:champs_private))).where(\"champs.type_de_champ_id = #{column.to_i}\").order(\"champs.value #{order}\")",
"render_path": null, "render_path": null,
"location": { "location": {
"type": "method", "type": "method",
"class": "ProcedurePresentation", "class": "ProcedurePresentation",
"method": "sorted_ids" "method": "sorted_ids"
}, },
"user_input": "sort[\"order\"]", "user_input": "order",
"confidence": "Weak", "confidence": "Weak",
"note": "Not an injection because `sort[\"order\"]` has passed `check_allowed_sort_order`" "note": "`column` and `order` come from the model, which is validated to prevent injection attacks. Furthermore, the sql injection attack on `column` would need to survive the `to_i`"
},
{
"warning_type": "SQL Injection",
"warning_code": 0,
"fingerprint": "e0e5b55126891df8fe144835ea99367ffd7a92ae6d7227a923fe79f4a79f67f4",
"check_name": "SQL",
"message": "Possible SQL injection",
"file": "app/models/procedure_presentation.rb",
"line": 101,
"link": "https://brakemanscanner.org/docs/warning_types/sql_injection/",
"code": "dossiers.includes(\"user\").order(\"#{sanitized_column(sort)} #{sort[\"order\"]}\")",
"render_path": null,
"location": {
"type": "method",
"class": "ProcedurePresentation",
"method": "sorted_ids"
},
"user_input": "sanitized_column(sort)",
"confidence": "Weak",
"note": "Not an injection because of `sanitized_column`"
} }
], ],
"updated": "2018-10-16 11:28:34 +0300", "updated": "2019-03-04 11:59:49 +0100",
"brakeman_version": "4.3.1" "brakeman_version": "4.3.1"
} }

View file

@ -14,7 +14,6 @@ Flipflop.configure do
end end
feature :web_hook feature :web_hook
feature :publish_draft
feature :enable_email_login_token feature :enable_email_login_token
feature :new_champs_editor feature :new_champs_editor

View file

@ -10,6 +10,7 @@ Rails.application.routes.draw do
post 'whitelist', on: :member post 'whitelist', on: :member
post 'draft', on: :member post 'draft', on: :member
post 'hide', on: :member post 'hide', on: :member
post 'add_administrateur', on: :member
end end
resources :dossiers, only: [:index, :show] do resources :dossiers, only: [:index, :show] do
@ -312,7 +313,7 @@ Rails.application.routes.draw do
patch 'update_displayed_fields' patch 'update_displayed_fields'
get 'update_sort/:table/:column' => 'procedures#update_sort', as: 'update_sort' get 'update_sort/:table/:column' => 'procedures#update_sort', as: 'update_sort'
post 'add_filter' post 'add_filter'
get 'remove_filter/:statut/:table/:column' => 'procedures#remove_filter', as: 'remove_filter' get 'remove_filter/:statut/:table/:column/:value' => 'procedures#remove_filter', as: 'remove_filter'
get 'download_dossiers' get 'download_dossiers'
resources :dossiers, only: [:show], param: :dossier_id do resources :dossiers, only: [:show], param: :dossier_id do

View file

@ -0,0 +1,5 @@
class RemoveTypeFromTypesDeChamp < ActiveRecord::Migration[5.2]
def change
remove_column :types_de_champ, :type, :string
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_02_26_105641) do ActiveRecord::Schema.define(version: 2019_03_11_140926) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -536,7 +536,6 @@ ActiveRecord::Schema.define(version: 2019_02_26_105641) do
t.integer "procedure_id" t.integer "procedure_id"
t.text "description" t.text "description"
t.boolean "mandatory", default: false t.boolean "mandatory", default: false
t.string "type"
t.boolean "private", default: false, null: false t.boolean "private", default: false, null: false
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
@ -559,6 +558,7 @@ ActiveRecord::Schema.define(version: 2019_02_26_105641) do
t.integer "order_place" t.integer "order_place"
t.string "lien_demarche" t.string "lien_demarche"
t.boolean "mandatory", default: false t.boolean "mandatory", default: false
t.index ["procedure_id"], name: "index_types_de_piece_justificative_on_procedure_id"
end end
create_table "users", id: :serial, force: :cascade do |t| create_table "users", id: :serial, force: :cascade do |t|

View file

@ -1,106 +0,0 @@
require Rails.root.join("lib", "tasks", "task_helper")
namespace :'2018_07_31_nutriscore' do
task migrate_dossiers: :environment do
source_procedure_id = ENV['SOURCE_PROCEDURE_ID'] || 4861
destination_procedure_id = ENV['DESTINATION_PROCEDURE_ID'] || 7009
source_procedure = Procedure.find(source_procedure_id)
destination_procedure = Procedure.find(destination_procedure_id)
mapping = Class.new(Tasks::DossierProcedureMigrator::ChampMapping) do
def setup_mapping
siret_order_place = 2
fonction_order_place = 9
zone_geographique_header_order_place = 18
pays_commercialisation_order_place = 19
header_engagement_order_place = 20
champ_opts = { header_engagement_order_place => { source_overrides: { 'libelle' => 'PARTIE 3 : ENGAGEMENT DE LEXPLOITANT' }, destination_overrides: { 'libelle' => 'PARTIE 4 : ENGAGEMENT DE LEXPLOITANT' } } }
pays_drop_down_values = "FRANCE\r\nACORES, MADERE\r\nAFGHANISTAN\r\nAFRIQUE DU SUD\r\nALASKA\r\nALBANIE\r\nALGERIE\r\nALLEMAGNE\r\nANDORRE\r\nANGOLA\r\nANGUILLA\r\nANTIGUA-ET-BARBUDA\r\nANTILLES NEERLANDAISES\r\nARABIE SAOUDITE\r\nARGENTINE\r\nARMENIE\r\nARUBA\r\nAUSTRALIE\r\nAUTRICHE\r\nAZERBAIDJAN\r\nBAHAMAS\r\nBAHREIN\r\nBANGLADESH\r\nBARBADE\r\nBELGIQUE\r\nBELIZE\r\nBENIN\r\nBERMUDES\r\nBHOUTAN\r\nBIELORUSSIE\r\nBIRMANIE\r\nBOLIVIE\r\nBONAIRE, SAINT EUSTACHE ET SABA\r\nBOSNIE-HERZEGOVINE\r\nBOTSWANA\r\nBOUVET (ILE)\r\nBRESIL\r\nBRUNEI\r\nBULGARIE\r\nBURKINA\r\nBURUNDI\r\nCAIMANES (ILES)\r\nCAMBODGE\r\nCAMEROUN\r\nCAMEROUN ET TOGO\r\nCANADA\r\nCANARIES (ILES)\r\nCAP-VERT\r\nCENTRAFRICAINE (REPUBLIQUE)\r\nCHILI\r\nCHINE\r\nCHRISTMAS (ILE)\r\nCHYPRE\r\nCLIPPERTON (ILE)\r\nCOCOS ou KEELING (ILES)\r\nCOLOMBIE\r\nCOMORES\r\nCONGO\r\nCONGO (REPUBLIQUE DEMOCRATIQUE)\r\nCOOK (ILES)\r\nCOREE\r\nCOREE (REPUBLIQUE DE)\r\nCOREE (REPUBLIQUE POPULAIRE DEMOCRATIQUE DE)\r\nCOSTA RICA\r\nCOTE D'IVOIRE\r\nCROATIE\r\nCUBA\r\nCURAÇAO\r\nDANEMARK\r\nDJIBOUTI\r\nDOMINICAINE (REPUBLIQUE)\r\nDOMINIQUE\r\nEGYPTE\r\nEL SALVADOR\r\nEMIRATS ARABES UNIS\r\nEQUATEUR\r\nERYTHREE\r\nESPAGNE\r\nESTONIE\r\nETATS MALAIS NON FEDERES\r\nETATS-UNIS\r\nETHIOPIE\r\nFEROE (ILES)\r\nFIDJI\r\nFINLANDE\r\nGABON\r\nGAMBIE\r\nGEORGIE\r\nGEORGIE DU SUD ET LES ILES SANDWICH DU SUD\r\nGHANA\r\nGIBRALTAR\r\nGOA\r\nGRECE\r\nGRENADE\r\nGROENLAND\r\nGUADELOUPE\r\nGUAM\r\nGUATEMALA\r\nGUERNESEY\r\nGUINEE\r\nGUINEE EQUATORIALE\r\nGUINEE-BISSAU\r\nGUYANA\r\nGUYANE\r\nHAITI\r\nHAWAII (ILES)\r\nHEARD ET MACDONALD (ILES)\r\nHONDURAS\r\nHONG-KONG\r\nHONGRIE\r\nILES PORTUGAISES DE L'OCEAN INDIEN\r\nINDE\r\nINDONESIE\r\nIRAN\r\nIRAQ\r\nIRLANDE, ou EIRE\r\nISLANDE\r\nISRAEL\r\nITALIE\r\nJAMAIQUE\r\nJAPON\r\nJERSEY\r\nJORDANIE\r\nKAMTCHATKA\r\nKAZAKHSTAN\r\nKENYA\r\nKIRGHIZISTAN\r\nKIRIBATI\r\nKOSOVO\r\nKOWEIT\r\nLA REUNION\r\nLABRADOR\r\nLAOS\r\nLESOTHO\r\nLETTONIE\r\nLIBAN\r\nLIBERIA\r\nLIBYE\r\nLIECHTENSTEIN\r\nLITUANIE\r\nLUXEMBOURG\r\nMACAO\r\nMACEDOINE DU NORD (REPUBLIQUE DE)\r\nMADAGASCAR\r\nMALAISIE\r\nMALAWI\r\nMALDIVES\r\nMALI\r\nMALOUINES, OU FALKLAND (ILES)\r\nMALTE\r\nMAN (ILE)\r\nMANDCHOURIE\r\nMARIANNES DU NORD (ILES)\r\nMAROC\r\nMARSHALL (ILES)\r\nMARTINIQUE\r\nMAURICE\r\nMAURITANIE\r\nMAYOTTE\r\nMEXIQUE\r\nMICRONESIE (ETATS FEDERES DE)\r\nMOLDAVIE\r\nMONACO\r\nMONGOLIE\r\nMONTENEGRO\r\nMONTSERRAT\r\nMOZAMBIQUE\r\nNAMIBIE\r\nNAURU\r\nNEPAL\r\nNICARAGUA\r\nNIGER\r\nNIGERIA\r\nNIUE\r\nNORFOLK (ILE)\r\nNORVEGE\r\nNOUVELLE-CALEDONIE\r\nNOUVELLE-ZELANDE\r\nOCEAN INDIEN (TERRITOIRE BRITANNIQUE DE L')\r\nOMAN\r\nOUGANDA\r\nOUZBEKISTAN\r\nPAKISTAN\r\nPALAOS (ILES)\r\nPALESTINE (Etat de)\r\nPANAMA\r\nPAPOUASIE-NOUVELLE-GUINEE\r\nPARAGUAY\r\nPAYS-BAS\r\nPEROU\r\nPHILIPPINES\r\nPITCAIRN (ILE)\r\nPOLOGNE\r\nPOLYNESIE FRANCAISE\r\nPORTO RICO\r\nPORTUGAL\r\nPOSSESSIONS BRITANNIQUES AU PROCHE-ORIENT\r\nPRESIDES\r\nPROVINCES ESPAGNOLES D'AFRIQUE\r\nQATAR\r\nREPUBLIQUE DEMOCRATIQUE ALLEMANDE\r\nREPUBLIQUE FEDERALE D'ALLEMAGNE\r\nROUMANIE\r\nROYAUME-UNI\r\nRUSSIE\r\nRWANDA\r\nSAHARA OCCIDENTAL\r\nSAINT-BARTHELEMY\r\nSAINT-CHRISTOPHE-ET-NIEVES\r\nSAINT-MARIN\r\nSAINT-MARTIN\r\nSAINT-MARTIN (PARTIE NEERLANDAISE)\r\nSAINT-PIERRE-ET-MIQUELON\r\nSAINT-VINCENT-ET-LES GRENADINES\r\nSAINTE HELENE, ASCENSION ET TRISTAN DA CUNHA\r\nSAINTE-LUCIE\r\nSALOMON (ILES)\r\nSAMOA AMERICAINES\r\nSAMOA OCCIDENTALES\r\nSAO TOME-ET-PRINCIPE\r\nSENEGAL\r\nSERBIE\r\nSEYCHELLES\r\nSIBERIE\r\nSIERRA LEONE\r\nSINGAPOUR\r\nSLOVAQUIE\r\nSLOVENIE\r\nSOMALIE\r\nSOUDAN\r\nSOUDAN ANGLO-EGYPTIEN, KENYA, OUGANDA\r\nSOUDAN DU SUD\r\nSRI LANKA\r\nSUEDE\r\nSUISSE\r\nSURINAME\r\nSVALBARD et ILE JAN MAYEN\r\nSWAZILAND\r\nSYRIE\r\nTADJIKISTAN\r\nTAIWAN\r\nTANGER\r\nTANZANIE\r\nTCHAD\r\nTCHECOSLOVAQUIE\r\nTCHEQUE (REPUBLIQUE)\r\nTERR. DES ETATS-UNIS D'AMERIQUE EN AMERIQUE\r\nTERR. DES ETATS-UNIS D'AMERIQUE EN OCEANIE\r\nTERR. DU ROYAUME-UNI DANS L'ATLANTIQUE SUD\r\nTERRE-NEUVE\r\nTERRES AUSTRALES FRANCAISES\r\nTERRITOIRES DU ROYAUME-UNI AUX ANTILLES\r\nTHAILANDE\r\nTIMOR ORIENTAL\r\nTOGO\r\nTOKELAU\r\nTONGA\r\nTRINITE-ET-TOBAGO\r\nTUNISIE\r\nTURKESTAN RUSSE\r\nTURKMENISTAN\r\nTURKS ET CAIQUES (ILES)\r\nTURQUIE\r\nTURQUIE D'EUROPE\r\nTUVALU\r\nUKRAINE\r\nURUGUAY\r\nVANUATU\r\nVATICAN, ou SAINT-SIEGE\r\nVENEZUELA\r\nVIERGES BRITANNIQUES (ILES)\r\nVIERGES DES ETATS-UNIS (ILES)\r\nVIET NAM\r\nVIET NAM DU NORD\r\nVIET NAM DU SUD\r\nWALLIS-ET-FUTUNA\r\nYEMEN\r\nYEMEN (REPUBLIQUE ARABE DU)\r\nYEMEN DEMOCRATIQUE\r\nZAMBIE\r\nZANZIBAR\r\nZIMBABWE"
((0..(zone_geographique_header_order_place - 1)).to_a - [siret_order_place, fonction_order_place]).each do |i|
map_source_to_destination_champ(i, i, **(champ_opts[i] || {}))
end
((pays_commercialisation_order_place + 1)..25).each do |i|
map_source_to_destination_champ(i - 2, i, **(champ_opts[i] || {}))
end
discard_source_champ(
TypeDeChamp.new(
type_champ: 'text',
order_place: siret_order_place,
libelle: 'Numéro SIRET'
)
)
discard_source_champ(
TypeDeChamp.new(
type_champ: 'text',
order_place: fonction_order_place,
libelle: 'Fonction'
)
)
compute_destination_champ(
TypeDeChamp.new(
type_champ: 'text',
order_place: fonction_order_place,
libelle: 'Fonction',
mandatory: true
)
) do |d, target_tdc|
c = d.champs.joins(:type_de_champ).find_by(types_de_champ: { order_place: fonction_order_place })
target_tdc.champ.create(
value: c&.value || 'Non renseigné',
dossier: d
)
end
compute_destination_champ(
TypeDeChamp.new(
type_champ: 'siret',
order_place: siret_order_place,
libelle: 'Numéro SIRET'
)
) do |d, target_tdc|
if d.etablissement.present?
d.etablissement.signature = d.etablissement.sign
end
target_tdc.champ.create(
value: d.etablissement&.siret,
etablissement: d.etablissement,
dossier: d
)
end
compute_destination_champ(
TypeDeChamp.new(
type_champ: 'header_section',
order_place: 18,
libelle: 'PARTIE 3 : ZONE GEOGRAPHIQUE'
)
) do |d, target_tdc|
target_tdc.champ.create(dossier: d)
end
compute_destination_champ(
TypeDeChamp.new(
type_champ: 'multiple_drop_down_list',
order_place: 19,
libelle: 'Pays de commercialisation',
drop_down_list: DropDownList.new(value: pays_drop_down_values)
)
) do |d, target_tdc|
target_tdc.champ.create(dossier: d, value: JSON.unparse(['FRANCE']))
end
end
end
Tasks::DossierProcedureMigrator.new(source_procedure, destination_procedure, mapping).migrate_procedure
AutoReceiveDossiersForProcedureJob.set(cron: "* * * * *").perform_later(destination_procedure_id, 'accepte')
end
end

View file

@ -1,61 +0,0 @@
class FixAutomaticDossierLogs_2019_01_16
def find_handlers
# rubocop:disable Security/YAMLLoad
Delayed::Job.where(queue: 'cron')
.map { |job| YAML.load(job.handler) }
.select { |handler| handler.job_data['job_class'] == 'AutoReceiveDossiersForProcedureJob' }
# rubocop:enable Security/YAMLLoad
end
def run
handlers = find_handlers
handlers
.map { |handler| handler.job_data['arguments'] }
.each do |(procedure_id, state)|
procedure = Procedure
.includes(:administrateur, dossiers: [:dossier_operation_logs, :follows])
.find(procedure_id)
rake_puts "working on procedure #{procedure_id}, #{procedure.libelle} whose admin is #{procedure.administrateur.email}"
case state
when Dossier.states.fetch(:en_instruction)
dossiers = procedure.dossiers.state_en_instruction
operation = 'passer_en_instruction'
when Dossier.states.fetch(:accepte)
dossiers = procedure.dossiers.accepte
operation = 'accepter'
end
dossier_operation_logs = DossierOperationLog
.where(dossier: dossiers, operation: operation)
rake_puts "affecting #{dossier_operation_logs.count} dossier_operation_logs"
dossier_operation_logs
.update_all(gestionnaire_id: nil, automatic_operation: true)
# if the dossier is only followed by the procedure administrateur
# unfollow
if state == Dossier.states.fetch(:en_instruction)
dossier_to_unfollows = dossiers
.select { |d| d.follows.count == 1 && d.follows.first.gestionnaire.email == procedure.administrateur.email }
rake_puts "affecting #{dossier_to_unfollows.count} dossiers"
dossier_to_unfollows
.each { |d| d.follows.destroy_all }
end
rake_puts ""
end
end
end
namespace :'2019_01_16_fix_automatic_dossier_logs' do
task run: :environment do
FixAutomaticDossierLogs_2019_01_16.new.run
end
end

View file

@ -1,37 +0,0 @@
require Rails.root.join("lib", "tasks", "task_helper")
namespace :after_party do
desc 'Deployment task: clone_service_for_transferred_procedures'
task clone_service_for_transferred_procedures: :environment do
rake_puts "Running deploy task 'clone_service_for_transferred_procedures'"
procedures = Procedure.includes(:service).where.not(service_id: nil)
procedures_to_fix_in_array = procedures.select do |p|
p.administrateur_id != p.service.administrateur_id
end
procedures_to_fix = Procedure.where(id: procedures_to_fix_in_array.map(&:id))
service_and_admin_list = procedures_to_fix.group(:service_id, :administrateur_id).count.keys
progress = ProgressReport.new(service_and_admin_list.count)
service_and_admin_list.each do |service_id, administrateur_id|
cloned_service = Service.find(service_id).clone_and_assign_to_administrateur(Administrateur.find(administrateur_id))
if cloned_service.save
rake_puts "Fixing Service #{service_id} for Administrateur #{administrateur_id}"
procedures_to_fix
.where(service_id: service_id, administrateur_id: administrateur_id)
.update_all(service_id: cloned_service.id)
else
rake_puts "Cannot fix Service #{service_id} for Administrateur #{administrateur_id}, it should be fixed manually. Errors : #{cloned_service.errors.full_messages}"
end
progress.inc
end
progress.finish
# Update task as completed. If you remove the line below, the task will
# run with every deploy (or every time you call after_party:run).
AfterParty::TaskRecord.create version: '20181219164523'
end # task :clone_service_for_transferred_procedures
end # namespace :after_party

View file

@ -0,0 +1,17 @@
namespace :after_party do
desc 'Deployment task: create_default_path_for_brouillons'
task create_default_path_for_brouillons: :environment do
puts "Running deploy task 'create_default_path_for_brouillons'"
# Put your task implementation HERE.
Procedure.brouillons.where(path: nil).each do |p|
p.path = SecureRandom.uuid
p.save
end
# Update task as completed. If you remove the line below, the task will
# run with every deploy (or every time you call after_party:run).
AfterParty::TaskRecord.create version: '20190306172842'
end # task :create_default_path_for_brouillons
end # namespace :after_party

View file

@ -260,8 +260,8 @@ module Tasks
end end
def check_same_administrateur def check_same_administrateur
if @source_procedure.administrateur != @destination_procedure.administrateur if @source_procedure.administrateur_ids.sort != @destination_procedure.administrateur_ids.sort
raise "Mismatching administrateurs #{@source_procedure.administrateur&.email}#{@destination_procedure.administrateur&.email}" raise "Mismatching administrateurs #{@source_procedure.administrateurs.pluck(:email)}#{@destination_procedure.administrateurs.pluck(:email)}"
end end
end end

View file

@ -1,82 +1,6 @@
require Rails.root.join("lib", "tasks", "task_helper") require Rails.root.join("lib", "tasks", "task_helper")
namespace :support do namespace :support do
desc <<~EOD
Give procedure #PROCEDURE_ID a new owner.
The new owner can be specified with NEW_OWNER_ID or NEW_OWNER_EMAIL.
EOD
task transfer_procedure_ownership: :environment do
new_owner_id = ENV['NEW_OWNER_ID']
new_owner_email = ENV['NEW_OWNER_EMAIL']
new_owner = nil
if new_owner_id.present?
rake_puts("Looking for new owner by id\n")
new_owner = Administrateur.find(new_owner_id)
elsif new_owner_email.present?
rake_puts("Looking for new owner by email\n")
new_owner = Administrateur.find_by('LOWER(email) = LOWER(?)', new_owner_email)
end
if new_owner.blank?
fail "Must specify a new owner"
end
procedure_id = ENV['PROCEDURE_ID']
procedure = Procedure.find(procedure_id)
rake_puts("Changing owner of procedure ##{procedure_id} from ##{procedure.administrateur_id} to ##{new_owner.id}")
procedure.administrateurs.delete(procedure.administrateur)
procedure.administrateurs << new_owner
procedure.update(administrateur: new_owner)
end
desc <<~EOD
Give all procedures owned by OLD_OWNER_ID or OLD_OWNER_EMAIL a new owner.
The new owner can be specified with NEW_OWNER_ID or NEW_OWNER_EMAIL.
EOD
task transfer_all_procedures_ownership: :environment do
old_owner_id = ENV['OLD_OWNER_ID']
old_owner_email = ENV['OLD_OWNER_EMAIL']
new_owner_id = ENV['NEW_OWNER_ID']
new_owner_email = ENV['NEW_OWNER_EMAIL']
old_owner = nil
if old_owner_id.present?
rake_puts("Looking for old owner by id\n")
old_owner = Administrateur.find(old_owner_id)
elsif old_owner_email.present?
rake_puts("Looking for old owner by email\n")
old_owner = Administrateur.find_by('LOWER(email) = LOWER(?)', old_owner_email)
end
if old_owner.blank?
fail "Must specify an old owner"
end
procedures = old_owner.procedures
new_owner = nil
if new_owner_id.present?
rake_puts("Looking for new owner by id\n")
new_owner = Administrateur.find(new_owner_id)
elsif new_owner_email.present?
rake_puts("Looking for new owner by email\n")
new_owner = Administrateur.find_by('LOWER(email) = LOWER(?)', new_owner_email)
end
if new_owner.blank?
fail "Must specify a new owner"
end
procedures.each do |procedure|
procedure.administrateurs.delete(procedure.administrateur)
procedure.administrateurs << new_owner
end
procedures.update_all(administrateur_id: new_owner.id)
end
desc <<~EOD desc <<~EOD
Delete the user account for a given USER_EMAIL. Delete the user account for a given USER_EMAIL.
Only works if the user has no dossier where the instruction has started. Only works if the user has no dossier where the instruction has started.
@ -136,28 +60,4 @@ namespace :support do
user.update(email: new_email) user.update(email: new_email)
end end
desc <<~EOD
Activate feature publish draft
EOD
task activate_publish_draft: :environment do
start_with = ENV['START_WITH']
administrateurs = Administrateur.where("email like ?", "#{start_with}%")
rake_puts("Activating publish draft for #{administrateurs.count} administrateurs...")
administrateurs.each do |a|
rake_puts("Activating publish draft for #{a.email}")
a.features["publish_draft"] = true
a.save
a.procedures.brouillon.each do |p|
if p.path.nil?
p.path = SecureRandom.uuid
p.save
end
end
end
end
end end

View file

@ -5,7 +5,7 @@ describe Admin::MailTemplatesController, type: :controller do
let(:initiated_mail) { Mails::InitiatedMail.default_for_procedure(procedure) } let(:initiated_mail) { Mails::InitiatedMail.default_for_procedure(procedure) }
before do before do
sign_in procedure.administrateur sign_in procedure.administrateurs.first
end end
describe 'GET index' do describe 'GET index' do

View file

@ -40,7 +40,8 @@ describe Admin::ProceduresController, type: :controller do
describe 'GET #index with sorting and pagination' do describe 'GET #index with sorting and pagination' do
before do before do
admin.procedures << create(:procedure, administrateur: admin) create(:procedure, administrateur: admin)
admin.reload
end end
subject { subject {
@ -196,7 +197,6 @@ describe Admin::ProceduresController, type: :controller do
it { expect(subject.description).to eq(description) } it { expect(subject.description).to eq(description) }
it { expect(subject.organisation).to eq(organisation) } it { expect(subject.organisation).to eq(organisation) }
it { expect(subject.direction).to eq(direction) } it { expect(subject.direction).to eq(direction) }
it { expect(subject.administrateur_id).to eq(admin.id) }
it { expect(subject.administrateurs).to eq([admin]) } it { expect(subject.administrateurs).to eq([admin]) }
it { expect(subject.duree_conservation_dossiers_dans_ds).to eq(duree_conservation_dossiers_dans_ds) } it { expect(subject.duree_conservation_dossiers_dans_ds).to eq(duree_conservation_dossiers_dans_ds) }
it { expect(subject.duree_conservation_dossiers_hors_ds).to eq(duree_conservation_dossiers_hors_ds) } it { expect(subject.duree_conservation_dossiers_hors_ds).to eq(duree_conservation_dossiers_hors_ds) }
@ -303,10 +303,6 @@ describe Admin::ProceduresController, type: :controller do
let!(:dossiers_count) { procedure.dossiers.count } let!(:dossiers_count) { procedure.dossiers.count }
describe 'dossiers are dropped' do describe 'dossiers are dropped' do
before do
Flipflop::FeatureSet.current.test!.switch!(:publish_draft, true)
end
subject { update_procedure } subject { update_procedure }
it { it {
@ -378,7 +374,7 @@ describe Admin::ProceduresController, type: :controller do
it 'does not publish the given procedure' do it 'does not publish the given procedure' do
expect(procedure.publiee?).to be_falsey expect(procedure.publiee?).to be_falsey
expect(procedure.path).to be_nil expect(procedure.path).not_to match(path)
expect(response.status).to eq 200 expect(response.status).to eq 200
end end
@ -394,7 +390,7 @@ describe Admin::ProceduresController, type: :controller do
it 'does not publish the given procedure' do it 'does not publish the given procedure' do
expect(procedure.publiee?).to be_falsey expect(procedure.publiee?).to be_falsey
expect(procedure.path).to be_nil expect(procedure.path).not_to match(path)
expect(response).to redirect_to :admin_procedures expect(response).to redirect_to :admin_procedures
expect(flash[:alert]).to have_content 'Lien de la démarche invalide' expect(flash[:alert]).to have_content 'Lien de la démarche invalide'
end end
@ -640,7 +636,7 @@ describe Admin::ProceduresController, type: :controller do
subject subject
end end
it { expect(Procedure.last.administrateur).to eq new_admin } it { expect(Procedure.last.administrateurs).to eq [new_admin] }
end end
end end
end end

View file

@ -1,10 +1,11 @@
require 'spec_helper' require 'spec_helper'
describe APIController, type: :controller do describe APIController, type: :controller do
describe 'valid_token_for_administrateur?' do describe 'valid_token_for_procedure?' do
let!(:admin) { create(:administrateur) } let(:procedure) { create(:procedure) }
let(:admin) { procedure.administrateurs.first }
subject { controller.send(:'valid_token_for_administrateur?', admin) } subject { !!controller.send(:find_administrateur_for_token, procedure) }
context 'when the admin has not any token' do context 'when the admin has not any token' do
context 'and the token is not given' do context 'and the token is not given' do

View file

@ -151,9 +151,7 @@ describe ApplicationController, type: :controller do
let(:current_gestionnaire) { create(:gestionnaire) } let(:current_gestionnaire) { create(:gestionnaire) }
before do before do
allow(current_gestionnaire).to receive(:feature_enabled?).and_return(feature_enabled)
allow(@controller).to receive(:current_gestionnaire).and_return(current_gestionnaire) allow(@controller).to receive(:current_gestionnaire).and_return(current_gestionnaire)
allow(@controller).to receive(:redirect_to) allow(@controller).to receive(:redirect_to)
allow(@controller).to receive(:trusted_device?).and_return(trusted_device) allow(@controller).to receive(:trusted_device?).and_return(trusted_device)
allow(@controller).to receive(:gestionnaire_signed_in?).and_return(gestionnaire_signed_in) allow(@controller).to receive(:gestionnaire_signed_in?).and_return(gestionnaire_signed_in)
@ -171,7 +169,9 @@ describe ApplicationController, type: :controller do
let(:gestionnaire_signed_in) { true } let(:gestionnaire_signed_in) { true }
context 'when the feature is activated' do context 'when the feature is activated' do
let(:feature_enabled) { true } before do
Flipflop::FeatureSet.current.test!.switch!(:enable_email_login_token, true)
end
context 'when the device is trusted' do context 'when the device is trusted' do
let(:trusted_device) { true } let(:trusted_device) { true }
@ -183,7 +183,9 @@ describe ApplicationController, type: :controller do
end end
context 'when the feature is activated' do context 'when the feature is activated' do
let(:feature_enabled) { true } before do
Flipflop::FeatureSet.current.test!.switch!(:enable_email_login_token, true)
end
context 'when the device is not trusted' do context 'when the device is not trusted' do
let(:trusted_device) { false } let(:trusted_device) { false }

View file

@ -815,7 +815,7 @@ describe NewUser::DossiersController, type: :controller do
let(:dossier) { create(:dossier, :en_construction, user: user, autorisation_donnees: true) } let(:dossier) { create(:dossier, :en_construction, user: user, autorisation_donnees: true) }
it "notifies the user and the admin of the deletion" do it "notifies the user and the admin of the deletion" do
expect(DossierMailer).to receive(:notify_deletion_to_administration).with(kind_of(DeletedDossier), dossier.procedure.administrateur.email).and_return(double(deliver_later: nil)) expect(DossierMailer).to receive(:notify_deletion_to_administration).with(kind_of(DeletedDossier), dossier.procedure.administrateurs.first.email).and_return(double(deliver_later: nil))
expect(DossierMailer).to receive(:notify_deletion_to_user).with(kind_of(DeletedDossier), dossier.user.email).and_return(double(deliver_later: nil)) expect(DossierMailer).to receive(:notify_deletion_to_user).with(kind_of(DeletedDossier), dossier.user.email).and_return(double(deliver_later: nil))
subject subject
end end

View file

@ -93,42 +93,6 @@ describe StatsController, type: :controller do
end end
end end
describe "#procedures_count_per_administrateur" do
let!(:administrateur_1) { create(:administrateur) }
let!(:administrateur_2) { create(:administrateur) }
let!(:administrateur_3) { create(:administrateur) }
let!(:administrateur_4) { create(:administrateur) }
let!(:administrateur_5) { create(:administrateur) }
before do
3.times do
create(:procedure, published_at: Time.zone.now, administrateur: administrateur_1)
end
2.times do
create(:procedure, published_at: Time.zone.now, administrateur: administrateur_2)
end
8.times do
create(:procedure, published_at: Time.zone.now, administrateur: administrateur_3)
end
create(:procedure, published_at: Time.zone.now, administrateur: administrateur_4)
end
let(:association) { Procedure.all }
subject { StatsController.new.send(:procedures_count_per_administrateur, association) }
it do
is_expected.to eq({
'Une démarche' => 1,
'Entre deux et cinq démarches' => 2,
'Plus de cinq démarches' => 1
})
end
end
describe "#dossier_instruction_mean_time" do describe "#dossier_instruction_mean_time" do
# Month-2: mean 3 days # Month-2: mean 3 days
# procedure_1: mean 2 days # procedure_1: mean 2 days

View file

@ -10,12 +10,13 @@ describe Users::SessionsController, type: :controller do
describe '#create' do describe '#create' do
context "when the user is also a gestionnaire and an administrateur" do context "when the user is also a gestionnaire and an administrateur" do
let!(:administrateur) { create(:administrateur, :with_admin_trusted_device, email: email, password: password) } let!(:administrateur) { create(:administrateur, email: email, password: password) }
let(:gestionnaire) { administrateur.gestionnaire } let(:gestionnaire) { administrateur.gestionnaire }
let(:trusted_device) { true } let(:trusted_device) { true }
let(:send_password) { password } let(:send_password) { password }
before do before do
Flipflop::FeatureSet.current.test!.switch!(:enable_email_login_token, true)
allow(controller).to receive(:trusted_device?).and_return(trusted_device) allow(controller).to receive(:trusted_device?).and_return(trusted_device)
allow(GestionnaireMailer).to receive(:send_login_token).and_return(double(deliver_later: true)) allow(GestionnaireMailer).to receive(:send_login_token).and_return(double(deliver_later: true))
end end

View file

@ -9,12 +9,6 @@ FactoryBot.define do
end end
end end
trait :with_admin_trusted_device do
after(:create) do |admin|
admin.gestionnaire.update(features: { "enable_email_login_token" => true })
end
end
trait :with_api_token do trait :with_api_token do
after(:create) do |admin| after(:create) do |admin|
admin.renew_api_token admin.renew_api_token
@ -23,7 +17,8 @@ FactoryBot.define do
trait :with_procedure do trait :with_procedure do
after(:create) do |admin| after(:create) do |admin|
admin.procedures << create(:simple_procedure, administrateur: admin) create(:simple_procedure, administrateur: admin)
admin.reload
end end
end end
end end

View file

@ -4,10 +4,4 @@ FactoryBot.define do
email { generate(:gestionnaire_email) } email { generate(:gestionnaire_email) }
password { 'password' } password { 'password' }
end end
trait :with_trusted_device do
after(:create) do |gestionnaire|
gestionnaire.update(features: { "enable_email_login_token" => true })
end
end
end end

View file

@ -7,13 +7,20 @@ FactoryBot.define do
direction { "direction DINSIC" } direction { "direction DINSIC" }
cadre_juridique { "un cadre juridique important" } cadre_juridique { "un cadre juridique important" }
published_at { nil } published_at { nil }
administrateur { create(:administrateur) }
duree_conservation_dossiers_dans_ds { 3 } duree_conservation_dossiers_dans_ds { 3 }
duree_conservation_dossiers_hors_ds { 6 } duree_conservation_dossiers_hors_ds { 6 }
ask_birthday { false } ask_birthday { false }
after(:build) do |procedure| transient do
procedure.administrateurs = [procedure.administrateur] administrateur {}
end
after(:build) do |procedure, evaluator|
if evaluator.administrateur
procedure.administrateurs = [evaluator.administrateur]
elsif procedure.administrateurs.empty?
procedure.administrateurs = [create(:administrateur)]
end
end end
factory :procedure_with_dossiers do factory :procedure_with_dossiers do
@ -30,7 +37,7 @@ FactoryBot.define do
after(:build) do |procedure, _evaluator| after(:build) do |procedure, _evaluator|
procedure.for_individual = true procedure.for_individual = true
procedure.types_de_champ << create(:type_de_champ, libelle: 'Texte obligatoire', mandatory: true) procedure.types_de_champ << create(:type_de_champ, libelle: 'Texte obligatoire', mandatory: true)
procedure.publish!(generate(:published_path)) procedure.publish!(procedure.administrateurs.first, generate(:published_path))
end end
end end
@ -128,13 +135,13 @@ FactoryBot.define do
trait :published do trait :published do
after(:build) do |procedure, _evaluator| after(:build) do |procedure, _evaluator|
procedure.publish!(generate(:published_path)) procedure.publish!(procedure.administrateurs.first, generate(:published_path))
end end
end end
trait :archived do trait :archived do
after(:build) do |procedure, _evaluator| after(:build) do |procedure, _evaluator|
procedure.publish!(generate(:published_path)) procedure.publish!(procedure.administrateurs.first, generate(:published_path))
procedure.archive! procedure.archive!
end end
end end
@ -143,14 +150,14 @@ FactoryBot.define do
# For now the behavior is the same than :archived # For now the behavior is the same than :archived
# (it may be different in the future though) # (it may be different in the future though)
after(:build) do |procedure, _evaluator| after(:build) do |procedure, _evaluator|
procedure.publish!(generate(:published_path)) procedure.publish!(procedure.administrateurs.first, generate(:published_path))
procedure.archive! procedure.archive!
end end
end end
trait :hidden do trait :hidden do
after(:build) do |procedure, _evaluator| after(:build) do |procedure, _evaluator|
procedure.publish!(generate(:published_path)) procedure.publish!(procedure.administrateurs.first, generate(:published_path))
procedure.hide! procedure.hide!
end end
end end

View file

@ -5,9 +5,10 @@ feature 'Administrator connection' do
let(:email) { 'admin1@admin.com' } let(:email) { 'admin1@admin.com' }
let(:password) { 'mon chien aime les bananes' } let(:password) { 'mon chien aime les bananes' }
let!(:admin) { create(:administrateur, :with_admin_trusted_device, :with_procedure, email: email, password: password) } let!(:admin) { create(:administrateur, :with_procedure, email: email, password: password) }
before do before do
Flipflop::FeatureSet.current.test!.switch!(:enable_email_login_token, true)
visit new_administrateur_session_path visit new_administrateur_session_path
end end

View file

@ -7,7 +7,6 @@ feature 'As an administrateur I wanna clone a procedure', js: true do
let(:administrateur) { create(:administrateur) } let(:administrateur) { create(:administrateur) }
before do before do
Flipflop::FeatureSet.current.test!.switch!(:publish_draft, true)
login_as administrateur, scope: :administrateur login_as administrateur, scope: :administrateur
visit new_from_existing_admin_procedures_path visit new_from_existing_admin_procedures_path
end end

View file

@ -8,7 +8,6 @@ feature 'As an administrateur I wanna create a new procedure', js: true do
let(:test_strategy) { Flipflop::FeatureSet.current.test! } let(:test_strategy) { Flipflop::FeatureSet.current.test! }
before do before do
test_strategy.switch!(:publish_draft, true)
test_strategy.switch!(:new_champs_editor, true) test_strategy.switch!(:new_champs_editor, true)
login_as administrateur, scope: :administrateur login_as administrateur, scope: :administrateur
visit root_path visit root_path
@ -50,29 +49,6 @@ feature 'As an administrateur I wanna create a new procedure', js: true do
expect(page).to have_current_path(champs_procedure_path(Procedure.last)) expect(page).to have_current_path(champs_procedure_path(Procedure.last))
end end
end end
context "when publish_draft disabled" do
before do
test_strategy.switch!(:publish_draft, false)
end
scenario 'Finding save button for new procedure, libelle, description and cadre_juridique required' do
expect(page).to have_selector('#new-procedure')
find('#new-procedure').click
click_on 'from-scratch'
expect(page).to have_current_path(new_admin_procedure_path)
fill_in 'procedure_duree_conservation_dossiers_dans_ds', with: '3'
fill_in 'procedure_duree_conservation_dossiers_hors_ds', with: '6'
click_on 'save-procedure'
expect(page).to have_text('Libelle doit être rempli')
fill_in_dummy_procedure_details(fill_path: false)
click_on 'save-procedure'
expect(page).to have_current_path(champs_procedure_path(Procedure.last))
end
end
end end
context 'Editing a new procedure' do context 'Editing a new procedure' do

View file

@ -14,7 +14,6 @@ feature 'Administrateurs can edit procedures', js: true do
end end
before do before do
Flipflop::FeatureSet.current.test!.switch!(:publish_draft, true)
login_as administrateur, scope: :administrateur login_as administrateur, scope: :administrateur
end end

View file

@ -1,7 +1,7 @@
require 'spec_helper' require 'spec_helper'
feature 'As an administrateur I can edit types de champ', js: true do feature 'As an administrateur I can edit types de champ', js: true do
let(:administrateur) { procedure.administrateur } let(:administrateur) { procedure.administrateurs.first }
let(:procedure) { create(:procedure) } let(:procedure) { create(:procedure) }
before do before do

View file

@ -4,11 +4,15 @@ feature 'The gestionnaire part' do
include ActiveJob::TestHelper include ActiveJob::TestHelper
let(:password) { 'secret_password' } let(:password) { 'secret_password' }
let!(:gestionnaire) { create(:gestionnaire, :with_trusted_device, password: password) } let!(:gestionnaire) { create(:gestionnaire, password: password) }
let!(:procedure) { create(:procedure, :published, gestionnaires: [gestionnaire]) } let!(:procedure) { create(:procedure, :published, gestionnaires: [gestionnaire]) }
let!(:dossier) { create(:dossier, state: Dossier.states.fetch(:en_construction), procedure: procedure) } let!(:dossier) { create(:dossier, state: Dossier.states.fetch(:en_construction), procedure: procedure) }
before do
Flipflop::FeatureSet.current.test!.switch!(:enable_email_login_token, true)
end
context 'when the gestionnaire is also a user' do context 'when the gestionnaire is also a user' do
let!(:user) { create(:user, email: gestionnaire.email, password: password) } let!(:user) { create(:user, email: gestionnaire.email, password: password) }

View file

@ -7,9 +7,11 @@ feature "procedure filters" do
let!(:new_unfollow_dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_instruction)) } let!(:new_unfollow_dossier) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_instruction)) }
let!(:champ) { Champ.find_by(type_de_champ_id: type_de_champ.id, dossier_id: new_unfollow_dossier.id) } let!(:champ) { Champ.find_by(type_de_champ_id: type_de_champ.id, dossier_id: new_unfollow_dossier.id) }
let!(:new_unfollow_dossier_2) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_instruction)) } let!(:new_unfollow_dossier_2) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_instruction)) }
let!(:champ_2) { Champ.find_by(type_de_champ_id: type_de_champ.id, dossier_id: new_unfollow_dossier_2.id) }
before do before do
champ.update(value: "Mon champ rempli") champ.update(value: "Mon champ rempli")
champ_2.update(value: "Mon autre champ rempli différemment")
login_as gestionnaire, scope: :gestionnaire login_as gestionnaire, scope: :gestionnaire
visit gestionnaire_procedure_path(procedure) visit gestionnaire_procedure_path(procedure)
end end
@ -66,7 +68,7 @@ feature "procedure filters" do
expect(page).not_to have_link(new_unfollow_dossier_2.user.email) expect(page).not_to have_link(new_unfollow_dossier_2.user.email)
end end
remove_filter remove_filter(champ.value)
within ".dossiers-table" do within ".dossiers-table" do
expect(page).to have_link(new_unfollow_dossier.id) expect(page).to have_link(new_unfollow_dossier.id)
@ -77,8 +79,43 @@ feature "procedure filters" do
end end
end end
def remove_filter scenario "should be able to add and remove two filters for the same field", js: true do
find(:xpath, "//span[contains(@class, 'filter')]/a").click add_filter(type_de_champ.libelle, champ.value)
add_filter(type_de_champ.libelle, champ_2.value)
expect(page).to have_content("#{type_de_champ.libelle} : #{champ.value}")
within ".dossiers-table" do
expect(page).to have_link(new_unfollow_dossier.id, exact: true)
expect(page).to have_link(new_unfollow_dossier.user.email)
expect(page).to have_link(new_unfollow_dossier_2.id, exact: true)
expect(page).to have_link(new_unfollow_dossier_2.user.email)
end
remove_filter(champ.value)
within ".dossiers-table" do
expect(page).not_to have_link(new_unfollow_dossier.id, exact: true)
expect(page).not_to have_link(new_unfollow_dossier.user.email)
expect(page).to have_link(new_unfollow_dossier_2.id, exact: true)
expect(page).to have_link(new_unfollow_dossier_2.user.email)
end
remove_filter(champ_2.value)
within ".dossiers-table" do
expect(page).to have_link(new_unfollow_dossier.id, exact: true)
expect(page).to have_link(new_unfollow_dossier.user.email)
expect(page).to have_link(new_unfollow_dossier_2.id, exact: true)
expect(page).to have_link(new_unfollow_dossier_2.user.email)
end
end
def remove_filter(filter_value)
find(:xpath, "(//span[contains(@class, 'filter')]/a[contains(@href, '#{URI.encode(filter_value)}')])[1]").click
end end
def add_filter(column_name, filter_value) def add_filter(column_name, filter_value)

View file

@ -1,126 +0,0 @@
require 'spec_helper'
describe '2018_07_31_nutriscore' do
let(:gestionnaire) { create(:gestionnaire) }
let(:proc_from) do
proc_from = create(:procedure, :published)
((0..23).to_a - [1, 2, 9, 18]).each do |i|
proc_from.types_de_champ << create(:type_de_champ_text, order_place: i, procedure: proc_from)
end
proc_from.types_de_champ << create(:type_de_champ_text, order_place: 9, libelle: 'Fonction', procedure: proc_from)
proc_from.types_de_champ << create(:type_de_champ_header_section, order_place: 18, libelle: 'PARTIE 3 : ENGAGEMENT DE LEXPLOITANT', procedure: proc_from)
proc_from.save
proc_from
end
let!(:type_champ_from) { create(:type_de_champ_textarea, order_place: 1, libelle: 'texte', procedure: proc_from) }
let!(:type_champ_siret_from) { create(:type_de_champ_text, order_place: 2, libelle: 'Numéro SIRET', procedure: proc_from) }
let!(:type_champ_fonction_from) {}
let(:etablissement) { create(:etablissement) }
let!(:dossier) { create(:dossier, procedure: proc_from, etablissement: etablissement) }
let(:proc_to) do
proc_to = create(:procedure, administrateur: proc_from.administrateur)
((0..17).to_a - [1, 2, 9]).each do |i|
libelle = proc_from.types_de_champ.find_by(order_place: i).libelle
proc_to.types_de_champ << create(:type_de_champ_text, order_place: i, libelle: libelle, procedure: proc_to)
end
proc_to.types_de_champ << create(:type_de_champ_header_section, order_place: 18, libelle: 'PARTIE 3 : ZONE GEOGRAPHIQUE', procedure: proc_to)
proc_to.types_de_champ << create(
:type_de_champ_multiple_drop_down_list,
order_place: 19,
libelle: 'Pays de commercialisation',
drop_down_list: create(:drop_down_list, value: (Champs::PaysChamp.pays - ['----']).join("\r\n")),
procedure: proc_to
)
proc_to.types_de_champ << create(:type_de_champ_header_section, order_place: 20, libelle: 'PARTIE 4 : ENGAGEMENT DE LEXPLOITANT', procedure: proc_to)
(21..25).each do |i|
libelle = proc_from.types_de_champ.find_by(order_place: i - 2).libelle
proc_to.types_de_champ << create(:type_de_champ_text, order_place: i, libelle: libelle, procedure: proc_to)
end
proc_to.save
proc_to
end
let!(:type_champ_to) { create(:type_de_champ_textarea, order_place: 1, libelle: 'texte', procedure: proc_to) }
let!(:type_champ_siret_to) { create(:type_de_champ_siret, order_place: 2, libelle: 'Numéro SIRET', procedure: proc_to) }
let!(:type_champ_fonction_to) { create(:type_de_champ_text, order_place: 9, libelle: 'Fonction', mandatory: true, procedure: proc_to) }
let(:rake_task) { Rake::Task['2018_07_31_nutriscore:migrate_dossiers'] }
def run_task
ENV['SOURCE_PROCEDURE_ID'] = proc_from.id.to_s
ENV['DESTINATION_PROCEDURE_ID'] = proc_to.id.to_s
rake_task.invoke
dossier.reload
proc_from.reload
proc_to.reload
end
after { rake_task.reenable }
context 'on happy path' do
before do
gestionnaire.assign_to_procedure(proc_from)
run_task
end
it { expect(dossier.procedure).to eq(proc_to) }
it { expect(dossier.champs.pluck(:type_de_champ_id)).to match_array(proc_to.types_de_champ.pluck(:id)) }
it { expect(dossier.champs.find_by(type_de_champ: type_champ_siret_to).value).to eq(etablissement.siret) }
it { expect(dossier.champs.find_by(type_de_champ: type_champ_siret_to).etablissement).to eq(etablissement) }
it { expect(proc_from).to be_archivee }
it { expect(proc_to).to be_publiee }
it { expect(proc_to.gestionnaires).to eq([gestionnaire]) }
end
context 'detecting error conditions' do
context 'with administrateur mismatch' do
let(:proc_to) { create(:procedure) }
it { expect { run_task }.to raise_exception(/^Mismatching administrateurs/) }
end
context 'with champ count mismatch' do
before { create(:type_de_champ_textarea, order_place: 26, libelle: 'texte', procedure: proc_to) }
it { expect { run_task }.to raise_exception('Incorrect destination size 27 (expected 26)') }
end
context 'with champ libelle mismatch' do
let!(:type_champ_to) { create(:type_de_champ_textarea, order_place: 1, libelle: 'autre texte', procedure: proc_to) }
it { expect { run_task }.to raise_exception(/incorrect libelle texte \(expected autre texte\)$/) }
end
context 'with champ type mismatch' do
let!(:type_champ_to) { create(:type_de_champ_text, order_place: 1, libelle: 'texte', procedure: proc_to) }
it { expect { run_task }.to raise_exception(/incorrect type champ textarea \(expected text\)$/) }
end
context 'with champ mandatoriness mismatch' do
let!(:type_champ_to) { create(:type_de_champ_textarea, order_place: 1, libelle: 'texte', mandatory: true, procedure: proc_to) }
it { expect { run_task }.to raise_exception(/champ should be mandatory$/) }
end
context 'with dropdown mismatch' do
let!(:type_champ_from) { create(:type_de_champ_drop_down_list, order_place: 1, libelle: 'dropdown', drop_down_list: create(:drop_down_list, value: 'something'), procedure: proc_from) }
let!(:type_champ_to) { create(:type_de_champ_drop_down_list, order_place: 1, libelle: 'dropdown', drop_down_list: create(:drop_down_list, value: 'something else'), procedure: proc_to) }
it { expect { run_task }.to raise_exception(/incorrect drop down list \["", "something"\] \(expected \["", "something else"\]\)$/) }
end
context 'with siret mismatch on source' do
let!(:type_champ_siret_from) { create(:type_de_champ_textarea, order_place: 2, libelle: 'Numéro SIRET', procedure: proc_from) }
it { expect { run_task }.to raise_exception(/incorrect type champ textarea \(expected text\)$/) }
end
context 'with siret mismatch on destination' do
let!(:type_champ_siret_to) { create(:type_de_champ_text, order_place: 2, libelle: 'Numéro SIRET', procedure: proc_to) }
it { expect { run_task }.to raise_exception(/incorrect type champ text \(expected siret\)$/) }
end
end
end

View file

@ -1,82 +0,0 @@
require 'spec_helper'
load Rails.root.join('lib', 'tasks', '2019_01_16_fix_automatic_dossier_logs.rake')
describe '2019_01_16_fix_automatic_dossier_logs' do
let!(:rake_task) { Rake::Task['2019_01_16_fix_automatic_dossier_logs:run'] }
let!(:administrateur) { create(:administrateur) }
let!(:another_gestionnaire) { create(:gestionnaire) }
let!(:procedure) { create(:procedure, administrateur: administrateur) }
let!(:dossier) { create(:dossier, procedure: procedure) }
let!(:fix_automatic_dossier_logs) { FixAutomaticDossierLogs_2019_01_16.new }
before do
allow(fix_automatic_dossier_logs).to receive(:find_handlers)
.and_return([double(job_data: { 'arguments' => [procedure.id, final_state] })])
end
subject do
fix_automatic_dossier_logs.run
dossier.reload
end
context 'when the dossiers are automatically moved to en_instruction' do
let(:final_state) { 'en_instruction' }
context 'and a dossier has been accidentally affected to an administrateur' do
before do
dossier.passer_en_instruction!(administrateur.gestionnaire)
control = DossierOperationLog.create(
gestionnaire: another_gestionnaire,
operation: 'refuser',
automatic_operation: false
)
dossier.dossier_operation_logs << control
subject
end
it { expect(dossier.follows.count).to eq(0) }
it do
expect(dossier_logs).to match_array([
[nil, 'passer_en_instruction', true],
[another_gestionnaire.id, "refuser", false]
])
end
end
context ', followed anyway by another person and accidentally ...' do
before do
another_gestionnaire.follow(dossier)
dossier.passer_en_instruction!(administrateur.gestionnaire)
subject
end
it { expect(dossier.follows.count).to eq(2) }
it { expect(dossier_logs).to match([[nil, 'passer_en_instruction', true]]) }
end
end
context 'when the dossiers are automatically moved to accepte' do
let(:final_state) { 'accepte' }
context 'and a dossier has been accidentally affected to an administrateur' do
before do
dossier.accepter!(administrateur.gestionnaire, '')
subject
end
it { expect(dossier_logs).to match([[nil, 'accepter', true]]) }
end
end
private
def dossier_logs
dossier.dossier_operation_logs.pluck(:gestionnaire_id, :operation, :automatic_operation)
end
end

View file

@ -1,32 +0,0 @@
describe '20181219164523_clone_service_for_transferred_procedures.rake' do
let(:rake_task) { Rake::Task['after_party:clone_service_for_transferred_procedures'] }
subject do
rake_task.invoke
end
after do
rake_task.reenable
end
context 'procedures from different admins share the same service' do
let(:admin1) { create(:administrateur) }
let(:admin2) { create(:administrateur) }
let(:service) { create(:service, administrateur: admin1) }
let!(:procedure1) { create(:procedure, service: service, administrateur: admin1) }
let!(:procedure2) { create(:procedure, service: service, administrateur: admin2) }
let!(:procedure3) { create(:procedure, service: service, administrateur: admin2) }
it 'clones service for procedure2 & procedure3' do
subject
expect(procedure1.reload.service).not_to eq(procedure2.reload.service)
expect(procedure1.reload.service).not_to eq(procedure3.reload.service)
expect(procedure2.reload.service).to eq(procedure3.reload.service)
end
it 'does nothing for procedure1' do
subject
expect(procedure1.reload.service).to eq(service)
end
end
end

View file

@ -1,24 +1,17 @@
require 'spec_helper' describe '20190306172842_create_default_path_for_brouillons.rake' do
let(:rake_task) { Rake::Task['after_party:create_default_path_for_brouillons'] }
describe 'activate_publish_draft#clean' do
let(:rake_task) { Rake::Task['support:activate_publish_draft'] }
let(:administrateur) { create(:administrateur) } let(:administrateur) { create(:administrateur) }
let!(:procedure) { create(:procedure, administrateur: administrateur) } let!(:procedure) { create(:procedure, administrateur: administrateur) }
let!(:procedure2) { create(:simple_procedure, administrateur: administrateur) } let!(:procedure2) { create(:simple_procedure, administrateur: administrateur) }
before do before do
ENV['START_WITH'] = administrateur.email
rake_task.invoke rake_task.invoke
administrateur.reload administrateur.reload
end end
after { rake_task.reenable } after { rake_task.reenable }
it 'activate feature for administrateur' do
expect(administrateur.features["publish_draft"]).to eq(true)
end
it 'create a path for his brouillon procedure' do it 'create a path for his brouillon procedure' do
expect(administrateur.procedures.brouillon.count).to eq(1) expect(administrateur.procedures.brouillon.count).to eq(1)
expect(administrateur.procedures.brouillon.first.path).not_to eq(nil) expect(administrateur.procedures.brouillon.first.path).not_to eq(nil)

View file

@ -652,7 +652,7 @@ describe Dossier do
let(:dossier) { create(:dossier, :en_construction) } let(:dossier) { create(:dossier, :en_construction) }
it 'notifies the procedure administrateur' do it 'notifies the procedure administrateur' do
expect(DossierMailer).to have_received(:notify_deletion_to_administration).once expect(DossierMailer).to have_received(:notify_deletion_to_administration).once
expect(DossierMailer).to have_received(:notify_deletion_to_administration).with(deleted_dossier, dossier.procedure.administrateur.email) expect(DossierMailer).to have_received(:notify_deletion_to_administration).with(deleted_dossier, dossier.procedure.administrateurs.first.email)
end end
end end

View file

@ -14,14 +14,14 @@ describe Gestionnaire, type: :model do
describe '#visible_procedures' do describe '#visible_procedures' do
let(:procedure_not_assigned) { create :procedure, administrateur: admin } let(:procedure_not_assigned) { create :procedure, administrateur: admin }
let(:procedure_without_link) { create :procedure, administrateur: admin } let(:procedure_with_default_path) { create :procedure, administrateur: admin }
let(:procedure_with_link) { create :procedure, :with_path, administrateur: admin } let(:procedure_with_custom_path) { create :procedure, :with_path, administrateur: admin }
let(:procedure_archived_manually) { create :procedure, :archived, administrateur: admin } let(:procedure_archived_manually) { create :procedure, :archived, administrateur: admin }
let(:procedure_archived_automatically) { create :procedure, :archived_automatically, administrateur: admin } let(:procedure_archived_automatically) { create :procedure, :archived_automatically, administrateur: admin }
before do before do
assign(procedure_without_link) assign(procedure_with_default_path)
assign(procedure_with_link) assign(procedure_with_custom_path)
assign(procedure_archived_manually) assign(procedure_archived_manually)
assign(procedure_archived_automatically) assign(procedure_archived_automatically)
end end
@ -30,8 +30,8 @@ describe Gestionnaire, type: :model do
it do it do
expect(subject).not_to include(procedure_not_assigned) expect(subject).not_to include(procedure_not_assigned)
expect(subject).not_to include(procedure_without_link) expect(subject).to include(procedure_with_default_path)
expect(subject).to include(procedure_with_link) expect(subject).to include(procedure_with_custom_path)
expect(subject).to include(procedure_archived_manually) expect(subject).to include(procedure_archived_manually)
expect(subject).to include(procedure_archived_automatically) expect(subject).to include(procedure_archived_automatically)
end end

View file

@ -377,25 +377,28 @@ describe ProcedurePresentation do
context 'for self table' do context 'for self table' do
context 'for created_at column' do context 'for created_at column' do
let(:filter) { [{ 'table' => 'self', 'column' => 'created_at', 'value' => '18/9/2018' }] }
let!(:kept_dossier) { create(:dossier, procedure: procedure, created_at: Time.zone.local(2018, 9, 18, 14, 28)) } let!(:kept_dossier) { create(:dossier, procedure: procedure, created_at: Time.zone.local(2018, 9, 18, 14, 28)) }
let!(:discarded_dossier) { create(:dossier, procedure: procedure, created_at: Time.zone.local(2018, 9, 17, 23, 59)) } let!(:discarded_dossier) { create(:dossier, procedure: procedure, created_at: Time.zone.local(2018, 9, 17, 23, 59)) }
let(:filter) { [{ 'table' => 'self', 'column' => 'created_at', 'value' => '18/9/2018' }] }
it { is_expected.to contain_exactly(kept_dossier.id) } it { is_expected.to contain_exactly(kept_dossier.id) }
end end
context 'for en_construction_at column' do context 'for en_construction_at column' do
let(:filter) { [{ 'table' => 'self', 'column' => 'en_construction_at', 'value' => '17/10/2018' }] }
let!(:kept_dossier) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: Time.zone.local(2018, 10, 17)) } let!(:kept_dossier) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: Time.zone.local(2018, 10, 17)) }
let!(:discarded_dossier) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: Time.zone.local(2013, 1, 1)) } let!(:discarded_dossier) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: Time.zone.local(2013, 1, 1)) }
let(:filter) { [{ 'table' => 'self', 'column' => 'en_construction_at', 'value' => '17/10/2018' }] }
it { is_expected.to contain_exactly(kept_dossier.id) } it { is_expected.to contain_exactly(kept_dossier.id) }
end end
context 'for updated_at column' do context 'for updated_at column' do
let(:filter) { [{ 'table' => 'self', 'column' => 'updated_at', 'value' => '18/9/2018' }] }
let(:kept_dossier) { create(:dossier, procedure: procedure) } let(:kept_dossier) { create(:dossier, procedure: procedure) }
let(:discarded_dossier) { create(:dossier, procedure: procedure) } let(:discarded_dossier) { create(:dossier, procedure: procedure) }
let(:filter) { [{ 'table' => 'self', 'column' => 'updated_at', 'value' => '18/9/2018' }] }
before do before do
kept_dossier.touch(time: Time.zone.local(2018, 9, 18, 14, 28)) kept_dossier.touch(time: Time.zone.local(2018, 9, 18, 14, 28))
@ -406,9 +409,10 @@ describe ProcedurePresentation do
end end
context 'ignore time of day' do context 'ignore time of day' do
let(:filter) { [{ 'table' => 'self', 'column' => 'en_construction_at', 'value' => '17/10/2018 19:30' }] }
let!(:kept_dossier) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: Time.zone.local(2018, 10, 17, 15, 56)) } let!(:kept_dossier) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: Time.zone.local(2018, 10, 17, 15, 56)) }
let!(:discarded_dossier) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: Time.zone.local(2018, 10, 18, 5, 42)) } let!(:discarded_dossier) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: Time.zone.local(2018, 10, 18, 5, 42)) }
let(:filter) { [{ 'table' => 'self', 'column' => 'en_construction_at', 'value' => '17/10/2018 19:30' }] }
it { is_expected.to contain_exactly(kept_dossier.id) } it { is_expected.to contain_exactly(kept_dossier.id) }
end end
@ -416,6 +420,7 @@ describe ProcedurePresentation do
context 'for a malformed date' do context 'for a malformed date' do
context 'when its a string' do context 'when its a string' do
let(:filter) { [{ 'table' => 'self', 'column' => 'updated_at', 'value' => 'malformed date' }] } let(:filter) { [{ 'table' => 'self', 'column' => 'updated_at', 'value' => 'malformed date' }] }
it { is_expected.to match([]) } it { is_expected.to match([]) }
end end
@ -425,13 +430,31 @@ describe ProcedurePresentation do
it { is_expected.to match([]) } it { is_expected.to match([]) }
end end
end end
context 'with multiple search values' do
let(:filter) do
[
{ 'table' => 'self', 'column' => 'en_construction_at', 'value' => '17/10/2018' },
{ 'table' => 'self', 'column' => 'en_construction_at', 'value' => '19/10/2018' }
]
end
let!(:kept_dossier) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: Time.zone.local(2018, 10, 17)) }
let!(:other_kept_dossier) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: Time.zone.local(2018, 10, 19)) }
let!(:discarded_dossier) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: Time.zone.local(2013, 1, 1)) }
it 'returns every dossier that matches any of the search criteria for a given column' do
is_expected.to contain_exactly(kept_dossier.id, other_kept_dossier.id)
end
end
end end
context 'for type_de_champ table' do context 'for type_de_champ table' do
let(:filter) { [{ 'table' => 'type_de_champ', 'column' => type_de_champ.id.to_s, 'value' => 'keep' }] }
let(:kept_dossier) { create(:dossier, procedure: procedure) } let(:kept_dossier) { create(:dossier, procedure: procedure) }
let(:discarded_dossier) { create(:dossier, procedure: procedure) } let(:discarded_dossier) { create(:dossier, procedure: procedure) }
let(:type_de_champ) { procedure.types_de_champ.first } let(:type_de_champ) { procedure.types_de_champ.first }
let(:filter) { [{ 'table' => 'type_de_champ', 'column' => type_de_champ.id.to_s, 'value' => 'keep' }] }
before do before do
type_de_champ.champ.create(dossier: kept_dossier, value: 'keep me') type_de_champ.champ.create(dossier: kept_dossier, value: 'keep me')
@ -439,13 +462,33 @@ describe ProcedurePresentation do
end end
it { is_expected.to contain_exactly(kept_dossier.id) } it { is_expected.to contain_exactly(kept_dossier.id) }
context 'with multiple search values' do
let(:filter) do
[
{ 'table' => 'type_de_champ', 'column' => type_de_champ.id.to_s, 'value' => 'keep' },
{ 'table' => 'type_de_champ', 'column' => type_de_champ.id.to_s, 'value' => 'and' }
]
end
let(:other_kept_dossier) { create(:dossier, procedure: procedure) }
before do
type_de_champ.champ.create(dossier: other_kept_dossier, value: 'and me too')
end
it 'returns every dossier that matches any of the search criteria for a given column' do
is_expected.to contain_exactly(kept_dossier.id, other_kept_dossier.id)
end
end
end end
context 'for type_de_champ_private table' do context 'for type_de_champ_private table' do
let(:filter) { [{ 'table' => 'type_de_champ_private', 'column' => type_de_champ_private.id.to_s, 'value' => 'keep' }] }
let(:kept_dossier) { create(:dossier, procedure: procedure) } let(:kept_dossier) { create(:dossier, procedure: procedure) }
let(:discarded_dossier) { create(:dossier, procedure: procedure) } let(:discarded_dossier) { create(:dossier, procedure: procedure) }
let(:type_de_champ_private) { procedure.types_de_champ_private.first } let(:type_de_champ_private) { procedure.types_de_champ_private.first }
let(:filter) { [{ 'table' => 'type_de_champ_private', 'column' => type_de_champ_private.id.to_s, 'value' => 'keep' }] }
before do before do
type_de_champ_private.champ.create(dossier: kept_dossier, value: 'keep me') type_de_champ_private.champ.create(dossier: kept_dossier, value: 'keep me')
@ -453,34 +496,101 @@ describe ProcedurePresentation do
end end
it { is_expected.to contain_exactly(kept_dossier.id) } it { is_expected.to contain_exactly(kept_dossier.id) }
context 'with multiple search values' do
let(:filter) do
[
{ 'table' => 'type_de_champ_private', 'column' => type_de_champ_private.id.to_s, 'value' => 'keep' },
{ 'table' => 'type_de_champ_private', 'column' => type_de_champ_private.id.to_s, 'value' => 'and' }
]
end
let(:other_kept_dossier) { create(:dossier, procedure: procedure) }
before do
type_de_champ_private.champ.create(dossier: other_kept_dossier, value: 'and me too')
end
it 'returns every dossier that matches any of the search criteria for a given column' do
is_expected.to contain_exactly(kept_dossier.id, other_kept_dossier.id)
end
end
end end
context 'for etablissement table' do context 'for etablissement table' do
context 'for entreprise_date_creation column' do context 'for entreprise_date_creation column' do
let!(:kept_dossier) { create(:dossier, procedure: procedure, etablissement: create(:etablissement, entreprise_date_creation: Time.zone.local(2018, 6, 21))) }
let!(:discarded_dossier) { create(:dossier, procedure: procedure, etablissement: create(:etablissement, entreprise_date_creation: Time.zone.local(2008, 6, 21))) }
let(:filter) { [{ 'table' => 'etablissement', 'column' => 'entreprise_date_creation', 'value' => '21/6/2018' }] } let(:filter) { [{ 'table' => 'etablissement', 'column' => 'entreprise_date_creation', 'value' => '21/6/2018' }] }
let!(:kept_dossier) { create(:dossier, procedure: procedure, etablissement: create(:etablissement, entreprise_date_creation: Time.zone.local(2018, 6, 21))) }
let!(:discarded_dossier) { create(:dossier, procedure: procedure, etablissement: create(:etablissement, entreprise_date_creation: Time.zone.local(2008, 6, 21))) }
it { is_expected.to contain_exactly(kept_dossier.id) } it { is_expected.to contain_exactly(kept_dossier.id) }
context 'with multiple search values' do
let(:filter) do
[
{ 'table' => 'etablissement', 'column' => 'entreprise_date_creation', 'value' => '21/6/2016' },
{ 'table' => 'etablissement', 'column' => 'entreprise_date_creation', 'value' => '21/6/2018' }
]
end
let!(:other_kept_dossier) { create(:dossier, procedure: procedure, etablissement: create(:etablissement, entreprise_date_creation: Time.zone.local(2016, 6, 21))) }
it 'returns every dossier that matches any of the search criteria for a given column' do
is_expected.to contain_exactly(kept_dossier.id, other_kept_dossier.id)
end
end
end end
context 'for code_postal column' do context 'for code_postal column' do
# All columns except entreprise_date_creation work exacly the same, just testing one # All columns except entreprise_date_creation work exacly the same, just testing one
let!(:kept_dossier) { create(:dossier, procedure: procedure, etablissement: create(:etablissement, code_postal: '75017')) }
let!(:discarded_dossier) { create(:dossier, procedure: procedure, etablissement: create(:etablissement, code_postal: '25000')) }
let(:filter) { [{ 'table' => 'etablissement', 'column' => 'code_postal', 'value' => '75017' }] } let(:filter) { [{ 'table' => 'etablissement', 'column' => 'code_postal', 'value' => '75017' }] }
let!(:kept_dossier) { create(:dossier, procedure: procedure, etablissement: create(:etablissement, code_postal: '75017')) }
let!(:discarded_dossier) { create(:dossier, procedure: procedure, etablissement: create(:etablissement, code_postal: '25000')) }
it { is_expected.to contain_exactly(kept_dossier.id) } it { is_expected.to contain_exactly(kept_dossier.id) }
context 'with multiple search values' do
let(:filter) do
[
{ 'table' => 'etablissement', 'column' => 'code_postal', 'value' => '75017' },
{ 'table' => 'etablissement', 'column' => 'code_postal', 'value' => '88100' }
]
end
let!(:other_kept_dossier) { create(:dossier, procedure: procedure, etablissement: create(:etablissement, code_postal: '88100')) }
it 'returns every dossier that matches any of the search criteria for a given column' do
is_expected.to contain_exactly(kept_dossier.id, other_kept_dossier.id)
end
end
end end
end end
context 'for user table' do context 'for user table' do
let!(:kept_dossier) { create(:dossier, procedure: procedure, user: create(:user, email: 'me@keepmail.com')) }
let!(:discarded_dossier) { create(:dossier, procedure: procedure, user: create(:user, email: 'me@discard.com')) }
let(:filter) { [{ 'table' => 'user', 'column' => 'email', 'value' => 'keepmail' }] } let(:filter) { [{ 'table' => 'user', 'column' => 'email', 'value' => 'keepmail' }] }
let!(:kept_dossier) { create(:dossier, procedure: procedure, user: create(:user, email: 'me@keepmail.com')) }
let!(:discarded_dossier) { create(:dossier, procedure: procedure, user: create(:user, email: 'me@discard.com')) }
it { is_expected.to contain_exactly(kept_dossier.id) } it { is_expected.to contain_exactly(kept_dossier.id) }
context 'with multiple search values' do
let(:filter) do
[
{ 'table' => 'user', 'column' => 'email', 'value' => 'keepmail' },
{ 'table' => 'user', 'column' => 'email', 'value' => 'beta.gouv.fr' }
]
end
let!(:other_kept_dossier) { create(:dossier, procedure: procedure, user: create(:user, email: 'bazinga@beta.gouv.fr')) }
it 'returns every dossier that matches any of the search criteria for a given column' do
is_expected.to contain_exactly(kept_dossier.id, other_kept_dossier.id)
end
end
end end
context 'for individual table' do context 'for individual table' do
@ -505,6 +615,109 @@ describe ProcedurePresentation do
it { is_expected.to contain_exactly(kept_dossier.id) } it { is_expected.to contain_exactly(kept_dossier.id) }
end end
context 'with multiple search values' do
let(:filter) do
[
{ 'table' => 'individual', 'column' => 'prenom', 'value' => 'Josephine' },
{ 'table' => 'individual', 'column' => 'prenom', 'value' => 'Romuald' }
]
end
let!(:other_kept_dossier) { create(:dossier, procedure: procedure, individual: create(:individual, gender: 'M', prenom: 'Romuald', nom: 'Pistis')) }
it 'returns every dossier that matches any of the search criteria for a given column' do
is_expected.to contain_exactly(kept_dossier.id, other_kept_dossier.id)
end
end
end
end
describe '#eager_load_displayed_fields' do
let(:procedure_presentation) { ProcedurePresentation.create(assign_to: assign_to, displayed_fields: [{ 'table' => table, 'column' => column }]) }
let!(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
let(:displayed_dossier) { procedure_presentation.eager_load_displayed_fields(procedure.dossiers).first }
context 'for type de champ' do
let(:table) { 'type_de_champ' }
let(:column) { procedure.types_de_champ.first.id }
it 'preloads the champs relation' do
# Ideally, we would only preload the champs for the matching column
expect(displayed_dossier.association(:champs)).to be_loaded
expect(displayed_dossier.association(:champs_private)).not_to be_loaded
expect(displayed_dossier.association(:user)).not_to be_loaded
expect(displayed_dossier.association(:individual)).not_to be_loaded
expect(displayed_dossier.association(:etablissement)).not_to be_loaded
end
end
context 'for type de champ private' do
let(:table) { 'type_de_champ_private' }
let(:column) { procedure.types_de_champ_private.first.id }
it 'preloads the champs relation' do
# Ideally, we would only preload the champs for the matching column
expect(displayed_dossier.association(:champs)).not_to be_loaded
expect(displayed_dossier.association(:champs_private)).to be_loaded
expect(displayed_dossier.association(:user)).not_to be_loaded
expect(displayed_dossier.association(:individual)).not_to be_loaded
expect(displayed_dossier.association(:etablissement)).not_to be_loaded
end
end
context 'for user' do
let(:table) { 'user' }
let(:column) { 'email' }
it 'preloads the user relation' do
expect(displayed_dossier.association(:champs)).not_to be_loaded
expect(displayed_dossier.association(:champs_private)).not_to be_loaded
expect(displayed_dossier.association(:user)).to be_loaded
expect(displayed_dossier.association(:individual)).not_to be_loaded
expect(displayed_dossier.association(:etablissement)).not_to be_loaded
end
end
context 'for individual' do
let(:table) { 'individual' }
let(:column) { 'nom' }
it 'preloads the individual relation' do
expect(displayed_dossier.association(:champs)).not_to be_loaded
expect(displayed_dossier.association(:champs_private)).not_to be_loaded
expect(displayed_dossier.association(:user)).not_to be_loaded
expect(displayed_dossier.association(:individual)).to be_loaded
expect(displayed_dossier.association(:etablissement)).not_to be_loaded
end
end
context 'for etablissement' do
let(:table) { 'etablissement' }
let(:column) { 'siret' }
it 'preloads the etablissement relation' do
expect(displayed_dossier.association(:champs)).not_to be_loaded
expect(displayed_dossier.association(:champs_private)).not_to be_loaded
expect(displayed_dossier.association(:user)).not_to be_loaded
expect(displayed_dossier.association(:individual)).not_to be_loaded
expect(displayed_dossier.association(:etablissement)).to be_loaded
end
end
context 'for self' do
let(:table) { 'self' }
let(:column) { 'created_at' }
it 'does not preload anything' do
expect(displayed_dossier.association(:champs)).not_to be_loaded
expect(displayed_dossier.association(:champs_private)).not_to be_loaded
expect(displayed_dossier.association(:user)).not_to be_loaded
expect(displayed_dossier.association(:individual)).not_to be_loaded
expect(displayed_dossier.association(:etablissement)).not_to be_loaded
end
end end
end end
end end

View file

@ -344,7 +344,7 @@ describe Procedure do
let!(:piece_justificative_1) { create(:type_de_piece_justificative, procedure: procedure, order_place: 1) } let!(:piece_justificative_1) { create(:type_de_piece_justificative, procedure: procedure, order_place: 1) }
let(:received_mail) { create(:received_mail) } let(:received_mail) { create(:received_mail) }
let(:from_library) { false } let(:from_library) { false }
let(:administrateur) { procedure.administrateur } let(:administrateur) { procedure.administrateurs.first }
before do before do
@logo = File.open('spec/fixtures/files/white.png') @logo = File.open('spec/fixtures/files/white.png')
@ -386,7 +386,7 @@ describe Procedure do
cloned_procedure = subject cloned_procedure = subject
cloned_procedure.parent_procedure_id = nil cloned_procedure.parent_procedure_id = nil
expect(cloned_procedure).to have_same_attributes_as(procedure) expect(cloned_procedure).to have_same_attributes_as(procedure, except: ["path"])
end end
it 'should not clone piece justificatives but create corresponding champs' do it 'should not clone piece justificatives but create corresponding champs' do
@ -419,7 +419,6 @@ describe Procedure do
end end
it 'should have one administrateur' do it 'should have one administrateur' do
expect(subject.administrateur).to eq(administrateur)
expect(subject.administrateurs).to eq([administrateur]) expect(subject.administrateurs).to eq([administrateur])
end end
end end
@ -444,7 +443,6 @@ describe Procedure do
end end
it 'should have one administrateur' do it 'should have one administrateur' do
expect(subject.administrateur).to eq(administrateur)
expect(subject.administrateurs).to eq([administrateur]) expect(subject.administrateurs).to eq([administrateur])
end end
end end
@ -479,7 +477,7 @@ describe Procedure do
expect(subject.published_at).to be_nil expect(subject.published_at).to be_nil
expect(subject.test_started_at).to be_nil expect(subject.test_started_at).to be_nil
expect(subject.aasm_state).to eq "brouillon" expect(subject.aasm_state).to eq "brouillon"
expect(subject.path).to be_nil expect(subject.path).not_to be_nil
end end
end end
@ -495,14 +493,14 @@ describe Procedure do
before do before do
Timecop.freeze(now) Timecop.freeze(now)
procedure.publish!("example-path") procedure.publish!(procedure.administrateurs.first, "example-path")
end end
after { Timecop.return } after { Timecop.return }
it { expect(procedure.archived_at).to eq(nil) } it { expect(procedure.archived_at).to eq(nil) }
it { expect(procedure.published_at).to eq(now) } it { expect(procedure.published_at).to eq(now) }
it { expect(Procedure.find_by(path: "example-path")).to eq(procedure) } it { expect(Procedure.find_by(path: "example-path")).to eq(procedure) }
it { expect(Procedure.find_by(path: "example-path").administrateur).to eq(procedure.administrateur) } it { expect(Procedure.find_by(path: "example-path").administrateurs).to eq(procedure.administrateurs) }
end end
describe "#brouillon?" do describe "#brouillon?" do
@ -638,9 +636,11 @@ describe Procedure do
end end
context "without a path" do context "without a path" do
let(:procedure) { create(:procedure) } let(:procedure) { create(:procedure, :archived) }
it { is_expected.to eq("dossiers_procedure-#{procedure.id}_2018-01-02_23-11.csv") } it do
is_expected.to eq("dossiers_procedure-#{procedure.id}_2018-01-02_23-11.csv")
end
end end
end end

View file

@ -59,7 +59,7 @@ describe DossierSerializer do
end end
let(:procedure) do let(:procedure) do
p = original_procedure.clone(original_procedure.administrateur, false) p = original_procedure.clone(original_procedure.administrateurs.first, false)
p.save p.save
p p
end end

View file

@ -21,7 +21,7 @@ describe ProcedureSerializer do
p p
end end
let(:procedure) { original_procedure.clone(original_procedure.administrateur, false) } let(:procedure) { original_procedure.clone(original_procedure.administrateurs.first, false) }
let(:type_pj) { original_procedure.types_de_piece_justificative.first } let(:type_pj) { original_procedure.types_de_piece_justificative.first }
let(:migrated_type_champ) { procedure.types_de_champ.find_by(libelle: type_pj.libelle) } let(:migrated_type_champ) { procedure.types_de_champ.find_by(libelle: type_pj.libelle) }

View file

@ -158,9 +158,12 @@ RSpec.configure do |config|
end end
} }
RSpec::Matchers.define :have_same_attributes_as do |expected| RSpec::Matchers.define :have_same_attributes_as do |expected, options|
match do |actual| match do |actual|
ignored = [:id, :procedure_id, :updated_at, :created_at] ignored = [:id, :procedure_id, :updated_at, :created_at]
if options.present? && options[:except]
ignored = ignored + options[:except]
end
actual.attributes.with_indifferent_access.except(*ignored) == expected.attributes.with_indifferent_access.except(*ignored) actual.attributes.with_indifferent_access.except(*ignored) == expected.attributes.with_indifferent_access.except(*ignored)
end end
end end

View file

@ -33,15 +33,15 @@ describe 'admin/procedures/show.html.haml', type: :view do
it { expect(rendered).not_to have_css('a#reopen-procedure') } it { expect(rendered).not_to have_css('a#reopen-procedure') }
end end
describe 'procedure link is not present' do describe 'procedure path is not customized' do
it { expect(rendered).to have_content('Cette démarche na pas encore de lien, et nest pas accessible par le public.') } it { expect(rendered).to have_content('Cette démarche est actuellement en test') }
end end
end end
end end
describe 'procedure is published' do describe 'procedure is published' do
before do before do
procedure.publish!('fake_path') procedure.publish!(procedure.administrateurs.first, 'fake_path')
procedure.reload procedure.reload
render render
end end
@ -59,7 +59,7 @@ describe 'admin/procedures/show.html.haml', type: :view do
describe 'procedure is archived' do describe 'procedure is archived' do
before do before do
procedure.publish!('fake_path') procedure.publish!(procedure.administrateurs.first, 'fake_path')
procedure.archive! procedure.archive!
procedure.reload procedure.reload
render render