Merge pull request #7267 from betagouv/main

2022-05-06-01
This commit is contained in:
mfo 2022-05-06 14:14:15 +02:00 committed by GitHub
commit f5d2fc7bdd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 327 additions and 733 deletions

70
.github/workflows/codeql-analysis.yml vendored Normal file
View file

@ -0,0 +1,70 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '28 22 * * 1'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript', 'ruby' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

View file

@ -159,7 +159,6 @@ class ApplicationController < ActionController::Base
gon.sentry = sentry_config
if administrateur_signed_in?
gon.sendinblue = sendinblue_config
gon.crisp = crisp_config
end
end
@ -285,26 +284,6 @@ class ApplicationController < ActionController::Base
}
end
def sendinblue_config
sendinblue = Rails.application.secrets.sendinblue
{
key: sendinblue[:client_key],
enabled: sendinblue[:enabled],
administrateur: {
email: current_user&.email,
payload: {
DS_SIGN_IN_COUNT: current_user&.sign_in_count,
DS_CREATED_AT: current_administrateur&.created_at,
DS_ACTIVE: current_user&.active?,
DS_ID: current_administrateur&.id,
DS_GESTIONNAIRE_ID: current_instructeur&.id,
DS_ROLES: current_user_roles
}
}
}
end
def crisp_config
crisp = Rails.application.secrets.crisp

View file

@ -70,7 +70,7 @@ module Users
procedure = Procedure.find_by(path: params[:path])
if procedure&.close?
flash.alert = t('errors.messages.procedure_archived')
flash.alert = t('errors.messages.procedure_archived', service_name: procedure.service.nom, service_phone_number: procedure.service.telephone, service_email: procedure.service.email)
else
flash.alert = t('errors.messages.procedure_not_found')
end

View file

@ -8,7 +8,6 @@ import * as Turbo from '@hotwired/turbo';
import '../shared/activestorage/ujs';
import '../shared/remote-poller';
import '../shared/safari-11-file-xhr-workaround';
import '../shared/franceconnect';
import '../shared/toggle-target';
import '../shared/ujs-error-handling';

View file

@ -1,2 +1 @@
import '../shared/track/sendinblue';
import '../shared/track/crisp';

View file

@ -1,123 +0,0 @@
const fconnect = {
tracesUrl: '/traces',
aboutUrl: ''
};
const document = window.document;
function init() {
fconnect.currentHost = 'fcp.integ01.dev-franceconnect.fr';
if (window.location.hostname == 'www.demarches-simplifiees.fr')
fconnect.currentHost = 'app.franceconnect.gouv.fr';
var fconnectProfile = document.getElementById('fconnect-profile');
if (fconnectProfile) {
var linkAccess = document.querySelector('#fconnect-profile > a');
var fcLogoutUrl = fconnectProfile.getAttribute('data-fc-logout-url');
var access = createFCAccessElement(fcLogoutUrl);
fconnectProfile.appendChild(access);
linkAccess.onclick = toggleElement.bind(access);
}
}
addEventListener('DOMContentLoaded', init);
function toggleElement(event) {
event.preventDefault();
if (this.style.display === 'block') {
this.style.display = 'none';
} else {
this.style.display = 'block';
}
}
function closeFCPopin(event) {
event.preventDefault();
fconnect.popin.className = 'fade-out';
setTimeout(function () {
document.body.removeChild(fconnect.popin);
}, 200);
}
function openFCPopin() {
fconnect.popin = document.createElement('div');
fconnect.popin.id = 'fc-background';
var iframe = createFCIframe();
document.body.appendChild(fconnect.popin);
fconnect.popin.appendChild(iframe);
setTimeout(function () {
fconnect.popin.className = 'fade-in';
}, 200);
}
function createFCIframe() {
var iframe = document.createElement('iframe');
iframe.setAttribute('id', 'fconnect-iframe');
iframe.frameBorder = 0;
iframe.name = 'fconnect-iframe';
return iframe;
}
function createFCAccessElement(logoutUrl) {
var access = document.createElement('div');
access.id = 'fconnect-access';
access.innerHTML = '<h5>Vous êtes identifié grâce à FranceConnect</h5>';
access.appendChild(createAboutLink());
access.appendChild(document.createElement('hr'));
access.appendChild(createHistoryLink());
access.appendChild(createLogoutElement(logoutUrl));
return access;
}
function createHistoryLink() {
var historyLink = document.createElement('a');
historyLink.target = 'fconnect-iframe';
historyLink.href = '//' + fconnect.currentHost + fconnect.tracesUrl;
historyLink.onclick = openFCPopin;
historyLink.innerHTML = 'Historique des connexions/échanges de données';
return historyLink;
}
function createAboutLink() {
var aboutLink = document.createElement('a');
aboutLink.href = fconnect.aboutUrl
? '//' + fconnect.currentHost + fconnect.aboutUrl
: '#';
if (fconnect.aboutUrl) {
aboutLink.target = 'fconnect-iframe';
aboutLink.onclick = openFCPopin;
}
aboutLink.innerHTML = "Qu'est-ce-que FranceConnect ?";
return aboutLink;
}
function createLogoutElement(logoutUrl) {
var elm = document.createElement('div');
elm.className = 'logout';
elm.innerHTML =
'<a class="btn btn-default" href="' + logoutUrl + '">Se déconnecter</a>';
return elm;
}
var eventMethod = window.addEventListener ? 'addEventListener' : 'attachEvent';
var eventer = window[eventMethod];
var messageEvent = eventMethod == 'attachEvent' ? 'onmessage' : 'message';
// Listen to message from child window
eventer(
messageEvent,
function (e) {
var key = e.message ? 'message' : 'data';
var data = e[key];
if (data === 'close_popup') {
closeFCPopin(e);
}
},
false
);

View file

@ -1,22 +0,0 @@
const { key, enabled, administrateur } = gon.sendinblue || {};
if (enabled) {
window.sib = {
equeue: [],
client_key: key,
email_id: administrateur.email
};
const script = document.createElement('script');
const firstScript = document.getElementsByTagName('script')[0];
script.type = 'text/javascript';
script.id = 'sendinblue-js';
script.async = true;
script.src = `https://sibautomation.com/sa.js?key=${window.sib.client_key}`;
firstScript.parentNode.insertBefore(script, firstScript);
window.sib.equeue.push({ page: [] });
window.sib.equeue.push({
identify: [administrateur.email, administrateur.payload]
});
}

View file

@ -1,7 +0,0 @@
class Cron::UpdateAdministrateurUsageStatisticsJob < Cron::CronJob
self.schedule_expression = "every day at 10 am"
def perform
AdministrateurUsageStatisticsService.new.update_administrateurs
end
end

View file

