commit
4cedddb6d7
37 changed files with 332 additions and 300 deletions
|
@ -43,4 +43,12 @@
|
|||
padding-left: $default-spacer * 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
.optional-justificatif {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
input[data-direct-upload-url] {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,16 +93,17 @@ module Gestionnaires
|
|||
|
||||
def terminer
|
||||
motivation = params[:dossier] && params[:dossier][:motivation]
|
||||
justificatif = params[:dossier] && params[:dossier][:justificatif_motivation]
|
||||
|
||||
case params[:process_action]
|
||||
when "refuser"
|
||||
dossier.refuser!(current_gestionnaire, motivation)
|
||||
dossier.refuser!(current_gestionnaire, motivation, justificatif)
|
||||
flash.notice = "Dossier considéré comme refusé."
|
||||
when "classer_sans_suite"
|
||||
dossier.classer_sans_suite!(current_gestionnaire, motivation)
|
||||
dossier.classer_sans_suite!(current_gestionnaire, motivation, justificatif)
|
||||
flash.notice = "Dossier considéré comme sans suite."
|
||||
when "accepter"
|
||||
dossier.accepter!(current_gestionnaire, motivation)
|
||||
dossier.accepter!(current_gestionnaire, motivation, justificatif)
|
||||
flash.notice = "Dossier traité avec succès."
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
module NewAdministrateur
|
||||
class ProcedureAdministrateursController < AdministrateurController
|
||||
before_action :retrieve_procedure
|
||||
|
||||
def index
|
||||
end
|
||||
|
||||
def create
|
||||
email = params.require(:administrateur)[:email]&.strip&.downcase
|
||||
|
||||
# Find the admin
|
||||
administrateur = Administrateur.find_by(email: email)
|
||||
if administrateur.nil?
|
||||
flash.alert = "L’administrateur « #{email} » n’existe pas. Invitez-le à demander un compte administrateur à l’addresse <a href=#{new_demande_url}>#{new_demande_url}</a>."
|
||||
return
|
||||
end
|
||||
|
||||
# Prevent duplicates (also enforced in the database in administrateurs_procedures)
|
||||
if @procedure.administrateurs.include?(administrateur)
|
||||
flash.alert = "L’administrateur « #{administrateur.email} » est déjà administrateur de « #{@procedure.libelle} »."
|
||||
return
|
||||
end
|
||||
|
||||
# Actually add the admin
|
||||
@procedure.administrateurs << administrateur
|
||||
@administrateur = administrateur
|
||||
flash.notice = "L’administrateur « #{administrateur.email} » a été ajouté à la démarche « #{@procedure.libelle} »."
|
||||
end
|
||||
|
||||
def destroy
|
||||
administrateur = @procedure.administrateurs.find(params[:id])
|
||||
|
||||
# Prevent self-removal (Also enforced in the UI)
|
||||
if administrateur == current_administrateur
|
||||
flash.error = "Vous ne pouvez pas vous retirer vous-même d’une démarche."
|
||||
end
|
||||
|
||||
# Actually remove the admin
|
||||
@procedure.administrateurs.delete(administrateur)
|
||||
@administrateur = administrateur
|
||||
flash.notice = "L’administrateur \« #{administrateur.email} » a été retiré de la démarche « #{@procedure.libelle} »."
|
||||
rescue ActiveRecord::ActiveRecordError => e
|
||||
flash.alert = e.message
|
||||
end
|
||||
end
|
||||
end
|
|
@ -11,3 +11,8 @@ export function motivationCancel() {
|
|||
document.querySelectorAll('.motivation').forEach(hide);
|
||||
show(document.querySelector('.dropdown-items'));
|
||||
}
|
||||
|
||||
export function showImportJustificatif(name) {
|
||||
show(document.querySelector('#justificatif_motivation_import_' + name));
|
||||
hide(document.querySelector('#justificatif_motivation_suggest_' + name));
|
||||
}
|
||||
|
|
|
@ -29,7 +29,11 @@ import '../new_design/champs/repetition';
|
|||
|
||||
import { toggleCondidentielExplanation } from '../new_design/avis';
|
||||
import { scrollMessagerie } from '../new_design/messagerie';
|
||||
import { showMotivation, motivationCancel } from '../new_design/state-button';
|
||||
import {
|
||||
showMotivation,
|
||||
motivationCancel,
|
||||
showImportJustificatif
|
||||
} from '../new_design/state-button';
|
||||
import { toggleChart } from '../new_design/toggle-chart';
|
||||
import { replaceSemicolonByComma } from '../new_design/avis';
|
||||
|
||||
|
@ -40,6 +44,7 @@ const DS = {
|
|||
scrollMessagerie,
|
||||
showMotivation,
|
||||
motivationCancel,
|
||||
showImportJustificatif,
|
||||
toggleChart,
|
||||
replaceSemicolonByComma
|
||||
};
|
||||
|
|
|
@ -10,8 +10,12 @@ addEventListener(INITIALIZE_EVENT, ({ target, detail: { id, file } }) => {
|
|||
ProgressBar.init(target, id, file);
|
||||
});
|
||||
|
||||
addEventListener(START_EVENT, ({ detail: { id } }) => {
|
||||
addEventListener(START_EVENT, ({ target, detail: { id } }) => {
|
||||
ProgressBar.start(id);
|
||||
const button = target.form.querySelector('button.primary');
|
||||
if (button) {
|
||||
delete button.dataset.confirm;
|
||||
}
|
||||
});
|
||||
|
||||
addEventListener(PROGRESS_EVENT, ({ detail: { id, progress } }) => {
|
||||
|
|
|
@ -2,4 +2,8 @@ class Champs::PaysChamp < Champs::TextChamp
|
|||
def self.pays
|
||||
ApiGeo::API.pays.pluck(:nom)
|
||||
end
|
||||
|
||||
def self.disabled_options
|
||||
pays.select { |v| (v =~ /^--.*--$/).present? }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -59,7 +59,13 @@ module TagsSubstitutionConcern
|
|||
{
|
||||
libelle: 'lien attestation',
|
||||
description: '',
|
||||
lambda: -> (d) { external_link(attestation_dossier_url(d)) },
|
||||
lambda: -> (d) {
|
||||
links = [external_link(attestation_dossier_url(d))]
|
||||
if d.justificatif_motivation.attached?
|
||||
links.push external_link("Télécharger le justificatif", url_for_justificatif_motivation(d))
|
||||
end
|
||||
links.join "<br />\n"
|
||||
},
|
||||
available_for_states: [Dossier.states.fetch(:accepte)]
|
||||
}
|
||||
]
|
||||
|
@ -138,8 +144,14 @@ module TagsSubstitutionConcern
|
|||
end
|
||||
end
|
||||
|
||||
def external_link(url)
|
||||
link_to(url, url, target: '_blank', rel: 'noopener')
|
||||
def external_link(url, title = nil)
|
||||
link_to(title || url, url, target: '_blank', rel: 'noopener')
|
||||
end
|
||||
|
||||
def url_for_justificatif_motivation(dossier)
|
||||
if dossier.justificatif_motivation.attached?
|
||||
Rails.application.routes.url_helpers.url_for(dossier.justificatif_motivation)
|
||||
end
|
||||
end
|
||||
|
||||
def dossier_tags
|
||||
|
|
|
@ -20,6 +20,8 @@ class Dossier < ApplicationRecord
|
|||
has_one :attestation, dependent: :destroy
|
||||
|
||||
has_many :pieces_justificatives, dependent: :destroy
|
||||
has_one_attached :justificatif_motivation
|
||||
|
||||
has_many :champs, -> { root.public_only.ordered }, dependent: :destroy
|
||||
has_many :champs_private, -> { root.private_only.ordered }, class_name: 'Champ', dependent: :destroy
|
||||
has_many :commentaires, dependent: :destroy
|
||||
|
@ -296,10 +298,12 @@ class Dossier < ApplicationRecord
|
|||
log_dossier_operation(gestionnaire, :repasser_en_construction)
|
||||
end
|
||||
|
||||
def accepter!(gestionnaire, motivation)
|
||||
def accepter!(gestionnaire, motivation, justificatif = nil)
|
||||
self.motivation = motivation
|
||||
self.en_instruction_at ||= Time.zone.now
|
||||
|
||||
if justificatif
|
||||
self.justificatif_motivation.attach(justificatif)
|
||||
end
|
||||
accepte!
|
||||
|
||||
if attestation.nil?
|
||||
|
@ -330,20 +334,24 @@ class Dossier < ApplicationRecord
|
|||
DeletedDossier.create_from_dossier(self)
|
||||
end
|
||||
|
||||
def refuser!(gestionnaire, motivation)
|
||||
def refuser!(gestionnaire, motivation, justificatif = nil)
|
||||
self.motivation = motivation
|
||||
self.en_instruction_at ||= Time.zone.now
|
||||
|
||||
if justificatif
|
||||
self.justificatif_motivation.attach(justificatif)
|
||||
end
|
||||
refuse!
|
||||
|
||||
NotificationMailer.send_refused_notification(self).deliver_later
|
||||
log_dossier_operation(gestionnaire, :refuser)
|
||||
end
|
||||
|
||||
def classer_sans_suite!(gestionnaire, motivation)
|
||||
def classer_sans_suite!(gestionnaire, motivation, justificatif = nil)
|
||||
self.motivation = motivation
|
||||
self.en_instruction_at ||= Time.zone.now
|
||||
|
||||
if justificatif
|
||||
self.justificatif_motivation.attach(justificatif)
|
||||
end
|
||||
sans_suite!
|
||||
|
||||
NotificationMailer.send_without_continuation_notification(self).deliver_later
|
||||
|
|
|
@ -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
|
||||
|
@ -17,7 +19,7 @@ class Procedure < ApplicationRecord
|
|||
|
||||
has_many :assign_to, dependent: :destroy
|
||||
has_many :administrateurs_procedures
|
||||
has_many :administrateurs, through: :administrateurs_procedures
|
||||
has_many :administrateurs, through: :administrateurs_procedures, after_remove: -> (procedure, _admin) { procedure.validate! }
|
||||
has_many :gestionnaires, through: :assign_to
|
||||
|
||||
has_one :initiated_mail, class_name: "Mails::InitiatedMail", dependent: :destroy
|
||||
|
@ -57,6 +59,7 @@ class Procedure < ApplicationRecord
|
|||
|
||||
validates :libelle, presence: true, allow_blank: false, allow_nil: false
|
||||
validates :description, presence: true, allow_blank: false, allow_nil: false
|
||||
validates :administrateurs, presence: true
|
||||
validate :check_juridique
|
||||
validates :path, format: { with: /\A[a-z0-9_\-]{3,50}\z/ }, uniqueness: { scope: :aasm_state, case_sensitive: false }, presence: true, allow_blank: false, allow_nil: true
|
||||
# FIXME: remove duree_conservation_required flag once all procedures are converted to the new style
|
||||
|
|
|
@ -22,6 +22,7 @@ class DossierSerializer < ActiveModel::Serializer
|
|||
has_many :champs_private
|
||||
has_many :pieces_justificatives
|
||||
has_many :types_de_piece_justificative
|
||||
has_one :justificatif_motivation
|
||||
|
||||
has_many :champs, serializer: ChampSerializer
|
||||
|
||||
|
@ -52,6 +53,12 @@ class DossierSerializer < ActiveModel::Serializer
|
|||
PiecesJustificativesService.serialize_champs_as_pjs(object)
|
||||
end
|
||||
|
||||
def justificatif_motivation
|
||||
if object.justificatif_motivation.attached?
|
||||
Rails.application.routes.url_helpers.url_for(object.justificatif_motivation)
|
||||
end
|
||||
end
|
||||
|
||||
def types_de_piece_justificative
|
||||
ActiveModelSerializers::SerializableResource.new(object.types_de_piece_justificative).serializable_hash +
|
||||
PiecesJustificativesService.serialize_types_de_champ_as_type_pj(object)
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
- if dossier.motivation.present?
|
||||
%h4 Motivation
|
||||
%p.dossier-motivation= dossier.motivation
|
||||
= render partial: 'new_user/dossiers/show/download_justificatif', locals: { dossier: dossier }
|
||||
|
||||
- if dossier.attestation.present?
|
||||
%h4 Attestation
|
||||
|
|
|
@ -28,6 +28,10 @@
|
|||
%li= unspecified_annotations_privee.libelle
|
||||
- else
|
||||
= text_area :dossier, :motivation, class: 'motivation-text-area', placeholder: placeholder, required: true
|
||||
.optional-justificatif{ id: "justificatif_motivation_suggest_#{popup_class}", onclick: "DS.showImportJustificatif('#{popup_class}');" }
|
||||
.button Ajouter un justificatif (optionnel)
|
||||
.hidden{ id: "justificatif_motivation_import_#{popup_class}" }
|
||||
= file_field :dossier, :justificatif_motivation, direct_upload: true
|
||||
.text-right
|
||||
- if title == 'Accepter' && dossier.procedure.attestation_template&.activated?
|
||||
= link_to "Voir l'attestation", apercu_attestation_gestionnaire_dossier_path(dossier.procedure, dossier), target: '_blank', rel: 'noopener', class: 'button', title: "Voir l'attestation qui sera envoyée au demandeur"
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
= stylesheet_link_tag 'new_design/new_application', media: 'all', 'data-turbolinks-track': 'reload'
|
||||
= stylesheet_link_tag 'new_design/print', media: 'print', 'data-turbolinks-track': 'reload'
|
||||
|
||||
= Gon::Base.render_data(camel_case: true, init: true)
|
||||
= Gon::Base.render_data(camel_case: true, init: true, nonce: request.content_security_policy_nonce)
|
||||
|
||||
- if Rails.env.development?
|
||||
= stylesheet_link_tag :xray
|
||||
|
|
|
@ -27,6 +27,10 @@
|
|||
- if @procedure.missing_steps.include?(:service)
|
||||
%p.missing-steps (à compléter)
|
||||
|
||||
%a#onglet-administrateurs{ href: url_for(procedure_administrateurs_path(@procedure)) }
|
||||
.procedure-list-element{ class: ('active' if active == 'Administrateurs') }
|
||||
Administrateurs
|
||||
|
||||
%a#onglet-instructeurs{ href: url_for(admin_procedure_instructeurs_path(@procedure)) }
|
||||
.procedure-list-element{ class: ('active' if active == 'Instructeurs') }
|
||||
Instructeurs
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
%tr{ id: "procedure-#{@procedure.id}-administrateur-#{administrateur.id}" }
|
||||
%td= administrateur.email
|
||||
%td= administrateur.created_at.strftime('%d/%m/%Y %H:%M')
|
||||
%td= administrateur.registration_state
|
||||
%td
|
||||
- if administrateur == current_administrateur
|
||||
C’est vous !
|
||||
- else
|
||||
= link_to 'Retirer',
|
||||
[@procedure, administrateur],
|
||||
method: :delete,
|
||||
'data-confirm': "Retirer « #{administrateur.email} » des administrateurs de « #{@procedure.libelle} » ?",
|
||||
remote: true
|
|
@ -0,0 +1,6 @@
|
|||
= render_flash(sticky: true)
|
||||
- if @administrateur
|
||||
= append_to_element("#procedure-#{@procedure.id}-administrateurs",
|
||||
partial: 'administrateur',
|
||||
locals: { administrateur: @administrateur })
|
||||
document.getElementById('procedure-#{@procedure.id}-new_administrateur').reset()
|
|
@ -0,0 +1,4 @@
|
|||
= render_flash(sticky: true)
|
||||
- if @administrateur
|
||||
= remove_element("#procedure-#{@procedure.id}-administrateur-#{@administrateur.id}")
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
= render partial: 'new_administrateur/breadcrumbs',
|
||||
locals: { steps: [link_to('Démarches', admin_procedures_path),
|
||||
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
|
||||
'Administrateurs'], preview: false }
|
||||
|
||||
.container
|
||||
%h1 Administrateurs de « #{@procedure.libelle} »
|
||||
%table.table
|
||||
%thead
|
||||
%th= 'Adresse email'
|
||||
%th= 'Enregistré le'
|
||||
%th= 'État'
|
||||
%tbody{ id: "procedure-#{@procedure.id}-administrateurs" }
|
||||
= render partial: 'administrateur', collection: @procedure.administrateurs.order(:email)
|
||||
%tfoot
|
||||
%tr
|
||||
%th{ colspan: 4 }
|
||||
= form_for @procedure.administrateurs.new,
|
||||
url: { controller: 'procedure_administrateurs' },
|
||||
html: { class: 'form', id: "procedure-#{@procedure.id}-new_administrateur" } ,
|
||||
remote: true do |f|
|
||||
= f.label :email do
|
||||
Ajouter un administrateur
|
||||
%span.notice= "Renseignez l’email d’un administrateur déjà enregistré sur demarches-simplifiees.fr pour lui permettre de modifier « #{@procedure.libelle} »."
|
||||
= f.email_field :email, placeholder: 'marie.dupont@exemple.fr', required: true
|
||||
= f.submit 'Ajouter comme administrateur', class: 'button primary send'
|
|
@ -0,0 +1,7 @@
|
|||
- if dossier.present?
|
||||
- justificatif = dossier.justificatif_motivation
|
||||
- if dossier.justificatif_motivation.attached? and dossier.justificatif_motivation.virus_scanner.safe?
|
||||
.action
|
||||
= link_to (justificatif), target: '_blank', class: 'button primary' do
|
||||
%span.icon.download
|
||||
Télécharger le justificatif
|
|
@ -1,7 +1,7 @@
|
|||
.container
|
||||
- if dossier.en_construction_at.present?
|
||||
.card
|
||||
= render partial: "shared/dossiers/horodatage", locals: { dossier: dossier }
|
||||
= render partial: "shared/dossiers/infos_generales", locals: { dossier: dossier }
|
||||
|
||||
.tab-title Identité du demandeur
|
||||
.card
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
%table.table.vertical.dossier-champs
|
||||
%tbody
|
||||
%tr
|
||||
%th.libelle Déposé le :
|
||||
%td= l(dossier.en_construction_at, format: '%d %B %Y')
|
11
app/views/shared/dossiers/_infos_generales.html.haml
Normal file
11
app/views/shared/dossiers/_infos_generales.html.haml
Normal file
|
@ -0,0 +1,11 @@
|
|||
%table.table.vertical.dossier-champs
|
||||
%tbody
|
||||
%tr
|
||||
%th.libelle Déposé le :
|
||||
%td= l(dossier.en_construction_at, format: '%d %B %Y')
|
||||
- if dossier.justificatif_motivation.attached?
|
||||
%tr
|
||||
%th.libelle Justificatif :
|
||||
%td
|
||||
.action
|
||||
= render partial: 'shared/piece_jointe/pj_link', locals: { object: dossier, pj: dossier.justificatif_motivation }
|
|
@ -1,4 +1,5 @@
|
|||
= form.select :value,
|
||||
Champs::PaysChamp.pays,
|
||||
disabled: Champs::PaysChamp.disabled_options,
|
||||
include_blank: true,
|
||||
required: champ.mandatory?
|
||||
|
|
|
@ -50,12 +50,15 @@
|
|||
%h3 Motif de l’acceptation
|
||||
%blockquote= dossier.motivation
|
||||
|
||||
= render partial: 'new_user/dossiers/show/download_justificatif', locals: { dossier: dossier }
|
||||
|
||||
- if dossier.attestation.present?
|
||||
.action
|
||||
= link_to attestation_dossier_path(dossier), target: '_blank', rel: 'noopener', class: 'button primary' do
|
||||
%span.icon.download
|
||||
Télécharger l’attestation
|
||||
|
||||
|
||||
- elsif dossier.refuse?
|
||||
.refuse
|
||||
%p.decision
|
||||
|
@ -68,6 +71,7 @@
|
|||
%h3 Motif du refus
|
||||
%blockquote= dossier.motivation
|
||||
|
||||
= render partial: 'new_user/dossiers/show/download_justificatif', locals: { dossier: @dossier }
|
||||
.action
|
||||
= link_to 'Envoyer un message à l’administration', messagerie_dossier_url(dossier, anchor: 'new_commentaire'), class: 'button'
|
||||
|
||||
|
@ -79,6 +83,8 @@
|
|||
= succeed '.' do
|
||||
%strong sans suite
|
||||
|
||||
= render partial: 'new_user/dossiers/show/download_justificatif', locals: { dossier: @dossier }
|
||||
|
||||
- if dossier.motivation.present?
|
||||
%h3 Motif du classement sans suite
|
||||
%blockquote= dossier.motivation
|
||||
|
|
|
@ -59,6 +59,9 @@ Rails.application.configure do
|
|||
port: 3000
|
||||
}
|
||||
|
||||
# Use Content-Security-Policy-Report-Only instead of Content-Security-Policy
|
||||
config.content_security_policy_report_only = true
|
||||
|
||||
# Raises error for missing translations
|
||||
# config.action_view.raise_on_missing_translations = true
|
||||
|
||||
|
|
|
@ -109,5 +109,7 @@ Rails.application.configure do
|
|||
host: ENV['APP_HOST']
|
||||
}
|
||||
|
||||
config.content_security_policy_report_only = true
|
||||
|
||||
config.lograge.enabled = ENV['LOGRAGE_ENABLED'] == 'enabled'
|
||||
end
|
||||
|
|
|
@ -45,6 +45,9 @@ Rails.application.configure do
|
|||
protocol: :http
|
||||
}
|
||||
|
||||
# Use Content-Security-Policy-Report-Only instead of Content-Security-Policy
|
||||
config.content_security_policy_report_only = true
|
||||
|
||||
config.active_job.queue_adapter = :test
|
||||
config.active_storage.service = :test
|
||||
|
||||
|
|
23
config/initializers/content_security_policy.rb
Normal file
23
config/initializers/content_security_policy.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
Rails.application.config.content_security_policy do |policy|
|
||||
# En cas de non respect d'une des règles, faire un POST sur cette URL
|
||||
policy.report_uri "https://e30e0ed9c14194254481124271b34a72.report-uri.com/r/d/csp/reportOnly"
|
||||
# Whitelist image
|
||||
policy.img_src :self, "https://*.openstreetmap.org"
|
||||
# Whitelist JS: nous, sendinblue et matomo, et… miniprofiler :(
|
||||
if Rails.env.development?
|
||||
# https://github.com/MiniProfiler/rack-mini-profiler/issues/327
|
||||
policy.script_src :self, "https://sibautomation.com", "//stats.data.gouv.fr", :unsafe_eval, :unsafe_inline
|
||||
else
|
||||
policy.script_src :self, "https://sibautomation.com", "//stats.data.gouv.fr"
|
||||
end
|
||||
# Génération d'un nonce pour les balises script inline qu'on maitrise (Gon)
|
||||
Rails.application.config.content_security_policy_nonce_generator = -> _request { SecureRandom.base64(16) }
|
||||
|
||||
# Pour les CSS, on a beaucoup de style inline et quelques balises <style>
|
||||
# c'est trop compliqué pour être rectifié immédiatement (et sans valeur ajoutée:
|
||||
# c'est hardcodé dans les vues, donc pas injectable).
|
||||
policy.style_src :self, :unsafe_inline
|
||||
# Pour tout le reste, par défaut on accepte uniquement ce qui vient de chez nous
|
||||
# et dans la notification on inclue la source de l'erreur
|
||||
policy.default_src :self, :data, :report_sample
|
||||
end
|
|
@ -374,6 +374,8 @@ Rails.application.routes.draw do
|
|||
get 'annotations'
|
||||
end
|
||||
|
||||
resources :administrateurs, controller: 'procedure_administrateurs', only: [:index, :create, :destroy]
|
||||
|
||||
resources :types_de_champ, only: [:create, :update, :destroy] do
|
||||
member do
|
||||
patch :move
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: add_path_to_procedures'
|
||||
task add_path_to_procedures: :environment do
|
||||
puts "Running deploy task 'add_path_to_procedures'"
|
||||
|
||||
def print_procedure(procedure)
|
||||
puts "#{procedure.id}##{procedure.path} - #{procedure.errors.full_messages}"
|
||||
end
|
||||
|
||||
puts "Démarches publiées :"
|
||||
Procedure.publiees.where(path: nil).find_each do |procedure|
|
||||
procedure.path = procedure.path
|
||||
if !procedure.save
|
||||
print_procedure(procedure)
|
||||
end
|
||||
end
|
||||
|
||||
puts "Démarches archivées :"
|
||||
Procedure.archivees.where(path: nil).find_each do |procedure|
|
||||
if procedure.procedure_path.present?
|
||||
procedure.path = procedure.path
|
||||
if !procedure.save
|
||||
print_procedure(procedure)
|
||||
end
|
||||
end
|
||||
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: '20180913161001'
|
||||
end
|
||||
end
|
|
@ -1,209 +0,0 @@
|
|||
require Rails.root.join("lib", "tasks", "task_helper")
|
||||
|
||||
namespace :after_party do
|
||||
desc 'Deployment task: restore_deleted_dossiers'
|
||||
task restore_deleted_dossiers: :environment do
|
||||
Class.new do
|
||||
def run
|
||||
rake_puts "Running deploy task 'restore_deleted_dossiers'"
|
||||
restore_candidats_libres_deleted_dossiers
|
||||
restore_neph_deleted_dossiers
|
||||
AfterParty::TaskRecord.create version: '20181009130216'
|
||||
end
|
||||
|
||||
def restore_candidats_libres_deleted_dossiers
|
||||
mapping = Class.new(Tasks::DossierProcedureMigrator::ChampMapping) do
|
||||
def setup_mapping
|
||||
champ_opts = {
|
||||
16 => {
|
||||
source_overrides: { 'libelle' => 'Adresse postale du candidat' },
|
||||
destination_overrides: { 'libelle' => 'Adresse postale complète du candidat' }
|
||||
}
|
||||
}
|
||||
(0..23).each do |i|
|
||||
map_source_to_destination_champ(i, i, **(champ_opts[i] || {}))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private_mapping = Class.new(Tasks::DossierProcedureMigrator::ChampMapping) do
|
||||
def setup_mapping
|
||||
compute_destination_champ(
|
||||
TypeDeChamp.new(
|
||||
type_champ: 'datetime',
|
||||
order_place: 0,
|
||||
libelle: 'Date et heure de convocation',
|
||||
mandatory: false
|
||||
)
|
||||
) do |d, target_tdc|
|
||||
target_tdc.champ.create(dossier: d)
|
||||
end
|
||||
compute_destination_champ(
|
||||
TypeDeChamp.new(
|
||||
type_champ: 'text',
|
||||
order_place: 1,
|
||||
libelle: 'Lieu de convocation',
|
||||
mandatory: false
|
||||
)
|
||||
) do |d, target_tdc|
|
||||
target_tdc.champ.create(dossier: d)
|
||||
end
|
||||
compute_destination_champ(
|
||||
TypeDeChamp.new(
|
||||
type_champ: 'address',
|
||||
order_place: 2,
|
||||
libelle: 'Adresse centre examen',
|
||||
mandatory: false
|
||||
)
|
||||
) do |d, target_tdc|
|
||||
target_tdc.champ.create(dossier: d)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pj_mapping = Class.new(Tasks::DossierProcedureMigrator::PieceJustificativeMapping) do
|
||||
def setup_mapping
|
||||
(0..3).each do |i|
|
||||
map_source_to_destination_pj(i, i + 2)
|
||||
end
|
||||
leave_destination_pj_blank(
|
||||
TypeDePieceJustificative.new(
|
||||
order_place: 0,
|
||||
libelle: "Télécharger la Charte de l'accompagnateur"
|
||||
)
|
||||
)
|
||||
leave_destination_pj_blank(
|
||||
TypeDePieceJustificative.new(
|
||||
order_place: 1,
|
||||
libelle: "Télécharger l'attestation d'assurance"
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
restore_deleted_dossiers(4860, 8603, mapping, private_mapping, pj_mapping)
|
||||
end
|
||||
|
||||
def restore_neph_deleted_dossiers
|
||||
mapping = Class.new(Tasks::DossierProcedureMigrator::ChampMapping) do
|
||||
def can_migrate?(dossier)
|
||||
!(dossier.termine? ||
|
||||
dossier.champs.joins(:type_de_champ).find_by(types_de_champ: { order_place: 3 }).value&.include?('"Demande de duplicata de dossier d\'inscription (suite perte)"'))
|
||||
end
|
||||
|
||||
def setup_mapping
|
||||
champ_opts = {
|
||||
3 => {
|
||||
source_overrides: { 'drop_down' => ["", "Demande de réactualisation du numéro NEPH", "Demande de communication du numéro NEPH", "Demande de duplicata de dossier d'inscription (suite perte)", "Demande de correction sur le Fichier National des Permis de conduire"] },
|
||||
destination_overrides: { 'drop_down' => ["", "Demande de réactualisation du numéro NEPH", "Demande de communication du numéro NEPH", "Demande de correction sur le Fichier National des Permis de conduire"] }
|
||||
}
|
||||
}
|
||||
(0..14).each do |i|
|
||||
map_source_to_destination_champ(i, i, **(champ_opts[i] || {}))
|
||||
end
|
||||
(16..22).each do |i|
|
||||
map_source_to_destination_champ(i, i + 2, **(champ_opts[i] || {}))
|
||||
end
|
||||
|
||||
discard_source_champ(
|
||||
TypeDeChamp.new(
|
||||
type_champ: 'address',
|
||||
order_place: 15,
|
||||
libelle: 'Adresse du candidat'
|
||||
)
|
||||
)
|
||||
|
||||
compute_destination_champ(
|
||||
TypeDeChamp.new(
|
||||
type_champ: 'address',
|
||||
order_place: 15,
|
||||
libelle: 'Adresse du candidat',
|
||||
mandatory: true
|
||||
)
|
||||
) do |d, target_tdc|
|
||||
value = d.champs.joins(:type_de_champ).find_by(types_de_champ: { order_place: 3 }).value
|
||||
if !d.brouillon?
|
||||
value ||= 'non renseigné'
|
||||
end
|
||||
target_tdc.champ.create(dossier: d, value: value)
|
||||
end
|
||||
|
||||
compute_destination_champ(
|
||||
TypeDeChamp.new(
|
||||
type_champ: 'address',
|
||||
order_place: 16,
|
||||
libelle: 'Code postal',
|
||||
mandatory: true
|
||||
)
|
||||
) do |d, target_tdc|
|
||||
target_tdc.champ.create(dossier: d, value: d.brouillon? ? nil : 'non renseigné')
|
||||
end
|
||||
|
||||
compute_destination_champ(
|
||||
TypeDeChamp.new(
|
||||
type_champ: 'address',
|
||||
order_place: 17,
|
||||
libelle: 'Ville',
|
||||
mandatory: true
|
||||
)
|
||||
) do |d, target_tdc|
|
||||
target_tdc.champ.create(dossier: d, value: d.brouillon? ? nil : 'non renseigné')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private_mapping = Class.new(Tasks::DossierProcedureMigrator::ChampMapping) do
|
||||
def setup_mapping
|
||||
(0..2).each do |i|
|
||||
map_source_to_destination_champ(i, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
pj_mapping = Class.new(Tasks::DossierProcedureMigrator::PieceJustificativeMapping) do
|
||||
def setup_mapping
|
||||
(0..3).each do |i|
|
||||
map_source_to_destination_pj(i, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
restore_deleted_dossiers(6388, 8770, mapping, private_mapping, pj_mapping)
|
||||
end
|
||||
|
||||
def restore_deleted_dossiers(deleted_procedure_id, new_procedure_id, champ_mapping, champ_private_mapping, pj_mapping)
|
||||
source_procedure = Procedure.unscoped.find(deleted_procedure_id)
|
||||
destination_procedure = Procedure.find(new_procedure_id)
|
||||
|
||||
deleted_dossiers = Dossier.unscoped
|
||||
.where(procedure_id: deleted_procedure_id)
|
||||
.where('dossiers.hidden_at >= ?', source_procedure.hidden_at)
|
||||
|
||||
deleted_dossier_ids = deleted_dossiers.pluck(:id).to_a
|
||||
deleted_dossiers.update_all(hidden_at: nil)
|
||||
|
||||
source_procedure
|
||||
.update_columns(
|
||||
hidden_at: nil,
|
||||
archived_at: source_procedure.hidden_at,
|
||||
aasm_state: :archivee
|
||||
)
|
||||
|
||||
migrator = Tasks::DossierProcedureMigrator.new(source_procedure, destination_procedure, champ_mapping, champ_private_mapping, pj_mapping) do |dossier|
|
||||
DossierMailer.notify_undelete_to_user(dossier).deliver_later
|
||||
end
|
||||
migrator.check_consistency
|
||||
migrator.migrate_dossiers
|
||||
|
||||
source_procedure.dossiers.where(id: deleted_dossier_ids).find_each do |dossier|
|
||||
if dossier.termine?
|
||||
DossierMailer.notify_undelete_to_user(dossier).deliver_later
|
||||
else
|
||||
rake_puts "Dossier #{dossier.id} non migré\n"
|
||||
DossierMailer.notify_unmigrated_to_user(dossier, destination_procedure).deliver_later
|
||||
end
|
||||
end
|
||||
end
|
||||
end.new.run
|
||||
end
|
||||
end
|
|
@ -1,10 +0,0 @@
|
|||
namespace :after_party do
|
||||
desc 'Deployment task: remove_invite_gestionnaires'
|
||||
task remove_invite_gestionnaires: :environment do
|
||||
InviteGestionnaire.destroy_all
|
||||
|
||||
# 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: '20181010102500'
|
||||
end
|
||||
end
|
|
@ -139,7 +139,7 @@ describe API::V1::DossiersController do
|
|||
let!(:dossier) { Timecop.freeze(date_creation) { create(:dossier, :with_entreprise, :en_construction, procedure: procedure, motivation: "Motivation") } }
|
||||
let(:dossier_id) { dossier.id }
|
||||
let(:body) { JSON.parse(retour.body, symbolize_names: true) }
|
||||
let(:field_list) { [:id, :created_at, :updated_at, :archived, :individual, :entreprise, :etablissement, :cerfa, :types_de_piece_justificative, :pieces_justificatives, :champs, :champs_private, :commentaires, :state, :simplified_state, :initiated_at, :processed_at, :received_at, :motivation, :email, :instructeurs] }
|
||||
let(:field_list) { [:id, :created_at, :updated_at, :archived, :individual, :entreprise, :etablissement, :cerfa, :types_de_piece_justificative, :pieces_justificatives, :champs, :champs_private, :commentaires, :state, :simplified_state, :initiated_at, :processed_at, :received_at, :motivation, :email, :instructeurs, :justificatif_motivation] }
|
||||
subject { body[:dossier] }
|
||||
|
||||
it 'return REST code 200', :show_in_doc do
|
||||
|
|
|
@ -7,6 +7,7 @@ describe Gestionnaires::DossiersController, type: :controller do
|
|||
let(:gestionnaires) { [gestionnaire] }
|
||||
let(:procedure) { create(:procedure, :published, gestionnaires: gestionnaires) }
|
||||
let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
|
||||
let(:fake_justificatif) { Rack::Test::UploadedFile.new("./spec/fixtures/files/piece_justificative_0.pdf", 'application/pdf') }
|
||||
|
||||
before { sign_in(gestionnaire) }
|
||||
|
||||
|
@ -141,24 +142,39 @@ describe Gestionnaires::DossiersController, type: :controller do
|
|||
sign_in gestionnaire
|
||||
end
|
||||
|
||||
subject { post :terminer, params: { process_action: "refuser", procedure_id: procedure.id, dossier_id: dossier.id }, format: 'js' }
|
||||
context 'simple refusal' do
|
||||
subject { post :terminer, params: { process_action: "refuser", procedure_id: procedure.id, dossier_id: dossier.id }, format: 'js' }
|
||||
|
||||
it 'change state to refuse' do
|
||||
subject
|
||||
it 'change state to refuse' do
|
||||
subject
|
||||
|
||||
dossier.reload
|
||||
expect(dossier.state).to eq(Dossier.states.fetch(:refuse))
|
||||
dossier.reload
|
||||
expect(dossier.state).to eq(Dossier.states.fetch(:refuse))
|
||||
expect(dossier.justificatif_motivation).to_not be_attached
|
||||
end
|
||||
|
||||
it 'Notification email is sent' do
|
||||
expect(NotificationMailer).to receive(:send_refused_notification)
|
||||
.with(dossier).and_return(NotificationMailer)
|
||||
expect(NotificationMailer).to receive(:deliver_later)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
it 'Notification email is sent' do
|
||||
expect(NotificationMailer).to receive(:send_refused_notification)
|
||||
.with(dossier).and_return(NotificationMailer)
|
||||
expect(NotificationMailer).to receive(:deliver_later)
|
||||
context 'refusal with a justificatif' do
|
||||
subject { post :terminer, params: { process_action: "refuser", procedure_id: procedure.id, dossier_id: dossier.id, dossier: { justificatif_motivation: fake_justificatif } }, format: 'js' }
|
||||
|
||||
subject
|
||||
it 'attachs a justificatif' do
|
||||
subject
|
||||
|
||||
dossier.reload
|
||||
expect(dossier.state).to eq(Dossier.states.fetch(:refuse))
|
||||
expect(dossier.justificatif_motivation).to be_attached
|
||||
end
|
||||
|
||||
it { expect(subject.body).to include('.state-button') }
|
||||
end
|
||||
|
||||
it { expect(subject.body).to include('.state-button') }
|
||||
end
|
||||
|
||||
context "with classer_sans_suite" do
|
||||
|
@ -166,25 +182,41 @@ describe Gestionnaires::DossiersController, type: :controller do
|
|||
dossier.en_instruction!
|
||||
sign_in gestionnaire
|
||||
end
|
||||
context 'without attachment' do
|
||||
subject { post :terminer, params: { process_action: "classer_sans_suite", procedure_id: procedure.id, dossier_id: dossier.id }, format: 'js' }
|
||||
|
||||
subject { post :terminer, params: { process_action: "classer_sans_suite", procedure_id: procedure.id, dossier_id: dossier.id }, format: 'js' }
|
||||
it 'change state to sans_suite' do
|
||||
subject
|
||||
|
||||
it 'change state to sans_suite' do
|
||||
subject
|
||||
dossier.reload
|
||||
expect(dossier.state).to eq(Dossier.states.fetch(:sans_suite))
|
||||
expect(dossier.justificatif_motivation).to_not be_attached
|
||||
end
|
||||
|
||||
dossier.reload
|
||||
expect(dossier.state).to eq(Dossier.states.fetch(:sans_suite))
|
||||
it 'Notification email is sent' do
|
||||
expect(NotificationMailer).to receive(:send_without_continuation_notification)
|
||||
.with(dossier).and_return(NotificationMailer)
|
||||
expect(NotificationMailer).to receive(:deliver_later)
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
it { expect(subject.body).to include('.state-button') }
|
||||
end
|
||||
|
||||
it 'Notification email is sent' do
|
||||
expect(NotificationMailer).to receive(:send_without_continuation_notification)
|
||||
.with(dossier).and_return(NotificationMailer)
|
||||
expect(NotificationMailer).to receive(:deliver_later)
|
||||
context 'with attachment' do
|
||||
subject { post :terminer, params: { process_action: "classer_sans_suite", procedure_id: procedure.id, dossier_id: dossier.id, dossier: { justificatif_motivation: fake_justificatif } }, format: 'js' }
|
||||
|
||||
subject
|
||||
it 'change state to sans_suite' do
|
||||
subject
|
||||
|
||||
dossier.reload
|
||||
expect(dossier.state).to eq(Dossier.states.fetch(:sans_suite))
|
||||
expect(dossier.justificatif_motivation).to be_attached
|
||||
end
|
||||
|
||||
it { expect(subject.body).to include('.state-button') }
|
||||
end
|
||||
|
||||
it { expect(subject.body).to include('.state-button') }
|
||||
end
|
||||
|
||||
context "with accepter" do
|
||||
|
@ -206,6 +238,7 @@ describe Gestionnaires::DossiersController, type: :controller do
|
|||
|
||||
dossier.reload
|
||||
expect(dossier.state).to eq(Dossier.states.fetch(:accepte))
|
||||
expect(dossier.justificatif_motivation).to_not be_attached
|
||||
end
|
||||
|
||||
context 'when the dossier does not have any attestation' do
|
||||
|
@ -261,6 +294,20 @@ describe Gestionnaires::DossiersController, type: :controller do
|
|||
|
||||
it { subject }
|
||||
end
|
||||
|
||||
context 'with an attachment' do
|
||||
subject { post :terminer, params: { process_action: "accepter", procedure_id: procedure.id, dossier_id: dossier.id, dossier: { justificatif_motivation: fake_justificatif } }, format: 'js' }
|
||||
|
||||
it 'change state to accepte' do
|
||||
subject
|
||||
|
||||
dossier.reload
|
||||
expect(dossier.state).to eq(Dossier.states.fetch(:accepte))
|
||||
expect(dossier.justificatif_motivation).to be_attached
|
||||
end
|
||||
|
||||
it { expect(subject.body).to include('.state-button') }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ describe MailTemplateConcern do
|
|||
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||
let(:dossier2) { create(:dossier, procedure: procedure) }
|
||||
let(:initiated_mail) { create(:initiated_mail, procedure: procedure) }
|
||||
let(:justificatif) { Rack::Test::UploadedFile.new("./spec/fixtures/files/piece_justificative_0.pdf", 'application/pdf') }
|
||||
|
||||
shared_examples "can replace tokens in template" do
|
||||
describe 'with no token to replace' do
|
||||
|
@ -74,10 +75,21 @@ describe MailTemplateConcern do
|
|||
mail.body = "--lien attestation--"
|
||||
end
|
||||
|
||||
describe "in closed mail" do
|
||||
describe "in closed mail without justificatif" do
|
||||
let(:mail) { create(:closed_mail, procedure: procedure) }
|
||||
it { is_expected.to eq("<a target=\"_blank\" rel=\"noopener\" href=\"http://localhost:3000/dossiers/#{dossier.id}/attestation\">http://localhost:3000/dossiers/#{dossier.id}/attestation</a>") }
|
||||
it { is_expected.to_not include("Télécharger le justificatif") }
|
||||
end
|
||||
|
||||
describe "in closed mail with justificatif" do
|
||||
before do
|
||||
dossier.justificatif_motivation.attach(justificatif)
|
||||
end
|
||||
let(:mail) { create(:closed_mail, procedure: procedure) }
|
||||
|
||||
it { is_expected.to eq("<a target=\"_blank\" rel=\"noopener\" href=\"http://localhost:3000/dossiers/#{dossier.id}/attestation\">http://localhost:3000/dossiers/#{dossier.id}/attestation</a>") }
|
||||
it { expect(dossier.justificatif_motivation).to be_attached }
|
||||
it { is_expected.to include("<a target=\"_blank\" rel=\"noopener\" href=\"http://localhost:3000/dossiers/#{dossier.id}/attestation\">http://localhost:3000/dossiers/#{dossier.id}/attestation</a>") }
|
||||
it { is_expected.to include("Télécharger le justificatif") }
|
||||
end
|
||||
|
||||
describe "in refuse mail" do
|
||||
|
@ -85,6 +97,12 @@ describe MailTemplateConcern do
|
|||
|
||||
it { is_expected.to eq("--lien attestation--") }
|
||||
end
|
||||
|
||||
describe "in without continuation mail" do
|
||||
let(:mail) { create(:without_continuation_mail, procedure: procedure) }
|
||||
|
||||
it { is_expected.to eq("--lien attestation--") }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -168,6 +168,10 @@ describe Procedure do
|
|||
it { is_expected.to allow_value('URRSAF').for(:organisation) }
|
||||
end
|
||||
|
||||
context 'administrateurs' do
|
||||
it { is_expected.not_to allow_value([]).for(:administrateurs) }
|
||||
end
|
||||
|
||||
context 'juridique' do
|
||||
it { is_expected.not_to allow_value(nil).for(:cadre_juridique) }
|
||||
it { is_expected.to allow_value('text').for(:cadre_juridique) }
|
||||
|
|
Loading…
Reference in a new issue