diff --git a/app/assets/stylesheets/forms.scss b/app/assets/stylesheets/forms.scss index 0c44aaead..5984421ba 100644 --- a/app/assets/stylesheets/forms.scss +++ b/app/assets/stylesheets/forms.scss @@ -359,7 +359,8 @@ .cnaf-inputs, .dgfip-inputs, - .pole-emploi-inputs { + .pole-emploi-inputs, + .mesri-inputs { display: flex; flex-wrap: wrap; justify-content: space-between; diff --git a/app/assets/stylesheets/mesri.scss b/app/assets/stylesheets/mesri.scss new file mode 100644 index 000000000..62f3891c1 --- /dev/null +++ b/app/assets/stylesheets/mesri.scss @@ -0,0 +1,30 @@ +@import "constants"; +@import "colors"; + +table.mesri { + margin: 2 * $default-padding 0 $default-padding $default-padding; + width: 100%; + + caption { + font-weight: bold; + margin-left: - $default-padding; + margin-bottom: $default-spacer; + text-align: left; + } + + th, + td { + font-weight: normal; + padding: $default-spacer; + } + + th.text-right { + text-align: right; + } + + &.horizontal { + th { + border-bottom: 1px solid $grey; + } + } +} diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 4fa81c42f..e6e377969 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -348,8 +348,8 @@ module Users def champs_params params.permit(dossier: { champs_attributes: [ - :id, :value, :value_other, :external_id, :primary_value, :secondary_value, :numero_allocataire, :code_postal, :identifiant, :numero_fiscal, :reference_avis, :piece_justificative_file, :departement, :code_departement, value: [], - champs_attributes: [:id, :_destroy, :value, :value_other, :external_id, :primary_value, :secondary_value, :numero_allocataire, :code_postal, :identifiant, :numero_fiscal, :reference_avis, :piece_justificative_file, :departement, :code_departement, value: []] + :id, :value, :value_other, :external_id, :primary_value, :secondary_value, :numero_allocataire, :code_postal, :identifiant, :numero_fiscal, :reference_avis, :ine, :piece_justificative_file, :departement, :code_departement, value: [], + champs_attributes: [:id, :_destroy, :value, :value_other, :external_id, :primary_value, :secondary_value, :numero_allocataire, :code_postal, :identifiant, :numero_fiscal, :reference_avis, :ine, :piece_justificative_file, :departement, :code_departement, value: []] ] }) end diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index 474cdb5ee..a4a9bf469 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -1983,6 +1983,11 @@ enum TypeDeChamp { """ linked_drop_down_list + """ + Données du Ministère de l'Enseignement Supérieur, de la Recherche et de l'Innovation + """ + mesri + """ Choix multiples """ diff --git a/app/lib/api_particulier/api.rb b/app/lib/api_particulier/api.rb index ba2207c22..8813f8948 100644 --- a/app/lib/api_particulier/api.rb +++ b/app/lib/api_particulier/api.rb @@ -4,7 +4,8 @@ class APIParticulier::API INTROSPECT_RESOURCE_NAME = "introspect" COMPOSITION_FAMILIALE_RESOURCE_NAME = "v2/composition-familiale" AVIS_IMPOSITION_RESOURCE_NAME = "v2/avis-imposition" - SITUATION_POLE_EMPLOI = "v2/situations-pole-emploi" + SITUATION_POLE_EMPLOI_RESOURCE_NAME = "v2/situations-pole-emploi" + ETUDIANTS_RESOURCE_NAME = "v2/etudiants" TIMEOUT = 20 @@ -31,7 +32,17 @@ class APIParticulier::API end def situation_pole_emploi(identifiant) - get(SITUATION_POLE_EMPLOI, identifiant: identifiant) + get(SITUATION_POLE_EMPLOI_RESOURCE_NAME, identifiant: identifiant) + end + + def etudiants(ine) + # NOTE: Paramètres d'appel mutuellement exclusifs, + # l'appel par INE est réservé aux acteurs de la sphère de l'enseignement + # - INE, l'Identifiant National Étudiant + # - état civil, constitué du nom, prénom, date de naissance, sexe et lieu de naissance + + # TODO: ajouter le support de l'état civil + get(ETUDIANTS_RESOURCE_NAME, ine: ine) end private diff --git a/app/lib/api_particulier/mesri_adapter.rb b/app/lib/api_particulier/mesri_adapter.rb new file mode 100644 index 000000000..ea27f4168 --- /dev/null +++ b/app/lib/api_particulier/mesri_adapter.rb @@ -0,0 +1,48 @@ +class APIParticulier::MesriAdapter + class InvalidSchemaError < ::StandardError + def initialize(errors) + super(errors.map(&:to_json).join("\n")) + end + end + + def initialize(api_particulier_token, ine, requested_sources) + @api = APIParticulier::API.new(api_particulier_token) + @ine = ine + @requested_sources = requested_sources + end + + def to_params + @api.etudiants(@ine) + .tap { |d| ensure_valid_schema!(d) } + .then { |d| extract_requested_sources(d) } + end + + private + + def ensure_valid_schema!(data) + if !schemer.valid?(data) + errors = schemer.validate(data).to_a + raise InvalidSchemaError.new(errors) + end + end + + def schemer + @schemer ||= JSONSchemer.schema(Rails.root.join('app/schemas/etudiants.json')) + end + + def extract_requested_sources(data) + @requested_sources['mesri']&.map do |(scope, sources)| + case scope + when 'inscriptions' + { scope => data[scope].filter_map { |d| d.slice(*sources) if d.key?('dateDebutInscription') } } + when 'admissions' + { scope => data['inscriptions'].filter_map { |d| d.slice(*sources) if d.key?('dateDebutAdmission') } } + when 'etablissements' + { scope => data['inscriptions'].map { |d| d['etablissement'].slice(*sources) }.uniq } + else + { scope => data.slice(*sources) } + end + end + &.flatten&.reduce(&:deep_merge) || {} + end +end diff --git a/app/lib/api_particulier/services/sources_service.rb b/app/lib/api_particulier/services/sources_service.rb index f17aad76d..72aa605e1 100644 --- a/app/lib/api_particulier/services/sources_service.rb +++ b/app/lib/api_particulier/services/sources_service.rb @@ -8,6 +8,7 @@ module APIParticulier def available_sources @procedure.api_particulier_scopes .filter_map { |provider_and_scope| raw_scopes[provider_and_scope] } + .uniq # remove provider/scope tuples duplicates (e.g. mesri inscriptions) .map { |provider, scope| extract_sources(provider, scope) } .reduce({}) { |acc, el| acc.deep_merge(el) { |_, this_val, other_val| this_val + other_val } } end @@ -80,7 +81,13 @@ module APIParticulier 'pole_emploi_identite' => ['pole_emploi', 'identite'], 'pole_emploi_adresse' => ['pole_emploi', 'adresse'], 'pole_emploi_contact' => ['pole_emploi', 'contact'], - 'pole_emploi_inscription' => ['pole_emploi', 'inscription'] + 'pole_emploi_inscription' => ['pole_emploi', 'inscription'], + 'mesri_identifiant' => ['mesri', 'identifiant'], + 'mesri_identite' => ['mesri', 'identite'], + 'mesri_inscription_etudiant' => ['mesri', 'inscriptions'], + 'mesri_inscription_autre' => ['mesri', 'inscriptions'], + 'mesri_admission' => ['mesri', 'admissions'], + 'mesri_etablissements' => ['mesri', 'etablissements'] } end @@ -123,6 +130,13 @@ module APIParticulier 'adresse' => ['INSEECommune', 'codePostal', 'localite', 'ligneVoie', 'ligneComplementDestinataire', 'ligneComplementAdresse', 'ligneComplementDistribution', 'ligneNom'], 'contact' => ['email', 'telephone', 'telephone2'], 'inscription' => ['dateInscription', 'dateCessationInscription', 'codeCertificationCNAV', 'codeCategorieInscription', 'libelleCategorieInscription'] + }, + 'mesri' => { + 'identifiant' => ['ine'], + 'identite' => ['nom', 'prenom', 'dateNaissance'], + 'inscriptions' => ['statut', 'regime', 'dateDebutInscription', 'dateFinInscription', 'codeCommune'], + 'admissions' => ['statut', 'regime', 'dateDebutAdmission', 'dateFinAdmission', 'codeCommune'], + 'etablissements' => ['uai', 'nom'] } } end diff --git a/app/models/champs/mesri_champ.rb b/app/models/champs/mesri_champ.rb new file mode 100644 index 000000000..b61cb0f60 --- /dev/null +++ b/app/models/champs/mesri_champ.rb @@ -0,0 +1,47 @@ +# == Schema Information +# +# Table name: champs +# +# id :integer not null, primary key +# data :jsonb +# fetch_external_data_exceptions :string is an Array +# private :boolean default(FALSE), not null +# rebased_at :datetime +# row :integer +# type :string +# value :string +# value_json :jsonb +# created_at :datetime +# updated_at :datetime +# dossier_id :integer +# etablissement_id :integer +# external_id :string +# parent_id :bigint +# type_de_champ_id :integer +# +class Champs::MesriChamp < Champs::TextChamp + # see https://github.com/betagouv/api-particulier/blob/master/src/presentation/middlewares/mesri-input-validation.middleware.ts + store_accessor :value_json, :ine + + def blank? + external_id.nil? + end + + def fetch_external_data? + true + end + + def fetch_external_data + return if !valid? + + APIParticulier::MesriAdapter.new( + procedure.api_particulier_token, + ine, + procedure.api_particulier_sources + ).to_params + end + + def external_id + { ine: ine }.to_json if ine.present? + end +end diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 5137b4b2d..d75e4372a 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -734,6 +734,10 @@ class Procedure < ApplicationRecord api_particulier_sources['pole_emploi'].present? end + def mesri_enabled? + api_particulier_sources['mesri'].present? + end + private def validate_for_publication? diff --git a/app/models/type_de_champ.rb b/app/models/type_de_champ.rb index b9b5dd0ca..229c56c46 100644 --- a/app/models/type_de_champ.rb +++ b/app/models/type_de_champ.rb @@ -52,7 +52,8 @@ class TypeDeChamp < ApplicationRecord annuaire_education: 'annuaire_education', cnaf: 'cnaf', dgfip: 'dgfip', - pole_emploi: 'pole_emploi' + pole_emploi: 'pole_emploi', + mesri: 'mesri' } belongs_to :revision, class_name: 'ProcedureRevision', optional: true @@ -330,6 +331,8 @@ class TypeDeChamp < ApplicationRecord procedure.dgfip_enabled? when TypeDeChamp.type_champs.fetch(:pole_emploi) procedure.pole_emploi_enabled? + when TypeDeChamp.type_champs.fetch(:mesri) + procedure.mesri_enabled? else true end diff --git a/app/models/types_de_champ/mesri_type_de_champ.rb b/app/models/types_de_champ/mesri_type_de_champ.rb new file mode 100644 index 000000000..1e1455fe1 --- /dev/null +++ b/app/models/types_de_champ/mesri_type_de_champ.rb @@ -0,0 +1,2 @@ +class TypesDeChamp::MesriTypeDeChamp < TypesDeChamp::TextTypeDeChamp +end diff --git a/app/schemas/etudiants.json b/app/schemas/etudiants.json new file mode 100644 index 000000000..2b302ed20 --- /dev/null +++ b/app/schemas/etudiants.json @@ -0,0 +1,57 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://demarches-simplifiees.fr/etudiants.schema.json", + "title": "statut étudiant", + "type": "object", + "properties": { + "ine": { + "type": "string" + }, + "nom": { + "type": "string" + }, + "prenom": { + "type": "string" + }, + "dateNaissance": { + "format": "date", + "type": "string" + }, + "inscriptions": { + "type": "array", + "items" : { + "type": "object", + "properties": { + "dateDebutInscription": { + "format": "date", + "type": "string" + }, + "dateFinInscription": { + "format": "date", + "type": "string" + }, + "statut": { + "enum": ["admis", "inscrit"] + }, + "regime": { + "enum": ["formation initiale", "formation continue"] + }, + "codeCommune": { + "type": "string" + }, + "etablissement": { + "type": "object", + "properties": { + "uai": { + "type": "string" + }, + "nom": { + "type": "string" + } + } + } + } + } + } + } +} diff --git a/app/schemas/situation-pole-emploi.json b/app/schemas/situation-pole-emploi.json index 01d6619ed..08fdbc69b 100644 --- a/app/schemas/situation-pole-emploi.json +++ b/app/schemas/situation-pole-emploi.json @@ -38,34 +38,32 @@ "email": { "type": "string" }, - "$defs": { - "adresse": { - "type": "object", - "properties": { - "codePostal": { - "type": "string" - }, - "INSEECommune": { - "type": "string" - }, - "localite": { - "type": "string" - }, - "ligneVoie": { - "type": "string" - }, - "ligneComplementDestinataire": { - "type": "string" - }, - "ligneComplementAdresse": { - "type": "string" - }, - "ligneComplementDistribution": { - "type": "string" - }, - "ligneNom": { - "type": "string" - } + "adresse": { + "type": "object", + "properties": { + "codePostal": { + "type": "string" + }, + "INSEECommune": { + "type": "string" + }, + "localite": { + "type": "string" + }, + "ligneVoie": { + "type": "string" + }, + "ligneComplementDestinataire": { + "type": "string" + }, + "ligneComplementAdresse": { + "type": "string" + }, + "ligneComplementDistribution": { + "type": "string" + }, + "ligneNom": { + "type": "string" } } }, diff --git a/app/views/shared/champs/mesri/_admissions.html.haml b/app/views/shared/champs/mesri/_admissions.html.haml new file mode 100644 index 000000000..78889945a --- /dev/null +++ b/app/views/shared/champs/mesri/_admissions.html.haml @@ -0,0 +1,11 @@ +%table.mesri + %caption #{t("api_particulier.providers.mesri.scopes.#{scope}.libelle")} : + - admissions.each do |admission| + - admission.slice('statut', 'regime', 'dateDebutAdmission', 'dateFinAdmission', 'codeCommune').keys.each do |key| + %tr + %th= t("api_particulier.providers.mesri.scopes.#{scope}.#{key}") + - case key + - when 'dateDebutAdmission', 'dateFinAdmission' + %td= try_format_date(Date.strptime(admission[key], "%Y-%m-%d")) + - else + %td= admission[key] diff --git a/app/views/shared/champs/mesri/_etablissements.html.haml b/app/views/shared/champs/mesri/_etablissements.html.haml new file mode 100644 index 000000000..b9317446b --- /dev/null +++ b/app/views/shared/champs/mesri/_etablissements.html.haml @@ -0,0 +1,7 @@ +%table.mesri + %caption #{t("api_particulier.providers.mesri.scopes.#{scope}.libelle")} : + - etablissements.each do |etablissement| + - etablissement.slice('uai', 'nom').keys.each do |key| + %tr + %th= t("api_particulier.providers.mesri.scopes.#{scope}.#{key}") + %td= etablissement[key] diff --git a/app/views/shared/champs/mesri/_identifiant.html.haml b/app/views/shared/champs/mesri/_identifiant.html.haml new file mode 100644 index 000000000..3027ada03 --- /dev/null +++ b/app/views/shared/champs/mesri/_identifiant.html.haml @@ -0,0 +1,6 @@ +%table.mesri + %caption #{t("api_particulier.providers.mesri.scopes.#{scope}.libelle")} : + - identifiant.slice('ine').keys.each do |key| + %tr + %th= t("api_particulier.providers.mesri.scopes.#{scope}.#{key}") + %td= identifiant[key] diff --git a/app/views/shared/champs/mesri/_identite.html.haml b/app/views/shared/champs/mesri/_identite.html.haml new file mode 100644 index 000000000..fdba5ab18 --- /dev/null +++ b/app/views/shared/champs/mesri/_identite.html.haml @@ -0,0 +1,10 @@ +%table.mesri + %caption #{t("api_particulier.providers.mesri.scopes.#{scope}.libelle")} : + - identite.slice('nom', 'prenom', 'dateNaissance').keys.each do |key| + %tr + %th= t("api_particulier.providers.mesri.scopes.#{scope}.#{key}") + - case key + - when 'dateNaissance' + %td= try_format_date(Date.strptime(identite[key], "%Y-%m-%d")) + - else + %td= identite[key] diff --git a/app/views/shared/champs/mesri/_inscriptions.html.haml b/app/views/shared/champs/mesri/_inscriptions.html.haml new file mode 100644 index 000000000..2171af239 --- /dev/null +++ b/app/views/shared/champs/mesri/_inscriptions.html.haml @@ -0,0 +1,11 @@ +%table.mesri + %caption #{t("api_particulier.providers.mesri.scopes.#{scope}.libelle")} : + - inscriptions.each do |inscription| + - inscription.slice('statut', 'regime', 'dateDebutInscription', 'dateFinInscription', 'codeCommune').keys.each do |key| + %tr + %th= t("api_particulier.providers.mesri.scopes.#{scope}.#{key}") + - case key + - when 'dateDebutInscription', 'dateFinInscription' + %td= try_format_date(Date.strptime(inscription[key], "%Y-%m-%d")) + - else + %td= inscription[key] diff --git a/app/views/shared/champs/mesri/_show.html.haml b/app/views/shared/champs/mesri/_show.html.haml new file mode 100644 index 000000000..309adfd7d --- /dev/null +++ b/app/views/shared/champs/mesri/_show.html.haml @@ -0,0 +1,25 @@ +- if champ.blank? + %p= t('.not_filled') +- elsif champ.data.blank? + %p= t('.fetching_data', ine: champ.ine) +- else + - if profile == 'usager' + - sources = champ.procedure.api_particulier_sources['mesri'].keys + - i18n_sources = sources.map { |s| I18n.t("#{s}.libelle", scope: 'api_particulier.providers.mesri.scopes') } + %p= t('.data_fetched', sources: i18n_sources.to_sentence, ine: champ.ine) + + - if profile == 'instructeur' + %p= t('.data_fetched_title') + + - champ.data.slice('identifiant', 'identite', 'inscriptions', 'admissions', 'etablissements').keys.each do |scope| + - case scope + - when 'identifiant' + = render partial: 'shared/champs/mesri/identifiant', locals: { scope: scope, identifiant: champ.data[scope] } + - when 'identite' + = render partial: 'shared/champs/mesri/identite', locals: { scope: scope, identite: champ.data[scope] } + - when 'inscriptions' + = render partial: 'shared/champs/mesri/inscriptions', locals: { scope: scope, inscriptions: champ.data[scope] } + - when 'admissions' + = render partial: 'shared/champs/mesri/admissions', locals: { scope: scope, admissions: champ.data[scope] } + - when 'etablissements' + = render partial: 'shared/champs/mesri/etablissements', locals: { scope: scope, etablissements: champ.data[scope] } diff --git a/app/views/shared/dossiers/_champ_row.html.haml b/app/views/shared/dossiers/_champ_row.html.haml index ea171bc8e..b20011dc6 100644 --- a/app/views/shared/dossiers/_champ_row.html.haml +++ b/app/views/shared/dossiers/_champ_row.html.haml @@ -42,6 +42,8 @@ = render partial: "shared/champs/dgfip/show", locals: { champ: c, profile: profile } - when TypeDeChamp.type_champs.fetch(:pole_emploi) = render partial: "shared/champs/pole_emploi/show", locals: { champ: c, profile: profile } + - when TypeDeChamp.type_champs.fetch(:mesri) + = render partial: "shared/champs/mesri/show", locals: { champ: c, profile: profile } - when TypeDeChamp.type_champs.fetch(:address) = render partial: "shared/champs/address/show", locals: { champ: c } - when TypeDeChamp.type_champs.fetch(:communes) diff --git a/app/views/shared/dossiers/editable_champs/_mesri.html.haml b/app/views/shared/dossiers/editable_champs/_mesri.html.haml new file mode 100644 index 000000000..cf90abcb7 --- /dev/null +++ b/app/views/shared/dossiers/editable_champs/_mesri.html.haml @@ -0,0 +1,7 @@ +.mesri-inputs + %div + = form.label :ine, t('.ine_label') + %p.notice= t('.ine_notice') + = form.text_field :ine, + required: champ.mandatory?, + aria: { describedby: describedby_id(champ) } diff --git a/config/locales/api_particulier.en.yml b/config/locales/api_particulier.en.yml index ddaadb426..fe0cd508a 100644 --- a/config/locales/api_particulier.en.yml +++ b/config/locales/api_particulier.en.yml @@ -104,3 +104,32 @@ en: codeCertificationCNAV: CNAV certification code codeCategorieInscription: registration category code libelleCategorieInscription: registration category label + mesri: + libelle: Student status + scopes: + identifiant: + libelle: Identifier + ine: INE + identite: + libelle: Identity + nom: last name + prenom: first name + dateNaissance: date of birth + inscriptions: + libelle: Registrations + statut: status + regime: system + dateDebutInscription: start date of registration + dateFinInscription: end date of registration + codeCommune: postal code + admissions: + libelle: Admissions + statut: status + regime: system + dateDebutAdmission: start date of admission + dateFinAdmission: end date of admission + codeCommune: postal code + etablissements: + libelle: Institutions + uai: UAI + nom: name diff --git a/config/locales/api_particulier.fr.yml b/config/locales/api_particulier.fr.yml index 53828cb6b..27a985faf 100644 --- a/config/locales/api_particulier.fr.yml +++ b/config/locales/api_particulier.fr.yml @@ -104,3 +104,32 @@ fr: codeCertificationCNAV: code de certification CNAV codeCategorieInscription: code de catégorie d’inscription libelleCategorieInscription: libellé de catégorie d’inscription + mesri: + libelle: Statut étudiant + scopes: + identifiant: + libelle: Identifiant + ine: INE + identite: + libelle: Identité + nom: nom + prenom: prénom + dateNaissance: date de naissance + inscriptions: + libelle: Inscriptions + statut: statut + regime: régime + dateDebutInscription: date de début d'inscription + dateFinInscription: date de fin d'inscription + codeCommune: code de la commune + admissions: + libelle: Admissions + statut: statut + regime: régime + dateDebutAdmission: date de début d'admission + dateFinAdmission: date de fin d'admission + codeCommune: code de la commune + etablissements: + libelle: Établissements + uai: UAI + nom: nom diff --git a/config/locales/fr.yml b/config/locales/fr.yml index db3984d4e..73d8381f8 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -421,7 +421,7 @@ fr: jeton_particulier: show: configure_token: "Configurer le jeton API Particulier" - api_particulier_description_html: "%{app_name} utilise API Particulier qui permet d'accéder aux données familiales (CAF), aux données fiscales (DGFiP) et au statut pôle-emploi d'un citoyen.
Renseignez ici le jeton API Particulier propre à votre démarche." + api_particulier_description_html: "%{app_name} utilise API Particulier qui permet d'accéder aux données familiales (CAF), aux données fiscales (DGFiP), au statut pôle-emploi et au statut étudiant d'un citoyen.
Renseignez ici le jeton API Particulier propre à votre démarche." token_description: "Il doit contenir au minimum 15 caractères." update: token_ok: "Le jeton a bien été mis à jour" @@ -435,7 +435,7 @@ fr: show: title: "Définir les sources de données" data_sources: "Sources de données" - explication_html: "

API Particulier facilite l’accès des administrations aux données familiales (CAF), aux données fiscales (DGFiP) et au statut pôle-emploi d'un citoyen pour simplifier les démarches administratives mises en œuvre par les collectivités et les administrations.
Cela permet aux administrations d’accéder à des informations certifiées à la source et ainsi :

Important : les disposition de l'article L144-8 n’autorisent que l’échange des informations strictement nécessaires pour traiter une démarche.

En conséquence, ne sélectionnez ici que les données auxquelles vous aurez accès d’un point de vue légal.

" + explication_html: "

API Particulier facilite l’accès des administrations aux données familiales (CAF), aux données fiscales (DGFiP), au statut pôle-emploi et au statut étudiant d'un citoyen pour simplifier les démarches administratives mises en œuvre par les collectivités et les administrations.
Cela permet aux administrations d’accéder à des informations certifiées à la source et ainsi :

Important : les disposition de l'article L144-8 n’autorisent que l’échange des informations strictement nécessaires pour traiter une démarche.

En conséquence, ne sélectionnez ici que les données auxquelles vous aurez accès d’un point de vue légal.

" update: sources_ok: 'Mise à jour effectuée' procedures: diff --git a/config/locales/models/type_de_champ/fr.yml b/config/locales/models/type_de_champ/fr.yml index f7d100fb0..61e1c112c 100644 --- a/config/locales/models/type_de_champ/fr.yml +++ b/config/locales/models/type_de_champ/fr.yml @@ -39,3 +39,4 @@ fr: cnaf: 'Données de la Caisse nationale des allocations familiales' dgfip: 'Données de la Direction générale des Finances publiques' pole_emploi: 'Situation Pôle emploi' + mesri: "Données du Ministère de l'Enseignement Supérieur, de la Recherche et de l'Innovation" diff --git a/config/locales/shared.en.yml b/config/locales/shared.en.yml index 00f1bd00a..7bcf9042e 100644 --- a/config/locales/shared.en.yml +++ b/config/locales/shared.en.yml @@ -15,6 +15,9 @@ en: pole_emploi: identifiant_label: Identifier identifiant_notice: It is usually composed of alphanumeric characters. + mesri: + ine_label: INE + ine_notice: Student National Number. It is usually composed of alphanumeric characters. header: expires_at: brouillon: "Expires at %{date} (%{duree_conservation_totale} months after the creation of this file)" @@ -42,3 +45,9 @@ en: fetching_data: "Fetching data for identifier %{identifiant}." data_fetched: "Data concerning %{sources} linked to the identifier %{identifiant} has been received from Pôle emploi." data_fetched_title: "Data received from Pôle emploi" + mesri: + show: + not_filled: not filled + fetching_data: "Fetching data for INE %{ine}." + data_fetched: "Data concerning %{sources} linked to the INE %{ine} has been received from the MESRI." + data_fetched_title: "Data received from the MESRI" diff --git a/config/locales/shared.fr.yml b/config/locales/shared.fr.yml index 2dc4b62ac..9d4a2a48d 100644 --- a/config/locales/shared.fr.yml +++ b/config/locales/shared.fr.yml @@ -15,6 +15,9 @@ fr: pole_emploi: identifiant_label: Identifiant identifiant_notice: Il est généralement composé de caractères alphanumériques. + mesri: + ine_label: INE + ine_notice: Identifiant National Étudiant. Il est généralement composé de caractères alphanumériques. header: expires_at: brouillon: "Expirera le %{date} (%{duree_conservation_totale} mois après la création du dossier)" @@ -44,3 +47,9 @@ fr: fetching_data: "La récupération automatique des données pour l'identifiant %{identifiant} est en cours." data_fetched: "Des données concernant %{sources} liées à l'identifiant %{identifiant} ont été reçues depuis Pôle emploi." data_fetched_title: "Données obtenues de Pôle emploi" + mesri: + show: + not_filled: non renseigné + fetching_data: "La récupération automatique des données pour l'INE %{ine} et en cours." + data_fetched: "Des données concernant %{sources} liées à l'INE %{ine} ont été reçues depuis le MESRI." + data_fetched_title: "Données obtenues du MESRI" diff --git a/spec/controllers/administrateurs/jeton_particulier_controller_spec.rb b/spec/controllers/administrateurs/jeton_particulier_controller_spec.rb index 3d26e31ff..ee78923e3 100644 --- a/spec/controllers/administrateurs/jeton_particulier_controller_spec.rb +++ b/spec/controllers/administrateurs/jeton_particulier_controller_spec.rb @@ -79,7 +79,13 @@ describe Administrateurs::JetonParticulierController, type: :controller do 'pole_emploi_identite', 'pole_emploi_adresse', 'pole_emploi_contact', - 'pole_emploi_inscription' + 'pole_emploi_inscription', + 'mesri_identifiant', + 'mesri_identite', + 'mesri_inscription_etudiant', + 'mesri_inscription_autre', + 'mesri_admission', + 'mesri_etablissements' ) expect(procedure.api_particulier_sources).to be_empty end diff --git a/spec/factories/champ.rb b/spec/factories/champ.rb index af2cc885d..384f90a72 100644 --- a/spec/factories/champ.rb +++ b/spec/factories/champ.rb @@ -197,6 +197,10 @@ FactoryBot.define do type_de_champ { association :type_de_champ_pole_emploi, procedure: dossier.procedure } end + factory :champ_mesri, class: 'Champs::MesriChamp' do + type_de_champ { association :type_de_champ_mesri, procedure: dossier.procedure } + end + factory :champ_siret, class: 'Champs::SiretChamp' do type_de_champ { association :type_de_champ_siret, procedure: dossier.procedure } association :etablissement, factory: [:etablissement] diff --git a/spec/factories/procedure.rb b/spec/factories/procedure.rb index ed082c909..75919801c 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -216,6 +216,12 @@ FactoryBot.define do end end + trait :with_mesri do + after(:build) do |procedure, _evaluator| + build(:type_de_champ_mesri, procedure: procedure) + end + end + trait :with_explication do after(:build) do |procedure, _evaluator| build(:type_de_champ_explication, procedure: procedure) diff --git a/spec/factories/type_de_champ.rb b/spec/factories/type_de_champ.rb index fcfff36a1..49ca747eb 100644 --- a/spec/factories/type_de_champ.rb +++ b/spec/factories/type_de_champ.rb @@ -163,6 +163,9 @@ FactoryBot.define do factory :type_de_champ_pole_emploi do type_champ { TypeDeChamp.type_champs.fetch(:pole_emploi) } end + factory :type_de_champ_mesri do + type_champ { TypeDeChamp.type_champs.fetch(:mesri) } + end factory :type_de_champ_carte do type_champ { TypeDeChamp.type_champs.fetch(:carte) } end diff --git a/spec/fixtures/cassettes/api_particulier/success/etudiants.yml b/spec/fixtures/cassettes/api_particulier/success/etudiants.yml new file mode 100644 index 000000000..2b80f807b --- /dev/null +++ b/spec/fixtures/cassettes/api_particulier/success/etudiants.yml @@ -0,0 +1,83 @@ +--- +http_interactions: +- request: + method: get + uri: https://particulier.api.gouv.fr/api/v2/etudiants?ine=090601811AB + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - demarches-simplifiees.fr + Accept: + - application/json + X-Api-Key: + - c6d23f3900b8fb4b3586c4804c051af79062f54b + Expect: + - '' + response: + status: + code: 200 + message: OK + headers: + Date: + - Tue, 16 Mar 2021 17:01:19 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '805' + Connection: + - keep-alive + Keep-Alive: + - timeout=5 + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Expose-Headers: + - Range,Content-Range,X-Content-Range,X-API-Key + Etag: + - W/"2eb-A0NiRd+gbJKIAT0y4tR4j9tjXb0" + Server: + - nginx + Strict-Transport-Security: + - max-age=15552000 + - max-age=15552000 + Vary: + - Origin, Accept + X-Gravitee-Request-Id: + - 7bfb7f99-ac2d-4443-bb7f-99ac2d0443c5 + X-Gravitee-Transaction-Id: + - f5dca8b3-2ab7-4c9a-9ca8-b32ab70c9a2b + body: + encoding: ASCII-8BIT + string: '{ + "ine": "090601811AB", + "nom": "DUBOIS", + "prenom": "Angela Claire Louise", + "dateNaissance": "1962-08-24", + "inscriptions": [ + { + "statut": "admis", + "regime": "formation continue", + "dateDebutAdmission": "2021-09-01T00:00:00.000Z", + "dateFinAdmission": "2022-08-31T00:00:00.000Z", + "etablissement": { + "uai": "0751722P", + "nom": "Université Pierre et Marie Curie - UPCM (Paris 6)" + }, + "codeCommune": "75106" + }, + { + "statut": "inscrit", + "regime": "formation continue", + "dateDebutInscription": "2022-09-01", + "dateFinInscription": "2023-08-31", + "etablissement": { + "uai": "0751722P", + "nom": "Université Pierre et Marie Curie - UPCM (Paris 6)" + }, + "codeCommune": "75106" + } + ] + }' + recorded_at: Tue, 16 Mar 2021 17:01:18 GMT +recorded_with: VCR 6.0.0 diff --git a/spec/fixtures/cassettes/api_particulier/success/etudiants_invalid.yml b/spec/fixtures/cassettes/api_particulier/success/etudiants_invalid.yml new file mode 100644 index 000000000..8b1c31f69 --- /dev/null +++ b/spec/fixtures/cassettes/api_particulier/success/etudiants_invalid.yml @@ -0,0 +1,83 @@ +--- +http_interactions: +- request: + method: get + uri: https://particulier.api.gouv.fr/api/v2/etudiants?ine=090601811AB + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - demarches-simplifiees.fr + Accept: + - application/json + X-Api-Key: + - c6d23f3900b8fb4b3586c4804c051af79062f54b + Expect: + - '' + response: + status: + code: 200 + message: OK + headers: + Date: + - Tue, 16 Mar 2021 17:01:19 GMT + Content-Type: + - application/json; charset=utf-8 + Content-Length: + - '806' + Connection: + - keep-alive + Keep-Alive: + - timeout=5 + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Expose-Headers: + - Range,Content-Range,X-Content-Range,X-API-Key + Etag: + - W/"2eb-A0NiRd+gbJKIAT0y4tR4j9tjXb0" + Server: + - nginx + Strict-Transport-Security: + - max-age=15552000 + - max-age=15552000 + Vary: + - Origin, Accept + X-Gravitee-Request-Id: + - 7bfb7f99-ac2d-4443-bb7f-99ac2d0443c5 + X-Gravitee-Transaction-Id: + - f5dca8b3-2ab7-4c9a-9ca8-b32ab70c9a2b + body: + encoding: ASCII-8BIT + string: '{ + "ine": "090601811AB", + "nom": "DUBOIS", + "prenom": "Angela Claire Louise", + "dateNaissance": "1962-08-24", + "inscriptions": [ + { + "statut": "absent", + "regime": "formation continue", + "dateDebutAdmission": "2021-09-01T00:00:00.000Z", + "dateFinAdmission": "2022-08-31T00:00:00.000Z", + "etablissement": { + "uai": "0751722P", + "nom": "Université Pierre et Marie Curie - UPCM (Paris 6)" + }, + "codeCommune": "75106" + }, + { + "statut": "inscrit", + "regime": "formation continue", + "dateDebutInscription": "2022-09-01", + "dateFinInscription": "2023-08-31", + "etablissement": { + "uai": "0751722P", + "nom": "Université Pierre et Marie Curie - UPCM (Paris 6)" + }, + "codeCommune": "75106" + } + ] + }' + recorded_at: Tue, 16 Mar 2021 17:01:18 GMT +recorded_with: VCR 6.0.0 diff --git a/spec/fixtures/cassettes/api_particulier/success/introspect.yml b/spec/fixtures/cassettes/api_particulier/success/introspect.yml index 3c59e6623..1d4e289b5 100644 --- a/spec/fixtures/cassettes/api_particulier/success/introspect.yml +++ b/spec/fixtures/cassettes/api_particulier/success/introspect.yml @@ -25,7 +25,7 @@ http_interactions: Content-Type: - application/json Content-Length: - - '257' + - '1068' Connection: - keep-alive Keep-Alive: @@ -38,6 +38,6 @@ http_interactions: - max-age=15552000 body: encoding: UTF-8 - string: '{"_id":"1d99db5a-a099-4314-ad2f-2707c6b505a6","name":"Application de sandbox","scopes":["cnaf_allocataires","cnaf_enfants","cnaf_adresse","cnaf_quotient_familial","dgfip_declarant1_nom","dgfip_declarant1_nom_naissance","dgfip_declarant1_prenoms","dgfip_declarant1_date_naissance","dgfip_declarant2_nom","dgfip_declarant2_nom_naissance","dgfip_declarant2_prenoms","dgfip_declarant2_date_naissance","dgfip_date_recouvrement","dgfip_date_etablissement","dgfip_adresse_fiscale_taxation","dgfip_adresse_fiscale_annee","dgfip_nombre_parts","dgfip_nombre_personnes_a_charge","dgfip_situation_familiale","dgfip_revenu_brut_global","dgfip_revenu_imposable","dgfip_impot_revenu_net_avant_corrections","dgfip_montant_impot","dgfip_revenu_fiscal_reference","dgfip_annee_impot","dgfip_annee_revenus","dgfip_erreur_correctif","dgfip_situation_partielle", "pole_emploi_identite","pole_emploi_adresse","pole_emploi_contact","pole_emploi_inscription"]}' + string: '{"_id":"1d99db5a-a099-4314-ad2f-2707c6b505a6","name":"Application de sandbox","scopes":["cnaf_allocataires","cnaf_enfants","cnaf_adresse","cnaf_quotient_familial","dgfip_declarant1_nom","dgfip_declarant1_nom_naissance","dgfip_declarant1_prenoms","dgfip_declarant1_date_naissance","dgfip_declarant2_nom","dgfip_declarant2_nom_naissance","dgfip_declarant2_prenoms","dgfip_declarant2_date_naissance","dgfip_date_recouvrement","dgfip_date_etablissement","dgfip_adresse_fiscale_taxation","dgfip_adresse_fiscale_annee","dgfip_nombre_parts","dgfip_nombre_personnes_a_charge","dgfip_situation_familiale","dgfip_revenu_brut_global","dgfip_revenu_imposable","dgfip_impot_revenu_net_avant_corrections","dgfip_montant_impot","dgfip_revenu_fiscal_reference","dgfip_annee_impot","dgfip_annee_revenus","dgfip_erreur_correctif","dgfip_situation_partielle", "pole_emploi_identite","pole_emploi_adresse","pole_emploi_contact","pole_emploi_inscription","mesri_identifiant","mesri_identite","mesri_inscription_etudiant","mesri_inscription_autre","mesri_admission","mesri_etablissements"]}' recorded_at: Tue, 16 Mar 2021 15:25:24 GMT recorded_with: VCR 6.0.0 diff --git a/spec/fixtures/files/api_particulier/etudiants.json b/spec/fixtures/files/api_particulier/etudiants.json new file mode 100644 index 000000000..25ee0bd67 --- /dev/null +++ b/spec/fixtures/files/api_particulier/etudiants.json @@ -0,0 +1,34 @@ +{ + "identifiant": { + "ine": "090601811AB" + }, + "identite": { + "nom": "DUBOIS", + "prenom": "Angela Claire Louise", + "dateNaissance": "1962-08-24" + }, + "admissions": [ + { + "statut": "admis", + "regime": "formation continue", + "dateDebutAdmission": "2021-09-01T00:00:00.000Z", + "dateFinAdmission": "2022-08-31T00:00:00.000Z", + "codeCommune": "75106" + } + ], + "inscriptions": [ + { + "statut": "inscrit", + "regime": "formation continue", + "dateDebutInscription": "2022-09-01", + "dateFinInscription": "2023-08-31", + "codeCommune": "75106" + } + ], + "etablissements": [ + { + "uai": "0751722P", + "nom": "Université Pierre et Marie Curie - UPCM (Paris 6)" + } + ] +} diff --git a/spec/lib/api_particulier/api_spec.rb b/spec/lib/api_particulier/api_spec.rb index 10e5cbfc8..55931a84c 100644 --- a/spec/lib/api_particulier/api_spec.rb +++ b/spec/lib/api_particulier/api_spec.rb @@ -41,7 +41,13 @@ describe APIParticulier::API do 'pole_emploi_identite', 'pole_emploi_adresse', 'pole_emploi_contact', - 'pole_emploi_inscription' + 'pole_emploi_inscription', + 'mesri_identifiant', + 'mesri_identite', + 'mesri_inscription_etudiant', + 'mesri_inscription_autre', + 'mesri_admission', + 'mesri_etablissements' ) end end diff --git a/spec/lib/api_particulier/mesri_adapter_spec.rb b/spec/lib/api_particulier/mesri_adapter_spec.rb new file mode 100644 index 000000000..5bb94d57c --- /dev/null +++ b/spec/lib/api_particulier/mesri_adapter_spec.rb @@ -0,0 +1,70 @@ +describe APIParticulier::MesriAdapter do + let(:adapter) { described_class.new(api_particulier_token, ine, requested_sources) } + + before { stub_const('API_PARTICULIER_URL', 'https://particulier.api.gouv.fr/api') } + + describe '#to_params' do + let(:api_particulier_token) { 'c6d23f3900b8fb4b3586c4804c051af79062f54b' } + let(:ine) { '090601811AB' } + + subject { VCR.use_cassette(cassette) { adapter.to_params } } + + context 'when the api answer is valid' do + let(:cassette) { 'api_particulier/success/etudiants' } + + context 'when the token has all the MESRI scopes' do + context 'and all the sources are requested' do + let(:requested_sources) do + { + 'mesri' => { + 'identifiant' => ['ine'], + 'identite' => ['nom', 'prenom', 'dateNaissance'], + 'inscriptions' => ['statut', 'regime', 'dateDebutInscription', 'dateFinInscription', 'codeCommune'], + 'admissions' => ['statut', 'regime', 'dateDebutAdmission', 'dateFinAdmission', 'codeCommune'], + 'etablissements' => ['uai', 'nom'] + } + } + end + + let(:result) { JSON.parse(File.read('spec/fixtures/files/api_particulier/etudiants.json')) } + + it { is_expected.to eq(result) } + end + + context 'when no sources is requested' do + let(:requested_sources) { {} } + + it { is_expected.to eq({}) } + end + + context 'when an admission statut is requested' do + let(:requested_sources) { { 'mesri' => { 'admissions' => ['statut'] } } } + + it { is_expected.to eq('admissions' => [{ 'statut' => 'admis' }]) } + end + + context 'when an inscription statut is requested' do + let(:requested_sources) { { 'mesri' => { 'inscriptions' => ['statut'] } } } + + it { is_expected.to eq('inscriptions' => [{ 'statut' => 'inscrit' }]) } + end + + context 'when a first name is requested' do + let(:requested_sources) { { 'mesri' => { 'identite' => ['prenom'] } } } + + it { is_expected.to eq('identite' => { 'prenom' => 'Angela Claire Louise' }) } + end + end + end + + context 'when the api answer is invalid' do + let(:cassette) { 'api_particulier/success/etudiants_invalid' } + + context 'when no sources is requested' do + let(:requested_sources) { {} } + + it { expect { subject }.to raise_error(APIParticulier::MesriAdapter::InvalidSchemaError) } + end + end + end +end diff --git a/spec/lib/api_particulier/services/sources_service_spec.rb b/spec/lib/api_particulier/services/sources_service_spec.rb index 36ffeb6d6..adc7c0966 100644 --- a/spec/lib/api_particulier/services/sources_service_spec.rb +++ b/spec/lib/api_particulier/services/sources_service_spec.rb @@ -62,6 +62,21 @@ describe APIParticulier::Services::SourcesService do it { is_expected.to match(pole_emploi_identite_et_adresse) } end + context 'when a procedure has a mesri_identite and a mesri_etablissements scopes' do + let(:api_particulier_scopes) { ['mesri_identite', 'mesri_etablissements'] } + + let(:mesri_identite_and_etablissements) do + { + 'mesri' => { + 'identite' => ['nom', 'prenom', 'dateNaissance'], + 'etablissements' => ['uai', 'nom'] + } + } + end + + it { is_expected.to match(mesri_identite_and_etablissements) } + end + context 'when a procedure has an unknown scope' do let(:api_particulier_scopes) { ['unknown_scope'] } diff --git a/spec/models/champs/mesri_champ_spec.rb b/spec/models/champs/mesri_champ_spec.rb new file mode 100644 index 000000000..9e8b1ccca --- /dev/null +++ b/spec/models/champs/mesri_champ_spec.rb @@ -0,0 +1,56 @@ +describe Champs::MesriChamp, type: :model do + let(:champ) { described_class.new } + + describe 'INE' do + before do + champ.ine = '090601811AB' + end + + it 'saves INE' do + expect(champ.ine).to eq('090601811AB') + end + end + + describe 'external_id' do + context 'when no data is given' do + before do + champ.ine = '' + champ.save + end + + it { expect(champ.external_id).to be_nil } + end + + context 'when all data required for an external fetch are given' do + before do + champ.ine = '090601811AB' + champ.save + end + + it { expect(JSON.parse(champ.external_id)).to eq("ine" => "090601811AB") } + end + end + + describe '#validate' do + let(:champ) { described_class.new(dossier: create(:dossier), type_de_champ: create(:type_de_champ_mesri)) } + let(:validation_context) { :create } + + subject { champ.valid?(validation_context) } + + before do + champ.ine = ine + end + + context 'when INE is valid' do + let(:ine) { '090601811AB' } + + it { is_expected.to be true } + end + + context 'when INE is nil' do + let(:ine) { nil } + + it { is_expected.to be true } + end + end +end diff --git a/spec/services/procedure_export_service_spec.rb b/spec/services/procedure_export_service_spec.rb index 22fc8fe8d..c91dace09 100644 --- a/spec/services/procedure_export_service_spec.rb +++ b/spec/services/procedure_export_service_spec.rb @@ -80,6 +80,7 @@ describe ProcedureExportService do "cnaf", "dgfip", "pole_emploi", + "mesri", "text" ] end @@ -170,6 +171,7 @@ describe ProcedureExportService do "cnaf", "dgfip", "pole_emploi", + "mesri", "text" ] end @@ -256,6 +258,7 @@ describe ProcedureExportService do "cnaf", "dgfip", "pole_emploi", + "mesri", "text" ] end diff --git a/spec/system/api_particulier/api_particulier_spec.rb b/spec/system/api_particulier/api_particulier_spec.rb index 784f38157..1a6ac2b9c 100644 --- a/spec/system/api_particulier/api_particulier_spec.rb +++ b/spec/system/api_particulier/api_particulier_spec.rb @@ -26,6 +26,13 @@ describe 'fetch API Particulier Data', js: true do 'adresse' => ['INSEECommune', 'codePostal', 'localite', 'ligneVoie', 'ligneComplementDestinataire', 'ligneComplementAdresse', 'ligneComplementDistribution', 'ligneNom'], 'contact' => ['email', 'telephone', 'telephone2'], 'inscription' => ['dateInscription', 'dateCessationInscription', 'codeCertificationCNAV', 'codeCategorieInscription', 'libelleCategorieInscription'] + }, + 'mesri' => { + 'identifiant' => ['ine'], + 'identite' => ['nom', 'prenom', 'dateNaissance'], + 'inscriptions' => ['statut', 'regime', 'dateDebutInscription', 'dateFinInscription', 'codeCommune'], + 'admissions' => ['statut', 'regime', 'dateDebutAdmission', 'dateFinAdmission', 'codeCommune'], + 'etablissements' => ['uai', 'nom'] } } end @@ -158,6 +165,37 @@ describe 'fetch API Particulier Data', js: true do check("libellé de catégorie d’inscription") end + within('#mesri-identifiant') do + check('INE') + end + + within('#mesri-identite') do + check('nom') + check('prénom') + check('date de naissance') + end + + within('#mesri-inscriptions') do + check('statut') + check('régime') + check("date de début d'inscription") + check("date de fin d'inscription") + check("code de la commune") + end + + within('#mesri-admissions') do + check('statut') + check('régime') + check("date de début d'admission") + check("date de fin d'admission") + check("code de la commune") + end + + within('#mesri-etablissements') do + check('UAI') + check('nom') + end + click_on 'Enregistrer' within('#cnaf-enfants') do @@ -166,10 +204,11 @@ describe 'fetch API Particulier Data', js: true do procedure.reload - expect(procedure.api_particulier_sources.keys).to contain_exactly('cnaf', 'dgfip', 'pole_emploi') + expect(procedure.api_particulier_sources.keys).to contain_exactly('cnaf', 'dgfip', 'pole_emploi', 'mesri') expect(procedure.api_particulier_sources['cnaf'].keys).to contain_exactly('adresse', 'allocataires', 'enfants', 'quotient_familial') expect(procedure.api_particulier_sources['dgfip'].keys).to contain_exactly('declarant1', 'declarant2', 'echeance_avis', 'foyer_fiscal', 'agregats_fiscaux', 'complements') expect(procedure.api_particulier_sources['pole_emploi'].keys).to contain_exactly('identite', 'adresse', 'contact', 'inscription') + expect(procedure.api_particulier_sources['mesri'].keys).to contain_exactly('identifiant', 'identite', 'inscriptions', 'admissions', 'etablissements') procedure.api_particulier_sources.each do |provider, scopes| scopes.each do |scope, fields| @@ -203,10 +242,11 @@ describe 'fetch API Particulier Data', js: true do let(:reference_avis) { '2097699999077' } let(:instructeur) { create(:instructeur) } let(:identifiant) { 'georges_moustaki_77' } + let(:ine) { '090601811AB' } let(:api_particulier_token) { '29eb50b65f64e8e00c0847a8bbcbd150e1f847' } let(:procedure) do - create(:procedure, :for_individual, :with_service, :with_cnaf, :with_dgfip, :with_pole_emploi, :published, + create(:procedure, :for_individual, :with_service, :with_cnaf, :with_dgfip, :with_pole_emploi, :with_mesri, :published, libelle: 'libellé de la procédure', path: 'libelle-de-la-procedure', instructeurs: [instructeur], @@ -336,6 +376,67 @@ describe 'fetch API Particulier Data', js: true do end end + context 'MESRI' do + let(:api_particulier_token) { 'c6d23f3900b8fb4b3586c4804c051af79062f54b' } + + scenario 'it can fill a MESRI field' do + visit commencer_path(path: procedure.path) + click_on 'Commencer la démarche' + + choose 'Madame' + fill_in 'individual_nom', with: 'Dubois' + fill_in 'individual_prenom', with: 'Angela Claire Louise' + + click_button('Continuer') + + fill_in "INE", with: 'wrong code' + + blur + expect(page).to have_css('span', text: 'Brouillon enregistré', visible: true) + + dossier = Dossier.last + mesri_champ = dossier.champs.fourth + + expect(mesri_champ.ine).to eq('wrong code') + + fill_in "INE", with: ine + + VCR.use_cassette('api_particulier/success/etudiants') do + perform_enqueued_jobs { click_on 'Déposer le dossier' } + end + + visit demande_dossier_path(dossier) + expect(page).to have_content(/Des données.*ont été reçues depuis le MESRI/) + + log_out + + login_as instructeur.user, scope: :user + + visit instructeur_dossier_path(procedure, dossier) + + expect(page).to have_content('INE 090601811AB') + + expect(page).to have_content('nom DUBOIS') + expect(page).to have_content('prénom Angela Claire Louise') + expect(page).to have_content('date de naissance 24 août 1962') + + expect(page).to have_content('statut inscrit') + expect(page).to have_content('régime formation continue') + expect(page).to have_content("date de début d'inscription 1 septembre 2022") + expect(page).to have_content("date de fin d'inscription 31 août 2023") + expect(page).to have_content('code de la commune 75106') + + expect(page).to have_content('statut admis') + expect(page).to have_content('régime formation continue') + expect(page).to have_content("date de début d'admission 1 septembre 2021") + expect(page).to have_content("date de fin d'admission 31 août 2022") + expect(page).to have_content('code de la commune 75106') + + expect(page).to have_content('UAI 0751722P') + expect(page).to have_content('nom Université Pierre et Marie Curie - UPCM (Paris 6)') + end + end + scenario 'it can fill a DGFiP field' do visit commencer_path(path: procedure.path) click_on 'Commencer la démarche'