diff --git a/app/services/api_recherche_entreprises_service.rb b/app/services/api_recherche_entreprises_service.rb new file mode 100644 index 000000000..f40287907 --- /dev/null +++ b/app/services/api_recherche_entreprises_service.rb @@ -0,0 +1,35 @@ +class APIRechercheEntreprisesService + include Dry::Monads[:result] + + def self.collectivite_territoriale?(siret:) + response = APIRechercheEntreprisesService.new.call(siret:) + + return false if response.failure? + + response.success&.dig(:complements, :collectivite_territoriale).present? + end + + def call(siret:) + result = API::Client.new.(url: "#{url}?q=#{siret}") + + return result if result.failure? + + body = result.success.body + + return Success(nil) if body[:results].empty? + + # the api returns the matching structure in the first element if it exists + structure = body[:results][0] + + # safety check : the api does fuzzy matching, so we need to check that the siret matches + return Failure() if structure[:matching_etablissements].all? { _1[:siret] != siret } + + Success(structure) + end + + private + + def url + "#{API_RECHERCHE_ENTREPRISE_URL}/search" + end +end diff --git a/config/initializers/02_urls.rb b/config/initializers/02_urls.rb index 81563ec89..0a7b5a81f 100644 --- a/config/initializers/02_urls.rb +++ b/config/initializers/02_urls.rb @@ -8,6 +8,7 @@ API_PARTICULIER_URL = ENV.fetch("API_PARTICULIER_URL", "https://particulier.api. API_TCHAP_URL = ENV.fetch("API_TCHAP_URL", "https://matrix.agent.tchap.gouv.fr/_matrix/identity/api/v1") API_COJO_URL = ENV.fetch("API_COJO_URL", nil) API_RNF_URL = ENV.fetch("API_RNF_URL", "https://rnf.dso.numerique-interieur.com") +API_RECHERCHE_ENTREPRISE_URL = ENV.fetch("API_RECHERCHE_ENTREPRISE_URL", "https://recherche-entreprises.api.gouv.fr") HELPSCOUT_API_URL = ENV.fetch("HELPSCOUT_API_URL", "https://api.helpscout.net/v2") SENDINBLUE_API_URL = ENV.fetch("SENDINBLUE_API_URL", "https://in-automate.sendinblue.com/api/v2") SENDINBLUE_API_V3_URL = ENV.fetch("SENDINBLUE_API_V3_URL", "https://api.sendinblue.com/v3") diff --git a/spec/fixtures/files/api_recherche_entreprises/col_ter_20006541500016.json b/spec/fixtures/files/api_recherche_entreprises/col_ter_20006541500016.json new file mode 100644 index 000000000..766e61848 --- /dev/null +++ b/spec/fixtures/files/api_recherche_entreprises/col_ter_20006541500016.json @@ -0,0 +1,640 @@ +{ + "results": [ + { + "siren": "200065415", + "nom_complet": "COMMUNE DE LA HAGUE", + "nom_raison_sociale": "COMMUNE DE LA HAGUE", + "sigle": null, + "nombre_etablissements": 51, + "nombre_etablissements_ouverts": 48, + "siege": { + "activite_principale": "84.11Z", + "activite_principale_registre_metier": null, + "annee_tranche_effectif_salarie": "2021", + "adresse": "8 RUE DES TOHAGUES 50440 LA HAGUE", + "caractere_employeur": null, + "cedex": null, + "code_pays_etranger": null, + "code_postal": "50440", + "commune": "50041", + "complement_adresse": null, + "coordonnees": "49.66215,-1.830805", + "date_creation": "2017-01-01", + "date_debut_activite": "2017-01-01", + "date_mise_a_jour": "2023-11-27T13:18:35", + "departement": "50", + "distribution_speciale": null, + "est_siege": true, + "etat_administratif": "A", + "geo_adresse": "8 Rue des Tohagues 50440 La Hague", + "geo_id": "50041_0120_00008", + "indice_repetition": null, + "latitude": "49.66215", + "libelle_cedex": null, + "libelle_commune": "LA HAGUE", + "libelle_commune_etranger": null, + "libelle_pays_etranger": null, + "libelle_voie": "DES TOHAGUES", + "liste_enseignes": [ + "MAIRIE" + ], + "liste_finess": null, + "liste_id_bio": null, + "liste_idcc": [ + "5021" + ], + "liste_id_organisme_formation": null, + "liste_rge": null, + "liste_uai": null, + "longitude": "-1.830805", + "nom_commercial": null, + "numero_voie": "8", + "region": "28", + "siret": "20006541500016", + "tranche_effectif_salarie": "32", + "type_voie": "RUE" + }, + "activite_principale": "84.11Z", + "categorie_entreprise": "ETI", + "caractere_employeur": null, + "annee_categorie_entreprise": "2021", + "date_creation": "2017-01-01", + "date_mise_a_jour": "2023-11-30T10:17:45", + "dirigeants": [], + "etat_administratif": "A", + "nature_juridique": "7210", + "section_activite_principale": "O", + "tranche_effectif_salarie": "32", + "annee_tranche_effectif_salarie": "2021", + "statut_diffusion": "O", + "matching_etablissements": [ + { + "activite_principale": "84.11Z", + "annee_tranche_effectif_salarie": "2021", + "adresse": "8 RUE DES TOHAGUES 50440 LA HAGUE", + "caractere_employeur": null, + "code_postal": "50440", + "commune": "50041", + "date_creation": "2017-01-01", + "date_debut_activite": "2017-01-01", + "est_siege": true, + "etat_administratif": "A", + "geo_id": "50041_0120_00008", + "latitude": "49.66215", + "libelle_commune": "LA HAGUE", + "liste_enseignes": [ + "MAIRIE" + ], + "liste_finess": null, + "liste_id_bio": null, + "liste_idcc": [ + "5021" + ], + "liste_id_organisme_formation": null, + "liste_rge": null, + "liste_uai": null, + "longitude": "-1.830805", + "nom_commercial": null, + "region": "28", + "siret": "20006541500016", + "tranche_effectif_salarie": "32" + } + ], + "finances": {}, + "complements": { + "collectivite_territoriale": { + "code": "50041", + "code_insee": "50041", + "elus": [ + { + "nom": "ADOUE", + "prenoms": "Chantal", + "annee_de_naissance": "19/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "ALLENO", + "prenoms": "Gwladys", + "annee_de_naissance": "04/0", + "fonction": "8ème adjoint au Maire", + "sexe": "F" + }, + { + "nom": "BEAUMONT", + "prenoms": "Monique", + "annee_de_naissance": "29/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "BECQUET", + "prenoms": "Dominique", + "annee_de_naissance": "17/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "BEDEL", + "prenoms": "Pauline", + "annee_de_naissance": "29/0", + "fonction": "Maire délégué", + "sexe": "F" + }, + { + "nom": "BELHOMME", + "prenoms": "Dominique", + "annee_de_naissance": "12/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "BELHOMME", + "prenoms": "Jérôme", + "annee_de_naissance": "14/0", + "fonction": "1er adjoint au Maire", + "sexe": "M" + }, + { + "nom": "BELHOMME", + "prenoms": "Jérôme", + "annee_de_naissance": "14/0", + "fonction": "Maire délégué", + "sexe": "M" + }, + { + "nom": "BONNISSENT", + "prenoms": "Marie-Suzanne", + "annee_de_naissance": "05/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "CANOVILLE", + "prenoms": "Laurent", + "annee_de_naissance": "29/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "CERVANTÈS", + "prenoms": "Simon", + "annee_de_naissance": "06/0", + "fonction": "Maire délégué", + "sexe": "M" + }, + { + "nom": "CHARDOT", + "prenoms": "Mélanie", + "annee_de_naissance": "12/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "CHARLES", + "prenoms": "Véronique", + "annee_de_naissance": "04/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "COLLET", + "prenoms": "Christian", + "annee_de_naissance": "20/1", + "fonction": null, + "sexe": "M" + }, + { + "nom": "CRANOIS", + "prenoms": "Louis", + "annee_de_naissance": "16/0", + "fonction": "Maire délégué", + "sexe": "M" + }, + { + "nom": "DALMONT", + "prenoms": "Hubert", + "annee_de_naissance": "29/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "DAMOURETTE", + "prenoms": "Etienne", + "annee_de_naissance": "02/1", + "fonction": null, + "sexe": "M" + }, + { + "nom": "DELACOUR", + "prenoms": "Thérèse", + "annee_de_naissance": "31/0", + "fonction": "Maire délégué", + "sexe": "F" + }, + { + "nom": "DESBOIS", + "prenoms": "Noëmie", + "annee_de_naissance": "14/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "DIGARD", + "prenoms": "Antoine", + "annee_de_naissance": "03/0", + "fonction": "3ème adjoint au Maire", + "sexe": "M" + }, + { + "nom": "DIGARD", + "prenoms": "Antoine", + "annee_de_naissance": "03/0", + "fonction": "Maire délégué", + "sexe": "M" + }, + { + "nom": "DUBOST", + "prenoms": "Hubert", + "annee_de_naissance": "17/1", + "fonction": "Maire délégué", + "sexe": "M" + }, + { + "nom": "DUBOST", + "prenoms": "Nathalie", + "annee_de_naissance": "07/0", + "fonction": "4ème adjoint au Maire", + "sexe": "F" + }, + { + "nom": "FLEURY", + "prenoms": "Jean-Marie", + "annee_de_naissance": "21/0", + "fonction": "11ème adjoint au Maire", + "sexe": "M" + }, + { + "nom": "FRACHET", + "prenoms": "Nadine", + "annee_de_naissance": "26/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "FRIGOUT", + "prenoms": "Jean-Marc", + "annee_de_naissance": "20/1", + "fonction": null, + "sexe": "M" + }, + { + "nom": "GASNIER", + "prenoms": "Philippe", + "annee_de_naissance": "22/1", + "fonction": "5ème adjoint au Maire", + "sexe": "M" + }, + { + "nom": "GASNIER", + "prenoms": "Philippe", + "annee_de_naissance": "22/1", + "fonction": "Maire délégué", + "sexe": "M" + }, + { + "nom": "GAUMAIN", + "prenoms": "Mathieu", + "annee_de_naissance": "06/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "GOACHET", + "prenoms": "Joseph", + "annee_de_naissance": "27/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "GROF", + "prenoms": "Béatrice", + "annee_de_naissance": "17/0", + "fonction": "2ème adjoint au Maire", + "sexe": "F" + }, + { + "nom": "GUILLEMETTE", + "prenoms": "Nathalie", + "annee_de_naissance": "30/0", + "fonction": "6ème adjoint au Maire", + "sexe": "F" + }, + { + "nom": "HAMELIN", + "prenoms": "Magali", + "annee_de_naissance": "16/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "HENRY", + "prenoms": "Claude", + "annee_de_naissance": "30/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "JACQUET-ROCQUET", + "prenoms": "Sophie", + "annee_de_naissance": "26/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "JOURDAIN", + "prenoms": "Patrick", + "annee_de_naissance": "28/0", + "fonction": "7ème adjoint au Maire", + "sexe": "M" + }, + { + "nom": "JOURDAIN", + "prenoms": "Patrick", + "annee_de_naissance": "28/0", + "fonction": "Maire délégué", + "sexe": "M" + }, + { + "nom": "JUMELIN", + "prenoms": "Pascale", + "annee_de_naissance": "24/0", + "fonction": "Maire délégué", + "sexe": "F" + }, + { + "nom": "LADVENU", + "prenoms": "Nathalie", + "annee_de_naissance": "14/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "LAGALLE", + "prenoms": "Marie-Laure", + "annee_de_naissance": "23/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "LAPPREND", + "prenoms": "Marie", + "annee_de_naissance": "08/1", + "fonction": "10ème adjoint au Maire", + "sexe": "F" + }, + { + "nom": "LARGERIE", + "prenoms": "Anne", + "annee_de_naissance": "03/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "LAVENU", + "prenoms": "Patrick", + "annee_de_naissance": "01/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "LECOSTEY", + "prenoms": "Fabrice", + "annee_de_naissance": "25/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "LECOSTEY", + "prenoms": "Jean", + "annee_de_naissance": "14/1", + "fonction": null, + "sexe": "M" + }, + { + "nom": "LEDAUPHIN", + "prenoms": "Nathalie", + "annee_de_naissance": "05/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "LEFRÉTEUR", + "prenoms": "Emmanuel", + "annee_de_naissance": "10/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "LEGELEUX", + "prenoms": "Yann", + "annee_de_naissance": "20/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "LELONG", + "prenoms": "Nadine", + "annee_de_naissance": "29/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "LELONG", + "prenoms": "Sébastien", + "annee_de_naissance": "15/0", + "fonction": "Maire délégué", + "sexe": "M" + }, + { + "nom": "LERENDU", + "prenoms": "Patrick", + "annee_de_naissance": "08/1", + "fonction": "Maire délégué", + "sexe": "M" + }, + { + "nom": "LESEIGNEUR-COURVAL", + "prenoms": "Thérèse", + "annee_de_naissance": "22/0", + "fonction": "Maire délégué", + "sexe": "F" + }, + { + "nom": "LETOURNEUR", + "prenoms": "Bruno", + "annee_de_naissance": "21/0", + "fonction": "Maire délégué", + "sexe": "M" + }, + { + "nom": "LUPO", + "prenoms": "Antoine", + "annee_de_naissance": "16/1", + "fonction": "Maire délégué", + "sexe": "M" + }, + { + "nom": "MAHIER", + "prenoms": "Manuela", + "annee_de_naissance": "30/1", + "fonction": "Maire", + "sexe": "F" + }, + { + "nom": "MAUGÉ", + "prenoms": "Caroline", + "annee_de_naissance": "13/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "MERCIER", + "prenoms": "Philippe", + "annee_de_naissance": "06/0", + "fonction": "9ème adjoint au Maire", + "sexe": "M" + }, + { + "nom": "MERCIER", + "prenoms": "Philippe", + "annee_de_naissance": "06/0", + "fonction": "Maire délégué", + "sexe": "M" + }, + { + "nom": "MONHUREL", + "prenoms": "Pascal", + "annee_de_naissance": "23/0", + "fonction": "Maire délégué", + "sexe": "M" + }, + { + "nom": "NICOLLE", + "prenoms": "Stéphanie", + "annee_de_naissance": "21/1", + "fonction": null, + "sexe": "F" + }, + { + "nom": "NOEL", + "prenoms": "Nelly", + "annee_de_naissance": "24/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "PELLERIN", + "prenoms": "Eric", + "annee_de_naissance": "17/0", + "fonction": "Maire délégué", + "sexe": "M" + }, + { + "nom": "PERROTTE", + "prenoms": "Thomas", + "annee_de_naissance": "15/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "RENOUF", + "prenoms": "Jean-Luc", + "annee_de_naissance": "17/0", + "fonction": "Maire délégué", + "sexe": "M" + }, + { + "nom": "ROUCAN", + "prenoms": "Robert", + "annee_de_naissance": "27/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "SANSON", + "prenoms": "Fabienne", + "annee_de_naissance": "10/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "SANSON", + "prenoms": "Noël", + "annee_de_naissance": "25/1", + "fonction": null, + "sexe": "M" + }, + { + "nom": "SEBIRE", + "prenoms": "Marine", + "annee_de_naissance": "07/0", + "fonction": null, + "sexe": "F" + }, + { + "nom": "TARDIF", + "prenoms": "Pierre", + "annee_de_naissance": "13/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "TESTELIN", + "prenoms": "Sébastien", + "annee_de_naissance": "03/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "TOLLEMER", + "prenoms": "Pierre", + "annee_de_naissance": "05/0", + "fonction": null, + "sexe": "M" + }, + { + "nom": "TRAVERT", + "prenoms": "Laurent", + "annee_de_naissance": "24/0", + "fonction": null, + "sexe": "M" + } + ], + "niveau": "commune" + }, + "convention_collective_renseignee": true, + "egapro_renseignee": false, + "est_association": false, + "est_bio": false, + "est_entrepreneur_individuel": false, + "est_entrepreneur_spectacle": true, + "est_ess": false, + "est_finess": false, + "est_organisme_formation": false, + "est_qualiopi": false, + "liste_id_organisme_formation": null, + "est_rge": false, + "est_service_public": true, + "est_societe_mission": false, + "est_uai": true, + "identifiant_association": null, + "statut_entrepreneur_spectacle": "valide" + } + } + ], + "total_results": 1, + "page": 1, + "per_page": 10, + "total_pages": 1 +} diff --git a/spec/fixtures/files/api_recherche_entreprises/dinum_13002526500013.json b/spec/fixtures/files/api_recherche_entreprises/dinum_13002526500013.json new file mode 100644 index 000000000..42d46bf68 --- /dev/null +++ b/spec/fixtures/files/api_recherche_entreprises/dinum_13002526500013.json @@ -0,0 +1,122 @@ +{ + "results": [ + { + "siren": "130025265", + "nom_complet": "DIRECTION INTERMINISTERIELLE DU NUMERIQUE (DINUM)", + "nom_raison_sociale": "DIRECTION INTERMINISTERIELLE DU NUMERIQUE", + "sigle": "DINUM", + "nombre_etablissements": 1, + "nombre_etablissements_ouverts": 1, + "siege": { + "activite_principale": "84.11Z", + "activite_principale_registre_metier": null, + "annee_tranche_effectif_salarie": "2021", + "adresse": "20 AV DE SEGUR 75007 PARIS 7", + "caractere_employeur": null, + "cedex": null, + "code_pays_etranger": null, + "code_postal": "75007", + "commune": "75107", + "complement_adresse": null, + "coordonnees": "48.850699,2.308628", + "date_creation": "2017-05-24", + "date_debut_activite": "2017-05-24", + "date_mise_a_jour": "2023-11-27T13:16:04", + "departement": "75", + "distribution_speciale": null, + "est_siege": true, + "etat_administratif": "A", + "geo_adresse": "20 Avenue de Ségur 75007 Paris", + "geo_id": "75107_8909_00020", + "indice_repetition": null, + "latitude": "48.850699", + "libelle_cedex": null, + "libelle_commune": "PARIS 7", + "libelle_commune_etranger": null, + "libelle_pays_etranger": null, + "libelle_voie": "DE SEGUR", + "liste_enseignes": null, + "liste_finess": null, + "liste_id_bio": null, + "liste_idcc": null, + "liste_id_organisme_formation": null, + "liste_rge": null, + "liste_uai": null, + "longitude": "2.308628", + "nom_commercial": null, + "numero_voie": "20", + "region": "11", + "siret": "13002526500013", + "tranche_effectif_salarie": "22", + "type_voie": "AV" + }, + "activite_principale": "84.11Z", + "categorie_entreprise": "PME", + "caractere_employeur": null, + "annee_categorie_entreprise": "2021", + "date_creation": "2017-05-24", + "date_mise_a_jour": "2023-11-30T10:17:13", + "dirigeants": [], + "etat_administratif": "A", + "nature_juridique": "7120", + "section_activite_principale": "O", + "tranche_effectif_salarie": "22", + "annee_tranche_effectif_salarie": "2021", + "statut_diffusion": "O", + "matching_etablissements": [ + { + "activite_principale": "84.11Z", + "annee_tranche_effectif_salarie": "2021", + "adresse": "20 AV DE SEGUR 75007 PARIS 7", + "caractere_employeur": null, + "code_postal": "75007", + "commune": "75107", + "date_creation": "2017-05-24", + "date_debut_activite": "2017-05-24", + "est_siege": true, + "etat_administratif": "A", + "geo_id": "75107_8909_00020", + "latitude": "48.850699", + "libelle_commune": "PARIS 7", + "liste_enseignes": null, + "liste_finess": null, + "liste_id_bio": null, + "liste_idcc": null, + "liste_id_organisme_formation": null, + "liste_rge": null, + "liste_uai": null, + "longitude": "2.308628", + "nom_commercial": null, + "region": "11", + "siret": "13002526500013", + "tranche_effectif_salarie": "22" + } + ], + "finances": {}, + "complements": { + "collectivite_territoriale": null, + "convention_collective_renseignee": false, + "egapro_renseignee": false, + "est_association": false, + "est_bio": false, + "est_entrepreneur_individuel": false, + "est_entrepreneur_spectacle": false, + "est_ess": false, + "est_finess": false, + "est_organisme_formation": false, + "est_qualiopi": false, + "liste_id_organisme_formation": null, + "est_rge": false, + "est_service_public": true, + "est_societe_mission": false, + "est_uai": false, + "identifiant_association": null, + "statut_entrepreneur_spectacle": null + } + } + ], + "total_results": 1, + "page": 1, + "per_page": 10, + "total_pages": 1 +} diff --git a/spec/services/api_recherche_entreprise_service_spec.rb b/spec/services/api_recherche_entreprise_service_spec.rb new file mode 100644 index 000000000..48a33a3db --- /dev/null +++ b/spec/services/api_recherche_entreprise_service_spec.rb @@ -0,0 +1,60 @@ +describe 'APIRechercheEntreprisesService' do + include Dry::Monads[:result] + OK = Data.define(:body, :response) + + def load_json(file_name) + Rails.root.join("spec/fixtures/files/api_recherche_entreprises/#{file_name}.json") + .then { File.read(_1) } + .then { JSON.parse(_1).with_indifferent_access } + end + + let(:col_ter_json) { load_json('col_ter_20006541500016') } + let(:dinum_json) { load_json('dinum_13002526500013') } + + describe '.collectivite_territoriale' do + let(:client_response) { Success(OK[json_response, '']) } + + subject { APIRechercheEntreprisesService.collectivite_territoriale?(siret:) } + + before { expect_any_instance_of(API::Client).to receive(:call).and_return(client_response) } + + context 'when the api returns some results' do + let(:json_response) { col_ter_json } + + context 'and the siret match' do + context 'and the structure is a collectivite territoriale' do + let(:siret) { '20006541500016' } + + it { is_expected.to be true } + end + + context 'and the structure is not a collectivite territoriale' do + let(:json_response) { dinum_json } + let(:siret) { '13002526500013' } + + it { is_expected.to be false } + end + end + + context 'and the siret does not match' do + let(:siret) { '20006541500666' } + + it { is_expected.to be false } + end + end + + context 'when the api returns no result' do + let(:json_response) { { results: [] } } + let(:siret) { '20006541500016' } + + it { is_expected.to be false } + end + + context 'when the api returns an error' do + let(:client_response) { Failure() } + let(:siret) { '20006541500016' } + + it { is_expected.to be false } + end + end +end