diff --git a/app/components/editable_champ/engagement_juridique_component.rb b/app/components/editable_champ/engagement_juridique_component.rb new file mode 100644 index 000000000..223cc0e9e --- /dev/null +++ b/app/components/editable_champ/engagement_juridique_component.rb @@ -0,0 +1,2 @@ +class EditableChamp::EngagementJuridiqueComponent < EditableChamp::EditableChampBaseComponent +end diff --git a/app/components/editable_champ/engagement_juridique_component/en.yml b/app/components/editable_champ/engagement_juridique_component/en.yml new file mode 100644 index 000000000..1491cafbf --- /dev/null +++ b/app/components/editable_champ/engagement_juridique_component/en.yml @@ -0,0 +1,2 @@ +--- +en: diff --git a/app/components/editable_champ/engagement_juridique_component/engagement_juridique_component.html.haml b/app/components/editable_champ/engagement_juridique_component/engagement_juridique_component.html.haml new file mode 100644 index 000000000..e88fc3332 --- /dev/null +++ b/app/components/editable_champ/engagement_juridique_component/engagement_juridique_component.html.haml @@ -0,0 +1 @@ += @form.text_field(:value, input_opts(id: @champ.input_id, required: @champ.required?, aria: { describedby: @champ.describedby_id })) diff --git a/app/components/editable_champ/engagement_juridique_component/fr.yml b/app/components/editable_champ/engagement_juridique_component/fr.yml new file mode 100644 index 000000000..09f6db466 --- /dev/null +++ b/app/components/editable_champ/engagement_juridique_component/fr.yml @@ -0,0 +1,2 @@ +--- +fr: diff --git a/app/components/procedure/chorus_form_component.rb b/app/components/procedure/chorus_form_component.rb index 7e2dc99a7..0602a9ccd 100644 --- a/app/components/procedure/chorus_form_component.rb +++ b/app/components/procedure/chorus_form_component.rb @@ -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 diff --git a/app/components/procedure/chorus_form_component/chorus_form_component.html.haml b/app/components/procedure/chorus_form_component/chorus_form_component.html.haml index a78da64dd..009eacf5a 100644 --- a/app/components/procedure/chorus_form_component/chorus_form_component.html.haml +++ b/app/components/procedure/chorus_form_component/chorus_form_component.html.haml @@ -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' diff --git a/app/components/types_de_champ_editor/champ_component.rb b/app/components/types_de_champ_editor/champ_component.rb index f6abcf06d..87fcd2fdf 100644 --- a/app/components/types_de_champ_editor/champ_component.rb +++ b/app/components/types_de_champ_editor/champ_component.rb @@ -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) diff --git a/app/controllers/administrateurs/chorus_controller.rb b/app/controllers/administrateurs/chorus_controller.rb index 1acd31a4d..2410e9255 100644 --- a/app/controllers/administrateurs/chorus_controller.rb +++ b/app/controllers/administrateurs/chorus_controller.rb @@ -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 diff --git a/app/controllers/data_sources/chorus_controller.rb b/app/controllers/data_sources/chorus_controller.rb index c734e869b..92aafc878 100644 --- a/app/controllers/data_sources/chorus_controller.rb +++ b/app/controllers/data_sources/chorus_controller.rb @@ -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 diff --git a/app/graphql/api/v2/schema.rb b/app/graphql/api/v2/schema.rb index ba90e45fb..bef5be7c3 100644 --- a/app/graphql/api/v2/schema.rb +++ b/app/graphql/api/v2/schema.rb @@ -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: diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index 2b35ec1ae..fdd8c3ad5 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -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 d’un 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 """ diff --git a/app/graphql/types/champ_descriptor_type.rb b/app/graphql/types/champ_descriptor_type.rb index 44563b102..3cf7d1302 100644 --- a/app/graphql/types/champ_descriptor_type.rb +++ b/app/graphql/types/champ_descriptor_type.rb @@ -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) diff --git a/app/graphql/types/champs/descriptor/engagement_juridique_champ_descriptor_type.rb b/app/graphql/types/champs/descriptor/engagement_juridique_champ_descriptor_type.rb new file mode 100644 index 000000000..9d6928358 --- /dev/null +++ b/app/graphql/types/champs/descriptor/engagement_juridique_champ_descriptor_type.rb @@ -0,0 +1,5 @@ +module Types::Champs::Descriptor + class EngagementJuridiqueChampDescriptorType < Types::BaseObject + implements Types::ChampDescriptorType + end +end diff --git a/app/models/champs/engagement_juridique_champ.rb b/app/models/champs/engagement_juridique_champ.rb new file mode 100644 index 000000000..903365e8d --- /dev/null +++ b/app/models/champs/engagement_juridique_champ.rb @@ -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 diff --git a/app/models/chorus_configuration.rb b/app/models/chorus_configuration.rb index 3447e7bc1..e4042eccd 100644 --- a/app/models/chorus_configuration.rb +++ b/app/models/chorus_configuration.rb @@ -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?) diff --git a/app/models/concerns/procedure_chorus_concern.rb b/app/models/concerns/procedure_chorus_concern.rb index fe71152f5..d940dfafd 100644 --- a/app/models/concerns/procedure_chorus_concern.rb +++ b/app/models/concerns/procedure_chorus_concern.rb @@ -7,7 +7,7 @@ module ProcedureChorusConcern end def chorusable? - feature_enabled?(:chorus) + feature_enabled?(:engagement_juridique_type_de_champ) end end end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index ed858ddab..178acd8e6 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -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}")], diff --git a/app/models/type_de_champ.rb b/app/models/type_de_champ.rb index f97d89439..a29369b95 100644 --- a/app/models/type_de_champ.rb +++ b/app/models/type_de_champ.rb @@ -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, diff --git a/app/models/types_de_champ/engagement_juridique_type_de_champ.rb b/app/models/types_de_champ/engagement_juridique_type_de_champ.rb new file mode 100644 index 000000000..bd076d2c8 --- /dev/null +++ b/app/models/types_de_champ/engagement_juridique_type_de_champ.rb @@ -0,0 +1,2 @@ +class TypesDeChamp::EngagementJuridiqueTypeDeChamp < TypesDeChamp::TypeDeChampBase +end diff --git a/app/validators/expression_reguliere_validator.rb b/app/validators/expression_reguliere_validator.rb index 568357856..08a44fdc4 100644 --- a/app/validators/expression_reguliere_validator.rb +++ b/app/validators/expression_reguliere_validator.rb @@ -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 diff --git a/app/views/administrateurs/chorus/add_champ_engagement_juridique.html.haml b/app/views/administrateurs/chorus/add_champ_engagement_juridique.html.haml new file mode 100644 index 000000000..f17989e2d --- /dev/null +++ b/app/views/administrateurs/chorus/add_champ_engagement_juridique.html.haml @@ -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' diff --git a/app/views/administrateurs/chorus/edit.html.haml b/app/views/administrateurs/chorus/edit.html.haml index bf7c2b1b5..4b6725491 100644 --- a/app/views/administrateurs/chorus/edit.html.haml +++ b/app/views/administrateurs/chorus/edit.html.haml @@ -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) diff --git a/config/routes.rb b/config/routes.rb index 24776934d..c102ef448 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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' diff --git a/db/schema.rb b/db/schema.rb index d9ac64549..bd188e760 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -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" diff --git a/spec/components/procedures/card/chorus_component_spec.rb b/spec/components/procedures/card/chorus_component_spec.rb index b5148c376..c1cba9bbb 100644 --- a/spec/components/procedures/card/chorus_component_spec.rb +++ b/spec/components/procedures/card/chorus_component_spec.rb @@ -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 diff --git a/spec/components/types_de_champ_editor/champ_component_spec.rb b/spec/components/types_de_champ_editor/champ_component_spec.rb index 09fe85b4d..40d1250ac 100644 --- a/spec/components/types_de_champ_editor/champ_component_spec.rb +++ b/spec/components/types_de_champ_editor/champ_component_spec.rb @@ -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 diff --git a/spec/controllers/administrateurs/chorus_controller_spec.rb b/spec/controllers/administrateurs/chorus_controller_spec.rb index c239a8d26..15a03cfe9 100644 --- a/spec/controllers/administrateurs/chorus_controller_spec.rb +++ b/spec/controllers/administrateurs/chorus_controller_spec.rb @@ -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 diff --git a/spec/factories/champ.rb b/spec/factories/champ.rb index 1f422f02f..57c190de4 100644 --- a/spec/factories/champ.rb +++ b/spec/factories/champ.rb @@ -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 } diff --git a/spec/factories/procedure.rb b/spec/factories/procedure.rb index a545fab33..fcb0a91e2 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -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 diff --git a/spec/factories/type_de_champ.rb b/spec/factories/type_de_champ.rb index 4d49abd7c..062eb609d 100644 --- a/spec/factories/type_de_champ.rb +++ b/spec/factories/type_de_champ.rb @@ -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 diff --git a/spec/lib/tasks/deployment/20220705164551_remove_unused_champs_spec.rb b/spec/lib/tasks/deployment/20220705164551_remove_unused_champs_spec.rb index bdf1b7848..1a4fdf386 100644 --- a/spec/lib/tasks/deployment/20220705164551_remove_unused_champs_spec.rb +++ b/spec/lib/tasks/deployment/20220705164551_remove_unused_champs_spec.rb @@ -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 diff --git a/spec/models/chorus_configuration_spec.rb b/spec/models/chorus_configuration_spec.rb index 5298b4e42..41fa7e038 100644 --- a/spec/models/chorus_configuration_spec.rb +++ b/spec/models/chorus_configuration_spec.rb @@ -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 diff --git a/spec/models/engagement_juridique_champ_spec.rb b/spec/models/engagement_juridique_champ_spec.rb new file mode 100644 index 000000000..7d5306fae --- /dev/null +++ b/spec/models/engagement_juridique_champ_spec.rb @@ -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 diff --git a/spec/services/procedure_export_service_spec.rb b/spec/services/procedure_export_service_spec.rb index 2b758b437..1d3f7c6e9 100644 --- a/spec/services/procedure_export_service_spec.rb +++ b/spec/services/procedure_export_service_spec.rb @@ -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