commit
92960b5ad0
88 changed files with 1400 additions and 460 deletions
26
app/assets/stylesheets/sources_particulier_form.scss
Normal file
26
app/assets/stylesheets/sources_particulier_form.scss
Normal file
|
@ -0,0 +1,26 @@
|
|||
@import "constants";
|
||||
|
||||
#sources-particulier-form {
|
||||
h2 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 2 * $default-padding;
|
||||
}
|
||||
|
||||
.explication {
|
||||
padding: $default-padding;
|
||||
|
||||
ul {
|
||||
list-style-type: circle;
|
||||
list-style-position: inside;
|
||||
padding-left: $default-padding;
|
||||
margin-bottom: $default-padding;
|
||||
}
|
||||
}
|
||||
|
||||
.form input[type="checkbox"] {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ class Champs::SiretController < ApplicationController
|
|||
begin
|
||||
etablissement = find_etablissement_with_siret
|
||||
rescue APIEntreprise::API::Error::RequestFailed, APIEntreprise::API::Error::ServiceUnavailable
|
||||
# i18n-tasks-use t('errors.siret_network_error')
|
||||
# i18n-tasks-use t('errors.messages.siret_network_error')
|
||||
return siret_error(:network_error)
|
||||
end
|
||||
if etablissement.nil?
|
||||
|
|
|
@ -2,9 +2,12 @@ module DevisePopulatedResource
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
# During a GET /password/edit, the resource is a brand new object.
|
||||
# This method gives access to the actual resource record, complete with email, relationships, etc.
|
||||
# This method gives access to the actual resource record (if available), complete with email, relationships, etc.
|
||||
#
|
||||
# If the resource can't be found (typically because the reset password token has expired),
|
||||
# returns the default blank record.
|
||||
def populated_resource
|
||||
resource_class.with_reset_password_token(resource.reset_password_token)
|
||||
resource_class.with_reset_password_token(resource.reset_password_token) || resource
|
||||
end
|
||||
|
||||
included do
|
||||
|
|
|
@ -11,7 +11,15 @@ module NewAdministrateur
|
|||
def index
|
||||
@procedure = procedure
|
||||
|
||||
@groupes_instructeurs = paginated_groupe_instructeurs
|
||||
if procedure.routee?
|
||||
@groupes_instructeurs = paginated_groupe_instructeurs
|
||||
@instructeurs = []
|
||||
@available_instructeur_emails = []
|
||||
else
|
||||
@groupes_instructeurs = []
|
||||
@instructeurs = paginated_instructeurs
|
||||
@available_instructeur_emails = available_instructeur_emails
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
|
@ -131,15 +139,17 @@ module NewAdministrateur
|
|||
else
|
||||
|
||||
if instructeurs.present?
|
||||
instructeurs.each do |instructeur|
|
||||
instructeur.assign_to_procedure(procedure)
|
||||
end
|
||||
procedure.defaut_groupe_instructeur.instructeurs << instructeurs
|
||||
flash[:notice] = "Les instructeurs ont bien été affectés à la démarche"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
redirect_to admin_procedure_groupe_instructeur_path(procedure, groupe_instructeur)
|
||||
if procedure.routee?
|
||||
redirect_to admin_procedure_groupe_instructeur_path(procedure, groupe_instructeur)
|
||||
else
|
||||
redirect_to admin_procedure_groupe_instructeurs_path(procedure)
|
||||
end
|
||||
end
|
||||
|
||||
def remove_instructeur
|
||||
|
@ -164,7 +174,12 @@ module NewAdministrateur
|
|||
end
|
||||
end
|
||||
end
|
||||
redirect_to admin_procedure_groupe_instructeur_path(procedure, groupe_instructeur)
|
||||
|
||||
if procedure.routee?
|
||||
redirect_to admin_procedure_groupe_instructeur_path(procedure, groupe_instructeur)
|
||||
else
|
||||
redirect_to admin_procedure_groupe_instructeurs_path(procedure)
|
||||
end
|
||||
end
|
||||
|
||||
def update_routing_criteria_name
|
||||
|
@ -174,6 +189,13 @@ module NewAdministrateur
|
|||
notice: "Le libellé est maintenant « #{procedure.routing_criteria_name} »."
|
||||
end
|
||||
|
||||
def update_routing_enabled
|
||||
procedure.update!(routing_enabled: true)
|
||||
|
||||
redirect_to admin_procedure_groupe_instructeurs_path(procedure),
|
||||
notice: "Le routage est activé."
|
||||
end
|
||||
|
||||
def import
|
||||
if !CSV_ACCEPTED_CONTENT_TYPES.include?(group_csv_file.content_type) && !CSV_ACCEPTED_CONTENT_TYPES.include?(marcel_content_type)
|
||||
flash[:alert] = "Importation impossible : veuillez importer un fichier CSV"
|
||||
|
@ -227,7 +249,11 @@ module NewAdministrateur
|
|||
end
|
||||
|
||||
def groupe_instructeur
|
||||
procedure.groupe_instructeurs.find(params[:id])
|
||||
if params[:id].present?
|
||||
procedure.groupe_instructeurs.find(params[:id])
|
||||
else
|
||||
procedure.defaut_groupe_instructeur
|
||||
end
|
||||
end
|
||||
|
||||
def instructeur_id
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
module NewAdministrateur
|
||||
class JetonParticulierController < AdministrateurController
|
||||
before_action :retrieve_procedure
|
||||
|
||||
def api_particulier
|
||||
end
|
||||
|
||||
def show
|
||||
end
|
||||
|
||||
def update
|
||||
@procedure.api_particulier_token = token
|
||||
|
||||
if @procedure.invalid?
|
||||
flash.now.alert = @procedure.errors.full_messages
|
||||
render :show
|
||||
elsif scopes.empty?
|
||||
flash.now.alert = t('.no_scopes_token')
|
||||
render :show
|
||||
else
|
||||
@procedure.update!(api_particulier_scopes: scopes, api_particulier_sources: {})
|
||||
|
||||
redirect_to admin_procedure_api_particulier_sources_path(procedure_id: @procedure.id),
|
||||
notice: t('.token_ok')
|
||||
end
|
||||
|
||||
rescue APIParticulier::Error::Unauthorized
|
||||
flash.now.alert = t('.not_found_token')
|
||||
render :show
|
||||
rescue APIParticulier::Error::HttpError
|
||||
flash.now.alert = t('.network_error')
|
||||
render :show
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def scopes
|
||||
@scopes ||= APIParticulier::API.new(token).scopes
|
||||
end
|
||||
|
||||
def token
|
||||
params[:procedure][:api_particulier_token]
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,32 @@
|
|||
module NewAdministrateur
|
||||
class SourcesParticulierController < AdministrateurController
|
||||
before_action :retrieve_procedure
|
||||
|
||||
def show
|
||||
@available_sources = sources_service.available_sources
|
||||
end
|
||||
|
||||
def update
|
||||
if @procedure.update(api_particulier_sources: sources_params)
|
||||
redirect_to admin_procedure_api_particulier_sources_path(@procedure), notice: t('.sources_ok')
|
||||
else
|
||||
flash.now.alert = @procedure.errors.full_messages
|
||||
render :show
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def sources_params
|
||||
requested_sources = params
|
||||
.with_defaults(api_particulier_sources: {})
|
||||
.to_unsafe_hash[:api_particulier_sources]
|
||||
|
||||
sources_service.sanitize(requested_sources)
|
||||
end
|
||||
|
||||
def sources_service
|
||||
@sources_service ||= APIParticulier::Services::SourcesService.new(@procedure)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -318,13 +318,13 @@ module Users
|
|||
|
||||
def show_demarche_en_test_banner
|
||||
if @dossier.present? && @dossier.revision.draft?
|
||||
flash.now.alert = t('.test_procedure')
|
||||
flash.now.alert = t('users.dossiers.test_procedure')
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_dossier_can_be_updated
|
||||
if !dossier.can_be_updated_by_user?
|
||||
flash.alert = t('.no_longer_editable')
|
||||
flash.alert = t('users.dossiers.no_longer_editable')
|
||||
redirect_to dossiers_path
|
||||
end
|
||||
end
|
||||
|
@ -425,7 +425,7 @@ module Users
|
|||
end
|
||||
|
||||
def forbidden!
|
||||
flash[:alert] = t('.no_access')
|
||||
flash[:alert] = t('users.dossiers.no_access')
|
||||
redirect_to root_path
|
||||
end
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ module Users
|
|||
if: -> { instructeur_signed_in? }
|
||||
|
||||
def show
|
||||
@waiting_transfers = current_user.dossiers.joins(:transfer).group('dossier_transfers.email').count.to_a
|
||||
end
|
||||
|
||||
def renew_api_token
|
||||
|
@ -27,6 +28,12 @@ module Users
|
|||
redirect_to profil_path
|
||||
end
|
||||
|
||||
def transfer_all_dossiers
|
||||
DossierTransfer.initiate(next_owner_email, current_user.dossiers)
|
||||
flash.notice = t('.new_transfer', count: current_user.dossiers.count, email: next_owner_email)
|
||||
redirect_to profil_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_email_params
|
||||
|
@ -40,5 +47,9 @@ module Users
|
|||
def redirect_if_instructeur
|
||||
redirect_to profil_path
|
||||
end
|
||||
|
||||
def next_owner_email
|
||||
params[:next_owner]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
34
app/lib/api_particulier/api.rb
Normal file
34
app/lib/api_particulier/api.rb
Normal file
|
@ -0,0 +1,34 @@
|
|||
class APIParticulier::API
|
||||
include APIParticulier::Error
|
||||
|
||||
INTROSPECT_RESOURCE_NAME = "introspect"
|
||||
|
||||
TIMEOUT = 20
|
||||
|
||||
def initialize(token)
|
||||
@token = token
|
||||
end
|
||||
|
||||
def scopes
|
||||
get(INTROSPECT_RESOURCE_NAME)[:scopes]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get(resource_name, params = {})
|
||||
url = [API_PARTICULIER_URL, resource_name].join("/")
|
||||
|
||||
response = Typhoeus.get(url,
|
||||
headers: { accept: "application/json", "X-API-Key": @token },
|
||||
params: params,
|
||||
timeout: TIMEOUT)
|
||||
|
||||
if response.success?
|
||||
JSON.parse(response.body, symbolize_names: true)
|
||||
elsif response.code == 401
|
||||
raise Unauthorized.new(response)
|
||||
else
|
||||
raise RequestFailed.new(response)
|
||||
end
|
||||
end
|
||||
end
|
32
app/lib/api_particulier/error.rb
Normal file
32
app/lib/api_particulier/error.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
module APIParticulier
|
||||
module Error
|
||||
class HttpError < ::StandardError
|
||||
def initialize(response)
|
||||
connect_time = response.connect_time
|
||||
curl_message = response.return_message
|
||||
http_error_code = response.code
|
||||
datetime = response.headers.fetch('Date', DateTime.current.inspect)
|
||||
total_time = response.total_time
|
||||
|
||||
uri = URI.parse(response.effective_url)
|
||||
url = "#{uri.host}#{uri.path}"
|
||||
|
||||
msg = <<~TEXT
|
||||
url: #{url}
|
||||
HTTP error code: #{http_error_code}
|
||||
#{response.body}
|
||||
curl message: #{curl_message}
|
||||
total time: #{total_time}
|
||||
connect time: #{connect_time}
|
||||
datetime: #{datetime}
|
||||
TEXT
|
||||
|
||||
super(msg)
|
||||
end
|
||||
end
|
||||
|
||||
class RequestFailed < HttpError; end
|
||||
|
||||
class Unauthorized < HttpError; end
|
||||
end
|
||||
end
|
64
app/lib/api_particulier/services/sources_service.rb
Normal file
64
app/lib/api_particulier/services/sources_service.rb
Normal file
|
@ -0,0 +1,64 @@
|
|||
module APIParticulier
|
||||
module Services
|
||||
class SourcesService
|
||||
def initialize(procedure)
|
||||
@procedure = procedure
|
||||
end
|
||||
|
||||
def available_sources
|
||||
@procedure.api_particulier_scopes
|
||||
.map { |provider_and_scope| raw_scopes[provider_and_scope] }
|
||||
.map { |provider, scope| extract_sources(provider, scope) }
|
||||
.reduce({}) { |acc, el| acc.deep_merge(el) }
|
||||
end
|
||||
|
||||
# Remove sources not available for the procedure
|
||||
def sanitize(requested_sources)
|
||||
requested_sources_a = h_to_a(requested_sources)
|
||||
available_sources_a = h_to_a(available_sources)
|
||||
|
||||
filtered_sources_a = requested_sources_a.intersection(available_sources_a)
|
||||
|
||||
a_to_h(filtered_sources_a)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# { 'cnaf' => { 'scope' => ['a', 'b'] }} => [['cnaf', 'scope', 'a'], ['cnaf', 'scope', 'b']]
|
||||
def h_to_a(h)
|
||||
h.reduce([]) { |acc, (provider, scopes)| scopes.each { |scope, values| values.each { |s, _| acc << [provider, scope, s] } }; acc }
|
||||
end
|
||||
|
||||
# [['cnaf', 'scope', 'a'], ['cnaf', 'scope', 'b']] => { 'cnaf' => { 'scope' => ['a', 'b'] }}
|
||||
def a_to_h(a)
|
||||
h = Hash.new { |h, k| h[k] = Hash.new { |h2, k2| h2[k2] = [] } }
|
||||
|
||||
a.reduce(h) { |acc, (provider, scope, source)| h[provider][scope] << source; acc }
|
||||
end
|
||||
|
||||
def extract_sources(provider, scope)
|
||||
{ provider => { scope => providers[provider][scope] } }
|
||||
end
|
||||
|
||||
def raw_scopes
|
||||
{
|
||||
'cnaf_allocataires' => ['cnaf', 'allocataires'],
|
||||
'cnaf_enfants' => ['cnaf', 'enfants'],
|
||||
'cnaf_adresse' => ['cnaf', 'adresse'],
|
||||
'cnaf_quotient_familial' => ['cnaf', 'quotient_familial']
|
||||
}
|
||||
end
|
||||
|
||||
def providers
|
||||
{
|
||||
'cnaf' => {
|
||||
'allocataires' => ['noms_prenoms', 'date_de_naissance', 'sexe'],
|
||||
'enfants' => ['noms_prenoms', 'date_de_naissance', 'sexe'],
|
||||
'adresse' => ['identite', 'complement_d_identite', 'complement_d_identite_geo', 'numero_et_rue', 'lieu_dit', 'code_postal_et_ville', 'pays'],
|
||||
'quotient_familial' => ['quotient_familial', 'annee', 'mois']
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,22 +3,22 @@ class Helpscout::FormAdapter
|
|||
|
||||
def self.options
|
||||
[
|
||||
[I18n.t(TYPE_INFO, scope: [:support, :question]), TYPE_INFO, FAQ_CONTACTER_SERVICE_EN_CHARGE_URL],
|
||||
[I18n.t(TYPE_PERDU, scope: [:support, :question]), TYPE_PERDU, LISTE_DES_DEMARCHES_URL],
|
||||
[I18n.t(TYPE_INSTRUCTION, scope: [:support, :question]), TYPE_INSTRUCTION, FAQ_OU_EN_EST_MON_DOSSIER_URL],
|
||||
[I18n.t(TYPE_AMELIORATION, scope: [:support, :question]), TYPE_AMELIORATION, FEATURE_UPVOTE_URL],
|
||||
[I18n.t(TYPE_AUTRE, scope: [:support, :question]), TYPE_AUTRE]
|
||||
[I18n.t(:question, scope: [:support, :index, TYPE_INFO]), TYPE_INFO, FAQ_CONTACTER_SERVICE_EN_CHARGE_URL],
|
||||
[I18n.t(:question, scope: [:support, :index, TYPE_PERDU]), TYPE_PERDU, LISTE_DES_DEMARCHES_URL],
|
||||
[I18n.t(:question, scope: [:support, :index, TYPE_INSTRUCTION]), TYPE_INSTRUCTION, FAQ_OU_EN_EST_MON_DOSSIER_URL],
|
||||
[I18n.t(:question, scope: [:support, :index, TYPE_AMELIORATION]), TYPE_AMELIORATION, FEATURE_UPVOTE_URL],
|
||||
[I18n.t(:question, scope: [:support, :index, TYPE_AUTRE]), TYPE_AUTRE]
|
||||
]
|
||||
end
|
||||
|
||||
def self.admin_options
|
||||
[
|
||||
[I18n.t(ADMIN_TYPE_QUESTION, scope: [:supportadmin], app_name: APPLICATION_NAME), ADMIN_TYPE_QUESTION],
|
||||
[I18n.t(ADMIN_TYPE_RDV, scope: [:supportadmin], app_name: APPLICATION_NAME), ADMIN_TYPE_RDV],
|
||||
[I18n.t(ADMIN_TYPE_SOUCIS, scope: [:supportadmin], app_name: APPLICATION_NAME), ADMIN_TYPE_SOUCIS],
|
||||
[I18n.t(ADMIN_TYPE_PRODUIT, scope: [:supportadmin]), ADMIN_TYPE_PRODUIT],
|
||||
[I18n.t(ADMIN_TYPE_DEMANDE_COMPTE, scope: [:supportadmin]), ADMIN_TYPE_DEMANDE_COMPTE],
|
||||
[I18n.t(ADMIN_TYPE_AUTRE, scope: [:supportadmin]), ADMIN_TYPE_AUTRE]
|
||||
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_QUESTION], app_name: APPLICATION_NAME), ADMIN_TYPE_QUESTION],
|
||||
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_RDV], app_name: APPLICATION_NAME), ADMIN_TYPE_RDV],
|
||||
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_SOUCIS], app_name: APPLICATION_NAME), ADMIN_TYPE_SOUCIS],
|
||||
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_PRODUIT]), ADMIN_TYPE_PRODUIT],
|
||||
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_DEMANDE_COMPTE]), ADMIN_TYPE_DEMANDE_COMPTE],
|
||||
[I18n.t(:question, scope: [:support, :admin, ADMIN_TYPE_AUTRE]), ADMIN_TYPE_AUTRE]
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -170,6 +170,8 @@ class DossierMailer < ApplicationMailer
|
|||
|
||||
# This is an override of `default_i18n_subject` method
|
||||
# https://api.rubyonrails.org/v5.0.0/classes/ActionMailer/Base.html#method-i-default_i18n_subject
|
||||
#
|
||||
# i18n-tasks-use t("dossier_mailer.#{action}.subject")
|
||||
def default_i18n_subject(interpolations = {})
|
||||
if interpolations[:state]
|
||||
mailer_scope = self.class.mailer_name.tr('/', '.')
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
class AttestationTemplate < ApplicationRecord
|
||||
include ActionView::Helpers::NumberHelper
|
||||
include TagsSubstitutionConcern
|
||||
include FileValidationConcern
|
||||
|
||||
belongs_to :procedure, optional: false
|
||||
|
||||
|
@ -21,8 +22,10 @@ class AttestationTemplate < ApplicationRecord
|
|||
has_one_attached :signature
|
||||
|
||||
validates :footer, length: { maximum: 190 }
|
||||
validates :logo, content_type: ['image/png', 'image/jpg', 'image/jpeg'], size: { less_than: 1.megabytes }
|
||||
validates :signature, content_type: ['image/png', 'image/jpg', 'image/jpeg'], size: { less_than: 1.megabytes }
|
||||
|
||||
FILE_MAX_SIZE = 1.megabytes
|
||||
validates :logo, content_type: ['image/png', 'image/jpg', 'image/jpeg'], size: file_size_validation(FILE_MAX_SIZE)
|
||||
validates :signature, content_type: ['image/png', 'image/jpg', 'image/jpeg'], size: file_size_validation(FILE_MAX_SIZE)
|
||||
|
||||
DOSSIER_STATE = Dossier.states.fetch(:accepte)
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#
|
||||
class Avis < ApplicationRecord
|
||||
include EmailSanitizableConcern
|
||||
include FileValidationConcern
|
||||
|
||||
belongs_to :dossier, inverse_of: :avis, touch: true, optional: false
|
||||
belongs_to :experts_procedure, optional: false
|
||||
|
@ -27,19 +28,19 @@ class Avis < ApplicationRecord
|
|||
has_one :expert, through: :experts_procedure
|
||||
has_one :procedure, through: :experts_procedure
|
||||
|
||||
FILE_MAX_SIZE = 20.megabytes
|
||||
validates :piece_justificative_file,
|
||||
content_type: AUTHORIZED_CONTENT_TYPES,
|
||||
size: { less_than: 20.megabytes }
|
||||
size: file_size_validation(FILE_MAX_SIZE)
|
||||
|
||||
validates :introduction_file,
|
||||
content_type: AUTHORIZED_CONTENT_TYPES,
|
||||
size: { less_than: 20.megabytes }
|
||||
size: file_size_validation(FILE_MAX_SIZE)
|
||||
|
||||
validates :email, format: { with: Devise.email_regexp, message: "n'est pas valide" }, allow_nil: true
|
||||
validates :claimant, presence: true
|
||||
validates :piece_justificative_file, size: { less_than: 20.megabytes }
|
||||
validates :introduction_file, size: { less_than: 20.megabytes }
|
||||
|
||||
validates :piece_justificative_file, size: file_size_validation(FILE_MAX_SIZE)
|
||||
validates :introduction_file, size: file_size_validation(FILE_MAX_SIZE)
|
||||
before_validation -> { sanitize_email(:email) }
|
||||
|
||||
default_scope { joins(:dossier) }
|
||||
|
|
|
@ -18,10 +18,11 @@
|
|||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::PieceJustificativeChamp < Champ
|
||||
MAX_SIZE = 200.megabytes
|
||||
include FileValidationConcern
|
||||
FILE_MAX_SIZE = 200.megabytes
|
||||
|
||||
validates :piece_justificative_file,
|
||||
size: { less_than: MAX_SIZE },
|
||||
size: file_size_validation(FILE_MAX_SIZE),
|
||||
if: -> { !type_de_champ.skip_pj_validation }
|
||||
|
||||
validates :piece_justificative_file,
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
# type_de_champ_id :integer
|
||||
#
|
||||
class Champs::TitreIdentiteChamp < Champ
|
||||
MAX_SIZE = 20.megabytes
|
||||
include FileValidationConcern
|
||||
FILE_MAX_SIZE = 20.megabytes
|
||||
|
||||
ACCEPTED_FORMATS = [
|
||||
"image/png",
|
||||
|
@ -30,7 +31,7 @@ class Champs::TitreIdentiteChamp < Champ
|
|||
#
|
||||
validates :piece_justificative_file,
|
||||
content_type: ACCEPTED_FORMATS,
|
||||
size: { less_than: MAX_SIZE }
|
||||
size: file_size_validation(FILE_MAX_SIZE)
|
||||
|
||||
def main_value_name
|
||||
:piece_justificative_file
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
# instructeur_id :bigint
|
||||
#
|
||||
class Commentaire < ApplicationRecord
|
||||
include FileValidationConcern
|
||||
|
||||
self.ignored_columns = [:user_id]
|
||||
belongs_to :dossier, inverse_of: :commentaires, touch: true, optional: false
|
||||
|
||||
|
@ -24,9 +26,10 @@ class Commentaire < ApplicationRecord
|
|||
|
||||
validates :body, presence: { message: "ne peut être vide" }
|
||||
|
||||
FILE_MAX_SIZE = 20.megabytes
|
||||
validates :piece_jointe,
|
||||
content_type: AUTHORIZED_CONTENT_TYPES,
|
||||
size: { less_than: 20.megabytes }
|
||||
size: file_size_validation(FILE_MAX_SIZE)
|
||||
|
||||
default_scope { order(created_at: :asc) }
|
||||
scope :updated_since?, -> (date) { where('commentaires.updated_at > ?', date) }
|
||||
|
|
8
app/models/concerns/file_validation_concern.rb
Normal file
8
app/models/concerns/file_validation_concern.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
module FileValidationConcern
|
||||
extend ActiveSupport::Concern
|
||||
class_methods do
|
||||
def file_size_validation(file_max_size = 200.megabytes)
|
||||
{ less_than: file_max_size, message: I18n.t('errors.messages.file_size_out_of_range', file_size_limit: ActiveSupport::NumberHelper.number_to_human_size(file_max_size)) }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -73,19 +73,15 @@ class Instructeur < ApplicationRecord
|
|||
end
|
||||
|
||||
def assign_to_procedure(procedure)
|
||||
begin
|
||||
assign_to.create({
|
||||
procedure: procedure,
|
||||
groupe_instructeur: procedure.defaut_groupe_instructeur
|
||||
})
|
||||
true
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
false
|
||||
if !procedure.defaut_groupe_instructeur.in?(groupe_instructeurs)
|
||||
groupe_instructeurs << procedure.defaut_groupe_instructeur
|
||||
end
|
||||
end
|
||||
|
||||
def remove_from_procedure(procedure)
|
||||
!!(procedure.defaut_groupe_instructeur.in?(groupe_instructeurs) && groupe_instructeurs.destroy(procedure.defaut_groupe_instructeur))
|
||||
if procedure.defaut_groupe_instructeur.in?(groupe_instructeurs)
|
||||
groupe_instructeurs.destroy(procedure.defaut_groupe_instructeur)
|
||||
end
|
||||
end
|
||||
|
||||
def last_week_overview
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
# aasm_state :string default("brouillon")
|
||||
# allow_expert_review :boolean default(TRUE), not null
|
||||
# api_entreprise_token :string
|
||||
# api_particulier_scopes :text default([]), is an Array
|
||||
# api_particulier_sources :jsonb
|
||||
# ask_birthday :boolean default(FALSE), not null
|
||||
# auto_archive_on :date
|
||||
# cadre_juridique :string
|
||||
|
@ -33,6 +35,7 @@
|
|||
# path :string not null
|
||||
# published_at :datetime
|
||||
# routing_criteria_name :text default("Votre ville")
|
||||
# routing_enabled :boolean
|
||||
# test_started_at :datetime
|
||||
# unpublished_at :datetime
|
||||
# web_hook_url :string
|
||||
|
@ -49,6 +52,7 @@
|
|||
class Procedure < ApplicationRecord
|
||||
include ProcedureStatsConcern
|
||||
include EncryptableConcern
|
||||
include FileValidationConcern
|
||||
|
||||
include Discard::Model
|
||||
self.discard_column = :hidden_at
|
||||
|
@ -237,6 +241,8 @@ class Procedure < ApplicationRecord
|
|||
validates :duree_conservation_dossiers_dans_ds, allow_nil: false, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: MAX_DUREE_CONSERVATION }
|
||||
validates :duree_conservation_dossiers_hors_ds, allow_nil: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
|
||||
validates_with MonAvisEmbedValidator
|
||||
|
||||
FILE_MAX_SIZE = 20.megabytes
|
||||
validates :notice, content_type: [
|
||||
"application/msword",
|
||||
"application/pdf",
|
||||
|
@ -249,7 +255,7 @@ class Procedure < ApplicationRecord
|
|||
"image/jpg",
|
||||
"image/png",
|
||||
"text/plain"
|
||||
], size: { less_than: 20.megabytes }, if: -> { new_record? || created_at > Date.new(2020, 2, 28) }
|
||||
], size: file_size_validation(FILE_MAX_SIZE), if: -> { new_record? || created_at > Date.new(2020, 2, 28) }
|
||||
|
||||
validates :deliberation, content_type: [
|
||||
"application/msword",
|
||||
|
@ -260,14 +266,15 @@ class Procedure < ApplicationRecord
|
|||
"image/jpg",
|
||||
"image/png",
|
||||
"text/plain"
|
||||
], size: { less_than: 20.megabytes }, if: -> { new_record? || created_at > Date.new(2020, 4, 29) }
|
||||
], size: file_size_validation(FILE_MAX_SIZE), if: -> { new_record? || created_at > Date.new(2020, 4, 29) }
|
||||
|
||||
LOGO_MAX_SIZE = 5.megabytes
|
||||
validates :logo, content_type: ['image/png', 'image/jpg', 'image/jpeg'],
|
||||
size: { less_than: 5.megabytes },
|
||||
size: file_size_validation(LOGO_MAX_SIZE),
|
||||
if: -> { new_record? || created_at > Date.new(2020, 11, 13) }
|
||||
|
||||
validates :api_entreprise_token, jwt_token: true, allow_blank: true
|
||||
validates :api_particulier_token, format: { with: /\A[A-Za-z0-9\-_=.]{15,}\z/, message: "n'est pas un jeton valide" }, allow_blank: true
|
||||
validates :api_particulier_token, format: { with: /\A[A-Za-z0-9\-_=.]{15,}\z/ }, allow_blank: true
|
||||
|
||||
before_save :update_juridique_required
|
||||
after_initialize :ensure_path_exists
|
||||
|
@ -447,6 +454,7 @@ class Procedure < ApplicationRecord
|
|||
procedure.administrateurs = [admin]
|
||||
procedure.api_entreprise_token = nil
|
||||
procedure.encrypted_api_particulier_token = nil
|
||||
procedure.api_particulier_scopes = []
|
||||
else
|
||||
procedure.administrateurs = administrateurs
|
||||
end
|
||||
|
@ -625,7 +633,7 @@ class Procedure < ApplicationRecord
|
|||
end
|
||||
|
||||
def routee?
|
||||
groupe_instructeurs.size > 1
|
||||
routing_enabled? || groupe_instructeurs.size > 1
|
||||
end
|
||||
|
||||
def defaut_groupe_instructeur_for_new_dossier
|
||||
|
|
|
@ -48,14 +48,13 @@
|
|||
- if matching_archive.status == 'generated' && matching_archive.file.attached?
|
||||
= link_to url_for(matching_archive.file), class: 'button primary' do
|
||||
%span.icon.download-white
|
||||
= t(:archive_ready_html, generated_period: time_ago_in_words(matching_archive.updated_at), scope: [:instructeurs, :procedure])
|
||||
= t(:archive_ready_html, scope: [:instructeurs, :procedure], generated_period: time_ago_in_words(matching_archive.updated_at))
|
||||
- else
|
||||
%span.icon.retry
|
||||
= t(:archive_pending_html, created_period: time_ago_in_words(matching_archive.created_at), scope: [:instructeurs, :procedure])
|
||||
= t(:archive_pending_html, scope: [:instructeurs, :procedure], created_period: time_ago_in_words(matching_archive.created_at))
|
||||
- elsif weight < 1.gigabyte
|
||||
= link_to instructeur_archives_path(@procedure, type:'monthly', month: month.strftime('%Y-%m')), method: :post, class: "button" do
|
||||
%span.icon.new-folder
|
||||
Demander la création
|
||||
- else
|
||||
Archive trop volumineuse
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ as defined by the routes in the `admin/` namespace
|
|||
|
||||
<%= link_to "Delayed Jobs", manager_delayed_job_path, class: "navigation__link" %>
|
||||
<%= link_to "Features", manager_flipper_path, class: "navigation__link" %>
|
||||
<% if Rails.env.production? %>
|
||||
<% if Rails.env.production? && ENV['SENDINBLUE_ENABLED'] == 'enabled'%>
|
||||
<%= link_to "Sendinblue", ENV.fetch("SENDINBLUE_LOGIN_URL"), class: "navigation__link", target: '_blank' %>
|
||||
<% end %>
|
||||
</nav>
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
.card
|
||||
= form_for procedure,
|
||||
url: { action: :update_routing_criteria_name },
|
||||
html: { class: 'form' } do |f|
|
||||
|
||||
= f.label :routing_criteria_name do
|
||||
Libellé du routage
|
||||
%p.notice Ce texte apparaitra sur le formulaire usager comme le libellé d’une liste
|
||||
= f.text_field :routing_criteria_name, placeholder: 'ex. Votre ville', required: true
|
||||
= f.submit 'Renommer', class: 'button primary send'
|
||||
|
||||
.card
|
||||
.card-title Gestion des Groupes
|
||||
|
||||
= form_for :groupe_instructeur, html: { class: 'form' } do |f|
|
||||
= f.label :label do
|
||||
Ajouter un groupe
|
||||
%p.notice Ce groupe sera un choix de la liste « #{procedure.routing_criteria_name} » .
|
||||
= f.text_field :label, placeholder: 'ex. Ville de Bordeaux', required: true
|
||||
= f.submit 'Ajouter le groupe', class: 'button primary send'
|
||||
|
||||
- csv_max_size = NewAdministrateur::GroupeInstructeursController::CSV_MAX_SIZE
|
||||
= form_tag import_admin_procedure_groupe_instructeurs_path(procedure), method: :post, multipart: true, class: "mt-4 form" do
|
||||
= label_tag "Importer par fichier CSV"
|
||||
%p.notice Le fichier csv doit comporter 2 colonnes (Groupe, Email) et être séparé par des virgules. L'import n'écrase pas les groupes et les instructeurs existants.
|
||||
%p.notice Le poids du fichier doit être inférieur à #{number_to_human_size(csv_max_size)}
|
||||
%p.mt-2.mb-2= link_to "Télécharger l'exemple de fichier CSV", "/import-groupe-test.csv"
|
||||
= file_field_tag :group_csv_file, required: true, accept: 'text/csv', size: "1"
|
||||
= submit_tag "Importer le fichier", class: 'button primary send', data: { disable_with: "Envoi..." }
|
||||
|
||||
%table.table.mt-2
|
||||
%thead
|
||||
%tr
|
||||
%th{ colspan: 2 }= t(".existing_groupe", count: groupes_instructeurs.total_count)
|
||||
%tbody
|
||||
- groupes_instructeurs.each do |group|
|
||||
%tr
|
||||
%td= group.label
|
||||
%td.actions= link_to "voir", admin_procedure_groupe_instructeur_path(procedure, group)
|
||||
- if groupes_instructeurs.many?
|
||||
- if group.dossiers.empty?
|
||||
%td.actions
|
||||
= link_to admin_procedure_groupe_instructeur_path(procedure, group), { method: :delete, class: 'button', data: { confirm: "Êtes-vous sûr de vouloir supprimer le groupe « #{group.label} » ?" }} do
|
||||
%span.icon.delete
|
||||
supprimer ce groupe
|
||||
- else
|
||||
%td.actions
|
||||
= link_to reaffecter_dossiers_admin_procedure_groupe_instructeur_path(procedure, group), class: 'button', title:'Réaffecter les dossiers à un autre groupe afin de pouvoir le supprimer' do
|
||||
%span.icon.follow
|
||||
déplacer les dossiers
|
||||
|
||||
= paginate groupes_instructeurs
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
.card
|
||||
.card-title Affectation des instructeurs
|
||||
= form_for :instructeur, url: { action: :add_instructeur, id: groupe_instructeur.id }, html: { class: 'form' } do |f|
|
||||
.instructeur-wrapper
|
||||
- if !procedure.routee?
|
||||
%p.notice Entrez les adresses email des instructeurs que vous souhaitez affecter à cette démarche
|
||||
- hidden_field_id = SecureRandom.uuid
|
||||
= hidden_field_tag :emails, nil, data: { uuid: hidden_field_id }
|
||||
= react_component("ComboMultipleDropdownList",
|
||||
options: available_instructeur_emails, selected: [], disabled: [],
|
||||
hiddenFieldId: hidden_field_id,
|
||||
label: 'email instructeur',
|
||||
acceptNewValues: true)
|
||||
|
||||
= f.submit 'Affecter', class: 'button primary send'
|
||||
|
||||
%table.table.mt-2
|
||||
%thead
|
||||
%tr
|
||||
%th{ colspan: 2 }= t('.assigned_instructeur', count: instructeurs.count)
|
||||
%tbody
|
||||
- instructeurs.each do |instructeur|
|
||||
%tr
|
||||
%td
|
||||
%span.icon.person
|
||||
#{instructeur.email}
|
||||
|
||||
- confirmation_message = procedure.routee? ? "Êtes-vous sûr de vouloir retirer l’instructeur « #{instructeur.email} » du groupe « #{groupe_instructeur.label} » ?" : "Êtes-vous sûr de vouloir retirer l’instructeur « #{instructeur.email} » de la démarche ?"
|
||||
%td.actions= button_to 'retirer',
|
||||
{ action: :remove_instructeur, id: groupe_instructeur.id },
|
||||
{ method: :delete,
|
||||
data: { confirm: confirmation_message },
|
||||
params: { instructeur: { id: instructeur.id }},
|
||||
class: 'button' }
|
||||
|
||||
= paginate instructeurs
|
|
@ -0,0 +1,5 @@
|
|||
.card
|
||||
.card-title Routage
|
||||
%p.notice= t('.notice_html')
|
||||
|
||||
= link_to 'Activer le routage', update_routing_enabled_admin_procedure_groupe_instructeurs_path(procedure), class: 'button primary', method: 'patch'
|
|
@ -1,57 +1,22 @@
|
|||
= render partial: 'new_administrateur/breadcrumbs',
|
||||
locals: { steps: [link_to('Démarches', admin_procedures_path),
|
||||
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
|
||||
'Groupes d’instructeurs'] }
|
||||
- if @procedure.routee?
|
||||
= render partial: 'new_administrateur/breadcrumbs',
|
||||
locals: { steps: [link_to('Démarches', admin_procedures_path),
|
||||
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
|
||||
'Groupes d’instructeurs'] }
|
||||
- else
|
||||
= render partial: 'new_administrateur/breadcrumbs',
|
||||
locals: { steps: [link_to('Démarches', admin_procedures_path),
|
||||
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
|
||||
'Instructeurs'] }
|
||||
|
||||
.container.groupe-instructeur
|
||||
.card
|
||||
= form_for @procedure,
|
||||
url: { action: :update_routing_criteria_name },
|
||||
html: { class: 'form' } do |f|
|
||||
- if @procedure.routee?
|
||||
= render partial: 'new_administrateur/groupe_instructeurs/edit', locals: { procedure: @procedure, groupes_instructeurs: @groupes_instructeurs }
|
||||
- else
|
||||
= render partial: 'new_administrateur/groupe_instructeurs/routing', locals: { procedure: @procedure }
|
||||
= render partial: 'new_administrateur/groupe_instructeurs/instructeurs',
|
||||
locals: { procedure: @procedure,
|
||||
groupe_instructeur: @procedure.defaut_groupe_instructeur,
|
||||
instructeurs: @instructeurs,
|
||||
available_instructeur_emails: @available_instructeur_emails }
|
||||
|
||||
= f.label :routing_criteria_name do
|
||||
Libellé du routage
|
||||
%p.notice Ce texte apparaitra sur le formulaire usager comme le libellé d’une liste
|
||||
= f.text_field :routing_criteria_name, placeholder: 'ex. Votre ville', required: true
|
||||
= f.submit 'Renommer', class: 'button primary send'
|
||||
|
||||
.card
|
||||
.card-title Gestion des Groupes
|
||||
|
||||
= form_for :groupe_instructeur, html: { class: 'form' } do |f|
|
||||
= f.label :label do
|
||||
Ajouter un groupe
|
||||
%p.notice Ce groupe sera un choix de la liste « #{@procedure.routing_criteria_name} » .
|
||||
= f.text_field :label, placeholder: 'ex. Ville de Bordeaux', required: true
|
||||
= f.submit 'Ajouter le groupe', class: 'button primary send'
|
||||
|
||||
- csv_max_size = NewAdministrateur::GroupeInstructeursController::CSV_MAX_SIZE
|
||||
= form_tag import_admin_procedure_groupe_instructeurs_path(@procedure), method: :post, multipart: true, class: "mt-4 form" do
|
||||
= label_tag "Importer par fichier CSV"
|
||||
%p.notice Le fichier csv doit comporter 2 colonnes (Groupe, Email) et être séparé par des virgules. L'import n'écrase pas les groupes et les instructeurs existants.
|
||||
%p.notice Le poids du fichier doit être inférieur à #{number_to_human_size(csv_max_size)}
|
||||
%p.mt-2.mb-2= link_to "Télécharger l'exemple de fichier CSV", "/import-groupe-test.csv"
|
||||
= file_field_tag :group_csv_file, required: true, accept: 'text/csv', size: "1"
|
||||
= submit_tag "Importer le fichier", class: 'button primary send', data: { disable_with: "Envoi..." }
|
||||
|
||||
%table.table.mt-2
|
||||
%thead
|
||||
%tr
|
||||
%th{ colspan: 2 }= t(".existing_groupe", count: @groupes_instructeurs.total_count)
|
||||
%tbody
|
||||
- @groupes_instructeurs.each do |group|
|
||||
%tr
|
||||
%td= group.label
|
||||
%td.actions= link_to "voir", admin_procedure_groupe_instructeur_path(@procedure, group)
|
||||
- if @groupes_instructeurs.many?
|
||||
- if group.dossiers.empty?
|
||||
%td.actions
|
||||
= link_to admin_procedure_groupe_instructeur_path(@procedure, group), { method: :delete, class: 'button', data: { confirm: "Êtes-vous sûr de vouloir supprimer le groupe « #{group.label} » ?" }} do
|
||||
%span.icon.delete
|
||||
supprimer ce groupe
|
||||
- else
|
||||
%td.actions
|
||||
= link_to reaffecter_dossiers_admin_procedure_groupe_instructeur_path(@procedure, group), class: 'button', title:'Réaffecter les dossiers à un autre groupe afin de pouvoir le supprimer' do
|
||||
%span.icon.follow
|
||||
déplacer les dossiers
|
||||
= paginate @groupes_instructeurs
|
||||
|
|
|
@ -1,55 +1,14 @@
|
|||
|
||||
- if feature_enabled?(:administrateur_routage)
|
||||
= render partial: 'new_administrateur/breadcrumbs',
|
||||
locals: { steps: [link_to('Démarches', admin_procedures_path),
|
||||
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
|
||||
link_to('Groupes d’instructeurs', admin_procedure_groupe_instructeurs_path(@procedure)),
|
||||
@groupe_instructeur.label] }
|
||||
- else
|
||||
= render partial: 'new_administrateur/breadcrumbs',
|
||||
locals: { steps: [link_to('Démarches', admin_procedures_path),
|
||||
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
|
||||
'Instructeurs'] }
|
||||
= render partial: 'new_administrateur/breadcrumbs',
|
||||
locals: { steps: [link_to('Démarches', admin_procedures_path),
|
||||
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
|
||||
link_to('Groupes d’instructeurs', admin_procedure_groupe_instructeurs_path(@procedure)),
|
||||
@groupe_instructeur.label] }
|
||||
|
||||
.container.groupe-instructeur
|
||||
|
||||
- if feature_enabled?(:administrateur_routage)
|
||||
= render partial: 'new_administrateur/groups_header'
|
||||
|
||||
.card
|
||||
.card-title Affectation des instructeurs
|
||||
= form_for :instructeur,
|
||||
url: { action: :add_instructeur },
|
||||
html: { class: 'form' } do |f|
|
||||
|
||||
.instructeur-wrapper
|
||||
- if !@procedure.routee?
|
||||
%p.notice Entrez les adresses email des instructeurs que vous souhaitez affecter à cette démarche
|
||||
- hidden_field_id = SecureRandom.uuid
|
||||
= hidden_field_tag :emails, nil, data: { uuid: hidden_field_id }
|
||||
= react_component("ComboMultipleDropdownList",
|
||||
options: @available_instructeur_emails, selected: [], disabled: [],
|
||||
hiddenFieldId: hidden_field_id,
|
||||
label: 'email instructeur',
|
||||
acceptNewValues: true)
|
||||
|
||||
= f.submit 'Affecter', class: 'button primary send'
|
||||
|
||||
%table.table.mt-2
|
||||
%thead
|
||||
%tr
|
||||
%th{ colspan: 2 }= t('.assigned_instructeur', count: @instructeurs.count)
|
||||
%tbody
|
||||
- @instructeurs.each do |instructeur|
|
||||
%tr
|
||||
%td
|
||||
%span.icon.person
|
||||
#{instructeur.email}
|
||||
%td.actions= button_to 'retirer',
|
||||
{ action: :remove_instructeur },
|
||||
{ method: :delete,
|
||||
data: { confirm: feature_enabled?(:administrateur_routage) ? "Êtes-vous sûr de vouloir retirer l’instructeur « #{instructeur.email} » du groupe « #{@groupe_instructeur.label} » ?" : "Êtes-vous sûr de vouloir retirer l’instructeur « #{instructeur.email} » de la démarche ?" },
|
||||
params: { instructeur: { id: instructeur.id }},
|
||||
class: 'button' }
|
||||
|
||||
= paginate @instructeurs
|
||||
= render partial: 'new_administrateur/groups_header'
|
||||
= render partial: 'new_administrateur/groupe_instructeurs/instructeurs',
|
||||
locals: { procedure: @procedure,
|
||||
groupe_instructeur: @groupe_instructeur,
|
||||
instructeurs: @instructeurs,
|
||||
available_instructeur_emails: @available_instructeur_emails }
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
= render partial: 'new_administrateur/breadcrumbs',
|
||||
locals: { steps: [link_to('Démarches', admin_procedures_path),
|
||||
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
|
||||
Procedure.human_attribute_name(:jeton_api_particulier)] }
|
||||
|
||||
.container
|
||||
.flex
|
||||
= link_to admin_procedure_api_particulier_jeton_path, class: 'card-admin' do
|
||||
- if @procedure.api_particulier_token.blank?
|
||||
%div
|
||||
%span.icon.clock
|
||||
%p.card-admin-status-todo= t('.needs_configuration')
|
||||
- else
|
||||
%div
|
||||
%span.icon.accept
|
||||
%p.card-admin-status-accept= t('.already_configured')
|
||||
%div
|
||||
%p.card-admin-title
|
||||
= Procedure.human_attribute_name(:jeton_api_particulier)
|
||||
%p.button= t('views.shared.actions.edit')
|
||||
|
||||
- if @procedure.api_particulier_scopes.present?
|
||||
= link_to admin_procedure_api_particulier_sources_path, class: 'card-admin' do
|
||||
- if @procedure.api_particulier_token.blank?
|
||||
%div
|
||||
%span.icon.clock
|
||||
%p.card-admin-status-todo= t('.needs_configuration')
|
||||
- else
|
||||
%div
|
||||
%span.icon.accept
|
||||
%p.card-admin-status-accept= t('.already_configured')
|
||||
%div
|
||||
%p.card-admin-title= t('new_administrateur.sources_particulier.show.data_sources')
|
||||
%p.button= t('views.shared.actions.edit')
|
|
@ -0,0 +1,22 @@
|
|||
= render partial: 'new_administrateur/breadcrumbs',
|
||||
locals: { steps: [link_to('Démarches', admin_procedures_path),
|
||||
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
|
||||
link_to(Procedure.human_attribute_name(:jeton_api_particulier), admin_procedure_api_particulier_path(@procedure)),
|
||||
'Jeton'] }
|
||||
|
||||
.container
|
||||
%h1.page-title
|
||||
= t('.configure_token')
|
||||
|
||||
.container
|
||||
%h1
|
||||
= form_with model: @procedure, url: admin_procedure_api_particulier_jeton_path, local: true, html: { class: 'form' } do |f|
|
||||
%p.explication
|
||||
= t('.api_particulier_description_html', app_name: APPLICATION_NAME)
|
||||
|
||||
= f.label :api_particulier_token
|
||||
.desc.mb-2
|
||||
%p= t('.token_description')
|
||||
= f.password_field :api_particulier_token, class: 'form-control', required: :required
|
||||
.text-right
|
||||
= f.button t('views.shared.actions.save'), class: 'button primary send'
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
.admin-procedures-list-row.actions.flex.justify-between
|
||||
%div
|
||||
- if feature_enabled?(:administrateur_routage)
|
||||
- if procedure.routee?
|
||||
%span.icon.person
|
||||
%span.badge.baseline= procedure.groupe_instructeurs.count
|
||||
- else
|
||||
|
|
|
@ -16,33 +16,39 @@
|
|||
%li.mb-1= t("update_description#{postfix}", label: change[:label], to: change[:to], scope: [:new_administrateur, :revision_changes])
|
||||
- when :mandatory
|
||||
- if change[:from] == false
|
||||
%li.mb-1= t(:enabled, label: change[:label], scope: [:new_administrateur, :revision_changes, "update_mandatory#{postfix}"])
|
||||
-# i18n-tasks-use t('new_administrateur.revision_changes.update_mandatory.enabled')
|
||||
-# i18n-tasks-use t('new_administrateur.revision_changes.update_mandatory_private.enabled')
|
||||
%li.mb-1= t("new_administrateur.revision_changes.update_mandatory#{postfix}.enabled", label: change[:label])
|
||||
- else
|
||||
%li.mb-1= t(:disabled, label: change[:label], scope: [:new_administrateur, :revision_changes, "update_mandatory#{postfix}"])
|
||||
-# i18n-tasks-use t('new_administrateur.revision_changes.update_mandatory.disabled')
|
||||
-# i18n-tasks-use t('new_administrateur.revision_changes.update_mandatory_private.disabled')
|
||||
%li.mb-1= t("new_administrateur.revision_changes.update_mandatory#{postfix}.disabled", label: change[:label])
|
||||
- when :piece_justificative_template
|
||||
%li.mb-1= t("update_piece_justificative_template#{postfix}", label: change[:label], scope: [:new_administrateur, :revision_changes])
|
||||
-# i18n-tasks-use t('new_administrateur.revision_changes.update_piece_justificative_template')
|
||||
-# i18n-tasks-use t('new_administrateur.revision_changes.update_piece_justificative_template_private')
|
||||
%li.mb-1= t("new_administrateur.revision_changes.update_piece_justificative_template#{postfix}", label: change[:label])
|
||||
- when :drop_down_options
|
||||
- added = change[:to].sort - change[:from].sort
|
||||
- removed = change[:from].sort - change[:to].sort
|
||||
%li.mb-1
|
||||
= t("update_drop_down_options#{postfix}", label: change[:label], scope: [:new_administrateur, :revision_changes])
|
||||
= t("update_drop_down_options#{postfix}", scope: [:new_administrateur, :revision_changes], label: change[:label])
|
||||
%ul
|
||||
- if added.present?
|
||||
%li= t(:add_option, items: added.map{ |term| "« #{term.strip} »" }.join(", "), scope: [:new_administrateur, :revision_changes])
|
||||
%li= t(:add_option, scope: [:new_administrateur, :revision_changes], items: added.map{ |term| "« #{term.strip} »" }.join(", "))
|
||||
- if removed.present?
|
||||
%li= t(:remove_option, items: removed.map{ |term| "« #{term.strip} »" }.join(", "), scope: [:new_administrateur, :revision_changes])
|
||||
%li= t(:remove_option, scope: [:new_administrateur, :revision_changes], items: removed.map{ |term| "« #{term.strip} »" }.join(", "))
|
||||
- when :carte_layers
|
||||
- added = change[:to].sort - change[:from].sort
|
||||
- removed = change[:from].sort - change[:to].sort
|
||||
%li.mb-1
|
||||
= t("update_carte_layers#{postfix}", label: change[:label], scope: [:new_administrateur, :revision_changes])
|
||||
= t("update_carte_layers#{postfix}", scope: [:new_administrateur, :revision_changes], label: change[:label])
|
||||
%ul
|
||||
- if added.present?
|
||||
%li= t(:add_option, items: added.map{ |term| "« #{t(term, scope: [:new_administrateur, :carte_layers])} »" }.join(", "), scope: [:new_administrateur, :revision_changes])
|
||||
%li= t(:add_option, scope: [:new_administrateur, :revision_changes], items: added.map{ |term| "« #{t(term, scope: [:new_administrateur, :carte_layers])} »" }.join(", "))
|
||||
- if removed.present?
|
||||
%li= t(:remove_option, items: removed.map{ |term| "« #{t(term, scope: [:new_administrateur, :carte_layers])} »" }.join(", "), scope: [:new_administrateur, :revision_changes])
|
||||
%li= t(:remove_option, scope: [:new_administrateur, :revision_changes], items: removed.map{ |term| "« #{t(term, scope: [:new_administrateur, :carte_layers])} »" }.join(", "))
|
||||
- move_changes, move_private_changes = changes.filter { |change| change[:op] == :move }.partition { |change| !change[:private] }
|
||||
- if move_changes.size != 0
|
||||
%li.mb-1= t(:move, count: move_changes.size, scope: [:new_administrateur, :revision_changes])
|
||||
%li.mb-1= t(:move, scope: [:new_administrateur, :revision_changes], count: move_changes.size)
|
||||
- if move_private_changes.size != 0
|
||||
%li.mb-1= t(:move_private, count: move_private_changes.size, scope: [:new_administrateur, :revision_changes])
|
||||
%li.mb-1= t(:move_private, scope: [:new_administrateur, :revision_changes], count: move_private_changes.size)
|
||||
|
|
|
@ -107,14 +107,8 @@
|
|||
%p.card-admin-subtitle Gestion de la démarche
|
||||
%p.button Modifier
|
||||
|
||||
|
||||
- if feature_enabled?(:administrateur_routage)
|
||||
- instructeur_link = admin_procedure_groupe_instructeurs_path(@procedure)
|
||||
- else
|
||||
- instructeur_link = admin_procedure_groupe_instructeur_path(@procedure, @procedure.defaut_groupe_instructeur)
|
||||
|
||||
= link_to instructeur_link, id: 'groupe-instructeurs', class: 'card-admin' do
|
||||
- if feature_enabled?(:administrateur_routage) || @procedure.instructeurs.count > 1
|
||||
= link_to admin_procedure_groupe_instructeurs_path(@procedure), id: 'groupe-instructeurs', class: 'card-admin' do
|
||||
- if @procedure.routee? || @procedure.instructeurs.count > 1
|
||||
%div
|
||||
%span.icon.accept
|
||||
%p.card-admin-status-accept Validé
|
||||
|
@ -124,12 +118,12 @@
|
|||
%p.card-admin-status-todo À faire
|
||||
%div
|
||||
%p.card-admin-title
|
||||
- if feature_enabled?(:administrateur_routage)
|
||||
- if @procedure.routee?
|
||||
%span.badge.baseline= @procedure.groupe_instructeurs.count
|
||||
- else
|
||||
%span.badge.baseline= @procedure.instructeurs.count
|
||||
|
||||
= feature_enabled?(:administrateur_routage) ? "Groupe Instructeurs" : "#{"Instructeur".pluralize(@procedure.instructeurs.count)}"
|
||||
= @procedure.routee? ? "Groupe Instructeurs" : "#{"Instructeur".pluralize(@procedure.instructeurs.count)}"
|
||||
%p.card-admin-subtitle Suivi des dossiers
|
||||
%p.button Modifier
|
||||
|
||||
|
@ -194,10 +188,25 @@
|
|||
%span.icon.clock
|
||||
%p.card-admin-status-todo À configurer
|
||||
%div
|
||||
%p.card-admin-title Jeton
|
||||
%p.card-admin-title Jeton Entreprise
|
||||
%p.card-admin-subtitle Configurer le jeton API entreprise
|
||||
%p.button Modifier
|
||||
|
||||
- if feature_enabled?(:api_particulier)
|
||||
= link_to admin_procedure_api_particulier_path(@procedure), class: 'card-admin' do
|
||||
- if @procedure.api_particulier_token.present?
|
||||
%div
|
||||
%span.icon.accept
|
||||
%p.card-admin-status-accept= t('.ready')
|
||||
- else
|
||||
%div
|
||||
%span.icon.clock
|
||||
%p.card-admin-status-todo= t('.needs_configuration')
|
||||
%div
|
||||
%p.card-admin-title= Procedure.human_attribute_name(:api_particulier_token)
|
||||
%p.card-admin-subtitle= t('.configure_api_particulier_token')
|
||||
%p.button= t('views.shared.actions.edit')
|
||||
|
||||
= link_to monavis_admin_procedure_path(@procedure), class: 'card-admin' do
|
||||
- if @procedure.monavis_embed.present?
|
||||
%div
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
= render partial: 'new_administrateur/breadcrumbs',
|
||||
locals: { steps: [link_to('Démarches', admin_procedures_path),
|
||||
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
|
||||
link_to(Procedure.human_attribute_name(:jeton_api_particulier), admin_procedure_api_particulier_path(@procedure)),
|
||||
t('.data_sources')] }
|
||||
|
||||
.container
|
||||
%h1.page-title= t('.title')
|
||||
|
||||
.container#sources-particulier-form.mb-2
|
||||
= form_with model: @procedure, url: admin_procedure_api_particulier_sources_path, local: true, html: { class: 'form' } do |f|
|
||||
.explication= t('.explication_html')
|
||||
|
||||
- @available_sources.each do |provider_key, scopes|
|
||||
%h2.header-section= t("api_particulier.providers.#{provider_key}.libelle")
|
||||
|
||||
- scopes.each do |scope_key, sources|
|
||||
%h3.explication-libelle= t("api_particulier.providers.#{provider_key}.scopes.#{scope_key}.libelle")
|
||||
%ul.procedure-admin-api-particulier-sources
|
||||
- sources.each do |source_key, enabled_hash|
|
||||
- enabled = (@procedure.api_particulier_sources.dig(provider_key, scope_key)&.include?(source_key)).present?
|
||||
%li
|
||||
%label
|
||||
= check_box_tag "api_particulier_sources[#{provider_key}][#{scope_key}][]", "#{source_key}", enabled
|
||||
#{t("api_particulier.providers.#{provider_key}.scopes.#{scope_key}.#{source_key}")}
|
||||
|
||||
.text-right
|
||||
= f.button t('views.shared.actions.save'), class: 'button primary send'
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
- when :network_error
|
||||
= t('errors.messages.siret_network_error')
|
||||
-# i18n-tasks-use t('errors.messages.siret_network_error')
|
||||
|
||||
- else
|
||||
- if siret.present? && siret == etablissement&.siret
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
%tbody
|
||||
- if etablissement.diffusable_commercialement == false && profile != 'instructeur'
|
||||
%tr
|
||||
%td= t('warning_for_private_info', etablissement: raison_sociale_or_name(etablissement), scope: 'views.shared.dossiers.identite_entreprise')
|
||||
%td= t('warning_for_private_info', scope: 'views.shared.dossiers.identite_entreprise', etablissement: raison_sociale_or_name(etablissement))
|
||||
- else
|
||||
%tr
|
||||
%th.libelle Dénomination :
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
Sélectionnez une des valeurs
|
||||
%label
|
||||
= form.radio_button :value, Individual::GENDER_FEMALE
|
||||
= t(Individual::GENDER_FEMALE, scope: 'activerecord.attributes.individual.gender_options')
|
||||
= Individual.human_attribute_name('gender.female')
|
||||
|
||||
%label
|
||||
= form.radio_button :value, Individual::GENDER_MALE
|
||||
= t('activerecord.attributes.individual.gender_options')[Individual::GENDER_MALE.to_sym] # GENDER_MALE contains a point letter
|
||||
= Individual.human_attribute_name('gender.male')
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
#contact-form
|
||||
.container
|
||||
%h1.new-h1
|
||||
= t('contact_team', scope: [:supportadmin])
|
||||
= t('.contact_team')
|
||||
|
||||
.description
|
||||
= t('admin_intro_html', scope: [:supportadmin], contact_path: contact_path)
|
||||
= t('.admin_intro_html', contact_path: contact_path)
|
||||
%br
|
||||
%p.mandatory-explanation= t('asterisk_html', scope: [:utils])
|
||||
|
||||
|
@ -14,19 +14,19 @@
|
|||
- if !user_signed_in?
|
||||
.contact-champ
|
||||
= label_tag :email do
|
||||
= t('pro_mail', scope: [:supportadmin])
|
||||
= t('.pro_mail')
|
||||
%span.mandatory *
|
||||
= text_field_tag :email, params[:email], required: true
|
||||
|
||||
.contact-champ
|
||||
= label_tag :type do
|
||||
= t('your_question', scope: [:support, :question])
|
||||
= t('.your_question')
|
||||
%span.mandatory *
|
||||
= select_tag :type, options_for_select(@options, params[:type])
|
||||
|
||||
.contact-champ
|
||||
= label_tag :phone do
|
||||
= t('professional_phone_number', scope: [:supportadmin])
|
||||
= t('.pro_phone_number')
|
||||
= text_field_tag :phone
|
||||
|
||||
.contact-champ
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
- content_for(:title, 'Contact')
|
||||
- content_for(:title, t('.contact'))
|
||||
- content_for :footer do
|
||||
= render partial: "root/footer"
|
||||
|
||||
#contact-form
|
||||
.container
|
||||
%h1.new-h1
|
||||
= t('contact', scope: [:support])
|
||||
= t('.contact')
|
||||
|
||||
= form_tag contact_path, method: :post, multipart: true, class: 'form' do |f|
|
||||
|
||||
.description
|
||||
%p= t('intro_html', scope: [:support])
|
||||
%p= t('.intro_html')
|
||||
%br
|
||||
%p.mandatory-explanation= t('asterisk_html', scope: [:utils])
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
|||
|
||||
.contact-champ
|
||||
= label_tag :type do
|
||||
= t('your_question', scope: [:support, :question])
|
||||
= t('.your_question')
|
||||
%span.mandatory *
|
||||
= hidden_field_tag :type, params[:type]
|
||||
%dl
|
||||
|
@ -37,9 +37,10 @@
|
|||
%dd
|
||||
.support.card.featured.hidden{ id: question_type }
|
||||
.card-title
|
||||
= t('our_answer', scope: [:support, :response])
|
||||
= t('.our_answer')
|
||||
.card-content
|
||||
= t("#{question_type}_html", scope: [:support, :response], base_url: APPLICATION_BASE_URL, "link_#{question_type}": link)
|
||||
-# i18n-tasks-use t("support.index.#{question_type}.answer_html")
|
||||
= t('answer_html', scope: [:support, :index, question_type], base_url: APPLICATION_BASE_URL, "link_#{question_type}": link)
|
||||
|
||||
.contact-champ
|
||||
= label_tag :dossier_id, t('file_number', scope: [:utils])
|
||||
|
@ -61,9 +62,9 @@
|
|||
= label_tag :piece_jointe do
|
||||
= t('pj', scope: [:utils])
|
||||
%p.notice.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_AMELIORATION } }
|
||||
= t('notice_pj_product', scope: [:support, :response])
|
||||
= t('.notice_pj_product')
|
||||
%p.notice.hidden{ data: { 'contact-type-only': Helpscout::FormAdapter::TYPE_AUTRE } }
|
||||
= t('notice_pj_other', scope: [:support, :response])
|
||||
= t('.notice_pj_other')
|
||||
= file_field_tag :piece_jointe
|
||||
|
||||
= hidden_field_tag :tags, @tags&.join(',')
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
- etablissement = @dossier.etablissement
|
||||
- if etablissement.diffusable_commercialement == false
|
||||
%p= t('warning_for_private_info', etablissement: raison_sociale_or_name(etablissement), scope: 'views.shared.dossiers.identite_entreprise')
|
||||
%p= t('warning_for_private_info', scope: 'views.shared.dossiers.identite_entreprise', etablissement: raison_sociale_or_name(etablissement))
|
||||
|
||||
- else
|
||||
%p
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
.radios
|
||||
%label
|
||||
= f.radio_button :gender, Individual::GENDER_FEMALE, required: true
|
||||
= t(Individual::GENDER_FEMALE, scope: 'activerecord.attributes.individual.gender_options')
|
||||
= Individual.human_attribute_name('gender.female')
|
||||
%label
|
||||
= f.radio_button :gender, Individual::GENDER_MALE, required: true
|
||||
= t('activerecord.attributes.individual.gender_options')[Individual::GENDER_MALE.to_sym] # GENDER_MALE contains a point letter
|
||||
= Individual.human_attribute_name('gender.male')
|
||||
|
||||
.flex
|
||||
.inline-champ
|
||||
|
|
|
@ -23,6 +23,22 @@
|
|||
= f.email_field :email, value: nil, placeholder: 'Nouvelle adresse email', required: true
|
||||
= f.submit "Changer mon adresse", class: 'button primary'
|
||||
|
||||
- if !instructeur_signed_in?
|
||||
.card
|
||||
.card-title= t('.transfer_title')
|
||||
= t('.transfer_explication_html')
|
||||
|
||||
= form_tag transfer_all_dossiers_path, class: 'form' do
|
||||
= email_field_tag :next_owner, nil, required: true
|
||||
= submit_tag "Transférer tous mes dossiers", class: 'button primary', data: { confirm: t('.transfer_confirmation') }
|
||||
|
||||
- if @waiting_transfers.any?
|
||||
.card.warning
|
||||
.card-title= t('.waiting_transfers')
|
||||
%ul
|
||||
- @waiting_transfers.each do |email, nb_dossier|
|
||||
%li= t('.one_waiting_transfer', email: email, count: nb_dossier)
|
||||
|
||||
- if current_administrateur.present?
|
||||
.card
|
||||
.card-title Jeton d’identification de l’API (token)
|
||||
|
|
|
@ -17,13 +17,11 @@
|
|||
?
|
||||
.email-suggestion-answer
|
||||
= button_tag type: 'button', class: 'button small', onclick: "DS.acceptEmailSuggestion()" do
|
||||
= t('simple_form.yes')
|
||||
= t('utils.yes')
|
||||
= button_tag type: 'button', class: 'button small', onclick: "DS.discardEmailSuggestionBox()" do
|
||||
= t('simple_form.no')
|
||||
= t('utils.no')
|
||||
|
||||
= f.label :password, t('views.registrations.new.password_label', min_length: PASSWORD_MIN_LENGTH), id: :user_password_label
|
||||
= f.password_field :password, autocomplete: 'new-password', value: @user.password, placeholder: t('views.registrations.new.password_placeholder', min_length: PASSWORD_MIN_LENGTH), 'aria-describedby': :user_password_label
|
||||
|
||||
= f.submit t('views.shared.account.create'), class: "button large primary expand"
|
||||
|
||||
|
||||
|
|
|
@ -75,3 +75,6 @@ DS_ENV="staging"
|
|||
|
||||
# Désactivé l'OTP pour SuperAdmin
|
||||
# SUPER_ADMIN_OTP_ENABLED = "disabled" # "enabled" par défaut
|
||||
|
||||
# API Particulier https://api.gouv.fr/les-api/api-particulier
|
||||
# API_PARTICULIER_URL="https://particulier.api.gouv.fr/api"
|
||||
|
|
|
@ -29,8 +29,8 @@ data:
|
|||
# External locale data (e.g. gems).
|
||||
# This data is not considered unused and is never written to.
|
||||
external:
|
||||
## Example (replace %#= with %=):
|
||||
# - "<%#= %x[bundle show vagrant].chomp %>/templates/locales/%{locale}.yml"
|
||||
- "<%= %x[bundle info --path administrate].chomp %>/config/locales/*%{locale}.yml"
|
||||
- "<%= %x[bundle info --path devise-i18n].chomp %>/rails/locales/*%{locale}.yml"
|
||||
|
||||
## Specify the router (see Readme for details). Valid values: conservative_router, pattern_router, or a custom class.
|
||||
# router: conservative_router
|
||||
|
@ -96,9 +96,11 @@ search:
|
|||
|
||||
## Consider these keys used:
|
||||
ignore_unused:
|
||||
- 'activerecord.models.*'
|
||||
- 'activerecord.attributes.*'
|
||||
- 'activerecord.errors.*'
|
||||
- 'errors.messages.blank'
|
||||
- 'errors.messages.content_type_invalid'
|
||||
- 'pluralize.*'
|
||||
- 'views.pagination.*'
|
||||
- 'time.formats.default'
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
# The following jobs were renamed, but instances using the old name
|
||||
# were still scheduled to run on the job queue.
|
||||
#
|
||||
# To ensure the job queue can instantiate these jobs using the previous
|
||||
# names, this file defines retro-compatibility aliases.
|
||||
#
|
||||
# Once all jobs running using the previous name will have run, this
|
||||
# file can be safely deleted.
|
||||
#
|
||||
# (That probably means a few hours after deploying the rename in production
|
||||
# - but let's keep these for a while to make external integrators's life easier.
|
||||
# To keep some margin, let's say this file can be safely deleted in May 2021.)
|
||||
|
||||
Rails.application.reloader.to_prepare do
|
||||
if !defined?(ApiEntreprise)
|
||||
require 'excon'
|
||||
|
||||
module ApiEntreprise
|
||||
Job = APIEntreprise::Job
|
||||
AssociationJob = APIEntreprise::AssociationJob
|
||||
AttestationFiscaleJob = APIEntreprise::AttestationFiscaleJob
|
||||
AttestationSocialeJob = APIEntreprise::AttestationSocialeJob
|
||||
BilansBdfJob = APIEntreprise::BilansBdfJob
|
||||
EffectifsAnnuelsJob = APIEntreprise::EffectifsAnnuelsJob
|
||||
EffectifsJob = APIEntreprise::EffectifsJob
|
||||
EntrepriseJob = APIEntreprise::EntrepriseJob
|
||||
ExercicesJob = APIEntreprise::ExercicesJob
|
||||
end
|
||||
|
||||
module Cron
|
||||
FixMissingAntivirusAnalysis = FixMissingAntivirusAnalysisJob
|
||||
end
|
||||
end
|
||||
end
|
|
@ -25,8 +25,8 @@ end
|
|||
|
||||
# A list of features to be deployed on first push
|
||||
features = [
|
||||
:administrateur_routage,
|
||||
:administrateur_web_hook,
|
||||
:api_particulier,
|
||||
:dossier_pdf_vide,
|
||||
:expert_not_allowed_to_invite,
|
||||
:hide_instructeur_email,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# API URLs
|
||||
API_ENTREPRISE_URL = ENV.fetch("API_ENTREPRISE_URL", "https://entreprise.api.gouv.fr/v2")
|
||||
API_EDUCATION_URL = ENV.fetch("API_EDUCATION_URL", "https://data.education.gouv.fr/api/records/1.0")
|
||||
API_PARTICULIER_URL = ENV.fetch("API_PARTICULIER_URL", "https://particulier.api.gouv.fr/api")
|
||||
HELPSCOUT_API_URL = ENV.fetch("HELPSCOUT_API_URL", "https://api.helpscout.net/v2")
|
||||
PIPEDRIVE_API_URL = ENV.fetch("PIPEDRIVE_API_URL", "https://api.pipedrive.com/v1")
|
||||
SENDINBLUE_API_URL = ENV.fetch("SENDINBLUE_API_URL", "https://in-automate.sendinblue.com/api/v2")
|
||||
|
|
5
config/locales/active_storage_validations.en.yml
Normal file
5
config/locales/active_storage_validations.en.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
en:
|
||||
errors:
|
||||
messages:
|
||||
content_type_invalid: "is not of an accepted type"
|
||||
file_size_out_of_range: "is too big. The file must be at most %{file_size_limit}."
|
|
@ -1,5 +1,5 @@
|
|||
fr:
|
||||
errors:
|
||||
messages:
|
||||
content_type_invalid: n’est pas d’un type accepté
|
||||
file_size_out_of_range: "est trop lourde, elle doit faire au plus 200 Mo."
|
||||
content_type_invalid: "n’est pas d’un type accepté"
|
||||
file_size_out_of_range: "est trop lourd(e). Le fichier doit faire au plus %{file_size_limit}."
|
||||
|
|
30
config/locales/api_particulier.fr.yml
Normal file
30
config/locales/api_particulier.fr.yml
Normal file
|
@ -0,0 +1,30 @@
|
|||
fr:
|
||||
api_particulier:
|
||||
providers:
|
||||
cnaf:
|
||||
libelle: Caisse d’allocations familiales (CAF)
|
||||
scopes:
|
||||
personne: &personne
|
||||
noms_prenoms: noms et prénoms
|
||||
date_de_naissance: date de naissance
|
||||
sexe: genre
|
||||
allocataires:
|
||||
libelle: allocataires
|
||||
<<: *personne
|
||||
enfants:
|
||||
libelle: enfants
|
||||
<<: *personne
|
||||
adresse:
|
||||
libelle: adresse
|
||||
identite: identité
|
||||
complement_d_identite: complément d’identité
|
||||
complement_d_identite_geo: complément d’identité géographique
|
||||
numero_et_rue: numéro et rue
|
||||
lieu_dit: lieu-dit
|
||||
code_postal_et_ville: code postal et ville
|
||||
pays: pays
|
||||
quotient_familial:
|
||||
libelle: quotient familial
|
||||
quotient_familial: quotient familial
|
||||
mois: mois
|
||||
annee: année
|
|
@ -32,6 +32,8 @@
|
|||
en:
|
||||
help: 'Help'
|
||||
utils:
|
||||
'yes': Yes
|
||||
'no': No
|
||||
deconnexion: "Log out"
|
||||
pj: "Attachments"
|
||||
asterisk_html: Fields marked by an asterisk ( <span class = mandatory>*</span> ) are mandatory.
|
||||
|
@ -64,7 +66,6 @@ en:
|
|||
start_procedure: Start the procedure
|
||||
existing_dossiers: You already have files for this procedure
|
||||
show_dossiers: View my current files
|
||||
start_new_dossier: Start a new file
|
||||
already_draft: "You already started to fill a file"
|
||||
already_draft_detail_html: "You started to fill a file for the \"%{procedure}\" procedure <strong>%{time_ago} ago</strong>"
|
||||
already_not_draft: "You already submitted a file"
|
||||
|
@ -185,7 +186,6 @@ en:
|
|||
status: "Status"
|
||||
updated: "Updated"
|
||||
actions: "Actions"
|
||||
accessibility_question: "What do you think about the accessibility of this service?"
|
||||
dossier_action:
|
||||
edit_dossier: "Edit the file"
|
||||
start_other_dossier: "Start an other file"
|
||||
|
@ -203,7 +203,7 @@ en:
|
|||
connection: Sign in
|
||||
are_you_new: First time on %{app_name}?
|
||||
find_procedure: Find your procedure
|
||||
password:
|
||||
passwords:
|
||||
reset_link_sent:
|
||||
got_it: Got it!
|
||||
open_your_mailbox: Now open your mailbox.
|
||||
|
@ -213,9 +213,9 @@ en:
|
|||
check_spams: Check your junk or spam email.
|
||||
check_account: Have you created a %{application_name} account with the adress %{email}? You will not receive any message if no account is linked to your email adress.
|
||||
check_france_connect_html: Have you once logged in with France Connect? If yes, <a href=\"%{href}\">try again with France Connect</a>.
|
||||
shared:
|
||||
email_can_take_a_while_html: <strong>Please note</strong> that this message can take up to 15 minutes to arrive.
|
||||
contact_us_if_any_trouble_html: You can contact us <a href=\"%{href}\">through this form</a> if a problem still exists.
|
||||
shared:
|
||||
email_can_take_a_while_html: <strong>Please note</strong> that this message can take up to 15 minutes to arrive.
|
||||
contact_us_if_any_trouble_html: You can contact us <a href=\"%{href}\">through this form</a> if a problem still exists.
|
||||
modal:
|
||||
publish:
|
||||
title:
|
||||
|
@ -352,13 +352,20 @@ en:
|
|||
users:
|
||||
dossiers:
|
||||
test_procedure: "This file is submitted on a test procedure. Any modification of the procedure by the administrator (addition of a field, publication of the procedure, etc.) will result in the removal of the file."
|
||||
message_send: "Your message has been sent to the instructor in charge of your file."
|
||||
no_access: "You do not have access to this file"
|
||||
no_longer_editable: "Your file can no longer be edited"
|
||||
undergoingreview: "Your file is undergoing review. It is no longer possible to delete your file. To cancel the undergoingreview contact the adminitration via the mailbox."
|
||||
deleted_dossier: "Your file has been successfully deleted"
|
||||
archived_dossier: "Your file will be archived for an additional month"
|
||||
draft_saved: "Your draft has been saved."
|
||||
no_establishment: "There is no establishment tied to this file"
|
||||
identity_saved: "Identity data is registred"
|
||||
no_longer_available: "The certificate is no longer available on this file."
|
||||
create_commentaire:
|
||||
message_send: "Your message has been sent to the instructor in charge of your file."
|
||||
ask_deletion:
|
||||
undergoingreview: "Your file is undergoing review. It is no longer possible to delete your file. To cancel the undergoingreview contact the adminitration via the mailbox."
|
||||
deleted_dossier: "Your file has been successfully deleted"
|
||||
extend_conservation:
|
||||
archived_dossier: "Your file will be archived for an additional month"
|
||||
update_brouillon:
|
||||
draft_saved: "Your draft has been saved."
|
||||
etablissement:
|
||||
no_establishment: "There is no establishment tied to this file"
|
||||
update_identite:
|
||||
identity_saved: "Identity data is registred"
|
||||
attestation:
|
||||
no_longer_available: "The certificate is no longer available on this file."
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
fr:
|
||||
help: 'Aide'
|
||||
utils:
|
||||
'yes': Oui
|
||||
'no': Non
|
||||
deconnexion: "Déconnexion"
|
||||
pj: "Pièces jointes"
|
||||
asterisk_html: Les champs suivis d’un astérisque ( <span class = mandatory> * </span> ) sont obligatoires.
|
||||
|
@ -97,6 +99,9 @@ fr:
|
|||
first: Premier
|
||||
truncate: '…'
|
||||
shared:
|
||||
actions:
|
||||
save: Enregistrer
|
||||
edit: Modifier
|
||||
greetings:
|
||||
hello: Bonjour,
|
||||
best_regards: Bonne journée,
|
||||
|
@ -177,7 +182,6 @@ fr:
|
|||
status: "Statut"
|
||||
updated: "Mis à jour"
|
||||
actions: "Actions"
|
||||
accessibility_question: "Que pensez-vous de la facilité d’utilisation de ce service ?"
|
||||
dossier_action:
|
||||
edit_dossier: "Modifier le dossier"
|
||||
start_other_dossier: "Commencer un autre dossier"
|
||||
|
@ -356,13 +360,51 @@ fr:
|
|||
users:
|
||||
dossiers:
|
||||
test_procedure: "Ce dossier est déposé sur une démarche en test. Toute modification de la démarche par l’administrateur (ajout d'un champ, publication de la démarche...) entraînera sa suppression."
|
||||
message_send: "Votre message a bien été envoyé à l’instructeur en charge de votre dossier."
|
||||
no_access: "Vous n’avez pas accès à ce dossier"
|
||||
no_longer_editable: "Votre dossier ne peut plus être modifié"
|
||||
undergoingreview: "L’instruction de votre dossier a commencé, il n’est plus possible de supprimer votre dossier. Si vous souhaitez annuler l’instruction contactez votre administration par la messagerie de votre dossier."
|
||||
deleted_dossier: "Votre dossier a bien été supprimé."
|
||||
archived_dossier: "Votre dossier sera conservé un mois supplémentaire"
|
||||
draft_saved: "Votre brouillon a bien été sauvegardé."
|
||||
no_establishment: "Aucun établissement n’est associé à ce dossier"
|
||||
identity_saved: "Identité enregistrée"
|
||||
no_longer_available: "L’attestation n'est plus disponible sur ce dossier."
|
||||
create_commentaire:
|
||||
message_send: "Votre message a bien été envoyé à l’instructeur en charge de votre dossier."
|
||||
ask_deletion:
|
||||
undergoingreview: "L’instruction de votre dossier a commencé, il n’est plus possible de supprimer votre dossier. Si vous souhaitez annuler l’instruction contactez votre administration par la messagerie de votre dossier."
|
||||
deleted_dossier: "Votre dossier a bien été supprimé."
|
||||
extend_conservation:
|
||||
archived_dossier: "Votre dossier sera conservé un mois supplémentaire"
|
||||
update_brouillon:
|
||||
draft_saved: "Votre brouillon a bien été sauvegardé."
|
||||
etablissement:
|
||||
no_establishment: "Aucun établissement n’est associé à ce dossier"
|
||||
update_identite:
|
||||
identity_saved: "Identité enregistrée"
|
||||
attestation:
|
||||
no_longer_available: "L’attestation n'est plus disponible sur ce dossier."
|
||||
new_administrateur:
|
||||
procedures:
|
||||
show:
|
||||
ready: "Validé"
|
||||
needs_configuration: "À configurer"
|
||||
configure_api_particulier_token: "Configurer le jeton API particulier"
|
||||
jeton_particulier:
|
||||
show:
|
||||
configure_token: "Configurer le jeton API Particulier"
|
||||
api_particulier_description_html: "%{app_name} utilise <a href=\"https://api.gouv.fr/les-api/api-particulier\">API Particulier</a> qui permet de récupérer les données familiales (CAF).<br />Renseignez ici le <a href=\"https://api.gouv.fr/les-api/api-particulier/demande-acces\">jeton API Particulier</a> propre à votre démarche."
|
||||
token_description: "Il doit contenir au minimum 15 caractères."
|
||||
update:
|
||||
token_ok: "Le jeton a bien été mis à jour"
|
||||
no_scopes_token: "Mise à jour impossible : le jeton n'a pas acces aux données.<br /><br />Vérifier le auprès de <a href='https://datapass.api.gouv.fr/'>https://datapass.api.gouv.fr/</a>"
|
||||
not_found_token: "Mise à jour impossible : le jeton n'a pas été trouvé ou n'est pas actif<br /><br />Vérifier le auprès de <a href='https://datapass.api.gouv.fr/'>https://datapass.api.gouv.fr/</a>"
|
||||
network_error: "Mise à jour impossible : une erreur réseau est survenue"
|
||||
api_particulier:
|
||||
already_configured: "Déjà rempli"
|
||||
needs_configuration: "À remplir"
|
||||
sources_particulier:
|
||||
show:
|
||||
title: "Définir les sources de données"
|
||||
data_sources: "Sources de données"
|
||||
explication_html: "<p>API Particulier facilite l’accès des administrations aux données familiales (CAF) pour simplifier les démarches administratives mises en œuvre par les collectivités et les administrations.<br> Cela permet aux administrations d’accéder à des informations certifiées à la source et ainsi : </p> <ul> <li>de s’affranchir des pièces justificatives lors des démarches en ligne,</li> <li>de réduire le nombre d’erreurs de saisie,</li> <li>d’écarter le risque de fraude documentaire.</li> </ul> <p> <strong>Important :</strong> les disposition de l'article <a href='https://www.legifrance.gouv.fr/affichCodeArticle.do?cidTexte=LEGITEXT000031366350&idArticle=LEGIARTI000031367412&dateTexte=&categorieLien=cid'>L144-8</a> n’autorisent que l’échange des informations strictement nécessaires pour traiter une démarche.<br /><br />En conséquence, ne sélectionnez ici que les données auxquelles vous aurez accès d’un point de vue légal.</p>"
|
||||
update:
|
||||
sources_ok: 'Mise à jour effectuée'
|
||||
procedures:
|
||||
show:
|
||||
ready: "Validé"
|
||||
needs_configuration: "À configurer"
|
||||
configure_api_particulier_token: "Configurer le jeton API particulier"
|
||||
|
|
|
@ -6,6 +6,6 @@ en:
|
|||
nom: Last name
|
||||
prenom: First name
|
||||
birthdate: Date de naissance
|
||||
gender_options:
|
||||
"Mme": "Ms"
|
||||
"M.": "Mr"
|
||||
individual/gender:
|
||||
female: Ms
|
||||
male: Mr
|
||||
|
|
|
@ -6,6 +6,6 @@ fr:
|
|||
nom: Nom
|
||||
prenom: Prénom
|
||||
birthdate: Date de naissance
|
||||
gender_options:
|
||||
"Mme": "Madame"
|
||||
"M.": "Monsieur"
|
||||
individual/gender:
|
||||
female: Madame
|
||||
male: Monsieur
|
||||
|
|
|
@ -16,3 +16,11 @@ fr:
|
|||
aasm_state/hidden: Suprimée
|
||||
declarative_with_state/en_instruction: En instruction
|
||||
declarative_with_state/accepte: Accepté
|
||||
api_particulier_token: Jeton API Particulier
|
||||
errors:
|
||||
models:
|
||||
procedure:
|
||||
attributes:
|
||||
api_particulier_token:
|
||||
invalid: 'n’a pas le bon format'
|
||||
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
en:
|
||||
simple_form:
|
||||
"yes": 'Yes'
|
||||
"no": 'No'
|
||||
required:
|
||||
text: 'required'
|
||||
mark: '*'
|
||||
# You can uncomment the line below if you need to overwrite the whole required html.
|
||||
# When using html, text and mark won’t be used.
|
||||
# html: '<abbr title="required">*</abbr>'
|
||||
error_notification:
|
||||
default_message: "Please review the problems below:"
|
||||
# Examples
|
||||
# labels:
|
||||
# defaults:
|
||||
# password: 'Password'
|
||||
# user:
|
||||
# new:
|
||||
# email: 'E-mail to sign in.'
|
||||
# edit:
|
||||
# email: 'E-mail.'
|
||||
# hints:
|
||||
# defaults:
|
||||
# username: 'User name to sign in.'
|
||||
# password: 'No special characters, please.'
|
||||
# include_blanks:
|
||||
# defaults:
|
||||
# age: 'Rather not say'
|
||||
# prompts:
|
||||
# defaults:
|
||||
# age: 'Select your age'
|
|
@ -1,31 +0,0 @@
|
|||
fr:
|
||||
simple_form:
|
||||
"yes": 'Oui'
|
||||
"no": 'Non'
|
||||
required:
|
||||
text: 'obligatoire'
|
||||
mark: '*'
|
||||
# You can uncomment the line below if you need to overwrite the whole required html.
|
||||
# When using html, text and mark won't be used.
|
||||
# html: '<abbr title="required">*</abbr>'
|
||||
error_notification:
|
||||
default_message: "Erreur, veuillez vérifier vos réponses:"
|
||||
# Examples
|
||||
# labels:
|
||||
# defaults:
|
||||
# password: 'Password'
|
||||
# user:
|
||||
# new:
|
||||
# email: 'E-mail to sign in.'
|
||||
# edit:
|
||||
# email: 'E-mail.'
|
||||
# hints:
|
||||
# defaults:
|
||||
# username: 'User name to sign in.'
|
||||
# password: 'No special characters, please.'
|
||||
# include_blanks:
|
||||
# defaults:
|
||||
# age: 'Rather not say'
|
||||
# prompts:
|
||||
# defaults:
|
||||
# age: 'Select your age'
|
|
@ -1,4 +1,4 @@
|
|||
fr:
|
||||
en:
|
||||
dossier_mailer:
|
||||
notify_new_answer:
|
||||
subject: New message on your file nº %{dossier_id} « %{libelle_demarche} »
|
||||
|
|
|
@ -3,7 +3,7 @@ fr:
|
|||
notify_new_answer:
|
||||
subject: Nouveau message pour votre dossier nº %{dossier_id} « %{libelle_demarche} »
|
||||
body_html: |
|
||||
Vous avez reçu un <b>nouveau message</b> de la part du service en charge de votre dossier.
|
||||
Vous avez reçu un <b>nouveau message</b> de la part du service en charge de votre dossier sur la démarche « %{libelle_demarche} ».
|
||||
link: |
|
||||
Pour consulter le message et y répondre, cliquez sur le bouton ci-dessous :
|
||||
body_draft_html: |
|
||||
|
|
|
@ -3,7 +3,7 @@ fr:
|
|||
notify_revert_to_instruction:
|
||||
subject: Votre dossier nº %{dossier_id} sur la démarche « %{libelle_demarche} » est en train d’être réexaminé
|
||||
body_html: |
|
||||
Votre dossier va être réexaminé, la précédente décision sur ce dossier est caduque.
|
||||
Votre dossier nº %{dossier_id} va être réexaminé, la précédente décision sur ce dossier est caduque.
|
||||
Vous pouvez retrouver le dossier que vous avez créé pour la démarche <b>« %{libelle_demarche} »</b> à l'adresse suivante :
|
||||
contact: |
|
||||
Pour obtenir le détail de cette modification de la décision, vous pouvez contacter par email :
|
||||
|
|
|
@ -4,7 +4,8 @@ fr:
|
|||
wrong_address:
|
||||
one: "%{value} n’est pas une adresse email valide"
|
||||
other: "%{value} ne sont pas des adresses emails valides"
|
||||
experts_assignment:
|
||||
create:
|
||||
experts_assignment:
|
||||
one: "L'expert %{value} a été affecté à la démarche n° %{procedure}"
|
||||
other: "Les experts %{value} ont été affectés à la démarche n° %{procedure}"
|
||||
groupe_instructeurs:
|
||||
|
@ -12,10 +13,6 @@ fr:
|
|||
existing_groupe:
|
||||
one: "%{count} groupe existe"
|
||||
other: "%{count} groupes existent"
|
||||
show:
|
||||
assigned_instructeur:
|
||||
one: "%{count} instructeur est affecté"
|
||||
other: "%{count} instructeurs sont affectés"
|
||||
add_instructeur:
|
||||
wrong_address:
|
||||
one: "%{value} n’est pas une adresse email valide"
|
||||
|
@ -27,3 +24,18 @@ fr:
|
|||
existing_groupe:
|
||||
one: "%{count} groupe existe"
|
||||
other: "%{count} groupes existent"
|
||||
instructeurs:
|
||||
assigned_instructeur:
|
||||
one: "%{count} instructeur est affecté"
|
||||
other: "%{count} instructeurs sont affectés"
|
||||
edit:
|
||||
existing_groupe:
|
||||
one: "%{count} groupe existe"
|
||||
other: "%{count} groupes existent"
|
||||
routing:
|
||||
notice_html: |
|
||||
Le routage est une fonctionnalité pour les démarches nécessitant le partage de l’instruction entre différents groupes en fonction d’un critère précis (territoire, thématique ou autre).
|
||||
<br><br>
|
||||
Cette fonctionnalité permet d’acheminer les dossier vers chaque groupe, et de ne plus avoir besoin de filtrer ses dossiers parmi une grande quantité de demandes. Elle est donc particulièrement adaptée pour les démarches nationales instruites localement.
|
||||
<br><br>
|
||||
Les instructeurs ne voient que les dossiers les concernant, et n’ont donc pas accès aux données extérieures à leur périmètre.
|
||||
|
|
|
@ -11,8 +11,8 @@ fr:
|
|||
update_description: La description du champ « %{label} » a été modifiée. La nouvelle description est « %{to} »
|
||||
update_type_champ: Le type du champ « %{label} » a été modifié. Il est maintenant de type « %{to} »
|
||||
update_mandatory:
|
||||
enable: Le champ « %{label} » est maintenant obligatoire
|
||||
disable: Le champ « %{label} » n’est plus obligatoire
|
||||
enabled: Le champ « %{label} » est maintenant obligatoire
|
||||
disabled: Le champ « %{label} » n’est plus obligatoire
|
||||
update_piece_justificative_template: Le modèle de pièce justificative du champ « %{label} » a été modifié
|
||||
update_drop_down_options: Les options de sélection du champ « %{label} » ont été modifiées
|
||||
update_carte_layers: Les référentiels cartographiques du champ « %{label} » ont été modifiés
|
||||
|
@ -25,8 +25,8 @@ fr:
|
|||
update_description_private: La description de l’annotation privée « %{label} » a été modifiée. La nouvelle description est « %{to} »
|
||||
update_type_champ_private: Le type de l’annotation privée « %{label} » a été modifié. Elle est maintenant de type « %{to} »
|
||||
update_mandatory_private:
|
||||
enable: L’annotation privée « %{label} » est maintenant obligatoire
|
||||
disable: L’annotation privée « %{label} » n’est plus obligatoire
|
||||
enabled: L’annotation privée « %{label} » est maintenant obligatoire
|
||||
disabled: L’annotation privée « %{label} » n’est plus obligatoire
|
||||
update_piece_justificative_template_private: Le modèle de pièce justificative de l’annotation privée « %{label} » a été modifié
|
||||
update_drop_down_options_private: Les options de sélection de l’annotation privée « %{label} » ont été modifiées
|
||||
update_carte_layers_private: Les référentiels cartographiques de l’annotation privée « %{label} » ont été modifiés
|
||||
|
|
56
config/locales/views/support/en.yml
Normal file
56
config/locales/views/support/en.yml
Normal file
|
@ -0,0 +1,56 @@
|
|||
en:
|
||||
support:
|
||||
index:
|
||||
contact: Contact
|
||||
intro_html: Contact us via this form and we will answer you as quickly as possible.<br>Make sure you provide all the required information so we can help you in the best way.
|
||||
your_question: Your question
|
||||
our_answer: 👉 Our answer
|
||||
notice_pj_product: A screenshot can help us identify the element to improve.
|
||||
notice_pj_other: A screenshot can help us identify the issue.
|
||||
procedure_info:
|
||||
question: I've encountered a problem while completing my application
|
||||
answer_html: "<p>Are you sure that all the mandatory fields (<span class= mandatory> * </span>) are properly filled?
|
||||
<p>If you have questions about the information requested, contact the service in charge of the procedure.</p>
|
||||
<p><a href=%{link_procedure_info}>Find more information</a></p>"
|
||||
instruction_info:
|
||||
question: I have a question about the instruction of my application
|
||||
answer_html: "<p>If you have questions about the instruction of your application (response delay for example), contact directly the instructor via our mail system.</p>
|
||||
<p><a href=%{link_instruction_info}>Find more information</a></p>
|
||||
<br>
|
||||
<p>If you are facing technical issues on the website, use the form below. We will not be able to inform you about the instruction of your application.</p>"
|
||||
product:
|
||||
question: I have an idea to improve the website
|
||||
answer_html: "<p>Got an idea? Please check our <strong>enhancement dashboard</strong></p>
|
||||
<p><ul><li>Vote for your priority improvements</li>
|
||||
<li>Share your own ideas</li></ul></p>
|
||||
<p><strong><a href=%{link_product}>➡ Access the enhancement dashboard</a></strong></p>"
|
||||
lost_user:
|
||||
question: I am having trouble finding the procedure I am looking for
|
||||
answer_html: "<p>We invite you to contact the administration in charge of the procedure so they can provide you the link.
|
||||
It should look like this: %{base_url}/commencer/NOM_DE_LA_DEMARCHE.</p>
|
||||
<br>
|
||||
<p>You can find here the most popular procedures (licence, detr, etc.) :</p>
|
||||
<p><a href=%{link_lost_user}>%{link_lost_user}</a></p>"
|
||||
other:
|
||||
question: Other topic
|
||||
admin:
|
||||
your_question: Your question
|
||||
admin_intro_html: "<p>As an administration, you can contact us through this form. We'll answer you as quickly as possibly by e-mail or phone.</p>
|
||||
<br>
|
||||
<p><strong>Caution, this form is dedicated to public bodies only.</strong>
|
||||
It does not concern individuals, companies nor associations (except those recognised of public utility). If you belong to one of these categories, contact us <a href=%{contact_path}>here</a>.</p>"
|
||||
contact_team: Contact our team
|
||||
pro_phone_number: Professional phone number (direct line)
|
||||
pro_mail: Professional email address
|
||||
admin question:
|
||||
question: I have a question about %{app_name}
|
||||
admin demande rdv:
|
||||
question: I request an appointment for an online presentation of %{app_name}
|
||||
admin soucis:
|
||||
question: I am facing a technical issue on %{app_name}
|
||||
admin suggestion produit:
|
||||
question: I have a suggestion for an evolution
|
||||
admin demande compte:
|
||||
question: I want to open an admin account with an Orange, Wanadoo, etc. email
|
||||
admin autre:
|
||||
question: Other topic
|
56
config/locales/views/support/fr.yml
Normal file
56
config/locales/views/support/fr.yml
Normal file
|
@ -0,0 +1,56 @@
|
|||
fr:
|
||||
support:
|
||||
index:
|
||||
contact: Contact
|
||||
intro_html: Contactez-nous via ce formulaire et nous vous répondrons dans les plus brefs délais.<br>Pensez bien à nous donner le plus d’informations possible pour que nous puissions vous aider au mieux.
|
||||
your_question: Votre question
|
||||
our_answer: 👉 Notre réponse
|
||||
notice_pj_product: Une capture d’écran peut nous aider à identifier plus facilement l’endroit à améliorer.
|
||||
notice_pj_other: Une capture d’écran peut nous aider à identifier plus facilement le problème.
|
||||
procedure_info:
|
||||
question: J’ai un problème lors du remplissage de mon dossier
|
||||
answer_html: "<p>Avez-vous bien vérifié que tous les champs obligatoires (<span class= mandatory> * </span>) sont remplis ?
|
||||
<p>Si vous avez des questions sur les informations à saisir, contactez les services en charge de la démarche.</p>
|
||||
<p><a href=%{link_procedure_info}>En savoir plus</a></p>"
|
||||
instruction_info:
|
||||
question: J’ai une question sur l’instruction de mon dossier
|
||||
answer_html: "<p>Si vous avez des questions sur l’instruction de votre dossier (par exemple sur les délais), nous vous invitons à contacter directement les services qui instruisent votre dossier par votre messagerie.</p>
|
||||
<p><a href=%{link_instruction_info}>En savoir plus</a></p>
|
||||
<br>
|
||||
<p>Si vous souhaitez poser une question pour un problème technique sur le site, utilisez le formulaire ci-dessous. Nous ne pourrons pas vous renseigner sur l’instruction de votre dossier.</p>"
|
||||
product:
|
||||
question: J’ai une idée d’amélioration pour votre site
|
||||
answer_html: "<p>Une idée ? Pensez à consulter notre <strong>tableau de bord des améliorations</strong></p>
|
||||
<p><ul><li>Votez pour vos améliorations prioritaires;</li>
|
||||
<li>Proposez votre propre idée.</li></ul></p>
|
||||
<p><strong><a href=%{link_product}>➡ Accéder au tableau des améliorations</a></strong></p>"
|
||||
lost_user:
|
||||
question: Je ne trouve pas la démarche que je veux faire
|
||||
answer_html: "<p>Nous vous invitons à contacter l’administration en charge de votre démarche pour qu’elle vous indique le lien à suivre. Celui-ci devrait ressembler à cela : %{base_url}/commencer/NOM_DE_LA_DEMARCHE.</p>
|
||||
<br>
|
||||
<p>Vous pouvez aussi consulter ici la liste de nos démarches les plus frequentes (permis, detr etc) :</p>
|
||||
<p><a href=%{link_lost_user}>%{link_lost_user}</a></p>"
|
||||
other:
|
||||
question: Autre sujet
|
||||
admin:
|
||||
your_question: Votre question
|
||||
admin_intro_html: "<p>En tant qu’administration, vous pouvez nous contactez via ce formulaire. Nous vous répondrons dans les plus brefs délais, par email ou par téléphone.</p>
|
||||
<br>
|
||||
<p><strong>Attention, ce formulaire est réservé uniquement aux organismes publics.</strong>
|
||||
Il ne concerne ni les particuliers, ni les entreprises, ni les associations (sauf celles reconnues d’utilité publique). Si c'est votre cas, rendez-vous sur notre
|
||||
<a href=%{contact_path}>formulaire de contact public</a>.</p>"
|
||||
contact_team: Contactez notre équipe
|
||||
pro_phone_number: Numéro de téléphone professionnel (ligne directe)
|
||||
pro_mail: Adresse e-mail professionnelle
|
||||
admin question:
|
||||
question: J’ai une question sur %{app_name}
|
||||
admin demande rdv:
|
||||
question: Demande de RDV pour une présentation à distance de %{app_name}
|
||||
admin soucis:
|
||||
question: J’ai un problème technique avec %{app_name}
|
||||
admin suggestion produit:
|
||||
question: J’ai une proposition d’évolution
|
||||
admin demande compte:
|
||||
question: Je souhaite ouvrir un compte administrateur avec un email Orange, Wanadoo, etc.
|
||||
admin autre:
|
||||
question: Autre sujet
|
|
@ -1,46 +0,0 @@
|
|||
en:
|
||||
support:
|
||||
contact: Contact
|
||||
intro_html: Contact us via this form and we will answer you as quickly as possible.<br>Make sure you provide all the required information so we can help you in the best way.
|
||||
question:
|
||||
your_question: Your question
|
||||
choose_question: Choose your question
|
||||
procedure_info: I've encountered a problem while completing my application
|
||||
instruction_info: I have a question about the instruction of my application
|
||||
product: I have an idea to improve the website
|
||||
lost_user: I am having trouble finding the procedure I am looking for
|
||||
other: Other topic
|
||||
response:
|
||||
our_answer: 👉 Our answer
|
||||
procedure_info_html: "<p>Are you sure that all the mandatory fields (<span class= mandatory> * </span>) are properly filled?
|
||||
<p>If you have questions about the information requested, contact the service in charge of the procedure.</p>
|
||||
<p><a href=%{link_procedure_info}>Find more information</a></p>"
|
||||
instruction_info_html: "<p>If you have questions about the instruction of your application (response delay for example), contact directly the instructor via our mail system.</p>
|
||||
<p><a href=%{link_instruction_info}>Find more information</a></p>
|
||||
<br>
|
||||
<p>If you are facing technical issues on the website, use the form below. We will not be able to inform you about the instruction of your application.</p>"
|
||||
product_html: "<p>Got an idea? Please check our <strong>enhancement dashboard</strong></p>
|
||||
<p><ul><li>Vote for your priority improvements</li>
|
||||
<li>Share your own ideas</li></ul></p>
|
||||
<p><strong><a href=%{link_product}>➡ Access the enhancement dashboard</a></strong></p>"
|
||||
lost_user_html: "<p>We invite you to contact the administration in charge of the procedure so they can provide you the link.
|
||||
It should look like this: %{base_url}/commencer/NOM_DE_LA_DEMARCHE.</p>
|
||||
<br>
|
||||
<p>You can find here the most popular procedures (licence, detr, etc.) :</p>
|
||||
<p><a href=%{link_lost_user}>%{link_lost_user}</a></p>"
|
||||
notice_pj_product: A screenshot can help us identify the element to improve.
|
||||
notice_pj_other: A screenshot can help us identify the issue.
|
||||
supportadmin:
|
||||
admin_intro_html: "<p>As an administration, you can contact us through this form. We'll answer you as quickly as possibly by e-mail or phone.</p>
|
||||
<br>
|
||||
<p><strong>Caution, this form is dedicated to public bodies only.</strong>
|
||||
It does not concern individuals, companies nor associations (except those recognised of public utility). If you belong to one of these categories, contact us <a href=%{contact_path}>here</a>.</p>"
|
||||
contact_team: Contact our team
|
||||
pro_phone_number: Professional phone number (direct line)
|
||||
pro_mail: Professional email address
|
||||
admin demande rdv: I request an appointment for an online presentation of %{app_name}
|
||||
admin question: I have a question about %{app_name}
|
||||
admin soucis: I am facing a technical issue on %{app_name}
|
||||
admin suggestion produit: I have a suggestion for an evolution
|
||||
admin demande compte: I want to open an admin account with an Orange, Wanadoo, etc. email
|
||||
admin autre: Other topic
|
|
@ -1,46 +0,0 @@
|
|||
fr:
|
||||
support:
|
||||
contact: Contact
|
||||
intro_html: Contactez-nous via ce formulaire et nous vous répondrons dans les plus brefs délais.<br>Pensez bien à nous donner le plus d’informations possible pour que nous puissions vous aider au mieux.
|
||||
question:
|
||||
your_question: Votre question
|
||||
choose_question: Choisir une question
|
||||
procedure_info: J’ai un problème lors du remplissage de mon dossier
|
||||
instruction_info: J’ai une question sur l’instruction de mon dossier
|
||||
product: J’ai une idée d’amélioration pour votre site
|
||||
lost_user: Je ne trouve pas la démarche que je veux faire
|
||||
other: Autre sujet
|
||||
response:
|
||||
our_answer: 👉 Notre réponse
|
||||
procedure_info_html: "<p>Avez-vous bien vérifié que tous les champs obligatoires (<span class= mandatory> * </span>) sont remplis ?
|
||||
<p>Si vous avez des questions sur les informations à saisir, contactez les services en charge de la démarche.</p>
|
||||
<p><a href=%{link_procedure_info}>En savoir plus</a></p>"
|
||||
instruction_info_html: "<p>Si vous avez des questions sur l’instruction de votre dossier (par exemple sur les délais), nous vous invitons à contacter directement les services qui instruisent votre dossier par votre messagerie.</p>
|
||||
<p><a href=%{link_instruction_info}>En savoir plus</a></p>
|
||||
<br>
|
||||
<p>Si vous souhaitez poser une question pour un problème technique sur le site, utilisez le formulaire ci-dessous. Nous ne pourrons pas vous renseigner sur l’instruction de votre dossier.</p>"
|
||||
product_html: "<p>Une idée ? Pensez à consulter notre <strong>tableau de bord des améliorations</strong></p>
|
||||
<p><ul><li>Votez pour vos améliorations prioritaires;</li>
|
||||
<li>Proposez votre propre idée.</li></ul></p>
|
||||
<p><strong><a href=%{link_product}>➡ Accéder au tableau des améliorations</a></strong></p>"
|
||||
lost_user_html: "<p>Nous vous invitons à contacter l’administration en charge de votre démarche pour qu’elle vous indique le lien à suivre. Celui-ci devrait ressembler à cela : %{base_url}/commencer/NOM_DE_LA_DEMARCHE.</p>
|
||||
<br>
|
||||
<p>Vous pouvez aussi consulter ici la liste de nos démarches les plus frequentes (permis, detr etc) :</p>
|
||||
<p><a href=%{link_lost_user}>%{link_lost_user}</a></p>"
|
||||
notice_pj_product: Une capture d’écran peut nous aider à identifier plus facilement l’endroit à améliorer.
|
||||
notice_pj_other: Une capture d’écran peut nous aider à identifier plus facilement le problème.
|
||||
supportadmin:
|
||||
admin_intro_html: "<p>En tant qu’administration, vous pouvez nous contactez via ce formulaire. Nous vous répondrons dans les plus brefs délais, par email ou par téléphone.</p>
|
||||
<br>
|
||||
<p><strong>Attention, ce formulaire est réservé uniquement aux organismes publics.</strong>
|
||||
Il ne concerne ni les particuliers, ni les entreprises, ni les associations (sauf celles reconnues d’utilité publique). Si c'est votre cas, rendez-vous sur notre
|
||||
<a href=%{contact_path}>formulaire de contact public</a>.</p>"
|
||||
contact_team: Contactez notre équipe
|
||||
pro_phone_number: Numéro de téléphone professionnel (ligne directe)
|
||||
pro_mail: Adresse e-mail professionnelle
|
||||
admin demande rdv: Demande de RDV pour une présentation à distance de %{app_name}
|
||||
admin question: J’ai une question sur %{app_name}
|
||||
admin soucis: J’ai un problème technique avec %{app_name}
|
||||
admin suggestion produit: J’ai une proposition d’évolution
|
||||
admin demande compte: Je souhaite ouvrir un compte administrateur avec un email Orange, Wanadoo, etc.
|
||||
admin autre: Autre sujet
|
16
config/locales/views/users/profil/fr.yml
Normal file
16
config/locales/views/users/profil/fr.yml
Normal file
|
@ -0,0 +1,16 @@
|
|||
fr:
|
||||
users:
|
||||
profil:
|
||||
show:
|
||||
transfer_title: Transferer tous vos dossiers
|
||||
transfer_explication_html: "<p>Cette fonctionnalité vous permet de changer le propriétaire de tous vos dossiers. C'est généralement utile lors d'un changement de poste ou si vous souhaitez fusionner plusieurs comptes.</p>
|
||||
<p>Adresse email du destinataire de tous vos dossiers</p>"
|
||||
waiting_transfers: "Transfert en attente :"
|
||||
one_waiting_transfer:
|
||||
one: "Le nouveau propriétaire %{email} doit confirmer le transfert d'un dossier en suivant les instructions reçues dans son mail."
|
||||
other: "Le nouveau propriétaire %{email} doit confirmer le transfert de vos %{count} dossiers en suivant les instructions reçues dans son mail."
|
||||
transfer_confirmation: "Confirmez-vous le transfert ?"
|
||||
transfer_all_dossiers:
|
||||
new_transfer:
|
||||
one: "Le transfert d'un dossier à %{email} est en cours"
|
||||
other: "Le transfert de %{count} dossiers à %{email} est en cours"
|
|
@ -276,6 +276,7 @@ Rails.application.routes.draw do
|
|||
# allow refresh 'renew api token' page
|
||||
get 'renew-api-token' => redirect('/profil')
|
||||
patch 'update_email' => 'profil#update_email'
|
||||
post 'transfer_all_dossiers' => 'profil#transfer_all_dossiers'
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -397,6 +398,13 @@ Rails.application.routes.draw do
|
|||
put :experts_require_administrateur_invitation
|
||||
end
|
||||
|
||||
get :api_particulier, controller: 'jeton_particulier'
|
||||
|
||||
resource 'api_particulier', only: [] do
|
||||
resource 'jeton', only: [:show, :update], controller: 'jeton_particulier'
|
||||
resource 'sources', only: [:show, :update], controller: 'sources_particulier'
|
||||
end
|
||||
|
||||
put 'clone'
|
||||
put 'archive'
|
||||
get 'publication' => 'procedures#publication', as: :publication
|
||||
|
@ -415,6 +423,7 @@ Rails.application.routes.draw do
|
|||
|
||||
collection do
|
||||
patch 'update_routing_criteria_name'
|
||||
patch 'update_routing_enabled'
|
||||
post 'import'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class AddAPIParticulierScopesToProcedure < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
add_column :procedures, :api_particulier_scopes, :text, array: true, default: []
|
||||
end
|
||||
end
|
|
@ -0,0 +1,6 @@
|
|||
class AddAPIParticulierSourcesToProcedure < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column :procedures, :api_particulier_sources, :jsonb, :default => {}
|
||||
add_index :procedures, :api_particulier_sources, using: :gin
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
class AddRoutingEnabledToProcedures < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
add_column :procedures, :routing_enabled, :boolean
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2021_08_27_161956) do
|
||||
ActiveRecord::Schema.define(version: 2021_09_15_170019) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -617,8 +617,12 @@ ActiveRecord::Schema.define(version: 2021_08_27_161956) do
|
|||
t.bigint "draft_revision_id"
|
||||
t.bigint "published_revision_id"
|
||||
t.boolean "allow_expert_review", default: true, null: false
|
||||
t.string "encrypted_api_particulier_token"
|
||||
t.boolean "experts_require_administrateur_invitation", default: false
|
||||
t.string "encrypted_api_particulier_token"
|
||||
t.text "api_particulier_scopes", default: [], array: true
|
||||
t.jsonb "api_particulier_sources", default: {}
|
||||
t.index ["api_particulier_sources"], name: "index_procedures_on_api_particulier_sources", using: :gin
|
||||
t.boolean "routing_enabled"
|
||||
t.index ["declarative_with_state"], name: "index_procedures_on_declarative_with_state"
|
||||
t.index ["draft_revision_id"], name: "index_procedures_on_draft_revision_id"
|
||||
t.index ["hidden_at"], name: "index_procedures_on_hidden_at"
|
||||
|
|
|
@ -2,6 +2,9 @@ task :lint do
|
|||
sh "bundle exec rubocop --parallel"
|
||||
sh "bundle exec haml-lint app/views/"
|
||||
sh "bundle exec scss-lint app/assets/stylesheets/"
|
||||
sh "bundle exec i18n-tasks missing --locales fr"
|
||||
sh "bundle exec i18n-tasks unused --locale en" # TODO: check for all locales
|
||||
sh "bundle exec i18n-tasks check-consistent-interpolations"
|
||||
sh "bundle exec brakeman --no-pager"
|
||||
sh "yarn lint:js"
|
||||
end
|
||||
|
|
|
@ -17,12 +17,27 @@ describe DevisePopulatedResource, type: :controller do
|
|||
end
|
||||
|
||||
context 'when initiating a password reset' do
|
||||
subject { get :edit, params: { reset_password_token: @token } }
|
||||
subject { get :edit, params: { reset_password_token: token } }
|
||||
|
||||
it 'returns the fully populated resource' do
|
||||
subject
|
||||
expect(controller.populated_resource.id).to eq(user.id)
|
||||
expect(controller.populated_resource.email).to eq(user.email)
|
||||
context 'with a valid token' do
|
||||
let(:token) { @token }
|
||||
|
||||
it 'returns the fully populated resource' do
|
||||
subject
|
||||
expect(controller.populated_resource.id).to eq(user.id)
|
||||
expect(controller.populated_resource.email).to eq(user.email)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an expired token' do
|
||||
let(:token) { 'invalid-token' }
|
||||
|
||||
it 'returns a new blank resource' do
|
||||
subject
|
||||
expect(controller.populated_resource).to be_present
|
||||
expect(controller.populated_resource.new_record?).to be(true)
|
||||
expect(controller.populated_resource.email).to be_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ describe NewAdministrateur::GroupeInstructeursController, type: :controller do
|
|||
render_views
|
||||
|
||||
let(:admin) { create(:administrateur) }
|
||||
let(:procedure) { create(:procedure, :published, :for_individual, administrateurs: [admin]) }
|
||||
let(:procedure) { create(:procedure, :published, :for_individual, administrateurs: [admin], routing_enabled: true) }
|
||||
let!(:gi_1_1) { procedure.defaut_groupe_instructeur }
|
||||
|
||||
let(:procedure2) { create(:procedure, :published) }
|
||||
|
@ -222,7 +222,7 @@ describe NewAdministrateur::GroupeInstructeursController, type: :controller do
|
|||
it { expect(response.status).to eq(200) }
|
||||
it { expect(subject.request.flash[:alert]).to be_nil }
|
||||
it { expect(subject.request.flash[:notice]).to be_present }
|
||||
it { expect(subject).to redirect_to admin_procedure_groupe_instructeur_path(procedure, procedure.defaut_groupe_instructeur) }
|
||||
it { expect(subject).to redirect_to admin_procedure_groupe_instructeurs_path(procedure) }
|
||||
end
|
||||
|
||||
context 'when there is at least one bad email' do
|
||||
|
@ -230,13 +230,13 @@ describe NewAdministrateur::GroupeInstructeursController, type: :controller do
|
|||
it { expect(response.status).to eq(200) }
|
||||
it { expect(subject.request.flash[:alert]).to be_present }
|
||||
it { expect(subject.request.flash[:notice]).to be_present }
|
||||
it { expect(subject).to redirect_to admin_procedure_groupe_instructeur_path(procedure, procedure.defaut_groupe_instructeur) }
|
||||
it { expect(subject).to redirect_to admin_procedure_groupe_instructeurs_path(procedure) }
|
||||
end
|
||||
|
||||
context 'when the admin wants to assign an instructor who is already assigned on this procedure' do
|
||||
let(:emails) { ['instructeur_1@ministere_a.gouv.fr'].to_json }
|
||||
it { expect(subject.request.flash[:alert]).to be_present }
|
||||
it { expect(subject).to redirect_to admin_procedure_groupe_instructeur_path(procedure, procedure.defaut_groupe_instructeur) }
|
||||
it { expect(subject).to redirect_to admin_procedure_groupe_instructeurs_path(procedure) }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -344,7 +344,7 @@ describe NewAdministrateur::GroupeInstructeursController, type: :controller do
|
|||
it { expect(subject.request.flash[:notice]).to be_present }
|
||||
it { expect(subject.request.flash[:alert]).to be_nil }
|
||||
it { expect(response.status).to eq(302) }
|
||||
it { expect(subject).to redirect_to admin_procedure_groupe_instructeur_path(procedure, gi_1_1) }
|
||||
it { expect(subject).to redirect_to admin_procedure_groupe_instructeurs_path(procedure) }
|
||||
end
|
||||
|
||||
context 'when the instructor is not assigned to the procedure' do
|
||||
|
@ -352,7 +352,7 @@ describe NewAdministrateur::GroupeInstructeursController, type: :controller do
|
|||
it { expect(subject.request.flash[:alert]).to be_present }
|
||||
it { expect(subject.request.flash[:notice]).to be_nil }
|
||||
it { expect(response.status).to eq(302) }
|
||||
it { expect(subject).to redirect_to admin_procedure_groupe_instructeur_path(procedure, gi_1_1) }
|
||||
it { expect(subject).to redirect_to admin_procedure_groupe_instructeurs_path(procedure) }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
describe NewAdministrateur::JetonParticulierController, type: :controller do
|
||||
let(:admin) { create(:administrateur) }
|
||||
let(:procedure) { create(:procedure, administrateur: admin) }
|
||||
|
||||
before do
|
||||
stub_const("API_PARTICULIER_URL", "https://particulier.api.gouv.fr/api")
|
||||
|
||||
sign_in(admin.user)
|
||||
end
|
||||
|
||||
describe "GET #api_particulier" do
|
||||
render_views
|
||||
|
||||
subject { get :api_particulier, params: { procedure_id: procedure.id } }
|
||||
|
||||
it do
|
||||
is_expected.to have_http_status(:success)
|
||||
expect(subject.body).to have_content('Jeton API particulier')
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET #show" do
|
||||
subject { get :show, params: { procedure_id: procedure.id } }
|
||||
|
||||
it { is_expected.to have_http_status(:success) }
|
||||
end
|
||||
|
||||
describe "PATCH #update" do
|
||||
let(:params) { { procedure_id: procedure.id, procedure: { api_particulier_token: token } } }
|
||||
|
||||
subject { patch :update, params: params }
|
||||
|
||||
context "when jeton has a valid shape" do
|
||||
let(:token) { "d7e9c9f4c3ca00caadde31f50fd4521a" }
|
||||
before do
|
||||
VCR.use_cassette(cassette) do
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context "and the api response is a success" do
|
||||
let(:cassette) { "api_particulier/success/introspect" }
|
||||
let(:procedure) { create(:procedure, administrateur: admin, api_particulier_sources: { cnaf: { allocataires: ['noms_prenoms'] } }) }
|
||||
|
||||
it 'saves the jeton' do
|
||||
expect(flash.alert).to be_nil
|
||||
expect(flash.notice).to eq("Le jeton a bien été mis à jour")
|
||||
expect(procedure.reload.api_particulier_token).to eql(token)
|
||||
expect(procedure.reload.api_particulier_scopes).to contain_exactly("dgfip_avis_imposition", "dgfip_adresse", "cnaf_allocataires", "cnaf_enfants", "cnaf_adresse", "cnaf_quotient_familial", "mesri_statut_etudiant")
|
||||
expect(procedure.reload.api_particulier_sources).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context "and the api response is a success but with an empty scopes" do
|
||||
let(:cassette) { "api_particulier/success/introspect_empty_scopes" }
|
||||
|
||||
it 'rejects the jeton' do
|
||||
expect(flash.alert).to include("le jeton n'a pas acces aux données")
|
||||
expect(flash.notice).to be_nil
|
||||
expect(procedure.reload.api_particulier_token).not_to eql(token)
|
||||
end
|
||||
end
|
||||
|
||||
context "and the api response is not unauthorized" do
|
||||
let(:cassette) { "api_particulier/unauthorized/introspect" }
|
||||
|
||||
it 'rejects the jeton' do
|
||||
expect(flash.alert).to include("Mise à jour impossible : le jeton n'a pas été trouvé ou n'est pas actif")
|
||||
expect(flash.notice).to be_nil
|
||||
expect(procedure.reload.api_particulier_token).not_to eql(token)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when jeton is invalid and no network call is made" do
|
||||
let(:token) { "jet0n 1nvalide" }
|
||||
|
||||
before { subject }
|
||||
|
||||
it 'rejects the jeton' do
|
||||
expect(flash.alert.first).to include("pas le bon format")
|
||||
expect(flash.notice).to be_nil
|
||||
expect(procedure.reload.api_particulier_token).not_to eql(token)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,60 @@
|
|||
describe NewAdministrateur::SourcesParticulierController, type: :controller do
|
||||
let(:admin) { create(:administrateur) }
|
||||
|
||||
before { sign_in(admin.user) }
|
||||
|
||||
describe "#show" do
|
||||
let(:procedure) { create(:procedure, administrateur: admin, api_particulier_scopes: ['cnaf_enfants'], api_particulier_sources: { cnaf: { enfants: ['noms_prenoms'] } }) }
|
||||
|
||||
render_views
|
||||
|
||||
subject { get :show, params: { procedure_id: procedure.id } }
|
||||
|
||||
it 'renders the sources form' do
|
||||
expect(subject.body).to include(I18n.t('api_particulier.providers.cnaf.scopes.enfants.date_de_naissance'))
|
||||
expect(subject.body).to have_selector("input#api_particulier_sources_cnaf_enfants_[value=noms_prenoms][checked=checked]")
|
||||
|
||||
expect(subject.body).to have_selector("input#api_particulier_sources_cnaf_enfants_[value=date_de_naissance]")
|
||||
expect(subject.body).not_to have_selector("input#api_particulier_sources_cnaf_enfants_[value=date_de_naissance][checked=checked]")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#update" do
|
||||
let(:procedure) { create(:procedure, administrateur: admin, api_particulier_scopes: ['cnaf_enfants'], api_particulier_sources: {}) }
|
||||
let(:params) { { procedure_id: procedure.id }.merge(requested_sources) }
|
||||
|
||||
before do
|
||||
patch :update, params: params
|
||||
procedure.reload
|
||||
end
|
||||
|
||||
context 'when no source is requested' do
|
||||
let(:requested_sources) { {} }
|
||||
|
||||
it { expect(procedure.api_particulier_sources).to be_empty }
|
||||
end
|
||||
|
||||
context 'when a forbidden source is requested' do
|
||||
let(:requested_sources) do
|
||||
{
|
||||
api_particulier_sources: { cnaf: { enfants: ['forbidden'] } }
|
||||
}
|
||||
end
|
||||
|
||||
it { expect(procedure.api_particulier_sources).to be_empty }
|
||||
end
|
||||
|
||||
context 'when an authorized source is requested' do
|
||||
let(:requested_sources) do
|
||||
{
|
||||
api_particulier_sources: { cnaf: { enfants: ['noms_prenoms'] } }
|
||||
}
|
||||
end
|
||||
|
||||
it 'saves the source' do
|
||||
expect(procedure.api_particulier_sources).to eq("cnaf" => { "enfants" => ["noms_prenoms"] })
|
||||
expect(flash.notice).to eq(I18n.t(".new_administrateur.sources_particulier.update.sources_ok"))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,6 +5,32 @@ describe Users::ProfilController, type: :controller do
|
|||
|
||||
before { sign_in(user) }
|
||||
|
||||
describe 'GET #show' do
|
||||
render_views
|
||||
|
||||
before { post :show }
|
||||
|
||||
context 'when the current user is not an instructeur' do
|
||||
it { expect(response.body).to include(I18n.t('users.profil.show.transfer_title')) }
|
||||
|
||||
context 'when an existing transfer exists' do
|
||||
let(:dossiers) { Array.new(3) { create(:dossier, user: user) } }
|
||||
let(:next_owner) { 'loulou@lou.com' }
|
||||
let!(:transfer) { DossierTransfer.initiate(next_owner, dossiers) }
|
||||
|
||||
before { post :show }
|
||||
|
||||
it { expect(response.body).to include(I18n.t('users.profil.show.one_waiting_transfer', count: dossiers.count, email: next_owner)) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the current user is an instructeur' do
|
||||
let(:user) { create(:instructeur).user }
|
||||
|
||||
it { expect(response.body).not_to include(I18n.t('users.profil.show.transfer_title')) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #renew_api_token' do
|
||||
let(:administrateur) { create(:administrateur) }
|
||||
|
||||
|
@ -72,4 +98,20 @@ describe Users::ProfilController, type: :controller do
|
|||
it { expect(response).to redirect_to(profil_path) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'POST #transfer_all_dossiers' do
|
||||
let!(:dossiers) { Array.new(3) { create(:dossier, user: user) } }
|
||||
let(:next_owner) { 'loulou@lou.com' }
|
||||
let(:created_transfer) { DossierTransfer.first }
|
||||
|
||||
before do
|
||||
post :transfer_all_dossiers, params: { next_owner: next_owner }
|
||||
end
|
||||
|
||||
it "transfer all dossiers" do
|
||||
expect(created_transfer.email).to eq(next_owner)
|
||||
expect(created_transfer.dossiers).to eq(dossiers)
|
||||
expect(flash.notice).to eq("Le transfert de 3 dossiers à #{next_owner} est en cours")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,8 +6,8 @@ feature 'The routing', js: true do
|
|||
let(:litteraire_user) { create(:user, password: password) }
|
||||
|
||||
before do
|
||||
procedure.update(routing_enabled: true)
|
||||
procedure.defaut_groupe_instructeur.instructeurs << administrateur.instructeur
|
||||
Flipper.enable_actor(:administrateur_routage, administrateur.user)
|
||||
end
|
||||
|
||||
scenario 'works' do
|
||||
|
@ -32,14 +32,14 @@ feature 'The routing', js: true do
|
|||
# add victor to littéraire groupe
|
||||
find("input[aria-label='email instructeur'").send_keys('victor@inst.com', :enter)
|
||||
perform_enqueued_jobs { click_on 'Affecter' }
|
||||
expect(page).to have_text("Les instructeurs ont bien été affectés à la démarche")
|
||||
expect(page).to have_text("L’instructeur victor@inst.com a été affecté au groupe « littéraire »")
|
||||
|
||||
victor = User.find_by(email: 'victor@inst.com').instructeur
|
||||
|
||||
# add superwoman to littéraire groupe
|
||||
find("input[aria-label='email instructeur'").send_keys('superwoman@inst.com', :enter)
|
||||
perform_enqueued_jobs { click_on 'Affecter' }
|
||||
expect(page).to have_text("Les instructeurs ont bien été affectés à la démarche")
|
||||
expect(page).to have_text("L’instructeur superwoman@inst.com a été affecté au groupe « littéraire »")
|
||||
|
||||
superwoman = User.find_by(email: 'superwoman@inst.com').instructeur
|
||||
|
||||
|
|
|
@ -98,4 +98,14 @@ feature 'Managing password:' do
|
|||
expect(page).to have_content('Votre mot de passe a bien été modifié.')
|
||||
end
|
||||
end
|
||||
|
||||
scenario 'the password reset token has expired' do
|
||||
visit edit_user_password_path(reset_password_token: 'invalid-password-token')
|
||||
expect(page).to have_content 'Changement de mot de passe'
|
||||
|
||||
fill_in 'user_password', with: 'SomePassword'
|
||||
fill_in 'user_password_confirmation', with: 'SomePassword'
|
||||
click_on 'Changer le mot de passe'
|
||||
expect(page).to have_content('Votre lien de nouveau mot de passe a expiré')
|
||||
end
|
||||
end
|
||||
|
|
44
spec/fixtures/cassettes/api_particulier/success/introspect.yml
vendored
Normal file
44
spec/fixtures/cassettes/api_particulier/success/introspect.yml
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
http_interactions:
|
||||
- request:
|
||||
method: get
|
||||
uri: https://particulier.api.gouv.fr/api/introspect
|
||||
body:
|
||||
encoding: US-ASCII
|
||||
string: ''
|
||||
headers:
|
||||
User-Agent:
|
||||
- demarches-simplifiees.fr
|
||||
Accept:
|
||||
- application/json
|
||||
X-Api-Key:
|
||||
- d7e9c9f4c3ca00caadde31f50fd4521a
|
||||
Expect:
|
||||
- ''
|
||||
response:
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
headers:
|
||||
Date:
|
||||
- Tue, 16 Mar 2021 15:25:24 GMT
|
||||
Content-Type:
|
||||
- application/json
|
||||
Content-Length:
|
||||
- '228'
|
||||
Connection:
|
||||
- keep-alive
|
||||
Keep-Alive:
|
||||
- timeout=5
|
||||
X-Gravitee-Request-Id:
|
||||
- 0e4dd327-de40-4052-8dd3-27de401052c4
|
||||
X-Gravitee-Transaction-Id:
|
||||
- cc30bb74-6516-46d9-b0bb-746516d6d904
|
||||
Strict-Transport-Security:
|
||||
- max-age=15552000
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: '{"_id":"1d99db5a-a099-4314-ad2f-2707c6b505a6","name":"Application de
|
||||
sandbox","scopes":["dgfip_avis_imposition","dgfip_adresse","cnaf_allocataires","cnaf_enfants","cnaf_adresse","cnaf_quotient_familial","mesri_statut_etudiant"]}'
|
||||
recorded_at: Tue, 16 Mar 2021 15:25:24 GMT
|
||||
recorded_with: VCR 6.0.0
|
44
spec/fixtures/cassettes/api_particulier/success/introspect_empty_scopes.yml
vendored
Normal file
44
spec/fixtures/cassettes/api_particulier/success/introspect_empty_scopes.yml
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
http_interactions:
|
||||
- request:
|
||||
method: get
|
||||
uri: https://particulier.api.gouv.fr/api/introspect
|
||||
body:
|
||||
encoding: US-ASCII
|
||||
string: ''
|
||||
headers:
|
||||
User-Agent:
|
||||
- demarches-simplifiees.fr
|
||||
Accept:
|
||||
- application/json
|
||||
X-Api-Key:
|
||||
- d7e9c9f4c3ca00caadde31f50fd4521a
|
||||
Expect:
|
||||
- ''
|
||||
response:
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
headers:
|
||||
Date:
|
||||
- Tue, 16 Mar 2021 15:25:24 GMT
|
||||
Content-Type:
|
||||
- application/json
|
||||
Content-Length:
|
||||
- '228'
|
||||
Connection:
|
||||
- keep-alive
|
||||
Keep-Alive:
|
||||
- timeout=5
|
||||
X-Gravitee-Request-Id:
|
||||
- 0e4dd327-de40-4052-8dd3-27de401052c4
|
||||
X-Gravitee-Transaction-Id:
|
||||
- cc30bb74-6516-46d9-b0bb-746516d6d904
|
||||
Strict-Transport-Security:
|
||||
- max-age=15552000
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: '{"_id":"1d99db5a-a099-4314-ad2f-2707c6b505a6","name":"Application de
|
||||
sandbox","scopes":[]}'
|
||||
recorded_at: Tue, 16 Mar 2021 15:25:24 GMT
|
||||
recorded_with: VCR 6.0.0
|
44
spec/fixtures/cassettes/api_particulier/unauthorized/introspect.yml
vendored
Normal file
44
spec/fixtures/cassettes/api_particulier/unauthorized/introspect.yml
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
http_interactions:
|
||||
- request:
|
||||
method: get
|
||||
uri: https://particulier.api.gouv.fr/api/introspect
|
||||
body:
|
||||
encoding: US-ASCII
|
||||
string: ''
|
||||
headers:
|
||||
User-Agent:
|
||||
- demarches-simplifiees.fr
|
||||
Accept:
|
||||
- application/json
|
||||
X-Api-Key:
|
||||
- d7e9c9f4c3ca00caadde31f50fd4521a
|
||||
Expect:
|
||||
- ''
|
||||
response:
|
||||
status:
|
||||
code: 401
|
||||
message: ''
|
||||
headers:
|
||||
Server:
|
||||
- nginx
|
||||
Date:
|
||||
- Wed, 15 Sep 2021 10:02:12 GMT
|
||||
Content-Type:
|
||||
- application/json; charset=utf-8
|
||||
Content-Length:
|
||||
- '134'
|
||||
X-Powered-By:
|
||||
- Express
|
||||
Vary:
|
||||
- Origin
|
||||
Etag:
|
||||
- W/"86-FwFf7uuVKCSJkazn1ZHnY1yVYUo"
|
||||
Strict-Transport-Security:
|
||||
- max-age=15724800; includeSubdomains
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: >
|
||||
{"error":"acces_denied","reason":"Token not found or inactive","message":"Votre jeton d'API n'a pas été trouvé ou n'est pas actif"}
|
||||
recorded_at: Wed, 15 Sep 2021 10:02:12 GMT
|
||||
recorded_with: VCR 6.0.0
|
28
spec/lib/api_particulier/api_spec.rb
Normal file
28
spec/lib/api_particulier/api_spec.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
describe APIParticulier::API do
|
||||
let(:token) { "d7e9c9f4c3ca00caadde31f50fd4521a" }
|
||||
let(:api) { APIParticulier::API.new(token) }
|
||||
|
||||
before { stub_const("API_PARTICULIER_URL", "https://particulier.api.gouv.fr/api") }
|
||||
|
||||
describe "scopes" do
|
||||
subject { api.scopes }
|
||||
|
||||
it "doit retourner une liste de scopes" do
|
||||
VCR.use_cassette("api_particulier/success/introspect") do
|
||||
expect(subject).to match_array(['dgfip_avis_imposition', 'dgfip_adresse', 'cnaf_allocataires', 'cnaf_enfants', 'cnaf_adresse', 'cnaf_quotient_familial', 'mesri_statut_etudiant'])
|
||||
end
|
||||
end
|
||||
|
||||
it "returns an unauthorized exception" do
|
||||
VCR.use_cassette("api_particulier/unauthorized/introspect") do
|
||||
begin
|
||||
subject
|
||||
rescue APIParticulier::Error::Unauthorized => e
|
||||
expect(e.message).to include('url: particulier.api.gouv.fr/api/introspect')
|
||||
expect(e.message).to include('HTTP error code: 401')
|
||||
expect(e.message).to include("Votre jeton d'API n'a pas été trouvé ou n'est pas actif")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
53
spec/lib/api_particulier/services/sources_service_spec.rb
Normal file
53
spec/lib/api_particulier/services/sources_service_spec.rb
Normal file
|
@ -0,0 +1,53 @@
|
|||
describe APIParticulier::Services::SourcesService do
|
||||
let(:service) { described_class.new(procedure) }
|
||||
|
||||
let(:procedure) { create(:procedure) }
|
||||
let(:api_particulier_scopes) { [] }
|
||||
let(:api_particulier_sources) { {} }
|
||||
|
||||
before do
|
||||
procedure.update(api_particulier_scopes: api_particulier_scopes)
|
||||
procedure.update(api_particulier_sources: api_particulier_sources)
|
||||
end
|
||||
|
||||
describe "#available_sources" do
|
||||
subject { service.available_sources }
|
||||
|
||||
context 'when the procedure doesn’t have any available scopes' do
|
||||
it { is_expected.to eq({}) }
|
||||
end
|
||||
|
||||
context 'when a procedure has a cnaf_allocataires and a cnaf_adresse scopes' do
|
||||
let(:api_particulier_scopes) { ['cnaf_allocataires', 'cnaf_enfants'] }
|
||||
|
||||
let(:cnaf_allocataires_and_enfants) do
|
||||
{
|
||||
'cnaf' => {
|
||||
'allocataires' => ['noms_prenoms', 'date_de_naissance', 'sexe'],
|
||||
'enfants' => ['noms_prenoms', 'date_de_naissance', 'sexe']
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
it { is_expected.to match(cnaf_allocataires_and_enfants) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#sanitize' do
|
||||
subject { service.sanitize(requested_sources) }
|
||||
|
||||
let(:api_particulier_scopes) { ['cnaf_allocataires', 'cnaf_adresse'] }
|
||||
let(:requested_sources) do
|
||||
{
|
||||
'cnaf' => {
|
||||
'allocataires' => ['noms_prenoms', 'forbidden_sources', { 'weird_object' => 1 }],
|
||||
'forbidden_scope' => ['any_source'],
|
||||
'adresse' => { 'weird_object' => 1 }
|
||||
},
|
||||
'forbidden_provider' => { 'anything_scope' => ['any_source'] }
|
||||
}
|
||||
end
|
||||
|
||||
it { is_expected.to eq({ 'cnaf' => { 'allocataires' => ['noms_prenoms'] } }) }
|
||||
end
|
||||
end
|
|
@ -21,7 +21,7 @@ describe Champs::PieceJustificativeChamp do
|
|||
subject { champ_pj }
|
||||
|
||||
context "by default" do
|
||||
it { is_expected.to validate_size_of(:piece_justificative_file).less_than(Champs::PieceJustificativeChamp::MAX_SIZE) }
|
||||
it { is_expected.to validate_size_of(:piece_justificative_file).less_than(Champs::PieceJustificativeChamp::FILE_MAX_SIZE) }
|
||||
it { is_expected.to validate_content_type_of(:piece_justificative_file).rejecting('application/x-ms-dos-executable') }
|
||||
it { expect(champ_pj.type_de_champ.skip_pj_validation).to be_falsy }
|
||||
end
|
||||
|
@ -29,7 +29,7 @@ describe Champs::PieceJustificativeChamp do
|
|||
context "when validation is disabled" do
|
||||
before { champ_pj.type_de_champ.update(skip_pj_validation: true) }
|
||||
|
||||
it { is_expected.not_to validate_size_of(:piece_justificative_file).less_than(Champs::PieceJustificativeChamp::MAX_SIZE) }
|
||||
it { is_expected.not_to validate_size_of(:piece_justificative_file).less_than(Champs::PieceJustificativeChamp::FILE_MAX_SIZE) }
|
||||
end
|
||||
|
||||
context "when content-type validation is disabled" do
|
||||
|
|
|
@ -326,7 +326,7 @@ describe Procedure do
|
|||
|
||||
describe 'clone' do
|
||||
let(:service) { create(:service) }
|
||||
let(:procedure) { create(:procedure, received_mail: received_mail, service: service, types_de_champ: [type_de_champ_0, type_de_champ_1, type_de_champ_2, type_de_champ_pj, type_de_champ_repetition], types_de_champ_private: [type_de_champ_private_0, type_de_champ_private_1, type_de_champ_private_2, type_de_champ_private_repetition]) }
|
||||
let(:procedure) { create(:procedure, received_mail: received_mail, service: service, types_de_champ: [type_de_champ_0, type_de_champ_1, type_de_champ_2, type_de_champ_pj, type_de_champ_repetition], types_de_champ_private: [type_de_champ_private_0, type_de_champ_private_1, type_de_champ_private_2, type_de_champ_private_repetition], api_particulier_token: '123456789012345', api_particulier_scopes: ['cnaf_famille']) }
|
||||
let(:type_de_champ_0) { build(:type_de_champ, position: 0) }
|
||||
let(:type_de_champ_1) { build(:type_de_champ, position: 1) }
|
||||
let(:type_de_champ_2) { build(:type_de_champ_drop_down_list, position: 2) }
|
||||
|
@ -471,6 +471,11 @@ describe Procedure do
|
|||
expect(subject.groupe_instructeurs.where(label: "groupe_1").first).to be nil
|
||||
end
|
||||
|
||||
it "should discard api_particulier_scopes and token" do
|
||||
expect(subject.encrypted_api_particulier_token).to be_nil
|
||||
expect(subject.api_particulier_scopes).to be_empty
|
||||
end
|
||||
|
||||
it 'should have a default groupe instructeur' do
|
||||
expect(subject.groupe_instructeurs.size).to eq(1)
|
||||
expect(subject.groupe_instructeurs.first.label).to eq(GroupeInstructeur::DEFAUT_LABEL)
|
||||
|
|
Loading…
Add table
Reference in a new issue