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 {
display: inline-block;
margin-right: 4 * $default-spacer;
img {
height: 34px;
.header-logo-wide {
margin-right: 4 * $default-spacer;
}
@media (max-width: $landing-breakpoint) {
height: 18px;
.header-logo-narrow {
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-right: 42px;
float: right;
width: 300px;
max-width: 300px;
min-width: 90px;
margin: 0;
}

View file

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

View file

@ -38,7 +38,10 @@ class API::V1::DossiersController < APIController
def fetch_procedure_and_check_token
@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
end

View file

@ -10,7 +10,10 @@ class API::V1::ProceduresController < APIController
def fetch_procedure_and_check_token
@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
end

View file

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

View file

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

View file

@ -81,13 +81,19 @@ module NewAdministrateur
end
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
def procedure_params
params.require(:procedure).permit(:id, :service_id)
end
def maybe_procedure
current_administrateur.procedures.find_by(id: params[:procedure_id])
end
def procedure
current_administrateur.procedures.find(params[:procedure_id])
end

View file

@ -87,7 +87,7 @@ module NewGestionnaire
@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) }
@ -102,17 +102,13 @@ module NewGestionnaire
end
fields = values.map do |value|
table, column = value.split("/")
procedure_presentation.fields.find do |field|
field['table'] == table && field['column'] == column
end
find_field(*value.split('/'))
end
procedure_presentation.update(displayed_fields: fields)
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)
end
@ -145,7 +141,7 @@ module NewGestionnaire
if params[:value].present?
filters = procedure_presentation.filters
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] << {
'label' => label,
@ -162,11 +158,9 @@ module NewGestionnaire
def remove_filter
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)
@ -194,6 +188,14 @@ module NewGestionnaire
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
@statut ||= (params[:statut].presence || 'a-suivre')
end
@ -228,9 +230,7 @@ module NewGestionnaire
end
def displayed_fields_values
procedure_presentation.displayed_fields.map do |field|
"#{field['table']}/#{field['column']}"
end
procedure_presentation.displayed_fields.map { |field| field_id(field) }
end
def current_filters
@ -238,44 +238,7 @@ module NewGestionnaire
end
def available_fields_to_filters
current_filters_fields_ids = current_filters.map do |field|
"#{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
procedure_presentation.fields_for_select
end
def kaminarize(current_page, total)

View file

@ -186,7 +186,7 @@ module NewUser
end
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?
dossier.delete_and_keep_track

View file

@ -23,8 +23,6 @@ class StatsController < ApplicationController
@dossiers_cumulative = cumulative_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?
@dossier_instruction_mean_time = Rails.cache.fetch("dossier_instruction_mean_time", expires_in: 1.day) do
dossier_instruction_mean_time(dossiers)
@ -234,15 +232,6 @@ class StatsController < ApplicationController
.reduce({}, :merge)
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)
(collection.sum.to_f / collection.size).round(2)
end

View file

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

View file

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

View file

