diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index 7a933303c..6b0b895f4 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -686,10 +686,24 @@ enum DossierState { sans_suite } +type Effectif { + """ + Année de l'effectif mensuel + """ + annee: String! + + """ + Mois de l'effectif mensuel + """ + mois: String! + nb: Float! +} + type Entreprise { capitalSocial: BigInt! codeEffectifEntreprise: String! dateCreation: ISO8601Date! + effectifs: [Effectif!]! formeJuridique: String! formeJuridiqueCode: String! inlineAdresse: String! diff --git a/app/graphql/types/personne_morale_type.rb b/app/graphql/types/personne_morale_type.rb index 357805f6c..5bc5f176b 100644 --- a/app/graphql/types/personne_morale_type.rb +++ b/app/graphql/types/personne_morale_type.rb @@ -1,6 +1,12 @@ module Types class PersonneMoraleType < Types::BaseObject class EntrepriseType < Types::BaseObject + class EffectifType < Types::BaseObject + field :mois, String, null: false, description: "Mois de l'effectif mensuel" + field :annee, String, null: false, description: "Année de l'effectif mensuel" + field :nb, Float, null: false + end + field :siren, String, null: false field :capital_social, GraphQL::Types::BigInt, null: false field :numero_tva_intracommunautaire, String, null: false @@ -10,10 +16,23 @@ module Types field :raison_sociale, String, null: false field :siret_siege_social, String, null: false field :code_effectif_entreprise, String, null: false + field :effectifs, [EffectifType], null: false field :date_creation, GraphQL::Types::ISO8601Date, null: false field :nom, String, null: false field :prenom, String, null: false field :inline_adresse, String, null: false + + def effectifs + if object.effectif_mensuel.present? + [ + { + mois: object.effectif_mois, + annee: object.effectif_annee, + nb: object.effectif_mensuel + } + ] + end + end end class AssociationType < Types::BaseObject diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index acdabec8a..e7f399a48 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -130,6 +130,14 @@ module ApplicationHelper datetime.present? ? I18n.l(datetime) : '' end + def try_format_mois_effectif(etablissement) + if etablissement.entreprise_effectif_mois.present? && etablissement.entreprise_effectif_annee.present? + [etablissement.entreprise_effectif_mois, etablissement.entreprise_effectif_annee].join('/') + else + '' + end + end + def dismiss_outdated_browser_banner cookies[:dismissed_outdated_browser_banner] = { value: 'true', diff --git a/app/javascript/new_design/dossiers/auto-uploads-controllers.js b/app/javascript/new_design/dossiers/auto-uploads-controllers.js index ad68b081d..90348566b 100644 --- a/app/javascript/new_design/dossiers/auto-uploads-controllers.js +++ b/app/javascript/new_design/dossiers/auto-uploads-controllers.js @@ -1,7 +1,13 @@ import Rails from '@rails/ujs'; import AutoUploadController from './auto-upload-controller.js'; +import { fire } from '@utils'; import { FAILURE_CONNECTIVITY } from '../../shared/activestorage/file-upload-error'; +// +// DEBUG +// +const originalImpl = FileReader.prototype.addEventListener; + // Manage multiple concurrent uploads. // // When the first upload starts, all the form "Submit" buttons are disabled. @@ -36,6 +42,25 @@ export default class AutoUploadsControllers { .querySelectorAll('button[type=submit]') .forEach(submitButton => Rails.disableElement(submitButton)); } + + // + // DEBUG: hook into FileReader onload event + // + if (FileReader.prototype.addEventListener === originalImpl) { + FileReader.prototype.addEventListener = function() { + // When DirectUploads attempts to add an event listener for "error", + // also insert a custom event listener of our that will report errors to Sentry. + if (arguments[0] == 'error') { + let handler = event => { + let message = `FileReader ${event.target.error.name}: ${event.target.error.message}`; + fire(document, 'sentry:capture-exception', new Error(message)); + }; + originalImpl.apply(this, ['error', handler]); + } + // Add the originally requested event listener + return originalImpl.apply(this, arguments); + }; + } } _decrementInFlightUploads(form) { @@ -48,5 +73,12 @@ export default class AutoUploadsControllers { .querySelectorAll('button[type=submit]') .forEach(submitButton => Rails.enableElement(submitButton)); } + + // + // DEBUG: remove the FileReader hook we set before. + // + if (this.inFlightUploadsCount == 0) { + FileReader.prototype.addEventListener = originalImpl; + } } } diff --git a/app/jobs/expired_dossiers_deletion_job.rb b/app/jobs/expired_dossiers_deletion_job.rb index 6adb8c39e..65c80d23d 100644 --- a/app/jobs/expired_dossiers_deletion_job.rb +++ b/app/jobs/expired_dossiers_deletion_job.rb @@ -3,5 +3,6 @@ class ExpiredDossiersDeletionJob < CronJob def perform(*args) ExpiredDossiersDeletionService.process_expired_dossiers_brouillon + ExpiredDossiersDeletionService.process_expired_dossiers_en_construction end end diff --git a/app/lib/api_entreprise/api.rb b/app/lib/api_entreprise/api.rb index af5b6acef..8c5c66eb4 100644 --- a/app/lib/api_entreprise/api.rb +++ b/app/lib/api_entreprise/api.rb @@ -3,6 +3,7 @@ class ApiEntreprise::API ETABLISSEMENT_RESOURCE_NAME = "etablissements" EXERCICES_RESOURCE_NAME = "exercices" RNA_RESOURCE_NAME = "associations" + EFFECTIFS_RESOURCE_NAME = "effectifs_mensuels_acoss_covid" TIMEOUT = 15 @@ -28,6 +29,11 @@ class ApiEntreprise::API call(RNA_RESOURCE_NAME, siret, procedure_id) end + def self.effectifs(siren, procedure_id, annee, mois) + endpoint = [EFFECTIFS_RESOURCE_NAME, annee, mois, "entreprise"].join('/') + call(endpoint, siren, procedure_id) + end + private def self.call(resource_name, siret_or_siren, procedure_id) diff --git a/app/lib/api_entreprise/effectifs_adapter.rb b/app/lib/api_entreprise/effectifs_adapter.rb new file mode 100644 index 000000000..ae768a3f9 --- /dev/null +++ b/app/lib/api_entreprise/effectifs_adapter.rb @@ -0,0 +1,26 @@ +class ApiEntreprise::EffectifsAdapter < ApiEntreprise::Adapter + def initialize(siren, procedure_id, annee, mois) + @siren = siren + @procedure_id = procedure_id + @annee = annee + @mois = mois + end + + private + + def get_resource + ApiEntreprise::API.effectifs(@siren, @procedure_id, @annee, @mois) + end + + def process_params + if data_source[:effectifs_mensuels].present? + { + entreprise_effectif_mensuel: data_source[:effectifs_mensuels], + entreprise_effectif_mois: @mois, + entreprise_effectif_annee: @annee + } + else + {} + end + end +end diff --git a/app/models/entreprise.rb b/app/models/entreprise.rb index 19cecd7e1..5fb6cf719 100644 --- a/app/models/entreprise.rb +++ b/app/models/entreprise.rb @@ -12,6 +12,9 @@ class Entreprise < Hashie::Dash property :raison_sociale property :siret_siege_social property :code_effectif_entreprise + property :effectif_mois + property :effectif_annee + property :effectif_mensuel property :date_creation property :nom property :prenom diff --git a/app/models/etablissement.rb b/app/models/etablissement.rb index 98584e672..4d3236e80 100644 --- a/app/models/etablissement.rb +++ b/app/models/etablissement.rb @@ -102,6 +102,9 @@ class Etablissement < ApplicationRecord raison_sociale: entreprise_raison_sociale, siret_siege_social: entreprise_siret_siege_social, code_effectif_entreprise: entreprise_code_effectif_entreprise, + effectif_mensuel: entreprise_effectif_mensuel, + effectif_mois: entreprise_effectif_mois, + effectif_annee: entreprise_effectif_annee, date_creation: entreprise_date_creation, nom: entreprise_nom, prenom: entreprise_prenom, diff --git a/app/serializers/entreprise_serializer.rb b/app/serializers/entreprise_serializer.rb index 3a52d7917..70437d8c8 100644 --- a/app/serializers/entreprise_serializer.rb +++ b/app/serializers/entreprise_serializer.rb @@ -8,6 +8,9 @@ class EntrepriseSerializer < ActiveModel::Serializer :raison_sociale, :siret_siege_social, :code_effectif_entreprise, + :effectif_mois, + :effectif_annee, + :effectif_mensuel, :date_creation, :nom, :prenom diff --git a/app/services/api_entreprise_service.rb b/app/services/api_entreprise_service.rb index 32fa8ada7..2b48acf8a 100644 --- a/app/services/api_entreprise_service.rb +++ b/app/services/api_entreprise_service.rb @@ -23,7 +23,27 @@ class ApiEntrepriseService rescue ApiEntreprise::API::RequestFailed end + begin + effectifs_params = ApiEntreprise::EffectifsAdapter.new(entreprise_params[:entreprise_siren], procedure_id, *get_current_valid_month_for_effectif).to_params + etablissement_params.merge!(effectifs_params) + rescue ApiEntreprise::API::RequestFailed + end + etablissement_params.merge(entreprise_params) end end + + private + + def self.get_current_valid_month_for_effectif + today = Date.today + date_update = Date.new(today.year, today.month, 15) + + if today >= date_update + [today.strftime("%Y"), today.strftime("%m")] + else + date = today - 1.month + [date.strftime("%Y"), date.strftime("%m")] + end + end end diff --git a/app/services/dossier_search_service.rb b/app/services/dossier_search_service.rb index 67308babb..d001772a2 100644 --- a/app/services/dossier_search_service.rb +++ b/app/services/dossier_search_service.rb @@ -62,8 +62,8 @@ class DossierSearchService def self.to_tsquery(search_terms) (search_terms || "") - .strip .gsub(/['?\\:&|!<>\(\)]/, "") # drop disallowed characters + .strip .split(/\s+/) # split words .map { |x| "#{x}:*" } # enable prefix matching .join(" & ") diff --git a/app/views/instructeurs/procedures/show.html.haml b/app/views/instructeurs/procedures/show.html.haml index ba85ed00e..9e4c01cbc 100644 --- a/app/views/instructeurs/procedures/show.html.haml +++ b/app/views/instructeurs/procedures/show.html.haml @@ -60,7 +60,7 @@ - if @statut == 'traites' %p.explication-onglet Les dossiers dans cet onglet sont terminés : ils ont été acceptés, refusés ou classés sans suite. - if @statut == 'tous' - %p.explication-onglet Tous les dossiers qui ont été déposés sur cette démarche, sans aucun filtre. + %p.explication-onglet Tous les dossiers qui ont été déposés sur cette démarche, quel que soit le statut. - if @statut == 'archives' %p.explication-onglet Les dossiers de cet onglet sont archivés : vous ne pouvez plus y répondre, et les demandeurs ne peuvent plus les modifier. .afficher-dossiers-supprimes diff --git a/app/views/shared/dossiers/_identite_entreprise.html.haml b/app/views/shared/dossiers/_identite_entreprise.html.haml index f1d18f84d..51a5dc51f 100644 --- a/app/views/shared/dossiers/_identite_entreprise.html.haml +++ b/app/views/shared/dossiers/_identite_entreprise.html.haml @@ -27,6 +27,12 @@ %tr %th.libelle Date de création : %td= try_format_date(etablissement.entreprise.date_creation) + - if profile == 'instructeur' + %tr + %th.libelle + Effectif mensuel + = try_format_mois_effectif(etablissement) + %td= etablissement.entreprise_effectif_mensuel %tr %th.libelle Effectif de l'organisation : %td= effectif(etablissement) diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index 0f48a942b..4c0a47886 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -17,6 +17,6 @@ Rails.application.config.content_security_policy do |policy| # pour détecter les erreurs lors de l'ajout d'une nouvelle brique externe durant le développement policy.report_uri "http://#{ENV['APP_HOST']}/csp/" # En développement, quand bin/webpack-dev-server est utilisé, on autorise les requêtes faites par le live-reload - policy.connect_src(*policy.connect_src, "ws://localhost:3035", "localhost:3035") + policy.connect_src(*policy.connect_src, "ws://localhost:3035", "http://localhost:3035") end end diff --git a/db/migrate/20200407135256_add_effectifs_mensuels_to_etablissements.rb b/db/migrate/20200407135256_add_effectifs_mensuels_to_etablissements.rb new file mode 100644 index 000000000..926a46c41 --- /dev/null +++ b/db/migrate/20200407135256_add_effectifs_mensuels_to_etablissements.rb @@ -0,0 +1,7 @@ +class AddEffectifsMensuelsToEtablissements < ActiveRecord::Migration[5.2] + def change + add_column :etablissements, :effectif_mois, :string + add_column :etablissements, :effectif_annee, :string + add_column :etablissements, :effectif_mensuel, :decimal + end +end diff --git a/db/migrate/20200421174642_rename_effectif_mensuel.rb b/db/migrate/20200421174642_rename_effectif_mensuel.rb new file mode 100644 index 000000000..9a92ad1aa --- /dev/null +++ b/db/migrate/20200421174642_rename_effectif_mensuel.rb @@ -0,0 +1,7 @@ +class RenameEffectifMensuel < ActiveRecord::Migration[5.2] + def change + rename_column :etablissements, :effectif_mensuel, :entreprise_effectif_mensuel + rename_column :etablissements, :effectif_mois, :entreprise_effectif_mois + rename_column :etablissements, :effectif_annee, :entreprise_effectif_annee + end +end diff --git a/db/schema.rb b/db/schema.rb index 4ea488b03..e7a2911e7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_04_09_075320) do +ActiveRecord::Schema.define(version: 2020_04_21_174642) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -310,6 +310,9 @@ ActiveRecord::Schema.define(version: 2020_04_09_075320) do t.datetime "created_at" t.datetime "updated_at" t.boolean "diffusable_commercialement" + t.string "entreprise_effectif_mois" + t.string "entreprise_effectif_annee" + t.decimal "entreprise_effectif_mensuel" t.index ["dossier_id"], name: "index_etablissements_on_dossier_id" end diff --git a/spec/controllers/api/v1/dossiers_controller_spec.rb b/spec/controllers/api/v1/dossiers_controller_spec.rb index cfbcc11c5..a329464ac 100644 --- a/spec/controllers/api/v1/dossiers_controller_spec.rb +++ b/spec/controllers/api/v1/dossiers_controller_spec.rb @@ -197,6 +197,9 @@ describe API::V1::DossiersController do :raison_sociale, :siret_siege_social, :code_effectif_entreprise, + :effectif_mois, + :effectif_annee, + :effectif_mensuel, :date_creation, :nom, :prenom diff --git a/spec/controllers/users/dossiers_controller_spec.rb b/spec/controllers/users/dossiers_controller_spec.rb index dfe54c93b..5779e099a 100644 --- a/spec/controllers/users/dossiers_controller_spec.rb +++ b/spec/controllers/users/dossiers_controller_spec.rb @@ -207,6 +207,11 @@ describe Users::DossiersController, type: :controller do let(:api_entreprise_status) { 200 } let(:api_entreprise_body) { File.read('spec/fixtures/files/api_entreprise/entreprises.json') } + let(:api_entreprise_effectifs_mensuels_status) { 200 } + let(:api_entreprise_effectifs_mensuels_body) { File.read('spec/fixtures/files/api_entreprise/effectifs.json') } + let(:annee) { "2020" } + let(:mois) { "02" } + let(:api_exercices_status) { 200 } let(:api_exercices_body) { File.read('spec/fixtures/files/api_entreprise/exercices.json') } @@ -222,12 +227,16 @@ describe Users::DossiersController, type: :controller do .to_return(status: api_exercices_status, body: api_exercices_body) stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/associations\/#{siret}?.*token=/) .to_return(status: api_association_status, body: api_association_body) + stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/effectifs_mensuels_acoss_covid\/#{annee}\/#{mois}\/entreprise\/#{siren}?.*token=/) + .to_return(body: api_entreprise_effectifs_mensuels_body, status: api_entreprise_effectifs_mensuels_status) end before do sign_in(user) stub_api_entreprise_requests end + before { Timecop.freeze(Time.zone.local(2020, 3, 14)) } + after { Timecop.return } subject! { post :update_siret, params: { id: dossier.id, user: { siret: params_siret } } } @@ -266,8 +275,7 @@ describe Users::DossiersController, type: :controller do end context 'with a valid SIRET' do - let(:params_siret) { '440 117 620 01530' } - + let(:params_siret) { '418 166 096 00051' } context 'When API-Entreprise is down' do let(:api_etablissement_status) { 502 } let(:api_body_status) { File.read('spec/fixtures/files/api_entreprise/exercices_unavailable.json') } @@ -319,6 +327,7 @@ describe Users::DossiersController, type: :controller do expect(dossier.etablissement.entreprise).to be_present expect(dossier.etablissement.exercices).to be_present expect(dossier.etablissement.association?).to be(true) + expect(dossier.etablissement.entreprise_effectif_mensuel).to be_present end end end diff --git a/spec/features/users/dossier_creation_spec.rb b/spec/features/users/dossier_creation_spec.rb index 6a1a357a8..5849cd1b4 100644 --- a/spec/features/users/dossier_creation_spec.rb +++ b/spec/features/users/dossier_creation_spec.rb @@ -1,6 +1,6 @@ feature 'Creating a new dossier:' do let(:user) { create(:user) } - let(:siret) { '40307130100044' } + let(:siret) { '41816609600051' } let(:siren) { siret[0...9] } context 'when the user is already signed in' do @@ -74,7 +74,11 @@ feature 'Creating a new dossier:' do .to_return(status: 200, body: File.read('spec/fixtures/files/api_entreprise/exercices.json')) stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/associations\/#{siret}?.*token=/) .to_return(status: 404, body: '') + stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/effectifs_mensuels_acoss_covid\/2020\/02\/entreprise\/#{siren}?.*token=/) + .to_return(status: 404, body: '') end + before { Timecop.freeze(Time.zone.local(2020, 3, 14)) } + after { Timecop.return } scenario 'the user can enter the SIRET of its etablissement and create a new draft' do visit commencer_path(path: procedure.path) diff --git a/spec/fixtures/files/api_entreprise/effectifs.json b/spec/fixtures/files/api_entreprise/effectifs.json new file mode 100644 index 000000000..133873115 --- /dev/null +++ b/spec/fixtures/files/api_entreprise/effectifs.json @@ -0,0 +1,6 @@ +{ + "siren": "418166096", + "annee": "2020", + "mois": "02", + "effectifs_mensuels": 100.5 +} diff --git a/spec/lib/api_entreprise/effectifs_adapter_spec.rb b/spec/lib/api_entreprise/effectifs_adapter_spec.rb new file mode 100644 index 000000000..205545247 --- /dev/null +++ b/spec/lib/api_entreprise/effectifs_adapter_spec.rb @@ -0,0 +1,26 @@ +describe ApiEntreprise::EffectifsAdapter do + let(:siren) { '418166096' } + let(:procedure_id) { 22 } + let(:annee) { "2020" } + let(:mois) { "02" } + let(:adapter) { described_class.new(siren, procedure_id, annee, mois) } + subject { adapter.to_params } + + before do + stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/effectifs_mensuels_acoss_covid\/#{annee}\/#{mois}\/entreprise\/#{siren}?.*token=/) + .to_return(body: body, status: status) + end + + context "when the SIREN is valid" do + let(:body) { File.read('spec/fixtures/files/api_entreprise/effectifs.json') } + let(:status) { 200 } + + it '#to_params class est une Hash ?' do + expect(subject).to be_an_instance_of(Hash) + end + + it "renvoie les effectifs du mois demandé" do + expect(subject[:entreprise_effectif_mensuel]).to eq(100.5) + end + end +end diff --git a/spec/services/api_entreprise_service_spec.rb b/spec/services/api_entreprise_service_spec.rb index 887333b33..ff6fec019 100644 --- a/spec/services/api_entreprise_service_spec.rb +++ b/spec/services/api_entreprise_service_spec.rb @@ -9,8 +9,13 @@ describe ApiEntrepriseService do .to_return(body: exercices_body, status: exercices_status) stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/associations\/.*token=/) .to_return(body: associations_body, status: associations_status) + stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/effectifs_mensuels_acoss_covid\/#{annee}\/#{mois}\/entreprise\/#{siren}?.*token=/) + .to_return(body: effectifs_mensuels_body, status: effectifs_mensuels_status) end + before { Timecop.freeze(Time.zone.local(2020, 3, 14)) } + after { Timecop.return } + let(:siren) { '418166096' } let(:siret) { '41816609600051' } let(:rna) { 'W595001988' } @@ -21,6 +26,12 @@ describe ApiEntrepriseService do let(:etablissements_status) { 200 } let(:etablissements_body) { File.read('spec/fixtures/files/api_entreprise/etablissements.json') } + let(:effectifs_mensuels_status) { 200 } + let(:effectifs_mensuels_body) { File.read('spec/fixtures/files/api_entreprise/effectifs.json') } + let(:annee) { "2020" } + let(:mois) { "02" } + let(:effectif_mensuel) { 100.5 } + let(:exercices_status) { 200 } let(:exercices_body) { File.read('spec/fixtures/files/api_entreprise/exercices.json') } @@ -35,6 +46,7 @@ describe ApiEntrepriseService do expect(result[:siret]).to eq(siret) expect(result[:association_rna]).to eq(rna) expect(result[:exercices_attributes]).to_not be_empty + expect(result[:entreprise_effectif_mensuel]).to eq(effectif_mensuel) end end diff --git a/spec/services/dossier_search_service_spec.rb b/spec/services/dossier_search_service_spec.rb index 950f5b918..16d613acc 100644 --- a/spec/services/dossier_search_service_spec.rb +++ b/spec/services/dossier_search_service_spec.rb @@ -198,5 +198,11 @@ describe DossierSearchService do it { expect(subject.size).to eq(1) } end + + describe 'search with a single forbidden character should not crash postgres' do + let(:terms) { '? OCTO' } + + it { expect(subject.size).to eq(3) } + end end end