Merge pull request #9621 from mfo/US/type-de-champs-ej

amelioration(chorus): ETQ admin, je peux ajouter un type de champ EngagementJuridique
This commit is contained in:
mfo 2023-11-09 14:40:49 +00:00 committed by GitHub
commit b61fa88fe0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 351 additions and 91 deletions

View file

@ -0,0 +1,2 @@
class EditableChamp::EngagementJuridiqueComponent < EditableChamp::EditableChampBaseComponent
end

View file

@ -0,0 +1,2 @@
---
en:

View file

@ -0,0 +1 @@
= @form.text_field(:value, input_opts(id: @champ.input_id, required: @champ.required?, aria: { describedby: @champ.describedby_id }))

View file

@ -0,0 +1,2 @@
---
fr:

View file

@ -3,13 +3,40 @@ class Procedure::ChorusFormComponent < ApplicationComponent
def initialize(procedure:)
@procedure = procedure
@chorus_configuration = @procedure.chorus_configuration
end
def map_attribute_to_autocomplete_endpoint
{
centre_de_coup: data_sources_search_centre_couts_path,
centre_de_cout: data_sources_search_centre_couts_path,
domaine_fonctionnel: data_sources_search_domaine_fonct_path,
referentiel_de_programmation: data_sources_search_ref_programmation_path
}
end
def format_displayed_value(attribute_name)
case attribute_name
when :centre_de_cout
ChorusConfiguration.format_centre_de_cout_label(@chorus_configuration.centre_de_cout)
when :domaine_fonctionnel
ChorusConfiguration.format_domaine_fonctionnel_label(@chorus_configuration.domaine_fonctionnel)
when :referentiel_de_programmation
ChorusConfiguration.format_ref_programmation_label(@chorus_configuration.referentiel_de_programmation)
else
raise 'unknown attribute_name'
end
end
def format_hidden_value(attribute_name)
case attribute_name
when :centre_de_cout
@chorus_configuration.centre_de_cout.to_json
when :domaine_fonctionnel
@chorus_configuration.domaine_fonctionnel.to_json
when :referentiel_de_programmation
@chorus_configuration.referentiel_de_programmation.to_json
else
raise 'unknown attribute_name'
end
end
end

View file

@ -1,9 +1,15 @@
= form_for([procedure, procedure.chorus_configuration],url: admin_procedure_chorus_path(procedure), method: :put) do |f|
= form_for([procedure, @chorus_configuration],url: admin_procedure_chorus_path(procedure), method: :put) do |f|
- map_attribute_to_autocomplete_endpoint.map do |chorus_configuration_attribute, datasource_endpoint|
- label_class_name = "#{chorus_configuration_attribute}-label"
- label_id = "#{chorus_configuration_attribute}-label"
.fr-select-group
= f.label chorus_configuration_attribute, class: 'fr-label', id: label_class_name
= render Dsfr::ComboboxComponent.new form: f, name: :chorus_configuration_attribute, url: datasource_endpoint, selected: procedure.chorus_configuration.format_displayed_value(chorus_configuration_attribute), id: chorus_configuration_attribute, class: 'fr-select', describedby: label_class_name do
= f.hidden_field chorus_configuration_attribute, data: { value_slot: 'data' }, value: procedure.chorus_configuration.format_hidden_value(chorus_configuration_attribute)
= f.label chorus_configuration_attribute, class: 'fr-label', id: label_id
= render Dsfr::ComboboxComponent.new form: f,
url: datasource_endpoint,
selected: format_displayed_value(chorus_configuration_attribute),
id: chorus_configuration_attribute,
class: 'fr-select',
describedby: label_id,
name: :chorus_configuration_attribute do
= f.hidden_field chorus_configuration_attribute, data: { value_slot: 'data' }, value: format_hidden_value(chorus_configuration_attribute)
= f.submit "Enregister", class: 'fr-btn'

View file

