From 6ab8ac0662318ccd89cf115cb708c127cf6d62c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Vantomme?= Date: Wed, 1 Dec 2021 10:21:30 +0100 Subject: [PATCH] feat (api particulier): add Pole emploi field --- app/assets/stylesheets/forms.scss | 3 +- app/assets/stylesheets/pole_emploi.scss | 30 +++ app/controllers/users/dossiers_controller.rb | 4 +- app/graphql/schema.graphql | 5 + app/models/champs/pole_emploi_champ.rb | 47 ++++ app/models/procedure.rb | 4 + app/models/type_de_champ.rb | 5 +- .../pole_emploi_type_de_champ.rb | 2 + .../sources_particulier/show.html.haml | 2 +- .../champs/pole_emploi/_adresse.html.haml | 6 + .../champs/pole_emploi/_contact.html.haml | 6 + .../champs/pole_emploi/_identite.html.haml | 12 + .../champs/pole_emploi/_inscription.html.haml | 10 + .../shared/champs/pole_emploi/_show.html.haml | 24 ++ .../shared/dossiers/_champ_row.html.haml | 2 + .../editable_champs/_pole_emploi.html.haml | 7 + config/locales/models/type_de_champ/fr.yml | 1 + config/locales/shared.en.yml | 9 + config/locales/shared.fr.yml | 9 + .../jeton_particulier_controller_spec.rb | 6 +- spec/factories/champ.rb | 4 + spec/factories/procedure.rb | 6 + spec/factories/type_de_champ.rb | 3 + .../api_particulier/success/introspect.yml | 4 +- spec/lib/api_particulier/api_spec.rb | 6 +- spec/models/champs/pole_emploi_champ_spec.rb | 56 +++++ .../services/procedure_export_service_spec.rb | 3 + .../api_particulier/api_particulier_spec.rb | 211 ++++++++++++++---- 28 files changed, 429 insertions(+), 58 deletions(-) create mode 100644 app/assets/stylesheets/pole_emploi.scss create mode 100644 app/models/champs/pole_emploi_champ.rb create mode 100644 app/models/types_de_champ/pole_emploi_type_de_champ.rb create mode 100644 app/views/shared/champs/pole_emploi/_adresse.html.haml create mode 100644 app/views/shared/champs/pole_emploi/_contact.html.haml create mode 100644 app/views/shared/champs/pole_emploi/_identite.html.haml create mode 100644 app/views/shared/champs/pole_emploi/_inscription.html.haml create mode 100644 app/views/shared/champs/pole_emploi/_show.html.haml create mode 100644 app/views/shared/dossiers/editable_champs/_pole_emploi.html.haml create mode 100644 spec/models/champs/pole_emploi_champ_spec.rb diff --git a/app/assets/stylesheets/forms.scss b/app/assets/stylesheets/forms.scss index 539a3926b..0c44aaead 100644 --- a/app/assets/stylesheets/forms.scss +++ b/app/assets/stylesheets/forms.scss @@ -358,7 +358,8 @@ } .cnaf-inputs, - .dgfip-inputs { + .dgfip-inputs, + .pole-emploi-inputs { display: flex; flex-wrap: wrap; justify-content: space-between; diff --git a/app/assets/stylesheets/pole_emploi.scss b/app/assets/stylesheets/pole_emploi.scss new file mode 100644 index 000000000..c51ee0103 --- /dev/null +++ b/app/assets/stylesheets/pole_emploi.scss @@ -0,0 +1,30 @@ +@import "constants"; +@import "colors"; + +table.pole-emploi { + 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 ac7179b96..87673cfa2 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, :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, :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, :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: []] ] }) end diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index 1013cd066..474cdb5ee 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -2008,6 +2008,11 @@ enum TypeDeChamp { """ piece_justificative + """ + Situation Pôle emploi + """ + pole_emploi + """ Régions """ diff --git a/app/models/champs/pole_emploi_champ.rb b/app/models/champs/pole_emploi_champ.rb new file mode 100644 index 000000000..4bc4124f0 --- /dev/null +++ b/app/models/champs/pole_emploi_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::PoleEmploiChamp < Champs::TextChamp + # see https://github.com/betagouv/api-particulier/blob/master/src/presentation/middlewares/pole-emploi-input-validation.middleware.ts + store_accessor :value_json, :identifiant + + def blank? + external_id.nil? + end + + def fetch_external_data? + true + end + + def fetch_external_data + return if !valid? + + APIParticulier::PoleEmploiAdapter.new( + procedure.api_particulier_token, + identifiant, + procedure.api_particulier_sources + ).to_params + end + + def external_id + { identifiant: identifiant }.to_json if identifiant.present? + end +end diff --git a/app/models/procedure.rb b/app/models/procedure.rb index a63696320..5137b4b2d 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -730,6 +730,10 @@ class Procedure < ApplicationRecord api_particulier_sources['dgfip'].present? end + def pole_emploi_enabled? + api_particulier_sources['pole_emploi'].present? + end + private def validate_for_publication? diff --git a/app/models/type_de_champ.rb b/app/models/type_de_champ.rb index dceb14139..b9b5dd0ca 100644 --- a/app/models/type_de_champ.rb +++ b/app/models/type_de_champ.rb @@ -51,7 +51,8 @@ class TypeDeChamp < ApplicationRecord iban: 'iban', annuaire_education: 'annuaire_education', cnaf: 'cnaf', - dgfip: 'dgfip' + dgfip: 'dgfip', + pole_emploi: 'pole_emploi' } belongs_to :revision, class_name: 'ProcedureRevision', optional: true @@ -327,6 +328,8 @@ class TypeDeChamp < ApplicationRecord procedure.cnaf_enabled? when TypeDeChamp.type_champs.fetch(:dgfip) procedure.dgfip_enabled? + when TypeDeChamp.type_champs.fetch(:pole_emploi) + procedure.pole_emploi_enabled? else true end diff --git a/app/models/types_de_champ/pole_emploi_type_de_champ.rb b/app/models/types_de_champ/pole_emploi_type_de_champ.rb new file mode 100644 index 000000000..ec71c731e --- /dev/null +++ b/app/models/types_de_champ/pole_emploi_type_de_champ.rb @@ -0,0 +1,2 @@ +class TypesDeChamp::PoleEmploiTypeDeChamp < TypesDeChamp::TextTypeDeChamp +end diff --git a/app/views/administrateurs/sources_particulier/show.html.haml b/app/views/administrateurs/sources_particulier/show.html.haml index ff040e2a0..882d034b0 100644 --- a/app/views/administrateurs/sources_particulier/show.html.haml +++ b/app/views/administrateurs/sources_particulier/show.html.haml @@ -16,7 +16,7 @@ - scopes.each do |scope_key, sources| %h3.explication-libelle= t("api_particulier.providers.#{provider_key}.scopes.#{scope_key}.libelle") - %ul.procedure-admin-api-particulier-sources{ id: scope_key } + %ul.procedure-admin-api-particulier-sources{ id: "#{provider_key}-#{scope_key}" } - sources.each do |source_key, enabled_hash| - enabled = (@procedure.api_particulier_sources.dig(provider_key, scope_key)&.include?(source_key)).present? %li diff --git a/app/views/shared/champs/pole_emploi/_adresse.html.haml b/app/views/shared/champs/pole_emploi/_adresse.html.haml new file mode 100644 index 000000000..fb697b908 --- /dev/null +++ b/app/views/shared/champs/pole_emploi/_adresse.html.haml @@ -0,0 +1,6 @@ +%table.pole-emploi + %caption #{t("api_particulier.providers.pole_emploi.scopes.#{scope}.libelle")} : + - adresse.slice('INSEECommune', 'codePostal', 'localite', 'ligneVoie', 'ligneComplementDestinataire', 'ligneComplementAdresse', 'ligneComplementDistribution', 'ligneNom').keys.each do |key| + %tr + %th= t("api_particulier.providers.pole_emploi.scopes.#{scope}.#{key}") + %td= adresse[key] diff --git a/app/views/shared/champs/pole_emploi/_contact.html.haml b/app/views/shared/champs/pole_emploi/_contact.html.haml new file mode 100644 index 000000000..a0a2bce99 --- /dev/null +++ b/app/views/shared/champs/pole_emploi/_contact.html.haml @@ -0,0 +1,6 @@ +%table.pole-emploi + %caption #{t("api_particulier.providers.pole_emploi.scopes.#{scope}.libelle")} : + - contact.slice('email', 'telephone', 'telephone2').keys.each do |key| + %tr + %th= t("api_particulier.providers.pole_emploi.scopes.#{scope}.#{key}") + %td= contact[key] diff --git a/app/views/shared/champs/pole_emploi/_identite.html.haml b/app/views/shared/champs/pole_emploi/_identite.html.haml new file mode 100644 index 000000000..da3eb1d31 --- /dev/null +++ b/app/views/shared/champs/pole_emploi/_identite.html.haml @@ -0,0 +1,12 @@ +%table.pole-emploi + %caption #{t("api_particulier.providers.pole_emploi.scopes.#{scope}.libelle")} : + - identite.slice('identifiant', 'civilite', 'nom', 'nomUsage', 'prenom', 'sexe', 'dateNaissance').keys.each do |key| + %tr + %th= t("api_particulier.providers.pole_emploi.scopes.#{scope}.#{key}") + - case key + - when 'dateNaissance' + %td= try_format_date(Date.strptime(identite[key], "%Y-%m-%d")) + - when 'sexe' + %td= t("api_particulier.providers.pole_emploi.scopes.#{scope}.#{identite[key]}") + - else + %td= identite[key] diff --git a/app/views/shared/champs/pole_emploi/_inscription.html.haml b/app/views/shared/champs/pole_emploi/_inscription.html.haml new file mode 100644 index 000000000..d51edec9b --- /dev/null +++ b/app/views/shared/champs/pole_emploi/_inscription.html.haml @@ -0,0 +1,10 @@ +%table.pole-emploi + %caption #{t("api_particulier.providers.pole_emploi.scopes.#{scope}.libelle")} : + - inscription.slice('dateInscription', 'dateCessationInscription', 'codeCertificationCNAV', 'codeCategorieInscription', 'libelleCategorieInscription').keys.each do |key| + %tr + %th= t("api_particulier.providers.pole_emploi.scopes.#{scope}.#{key}") + - case key + - when 'dateInscription', 'dateCessationInscription' + %td= try_format_date(Date.strptime(inscription[key], "%Y-%m-%d")) + - else + %td= inscription[key] diff --git a/app/views/shared/champs/pole_emploi/_show.html.haml b/app/views/shared/champs/pole_emploi/_show.html.haml new file mode 100644 index 000000000..a0218db38 --- /dev/null +++ b/app/views/shared/champs/pole_emploi/_show.html.haml @@ -0,0 +1,24 @@ +- if champ.blank? + %p= t('.not_filled') +- elsif champ.data.blank? + %p= t('.fetching_data', + identifiant: champ.identifiant) +- else + - if profile == 'usager' + - sources = champ.procedure.api_particulier_sources['pole_emploi'].keys + - i18n_sources = sources.map { |s| I18n.t("#{s}.libelle", scope: 'api_particulier.providers.pole_emploi.scopes') } + %p= t('.data_fetched', sources: i18n_sources.to_sentence, identifiant: champ.identifiant) + + - if profile == 'instructeur' + %p= t('.data_fetched_title') + + - champ.data.slice('identite', 'adresse', 'contact', 'inscription').keys.each do |scope| + - case scope + - when 'identite' + = render partial: 'shared/champs/pole_emploi/identite', locals: { scope: scope, identite: champ.data[scope] } + - when 'adresse' + = render partial: 'shared/champs/pole_emploi/adresse', locals: { scope: scope, adresse: champ.data[scope] } + - when 'contact' + = render partial: 'shared/champs/pole_emploi/contact', locals: { scope: scope, contact: champ.data[scope] } + - when 'inscription' + = render partial: 'shared/champs/pole_emploi/inscription', locals: { scope: scope, inscription: champ.data[scope] } diff --git a/app/views/shared/dossiers/_champ_row.html.haml b/app/views/shared/dossiers/_champ_row.html.haml index 87d98660e..ea171bc8e 100644 --- a/app/views/shared/dossiers/_champ_row.html.haml +++ b/app/views/shared/dossiers/_champ_row.html.haml @@ -40,6 +40,8 @@ = render partial: "shared/champs/cnaf/show", locals: { champ: c, profile: profile } - when TypeDeChamp.type_champs.fetch(:dgfip) = 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(: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/_pole_emploi.html.haml b/app/views/shared/dossiers/editable_champs/_pole_emploi.html.haml new file mode 100644 index 000000000..c8a6dff46 --- /dev/null +++ b/app/views/shared/dossiers/editable_champs/_pole_emploi.html.haml @@ -0,0 +1,7 @@ +.pole_emploi-inputs + %div + = form.label :identifiant, t('.identifiant_label') + %p.notice= t('.identifiant_notice') + = form.text_field :identifiant, + required: champ.mandatory?, + aria: { describedby: describedby_id(champ) } diff --git a/config/locales/models/type_de_champ/fr.yml b/config/locales/models/type_de_champ/fr.yml index 95a3533e5..f7d100fb0 100644 --- a/config/locales/models/type_de_champ/fr.yml +++ b/config/locales/models/type_de_champ/fr.yml @@ -38,3 +38,4 @@ fr: annuaire_education: 'Annuaire de l’éducation' 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' diff --git a/config/locales/shared.en.yml b/config/locales/shared.en.yml index 15f02a8ae..00f1bd00a 100644 --- a/config/locales/shared.en.yml +++ b/config/locales/shared.en.yml @@ -12,6 +12,9 @@ en: numero_fiscal_notice: It is usually composed of 13 to 14 characters. reference_avis_label: Tax notice reference reference_avis_notice: It is usually composed of 13 to 14 characters. + pole_emploi: + identifiant_label: Identifier + identifiant_notice: It is usually composed of alphanumeric characters. header: expires_at: brouillon: "Expires at %{date} (%{duree_conservation_totale} months after the creation of this file)" @@ -33,3 +36,9 @@ en: fetching_data: "Fetching data for declarant No. %{numero_fiscal} with tax notice reference %{reference_avis}." data_fetched: "Data concerning %{sources} linked to the declarant Nº %{numero_fiscal} with tax notice reference %{reference_avis} has been received from the DGFiP." data_fetched_title: "Data received from la Direction générale des Finances publiques" + pole_emploi: + show: + not_filled: not filled + 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" diff --git a/config/locales/shared.fr.yml b/config/locales/shared.fr.yml index 935066cf9..2dc4b62ac 100644 --- a/config/locales/shared.fr.yml +++ b/config/locales/shared.fr.yml @@ -12,6 +12,9 @@ fr: numero_fiscal_notice: Il est généralement composé de 13 ou 14 caractères. reference_avis_label: La référence d'avis d'imposition reference_avis_notice: Elle est généralement composée de 13 ou 14 caractères. + pole_emploi: + identifiant_label: Identifiant + identifiant_notice: 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)" @@ -35,3 +38,9 @@ fr: fetching_data: "La récupération automatique des données pour le déclarant Nº %{numero_fiscal} avec la référence d'avis %{reference_avis} est en cours." data_fetched: "Des données concernant %{sources} liées au déclarant Nº %{numero_fiscal} avec la référence d'avis %{reference_avis} ont été reçues depuis la DGFiP." data_fetched_title: "Données obtenues de la Direction générale des Finances publiques" + pole_emploi: + show: + not_filled: non renseigné + 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" diff --git a/spec/controllers/administrateurs/jeton_particulier_controller_spec.rb b/spec/controllers/administrateurs/jeton_particulier_controller_spec.rb index 86af4a74b..3d26e31ff 100644 --- a/spec/controllers/administrateurs/jeton_particulier_controller_spec.rb +++ b/spec/controllers/administrateurs/jeton_particulier_controller_spec.rb @@ -75,7 +75,11 @@ describe Administrateurs::JetonParticulierController, type: :controller do 'dgfip_revenu_fiscal_reference', 'dgfip_revenu_imposable', 'dgfip_situation_familiale', - 'dgfip_situation_partielle' + 'dgfip_situation_partielle', + 'pole_emploi_identite', + 'pole_emploi_adresse', + 'pole_emploi_contact', + 'pole_emploi_inscription' ) expect(procedure.api_particulier_sources).to be_empty end diff --git a/spec/factories/champ.rb b/spec/factories/champ.rb index 41da9d524..af2cc885d 100644 --- a/spec/factories/champ.rb +++ b/spec/factories/champ.rb @@ -193,6 +193,10 @@ FactoryBot.define do type_de_champ { association :type_de_champ_dgfip, procedure: dossier.procedure } end + factory :champ_pole_emploi, class: 'Champs::PoleEmploiChamp' do + type_de_champ { association :type_de_champ_pole_emploi, 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 ecc632344..6a4caa34e 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -206,6 +206,12 @@ FactoryBot.define do end end + trait :with_pole_emploi do + after(:build) do |procedure, _evaluator| + build(:type_de_champ_pole_emploi, 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 6cae8d49f..fcfff36a1 100644 --- a/spec/factories/type_de_champ.rb +++ b/spec/factories/type_de_champ.rb @@ -160,6 +160,9 @@ FactoryBot.define do factory :type_de_champ_dgfip do type_champ { TypeDeChamp.type_champs.fetch(:dgfip) } end + factory :type_de_champ_pole_emploi do + type_champ { TypeDeChamp.type_champs.fetch(:pole_emploi) } + end factory :type_de_champ_carte do type_champ { TypeDeChamp.type_champs.fetch(:carte) } end diff --git a/spec/fixtures/cassettes/api_particulier/success/introspect.yml b/spec/fixtures/cassettes/api_particulier/success/introspect.yml index 81dcff1f9..3c59e6623 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: - - '228' + - '257' 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"]}' + 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"]}' recorded_at: Tue, 16 Mar 2021 15:25:24 GMT recorded_with: VCR 6.0.0 diff --git a/spec/lib/api_particulier/api_spec.rb b/spec/lib/api_particulier/api_spec.rb index 14e5ce1f5..10e5cbfc8 100644 --- a/spec/lib/api_particulier/api_spec.rb +++ b/spec/lib/api_particulier/api_spec.rb @@ -37,7 +37,11 @@ describe APIParticulier::API do 'dgfip_revenu_fiscal_reference', 'dgfip_revenu_imposable', 'dgfip_situation_familiale', - 'dgfip_situation_partielle' + 'dgfip_situation_partielle', + 'pole_emploi_identite', + 'pole_emploi_adresse', + 'pole_emploi_contact', + 'pole_emploi_inscription' ) end end diff --git a/spec/models/champs/pole_emploi_champ_spec.rb b/spec/models/champs/pole_emploi_champ_spec.rb new file mode 100644 index 000000000..baf248152 --- /dev/null +++ b/spec/models/champs/pole_emploi_champ_spec.rb @@ -0,0 +1,56 @@ +describe Champs::PoleEmploiChamp, type: :model do + let(:champ) { described_class.new } + + describe 'identifiant' do + before do + champ.identifiant = 'georges_moustaki_77' + end + + it 'saves identifiant' do + expect(champ.identifiant).to eq('georges_moustaki_77') + end + end + + describe 'external_id' do + context 'when no data is given' do + before do + champ.identifiant = '' + 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.identifiant = 'georges_moustaki_77' + champ.save + end + + it { expect(JSON.parse(champ.external_id)).to eq("identifiant" => "georges_moustaki_77") } + end + end + + describe '#validate' do + let(:champ) { described_class.new(dossier: create(:dossier), type_de_champ: create(:type_de_champ_pole_emploi)) } + let(:validation_context) { :create } + + subject { champ.valid?(validation_context) } + + before do + champ.identifiant = identifiant + end + + context 'when identifiant is valid' do + let(:identifiant) { 'georges_moustaki_77' } + + it { is_expected.to be true } + end + + context 'when identifiant is nil' do + let(:identifiant) { 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 d9a851996..22fc8fe8d 100644 --- a/spec/services/procedure_export_service_spec.rb +++ b/spec/services/procedure_export_service_spec.rb @@ -79,6 +79,7 @@ describe ProcedureExportService do "annuaire_education", "cnaf", "dgfip", + "pole_emploi", "text" ] end @@ -168,6 +169,7 @@ describe ProcedureExportService do "annuaire_education", "cnaf", "dgfip", + "pole_emploi", "text" ] end @@ -253,6 +255,7 @@ describe ProcedureExportService do "annuaire_education", "cnaf", "dgfip", + "pole_emploi", "text" ] end diff --git a/spec/system/api_particulier/api_particulier_spec.rb b/spec/system/api_particulier/api_particulier_spec.rb index b3060bb87..784f38157 100644 --- a/spec/system/api_particulier/api_particulier_spec.rb +++ b/spec/system/api_particulier/api_particulier_spec.rb @@ -20,6 +20,12 @@ describe 'fetch API Particulier Data', js: true do 'foyer_fiscal' => ['adresse', 'annee', 'nombreParts', 'nombrePersonnesCharge', 'situationFamille'], 'agregats_fiscaux' => ['anneeImpots', 'anneeRevenus', 'impotRevenuNetAvantCorrections', 'montantImpot', 'revenuBrutGlobal', 'revenuFiscalReference', 'revenuImposable'], 'complements' => ['situationPartielle', 'erreurCorrectif'] + }, + 'pole_emploi' => { + 'identite' => ['identifiant', 'civilite', 'nom', 'nomUsage', 'prenom', 'sexe', 'dateNaissance'], + 'adresse' => ['INSEECommune', 'codePostal', 'localite', 'ligneVoie', 'ligneComplementDestinataire', 'ligneComplementAdresse', 'ligneComplementDistribution', 'ligneNom'], + 'contact' => ['email', 'telephone', 'telephone2'], + 'inscription' => ['dateInscription', 'dateCessationInscription', 'codeCertificationCNAV', 'codeCategorieInscription', 'libelleCategorieInscription'] } } end @@ -29,7 +35,7 @@ describe 'fetch API Particulier Data', js: true do Flipper.enable(:api_particulier) end - context 'when an administrateur is logged' do + context 'when an administrateur is logged in' do let(:procedure) do create(:procedure, :with_service, :with_instructeur, aasm_state: :brouillon, @@ -56,14 +62,14 @@ describe 'fetch API Particulier Data', js: true do expect(page).to have_current_path(admin_procedure_api_particulier_sources_path(procedure)) ['allocataires', 'enfants'].each do |scope| - within("##{scope}") do + within("#cnaf-#{scope}") do check('noms et prénoms') check('date de naissance') check('sexe') end end - within('#adresse') do + within('#cnaf-adresse') do check('identité') check('complément d’identité') check('complément d’identité géographique') @@ -73,14 +79,14 @@ describe 'fetch API Particulier Data', js: true do check('pays') end - within('#quotient_familial') do + within('#cnaf-quotient_familial') do check('quotient familial') check('année') check('mois') end ['declarant1', 'declarant2'].each do |scope| - within("##{scope}") do + within("#dgfip-#{scope}") do check('nom') check('nom de naissance') check('prénoms') @@ -88,13 +94,13 @@ describe 'fetch API Particulier Data', js: true do end end - scroll_to(find('#echeance_avis')) - within ('#echeance_avis') do + scroll_to(find('#dgfip-echeance_avis')) + within ('#dgfip-echeance_avis') do check('date de recouvrement') check("date d’établissement") end - within('#foyer_fiscal') do + within('#dgfip-foyer_fiscal') do check('année') check('adresse') check('nombre de parts') @@ -102,7 +108,7 @@ describe 'fetch API Particulier Data', js: true do check('nombre de personnes à charge') end - within('#agregats_fiscaux') do + within('#dgfip-agregats_fiscaux') do check('revenu brut global') check('revenu imposable') check('impôt sur le revenu net avant correction') @@ -112,22 +118,58 @@ describe 'fetch API Particulier Data', js: true do check('année des revenus') end - within('#complements') do + within('#dgfip-complements') do check('erreur correctif') check('situation partielle') end + within('#pole_emploi-identite') do + check('identifiant') + check('civilité') + check('nom') + check("nom d’usage") + check('prénom') + check('sexe') + check('date de naissance') + end + + within('#pole_emploi-adresse') do + check('code INSEE de la commune') + check('code postal') + check('localité') + check('voie') + check('destinataire') + check('adresse') + check('distribution') + check('nom') + end + + within('#pole_emploi-contact') do + check('email') + check('téléphone') + check('téléphone 2') + end + + within('#pole_emploi-inscription') do + check("date d’inscription") + check("date de cessation d’inscription") + check('code de certification CNAV') + check("code de catégorie d’inscription") + check("libellé de catégorie d’inscription") + end + click_on 'Enregistrer' - within('#enfants') do + within('#cnaf-enfants') do expect(find('input[value=nomPrenom]')).to be_checked end procedure.reload - expect(procedure.api_particulier_sources.keys).to contain_exactly('cnaf', 'dgfip') + expect(procedure.api_particulier_sources.keys).to contain_exactly('cnaf', 'dgfip', 'pole_emploi') 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') procedure.api_particulier_sources.each do |provider, scopes| scopes.each do |scope, fields| @@ -153,17 +195,18 @@ describe 'fetch API Particulier Data', js: true do end end - context 'when an user is logged' do + context 'when a user is logged in' do let(:user) { create(:user) } - let(:api_particulier_token) { '29eb50b65f64e8e00c0847a8bbcbd150e1f847' } let(:numero_allocataire) { '5843972' } let(:code_postal) { '92110' } let(:numero_fiscal) { '2097699999077' } let(:reference_avis) { '2097699999077' } let(:instructeur) { create(:instructeur) } + let(:identifiant) { 'georges_moustaki_77' } + let(:api_particulier_token) { '29eb50b65f64e8e00c0847a8bbcbd150e1f847' } let(:procedure) do - create(:procedure, :for_individual, :with_service, :with_cnaf, :with_dgfip, :published, + create(:procedure, :for_individual, :with_service, :with_cnaf, :with_dgfip, :with_pole_emploi, :published, libelle: 'libellé de la procédure', path: 'libelle-de-la-procedure', instructeurs: [instructeur], @@ -173,54 +216,124 @@ describe 'fetch API Particulier Data', js: true do before { login_as user, scope: :user } - scenario 'it can fill an cnaf champ' do - visit commencer_path(path: procedure.path) - click_on 'Commencer la démarche' + context 'CNAF' do + scenario 'it can fill an cnaf champ' do + visit commencer_path(path: procedure.path) + click_on 'Commencer la démarche' - choose 'Monsieur' - fill_in 'individual_nom', with: 'Nom' - fill_in 'individual_prenom', with: 'Prenom' + choose 'Monsieur' + fill_in 'individual_nom', with: 'Nom' + fill_in 'individual_prenom', with: 'Prenom' - click_button('Continuer') + click_button('Continuer') - fill_in 'Le numéro d’allocataire CAF', with: numero_allocataire - fill_in 'Le code postal', with: 'wrong_code' + fill_in 'Le numéro d’allocataire CAF', with: numero_allocataire + fill_in 'Le code postal', with: 'wrong_code' - blur - expect(page).to have_css('span', text: 'Brouillon enregistré', visible: true) + blur + expect(page).to have_css('span', text: 'Brouillon enregistré', visible: true) - dossier = Dossier.last - expect(dossier.champs.first.code_postal).to eq('wrong_code') + dossier = Dossier.last + expect(dossier.champs.first.code_postal).to eq('wrong_code') - click_on 'Déposer le dossier' - expect(page).to have_content(/code postal doit posséder 5 caractères/) + click_on 'Déposer le dossier' + expect(page).to have_content(/code postal doit posséder 5 caractères/) - fill_in 'Le code postal', with: code_postal + fill_in 'Le code postal', with: code_postal - VCR.use_cassette('api_particulier/success/composition_familiale') do - perform_enqueued_jobs { click_on 'Déposer le dossier' } + VCR.use_cassette('api_particulier/success/composition_familiale') 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 la CAF/) + + log_out + + login_as instructeur.user, scope: :user + + visit instructeur_dossier_path(procedure, dossier) + + expect(page).to have_content('code postal et ville 92110 Clichy') + expect(page).to have_content('identité Mr SNOW Eric') + expect(page).to have_content('complément d’identité ne connait rien') + expect(page).to have_content('numéro et rue 109 rue La Boétie') + expect(page).to have_content('pays FRANCE') + expect(page).to have_content('complément d’identité géographique au nord de paris') + expect(page).to have_content('lieu-dit glagla') + expect(page).to have_content('ERIC SNOW masculin 07/01/1991') + expect(page).to have_content('SANSA SNOW féminin 15/01/1992') + expect(page).to have_content('PAUL SNOW masculin 04/01/2018') + expect(page).to have_content('1856 6 2021') end + end - visit demande_dossier_path(dossier) - expect(page).to have_content(/Des données.*ont été reçues depuis la CAF/) + context 'Pôle emploi' do + let(:api_particulier_token) { '06fd8675601267d2988cbbdef56ecb0de1d45223' } - log_out + scenario 'it can fill a Pôle emploi field' do + visit commencer_path(path: procedure.path) + click_on 'Commencer la démarche' - login_as instructeur.user, scope: :user + choose 'Monsieur' + fill_in 'individual_nom', with: 'Moustaki' + fill_in 'individual_prenom', with: 'Georges' - visit instructeur_dossier_path(procedure, dossier) + click_button('Continuer') - expect(page).to have_content('code postal et ville 92110 Clichy') - expect(page).to have_content('identité Mr SNOW Eric') - expect(page).to have_content('complément d’identité ne connait rien') - expect(page).to have_content('numéro et rue 109 rue La Boétie') - expect(page).to have_content('pays FRANCE') - expect(page).to have_content('complément d’identité géographique au nord de paris') - expect(page).to have_content('lieu-dit glagla') - expect(page).to have_content('ERIC SNOW masculin 07/01/1991') - expect(page).to have_content('SANSA SNOW féminin 15/01/1992') - expect(page).to have_content('PAUL SNOW masculin 04/01/2018') - expect(page).to have_content('1856 6 2021') + fill_in "Identifiant", with: 'wrong code' + + blur + expect(page).to have_css('span', text: 'Brouillon enregistré', visible: true) + + dossier = Dossier.last + pole_emploi_champ = dossier.champs.third + + expect(pole_emploi_champ.identifiant).to eq('wrong code') + + fill_in "Identifiant", with: identifiant + + VCR.use_cassette('api_particulier/success/situation_pole_emploi') 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 Pôle emploi/) + + log_out + + login_as instructeur.user, scope: :user + + visit instructeur_dossier_path(procedure, dossier) + + expect(page).to have_content('identifiant georges_moustaki_77') + expect(page).to have_content('civilité M.') + expect(page).to have_content('nom Moustaki') + expect(page).to have_content("nom d’usage Moustaki") + expect(page).to have_content('prénom Georges') + expect(page).to have_content('sexe masculin') + expect(page).to have_content('date de naissance 3 mai 1934') + + expect(page).to have_content('code INSEE de la commune 75118') + expect(page).to have_content('code postal 75018') + expect(page).to have_content('localité 75018 Paris') + expect(page).to have_content('voie 3 rue des Huttes') + expect(page).to have_content('nom MOUSTAKI') + + expect(page).to have_content('email georges@moustaki.fr') + expect(page).to have_content('téléphone 0629212921') + + expect(page).to have_content("date d’inscription 3 mai 1965") + expect(page).to have_content("date de cessation d’inscription 3 mai 1966") + expect(page).to have_content('code de certification CNAV VC') + expect(page).to have_content("code de catégorie d’inscription 1") + expect(page).to have_content("libellé de catégorie d’inscription PERSONNE SANS EMPLOI DISPONIBLE DUREE INDETERMINEE PLEIN TPS") + + expect(page).not_to have_content('téléphone 2') + expect(page).not_to have_content('destinataire') + expect(page).not_to have_content('adresse') + expect(page).not_to have_content('distribution') + end end scenario 'it can fill a DGFiP field' do