diff --git a/app/assets/stylesheets/cnaf.scss b/app/assets/stylesheets/cnaf.scss new file mode 100644 index 000000000..9cbd7ea36 --- /dev/null +++ b/app/assets/stylesheets/cnaf.scss @@ -0,0 +1,30 @@ +@import "constants"; +@import "colors"; + +table.cnaf { + 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/assets/stylesheets/forms.scss b/app/assets/stylesheets/forms.scss index a21886db7..717b9b51e 100644 --- a/app/assets/stylesheets/forms.scss +++ b/app/assets/stylesheets/forms.scss @@ -356,6 +356,17 @@ } } + .cnaf-inputs { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + max-width: 700px; + + input { + width: inherit; + } + } + input.aa-input, input.aa-hint { border-radius: 4px; diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 45659b21a..721ddf54b 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -337,8 +337,8 @@ module Users def champs_params params.permit(dossier: { champs_attributes: [ - :id, :value, :external_id, :primary_value, :secondary_value, :piece_justificative_file, value: [], - champs_attributes: [:id, :_destroy, :value, :external_id, :primary_value, :secondary_value, :piece_justificative_file, value: []] + :id, :value, :external_id, :primary_value, :secondary_value, :numero_allocataire, :code_postal, :piece_justificative_file, value: [], + champs_attributes: [:id, :_destroy, :value, :external_id, :primary_value, :secondary_value, :numero_allocataire, :code_postal, :piece_justificative_file, value: []] ] }) end @@ -382,7 +382,8 @@ module Users if @dossier.champs.any?(&:changed_for_autosave?) @dossier.last_champ_updated_at = Time.zone.now end - if !@dossier.save + + if !@dossier.save(**validation_options) errors += @dossier.errors.full_messages elsif change_groupe_instructeur? @dossier.assign_to_groupe_instructeur(groupe_instructeur_from_params) @@ -453,5 +454,16 @@ module Users def save_draft? dossier.brouillon? && !params[:submit_draft] end + + def validation_options + if save_draft? + { context: :brouillon } + else + # rubocop:disable Lint/BooleanSymbol + # Force ActiveRecord to re-validate associated records. + { context: :false } + # rubocop:enable Lint/BooleanSymbol + end + end end end diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index 5f73dbc34..24e02e2d3 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -1869,6 +1869,11 @@ enum TypeDeChamp { """ civilite + """ + Données de la Caisse nationale des allocations familiales + """ + cnaf + """ Communes """ diff --git a/app/lib/api_particulier/cnaf_adapter.rb b/app/lib/api_particulier/cnaf_adapter.rb index 7a2412d82..5377fecaf 100644 --- a/app/lib/api_particulier/cnaf_adapter.rb +++ b/app/lib/api_particulier/cnaf_adapter.rb @@ -1,4 +1,4 @@ -class APIParticulier::CNAFAdapter +class APIParticulier::CnafAdapter class InvalidSchemaError < ::StandardError def initialize(errors) super(errors.map(&:to_json).join("\n")) diff --git a/app/lib/api_particulier/error.rb b/app/lib/api_particulier/error.rb index acc810451..4224bcad7 100644 --- a/app/lib/api_particulier/error.rb +++ b/app/lib/api_particulier/error.rb @@ -14,7 +14,7 @@ module APIParticulier msg = <<~TEXT url: #{url} HTTP error code: #{http_error_code} - #{response.body} + #{response.body.force_encoding('UTF-8')} curl message: #{curl_message} total time: #{total_time} connect time: #{connect_time} diff --git a/app/lib/api_particulier/services/sources_service.rb b/app/lib/api_particulier/services/sources_service.rb index 26971ae32..4d66c32d0 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 .map { |provider_and_scope| raw_scopes[provider_and_scope] } + .compact .map { |provider, scope| extract_sources(provider, scope) } .reduce({}) { |acc, el| acc.deep_merge(el) } end diff --git a/app/models/champ.rb b/app/models/champ.rb index 539c5af8b..61930b3ab 100644 --- a/app/models/champ.rb +++ b/app/models/champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/address_champ.rb b/app/models/champs/address_champ.rb index 72da63012..3e7870b50 100644 --- a/app/models/champs/address_champ.rb +++ b/app/models/champs/address_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/annuaire_education_champ.rb b/app/models/champs/annuaire_education_champ.rb index d5a895224..22141ca17 100644 --- a/app/models/champs/annuaire_education_champ.rb +++ b/app/models/champs/annuaire_education_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/carte_champ.rb b/app/models/champs/carte_champ.rb index f8b67c82c..1e91e529e 100644 --- a/app/models/champs/carte_champ.rb +++ b/app/models/champs/carte_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/checkbox_champ.rb b/app/models/champs/checkbox_champ.rb index 40b38af23..82f009577 100644 --- a/app/models/champs/checkbox_champ.rb +++ b/app/models/champs/checkbox_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/civilite_champ.rb b/app/models/champs/civilite_champ.rb index 8f04758cb..ace32a968 100644 --- a/app/models/champs/civilite_champ.rb +++ b/app/models/champs/civilite_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/cnaf_champ.rb b/app/models/champs/cnaf_champ.rb new file mode 100644 index 000000000..fc3c10744 --- /dev/null +++ b/app/models/champs/cnaf_champ.rb @@ -0,0 +1,52 @@ +# == 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 +# 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::CnafChamp < Champs::TextChamp + # see https://github.com/betagouv/api-particulier/blob/master/src/presentation/middlewares/cnaf-input-validation.middleware.ts + validates :numero_allocataire, format: { with: /\A\d{1,7}\z/ }, if: -> { code_postal.present? && validation_context != :brouillon } + validates :code_postal, format: { with: /\A\w{5}\z/ }, if: -> { numero_allocataire.present? && validation_context != :brouillon } + + store_accessor :value_json, :numero_allocataire, :code_postal + + def blank? + external_id.nil? + end + + def fetch_external_data? + true + end + + def fetch_external_data + if valid? + APIParticulier::CnafAdapter.new( + procedure.api_particulier_token, + numero_allocataire, + code_postal, + procedure.api_particulier_sources + ).to_params + end + end + + def external_id + if numero_allocataire.present? && code_postal.present? + { code_postal: code_postal, numero_allocataire: numero_allocataire }.to_json + end + end +end diff --git a/app/models/champs/commune_champ.rb b/app/models/champs/commune_champ.rb index 34569584f..349221cf1 100644 --- a/app/models/champs/commune_champ.rb +++ b/app/models/champs/commune_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/date_champ.rb b/app/models/champs/date_champ.rb index 1eca35bf8..2e09bd29d 100644 --- a/app/models/champs/date_champ.rb +++ b/app/models/champs/date_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/datetime_champ.rb b/app/models/champs/datetime_champ.rb index cf2d6c7e1..f7f5bd496 100644 --- a/app/models/champs/datetime_champ.rb +++ b/app/models/champs/datetime_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/decimal_number_champ.rb b/app/models/champs/decimal_number_champ.rb index b907c1c50..f574c07ca 100644 --- a/app/models/champs/decimal_number_champ.rb +++ b/app/models/champs/decimal_number_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/departement_champ.rb b/app/models/champs/departement_champ.rb index d4eaa8501..ac5c89633 100644 --- a/app/models/champs/departement_champ.rb +++ b/app/models/champs/departement_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/dossier_link_champ.rb b/app/models/champs/dossier_link_champ.rb index 39ba322f8..866f70197 100644 --- a/app/models/champs/dossier_link_champ.rb +++ b/app/models/champs/dossier_link_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/drop_down_list_champ.rb b/app/models/champs/drop_down_list_champ.rb index 29b9be33a..92f52e3f1 100644 --- a/app/models/champs/drop_down_list_champ.rb +++ b/app/models/champs/drop_down_list_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/email_champ.rb b/app/models/champs/email_champ.rb index 67bdd1ca4..c8a1949b0 100644 --- a/app/models/champs/email_champ.rb +++ b/app/models/champs/email_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/engagement_champ.rb b/app/models/champs/engagement_champ.rb index b6992749e..986623136 100644 --- a/app/models/champs/engagement_champ.rb +++ b/app/models/champs/engagement_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/explication_champ.rb b/app/models/champs/explication_champ.rb index 022149978..d20108393 100644 --- a/app/models/champs/explication_champ.rb +++ b/app/models/champs/explication_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/header_section_champ.rb b/app/models/champs/header_section_champ.rb index 983e60b04..0ff9c7659 100644 --- a/app/models/champs/header_section_champ.rb +++ b/app/models/champs/header_section_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/iban_champ.rb b/app/models/champs/iban_champ.rb index feb3429dd..71ba23281 100644 --- a/app/models/champs/iban_champ.rb +++ b/app/models/champs/iban_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/integer_number_champ.rb b/app/models/champs/integer_number_champ.rb index 68bf2e173..8272800e1 100644 --- a/app/models/champs/integer_number_champ.rb +++ b/app/models/champs/integer_number_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/linked_drop_down_list_champ.rb b/app/models/champs/linked_drop_down_list_champ.rb index 157f4af89..f7022d727 100644 --- a/app/models/champs/linked_drop_down_list_champ.rb +++ b/app/models/champs/linked_drop_down_list_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/multiple_drop_down_list_champ.rb b/app/models/champs/multiple_drop_down_list_champ.rb index 5a239151c..87429b0ee 100644 --- a/app/models/champs/multiple_drop_down_list_champ.rb +++ b/app/models/champs/multiple_drop_down_list_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/number_champ.rb b/app/models/champs/number_champ.rb index e0186615c..fa2ec9b47 100644 --- a/app/models/champs/number_champ.rb +++ b/app/models/champs/number_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/pays_champ.rb b/app/models/champs/pays_champ.rb index bddc575ef..634391a31 100644 --- a/app/models/champs/pays_champ.rb +++ b/app/models/champs/pays_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/phone_champ.rb b/app/models/champs/phone_champ.rb index da1166e6b..15002fe60 100644 --- a/app/models/champs/phone_champ.rb +++ b/app/models/champs/phone_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/piece_justificative_champ.rb b/app/models/champs/piece_justificative_champ.rb index 862c57b67..245750c01 100644 --- a/app/models/champs/piece_justificative_champ.rb +++ b/app/models/champs/piece_justificative_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/region_champ.rb b/app/models/champs/region_champ.rb index cf5e89075..255e4abb2 100644 --- a/app/models/champs/region_champ.rb +++ b/app/models/champs/region_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/repetition_champ.rb b/app/models/champs/repetition_champ.rb index 7e9ac54aa..b07fd0958 100644 --- a/app/models/champs/repetition_champ.rb +++ b/app/models/champs/repetition_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/siret_champ.rb b/app/models/champs/siret_champ.rb index 2e21f1ff3..b9b5b27b5 100644 --- a/app/models/champs/siret_champ.rb +++ b/app/models/champs/siret_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/text_champ.rb b/app/models/champs/text_champ.rb index 11fcb3939..1376cce48 100644 --- a/app/models/champs/text_champ.rb +++ b/app/models/champs/text_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/textarea_champ.rb b/app/models/champs/textarea_champ.rb index 89177feb2..028950a5f 100644 --- a/app/models/champs/textarea_champ.rb +++ b/app/models/champs/textarea_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/titre_identite_champ.rb b/app/models/champs/titre_identite_champ.rb index 289546476..982ae7ae7 100644 --- a/app/models/champs/titre_identite_champ.rb +++ b/app/models/champs/titre_identite_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/champs/yes_no_champ.rb b/app/models/champs/yes_no_champ.rb index ccbd46186..fc8346cfb 100644 --- a/app/models/champs/yes_no_champ.rb +++ b/app/models/champs/yes_no_champ.rb @@ -9,6 +9,7 @@ # row :integer # type :string # value :string +# value_json :jsonb # created_at :datetime # updated_at :datetime # dossier_id :integer diff --git a/app/models/procedure.rb b/app/models/procedure.rb index e74f8f47d..df9aba673 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -716,6 +716,10 @@ class Procedure < ApplicationRecord published_revision.touch(:published_at) end + def cnaf_enabled? + api_particulier_sources['cnaf'].present? + end + private def before_publish diff --git a/app/models/type_de_champ.rb b/app/models/type_de_champ.rb index 2976b6e7d..dcf5b096d 100644 --- a/app/models/type_de_champ.rb +++ b/app/models/type_de_champ.rb @@ -48,7 +48,8 @@ class TypeDeChamp < ApplicationRecord repetition: 'repetition', titre_identite: 'titre_identite', iban: 'iban', - annuaire_education: 'annuaire_education' + annuaire_education: 'annuaire_education', + cnaf: 'cnaf' } belongs_to :revision, class_name: 'ProcedureRevision', optional: true @@ -291,17 +292,26 @@ class TypeDeChamp < ApplicationRecord def self.type_de_champ_types_for(procedure, user) has_legacy_number = (procedure.types_de_champ + procedure.types_de_champ_private).any?(&:legacy_number?) - show_number = -> (tdc) { tdc != TypeDeChamp.type_champs.fetch(:number) || has_legacy_number } - - enabled_featured_champ = -> (tdc) do + filter_featured_tdc = -> (tdc) do feature_name = FEATURE_FLAGS[tdc] feature_name.blank? || Flipper.enabled?(feature_name, user) end + filter_tdc = -> (tdc) do + case tdc + when TypeDeChamp.type_champs.fetch(:number) + has_legacy_number + when TypeDeChamp.type_champs.fetch(:cnaf) + procedure.cnaf_enabled? + else + true + end + end + type_champs .keys - .filter(&show_number) - .filter(&enabled_featured_champ) + .filter(&filter_tdc) + .filter(&filter_featured_tdc) .map { |tdc| [I18n.t("activerecord.attributes.type_de_champ.type_champs.#{tdc}"), tdc] } .sort_by(&:first) end diff --git a/app/models/types_de_champ/cnaf_type_de_champ.rb b/app/models/types_de_champ/cnaf_type_de_champ.rb new file mode 100644 index 000000000..2702bec84 --- /dev/null +++ b/app/models/types_de_champ/cnaf_type_de_champ.rb @@ -0,0 +1,2 @@ +class TypesDeChamp::CnafTypeDeChamp < TypesDeChamp::TextTypeDeChamp +end diff --git a/app/views/new_administrateur/jeton_particulier/api_particulier.html.haml b/app/views/new_administrateur/jeton_particulier/api_particulier.html.haml index ad8f7bc14..40f23fa2c 100644 --- a/app/views/new_administrateur/jeton_particulier/api_particulier.html.haml +++ b/app/views/new_administrateur/jeton_particulier/api_particulier.html.haml @@ -5,7 +5,7 @@ .container .flex - = link_to admin_procedure_api_particulier_jeton_path, class: 'card-admin' do + = link_to admin_procedure_api_particulier_jeton_path, class: 'card-admin', id: 'add-jeton' do - if @procedure.api_particulier_token.blank? %div %span.icon.clock diff --git a/app/views/new_administrateur/procedures/show.html.haml b/app/views/new_administrateur/procedures/show.html.haml index d0aa14694..40d26f61e 100644 --- a/app/views/new_administrateur/procedures/show.html.haml +++ b/app/views/new_administrateur/procedures/show.html.haml @@ -193,7 +193,7 @@ %p.button Modifier - if feature_enabled?(:api_particulier) - = link_to admin_procedure_api_particulier_path(@procedure), class: 'card-admin' do + = link_to admin_procedure_api_particulier_path(@procedure), class: 'card-admin', id: 'api-particulier' do - if @procedure.api_particulier_token.present? %div %span.icon.accept diff --git a/app/views/new_administrateur/sources_particulier/show.html.haml b/app/views/new_administrateur/sources_particulier/show.html.haml index cd06d96c6..6f839d482 100644 --- a/app/views/new_administrateur/sources_particulier/show.html.haml +++ b/app/views/new_administrateur/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 + %ul.procedure-admin-api-particulier-sources{ id: 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/cnaf/_adresse.html.haml b/app/views/shared/champs/cnaf/_adresse.html.haml new file mode 100644 index 000000000..9c3cbdd1e --- /dev/null +++ b/app/views/shared/champs/cnaf/_adresse.html.haml @@ -0,0 +1,10 @@ +%table.cnaf + %caption #{t("api_particulier.providers.cnaf.scopes.adresse.libelle")} : + - for key in ['identite', 'complementIdentite', 'numeroRue', 'complementIdentiteGeo', 'lieuDit', 'codePostalVille', 'pays'] do + - if adresse[key].present? + %tr + %th= t("api_particulier.providers.cnaf.scopes.adresse.#{key}") + %td= adresse[key] + + + diff --git a/app/views/shared/champs/cnaf/_personnes.html.haml b/app/views/shared/champs/cnaf/_personnes.html.haml new file mode 100644 index 000000000..d4fa3ee95 --- /dev/null +++ b/app/views/shared/champs/cnaf/_personnes.html.haml @@ -0,0 +1,19 @@ +%table.cnaf.horizontal + %caption #{t("api_particulier.providers.cnaf.scopes.#{scope}.libelle")} : + %thead + %tr + - for key in ['nomPrenom', 'sexe', 'dateDeNaissance'] do + - if personnes.first[key].present? + %th{ class: "#{"text-right" if key == 'dateDeNaissance'}" }= t("api_particulier.providers.cnaf.scopes.personne.#{key}") + %tbody + - personnes.each do |personne| + %tr + - for key in ['nomPrenom', 'sexe', 'dateDeNaissance'] do + - if personne[key].present? + - case key + - when 'dateDeNaissance' + %td.text-right= try_format_datetime(Date.strptime(personne[key], "%d%m%Y")) + - when 'sexe' + %td= t("api_particulier.providers.cnaf.scopes.personne.#{personne[key]}") + - else + %td= personne[key] diff --git a/app/views/shared/champs/cnaf/_quotient_familial.html.haml b/app/views/shared/champs/cnaf/_quotient_familial.html.haml new file mode 100644 index 000000000..b01d09ee7 --- /dev/null +++ b/app/views/shared/champs/cnaf/_quotient_familial.html.haml @@ -0,0 +1,14 @@ +%table.cnaf.horizontal + %caption #{t("api_particulier.providers.cnaf.scopes.quotient_familial.libelle")} : + %thead + %tr + - for key in ['quotientFamilial', 'mois', 'annee'] do + - if quotient_familial[key].present? + %th.text-right= t("api_particulier.providers.cnaf.scopes.quotient_familial.#{key}") + %tbody + %tr + - for key in ['quotientFamilial', 'mois', 'annee'] do + - if quotient_familial[key].present? + %td.text-right= quotient_familial[key] + - else + %td diff --git a/app/views/shared/champs/cnaf/_show.html.haml b/app/views/shared/champs/cnaf/_show.html.haml new file mode 100644 index 000000000..82a7333c6 --- /dev/null +++ b/app/views/shared/champs/cnaf/_show.html.haml @@ -0,0 +1,24 @@ +- if champ.blank? + %p= t('.not_filled') +- elsif champ.data.blank? + %p= t('.fetching_data', + numero_allocataire: champ.numero_allocataire, + code_postal: champ.code_postal) +- else + - if profile == 'usager' + %p= t('.data_fetched', + sources: champ.procedure.api_particulier_sources['cnaf'].keys.map(&:to_s).join(', '), + numero_allocataire: champ.numero_allocataire, + code_postal: champ.code_postal) + + - if profile == 'instructeur' + %p= t('.data_fetched_title') + + - ['adresse', 'quotient_familial', 'enfants', 'allocataires'].each do |scope| + - if champ.data[scope].present? + - if scope == 'quotient_familial' + = render partial: 'shared/champs/cnaf/quotient_familial', locals: { quotient_familial: champ.data[scope] } + - if scope.in? ['enfants', 'allocataires'] + = render partial: 'shared/champs/cnaf/personnes', locals: { scope: scope, personnes: champ.data[scope] } + - elsif scope == 'adresse' + = render partial: 'shared/champs/cnaf/adresse', locals: { adresse: champ.data[scope] } diff --git a/app/views/shared/dossiers/_champ_row.html.haml b/app/views/shared/dossiers/_champ_row.html.haml index 9f4057894..0264327cf 100644 --- a/app/views/shared/dossiers/_champ_row.html.haml +++ b/app/views/shared/dossiers/_champ_row.html.haml @@ -36,6 +36,8 @@ = render partial: "shared/champs/textarea/show", locals: { champ: c } - when TypeDeChamp.type_champs.fetch(:annuaire_education) = render partial: "shared/champs/annuaire_education/show", locals: { champ: c } + - when TypeDeChamp.type_champs.fetch(:cnaf) + = render partial: "shared/champs/cnaf/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/_cnaf.html.haml b/app/views/shared/dossiers/editable_champs/_cnaf.html.haml new file mode 100644 index 000000000..1d8d90610 --- /dev/null +++ b/app/views/shared/dossiers/editable_champs/_cnaf.html.haml @@ -0,0 +1,16 @@ +.cnaf-inputs + %div + = form.label :numero_allocataire, t('.numero_allocataire_label') + %p.notice= t('.numero_allocataire_notice') + = form.text_field :numero_allocataire, + required: champ.mandatory?, + size: 7, + aria: { describedby: describedby_id(champ) } + + %div + = form.label :code_postal, t('.code_postal_label') + %p.notice= t('.code_postal_notice') + = form.text_field :code_postal, + size: 5, + required: champ.mandatory?, + aria: { describedby: describedby_id(champ) } diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index 1631f421b..c1ecd861e 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -14,7 +14,6 @@ ActiveSupport::Inflector.inflections(:en) do |inflect| inflect.acronym 'JSON' inflect.acronym 'RNA' inflect.acronym 'URL' - inflect.acronym 'CNAF' inflect.irregular 'type_de_champ', 'types_de_champ' inflect.irregular 'type_de_champ_private', 'types_de_champ_private' inflect.irregular 'procedure_revision_type_de_champ', 'procedure_revision_types_de_champ' diff --git a/config/locales/api_particulier.en.yml b/config/locales/api_particulier.en.yml new file mode 100644 index 000000000..6c0abf1b5 --- /dev/null +++ b/config/locales/api_particulier.en.yml @@ -0,0 +1,32 @@ +en: + api_particulier: + providers: + cnaf: + libelle: Caisse nationale d’allocations familiales (CAF) + scopes: + personne: &personne + nomPrenom: first and last name + dateDeNaissance: birth date + sexe: sex + M: male + F: female + allocataires: + libelle: beneficiaries + <<: *personne + enfants: + libelle: children + <<: *personne + adresse: + libelle: address + identite: identity + complementIdentite: complément d’identité + complementIdentiteGeo: complément d’identité géographique + numeroRue: number and street + lieuDit: lieu-dit + codePostalVille: postcode and city + pays: country + quotient_familial: + libelle: quotient familial + quotientFamilial: quotient familial + mois: month + annee: year diff --git a/config/locales/api_particulier.fr.yml b/config/locales/api_particulier.fr.yml index 39841906f..352250e17 100644 --- a/config/locales/api_particulier.fr.yml +++ b/config/locales/api_particulier.fr.yml @@ -2,12 +2,14 @@ fr: api_particulier: providers: cnaf: - libelle: Caisse d’allocations familiales (CAF) + libelle: Caisse nationale d’allocations familiales (CAF) scopes: personne: &personne nomPrenom: noms et prénoms dateDeNaissance: date de naissance - sexe: genre + sexe: sexe + M: masculin + F: féminin allocataires: libelle: allocataires <<: *personne diff --git a/config/locales/en.yml b/config/locales/en.yml index 0224de480..5c6bccab0 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -274,6 +274,12 @@ en: taken: is already used for procedure. You cannot use it because it belongs to another administrator. # taken_can_be_claimed: est identique à celui d’une autre de vos procedures publiées. Si vous publiez cette procedure, l’ancienne sera dépubliée et ne sera plus accessible au public. Les utilisateurs qui ont commencé un brouillon vont pouvoir le déposer. invalid: is not valid. It must countain between 3 and 50 characters among a-z, 0-9, '_' and '-'. + "champs/cnaf_champ": + attributes: + numero_allocataire: + invalid: "must be a maximum of 7 digits" + code_postal: + invalid: "must be 5 characters long" errors: messages: dossier_not_found: "The file does not exist or you do not have access to it." diff --git a/config/locales/fr.yml b/config/locales/fr.yml index cf9eef404..d2361db5a 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -280,6 +280,12 @@ fr: taken: est déjà utilisé par une démarche. Vous ne pouvez pas l’utiliser car il appartient à un autre administrateur. taken_can_be_claimed: est identique à celui d’une autre de vos démarches publiées. Si vous publiez cette démarche, l’ancienne sera dépubliée et ne sera plus accessible au public. Les utilisateurs qui ont commencé un brouillon vont pouvoir le déposer. invalid: n’est pas valide. Il doit comporter au moins 3 caractères, au plus 50 caractères et seuls les caractères a-z, 0-9, '_' et '-' sont autorisés. + "champs/cnaf_champ": + attributes: + numero_allocataire: + invalid: "doit être composé au maximum de 7 chiffres" + code_postal: + invalid: "doit posséder 5 caractères" errors: messages: saml_not_authorized: "Vous n’êtes pas autorisé à accéder à ce service." diff --git a/config/locales/models/champs/fr.yml b/config/locales/models/champs/fr.yml deleted file mode 100644 index 7a0730a32..000000000 --- a/config/locales/models/champs/fr.yml +++ /dev/null @@ -1,6 +0,0 @@ -fr: - activerecord: - attributes: - champ: - value: La valeur du champ - piece_justificative_file: La pièce justificative diff --git a/config/locales/models/type_de_champ/fr.yml b/config/locales/models/type_de_champ/fr.yml index 9d42446f4..15a4d2aad 100644 --- a/config/locales/models/type_de_champ/fr.yml +++ b/config/locales/models/type_de_champ/fr.yml @@ -36,3 +36,5 @@ fr: titre_identite: 'Titre identité' iban: 'Iban' annuaire_education: 'Annuaire de l’éducation' + cnaf: 'Données de la Caisse nationale des allocations familiales' + diff --git a/config/locales/shared.en.yml b/config/locales/shared.en.yml new file mode 100644 index 000000000..7ef3d6294 --- /dev/null +++ b/config/locales/shared.en.yml @@ -0,0 +1,16 @@ +en: + shared: + dossiers: + editable_champs: + cnaf: + numero_allocataire_label: CAF benefit number + numero_allocataire_notice: It is usually composed of 7 digits. + code_postal_label: postal code + code_postal_notice: It is usually composed of 5 digits. + champs: + cnaf: + show: + not_filled: not filled + fetching_data: "Fetching data for recipient No. %{numero_allocataire} with postal code %{code_postal}." + data_fetched: "Data concerning %{sources} linked to the account Nº %{numero_allocataire} with the postal code %{code_postal} has been received from the CAF." + data_fetched_title: "Data received from la Caisse nationale d’allocations familiales" diff --git a/config/locales/shared.fr.yml b/config/locales/shared.fr.yml new file mode 100644 index 000000000..a0bf159e1 --- /dev/null +++ b/config/locales/shared.fr.yml @@ -0,0 +1,16 @@ +fr: + shared: + dossiers: + editable_champs: + cnaf: + numero_allocataire_label: Le numéro d’allocataire CAF + numero_allocataire_notice: Il est généralement composé de 7 chiffres. + code_postal_label: Le code postal + code_postal_notice: Il est généralement composé de 5 chiffres. + champs: + cnaf: + show: + not_filled: non renseigné + fetching_data: "La récupération automatique des données pour l’allocataire Nº %{numero_allocataire} avec le code postal %{code_postal} est en cours." + data_fetched: "Des données concernant %{sources} liées au compte Nº %{numero_allocataire} avec le code postal %{code_postal} ont été reçues depuis la CAF." + data_fetched_title: "Données obtenues de la Caisse nationale d’allocations familiales" diff --git a/db/migrate/20211005133027_add_value_json_column_to_champ.rb b/db/migrate/20211005133027_add_value_json_column_to_champ.rb new file mode 100644 index 000000000..92b9d7a6e --- /dev/null +++ b/db/migrate/20211005133027_add_value_json_column_to_champ.rb @@ -0,0 +1,5 @@ +class AddValueJSONColumnToChamp < ActiveRecord::Migration[6.1] + def change + add_column :champs, :value_json, :jsonb + end +end diff --git a/db/schema.rb b/db/schema.rb index 2d29640fd..cd272f562 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -189,6 +189,7 @@ ActiveRecord::Schema.define(version: 2021_10_06_164955) do t.jsonb "data" t.string "external_id" t.string "fetch_external_data_exceptions", array: true + t.jsonb "value_json" t.index ["dossier_id"], name: "index_champs_on_dossier_id" t.index ["parent_id"], name: "index_champs_on_parent_id" t.index ["private"], name: "index_champs_on_private" diff --git a/spec/factories/champ.rb b/spec/factories/champ.rb index 03a36d695..b438befbb 100644 --- a/spec/factories/champ.rb +++ b/spec/factories/champ.rb @@ -185,6 +185,10 @@ FactoryBot.define do type_de_champ { association :type_de_champ_annuaire_education, procedure: dossier.procedure } end + factory :champ_cnaf, class: 'Champs::CnafChamp' do + type_de_champ { association :type_de_champ_cnaf, procedure: dossier.procedure } + end + factory :champ_siret, class: 'Champs::SiretChamp' do association :type_de_champ, factory: [:type_de_champ_siret] association :etablissement, factory: [:etablissement] diff --git a/spec/factories/procedure.rb b/spec/factories/procedure.rb index 2651a971e..47a85de6a 100644 --- a/spec/factories/procedure.rb +++ b/spec/factories/procedure.rb @@ -205,6 +205,12 @@ FactoryBot.define do end end + trait :with_cnaf do + after(:build) do |procedure, _evaluator| + build(:type_de_champ_cnaf, 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 ae6fa4cc1..9f5223e86 100644 --- a/spec/factories/type_de_champ.rb +++ b/spec/factories/type_de_champ.rb @@ -151,6 +151,9 @@ FactoryBot.define do factory :type_de_champ_annuaire_education do type_champ { TypeDeChamp.type_champs.fetch(:annuaire_education) } end + factory :type_de_champ_cnaf do + type_champ { TypeDeChamp.type_champs.fetch(:cnaf) } + end factory :type_de_champ_carte do type_champ { TypeDeChamp.type_champs.fetch(:carte) } end diff --git a/spec/features/api_particulier/api_particulier_spec.rb b/spec/features/api_particulier/api_particulier_spec.rb new file mode 100644 index 000000000..de793f453 --- /dev/null +++ b/spec/features/api_particulier/api_particulier_spec.rb @@ -0,0 +1,167 @@ +feature 'fetch API Particulier Data', js: true do + let(:administrateur) { create(:administrateur) } + + let(:expected_token) { 'd7e9c9f4c3ca00caadde31f50fd4521a' } + + let(:expected_sources) do + { + "cnaf" => + { + "adresse" => ["identite", "complementIdentite", "complementIdentiteGeo", "numeroRue", "lieuDit", "codePostalVille", "pays"], + "allocataires" => ["nomPrenom", "dateDeNaissance", "sexe"], + "enfants" => ["nomPrenom", "dateDeNaissance", "sexe"], + "quotient_familial" => ["quotientFamilial", "annee", "mois"] + } + } + end + + before do + stub_const("API_PARTICULIER_URL", "https://particulier.api.gouv.fr/api") + Flipper.enable(:api_particulier) + end + + context "when an administrateur is logged" do + let(:procedure) do + create(:procedure, :with_service, :with_instructeur, + aasm_state: :brouillon, + administrateurs: [administrateur], + libelle: "libellé de la procédure", + path: "libelle-de-la-procedure") + end + + before { login_as administrateur.user, scope: :user } + + scenario 'it can enable api particulier' do + visit admin_procedure_path(procedure) + expect(page).to have_content("Configurer le jeton API particulier") + + find('#api-particulier').click + expect(page).to have_current_path(admin_procedure_api_particulier_path(procedure)) + + find('#add-jeton').click + expect(page).to have_current_path(admin_procedure_api_particulier_jeton_path(procedure)) + + fill_in 'procedure_api_particulier_token', with: expected_token + VCR.use_cassette("api_particulier/success/introspect") { click_on 'Enregistrer' } + expect(page).to have_text('Le jeton a bien été mis à jour') + expect(page).to have_current_path(admin_procedure_api_particulier_sources_path(procedure)) + + ['allocataires', 'enfants'].each do |scope| + within("##{scope}") do + check('noms et prénoms') + check('date de naissance') + check('sexe') + end + end + + within("#adresse") do + check('identité') + check('complément d’identité') + check('complément d’identité géographique') + check('numéro et rue') + check('lieu-dit') + check('code postal et ville') + check('pays') + end + + within("#quotient_familial") do + check('quotient familial') + check('année') + check('mois') + end + + click_on "Enregistrer" + + within("#enfants") do + expect(find('input[value=nomPrenom]')).to be_checked + end + + expect(procedure.reload.api_particulier_sources).to eq(expected_sources) + + visit champs_admin_procedure_path(procedure) + + add_champ + select('Données de la Caisse nationale des allocations familiales', from: 'champ-0-type_champ') + fill_in 'champ-0-libelle', with: 'libellé de champ' + blur + expect(page).to have_content('Formulaire enregistré') + + visit admin_procedure_path(procedure) + find('#publish-procedure-link').click + expect(find_field('procedure_path').value).to eq procedure.path + fill_in 'lien_site_web', with: 'http://some.website' + click_on 'Publier' + + expect(page).to have_text('Démarche publiée') + end + end + + context 'when an user is logged' do + let(:user) { create(:user) } + let(:api_particulier_token) { '29eb50b65f64e8e00c0847a8bbcbd150e1f847' } + let(:numero_allocataire) { '5843972' } + let(:code_postal) { '92110' } + let(:instructeur) { create(:instructeur) } + + let(:procedure) do + create(:procedure, :for_individual, :with_service, :with_cnaf, :published, + libelle: "libellé de la procédure", + path: "libelle-de-la-procedure", + instructeurs: [instructeur], + api_particulier_sources: expected_sources, + api_particulier_token: api_particulier_token) + end + + 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' + + choose 'Monsieur' + fill_in 'individual_nom', with: 'Nom' + fill_in 'individual_prenom', with: 'Prenom' + + click_button('Continuer') + + 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) + + 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/) + + 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' } + 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 +end diff --git a/spec/features/routing/full_scenario_spec.rb b/spec/features/routing/full_scenario_spec.rb index c09c3f706..47cdee2d5 100644 --- a/spec/features/routing/full_scenario_spec.rb +++ b/spec/features/routing/full_scenario_spec.rb @@ -63,7 +63,7 @@ feature 'The routing', js: true do # publish publish_procedure(procedure) - log_out(old_layout: true) + log_out # 2 users fill a dossier in each group user_send_dossier(scientifique_user, 'scientifique') @@ -222,15 +222,4 @@ feature 'The routing', js: true do expect(page).to have_text('Mot de passe enregistré') end - - def log_out(old_layout: false) - if old_layout - page.all('.dropdown-button').first.click - click_on 'Se déconnecter' - else - click_button(title: 'Mon compte') - click_on 'Se déconnecter' - end - expect(page).to have_current_path(root_path) - end end diff --git a/spec/fixtures/cassettes/api_particulier/success/composition_familiale.yml b/spec/fixtures/cassettes/api_particulier/success/composition_familiale.yml index 7732aaf4d..22497f8a6 100644 --- a/spec/fixtures/cassettes/api_particulier/success/composition_familiale.yml +++ b/spec/fixtures/cassettes/api_particulier/success/composition_familiale.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://particulier-test.api.gouv.fr/api/v2/composition-familiale?codePostal=92110&numeroAllocataire=5843972 + uri: https://particulier.api.gouv.fr/api/v2/composition-familiale?codePostal=92110&numeroAllocataire=5843972 body: encoding: US-ASCII string: '' diff --git a/spec/fixtures/cassettes/api_particulier/success/composition_familiale_invalid.yml b/spec/fixtures/cassettes/api_particulier/success/composition_familiale_invalid.yml index e562a0871..49b3f8ccf 100644 --- a/spec/fixtures/cassettes/api_particulier/success/composition_familiale_invalid.yml +++ b/spec/fixtures/cassettes/api_particulier/success/composition_familiale_invalid.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: https://particulier-test.api.gouv.fr/api/v2/composition-familiale?codePostal=92110&numeroAllocataire=5843972 + uri: https://particulier.api.gouv.fr/api/v2/composition-familiale?codePostal=92110&numeroAllocataire=5843972 body: encoding: US-ASCII string: '' diff --git a/spec/lib/api_particulier/cnaf_adapter_spec.rb b/spec/lib/api_particulier/cnaf_adapter_spec.rb index 362a0c5f5..bd55a2a5f 100644 --- a/spec/lib/api_particulier/cnaf_adapter_spec.rb +++ b/spec/lib/api_particulier/cnaf_adapter_spec.rb @@ -1,7 +1,7 @@ -describe APIParticulier::CNAFAdapter do +describe APIParticulier::CnafAdapter do let(:adapter) { described_class.new(api_particulier_token, numero_allocataire, code_postal, requested_sources) } - before { stub_const("API_PARTICULIER_URL", "https://particulier-test.api.gouv.fr/api") } + before { stub_const("API_PARTICULIER_URL", "https://particulier.api.gouv.fr/api") } describe '#to_params' do let(:api_particulier_token) { '29eb50b65f64e8e00c0847a8bbcbd150e1f847' } @@ -63,7 +63,7 @@ describe APIParticulier::CNAFAdapter do context 'when no sources is requested' do let(:requested_sources) { {} } - it { expect { subject }.to raise_error(APIParticulier::CNAFAdapter::InvalidSchemaError) } + it { expect { subject }.to raise_error(APIParticulier::CnafAdapter::InvalidSchemaError) } 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 33e0c5208..9619732b5 100644 --- a/spec/lib/api_particulier/services/sources_service_spec.rb +++ b/spec/lib/api_particulier/services/sources_service_spec.rb @@ -31,6 +31,12 @@ describe APIParticulier::Services::SourcesService do it { is_expected.to match(cnaf_allocataires_and_enfants) } end + + context 'when a procedure has an unknown scope' do + let(:api_particulier_scopes) { ['unknown_scope'] } + + it { is_expected.to match({}) } + end end describe '#sanitize' do diff --git a/spec/models/champs/cnaf_champ_spec.rb b/spec/models/champs/cnaf_champ_spec.rb new file mode 100644 index 000000000..405c820a2 --- /dev/null +++ b/spec/models/champs/cnaf_champ_spec.rb @@ -0,0 +1,103 @@ +describe Champs::CnafChamp, type: :model do + let(:champ) { described_class.new } + + describe 'numero_allocataire and code_postal' do + before do + champ.numero_allocataire = '1234567' + champ.code_postal = '12345' + end + + it 'saves numero_allocataire and code_postal' do + expect(champ.numero_allocataire).to eq('1234567') + expect(champ.code_postal).to eq('12345') + end + end + + describe 'external_id' do + context 'when only one data is given' do + before do + champ.numero_allocataire = '1234567' + 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.numero_allocataire = '1234567' + champ.code_postal = '12345' + champ.save + end + + it { expect(JSON.parse(champ.external_id)).to eq({ "code_postal" => "12345", "numero_allocataire" => "1234567" }) } + end + end + + describe '#validate' do + let(:numero_allocataire) { '1234567' } + let(:code_postal) { '12345' } + let(:champ) { described_class.new(dossier: create(:dossier), type_de_champ: create(:type_de_champ_cnaf)) } + let(:validation_context) { :create } + + subject { champ.valid?(validation_context) } + + before do + champ.numero_allocataire = numero_allocataire + champ.code_postal = code_postal + end + + context 'when numero_allocataire and code_postal are valids' do + it { is_expected.to be true } + end + + context 'when numero_allocataire and code_postal are nil' do + let(:numero_allocataire) { nil } + let(:code_postal) { nil } + + it { is_expected.to be true } + end + + context 'when only code_postal is nil' do + let(:code_postal) { nil } + + it do + is_expected.to be false + expect(champ.errors.full_messages).to eq(["Code postal doit posséder 5 caractères"]) + end + end + + context 'when only numero_allocataire is nil' do + let(:numero_allocataire) { nil } + + it do + is_expected.to be false + expect(champ.errors.full_messages).to eq(["Numero allocataire doit être composé au maximum de 7 chiffres"]) + end + end + + context 'when numero_allocataire is invalid' do + let(:numero_allocataire) { '123456a' } + + it do + is_expected.to be false + expect(champ.errors.full_messages).to eq(["Numero allocataire doit être composé au maximum de 7 chiffres"]) + end + + context 'and the validation_context is :brouillon' do + let(:validation_context) { :brouillon } + + it { is_expected.to be true } + end + end + + context 'when code_postal is invalid' do + let(:code_postal) { '123456' } + + it do + is_expected.to be false + expect(champ.errors.full_messages).to eq(["Code postal doit posséder 5 caractères"]) + end + end + end +end diff --git a/spec/services/procedure_export_service_spec.rb b/spec/services/procedure_export_service_spec.rb index bca98ba9c..6888e77e1 100644 --- a/spec/services/procedure_export_service_spec.rb +++ b/spec/services/procedure_export_service_spec.rb @@ -77,6 +77,7 @@ describe ProcedureExportService do "titre_identite", "iban", "annuaire_education", + "cnaf", "text" ] end @@ -164,6 +165,7 @@ describe ProcedureExportService do "titre_identite", "iban", "annuaire_education", + "cnaf", "text" ] end @@ -247,6 +249,7 @@ describe ProcedureExportService do "titre_identite", "iban", "annuaire_education", + "cnaf", "text" ] end diff --git a/spec/support/feature_helpers.rb b/spec/support/feature_helpers.rb index 5b63e9386..371a906e8 100644 --- a/spec/support/feature_helpers.rb +++ b/spec/support/feature_helpers.rb @@ -142,6 +142,13 @@ module FeatureHelpers have_css("##{form_id_for(libelle)}[value=\"#{with}\"]") end + def log_out + click_button(title: 'Mon compte') + click_on 'Se déconnecter' + + expect(page).to have_current_path(root_path) + end + # Keep the brower window open after a test success of failure, to # allow inspecting the page or the console. #