@ -1109,20 +1109,17 @@ class Dossier < ApplicationRecord
if procedure.routee?
columns << ['Groupe instructeur', groupe_instructeur.label]
end
columns + self.class.champs_for_export(champs + champs_private, types_de_champ)
end
# Get all the champs values for the types de champ in the final list.
# Dossier might not have corresponding champ display nil.
# To do so, we build a virtual champ when there is no value so we can call for_export with all indexes
def self.champs_for_export(champs, types_de_champ)
# Index values by stable_id
values = champs.reject(&:exclude_from_export?)
.index_by(&:stable_id)
.transform_values(&:for_export)
# Get all the champs values for the types de champ in the final list.
# Dossier might not have corresponding champ display nil.
types_de_champ.flat_map do |type_de_champ|
Array.wrap(values[type_de_champ.stable_id] || [nil]).map.with_index do |champ_value, index|
champ_or_new = champs.find { |champ| champ.stable_id == type_de_champ.stable_id }
champ_or_new ||= type_de_champ.champ.build
Array.wrap(champ_or_new.for_export || [nil]).map.with_index do |champ_value, index|
[type_de_champ.libelle_for_export(index), champ_value]
end
end

View file

@ -713,9 +713,20 @@ class Procedure < ApplicationRecord
end
def create_new_revision
draft_revision
new_draft = draft_revision
.deep_clone(include: [:revision_types_de_champ])
.tap(&:save!)
children = new_draft.revision_types_de_champ.where.not(parent_id: nil)
children.each do |child|
old_parent = draft_revision.revision_types_de_champ.find(child.parent_id)
new_parent = new_draft.revision_types_de_champ.find_by(type_de_champ_id: old_parent.type_de_champ_id)
child.update!(parent_id: new_parent.id)
end
new_draft.revision_types_de_champ.reload
new_draft
end
def average_dossier_weight

View file

@ -53,23 +53,23 @@ class ProcedureRevision < ApplicationRecord
end
end
def find_or_clone_type_de_champ(id)
type_de_champ = find_type_de_champ_by_id(id)
def find_or_clone_type_de_champ(stable_id)
type_de_champ = find_type_de_champ_by_stable_id(stable_id)
if type_de_champ.revision == self
if type_de_champ.only_present_on_draft?
type_de_champ
elsif type_de_champ.parent.present?
find_or_clone_type_de_champ(type_de_champ.parent.stable_id).types_de_champ.find_by!(stable_id: id)
find_or_clone_type_de_champ(type_de_champ.parent.stable_id).types_de_champ.find_by!(stable_id: stable_id)
else
revise_type_de_champ(type_de_champ)
end
end
def move_type_de_champ(id, position)
type_de_champ = find_type_de_champ_by_id(id)
def move_type_de_champ(stable_id, position)
type_de_champ = find_type_de_champ_by_stable_id(stable_id)
if type_de_champ.parent.present?
repetition_type_de_champ = find_or_clone_type_de_champ(id).parent
repetition_type_de_champ = find_or_clone_type_de_champ(stable_id).parent
move_type_de_champ_hash(repetition_type_de_champ.types_de_champ.to_a, type_de_champ, position).each do |(id, position)|
type_de_champ = repetition_type_de_champ.types_de_champ.find(id)
@ -85,13 +85,13 @@ class ProcedureRevision < ApplicationRecord
end
end
def remove_type_de_champ(id)
type_de_champ = find_type_de_champ_by_id(id)
def remove_type_de_champ(stable_id)
type_de_champ = find_type_de_champ_by_stable_id(stable_id)
if type_de_champ.revision == self
if type_de_champ.only_present_on_draft?
type_de_champ.destroy
elsif type_de_champ.parent.present?
find_or_clone_type_de_champ(id).destroy
find_or_clone_type_de_champ(stable_id).destroy
else
types_de_champ.delete(type_de_champ)
end
@ -384,9 +384,8 @@ class ProcedureRevision < ApplicationRecord
cloned_type_de_champ
end
def find_type_de_champ_by_id(id)
types_de_champ.find_by(stable_id: id) ||
types_de_champ_in_repetition.find_by!(stable_id: id)
def find_type_de_champ_by_stable_id(stable_id)
types_de_champ.find_by(stable_id: stable_id)
end
def types_de_champ_in_repetition

View file