@ -7,9 +7,8 @@ class Administrateur < ApplicationRecord
:recoverable, :rememberable, :trackable, :validatable
has_and_belongs_to_many :gestionnaires
has_many :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 :dossiers, -> { state_not_brouillon }, through: :procedures
@ -117,7 +116,7 @@ class Administrateur < ApplicationRecord
end
def owns?(procedure)
id == procedure.administrateur_id
procedure.administrateurs.include?(self)
end
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
include DossierFilteringConcern
enum state: {
brouillon: 'brouillon',
en_construction: 'en_construction',
@ -262,7 +264,7 @@ class Dossier < ApplicationRecord
update(hidden_at: deleted_dossier.deleted_at)
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|
DossierMailer.notify_deletion_to_administration(deleted_dossier, email).deliver_later
end

View file

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

View file

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

View file

@ -1,7 +1,4 @@
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: {
text: 'text',
textarea: 'textarea',

View file

@ -109,7 +109,7 @@ class AdministrateurUsageStatisticsService
end
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
def nb_services_by_administrateur_id
@ -128,9 +128,9 @@ class AdministrateurUsageStatisticsService
result = {}
Dossier
.joins(:procedure)
.joins(procedure: [:administrateurs])
.group(
:administrateur_id,
'administrateurs.id',
:procedure_id,
<<~EOSQL
CASE

View file

@ -3,10 +3,7 @@
%li{ class: @draft_class }
%a{ :href => "#{url_for :admin_procedures_draft}" }
%h5.text-primary
- if Flipflop.publish_draft?
En test
- else
Brouillons
En test
%li{ class: @active_class }
%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|
= render partial: 'informations', locals: { f: f }
.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'
- else
= f.button 'Valider', class: 'btn btn-info', id: 'save-procedure', disabled: true

View file

@ -27,4 +27,4 @@
%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')
%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}
%br
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
Il n'y a aucune démarche douteuse aujourd'hui

View file

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

View file

@ -6,7 +6,8 @@
.flex.align-center
= 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?
- current_url = request.path_info

View file

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

View file

@ -21,7 +21,7 @@
.procedure-list-element{ class: ('active' if active == '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
Service
- if @procedure.missing_steps.include?(:service)

View file

@ -58,8 +58,15 @@ as well as a link to its edit page.
) %>
</dt>
<dd class="attribute-data attribute-data--<%=attribute.html_class%>"
><%= render_field attribute, page: page %></dd>
<dd class="attribute-data attribute-data--<%=attribute.html_class%>">
<%= 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 %>
</dl>
</section>

View file

@ -9,7 +9,7 @@
= 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.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 },
required: true

View file

@ -57,11 +57,16 @@
%br
= submit_tag "Ajouter le filtre", class: 'button'
- @current_filters.each do |filter|
%span.filter
= link_to remove_filter_gestionnaire_procedure_path(@procedure, statut: @statut, table: filter['table'], column: filter['column']) do
%img.close-icon{ src: image_url("close.svg") }
= "#{filter['label'].truncate(50)} : #{filter['value']}"
- @current_filters.group_by { |filter| filter['table'] }.each_with_index do |(table, filters), i|
- if i > 0
et
- filters.each_with_index do |filter, i|
- 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
%thead
%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 'FAQ', FAQ_URL
= link_to 'Documentation', DOC_URL
= contact_link "Contact technique", class: "footer-link", dossier_id: dossier&.id
= link_to 'Aide', FAQ_URL

View file

@ -43,7 +43,7 @@
%ul.numbers
%li.number
.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<
administrations
%br<>

View file

@ -84,15 +84,6 @@
.chart.cumulative-dossiers-chart.hidden
= 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?
.stat-card.stat-card-half.pull-left
%span.stat-card-title Temps de traitement moyen d'un dossier

View file

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

View file

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

View file

@ -10,6 +10,7 @@ Rails.application.routes.draw do
post 'whitelist', on: :member
post 'draft', on: :member
post 'hide', on: :member
post 'add_administrateur', on: :member
end
resources :dossiers, only: [:index, :show] do
@ -312,7 +313,7 @@ Rails.application.routes.draw do
patch 'update_displayed_fields'
get 'update_sort/:table/:column' => 'procedures#update_sort', as: 'update_sort'
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'
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.
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
enable_extension "plpgsql"
@ -536,7 +536,6 @@ ActiveRecord::Schema.define(version: 2019_02_26_105641) do
t.integer "procedure_id"
t.text "description"
t.boolean "mandatory", default: false
t.string "type"
t.boolean "private", default: false, null: false
t.datetime "created_at"
t.datetime "updated_at"
@ -559,6 +558,7 @@ ActiveRecord::Schema.define(version: 2019_02_26_105641) do
t.integer "order_place"
t.string "lien_demarche"
t.boolean "mandatory", default: false
t.index ["procedure_id"], name: "index_types_de_piece_justificative_on_procedure_id"
end
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
def check_same_administrateur
if @source_procedure.administrateur != @destination_procedure.administrateur
raise "Mismatching administrateurs #{@source_procedure.administrateur&.email}#{@destination_procedure.administrateur&.email}"
if @source_procedure.administrateur_ids.sort != @destination_procedure.administrateur_ids.sort
raise "Mismatching administrateurs #{@source_procedure.administrateurs.pluck(:email)}#{@destination_procedure.administrateurs.pluck(:email)}"
end
end

View file

@ -1,82 +1,6 @@
require Rails.root.join("lib", "tasks", "task_helper")
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
Delete the user account for a given USER_EMAIL.
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)
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

View file

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

View file

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

View file

@ -1,10 +1,11 @@
require 'spec_helper'
describe APIController, type: :controller do
describe 'valid_token_for_administrateur?' do
let!(:admin) { create(:administrateur) }
describe 'valid_token_for_procedure?' do
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 'and the token is not given' do

View file

@ -151,9 +151,7 @@ describe ApplicationController, type: :controller do
let(:current_gestionnaire) { create(:gestionnaire) }
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(:redirect_to)
allow(@controller).to receive(:trusted_device?).and_return(trusted_device)
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 }
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
let(:trusted_device) { true }
@ -183,7 +183,9 @@ describe ApplicationController, type: :controller do
end
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
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) }
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))
subject
end