@ -63,6 +63,7 @@ class TypesDeChampEditor::ChampComponent < ApplicationComponent
.filter(&method(:filter_type_champ))
.filter(&method(:filter_featured_type_champ))
.filter(&method(:filter_block_type_champ))
.filter(&method(:filter_public_or_private_only_type_champ))
.group_by { TypeDeChamp::TYPE_DE_CHAMP_TO_CATEGORIE.fetch(_1.to_sym) }
.sort_by { |k, _v| TypeDeChamp::CATEGORIES.find_index(k) }
.to_h do |cat, tdc|
@ -91,6 +92,14 @@ class TypesDeChampEditor::ChampComponent < ApplicationComponent
!coordinate.child? || !EXCLUDE_FROM_BLOCK.include?(type_champ)
end
def filter_public_or_private_only_type_champ(type_champ)
if coordinate.private?
true
else
!TypeDeChamp::PRIVATE_ONLY_TYPES.include?(type_champ)
end
end
def filter_featured_type_champ(type_champ)
feature_name = TypeDeChamp::FEATURE_FLAGS[type_champ.to_sym]
feature_name.blank? || procedure.feature_enabled?(feature_name)

View file

@ -11,14 +11,22 @@ module Administrateurs
if @configuration.valid?
@procedure.update!(chorus: @configuration.attributes)
flash.notice = "La configuration Chorus a été mise à jour et prend immédiatement effet pour les nouveaux dossiers."
redirect_to admin_procedure_path(@procedure)
if @configuration.complete?
flash.notice = "La configuration Chorus a été mise à jour."
redirect_to add_champ_engagement_juridique_admin_procedure_chorus_path(@procedure)
else
flash.notice = "La configuration Chorus a été mise à jour. Veuillez renseigner le reste des informations pour faciliter le rapprochement des données."
redirect_to edit_admin_procedure_chorus_path(@procedure)
end
else
flash.now.alert = "Des erreurs empêchent la validation du connecteur chorus. Corrigez les erreurs"
render :edit
end
end
def add_champ_engagement_juridique
end
private
def search_params
@ -27,7 +35,7 @@ module Administrateurs
def configurations_params
params.require(:chorus_configuration)
.permit(:centre_de_coup, :domaine_fonctionnel, :referentiel_de_programmation)
.permit(:centre_de_cout, :domaine_fonctionnel, :referentiel_de_programmation)
end
end
end

View file

@ -10,7 +10,7 @@ class DataSources::ChorusController < ApplicationController
def search_centre_couts
result_json = APIBretagneService.new.search_centre_couts(code_or_label: params[:q])
render json: format_result(result_json:,
label_formatter: ChorusConfiguration.method(:format_centre_de_coup_label))
label_formatter: ChorusConfiguration.method(:format_centre_de_cout_label))
end
def search_ref_programmation
@ -25,7 +25,7 @@ class DataSources::ChorusController < ApplicationController
result_json.map do |item|
{
label: label_formatter.call(item),
value: "#{item[:label]} - #{item[:code_programme]}",
value: item[:code],
data: item
}
end

View file

@ -116,7 +116,8 @@ class API::V2::Schema < GraphQL::Schema
Types::Champs::Descriptor::TextChampDescriptorType,
Types::Champs::Descriptor::TitreIdentiteChampDescriptorType,
Types::Champs::Descriptor::YesNoChampDescriptorType,
Types::Champs::Descriptor::ExpressionReguliereChampDescriptorType
Types::Champs::Descriptor::ExpressionReguliereChampDescriptorType,
Types::Champs::Descriptor::EngagementJuridiqueChampDescriptorType
def self.unauthorized_object(error)
# Add a top-level error to the response instead of returning nil:

View file

