diff --git a/app/components/procedure/card/chorus_component.rb b/app/components/procedure/card/chorus_component.rb new file mode 100644 index 000000000..88a36ab3b --- /dev/null +++ b/app/components/procedure/card/chorus_component.rb @@ -0,0 +1,13 @@ +class Procedure::Card::ChorusComponent < ApplicationComponent + def initialize(procedure:) + @procedure = procedure + end + + def render? + @procedure.chorusable? + end + + def complete? + @procedure.chorus_configuration.complete? + end +end diff --git a/app/components/procedure/card/chorus_component/chorus_component.html.haml b/app/components/procedure/card/chorus_component/chorus_component.html.haml new file mode 100644 index 000000000..8fd65667b --- /dev/null +++ b/app/components/procedure/card/chorus_component/chorus_component.html.haml @@ -0,0 +1,16 @@ +.fr-col-6.fr-col-md-4.fr-col-lg-3.chorus-component + = link_to edit_admin_procedure_chorus_path(@procedure), class: 'fr-tile fr-enlarge-link', title: 'Configurer le cadre budgetaire Chorus' do + .fr-tile__body.flex.column.align-center.justify-between + - if !@procedure.chorus_configuration.complete? + %div + %span.icon.clock + %p.fr-tile-status-todo À compléter + - else + %div + %span.icon.accept + %p.fr-tile-status-accept Configuré + %div + %h3.fr-h6.fr-mt-10v + Connecteur Chorus + %p.fr-tile-subtitle Vous traitez des données de subvention d'état ? + %p.fr-btn.fr-btn--tertiary Configurer diff --git a/app/components/procedure/chorus_form_component.rb b/app/components/procedure/chorus_form_component.rb new file mode 100644 index 000000000..7e2dc99a7 --- /dev/null +++ b/app/components/procedure/chorus_form_component.rb @@ -0,0 +1,15 @@ +class Procedure::ChorusFormComponent < ApplicationComponent + attr_reader :procedure + + def initialize(procedure:) + @procedure = procedure + end + + def map_attribute_to_autocomplete_endpoint + { + centre_de_coup: data_sources_search_centre_couts_path, + domaine_fonctionnel: data_sources_search_domaine_fonct_path, + referentiel_de_programmation: data_sources_search_ref_programmation_path + } + 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 new file mode 100644 index 000000000..a78da64dd --- /dev/null +++ b/app/components/procedure/chorus_form_component/chorus_form_component.html.haml @@ -0,0 +1,9 @@ += form_for([procedure, 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" + .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.submit "Enregister", class: 'fr-btn' diff --git a/app/controllers/administrateurs/chorus_controller.rb b/app/controllers/administrateurs/chorus_controller.rb new file mode 100644 index 000000000..1acd31a4d --- /dev/null +++ b/app/controllers/administrateurs/chorus_controller.rb @@ -0,0 +1,33 @@ +module Administrateurs + class ChorusController < AdministrateurController + before_action :retrieve_procedure + + def edit + end + + def update + @configuration = @procedure.chorus_configuration + @configuration.assign_attributes(configurations_params) + 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) + else + flash.now.alert = "Des erreurs empêchent la validation du connecteur chorus. Corrigez les erreurs" + render :edit + end + end + + private + + def search_params + params.permit(:q) + end + + def configurations_params + params.require(:chorus_configuration) + .permit(:centre_de_coup, :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 new file mode 100644 index 000000000..c734e869b --- /dev/null +++ b/app/controllers/data_sources/chorus_controller.rb @@ -0,0 +1,33 @@ +class DataSources::ChorusController < ApplicationController + before_action :authenticate_administrateur! + + def search_domaine_fonct + result_json = APIBretagneService.new.search_domaine_fonct(code_or_label: params[:q]) + render json: format_result(result_json:, + label_formatter: ChorusConfiguration.method(:format_domaine_fonctionnel_label)) + end + + 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)) + end + + def search_ref_programmation + result_json = APIBretagneService.new.search_ref_programmation(code_or_label: params[:q]) + render json: format_result(result_json:, + label_formatter: ChorusConfiguration.method(:format_ref_programmation_label)) + end + + private + + def format_result(result_json:, label_formatter:) + result_json.map do |item| + { + label: label_formatter.call(item), + value: "#{item[:label]} - #{item[:code_programme]}", + data: item + } + end + end +end diff --git a/app/models/chorus_configuration.rb b/app/models/chorus_configuration.rb new file mode 100644 index 000000000..3447e7bc1 --- /dev/null +++ b/app/models/chorus_configuration.rb @@ -0,0 +1,60 @@ +class ChorusConfiguration + include ActiveModel::Model + include ActiveModel::Attributes + + attribute :centre_de_coup, :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) + return "" if api_result.blank? + api_result = api_result.symbolize_keys + "#{api_result[:description]} - #{api_result[:code]}" + end + + def self.format_domaine_fonctionnel_label(api_result) + return "" if api_result.blank? + api_result = api_result.symbolize_keys + "#{api_result[:label]} - #{api_result[:code]}" + end + + def self.format_ref_programmation_label(api_result) + return "" if api_result.blank? + api_result = api_result.symbolize_keys + "#{api_result[:label]} - #{api_result[:code]}" + end + + def complete? + [ + centre_de_coup, + domaine_fonctionnel, + referentiel_de_programmation + ].all?(&:present?) + end +end diff --git a/app/models/concerns/procedure_chorus_concern.rb b/app/models/concerns/procedure_chorus_concern.rb new file mode 100644 index 000000000..fe71152f5 --- /dev/null +++ b/app/models/concerns/procedure_chorus_concern.rb @@ -0,0 +1,13 @@ +module ProcedureChorusConcern + extend ActiveSupport::Concern + + included do + def chorus_configuration + @chorus_configuration ||= ChorusConfiguration.new(chorus) + end + + def chorusable? + feature_enabled?(:chorus) + end + end +end diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 742b92343..79b1bc2ed 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -4,6 +4,7 @@ class Procedure < ApplicationRecord include InitiationProcedureConcern include ProcedureGroupeInstructeurAPIHackConcern include ProcedureSVASVRConcern + include ProcedureChorusConcern include Discard::Model self.discard_column = :hidden_at diff --git a/app/services/api_bretagne_service.rb b/app/services/api_bretagne_service.rb new file mode 100644 index 000000000..b4d5ee5b7 --- /dev/null +++ b/app/services/api_bretagne_service.rb @@ -0,0 +1,93 @@ +class APIBretagneService + include Dry::Monads[:result] + HOST = 'https://api.databretagne.fr' + ENDPOINTS = { + # see: https://api.databretagne.fr/budget/doc#operations-Auth_Controller-post_login + "login" => "/budget/api/v1/auth/login", + # see: https://api.databretagne.fr/budget/doc#operations-Centre_couts-get_ref_controller_list + "centre-couts" => '/budget/api/v1/centre-couts', + # see: https://api.databretagne.fr/budget/doc#operations-Domaine_Fonctionnel-get_ref_controller_list + "domaine-fonct" => '/budget/api/v1/domaine-fonct', + # see: https://api.databretagne.fr/budget/doc#operations-Referentiel_Programmation-get_ref_controller_list + "ref-programmation" => '/budget/api/v1/ref-programmation' + } + + def search_domaine_fonct(code_or_label: "") + request(endpoint: ENDPOINTS.fetch('domaine-fonct'), code_or_label:) + end + + def search_centre_couts(code_or_label: "") + request(endpoint: ENDPOINTS.fetch('centre-couts'), code_or_label:) + end + + def search_ref_programmation(code_or_label: "") + request(endpoint: ENDPOINTS.fetch('ref-programmation'), code_or_label:) + end + + private + + def request(endpoint:, code_or_label:) + return [] if (code_or_label || "").strip.size < 3 + url = build_url(endpoint) + fetch_page(url:, params: { query: code_or_label, page_number: 1 })[:items] || [] + end + + def fetch_page(url:, params:, remaining_retry_count: 1) + result = call(url:, params:) + + case result + in Failure(code:, reason:) if code.in?(401..403) + if remaining_retry_count > 0 + login + fetch_page(url:, params:, remaining_retry_count: 0) + else + fail "APIBretagneService, #{reason} #{code}" + end + in Success(body:) + body + else # no response gives back a 204, so we don't try to JSON.parse(nil) to avoid error + { items: [] } + end + end + + def call(url:, params:) + API::Client.new.(url:, params:, authorization_token:, method:) + end + + def method + :get + end + + def authorization_token + result = login + case result + in Success(token:) + @token = token + in Failure(reason:, code:) + fail "APIBretagneService, #{reason} #{code}" + end + end + + def login + result = API::Client.new.call(url: build_url(ENDPOINTS.fetch("login")), + json: { + email: ENV['API_DATABRETAGE_USERNAME'], + password: ENV['API_DATABRETAGE_PASSWORD'] + }, + method: :post) + case result + in Success(body:) + Success(token: body.split("Bearer ")[1]) + in Failure(code:, reason:) if code.in?(403) + Failure(API::Client::Error[:invalid_credential, code, false, reason]) + else + Failure(API::Client::Error[:api_down]) + end + end + + def build_url(endpoint) + uri = URI(HOST) + uri.path = endpoint + uri + end +end diff --git a/app/views/administrateurs/chorus/edit.html.haml b/app/views/administrateurs/chorus/edit.html.haml new file mode 100644 index 000000000..bf7c2b1b5 --- /dev/null +++ b/app/views/administrateurs/chorus/edit.html.haml @@ -0,0 +1,11 @@ += render partial: 'administrateurs/breadcrumbs', + locals: { steps: [['Démarches', admin_procedures_path], + [@procedure.libelle.truncate_words(10), admin_procedure_path(@procedure)], + ['Connecteur Chorus']] } + + +.container + %h1.fr-h1 + Cadre budgétaire + + = render Procedure::ChorusFormComponent.new(procedure: @procedure) diff --git a/app/views/administrateurs/procedures/show.html.haml b/app/views/administrateurs/procedures/show.html.haml index bb4ad9d22..0df9bb013 100644 --- a/app/views/administrateurs/procedures/show.html.haml +++ b/app/views/administrateurs/procedures/show.html.haml @@ -66,3 +66,4 @@ = render Procedure::Card::SVASVRComponent.new(procedure: @procedure) if @procedure.sva_svr_enabled? || @procedure.feature_enabled?(:sva) = render Procedure::Card::MonAvisComponent.new(procedure: @procedure) = render Procedure::Card::DossierSubmittedMessageComponent.new(procedure: @procedure) + = render Procedure::Card::ChorusComponent.new(procedure: @procedure) diff --git a/config/initializers/attribute_types.rb b/config/initializers/attribute_types.rb new file mode 100644 index 000000000..153838f86 --- /dev/null +++ b/config/initializers/attribute_types.rb @@ -0,0 +1,11 @@ +class SimpleJsonType < ActiveModel::Type::Value + def cast(value) + return nil if value.blank? + return value if value.is_a?(Hash) + JSON.parse(value) + rescue JSON::ParserError + {} + end +end + +ActiveModel::Type.register(:simple_json, SimpleJsonType) diff --git a/config/routes.rb b/config/routes.rb index 51c5d8ee2..4443b6525 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -224,6 +224,9 @@ Rails.application.routes.draw do namespace :data_sources do get :adresse, to: 'adresse#search', as: :data_source_adresse + get :search_domaine_fonct, to: 'chorus#search_domaine_fonct', as: :search_domaine_fonct + get :search_centre_couts, to: 'chorus#search_centre_couts', as: :search_centre_couts + get :search_ref_programmation, to: 'chorus#search_ref_programmation', as: :search_ref_programmation end # @@ -602,6 +605,7 @@ 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 :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/migrate/20230828131618_add_chorus_column_to_procedure.rb b/db/migrate/20230828131618_add_chorus_column_to_procedure.rb new file mode 100644 index 000000000..fd17d5254 --- /dev/null +++ b/db/migrate/20230828131618_add_chorus_column_to_procedure.rb @@ -0,0 +1,5 @@ +class AddChorusColumnToProcedure < ActiveRecord::Migration[7.0] + def change + add_column :procedures, :chorus, :jsonb, default: {}, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 52f2c2ca3..3dc696f7e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -797,6 +797,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_10_10_103144) do t.string "cadre_juridique" t.bigint "canonical_procedure_id" t.boolean "cerfa_flag", default: false + t.jsonb "chorus", default: {}, null: false t.boolean "cloned_from_library", default: false t.datetime "closed_at", precision: 6 t.datetime "created_at", precision: 6, null: false diff --git a/spec/components/procedures/card/chorus_component_spec.rb b/spec/components/procedures/card/chorus_component_spec.rb new file mode 100644 index 000000000..b5148c376 --- /dev/null +++ b/spec/components/procedures/card/chorus_component_spec.rb @@ -0,0 +1,24 @@ +describe Procedure::Card::ChorusComponent, type: :component do + describe 'render' do + let(:procedure) { create(:procedure) } + + subject do + render_inline(described_class.new(procedure: procedure)) + end + + context 'feature flag not active' do + it 'does not render' do + subject + expect(page).not_to have_text('Connecteur Chorus') + end + end + context 'feature flag active' do + before { Flipper.enable_actor :chorus, procedure } + + it 'render the template' do + subject + expect(page).to have_text('Connecteur Chorus') + end + end + end +end diff --git a/spec/controllers/administrateurs/chorus_controller_spec.rb b/spec/controllers/administrateurs/chorus_controller_spec.rb new file mode 100644 index 000000000..c239a8d26 --- /dev/null +++ b/spec/controllers/administrateurs/chorus_controller_spec.rb @@ -0,0 +1,76 @@ +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]) } + subject { get :edit, params: { procedure_id: procedure.id } } + + context 'not signed in' do + it { is_expected.to redirect_to(new_user_session_path) } + end + + context '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 + before { sign_in(admin.user) } + it { is_expected.to have_http_status(200) } + + context 'rendered' do + render_views + + it { is_expected.to have_http_status(200) } + end + end + end + + describe 'update' do + let(:user) { create(:user) } + let(:admin) { create(:administrateur, user: create(:user)) } + let(:procedure) { create(:procedure, administrateurs: [admin]) } + let(:chorus_configuration_params) { {} } + subject do + put :update, + params: { + procedure_id: procedure.id, + chorus_configuration: chorus_configuration_params + } + end + + context 'not signed in' do + it { is_expected.to redirect_to(new_user_session_path) } + end + + context '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 + before { sign_in(admin.user) } + + context "valid payload" do + let(:centre_de_coup) { '{"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: + } + 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.") + procedure.reload + expect(procedure.chorus_configuration.centre_de_coup).to eq(JSON.parse(centre_de_coup)) + 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 +end diff --git a/spec/controllers/data_sources/chorus_controller_spec.rb b/spec/controllers/data_sources/chorus_controller_spec.rb new file mode 100644 index 000000000..241f133e4 --- /dev/null +++ b/spec/controllers/data_sources/chorus_controller_spec.rb @@ -0,0 +1,54 @@ +describe DataSources::ChorusController do + let(:administrateur) { create(:administrateur) } + + render_views + + before do + sign_in(administrateur.user) + end + + describe 'search_domaine_fonct' do + let(:mock_api_response) do + JSON.parse(Rails.root.join("spec/fixtures/files/api_databretagne/domaine-fonct.json").read)['items'] + end + + before do + allow_any_instance_of(APIBretagneService).to receive(:search_domaine_fonct).and_return(mock_api_response) + end + + it 'works' do + get :search_domaine_fonct, params: { q: "Dépenses" } + expect(response.parsed_body.size).to eq(mock_api_response.size) + end + end + + describe 'search_centre_couts' do + let(:mock_api_response) do + JSON.parse(Rails.root.join("spec/fixtures/files/api_databretagne/centre-couts.json").read)['items'] + end + + before do + allow_any_instance_of(APIBretagneService).to receive(:search_centre_couts).and_return(mock_api_response) + end + + it 'works' do + get :search_centre_couts, params: { q: "Dépenses" } + expect(response.parsed_body.size).to eq(mock_api_response.size) + end + end + + describe 'search_ref_programmation' do + let(:mock_api_response) do + JSON.parse(Rails.root.join("spec/fixtures/files/api_databretagne/ref-programmation.json").read)['items'] + end + + before do + allow_any_instance_of(APIBretagneService).to receive(:search_ref_programmation).and_return(mock_api_response) + end + + it 'works' do + get :search_ref_programmation, params: { q: "Dépenses" } + expect(response.parsed_body.size).to eq(mock_api_response.size) + end + end +end diff --git a/spec/factories/procedure.rb b/spec/factories/procedure.rb index cc2eb519f..a545fab33 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -422,6 +422,22 @@ FactoryBot.define do trait :svr do sva_svr { SVASVRConfiguration.new(decision: :svr).attributes } end + + trait :empty_chorus do + chorus { ChorusConfiguration.new } + end + + trait :partial_chorus do + chorus { ChorusConfiguration.new(centre_de_coup: { a: 1 }) } + end + + trait :filled_chorus do + chorus do + ChorusConfiguration.new(centre_de_coup: { a: 1 }, + domaine_fonctionnel: { b: 2 }, + referentiel_de_programmation: { c: 3 }) + end + end end end diff --git a/spec/fixtures/files/api_databretagne/centre-couts.json b/spec/fixtures/files/api_databretagne/centre-couts.json new file mode 100644 index 000000000..4e16d69af --- /dev/null +++ b/spec/fixtures/files/api_databretagne/centre-couts.json @@ -0,0 +1,709 @@ +{ + "items": [ + { + "ville": null, + "code": "#", + "description": null, + "label": null, + "code_postal": null + }, + { + "ville": "PARIS", + "code": "16GY991", + "description": "(R) Hors Armee de Terre", + "label": "(R)HORS ADT", + "code_postal": "75000" + }, + { + "ville": "Nice", + "code": "2", + "description": "Unites Csfa BA 943 Nice", + "label": "Unites Csfa BA 943", + "code_postal": "6000" + }, + { + "ville": null, + "code": "3070ATE012", + "description": "Centre de coût mutualisé de l’ATE 12", + "label": "PRF0ATE012", + "code_postal": null + }, + { + "ville": "PARIS", + "code": "AAIAC00075", + "description": "AC", + "label": "AC", + "code_postal": "75001" + }, + { + "ville": "PARIS", + "code": "AAIACNU075", + "description": "ACNUSA", + "label": "ACNUSA", + "code_postal": "75006" + }, + { + "ville": "PARIS", + "code": "AAIASN1075", + "description": "Autorite de Surete Nucleaire", + "label": "ASN", + "code_postal": "75012" + }, + { + "ville": "Paris 07", + "code": "AAICADA075", + "description": "CADA", + "label": "CADA", + "code_postal": "75700" + }, + { + "ville": "Paris 07", + "code": "AAICCNE075", + "description": "CCNE", + "label": "CCNE", + "code_postal": "75700" + }, + { + "ville": "Paris 07", + "code": "AAICCSD075", + "description": "CCSDN", + "label": "CCSDN", + "code_postal": "75700" + }, + { + "ville": "Paris 19", + "code": "AAICGLP075", + "description": "CGLPL", + "label": "CGLPL", + "code_postal": "75019" + }, + { + "ville": "Paris 07", + "code": "AAICNCD075", + "description": "CNCDH", + "label": "CNCDH", + "code_postal": "75700" + }, + { + "ville": "Paris 07", + "code": "AAICNCI075", + "description": "CNCIS", + "label": "CNCIS", + "code_postal": "75700" + }, + { + "ville": "PARIS", + "code": "AAICNDP075", + "description": "Commission Nationale du Débat Public", + "label": "CNDP", + "code_postal": "75007" + }, + { + "ville": "Paris 07", + "code": "AAICNDS075", + "description": "CNDS", + "label": "CNDS", + "code_postal": "75700" + }, + { + "ville": "Paris cedex 15", + "code": "AAIRCEP075", + "description": "ARCEP", + "label": "ARCEP", + "code_postal": "75730" + }, + { + "ville": "Paris", + "code": "AAIRE00075", + "description": "CRE commun", + "label": "CRE", + "code_postal": "75379" + }, + { + "ville": null, + "code": "AAIRE01075", + "description": "CRE-PRESIDENT", + "label": "CRE-PRESIDENT", + "code_postal": null + }, + { + "ville": "Paris", + "code": "AAIRE02075", + "description": "CRE Collège/DG", + "label": "CRE01 Collège/DG", + "code_postal": "75379" + }, + { + "ville": "Paris", + "code": "AAIRE03075", + "description": "CRE DRH", + "label": "CRE02 DRH", + "code_postal": "75379" + }, + { + "ville": "Paris", + "code": "AAIRE04075", + "description": "CRE-Direction des Réseaux", + "label": "CRE-DR", + "code_postal": "75379" + }, + { + "ville": "Paris", + "code": "AAIRE05075", + "description": "CRE-Secrétaire Général", + "label": "CRE-SG", + "code_postal": "75379" + }, + { + "ville": "Paris", + "code": "AAIRE06075", + "description": "CRE-Dir Dév Marchés et Transition Energ", + "label": "CRE-DDMTE", + "code_postal": "75379" + }, + { + "ville": "Paris", + "code": "AAIRE07075", + "description": "CRE DSMG", + "label": "CRE DSMG", + "code_postal": "75379" + }, + { + "ville": "Paris", + "code": "AAIRE08075", + "description": "CRE-Direction des Affaires Juridiques", + "label": "CRE-DAJ", + "code_postal": "75379" + }, + { + "ville": "Paris", + "code": "AAIRE09075", + "description": "CRE-Dir affaires europ, inter et coop", + "label": "CRE-DAEIC", + "code_postal": "75379" + }, + { + "ville": "Paris", + "code": "AAIRE10075", + "description": "CRE - DCRI Comm et Relations Instit", + "label": "CRE - DCRI", + "code_postal": "75379" + }, + { + "ville": "Paris", + "code": "AAIRE11075", + "description": "CRE PAYE", + "label": "CRE10 PAYE", + "code_postal": "75379" + }, + { + "ville": null, + "code": "AAIRE12075", + "description": "CRE-COMMISSAIRE1", + "label": "CRE-COMMISSAIRE1", + "code_postal": null + }, + { + "ville": null, + "code": "AAIRE13075", + "description": "CRE-COMMISSAIRE2", + "label": "CRE-COMMISSAIRE2", + "code_postal": null + }, + { + "ville": null, + "code": "AAIRE14075", + "description": "CRE-COMMISSAIRE3", + "label": "CRE-COMMISSAIRE3", + "code_postal": null + }, + { + "ville": null, + "code": "AAIRE15075", + "description": "CRE-COMMISSAIRE4", + "label": "CRE-COMMISSAIRE4", + "code_postal": null + }, + { + "ville": null, + "code": "AAIRE16075", + "description": "CRE-COMMISSAIRE5", + "label": "CRE-COMMISSAIRE5", + "code_postal": null + }, + { + "ville": null, + "code": "AAIRE17075", + "description": "CRE-Service Prospective et Innovation", + "label": "CRE-SPI", + "code_postal": null + }, + { + "ville": null, + "code": "AAIRE18075", + "description": "CRE-CS2", + "label": "CRE-CS2", + "code_postal": null + }, + { + "ville": null, + "code": "AAIRE19075", + "description": "CRE-RI", + "label": "CRE-RI", + "code_postal": null + }, + { + "ville": null, + "code": "AAIRE20075", + "description": "CRE-AUTRES", + "label": "CRE-AUTRES", + "code_postal": null + }, + { + "ville": null, + "code": "AAIRE21075", + "description": "CRE-CORDIS", + "label": "CRE-CORDIS", + "code_postal": null + }, + { + "ville": null, + "code": "AAIRE22075", + "description": "CRE-SIGR", + "label": "CRE-SIGR", + "code_postal": null + }, + { + "ville": null, + "code": "AAIRE23075", + "description": "CRE-Direction des Affaires Financières", + "label": "CRE-DAF", + "code_postal": null + }, + { + "ville": null, + "code": "AAIRE24075", + "description": "CRE-SGS", + "label": "CRE-SGS", + "code_postal": null + }, + { + "ville": null, + "code": "AAIRE25075", + "description": "CRE-ACTION SOCIALE", + "label": "CRE-ACTION SOCIALE", + "code_postal": null + }, + { + "ville": null, + "code": "AAIRE26075", + "description": "CRE-FONCTIONNEMENT COURANT", + "label": "CRE-FONCTIONNEMENT", + "code_postal": null + }, + { + "ville": "Paris", + "code": "AAIRE27075", + "description": "CRE - STP Service Transfo et Projets", + "label": "CRE - STP", + "code_postal": "75379" + }, + { + "ville": "Paris", + "code": "AAIRE28075", + "description": "CRE-DAEF", + "label": "CRE-DAEF", + "code_postal": "75379" + }, + { + "ville": null, + "code": "AAIRE75ASP", + "description": "CRE Contentieux MESSER", + "label": "CRE Cont MESSER", + "code_postal": null + }, + { + "ville": null, + "code": "ACCELER001", + "description": "Accélérateur TE2", + "label": "Accélérat TE2", + "code_postal": null + }, + { + "ville": "PARIS", + "code": "ACI0DTA075", + "description": "Direction du Transport Aerien", + "label": "DTA", + "code_postal": "75720" + }, + { + "ville": "ST PIERRE ET MIQUELON", + "code": "ACIAERO975", + "description": "SAC SPM Aérogare", + "label": "SAC SPM Aérogare", + "code_postal": "97500" + }, + { + "ville": "NOUMEA", + "code": "ACIDC98098", + "description": "Directionn de l'Aviation Civile NC", + "label": "DAC NC", + "code_postal": "98845" + }, + { + "ville": "FORT-DE-FRANCE CEDEX", + "code": "ACIDCA2972", + "description": "DSAC ANTILLES-GUYANE", + "label": "DSAC ANTILLES-GUYANE", + "code_postal": "97262" + }, + { + "ville": "SAINT-DENIS MESSAG CEDEX 9", + "code": "ACIDCA4974", + "description": "DSAC OCEAN INDIEN REUNION MAYOTTE", + "label": "DSAC OI REUNION MAYO", + "code_postal": "97408" + }, + { + "ville": "SAINT-PIERRE-ET-MIQUELON", + "code": "ACIDCA5975", + "description": "SAC SAINT-PIERRE-ET-MIQUELON", + "label": "SAC SPMI", + "code_postal": "97500" + }, + { + "ville": "UVEA-WALLIS", + "code": "ACIDCB6986", + "description": "SAC WALLIS FUTUNA", + "label": "SAC WALLIS FUTUNA", + "code_postal": "98600" + }, + { + "ville": "FAA-A TAHITI", + "code": "ACIDCB7987", + "description": "SAC POLYNESIE", + "label": "SAC POLYNESIE", + "code_postal": "98702" + }, + { + "ville": "PARIS", + "code": "ACIDSAC075", + "description": "Dir de la Sécurité de l Aviation Civile", + "label": "DSAC", + "code_postal": "75720" + }, + { + "ville": "PARIS", + "code": "ACIDSNA075", + "description": "DSNA", + "label": "DSNA", + "code_postal": "75015" + }, + { + "ville": "PARIS", + "code": "ACIOCAC075", + "description": "Direction Generale de l'Aviation Civile", + "label": "DGAC", + "code_postal": "75720" + }, + { + "ville": "ST PIERRE ET MIQUELON", + "code": "ACISLIA975", + "description": "SAC SPM SSLIA", + "label": "SAC SPM SSLIA", + "code_postal": "97500" + }, + { + "ville": "PARIS", + "code": "ACISNIA075", + "description": "Service National d'Ingénierie Aéro", + "label": "SNIA", + "code_postal": "75700" + }, + { + "ville": "VBONNEUIL SUR MARNE CEDEX", + "code": "ACISTAC094", + "description": "Service Technique de l'Aviation civile", + "label": "STAC", + "code_postal": "94381" + }, + { + "ville": "PARIS", + "code": "ADCACTF075", + "description": "Serv. gest. bien ADC", + "label": "CC S. gest. bien ADC", + "code_postal": "75105" + }, + { + "ville": "PARIS", + "code": "ADCAIAC075", + "description": "BAIAC", + "label": "CC BAIAC", + "code_postal": "75105" + }, + { + "ville": null, + "code": "ADCAISC075", + "description": "Centre de cout BAIATSC", + "label": "BAIATSC", + "code_postal": null + }, + { + "ville": null, + "code": "ADCAJCX075", + "description": "CC DLPAJ suivi du contentieux", + "label": "CC DLPAJ CTX", + "code_postal": null + }, + { + "ville": "PARIS", + "code": "ADCBAFE075", + "description": "BAFED", + "label": "CC BAFED", + "code_postal": "75105" + }, + { + "ville": null, + "code": "ADCBCAM075", + "description": "BCAM", + "label": "BCAM", + "code_postal": null + }, + { + "ville": "PARIS", + "code": "ADCBEEP075", + "description": "BEEP", + "label": "CC BEEP", + "code_postal": "75008" + }, + { + "ville": "PARIS", + "code": "ADCBFAC075", + "description": "BBFAC", + "label": "CC BBFAC", + "code_postal": "75105" + }, + { + "ville": "PARIS", + "code": "ADCBFPP075", + "description": "BFPP", + "label": "CC BFPP", + "code_postal": "75105" + }, + { + "ville": "PARIS", + "code": "ADCBGCP075", + "description": "BGCPAC", + "label": "CC BGCPAC", + "code_postal": "75008" + }, + { + "ville": null, + "code": "ADCBMAT075", + "description": "BMAT", + "label": "BMAT", + "code_postal": null + }, + { + "ville": "PARIS", + "code": "ADCBPAI075", + "description": "BPAI", + "label": "CC BPAI", + "code_postal": "75105" + }, + { + "ville": null, + "code": "ADCBQPA075", + "description": "BQPPAT", + "label": "BQPPAT", + "code_postal": null + }, + { + "ville": "PARIS", + "code": "ADCBRH1075", + "description": "BRH (T2)", + "label": "CC DRH (T2)", + "code_postal": "75105" + }, + { + "ville": "PARIS", + "code": "ADCCBMI075", + "description": "BBFAC CABMIN", + "label": "CC BBFAC CABMIN", + "code_postal": "75105" + }, + { + "ville": "PARIS", + "code": "ADCCBOM075", + "description": "BBFAC CABOM", + "label": "CC BBFAC CABOM", + "code_postal": "75105" + }, + { + "ville": null, + "code": "ADCCCSP075", + "description": "BBFAC JAS-CCSP", + "label": "BBFAC JAS-CCSP", + "code_postal": null + }, + { + "ville": "PARIS cedex 17", + "code": "ADCCNCC075", + "description": "CNCCFP", + "label": "CC CNCCFP", + "code_postal": "75176" + }, + { + "ville": null, + "code": "ADCCOMM075", + "description": "Dép. T2 agents GN DICOM", + "label": "T2 agents GN DICOM", + "code_postal": null + }, + { + "ville": null, + "code": "ADCCPFI075", + "description": "Dép. T2 agents CPFi DEPAFI", + "label": "T2 agents GN CPFi", + "code_postal": null + }, + { + "ville": "AGEN CEDEX", + "code": "ADCCSAI075", + "description": "Centre de coût SAILMI", + "label": "CC SAILMI", + "code_postal": "47004" + }, + { + "ville": null, + "code": "ADCCSAT075", + "description": "DEPENSES FONCTIONNEMENT PREFETS CSATE", + "label": "DEP FC PREFETS CSATE", + "code_postal": null + }, + { + "ville": "PARIS", + "code": "ADCCSER075", + "description": "CC FNPC", + "label": "CC FNPC", + "code_postal": "75015" + }, + { + "ville": "PARIS", + "code": "ADCDDE1075", + "description": "CC DDE ADC DEGEOM", + "label": "CC DDE ADC DEGEOM", + "code_postal": "75007" + }, + { + "ville": "AGEN CEDEX", + "code": "ADCDDI0075", + "description": "MI 0333 DDI", + "label": "MI 0333 ADCDDI0075", + "code_postal": "47004" + }, + { + "ville": "PARIS", + "code": "ADCDGCL075", + "description": "DGCL", + "label": "DGCL", + "code_postal": "75000" + }, + { + "ville": "PARIS", + "code": "ADCDGOM075", + "description": "DEGEOM (T2)", + "label": "CC DEGEOM (T2)", + "code_postal": "75105" + }, + { + "ville": "PARIS", + "code": "ADCDICO075", + "description": "DICOM", + "label": "CC DICOM", + "code_postal": "75008" + }, + { + "ville": "PARIS", + "code": "ADCDLPA075", + "description": "DLPAJ", + "label": "CC DLPAJ", + "code_postal": "75008" + }, + { + "ville": "PARIS", + "code": "ADCDMA1075", + "description": "DMAT", + "label": "DMAT", + "code_postal": "75008" + }, + { + "ville": "AGEN CEDEX", + "code": "ADCDNUM075", + "description": "Centre de cout DNUM", + "label": "CC DNUM", + "code_postal": "47004" + }, + { + "ville": null, + "code": "ADCDPSU075", + "description": "Projet site unique", + "label": "Projet site unique", + "code_postal": null + }, + { + "ville": "PARIS", + "code": "ADCDPTS075", + "description": "CC FAETON", + "label": "CC FAETON", + "code_postal": "75015" + }, + { + "ville": "PARIS", + "code": "ADCDSIC075", + "description": "CC DSIC", + "label": "CC DSIC", + "code_postal": "75015" + }, + { + "ville": "PARIS", + "code": "ADCELEG075", + "description": "Vacations Elections Personnel GN", + "label": "CC Vac Elec GN", + "code_postal": "75008" + }, + { + "ville": "PARIS", + "code": "ADCELEP075", + "description": "Vacations Elections Personnel PN", + "label": "CC Vac Elec PN", + "code_postal": "75008" + }, + { + "ville": "PARIS", + "code": "ADCELEX075", + "description": "Vacations Elections Personnel Autre", + "label": "CC Vac Elec Autre", + "code_postal": "75008" + }, + { + "ville": null, + "code": "ADCEM01075", + "description": "BBFAC CAB SG", + "label": "BBFAC CAB SG", + "code_postal": null + }, + { + "ville": null, + "code": "ADCEM02075", + "description": "BBFAC SHFD SG", + "label": "BBFAC SHFD SG", + "code_postal": null + } + ], + "pageInfo": { + "totalRows": 18988, + "page": 1, + "pageSize": 100 + } +} \ No newline at end of file diff --git a/spec/fixtures/files/api_databretagne/domaine-fonct.json b/spec/fixtures/files/api_databretagne/domaine-fonct.json new file mode 100644 index 000000000..993aa85d0 --- /dev/null +++ b/spec/fixtures/files/api_databretagne/domaine-fonct.json @@ -0,0 +1,609 @@ +{ + "items": [ + { + "code_programme": "101", + "code": "0101", + "description": null, + "label": "Accès au droit et à la justice" + }, + { + "code_programme": "101", + "code": "0101-01", + "description": null, + "label": "Aide juridictionnelle" + }, + { + "code_programme": "101", + "code": "0101-01-03", + "description": null, + "label": null + }, + { + "code_programme": "101", + "code": "0101-02", + "description": null, + "label": "Développement de l'accès au droit et du réseau judiciaire de proximité" + }, + { + "code_programme": "101", + "code": "0101-03", + "description": null, + "label": "Aide aux victimes" + }, + { + "code_programme": "101", + "code": "0101-04", + "description": null, + "label": "Médiation familiale et espaces de rencontre" + }, + { + "code_programme": "101", + "code": "0101-05", + "description": null, + "label": "Indemnisation des avoués" + }, + { + "code_programme": "102", + "code": "0102", + "description": null, + "label": "Accès et retour à l'emploi" + }, + { + "code_programme": "102", + "code": "0102-01", + "description": null, + "label": "Amélioration de l'efficacité du service public de l'emploi" + }, + { + "code_programme": "102", + "code": "0102-01-01", + "description": null, + "label": "Indemnisation des demandeurs d'emploi" + }, + { + "code_programme": "102", + "code": "0102-01-02", + "description": null, + "label": "Coordination du service public de l'emploi" + }, + { + "code_programme": "102", + "code": "0102-02", + "description": null, + "label": "Amélioration des dispositifs en faveur de l'emploi des personnes les plus éloignées du marché du travail" + }, + { + "code_programme": "102", + "code": "0102-02-01", + "description": null, + "label": "Insertion dans l'emploi au moyen de contrats aidés" + }, + { + "code_programme": "102", + "code": "0102-02-02", + "description": null, + "label": "Accompagnement des publics les plus en difficultés" + }, + { + "code_programme": "102", + "code": "0102-03", + "description": null, + "label": "Plan d'investissement des compétences" + }, + { + "code_programme": "102", + "code": "0102-04", + "description": null, + "label": "AideExcepContPro" + }, + { + "code_programme": "103", + "code": "0103", + "description": null, + "label": "Accompagnement des mutations économiques et développement de l'emploi" + }, + { + "code_programme": "103", + "code": "0103-01", + "description": null, + "label": "Anticipation et accompagnement des conséquences des mutations économiques sur l'emploi" + }, + { + "code_programme": "103", + "code": "0103-01-01", + "description": null, + "label": "Développement de l'emploi en TPE-PME" + }, + { + "code_programme": "103", + "code": "0103-01-02", + "description": null, + "label": "Implication des branches et des entreprises dans la prévention du licenciement et le reclassement des salariés" + }, + { + "code_programme": "103", + "code": "0103-02", + "description": null, + "label": "Amélioration de l'insertion dans l'emploi par l'adaptation des qualifications et la reconnaissance des compétences" + }, + { + "code_programme": "103", + "code": "0103-02-03", + "description": null, + "label": "Reconnaissance des compétences acquises par les personnes" + }, + { + "code_programme": "103", + "code": "0103-02-04", + "description": null, + "label": "Amélioration de l'accès à la qualification par le développement de l'alternance et de la certification" + }, + { + "code_programme": "103", + "code": "0103-03", + "description": null, + "label": "Développement de l'emploi" + }, + { + "code_programme": "103", + "code": "0103-03-01", + "description": null, + "label": "Baisse du coût du travail pour faciliter le développement de territoires et de secteurs à forts potentiels d'emploi" + }, + { + "code_programme": "103", + "code": "0103-03-02", + "description": null, + "label": "Promotion de l'activité" + }, + { + "code_programme": "103", + "code": "0103-03-03", + "description": null, + "label": "Aide à l'embauche" + }, + { + "code_programme": "103", + "code": "0103-04", + "description": null, + "label": "Plan d'investissement des compétences" + }, + { + "code_programme": "103", + "code": "0103-05", + "description": null, + "label": "Aide except apprentis" + }, + { + "code_programme": "104", + "code": "0104", + "description": null, + "label": "Intégration et accès à la nationalité française" + }, + { + "code_programme": "104", + "code": "0104-11", + "description": null, + "label": "Accueil des étrangers primo arrivants" + }, + { + "code_programme": "104", + "code": "0104-11-02", + "description": null, + "label": "Accueil des étrangers primo-arrivants - Dépenses sur crédits nationaux" + }, + { + "code_programme": "104", + "code": "0104-11-04", + "description": null, + "label": "Accueil des étrangers primo-arrivants - Dépenses à la charge du fonds européen pour l'intégration (FEI)" + }, + { + "code_programme": "104", + "code": "0104-11-05", + "description": null, + "label": "Accueil des étrangers primo-arrivants - Dépenses à la charge du Fonds européen asile, migration et intégration" + }, + { + "code_programme": "104", + "code": "0104-12", + "description": null, + "label": "Actions d'accompagnement des étrangers en situation régulière" + }, + { + "code_programme": "104", + "code": "0104-12-02", + "description": null, + "label": "Actions d'accompagnement des primo-arrivants - Dépenses sur crédits nationaux" + }, + { + "code_programme": "104", + "code": "0104-12-08", + "description": null, + "label": "Actions d'accompagnement des primo-arrivants. Dépenses à la charge du Fonds européen pour l'intégration (FEI)" + }, + { + "code_programme": "104", + "code": "0104-12-14", + "description": null, + "label": "Actions d'accompagnement des étrangers. Dépenses à la charge du Fonds européen asile, migration et intégration (FAMI)" + }, + { + "code_programme": "104", + "code": "0104-12-15", + "description": null, + "label": "Actions Réfugiés" + }, + { + "code_programme": "104", + "code": "0104-14", + "description": null, + "label": "Accès à la nationalité française" + }, + { + "code_programme": "104", + "code": "0104-15", + "description": null, + "label": "Accompagnement des réfugiés" + }, + { + "code_programme": "104", + "code": "0104-15-01", + "description": null, + "label": "Centres provisoires d'hébergement des réfugiés" + }, + { + "code_programme": "104", + "code": "0104-15-04", + "description": null, + "label": "Aide et accompagnement des réfugiés. Dépenses à la charge du Fonds Européen pour les réfugiés (FER)" + }, + { + "code_programme": "104", + "code": "0104-15-12", + "description": null, + "label": "Aide et accompagnement des réfugiés - Dépenses sur crédits nationaux" + }, + { + "code_programme": "104", + "code": "0104-15-13", + "description": null, + "label": "Aide et accompagnement des réfugiés - dépenses à la charge du Fonds européen asile, migration et intégration (FAMI)" + }, + { + "code_programme": "104", + "code": "0104-16", + "description": null, + "label": "Accompagnement du plan de traitement des foyers de travailleurs migrants" + }, + { + "code_programme": "104", + "code": "0104-16-01", + "description": null, + "label": "Foyers de travailleurs migrants - Dépenses sur crédits nationaux" + }, + { + "code_programme": "105", + "code": "0105", + "description": null, + "label": "Action de la France en Europe et dans le monde" + }, + { + "code_programme": "105", + "code": "0105-01", + "description": null, + "label": "Coordination de l'action diplomatique" + }, + { + "code_programme": "105", + "code": "0105-01-01", + "description": null, + "label": "Etat major : cabinets" + }, + { + "code_programme": "105", + "code": "0105-01-08", + "description": null, + "label": "Etat major : logistique de niveau chef d'Etat ou de gouvernement" + }, + { + "code_programme": "105", + "code": "0105-01-09", + "description": null, + "label": "Etat-major : affaires politiques" + }, + { + "code_programme": "105", + "code": "0105-01-15", + "description": null, + "label": "Personnel concourant à l'action coordination de l'action diplomatique" + }, + { + "code_programme": "105", + "code": "0105-01-16", + "description": null, + "label": "Sécurité des communautés françaises" + }, + { + "code_programme": "105", + "code": "0105-02", + "description": null, + "label": "Action européenne" + }, + { + "code_programme": "105", + "code": "0105-02-01", + "description": null, + "label": "Moyens de fonctionnement : services d'administration centrale" + }, + { + "code_programme": "105", + "code": "0105-02-07", + "description": null, + "label": "Dépenses d'intervention et de communication du ministre chargé des affaires européennes" + }, + { + "code_programme": "105", + "code": "0105-02-08", + "description": null, + "label": "Contributions aux organisations européennes" + }, + { + "code_programme": "105", + "code": "0105-02-11", + "description": null, + "label": "Personnel concourant à l'action action européenne" + }, + { + "code_programme": "105", + "code": "0105-04", + "description": null, + "label": "Contributions internationales" + }, + { + "code_programme": "105", + "code": "0105-04-01", + "description": null, + "label": "Opérations de maintien de la paix : participations obligatoires aux opérations de maintien de la paix des Nations Unies (en devises)" + }, + { + "code_programme": "105", + "code": "0105-04-02", + "description": null, + "label": "Contributions en euros aux organisations internationales" + }, + { + "code_programme": "105", + "code": "0105-04-05", + "description": null, + "label": "Contributions en devises aux organisations internationales" + }, + { + "code_programme": "105", + "code": "0105-05", + "description": null, + "label": "Coopération de sécurité et de défense" + }, + { + "code_programme": "105", + "code": "0105-05-01", + "description": null, + "label": "Formation des élites et cadres de sécurité et de défense" + }, + { + "code_programme": "105", + "code": "0105-05-02", + "description": null, + "label": "Ingénierie de sécurité et de défense" + }, + { + "code_programme": "105", + "code": "0105-05-03", + "description": null, + "label": "Fonctions de direction et soutien en France et étranger" + }, + { + "code_programme": "105", + "code": "0105-05-04", + "description": null, + "label": "Personnel concourant à l'action coopération de sécurité et de défense" + }, + { + "code_programme": "105", + "code": "0105-06", + "description": null, + "label": "Soutien" + }, + { + "code_programme": "105", + "code": "0105-06-06", + "description": null, + "label": "Ressources humaines" + }, + { + "code_programme": "105", + "code": "0105-06-08", + "description": null, + "label": "Systèmes d'information et de télécommunication" + }, + { + "code_programme": "105", + "code": "0105-06-10", + "description": null, + "label": "Logistique diplomatique" + }, + { + "code_programme": "105", + "code": "0105-06-11", + "description": null, + "label": "Politique immobilière" + }, + { + "code_programme": "105", + "code": "0105-06-12", + "description": null, + "label": "Sécurité diplomatique" + }, + { + "code_programme": "105", + "code": "0105-06-14", + "description": null, + "label": "Personnel concourant à l'action soutien" + }, + { + "code_programme": "105", + "code": "0105-07", + "description": null, + "label": "Réseau diplomatique" + }, + { + "code_programme": "105", + "code": "0105-07-01", + "description": null, + "label": "Réseau diplomatique : postes à l'étranger - dépenses de structures du ministère des Affaires étrangères" + }, + { + "code_programme": "105", + "code": "0105-07-02", + "description": null, + "label": "Personnel du Ministère des Affaires étrangères et européennes concourant à l'action réseau diplomatique" + }, + { + "code_programme": "105", + "code": "0105-07-03", + "description": null, + "label": "Réseau diplomatique : postes à l'étranger - dépenses de structures hors ministère des Affaires étrangères" + }, + { + "code_programme": "105", + "code": "0105-07-04", + "description": null, + "label": "Personnel hors Ministère des Affaires étrangères et européennes, concourant à l'action réseau diplomatique" + }, + { + "code_programme": "105", + "code": "0105-99", + "description": null, + "label": "Dépenses de personnel à reventiler entre les actions du programme" + }, + { + "code_programme": "107", + "code": "0107", + "description": null, + "label": "Administration pénitentiaire" + }, + { + "code_programme": "107", + "code": "0107-01", + "description": null, + "label": "Garde et contrôle des personnes placées sous main de justice" + }, + { + "code_programme": "107", + "code": "0107-02", + "description": null, + "label": "Accueil et accompagnement des personnes placées sous main de justice" + }, + { + "code_programme": "107", + "code": "0107-04", + "description": null, + "label": "Soutien et formation" + }, + { + "code_programme": "107", + "code": "0107-99", + "description": null, + "label": "Dépenses de personnel du programme à reventiler" + }, + { + "code_programme": "109", + "code": "0109", + "description": null, + "label": "Aide à l'accès au logement" + }, + { + "code_programme": "109", + "code": "0109-01", + "description": null, + "label": "Aides personnelles" + }, + { + "code_programme": "109", + "code": "0109-02", + "description": null, + "label": "Information relative au logement et accompagnement des publics en difficulté" + }, + { + "code_programme": "109", + "code": "0109-02-01", + "description": null, + "label": "Information sur le logement (ANIL et ADIL)" + }, + { + "code_programme": "109", + "code": "0109-02-02", + "description": null, + "label": "Autres interventions de l'Etat en faveur du logement des publics en difficulté" + }, + { + "code_programme": "109", + "code": "0109-03", + "description": null, + "label": "Sécurisation des risques locatifs" + }, + { + "code_programme": "109", + "code": "0109-03-01", + "description": null, + "label": "Garantie des risques locatifs" + }, + { + "code_programme": "110", + "code": "0110", + "description": null, + "label": "Aide économique et financière au développement" + }, + { + "code_programme": "110", + "code": "0110-01", + "description": null, + "label": "Aide économique et financière multilatérale" + }, + { + "code_programme": "110", + "code": "0110-01-10", + "description": null, + "label": "Association internationale de développement" + }, + { + "code_programme": "110", + "code": "0110-01-13", + "description": null, + "label": "Groupe de la banque mondiale" + }, + { + "code_programme": "110", + "code": "0110-01-16", + "description": null, + "label": "Coopération technique" + }, + { + "code_programme": "110", + "code": "0110-01-18", + "description": null, + "label": "Fonds africain de développement" + }, + { + "code_programme": "110", + "code": "0110-01-24", + "description": null, + "label": "Fonds asiatique de développement" + } + ], + "pageInfo": { + "totalRows": 3066, + "page": 1, + "pageSize": 100 + } +} \ No newline at end of file diff --git a/spec/fixtures/files/api_databretagne/ref-programmation.json b/spec/fixtures/files/api_databretagne/ref-programmation.json new file mode 100644 index 000000000..30f02b27e --- /dev/null +++ b/spec/fixtures/files/api_databretagne/ref-programmation.json @@ -0,0 +1,609 @@ +{ + "items": [ + { + "code_programme": "101", + "code": "010101010101", + "description": null, + "label": "DOTATIONS CARPA AJ ET AUTRES INTERVENTIONS" + }, + { + "code_programme": "101", + "code": "010101010106", + "description": null, + "label": "RETRIBUER AVOCATS CE CCASS MISSIONS AJ" + }, + { + "code_programme": "101", + "code": "010101010113", + "description": null, + "label": "RETRIBUER HUISSIERS" + }, + { + "code_programme": "101", + "code": "010101010114", + "description": null, + "label": "AUTRES HONOR FRAIS ACTES PROCED ENQUETES TRADUCT" + }, + { + "code_programme": "101", + "code": "010101010115", + "description": null, + "label": "RETRIBUER EXPERTS MISSIONS AJ" + }, + { + "code_programme": "101", + "code": "010101010116", + "description": null, + "label": "RETRIBUER MEDIATEURS" + }, + { + "code_programme": "101", + "code": "010101010117", + "description": null, + "label": "DOTATION CONTRACTUALISATION" + }, + { + "code_programme": "101", + "code": "010101010118", + "description": null, + "label": "CONDUITE CHANGEMENT AIDE JURIDICTIONNELLE" + }, + { + "code_programme": "101", + "code": "010101040101", + "description": null, + "label": "VERSER UNE SUBVENTION EN FAVEUR DES CDAD" + }, + { + "code_programme": "101", + "code": "010101040103", + "description": null, + "label": "MAISONS DE JUSTICE ET DU DROIT" + }, + { + "code_programme": "101", + "code": "010101040104", + "description": null, + "label": "VERSER UNE SUBV ASSOC FED AD" + }, + { + "code_programme": "101", + "code": "010101050105", + "description": null, + "label": "MEDIATION FAMILIALE" + }, + { + "code_programme": "101", + "code": "010101050106", + "description": null, + "label": "ESPACES RENCONTRE" + }, + { + "code_programme": "101", + "code": "010101050107", + "description": null, + "label": "MEDIATION - HORS MEDIATION FAMILIALE" + }, + { + "code_programme": "101", + "code": "010101060102", + "description": null, + "label": "DOTATION AIDE AUX VICTIMES HORS BAV" + }, + { + "code_programme": "101", + "code": "010101060105", + "description": null, + "label": "ASSISTANCE TELEPH SITE INTERNET AV TGD" + }, + { + "code_programme": "101", + "code": "010101060106", + "description": null, + "label": "CREATION SOUTIEN EQ BUREAUX AIDE VICTIMES" + }, + { + "code_programme": "101", + "code": "010101070101", + "description": null, + "label": "CONTRIBUTION AU FIDA" + }, + { + "code_programme": "102", + "code": "010200000101", + "description": null, + "label": "Allocation de solidarité spécifique (ASS)" + }, + { + "code_programme": "102", + "code": "010200000102", + "description": null, + "label": "Contribution exceptionnelle de solidarité - FDS" + }, + { + "code_programme": "102", + "code": "010200000202", + "description": null, + "label": "Allocations complémentaires" + }, + { + "code_programme": "102", + "code": "010200000203", + "description": null, + "label": "Aide au pouvoir d'achat des demandeurs d'emploi" + }, + { + "code_programme": "102", + "code": "010200000301", + "description": null, + "label": "Allocations temporaires d'attente (ATA)" + }, + { + "code_programme": "102", + "code": "010200000304", + "description": null, + "label": "Intermittents" + }, + { + "code_programme": "102", + "code": "010200000702", + "description": null, + "label": "Maisons de l'emploi" + }, + { + "code_programme": "102", + "code": "010200000703", + "description": null, + "label": "Maison de l'emploi - investissement" + }, + { + "code_programme": "102", + "code": "010200000704", + "description": null, + "label": "Clauses sociales" + }, + { + "code_programme": "102", + "code": "010200000801", + "description": null, + "label": "Pôle emploi" + }, + { + "code_programme": "102", + "code": "010200001005", + "description": null, + "label": "Contrats initiative Emploi (CIE)-Marchand" + }, + { + "code_programme": "102", + "code": "010200001006", + "description": null, + "label": "Parcours Emploi Compétences (PEC)-CAE-Non Marchand" + }, + { + "code_programme": "102", + "code": "010200001007", + "description": null, + "label": "Emplois d'avenir" + }, + { + "code_programme": "102", + "code": "010200001101", + "description": null, + "label": "Chantier de développement local (Mayotte)" + }, + { + "code_programme": "102", + "code": "010200001102", + "description": null, + "label": "Congé solidarité (secteur marchand - outre mer)" + }, + { + "code_programme": "102", + "code": "010200001103", + "description": null, + "label": "Contrats d'accès à l'emploi DOM" + }, + { + "code_programme": "102", + "code": "010200001106", + "description": null, + "label": "Contrats emploi jeune et consolidation" + }, + { + "code_programme": "102", + "code": "010200001107", + "description": null, + "label": "Contrats emploi solidarité" + }, + { + "code_programme": "102", + "code": "010200001109", + "description": null, + "label": "Stage d'insertion et de formation professionnelle" + }, + { + "code_programme": "102", + "code": "010200001201", + "description": null, + "label": "Contrats d'accompagnement dans l'emploi (CAE) DOM" + }, + { + "code_programme": "102", + "code": "010200001210", + "description": null, + "label": "CAE, CAV" + }, + { + "code_programme": "102", + "code": "010200001301", + "description": null, + "label": "Agence Services et Paiement (ASP)-Frais de gestion" + }, + { + "code_programme": "102", + "code": "010200001302", + "description": null, + "label": "ASP-Dotation d'investissement" + }, + { + "code_programme": "102", + "code": "010200001401", + "description": null, + "label": "Contrat d'autonomie pour les jeunes des quartiers" + }, + { + "code_programme": "102", + "code": "010200001501", + "description": null, + "label": "Entreprises adaptées-Aides au poste classiques" + }, + { + "code_programme": "102", + "code": "010200001503", + "description": null, + "label": "Programmes Régionaux d'insertion des TH (PRITH)" + }, + { + "code_programme": "102", + "code": "010200001504", + "description": null, + "label": "PROG Ins. travailleurs hand. CPER" + }, + { + "code_programme": "102", + "code": "010200001505", + "description": null, + "label": "Subventions aux entreprises adaptées" + }, + { + "code_programme": "102", + "code": "010200001601", + "description": null, + "label": "IAE-Associations intermédiaires(AI)-AAP classiques" + }, + { + "code_programme": "102", + "code": "010200001602", + "description": null, + "label": "IAE-Chantiers d'insertion (ACI)-AAP classiques" + }, + { + "code_programme": "102", + "code": "010200001603", + "description": null, + "label": "IAE- Entreprises d'insertion (EI)-AAP classiques" + }, + { + "code_programme": "102", + "code": "010200001604", + "description": null, + "label": "Exos-Structures agréées Réinsertion Pro" + }, + { + "code_programme": "102", + "code": "010200001605", + "description": null, + "label": "IAE-Fonds départemental d'insertion (FDI)" + }, + { + "code_programme": "102", + "code": "010200001607", + "description": null, + "label": "Réserves parlementaires" + }, + { + "code_programme": "102", + "code": "010200001608", + "description": null, + "label": "ETCLD-Contribution développement l'emploi (CDE)" + }, + { + "code_programme": "102", + "code": "010200001609", + "description": null, + "label": "Exonération association intermédiaire" + }, + { + "code_programme": "102", + "code": "010200001610", + "description": null, + "label": "IAE-Exos-Ateliers et Chantiers d'insertion (ACI)" + }, + { + "code_programme": "102", + "code": "010200001611", + "description": null, + "label": "Contrats à impact social (CIS)" + }, + { + "code_programme": "102", + "code": "010200001612", + "description": null, + "label": "FIE-Initiatives territoriales (IT)" + }, + { + "code_programme": "102", + "code": "010200001613", + "description": null, + "label": "IAE-Entrep Travail Temp Inser(ETTI)-AAP classiques" + }, + { + "code_programme": "102", + "code": "010200001614", + "description": null, + "label": "IAE-Entrep d'inser travail indépendant (EITI)" + }, + { + "code_programme": "102", + "code": "010200001615", + "description": null, + "label": "IAE-SEVE Emploi" + }, + { + "code_programme": "102", + "code": "010200001616", + "description": null, + "label": "Contrat Pro IAE" + }, + { + "code_programme": "102", + "code": "010200001617", + "description": null, + "label": "IAE-Chantiers d'insertion (ACI)-Contrats Pass" + }, + { + "code_programme": "102", + "code": "010200001618", + "description": null, + "label": "CDI Séniors IAE" + }, + { + "code_programme": "102", + "code": "010200001619", + "description": null, + "label": "IAE-Aide création d'entreprise- Accompagnement" + }, + { + "code_programme": "102", + "code": "010200001701", + "description": null, + "label": "Accompagnement" + }, + { + "code_programme": "102", + "code": "010200001702", + "description": null, + "label": "Actions de parrainage" + }, + { + "code_programme": "102", + "code": "010200001704", + "description": null, + "label": "EPIDE-Fonctionnement" + }, + { + "code_programme": "102", + "code": "010200001705", + "description": null, + "label": "Fonds Insertion professionnelle des jeunes (FIPJ)" + }, + { + "code_programme": "102", + "code": "010200001706", + "description": null, + "label": "Missions locales" + }, + { + "code_programme": "102", + "code": "010200001707", + "description": null, + "label": "Missions locales Accompagnement EAV" + }, + { + "code_programme": "102", + "code": "010200001708", + "description": null, + "label": "Structuration Réseau ML (UNML et ARML)" + }, + { + "code_programme": "102", + "code": "010200001710", + "description": null, + "label": "Ecoles de la deuxième chance (E2C)" + }, + { + "code_programme": "102", + "code": "010200001714", + "description": null, + "label": "EPIDE- Dotation d'investissement" + }, + { + "code_programme": "102", + "code": "010200001716", + "description": null, + "label": "Allocation APEC" + }, + { + "code_programme": "102", + "code": "010200001717", + "description": null, + "label": "Allocation AIJ" + }, + { + "code_programme": "102", + "code": "010200001718", + "description": null, + "label": "Allocation Jeunes étudiants diplômés" + }, + { + "code_programme": "102", + "code": "010200001719", + "description": null, + "label": "Mentorat" + }, + { + "code_programme": "102", + "code": "010200001720", + "description": null, + "label": "CEJ-Allocation Jeunes Ponctuelle PE (AJPE)" + }, + { + "code_programme": "102", + "code": "010200001721", + "description": null, + "label": "CEJ-Nouveaux acteurs accomp des jeunes en rupture" + }, + { + "code_programme": "102", + "code": "010200001722", + "description": null, + "label": "CEJ-Allocation Mensuelle Pôle Emploi" + }, + { + "code_programme": "102", + "code": "010200001723", + "description": null, + "label": "CEJ-Remobilisation jeunes en rupture" + }, + { + "code_programme": "102", + "code": "010200001724", + "description": null, + "label": "CEJ-Offre de service digitale" + }, + { + "code_programme": "102", + "code": "010200001725", + "description": null, + "label": "CEJ-Pôle emploi" + }, + { + "code_programme": "102", + "code": "010200001726", + "description": null, + "label": "CEJ-Allocation Mensuelle Missions locales" + }, + { + "code_programme": "102", + "code": "010200001727", + "description": null, + "label": "CEJ-Accompagnement APEC" + }, + { + "code_programme": "102", + "code": "010200001728", + "description": null, + "label": "Marseille Création d'entreprises Jeunes" + }, + { + "code_programme": "102", + "code": "010200001729", + "description": null, + "label": "Allocation ponctuelle PACEA Missions locales" + }, + { + "code_programme": "102", + "code": "010200001801", + "description": null, + "label": "Mise en situation Emploi Publics fragiles ou spéc." + }, + { + "code_programme": "102", + "code": "010200001901", + "description": null, + "label": "Allocation équivalent retraite (AER)" + }, + { + "code_programme": "102", + "code": "010200001902", + "description": null, + "label": "R2F" + }, + { + "code_programme": "102", + "code": "010200002001", + "description": null, + "label": "Garanties jeunes Accompagnement" + }, + { + "code_programme": "102", + "code": "010200002002", + "description": null, + "label": "Garanties Jeunes Allocations" + }, + { + "code_programme": "102", + "code": "010200002003", + "description": null, + "label": "Allocation PACEA" + }, + { + "code_programme": "102", + "code": "010200002004", + "description": null, + "label": "PIC- Programmes nationaux - Accompagnement" + }, + { + "code_programme": "102", + "code": "010200002005", + "description": null, + "label": "PIC-R2F" + }, + { + "code_programme": "102", + "code": "010200002101", + "description": null, + "label": "Aide exceptionnelle contrats professionnalisation" + }, + { + "code_programme": "102", + "code": "010200002201", + "description": null, + "label": "Expérimentations SPIE" + }, + { + "code_programme": "102", + "code": "010200002301", + "description": null, + "label": "Accompagnements mobilité résidentielle Pôle Emploi" + }, + { + "code_programme": "102", + "code": "010200002302", + "description": null, + "label": "Actions en faveur mobilité des demandeurs d'emploi" + }, + { + "code_programme": "102", + "code": "010200002401", + "description": null, + "label": "ASS-Création Reprise d'Entreprise (ACCRE-ASS)" + } + ], + "pageInfo": { + "totalRows": 13381, + "page": 1, + "pageSize": 100 + } +} diff --git a/spec/models/chorus_configuration_spec.rb b/spec/models/chorus_configuration_spec.rb new file mode 100644 index 000000000..5298b4e42 --- /dev/null +++ b/spec/models/chorus_configuration_spec.rb @@ -0,0 +1,52 @@ +describe ChorusConfiguration do + context 'empty' do + subject { create(:procedure, :empty_chorus) } + it { is_expected.to be_valid } + end + + context 'partially filled chorus_configuration' do + subject { create(:procedure, :partial_chorus) } + it { is_expected.to be_valid } + end + + context 'fully filled chorus_configuration' do + subject { create(:procedure, :filled_chorus) } + it { is_expected.to be_valid } + end + + describe 'ChorusConfiguration' do + it 'works without args' do + expect { ChorusConfiguration.new }.not_to raise_error + end + + it 'works with args' do + expect { ChorusConfiguration.new({}) }.not_to raise_error + end + + it 'works with existing args' do + expect do + cc = ChorusConfiguration.new() + cc.assign_attributes(centre_de_coup: {}, domaine_fonctionnel: {}, referentiel_de_programmation: {}) + end.not_to raise_error + end + end + + describe '#complete?' do + subject { procedure.chorus_configuration.complete? } + + context 'without data' do + let(:procedure) { create(:procedure, :empty_chorus) } + it { is_expected.to be_falsey } + end + + context 'with partial data' do + let(:procedure) { create(:procedure, :partial_chorus) } + it { is_expected.to be_falsey } + end + + context 'with all data' do + let(:procedure) { create(:procedure, :filled_chorus) } + it { is_expected.to be_truthy } + end + end +end