View file

@ -93,42 +93,6 @@ describe StatsController, type: :controller do
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
# Month-2: mean 3 days
# procedure_1: mean 2 days

View file

@ -10,12 +10,13 @@ describe Users::SessionsController, type: :controller do
describe '#create' 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(:trusted_device) { true }
let(:send_password) { password }
before do
Flipflop::FeatureSet.current.test!.switch!(:enable_email_login_token, true)
allow(controller).to receive(:trusted_device?).and_return(trusted_device)
allow(GestionnaireMailer).to receive(:send_login_token).and_return(double(deliver_later: true))
end

View file

@ -9,12 +9,6 @@ FactoryBot.define do
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
after(:create) do |admin|
admin.renew_api_token
@ -23,7 +17,8 @@ FactoryBot.define do
trait :with_procedure do
after(:create) do |admin|
admin.procedures << create(:simple_procedure, administrateur: admin)
create(:simple_procedure, administrateur: admin)
admin.reload
end
end
end

View file

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

View file

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

View file

@ -5,9 +5,10 @@ feature 'Administrator connection' do
let(:email) { 'admin1@admin.com' }
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
Flipflop::FeatureSet.current.test!.switch!(:enable_email_login_token, true)
visit new_administrateur_session_path
end

View file

@ -7,7 +7,6 @@ feature 'As an administrateur I wanna clone a procedure', js: true do
let(:administrateur) { create(:administrateur) }
before do
Flipflop::FeatureSet.current.test!.switch!(:publish_draft, true)
login_as administrateur, scope: :administrateur
visit new_from_existing_admin_procedures_path
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! }
before do
test_strategy.switch!(:publish_draft, true)
test_strategy.switch!(:new_champs_editor, true)
login_as administrateur, scope: :administrateur
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))
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
context 'Editing a new procedure' do

View file

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

View file

@ -1,7 +1,7 @@
require 'spec_helper'
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) }
before do

View file