@ -2172,6 +2172,34 @@ type EmailChampDescriptor implements ChampDescriptor {
type: TypeDeChamp! @deprecated(reason: "Utilisez le champ `__typename` à la place.")
}
type EngagementJuridiqueChampDescriptor implements ChampDescriptor {
"""
Description des champs dun bloc répétable.
"""
champDescriptors: [ChampDescriptor!] @deprecated(reason: "Utilisez le champ `RepetitionChampDescriptor.champ_descriptors` à la place.")
"""
Description du champ.
"""
description: String
id: ID!
"""
Libellé du champ.
"""
label: String!
"""
Est-ce que le champ est obligatoire ?
"""
required: Boolean!
"""
Type de la valeur du champ.
"""
type: TypeDeChamp! @deprecated(reason: "Utilisez le champ `__typename` à la place.")
}
type Entreprise {
attestationFiscaleAttachment: File
attestationSocialeAttachment: File
@ -4019,6 +4047,11 @@ enum TypeDeChamp {
"""
email
"""
Translation missing: fr.activerecord.attributes.type_de_champ.type_champs.engagement_juridique
"""
engagement_juridique
"""
EPCI
"""

View file

@ -22,6 +22,9 @@ module Types
definition_methods do
def resolve_type(object, context)
case object.type_champ
when TypeDeChamp.type_champs.fetch(:engagement_juridique)
Types::Champs::Descriptor::EngagementJuridiqueChampDescriptorType
when TypeDeChamp.type_champs.fetch(:text)
Types::Champs::Descriptor::TextChampDescriptorType
when TypeDeChamp.type_champs.fetch(:textarea)

View file

@ -0,0 +1,5 @@
module Types::Champs::Descriptor
class EngagementJuridiqueChampDescriptorType < Types::BaseObject
implements Types::ChampDescriptorType
end
end

View file

@ -0,0 +1,7 @@
class Champs::EngagementJuridiqueChamp < Champ
# cf: https://communaute.chorus-pro.gouv.fr/documentation/creer-un-engagement/#1522314752186-a34f3662-0644b5d1-16c22add-8ea097de-3a0a
validates_with ExpressionReguliereValidator,
expression_reguliere: /([A-Z]|[0-9]|\-|\_|\+|\/)+/,
expression_reguliere_error_message: "Le numéro d'EJ ne peut contenir que des caractères alphanumérique et les caractères spéciaux suivant : “-“ ; “_“ ; “+“ ; “/“",
if: -> { validation_context != :brouillon }
end

View file

@ -2,37 +2,11 @@ class ChorusConfiguration
include ActiveModel::Model
include ActiveModel::Attributes
attribute :centre_de_coup, :simple_json, default: '{}'
attribute :centre_de_cout, :simple_json, default: '{}'
attribute :domaine_fonctionnel, :simple_json, default: '{}'
attribute :referentiel_de_programmation, :simple_json, default: '{}'
def format_displayed_value(attribute_name)
case attribute_name
when :centre_de_coup
ChorusConfiguration.format_centre_de_coup_label(centre_de_coup)
when :domaine_fonctionnel
ChorusConfiguration.format_domaine_fonctionnel_label(domaine_fonctionnel)
when :referentiel_de_programmation
ChorusConfiguration.format_ref_programmation_label(referentiel_de_programmation)
else
raise 'unknown attribute_name'
end
end
def format_hidden_value(attribute_name)
case attribute_name
when :centre_de_coup
centre_de_coup.to_json
when :domaine_fonctionnel
domaine_fonctionnel.to_json
when :referentiel_de_programmation
referentiel_de_programmation.to_json
else
raise 'unknown attribute_name'
end
end
def self.format_centre_de_coup_label(api_result)
def self.format_centre_de_cout_label(api_result)
return "" if api_result.blank?
api_result = api_result.symbolize_keys
"#{api_result[:description]} - #{api_result[:code]}"
@ -52,7 +26,7 @@ class ChorusConfiguration
def complete?
[
centre_de_coup,
centre_de_cout,
domaine_fonctionnel,
referentiel_de_programmation
].all?(&:present?)

View file

@ -7,7 +7,7 @@ module ProcedureChorusConcern
end
def chorusable?
feature_enabled?(:chorus)
feature_enabled?(:engagement_juridique_type_de_champ)
end
end
end

View file

@ -1228,6 +1228,13 @@ class Dossier < ApplicationRecord
columns << ['Entreprise raison sociale', etablissement&.entreprise_raison_sociale]
end
if procedure.chorusable? && procedure.chorus.complete?
columns += [
['Domaine Fonctionnel', procedure.chorus_configuration.domaine_fonctionnel.code],
['Référentiel De Programmation', procedure.chorus_configuration.referentiel_de_programmation.code],
['Centre De Coup', procedure.chorus_configuration.centre_de_cout.code]
]
end
columns += [
['Archivé', :archived],
['État du dossier', Dossier.human_attribute_name("state.#{state}")],

View file

@ -3,9 +3,12 @@ class TypeDeChamp < ApplicationRecord
FILE_MAX_SIZE = 200.megabytes
FEATURE_FLAGS = {
engagement_juridique: :engagement_juridique_type_de_champ,
cojo: :cojo_type_de_champ,
expression_reguliere: :expression_reguliere_type_de_champ
}
MINIMUM_TEXTAREA_CHARACTER_LIMIT_LENGTH = 400
STRUCTURE = :structure
@ -20,6 +23,8 @@ class TypeDeChamp < ApplicationRecord
CATEGORIES = [STRUCTURE, ETAT_CIVIL, LOCALISATION, PAIEMENT_IDENTIFICATION, STANDARD, PIECES_JOINTES, CHOICE, REFERENTIEL_EXTERNE]
TYPE_DE_CHAMP_TO_CATEGORIE = {
engagement_juridique: REFERENTIEL_EXTERNE,
header_section: STRUCTURE,
repetition: STRUCTURE,
dossier_link: STRUCTURE,
@ -62,6 +67,8 @@ class TypeDeChamp < ApplicationRecord
}
enum type_champs: {
engagement_juridique: 'engagement_juridique',
header_section: 'header_section',
repetition: 'repetition',
dossier_link: 'dossier_link',
@ -111,6 +118,10 @@ class TypeDeChamp < ApplicationRecord
type_champs.fetch(:epci)
]
PRIVATE_ONLY_TYPES = [
type_champs.fetch(:engagement_juridique)
]
store_accessor :options,
:cadastres,
:old_pj,

View file

@ -0,0 +1,2 @@
class TypesDeChamp::EngagementJuridiqueTypeDeChamp < TypesDeChamp::TypeDeChampBase
end

View file

@ -2,9 +2,12 @@ class ExpressionReguliereValidator < ActiveModel::Validator
TIMEOUT = 1.second.freeze
def validate(record)
expression_reguliere = options[:expression_reguliere] || record.expression_reguliere
expression_reguliere_error_message = options[:expression_reguliere_error_message] || record.expression_reguliere_error_message
if record.value.present?
if !record.value.match?(Regexp.new(record.expression_reguliere, timeout: TIMEOUT))
record.errors.add(:value, :invalid_regexp, expression_reguliere_error_message: record.expression_reguliere_error_message)
if !record.value.match?(Regexp.new(expression_reguliere, timeout: TIMEOUT))
record.errors.add(:value, :invalid_regexp, expression_reguliere_error_message: expression_reguliere_error_message)
end
end
rescue Regexp::TimeoutError

View file

@ -0,0 +1,16 @@
= render partial: 'administrateurs/breadcrumbs',
locals: { steps: [['Démarches', admin_procedures_path],
[@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)],
['Connecteur Chorus']] }
.fr-container
%h1.fr-h1
Cadre budgétaire
= render Dsfr::CalloutComponent.new(title: "Cas d'usage") do |c|
- c.with_body do
%p Ajouter un champs Engagement Juridique aux annotations privées afin de renseigner l'EJ directement dans DS
%p L'EJ sera automatiquement ajouté aux exports des dossiers
= link_to "Ajouter une annotation privée EJ", annotations_admin_procedure_path(@procedure), class: 'btn fr-btn'

View file

@ -8,4 +8,11 @@
%h1.fr-h1
Cadre budgétaire
= render Dsfr::CalloutComponent.new(title: "Cas d'usage") do |c|
- c.with_body do
%p Vous traitez une démarche liées a des subventions qui seront payées au travers de Chorus ?
%p Les agent traitants les dossiers seront ammenés a suivre la planification budgetaire des dossiers traités ?
%p En renseignant le cadre budgetaire de la subvention, vous donnez les moyens de rapprocher les dossiers traités sur démarches-simplifiées avec les données extraites de Chorus
%p Ce cadre budgetaire sera automatiquement ajouté aux exports des dossiers
= render Procedure::ChorusFormComponent.new(procedure: @procedure)

View file

@ -619,7 +619,9 @@ Rails.application.routes.draw do
resource :attestation_template, only: [:show, :edit, :update, :create] do
get 'preview', on: :member
end
resource :chorus, only: [:edit, :update]
resource :chorus, only: [:edit, :update] do
get 'add_champ_engagement_juridique'
end
resource :dossier_submitted_message, only: [:edit, :update, :create]
# ADDED TO ACCESS IT FROM THE IFRAME
get 'attestation_template/preview' => 'attestation_templates#preview'

View file

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_10_26_161609) do
ActiveRecord::Schema[7.0].define(version: 2023_11_03_084116) do
# These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto"
enable_extension "plpgsql"

View file

@ -13,7 +13,7 @@ describe Procedure::Card::ChorusComponent, type: :component do
end
end
context 'feature flag active' do
before { Flipper.enable_actor :chorus, procedure }
before { Flipper.enable_actor :engagement_juridique_type_de_champ, procedure }
it 'render the template' do
subject

View file

@ -1,29 +1,53 @@
describe TypesDeChampEditor::ChampComponent, type: :component do
describe 'render' do
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :drop_down_list, libelle: 'Votre ville', options: ['Paris', 'Lyon', 'Marseille'] }]) }
let(:drop_down_tdc) { procedure.draft_revision.types_de_champ.first }
let(:coordinate) { drop_down_tdc.revision_type_de_champ }
let(:component) { described_class.new(coordinate:, upper_coordinates: []) }
let(:routing_rules_stable_ids) { [] }
before do
Flipper.enable_actor(:engagement_juridique_type_de_champ, procedure)
allow_any_instance_of(Procedure).to receive(:stable_ids_used_by_routing_rules).and_return(routing_rules_stable_ids)
render_inline(component)
end
context 'drop down tdc not used for routing' do
it do
expect(page).not_to have_text(/utilisé pour\nle routage/)
expect(page).not_to have_css("select[disabled=\"disabled\"]")
describe 'tdc dropdown' do
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :drop_down_list, libelle: 'Votre ville', options: ['Paris', 'Lyon', 'Marseille'] }]) }
let(:tdc) { procedure.draft_revision.types_de_champ.first }
let(:coordinate) { tdc.revision_type_de_champ }
context 'drop down tdc not used for routing' do
it do
expect(page).not_to have_text(/utilisé pour\nle routage/)
expect(page).not_to have_css("select[disabled=\"disabled\"]")
end
end
context 'drop down tdc used for routing' do
let(:routing_rules_stable_ids) { [tdc.stable_id] }
it do
expect(page).to have_css("select[disabled=\"disabled\"]")
expect(page).to have_text(/utilisé pour\nle routage/)
end
end
end
context 'drop down tdc used for routing' do
let(:routing_rules_stable_ids) { [drop_down_tdc.stable_id] }
describe 'tdc ej' do
let(:procedure) { create(:procedure, types_de_champ_public: [{ type: :text }], types_de_champ_private: [{ type: :text }]) }
it do
expect(page).to have_css("select[disabled=\"disabled\"]")
expect(page).to have_text(/utilisé pour\nle routage/)
context 'when coordinate public' do
let(:coordinate) { procedure.draft_revision.revision_types_de_champ_public.first }
it 'does not include Engagement Juridique' do
expect(page).not_to have_css('option', text: "Engagement Juridique")
end
end
context 'when coordinate private' do
let(:coordinate) { procedure.draft_revision.revision_types_de_champ_private.first }
it 'includes Engagement Juridique' do
expect(page).to have_css('option', text: "Engagement Juridique")
end
end
end
end

View file

@ -1,20 +1,21 @@
describe Administrateurs::ChorusController, type: :controller do
describe 'edit' do
let(:user) { create(:user) }
let(:admin) { create(:administrateur, user: create(:user)) }
let(:procedure) { create(:procedure, administrateurs: [admin]) }
let(:user) { create(:user) }
let(:admin) { create(:administrateur, user: create(:user)) }
let(:procedure) { create(:procedure, administrateurs: [admin]) }
describe '#edit' do
subject { get :edit, params: { procedure_id: procedure.id } }
context 'not signed in' do
context 'when user is not signed in' do
it { is_expected.to redirect_to(new_user_session_path) }
end
context 'signed in but not admin of procedure' do
context 'when user is signed in but not admin of procedure' do
before { sign_in(user) }
it { is_expected.to redirect_to(new_user_session_path) }
end
context 'signed as admin' do
context 'when user is signed as admin' do
before { sign_in(admin.user) }
it { is_expected.to have_http_status(200) }
@ -26,10 +27,7 @@ describe Administrateurs::ChorusController, type: :controller do
end
end
describe 'update' do
let(:user) { create(:user) }
let(:admin) { create(:administrateur, user: create(:user)) }
let(:procedure) { create(:procedure, administrateurs: [admin]) }
describe '#update' do
let(:chorus_configuration_params) { {} }
subject do
put :update,
@ -39,38 +37,83 @@ describe Administrateurs::ChorusController, type: :controller do
}
end
context 'not signed in' do
context 'when user is not signed in' do
it { is_expected.to redirect_to(new_user_session_path) }
end
context 'signed in but not admin of procedure' do
context 'when user is signed in but not admin of procedure' do
before { sign_in(user) }
it { is_expected.to redirect_to(new_user_session_path) }
end
context 'signed as admin' do
context 'when user is signed as admin' do
before { sign_in(admin.user) }
let(:domaine_fonctionnel) { nil }
let(:referentiel_de_programmation) { nil }
context "valid payload" do
let(:centre_de_coup) { '{"code":"D00C8DX004","label":"Aumôniers+protestant","ville":null,"code_postal":null,"description":"Aumoniers+protestants"}' }
context "partial valid payload" do
let(:centre_de_cout) { '{"code":"D00C8DX004","label":"Aumôniers+protestant","ville":null,"code_postal":null,"description":"Aumoniers+protestants"}' }
let(:chorus_configuration_params) do
{
centre_de_cout:, domaine_fonctionnel:, referentiel_de_programmation:
}
end
it 'updates params and redirect back to complete all infos' do
expect(subject).to redirect_to(edit_admin_procedure_chorus_path(procedure))
expect(flash[:notice]).to eq("La configuration Chorus a été mise à jour. Veuillez renseigner le reste des informations pour faciliter le rapprochement des données.")
procedure.reload
expect(procedure.chorus_configuration.centre_de_cout).to eq(JSON.parse(centre_de_cout))
expect(procedure.chorus_configuration.domaine_fonctionnel).to eq(nil)
expect(procedure.chorus_configuration.referentiel_de_programmation).to eq(nil)
end
end
context "full valid payload" do
let(:centre_de_cout) { '{"code":"D00C8DX004","label":"Aumôniers+protestant","ville":null,"code_postal":null,"description":"Aumoniers+protestants"}' }
let(:domaine_fonctionnel) { '{"code":"0105-05-01","label":"Formation+des+élites+et+cadres+de+sécurité+et+de+défense","description":null,"code_programme":"105"}' }
let(:referentiel_de_programmation) { '{"code":"010101010101","label":"DOTATIONS+CARPA+AJ+ET+AUTRES+INTERVENTIONS","description":null,"code_programme":"101"}' }
let(:chorus_configuration_params) do
{
centre_de_coup:, domaine_fonctionnel:, referentiel_de_programmation:
centre_de_cout:, domaine_fonctionnel:, referentiel_de_programmation:
}
end
it { is_expected.to redirect_to(admin_procedure_path(procedure)) }
it 'updates params' do
subject
expect(flash[:notice]).to eq("La configuration Chorus a été mise à jour et prend immédiatement effet pour les nouveaux dossiers.")
it 'updates params and redirects to add champs EngagementJuridique' do
expect(subject).to redirect_to(add_champ_engagement_juridique_admin_procedure_chorus_path(procedure))
expect(flash[:notice]).to eq("La configuration Chorus a été mise à jour.")
procedure.reload
expect(procedure.chorus_configuration.centre_de_coup).to eq(JSON.parse(centre_de_coup))
expect(procedure.chorus_configuration.centre_de_cout).to eq(JSON.parse(centre_de_cout))
expect(procedure.chorus_configuration.domaine_fonctionnel).to eq(JSON.parse(domaine_fonctionnel))
expect(procedure.chorus_configuration.referentiel_de_programmation).to eq(JSON.parse(referentiel_de_programmation))
end
end
end
end
describe '#add_champ_engagement_juridique' do
render_views
subject { get :add_champ_engagement_juridique, params: { procedure_id: procedure.id } }
context 'when user is not signed in' do
it { is_expected.to redirect_to(new_user_session_path) }
end
context 'when user is signed in but not admin of procedure' do
before { sign_in(user) }
it { is_expected.to redirect_to(new_user_session_path) }
end
context 'when user is signed as admin' do
before { sign_in(admin.user) }
it 'have links to add annotation' do
expect(subject).to have_http_status(:success)
expect(response.body).to have_link("Ajouter une annotation privée EJ", href: annotations_admin_procedure_path(procedure))
end
end
end
end

View file

@ -238,9 +238,12 @@ FactoryBot.define do
value { 'W173847273' }
end
factory :champ_engagement_juridique, class: 'Champs::EngagementJuridiqueChamp' do
type_de_champ { association :type_de_champ_engagement_juridique, procedure: dossier.procedure }
end
factory :champ_cojo, class: 'Champs::COJOChamp' do
type_de_champ { association :type_de_champ_cojo, procedure: dossier.procedure }
end
type_de_champ { association :type_de_champ_cojo, procedure: dossier.procedure }
end
factory :champ_rnf, class: 'Champs::RNFChamp' do
type_de_champ { association :type_de_champ_rnf, procedure: dossier.procedure }

View file

@ -428,12 +428,12 @@ FactoryBot.define do
end
trait :partial_chorus do
chorus { ChorusConfiguration.new(centre_de_coup: { a: 1 }) }
chorus { ChorusConfiguration.new(centre_de_cout: { a: 1 }) }
end
trait :filled_chorus do
chorus do
ChorusConfiguration.new(centre_de_coup: { a: 1 },
ChorusConfiguration.new(centre_de_cout: { a: 1 },
domaine_fonctionnel: { b: 2 },
referentiel_de_programmation: { c: 3 })
end

View file

@ -184,6 +184,9 @@ FactoryBot.define do
factory :type_de_champ_epci do
type_champ { TypeDeChamp.type_champs.fetch(:epci) }
end
factory :type_de_champ_engagement_juridique do
type_champ { TypeDeChamp.type_champs.fetch(:engagement_juridique) }
end
factory :type_de_champ_cojo do
type_champ { TypeDeChamp.type_champs.fetch(:cojo) }
end

View file

@ -14,9 +14,9 @@ describe '20220705164551_remove_unused_champs' do
describe 'remove_unused_champs' do
it "with bad champs" do
expect(Champ.where(dossier: dossier).count).to eq(43)
expect(Champ.where(dossier: dossier).count).to eq(44)
run_task
expect(Champ.where(dossier: dossier).count).to eq(42)
expect(Champ.where(dossier: dossier).count).to eq(43)
end
end
end

View file

@ -26,7 +26,7 @@ describe ChorusConfiguration do
it 'works with existing args' do
expect do
cc = ChorusConfiguration.new()
cc.assign_attributes(centre_de_coup: {}, domaine_fonctionnel: {}, referentiel_de_programmation: {})
cc.assign_attributes(centre_de_cout: {}, domaine_fonctionnel: {}, referentiel_de_programmation: {})
end.not_to raise_error
end
end

View file

@ -0,0 +1,41 @@
describe Champs::EngagementJuridiqueChamp do
describe 'validation' do
let(:champ) { build(:champ_engagement_juridique, value: value) }
subject { champ.valid? }
context 'with [A-Z]' do
let(:value) { "ABC" }
it { is_expected.to be_truthy }
end
context 'with [0-9]' do
let(:value) { "ABC" }
it { is_expected.to be_truthy }
end
context 'with -' do
let(:value) { "-" }
it { is_expected.to be_truthy }
end
context 'with _' do
let(:value) { "_" }
it { is_expected.to be_truthy }
end
context 'with +' do
let(:value) { "+" }
it { is_expected.to be_truthy }
end
context 'with /' do
let(:value) { "/" }
it { is_expected.to be_truthy }
end
context 'with *' do
let(:value) { "*" }
it { is_expected.to be_falsey }
end
end
end

View file

@ -91,7 +91,8 @@ describe ProcedureExportService do
"epci (Département)",
"cojo",
"expression_reguliere",
"rnf"
"rnf",
"engagement_juridique"
]
end
@ -140,6 +141,21 @@ describe ProcedureExportService do
end
it { expect(dossiers_sheet.data.first.size).to eq(nominal_headers.size) }
end
context 'with procedure chorus' do
let(:procedure) { create(:procedure, :published, :for_individual, :filled_chorus, :with_all_champs) }
let!(:dossier) { create(:dossier, :en_instruction, :with_populated_champs, procedure: procedure) }
it 'includes chorus headers' do
expected_headers = [
'Domaine Fonctionnel',
'Référentiel De Programmation',
'Centre De Coup'
]
expect(dossiers_sheet.headers).to match_array(nominal_headers)
end
end
end
describe 'Etablissement sheet' do
@ -204,7 +220,8 @@ describe ProcedureExportService do
"epci (Département)",
"cojo",
"expression_reguliere",
"rnf"
"rnf",
"engagement_juridique"
]
end
@ -300,7 +317,8 @@ describe ProcedureExportService do
"epci (Département)",
"cojo",
"expression_reguliere",
"rnf"
"rnf",
"engagement_juridique"
]
end