@ -164,6 +164,10 @@ class TypeDeChamp < ApplicationRecord
end
end
def only_present_on_draft?
revisions.size == 1
end
def non_fillable?
type_champ.in?([
TypeDeChamp.type_champs.fetch(:header_section),

View file

@ -1,163 +0,0 @@
# Note: this class uses a `synthetic_state` for Dossier, that diverges from the standard state:
# - 'termine' is the synthetic_state for all dossiers
# whose state is 'accepte', 'refuse' or 'sans_suite',
# even when `archive` is true
# - 'archive' is the synthetic_state for all dossiers
# where archive is true,
# except those whose synthetic_state is already 'termine'
# - For all other dossiers, the synthetic_state and the state are the same
class AdministrateurUsageStatisticsService
def update_administrateurs
Administrateur.includes(:user).find_each do |administrateur|
stats = administrateur_stats(administrateur)
api.update_contact(administrateur.email, stats)
end
api.run
end
private
def api
@api ||= Sendinblue::API.new_properly_configured!
end
def administrateur_stats(administrateur)
nb_dossiers_by_procedure_id = nb_dossiers_by_procedure_id(administrateur.id)
nb_dossiers_by_synthetic_state = nb_dossiers_by_synthetic_state(administrateur.id)
nb_dossiers_roi = nb_dossiers_by_procedure_id.reject { |procedure_id, _count| is_brouillon(procedure_id) }.map { |_procedure_id, count| count }.sum
result = {
ds_sign_in_count: administrateur&.user&.sign_in_count,
ds_created_at: administrateur.created_at,
ds_active: administrateur.user.active?,
ds_id: administrateur.id,
nb_services: nb_services_by_administrateur_id[administrateur.id],
nb_instructeurs: nb_instructeurs_by_administrateur_id[administrateur.id],
ds_nb_demarches_actives: nb_demarches_by_administrateur_id_and_state[[administrateur.id, "publiee"]],
ds_nb_demarches_archives: nb_demarches_by_administrateur_id_and_state[[administrateur.id, "close"]],
ds_nb_demarches_brouillons: nb_demarches_by_administrateur_id_and_state[[administrateur.id, "brouillon"]],
nb_demarches_test: nb_dossiers_by_procedure_id
.filter { |procedure_id, count| count > 0 && is_brouillon(procedure_id) }
.count,
nb_demarches_prod: nb_dossiers_by_procedure_id
.reject { |procedure_id, count| count == 0 || is_brouillon(procedure_id) }
.count,
nb_demarches_prod_20: nb_dossiers_by_procedure_id
.reject { |procedure_id, count| count < 20 || is_brouillon(procedure_id) }
.count,
nb_dossiers: nb_dossiers_by_procedure_id
.reject { |procedure_id, _count| is_brouillon(procedure_id) }
.map { |_procedure_id, count| count }
.sum,
nb_dossiers_max: nb_dossiers_by_procedure_id
.reject { |procedure_id, _count| is_brouillon(procedure_id) }
.map { |_procedure_id, count| count }
.max || 0,
nb_dossiers_traite: nb_dossiers_by_synthetic_state['termine'],
nb_dossiers_dossier_en_instruction: nb_dossiers_by_synthetic_state['en_instruction'],
admin_roi_low: nb_dossiers_roi * 7,
admin_roi_high: nb_dossiers_roi * 17
}
if administrateur&.user&.current_sign_in_at.present?
result[:ds_current_sign_in_at] = administrateur.user.current_sign_in_at
end
if administrateur&.user&.last_sign_in_at.present?
result[:ds_last_sign_in_at] = administrateur.user.last_sign_in_at
end
result
end
# Returns a hash { procedure_id => dossier_count }:
# - The keys are the ids of procedures owned by administrateur_id
# - The values are the number of dossiers for that procedure.
# Brouillons, and dossiers that are 'archive' but not 'termine', are not counted.
def nb_dossiers_by_procedure_id(administrateur_id)
with_default(
0,
nb_dossiers_by_administrateur_id_and_procedure_id_and_synthetic_state[administrateur_id]
.transform_values do |nb_dossiers_by_synthetic_state|
nb_dossiers_by_synthetic_state
.reject { |synthetic_state, _count| ['brouillon', 'archive'].include?(synthetic_state) }
.map { |_synthetic_state, count| count }
.sum
end
)
end
# Returns a hash { synthetic_state => dossier_count }
# - The keys are dossier synthetic_states (see class comment)
# - The values are the number of dossiers in that synthetic state, for procedures owned by `administrateur_id`
# Dossier on procedures en test are not counted
def nb_dossiers_by_synthetic_state(administrateur_id)
with_default(
0,
nb_dossiers_by_administrateur_id_and_procedure_id_and_synthetic_state[administrateur_id]
.reject { |procedure_id, _nb_dossiers_by_synthetic_state| is_brouillon(procedure_id) }
.flat_map { |_procedure_id, nb_dossiers_by_synthetic_state| nb_dossiers_by_synthetic_state.to_a }
.group_by { |synthetic_state, _count| synthetic_state }
.transform_values { |synthetic_states_and_counts| synthetic_states_and_counts.map { |_synthetic_state, count| count }.sum }
)
end
def nb_demarches_by_administrateur_id_and_state
@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
@nb_services_by_administrateur_id ||= with_default(0, Service.group(:administrateur_id).count)
end
def nb_instructeurs_by_administrateur_id
@nb_instructeurs_by_administrateur_id ||= with_default(0, Administrateur.joins(:instructeurs).group('administrateurs.id').count)
end
def nb_dossiers_by_administrateur_id_and_procedure_id_and_synthetic_state
if @nb_dossiers_by_administrateur_id_and_procedure_id_and_synthetic_state.present?
return @nb_dossiers_by_administrateur_id_and_procedure_id_and_synthetic_state
end
result = {}
Dossier
.joins(revision: { procedure: [:administrateurs] })
.group(
'administrateurs.id',
'procedure_revisions.procedure_id',
<<~EOSQL
CASE
WHEN state IN('accepte', 'refuse', 'sans_suite') THEN 'termine'
WHEN archived THEN 'archive'
ELSE state
END
EOSQL
)
.count
.each do |(administrateur_id, procedure_id, synthetic_state), count|
result.deep_merge!(
{ administrateur_id => { procedure_id => { synthetic_state => count } } }
)
end
@nb_dossiers_by_administrateur_id_and_procedure_id_and_synthetic_state =
with_default({}, result)
end
def is_brouillon(procedure_id)
procedure_states[procedure_id] == 'brouillon'
end
def procedure_states
@procedure_states ||= Procedure.pluck(:id, :aasm_state).to_h
end
def with_default(default, hash)
hash.default = default
hash
end
end

View file

@ -12,6 +12,8 @@
- else
%p
= t('users.dossiers.header.banner.procedure_deleted_dossier_en_cours_content')
%p
= t('users.dossiers.header.banner.contact_service', service_name: dossier.procedure.service.nom, service_phone_number: dossier.procedure.service.telephone, service_email: dossier.procedure.service.email)
- else
.flex.justify-between
@ -19,4 +21,6 @@
= render(partial: 'users/dossiers/show/print_dossier', locals: { dossier: dossier }) if !dossier.brouillon?
%p
= t('users.dossiers.header.banner.procedure_close_content')
%p
= t('users.dossiers.header.banner.contact_service', service_name: dossier.procedure.service.nom, service_phone_number: dossier.procedure.service.telephone, service_email: dossier.procedure.service.email)

View file

@ -12,7 +12,7 @@ Rails.application.config.content_security_policy do |policy|
# Javascript: allow us, SendInBlue and Matomo.
# We need unsafe_inline because miniprofiler and us have some inline buttons :(
scripts_whitelist = ["*.sendinblue.com", "*.crisp.chat", "crisp.chat", "*.sibautomation.com", "sibautomation.com", "cdn.jsdelivr.net", "maxcdn.bootstrapcdn.com", "code.jquery.com"]
scripts_whitelist = ["*.crisp.chat", "crisp.chat", "cdn.jsdelivr.net", "maxcdn.bootstrapcdn.com", "code.jquery.com"]
scripts_whitelist << URI(MATOMO_IFRAME_URL).host if MATOMO_IFRAME_URL.present?
policy.script_src(:self, :unsafe_eval, :unsafe_inline, :blob, *scripts_whitelist)
@ -20,7 +20,7 @@ Rails.application.config.content_security_policy do |policy|
# It's too complicated to be fixed right now (and it wouldn't add value: this is hardcoded in views, so not subject to injections)
policy.style_src(:self, :unsafe_inline, "*.crisp.chat", "crisp.chat", 'cdn.jsdelivr.net', 'maxcdn.bootstrapcdn.com')
connect_whitelist = ["wss://*.crisp.chat", "*.crisp.chat", "in-automate.sendinblue.com", "app.franceconnect.gouv.fr", "sentry.io", "openmaptiles.geo.data.gouv.fr", "openmaptiles.github.io", "tiles.geo.api.gouv.fr", "wxs.ign.fr"]
connect_whitelist = ["wss://*.crisp.chat", "*.crisp.chat", "app.franceconnect.gouv.fr", "sentry.io", "openmaptiles.geo.data.gouv.fr", "openmaptiles.github.io", "tiles.geo.api.gouv.fr", "wxs.ign.fr"]
connect_whitelist << ENV.fetch('APP_HOST')
connect_whitelist << URI(DS_PROXY_URL).host if DS_PROXY_URL.present?
connect_whitelist << URI(API_ADRESSE_URL).host if API_ADRESSE_URL.present?

View file

@ -359,7 +359,7 @@ en:
# # etablissement_fail: 'Désolé, nous navons pas réussi à enregistrer létablissement correspondant à ce numéro SIRET'
france_connect:
connexion: "Error trying to connect to France Connect."
procedure_archived: "This procedure has been closed, it is no longer possible to submit a file."
procedure_archived: This procedure has been closed, it is no longer possible to submit a file. For more information, please contact the service %{service_name}, available at %{service_phone_number} or by email %{service_email}
# # procedure_not_draft: "This procedure is not a draft anymore."
# cadastres_empty:
# one: "Aucune parcelle cadastrale sur la zone sélectionnée"

View file

@ -364,7 +364,7 @@ fr:
france_connect:
connexion: "Erreur lors de la connexion à France Connect."
forbidden_html: "Seul-e-s les usagers peuvent se connecter via France Connect. En tant quinstructeur ou administrateur, nous vous invitons à <a href='%{reset_link}'>réininitialiser votre mot de passe</a>."
procedure_archived: "Cette démarche en ligne a été close, il nest plus possible de déposer de dossier."
procedure_archived: Cette démarche en ligne a été close, il nest plus possible de déposer de dossier. Pour plus d'informations veuillez contacter le service %{service_name} au %{service_phone_number} ou par email à %{service_email}
empty_repetition: '« %{value} » doit comporter au moins un champ répétable'
empty_drop_down: '« %{value} » doit comporter au moins un choix sélectionnable'
# procedure_not_draft: "Cette démarche nest maintenant plus en brouillon."

View file

@ -4,6 +4,12 @@ en:
header:
banner:
title: Your file will expire
procedure_deleted_title: The procedure linked to your file is deleted
procedure_close_title: The procedure linked to your file is closed
procedure_deleted_dossier_en_cours_content: You can still consult your file, but it is no longer possible to modify it
procedure_deleted_dossier_termine_content: Your file has been processed by the administration, no action is possible
procedure_close_content: You can still consult your file, but it will not be processed by the administration
contact_service: For more information, please contact the service %{service_name}, available at %{service_phone_number} or by email %{service_email}
states:
brouillon: Your file is still in draft and will soon expire. So it will be deleted soon without being instructed. If you want to pursue your procedure you can submit it now. Otherwise you are able to delay its expiration by clicking on the underneath button.
en_construction: Your file is pending for instruction. The maximum delay is 6 months, but you can extend the duration by a month by clicking on the underneath button.

View file

@ -8,6 +8,7 @@ fr:
procedure_deleted_dossier_en_cours_content: "Vous pouvez toujours consulter votre dossier, mais il nest plus possible de le modifier."
procedure_deleted_dossier_termine_content: "Votre dossier a été traité par l'administration, aucune action n'est possible"
procedure_close_content: "Vous pouvez toujours consulter votre dossier, mais il ne sera pas traité par l'administration"
contact_service: Pour plus d'informations, veuillez vous rapprocher du service %{service_name}, disponible au %{service_phone_number} ou par email %{service_email}
title: Votre dossier va expirer
states:
brouillon: Votre dossier est en brouillon, mais va bientôt expirer. Cela signifie quil va bientôt être supprimé sans avoir été déposé. Si vous souhaitez le conserver afin de poursuivre la démarche, vous pouvez le conserver un mois de plus en cliquant sur le bouton ci-dessous.

View file

@ -642,12 +642,15 @@ describe Instructeurs::DossiersController, type: :controller do
describe "#update_annotations" do
let(:procedure) do
create(:procedure, :published, types_de_champ_private: [
procedure = create(:procedure, :published, types_de_champ_private: [
build(:type_de_champ_multiple_drop_down_list, position: 0),
build(:type_de_champ_linked_drop_down_list, position: 1),
build(:type_de_champ_datetime, position: 2),
build(:type_de_champ_repetition, :with_types_de_champ, position: 3)
build(:type_de_champ_datetime, position: 2)
], instructeurs: instructeurs)
create(:type_de_champ_repetition, :with_types_de_champ, procedure: procedure, position: 3, private: true)
procedure
end
let(:dossier) { create(:dossier, :en_construction, :with_populated_annotations, procedure: procedure) }
let(:another_instructeur) { create(:instructeur) }

View file

@ -190,6 +190,12 @@ FactoryBot.define do
end
end
trait :with_repetition_commune do
after(:build) do |procedure, _evaluator|
build(:type_de_champ_repetition, types_de_champ: [build(:type_de_champ_communes)], procedure: procedure)
end
end
trait :with_number do
after(:build) do |procedure, _evaluator|
build(:type_de_champ_number, procedure: procedure)

View file

@ -188,8 +188,17 @@ FactoryBot.define do
end
trait :with_types_de_champ do
after(:build) do |type_de_champ, _evaluator|
build(:type_de_champ, libelle: 'sub type de champ', parent: type_de_champ)
after(:build) do |type_de_champ, evaluator|
tdc = build(:type_de_champ, libelle: 'sub type de champ', parent: type_de_champ)
evaluator.procedure.save
ProcedureRevisionTypeDeChamp.create!(
revision_id: evaluator.procedure.active_revision.id,
type_de_champ_id: tdc.id,
parent_id: tdc.parent.revision_type_de_champ.id,
position: 0
)
end
end
end

View file

@ -1458,6 +1458,17 @@ describe Dossier do
expect(repetition_second_revision_champs_for_export.map { |(libelle)| libelle }).to eq(procedure.types_de_champ_for_procedure_presentation.repetition.map(&:libelle_for_export))
expect(repetition_second_revision_champs_for_export.first.size).to eq(2)
end
context 'within a repetition having a type de champs commune (multiple values for export)' do
it 'works' do
proc_test = create(:procedure, :with_repetition_commune)
dossier_test = create(:dossier, procedure: proc_test)
repetition = proc_test.types_de_champ_for_procedure_presentation.repetition.first
type_champs = repetition.types_de_champ_for_revision(proc_test.active_revision).to_a
expect(type_champs.size).to eq(1)
expect(Dossier.champs_for_export(dossier.champs, type_champs).size).to eq(2)
end
end
end
context "when procedure brouillon" do

View file

@ -1,42 +1,43 @@
describe ProcedureRevision do
let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private, :with_repetition) }
let(:revision) { procedure.active_revision }
let(:type_de_champ) { revision.types_de_champ_public.first }
let(:type_de_champ_private) { revision.types_de_champ_private.first }
let(:draft) { procedure.draft_revision }
let(:type_de_champ_public) { draft.types_de_champ_public.first }
let(:type_de_champ_private) { draft.types_de_champ_private.first }
let(:type_de_champ_repetition) do
type_de_champ = revision.types_de_champ_public.repetition.first
type_de_champ.update(stable_id: 3333)
type_de_champ
repetition = draft.types_de_champ_public.repetition.first
repetition.update(stable_id: 3333)
repetition
end
describe '#add_type_de_champ' do
let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private, :with_repetition) }
it 'type_de_champ' do
expect(revision.types_de_champ_public.size).to eq(2)
new_type_de_champ = revision.add_type_de_champ({
expect(draft.types_de_champ_public.size).to eq(2)
new_type_de_champ = draft.add_type_de_champ({
type_champ: TypeDeChamp.type_champs.fetch(:text),
libelle: "Un champ text"
})
revision.reload
expect(revision.types_de_champ_public.size).to eq(3)
expect(revision.types_de_champ_public.last).to eq(new_type_de_champ)
expect(revision.revision_types_de_champ_public.last.position).to eq(2)
expect(revision.revision_types_de_champ_public.last.type_de_champ).to eq(new_type_de_champ)
draft.reload
expect(draft.types_de_champ_public.size).to eq(3)
expect(draft.types_de_champ_public.last).to eq(new_type_de_champ)
expect(draft.revision_types_de_champ_public.last.position).to eq(2)
expect(draft.revision_types_de_champ_public.last.type_de_champ).to eq(new_type_de_champ)
end
it 'type_de_champ_private' do
expect(revision.types_de_champ_private.size).to eq(1)
revision.add_type_de_champ({
expect(draft.types_de_champ_private.size).to eq(1)
draft.add_type_de_champ({
type_champ: TypeDeChamp.type_champs.fetch(:text),
libelle: "Un champ text",
private: true
})
revision.reload
expect(revision.types_de_champ_private.size).to eq(2)
draft.reload
expect(draft.types_de_champ_private.size).to eq(2)
end
it 'type_de_champ_repetition' do
expect(type_de_champ_repetition.types_de_champ.size).to eq(1)
revision.add_type_de_champ({
draft.add_type_de_champ({
type_champ: TypeDeChamp.type_champs.fetch(:text),
libelle: "Un champ text",
parent_id: type_de_champ_repetition.stable_id
@ -47,131 +48,159 @@ describe ProcedureRevision do
describe '#move_type_de_champ' do
let(:procedure) { create(:procedure, :with_type_de_champ, types_de_champ_count: 4) }
let(:last_type_de_champ) { revision.types_de_champ_public.last }
it 'move down' do
expect(revision.types_de_champ_public.index(type_de_champ)).to eq(0)
type_de_champ.update(order_place: nil)
revision.move_type_de_champ(type_de_champ.stable_id, 2)
revision.reload
expect(revision.types_de_champ_public.index(type_de_champ)).to eq(2)
expect(revision.procedure.types_de_champ.index(type_de_champ)).to eq(2)
expect(revision.procedure.types_de_champ_for_procedure_presentation.not_repetition.index(type_de_champ)).to eq(2)
end
it 'move up' do
expect(revision.types_de_champ_public.index(last_type_de_champ)).to eq(3)
last_type_de_champ.update(order_place: nil)
revision.move_type_de_champ(last_type_de_champ.stable_id, 0)
revision.reload
expect(revision.types_de_champ_public.index(last_type_de_champ)).to eq(0)
expect(revision.procedure.types_de_champ.index(last_type_de_champ)).to eq(0)
expect(revision.procedure.types_de_champ_for_procedure_presentation.not_repetition.index(last_type_de_champ)).to eq(0)
end
context 'repetition' do
let(:procedure) { create(:procedure, :with_repetition) }
let(:type_de_champ) { type_de_champ_repetition.types_de_champ.first }
let(:last_type_de_champ) { type_de_champ_repetition.types_de_champ.last }
before do
revision.add_type_de_champ({
type_champ: TypeDeChamp.type_champs.fetch(:text),
libelle: "Un champ text",
parent_id: type_de_champ_repetition.stable_id
})
revision.add_type_de_champ({
type_champ: TypeDeChamp.type_champs.fetch(:text),
libelle: "Un champ text",
parent_id: type_de_champ_repetition.stable_id
})
type_de_champ_repetition.reload
end
let(:last_type_de_champ) { draft.types_de_champ_public.last }
context 'with 4 types de champ publiques' do
it 'move down' do
expect(type_de_champ_repetition.types_de_champ.index(type_de_champ)).to eq(0)
revision.move_type_de_champ(type_de_champ.stable_id, 2)
type_de_champ_repetition.reload
expect(type_de_champ_repetition.types_de_champ.index(type_de_champ)).to eq(2)
expect(draft.types_de_champ_public.index(type_de_champ_public)).to eq(0)
type_de_champ_public.update(order_place: nil)
draft.move_type_de_champ(type_de_champ_public.stable_id, 2)
draft.reload
expect(draft.types_de_champ_public.index(type_de_champ_public)).to eq(2)
expect(draft.procedure.types_de_champ.index(type_de_champ_public)).to eq(2)
expect(draft.procedure.types_de_champ_for_procedure_presentation.not_repetition.index(type_de_champ_public)).to eq(2)
end
it 'move up' do
expect(type_de_champ_repetition.types_de_champ.index(last_type_de_champ)).to eq(2)
revision.move_type_de_champ(last_type_de_champ.stable_id, 0)
expect(draft.types_de_champ_public.index(last_type_de_champ)).to eq(3)
last_type_de_champ.update(order_place: nil)
draft.move_type_de_champ(last_type_de_champ.stable_id, 0)
draft.reload
expect(draft.types_de_champ_public.index(last_type_de_champ)).to eq(0)
expect(draft.procedure.types_de_champ.index(last_type_de_champ)).to eq(0)
expect(draft.procedure.types_de_champ_for_procedure_presentation.not_repetition.index(last_type_de_champ)).to eq(0)
end
end
context 'with a champ repetition repetition' do
let(:procedure) { create(:procedure, :with_repetition) }
let!(:second_child) do
draft.add_type_de_champ({
type_champ: TypeDeChamp.type_champs.fetch(:text),
libelle: "second child",
parent_id: type_de_champ_repetition.stable_id
})
end
let!(:last_child) do
draft.add_type_de_champ({
type_champ: TypeDeChamp.type_champs.fetch(:text),
libelle: "last child",
parent_id: type_de_champ_repetition.stable_id
})
end
it 'move down' do
expect(type_de_champ_repetition.types_de_champ.index(second_child)).to eq(1)
draft.move_type_de_champ(second_child.stable_id, 2)
type_de_champ_repetition.reload
expect(type_de_champ_repetition.types_de_champ.index(last_type_de_champ)).to eq(0)
expect(type_de_champ_repetition.types_de_champ.index(second_child)).to eq(2)
end
it 'move up' do
expect(type_de_champ_repetition.types_de_champ.index(last_child)).to eq(2)
draft.move_type_de_champ(last_child.stable_id, 0)
type_de_champ_repetition.reload
expect(type_de_champ_repetition.types_de_champ.index(last_child)).to eq(0)
end
end
end
describe '#remove_type_de_champ' do
it 'type_de_champ' do
expect(revision.types_de_champ_public.size).to eq(2)
revision.remove_type_de_champ(type_de_champ.stable_id)
procedure.reload
expect(revision.types_de_champ_public.size).to eq(1)
context 'for a classic tdc' do
let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private) }
it 'type_de_champ' do
draft.remove_type_de_champ(type_de_champ_public.stable_id)
expect(draft.types_de_champ_public).to be_empty
end
it 'type_de_champ_private' do
draft.remove_type_de_champ(type_de_champ_private.stable_id)
expect(draft.types_de_champ_private).to be_empty
end
end
it 'type_de_champ_private' do
expect(revision.types_de_champ_private.size).to eq(1)
revision.remove_type_de_champ(type_de_champ_private.stable_id)
expect(revision.types_de_champ_private.size).to eq(0)
end
context 'for a type_de_champ_repetition' do
let(:procedure) { create(:procedure, :with_repetition) }
it 'type_de_champ_repetition' do
expect(type_de_champ_repetition.types_de_champ.size).to eq(1)
expect(revision.types_de_champ_public.size).to eq(2)
revision.remove_type_de_champ(type_de_champ_repetition.types_de_champ.first.stable_id)
type_de_champ_repetition.reload
expect(type_de_champ_repetition.types_de_champ.size).to eq(0)
expect(revision.types_de_champ_public.size).to eq(2)
it 'can remove its children' do
draft.remove_type_de_champ(type_de_champ_repetition.types_de_champ.first.stable_id)
expect(type_de_champ_repetition.types_de_champ).to be_empty
expect(draft.types_de_champ_public.size).to eq(1)
end
end
end
describe '#create_new_revision' do
let(:new_revision) { procedure.create_new_revision }
let(:new_draft) { procedure.create_new_revision }
before { new_revision.save }
context 'from a simple procedure' do
let(:procedure) { create(:procedure) }
it 'should be part of procedure' do
expect(new_revision.procedure).to eq(revision.procedure)
expect(procedure.revisions.count).to eq(2)
expect(procedure.revisions).to eq([revision, new_revision])
it 'should be part of procedure' do
expect(new_draft.procedure).to eq(draft.procedure)
expect(procedure.revisions.count).to eq(2)
expect(procedure.revisions).to eq([draft, new_draft])
end
end
it 'should have types_de_champ' do
expect(new_revision.types_de_champ_public.count).to eq(2)
expect(new_revision.types_de_champ_private.count).to eq(1)
expect(new_revision.types_de_champ_public).to eq(revision.types_de_champ_public)
expect(new_revision.types_de_champ_private).to eq(revision.types_de_champ_private)
context 'with simple tdc' do
let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private) }
expect(new_revision.revision_types_de_champ_public.count).to eq(2)
expect(new_revision.revision_types_de_champ_private.count).to eq(1)
expect(new_revision.revision_types_de_champ_public.count).to eq(revision.revision_types_de_champ_public.count)
expect(new_revision.revision_types_de_champ_private.count).to eq(revision.revision_types_de_champ_private.count)
expect(new_revision.revision_types_de_champ_public).not_to eq(revision.revision_types_de_champ_public)
expect(new_revision.revision_types_de_champ_private).not_to eq(revision.revision_types_de_champ_private)
it 'should have the same tdcs with different links' do
expect(new_draft.types_de_champ_public.count).to eq(1)
expect(new_draft.types_de_champ_private.count).to eq(1)
expect(new_draft.types_de_champ_public).to eq(draft.types_de_champ_public)
expect(new_draft.types_de_champ_private).to eq(draft.types_de_champ_private)
expect(new_draft.revision_types_de_champ_public.count).to eq(1)
expect(new_draft.revision_types_de_champ_private.count).to eq(1)
expect(new_draft.revision_types_de_champ_public).not_to eq(draft.revision_types_de_champ_public)
expect(new_draft.revision_types_de_champ_private).not_to eq(draft.revision_types_de_champ_private)
end
end
context 'with repetition_type_de_champ' do
let(:procedure) { create(:procedure, :with_repetition) }
it 'should have the same tdcs with different links' do
expect(new_draft.types_de_champ.count).to eq(2)
expect(new_draft.types_de_champ).to eq(draft.types_de_champ)
new_repetition, new_child = new_draft.types_de_champ.partition(&:repetition?).map(&:first)
parent = new_draft.revision_types_de_champ.find_by(type_de_champ: new_repetition)
child = new_draft.revision_types_de_champ.find_by(type_de_champ: new_child)
expect(child.parent_id).to eq(parent.id)
end
end
describe '#compare' do
let(:type_de_champ_first) { revision.types_de_champ_public.first }
let(:type_de_champ_second) { revision.types_de_champ_public.second }
let(:procedure) { create(:procedure, :with_type_de_champ, :with_type_de_champ_private, :with_repetition) }
let(:type_de_champ_first) { draft.types_de_champ_public.first }
let(:type_de_champ_second) { draft.types_de_champ_public.second }
it 'type_de_champ' do
expect(new_revision.types_de_champ_public.size).to eq(2)
new_type_de_champ = new_revision.add_type_de_champ({
expect(new_draft.types_de_champ_public.size).to eq(2)
new_type_de_champ = new_draft.add_type_de_champ({
type_champ: TypeDeChamp.type_champs.fetch(:text),
libelle: "Un champ text"
})
revision.reload
new_revision.reload
expect(new_revision.types_de_champ_public.size).to eq(3)
expect(new_revision.types_de_champ_public.last).to eq(new_type_de_champ)
expect(new_revision.revision_types_de_champ_public.last.position).to eq(2)
expect(new_revision.revision_types_de_champ_public.last.type_de_champ).to eq(new_type_de_champ)
expect(new_revision.revision_types_de_champ_public.last.type_de_champ.revision).to eq(new_revision)
expect(procedure.active_revision.different_from?(new_revision)).to be_truthy
expect(procedure.active_revision.compare(new_revision)).to eq([
draft.reload
new_draft.reload
expect(new_draft.types_de_champ_public.size).to eq(3)
expect(new_draft.types_de_champ_public.last).to eq(new_type_de_champ)
expect(new_draft.revision_types_de_champ_public.last.position).to eq(2)
expect(new_draft.revision_types_de_champ_public.last.type_de_champ).to eq(new_type_de_champ)
expect(new_draft.revision_types_de_champ_public.last.type_de_champ.revision).to eq(new_draft)
expect(procedure.active_revision.different_from?(new_draft)).to be_truthy
expect(procedure.active_revision.compare(new_draft)).to eq([
{
model: :type_de_champ,
op: :add,
@ -181,8 +210,8 @@ describe ProcedureRevision do
}
])
new_revision.find_or_clone_type_de_champ(new_revision.types_de_champ_public.first.stable_id).update(libelle: 'modifier le libelle')
expect(procedure.active_revision.compare(new_revision.reload)).to eq([
new_draft.find_or_clone_type_de_champ(new_draft.types_de_champ_public.first.stable_id).update(libelle: 'modifier le libelle')
expect(procedure.active_revision.compare(new_draft.reload)).to eq([
{
model: :type_de_champ,
op: :update,
@ -201,10 +230,10 @@ describe ProcedureRevision do
stable_id: new_type_de_champ.stable_id
}
])
expect(new_revision.types_de_champ_public.first.revision).to eq(new_revision)
expect(new_draft.types_de_champ_public.first.revision).to eq(new_draft)
new_revision.move_type_de_champ(new_revision.types_de_champ_public.second.stable_id, 2)
expect(procedure.active_revision.compare(new_revision.reload)).to eq([
new_draft.move_type_de_champ(new_draft.types_de_champ_public.second.stable_id, 2)
expect(procedure.active_revision.compare(new_draft.reload)).to eq([
{
model: :type_de_champ,
op: :update,
@ -232,10 +261,10 @@ describe ProcedureRevision do
stable_id: type_de_champ_second.stable_id
}
])
expect(new_revision.types_de_champ_public.last.revision).to eq(revision)
expect(new_draft.types_de_champ_public.last.revision).to eq(draft)
new_revision.remove_type_de_champ(new_revision.types_de_champ_public.first.stable_id)
expect(procedure.active_revision.compare(new_revision.reload)).to eq([
new_draft.remove_type_de_champ(new_draft.types_de_champ_public.first.stable_id)
expect(procedure.active_revision.compare(new_draft.reload)).to eq([
{
model: :type_de_champ,
op: :remove,
@ -252,9 +281,9 @@ describe ProcedureRevision do
}
])
new_revision.find_or_clone_type_de_champ(new_revision.types_de_champ_public.last.stable_id).update(description: 'une description')
new_revision.find_or_clone_type_de_champ(new_revision.types_de_champ_public.last.stable_id).update(mandatory: true)
expect(procedure.active_revision.compare(new_revision.reload)).to eq([
new_draft.find_or_clone_type_de_champ(new_draft.types_de_champ_public.last.stable_id).update(description: 'une description')
new_draft.find_or_clone_type_de_champ(new_draft.types_de_champ_public.last.stable_id).update(mandatory: true)
expect(procedure.active_revision.compare(new_draft.reload)).to eq([
{
model: :type_de_champ,
op: :remove,
@ -291,9 +320,9 @@ describe ProcedureRevision do
}
])
new_revision.find_or_clone_type_de_champ(new_revision.types_de_champ_public.last.types_de_champ.first.stable_id).update(type_champ: :drop_down_list)
new_revision.find_or_clone_type_de_champ(new_revision.types_de_champ_public.last.types_de_champ.first.stable_id).update(drop_down_options: ['one', 'two'])
expect(procedure.active_revision.compare(new_revision.reload)).to eq([
new_draft.find_or_clone_type_de_champ(new_draft.types_de_champ_public.last.types_de_champ.first.stable_id).update(type_champ: :drop_down_list)
new_draft.find_or_clone_type_de_champ(new_draft.types_de_champ_public.last.types_de_champ.first.stable_id).update(drop_down_options: ['one', 'two'])
expect(procedure.active_revision.compare(new_draft.reload)).to eq([
{
model: :type_de_champ,
op: :remove,
@ -336,7 +365,7 @@ describe ProcedureRevision do
private: false,
from: "text",
to: "drop_down_list",
stable_id: new_revision.types_de_champ_public.last.types_de_champ.first.stable_id
stable_id: new_draft.types_de_champ_public.last.types_de_champ.first.stable_id
},
{
model: :type_de_champ,
@ -346,13 +375,13 @@ describe ProcedureRevision do
private: false,
from: [],
to: ["one", "two"],
stable_id: new_revision.types_de_champ_public.last.types_de_champ.first.stable_id
stable_id: new_draft.types_de_champ_public.last.types_de_champ.first.stable_id
}
])
new_revision.find_or_clone_type_de_champ(new_revision.types_de_champ_public.last.types_de_champ.first.stable_id).update(type_champ: :carte)
new_revision.find_or_clone_type_de_champ(new_revision.types_de_champ_public.last.types_de_champ.first.stable_id).update(options: { cadastres: true, znieff: true })
expect(procedure.active_revision.compare(new_revision.reload)).to eq([
new_draft.find_or_clone_type_de_champ(new_draft.types_de_champ_public.last.types_de_champ.first.stable_id).update(type_champ: :carte)
new_draft.find_or_clone_type_de_champ(new_draft.types_de_champ_public.last.types_de_champ.first.stable_id).update(options: { cadastres: true, znieff: true })
expect(procedure.active_revision.compare(new_draft.reload)).to eq([
{
model: :type_de_champ,
op: :remove,
@ -395,7 +424,7 @@ describe ProcedureRevision do
private: false,
from: "text",
to: "carte",
stable_id: new_revision.types_de_champ_public.last.types_de_champ.first.stable_id
stable_id: new_draft.types_de_champ_public.last.types_de_champ.first.stable_id
},
{
model: :type_de_champ,
@ -405,7 +434,7 @@ describe ProcedureRevision do
private: false,
from: [],
to: [:cadastres, :znieff],
stable_id: new_revision.types_de_champ_public.last.types_de_champ.first.stable_id
stable_id: new_draft.types_de_champ_public.last.types_de_champ.first.stable_id
}
])
end

View file

@ -90,7 +90,8 @@ shared_examples 'type_de_champ_spec' do
end
describe 'changing the type_champ from a repetition' do
let(:tdc) { create(:type_de_champ_repetition, :with_types_de_champ) }
let!(:procedure) { create(:procedure) }
let(:tdc) { create(:type_de_champ_repetition, :with_types_de_champ, procedure: procedure) }
before do
tdc.update_attribute('type_champ', target_type_champ)

View file

@ -1,224 +0,0 @@
describe AdministrateurUsageStatisticsService do
describe '#administrateur_stats' do
let(:service) { AdministrateurUsageStatisticsService.new }
subject { service.send(:administrateur_stats, administrateur) }
before { Timecop.freeze(Time.zone.now) }
after { Timecop.return }
context 'for an administrateur that has nothing' do
let(:administrateur) { create(:administrateur) }
it do
is_expected.to eq(
ds_sign_in_count: 0,
ds_created_at: Time.zone.now,
ds_active: false,
ds_id: administrateur.id,
nb_services: 0,
nb_instructeurs: 0,
ds_nb_demarches_actives: 0,
ds_nb_demarches_archives: 0,
ds_nb_demarches_brouillons: 0,
nb_demarches_test: 0,
nb_demarches_prod: 0,
nb_demarches_prod_20: 0,
nb_dossiers: 0,
nb_dossiers_max: 0,
nb_dossiers_traite: 0,
nb_dossiers_dossier_en_instruction: 0,
admin_roi_low: 0,
admin_roi_high: 0
)
end
end
context 'for an administrateur that has plenty of things' do
let(:administrateur) do
create(:administrateur,
user: create(:user, :with_strong_password, sign_in_count: 17, current_sign_in_at: Time.zone.local(2019, 3, 7), last_sign_in_at: Time.zone.local(2019, 2, 27)),
services: [create(:service)],
instructeurs: [create(:instructeur)])
end
it do
is_expected.to include(
ds_sign_in_count: 17,
ds_current_sign_in_at: Time.zone.local(2019, 3, 7),
ds_last_sign_in_at: Time.zone.local(2019, 2, 27),
ds_created_at: Time.zone.now,
ds_active: true,
ds_id: administrateur.id,
nb_services: 1,
nb_instructeurs: 1
)
end
end
context 'counting procedures and dossiers' do
let(:administrateur) do
create(:administrateur, procedures: [procedure])
end
context 'with a freshly active procedure' do
let(:procedure) { create(:procedure, :published) }
it do
is_expected.to include(
ds_nb_demarches_actives: 1,
ds_nb_demarches_archives: 0,
ds_nb_demarches_brouillons: 0,
nb_demarches_test: 0,
nb_demarches_prod: 0,
nb_demarches_prod_20: 0,
nb_dossiers: 0,
nb_dossiers_max: 0,
nb_dossiers_traite: 0,
nb_dossiers_dossier_en_instruction: 0,
admin_roi_low: 0,
admin_roi_high: 0
)
end
end
context 'with a procedure close' do
let(:procedure) { create(:procedure, :closed) }
let!(:dossiers) do
(1..7).flat_map do
[
create(:dossier, :en_construction, procedure: procedure),
create(:dossier, :en_instruction, procedure: procedure),
create(:dossier, :accepte, procedure: procedure)
]
end
end
it do
is_expected.to include(
ds_nb_demarches_actives: 0,
ds_nb_demarches_archives: 1,
ds_nb_demarches_brouillons: 0,
nb_demarches_test: 0,
nb_demarches_prod: 1,
nb_demarches_prod_20: 1,
nb_dossiers: 21,
nb_dossiers_max: 21,
nb_dossiers_traite: 7,
nb_dossiers_dossier_en_instruction: 7,
admin_roi_low: 147,
admin_roi_high: 357
)
end
end
context 'with a procedure brouillon' do
let(:procedure) { create(:procedure) }
it do
is_expected.to include(
ds_nb_demarches_actives: 0,
ds_nb_demarches_archives: 0,
ds_nb_demarches_brouillons: 1,
nb_demarches_test: 0,
nb_demarches_prod: 0,
nb_demarches_prod_20: 0,
nb_dossiers: 0,
nb_dossiers_max: 0,
nb_dossiers_traite: 0,
nb_dossiers_dossier_en_instruction: 0,
admin_roi_low: 0,
admin_roi_high: 0
)
end
end
context 'with a procedure en test' do
let(:procedure) { create(:procedure) }
let!(:dossiers) do
(1..7).flat_map do
[
create(:dossier, :en_construction, procedure: procedure),
create(:dossier, :en_instruction, procedure: procedure),
create(:dossier, :accepte, procedure: procedure)
]
end
end
it do
is_expected.to include(
ds_nb_demarches_actives: 0,
ds_nb_demarches_archives: 0,
ds_nb_demarches_brouillons: 1,
nb_demarches_test: 1,
nb_demarches_prod: 0,
nb_demarches_prod_20: 0,
nb_dossiers: 0,
nb_dossiers_max: 0,
nb_dossiers_traite: 0,
nb_dossiers_dossier_en_instruction: 0,
admin_roi_low: 0,
admin_roi_high: 0
)
end
end
context 'with a procedure en prod' do
let(:procedure) { create(:procedure, :published) }
let!(:dossiers) do
[
create(:dossier, :en_construction, procedure: procedure),
create(:dossier, :en_instruction, procedure: procedure),
create(:dossier, :accepte, procedure: procedure)
]
end
it do
is_expected.to include(
ds_nb_demarches_actives: 1,
ds_nb_demarches_archives: 0,
ds_nb_demarches_brouillons: 0,
nb_demarches_test: 0,
nb_demarches_prod: 1,
nb_demarches_prod_20: 0,
nb_dossiers: 3,
nb_dossiers_max: 3,
nb_dossiers_traite: 1,
nb_dossiers_dossier_en_instruction: 1,
admin_roi_low: 21,
admin_roi_high: 51
)
end
end
context 'with a procedure en prod and more than 20 dossiers' do
let(:procedure) { create(:procedure, :published) }
let!(:dossiers) do
(1..7).flat_map do
[
create(:dossier, :en_construction, procedure: procedure),
create(:dossier, :en_instruction, procedure: procedure),
create(:dossier, :accepte, procedure: procedure)
]
end
end
it do
is_expected.to include(
ds_nb_demarches_actives: 1,
ds_nb_demarches_archives: 0,
ds_nb_demarches_brouillons: 0,
nb_demarches_test: 0,
nb_demarches_prod: 1,
nb_demarches_prod_20: 1,
nb_dossiers: 21,
nb_dossiers_max: 21,
nb_dossiers_traite: 7,
nb_dossiers_dossier_en_instruction: 7,
admin_roi_low: 147,
admin_roi_high: 357
)
end
end
end
end
end

View file

@ -2,6 +2,11 @@ describe DownloadableFileService do
let(:procedure) { create(:procedure, :published) }
let(:service) { ProcedureArchiveService.new(procedure) }
before do
FileUtils.mkdir_p('/tmp/test_archive_creation')
stub_const("DownloadableFileService::ARCHIVE_CREATION_DIR", '/tmp/test_archive_creation')
end
describe '#download_and_zip' do
let(:archive) { build(:archive, id: '3') }
let(:filename) { service.send(:zip_root_folder, archive) }

View file

@ -32,7 +32,7 @@ describe 'users/dossiers/show/header.html.haml', type: :view do
end
context "when the procedure is discarded with a dossier en construction" do
let(:procedure) { create(:procedure, :discarded) }
let(:procedure) { create(:procedure, :with_service, :discarded) }
let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
it 'affiche que la démarche est supprimée' do
@ -46,7 +46,7 @@ describe 'users/dossiers/show/header.html.haml', type: :view do
end
context "when the procedure is discarded with a dossier terminé" do
let(:procedure) { create(:procedure, :discarded) }
let(:procedure) { create(:procedure, :with_service, :discarded) }
let(:dossier) { create(:dossier, :accepte, procedure: procedure) }
it 'affiche que la démarche est supprimée' do