@ -4,11 +4,15 @@ feature 'The gestionnaire part' do
include ActiveJob::TestHelper
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!(: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
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!(: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!(:champ_2) { Champ.find_by(type_de_champ_id: type_de_champ.id, dossier_id: new_unfollow_dossier_2.id) }
before do
champ.update(value: "Mon champ rempli")
champ_2.update(value: "Mon autre champ rempli différemment")
login_as gestionnaire, scope: :gestionnaire
visit gestionnaire_procedure_path(procedure)
end
@ -66,7 +68,7 @@ feature "procedure filters" do
expect(page).not_to have_link(new_unfollow_dossier_2.user.email)
end
remove_filter
remove_filter(champ.value)
within ".dossiers-table" do
expect(page).to have_link(new_unfollow_dossier.id)
@ -77,8 +79,43 @@ feature "procedure filters" do
end
end
def remove_filter
find(:xpath, "//span[contains(@class, 'filter')]/a").click
scenario "should be able to add and remove two filters for the same field", js: true do
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
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 'activate_publish_draft#clean' do
let(:rake_task) { Rake::Task['support:activate_publish_draft'] }
describe '20190306172842_create_default_path_for_brouillons.rake' do
let(:rake_task) { Rake::Task['after_party:create_default_path_for_brouillons'] }
let(:administrateur) { create(:administrateur) }
let!(:procedure) { create(:procedure, administrateur: administrateur) }
let!(:procedure2) { create(:simple_procedure, administrateur: administrateur) }
before do
ENV['START_WITH'] = administrateur.email
rake_task.invoke
administrateur.reload
end
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
expect(administrateur.procedures.brouillon.count).to eq(1)
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) }
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).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

View file

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

View file

@ -377,25 +377,28 @@ describe ProcedurePresentation do
context 'for self table' 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!(: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) }
end
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!(: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) }
end
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(:discarded_dossier) { create(:dossier, procedure: procedure) }
let(:filter) { [{ 'table' => 'self', 'column' => 'updated_at', 'value' => '18/9/2018' }] }
before do
kept_dossier.touch(time: Time.zone.local(2018, 9, 18, 14, 28))
@ -406,9 +409,10 @@ describe ProcedurePresentation do
end
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!(: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) }
end
@ -416,6 +420,7 @@ describe ProcedurePresentation do
context 'for a malformed date' do
context 'when its a string' do
let(:filter) { [{ 'table' => 'self', 'column' => 'updated_at', 'value' => 'malformed date' }] }
it { is_expected.to match([]) }
end
@ -425,13 +430,31 @@ describe ProcedurePresentation do
it { is_expected.to match([]) }
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
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(:discarded_dossier) { create(:dossier, procedure: procedure) }
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
type_de_champ.champ.create(dossier: kept_dossier, value: 'keep me')
@ -439,13 +462,33 @@ describe ProcedurePresentation do
end
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
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(:discarded_dossier) { create(:dossier, procedure: procedure) }
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
type_de_champ_private.champ.create(dossier: kept_dossier, value: 'keep me')
@ -453,34 +496,101 @@ describe ProcedurePresentation do
end
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
context 'for etablissement table' 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!(: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) }
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
context 'for code_postal column' do
# 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!(: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) }
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
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!(: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) }
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
context 'for individual table' do
@ -505,6 +615,109 @@ describe ProcedurePresentation do
it { is_expected.to contain_exactly(kept_dossier.id) }
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

View file

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

View file

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

View file

@ -21,7 +21,7 @@ describe ProcedureSerializer do
p
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(:migrated_type_champ) { procedure.types_de_champ.find_by(libelle: type_pj.libelle) }

View file

@ -158,9 +158,12 @@ RSpec.configure do |config|
end
}
RSpec::Matchers.define :have_same_attributes_as do |expected|
RSpec::Matchers.define :have_same_attributes_as do |expected, options|
match do |actual|
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)
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') }
end
describe 'procedure link is not present' do
it { expect(rendered).to have_content('Cette démarche na pas encore de lien, et nest pas accessible par le public.') }
describe 'procedure path is not customized' do
it { expect(rendered).to have_content('Cette démarche est actuellement en test') }
end
end
end
describe 'procedure is published' do
before do
procedure.publish!('fake_path')
procedure.publish!(procedure.administrateurs.first, 'fake_path')
procedure.reload
render
end
@ -59,7 +59,7 @@ describe 'admin/procedures/show.html.haml', type: :view do
describe 'procedure is archived' do
before do
procedure.publish!('fake_path')
procedure.publish!(procedure.administrateurs.first, 'fake_path')
procedure.archive!
procedure.reload
render