diff --git a/Gemfile b/Gemfile index 81ac050d8..db2a7c5fd 100644 --- a/Gemfile +++ b/Gemfile @@ -29,6 +29,7 @@ gem 'flipper-active_record' gem 'flipper-ui' gem 'fog-openstack' gem 'font-awesome-rails' +gem 'geocoder' gem 'gon' gem 'graphiql-rails' gem 'graphql' @@ -49,6 +50,7 @@ gem 'openstack' gem 'pg' gem 'phonelib' gem 'prawn' # PDF Generation +gem 'prawn-svg' gem 'prawn_rails' gem 'premailer-rails' gem 'puma' # Use Puma as the app server @@ -64,7 +66,6 @@ gem 'rgeo-geojson' gem 'sanitize-url' gem 'sassc-rails' # Use SCSS for stylesheets gem 'scenic' -gem 'select2-rails' gem 'sentry-raven' gem 'skylight' gem 'smart_listing' diff --git a/Gemfile.lock b/Gemfile.lock index 98b4576bf..c3d75309d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -232,6 +232,7 @@ GEM font-awesome-rails (4.7.0.4) railties (>= 3.2, < 6.0) formatador (0.2.5) + geocoder (1.6.0) globalid (0.4.2) activesupport (>= 4.2.0) gon (6.2.1) @@ -425,6 +426,9 @@ GEM prawn (2.2.2) pdf-core (~> 0.7.0) ttfunk (~> 1.5) + prawn-svg (0.29.1) + css_parser (~> 1.6) + prawn (>= 0.11.1, < 3) prawn_rails (0.0.11) prawn (>= 0.11.1) railties (>= 3.0.0) @@ -592,8 +596,6 @@ GEM scss_lint (0.57.1) rake (>= 0.9, < 13) sass (~> 3.5, >= 3.5.5) - select2-rails (4.0.3) - thor (~> 0.14) selectize-rails (0.12.6) selenium-webdriver (3.141.0) childprocess (~> 0.5) @@ -742,6 +744,7 @@ DEPENDENCIES flipper-ui fog-openstack font-awesome-rails + geocoder gon graphiql-rails graphql @@ -770,6 +773,7 @@ DEPENDENCIES pg phonelib prawn + prawn-svg prawn_rails premailer-rails pry-byebug @@ -793,7 +797,6 @@ DEPENDENCIES sassc-rails scenic scss_lint - select2-rails sentry-raven shoulda-matchers simple_xlsx_reader diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index e9dd51d18..2f3b5b45e 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -29,7 +29,6 @@ // = require search // = require site_banner // = require switch_menu -// = require autocomplete // = require users // = require attestation_template_edit diff --git a/app/assets/stylesheets/autocomplete.scss b/app/assets/stylesheets/autocomplete.scss deleted file mode 100644 index b46086c3f..000000000 --- a/app/assets/stylesheets/autocomplete.scss +++ /dev/null @@ -1,33 +0,0 @@ -.algolia-autocomplete { - width: 100%; -} - -.algolia-autocomplete .aa-input, -.algolia-autocomplete .aa-hint { - width: 100%; -} - -.algolia-autocomplete .aa-hint { - color: #999999; -} - -.algolia-autocomplete .aa-dropdown-menu { - width: 100%; - background-color: #FFFFFF; - border: 1px solid #999999; - border-top: none; -} - -.algolia-autocomplete .aa-dropdown-menu .aa-suggestion { - cursor: pointer; - padding: 5px 4px; -} - -.algolia-autocomplete .aa-dropdown-menu .aa-suggestion.aa-cursor { - background-color: #B2D7FF; -} - -.algolia-autocomplete .aa-dropdown-menu .aa-suggestion em { - font-weight: bold; - font-style: normal; -} diff --git a/app/assets/stylesheets/new_design/forms.scss b/app/assets/stylesheets/new_design/forms.scss index 07455dfff..62d37cb09 100644 --- a/app/assets/stylesheets/new_design/forms.scss +++ b/app/assets/stylesheets/new_design/forms.scss @@ -242,6 +242,16 @@ } } + .select2-selection--single { + min-height: 62px; + + // scss-lint:disable SelectorFormat + .select2-selection__arrow { + display: none; + } + // scss-lint:enable + } + // scss-lint:disable SelectorFormat .select2-selection__rendered { padding: $default-padding; diff --git a/app/assets/stylesheets/new_design/map.scss b/app/assets/stylesheets/new_design/map.scss index aa30790d2..588030137 100644 --- a/app/assets/stylesheets/new_design/map.scss +++ b/app/assets/stylesheets/new_design/map.scss @@ -63,6 +63,10 @@ width: 200px; margin-right: 10px; } + + .select2-container { + margin-bottom: 0; + } } } diff --git a/app/assets/stylesheets/new_design/new_application.scss b/app/assets/stylesheets/new_design/new_application.scss index 2fffd36b7..af88e9f77 100644 --- a/app/assets/stylesheets/new_design/new_application.scss +++ b/app/assets/stylesheets/new_design/new_application.scss @@ -5,6 +5,5 @@ // = require ./fonts // = require leaflet // = require select2 -// = require autocomplete // = require_tree . // = stub ./print.scss diff --git a/app/controllers/address_controller.rb b/app/controllers/address_controller.rb deleted file mode 100644 index 6ae345d37..000000000 --- a/app/controllers/address_controller.rb +++ /dev/null @@ -1,24 +0,0 @@ -class AddressController < ApplicationController - def suggestions - request = params[:request] - - json = ApiAdresse::AddressAdapter.new(request).get_suggestions.map do |value| - { label: value } - end.to_json - - render json: json - end - - def geocode - request = params[:request] - - point = ApiAdresse::PointAdapter.new(request).geocode - - if point.present? - lon = point.x.to_s - lat = point.y.to_s - end - - render json: { lon: lon, lat: lat, zoom: '14', dossier_id: params[:dossier_id] } - end -end diff --git a/app/controllers/api_geo_test_controller.rb b/app/controllers/api_geo_test_controller.rb new file mode 100644 index 000000000..9328dc830 --- /dev/null +++ b/app/controllers/api_geo_test_controller.rb @@ -0,0 +1,13 @@ +class APIGeoTestController < ActionController::Base + def regions + render json: [{ nom: 'Martinique' }] + end + + def departements + render json: [{ nom: 'Aisne', code: '02' }] + end + + def communes + render json: [{ nom: 'Ambléon' }] + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e5cba17b3..c192c0f75 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -117,6 +117,7 @@ class ApplicationController < ActionController::Base def setup_javascript_settings gon.autosave = Rails.application.config.ds_autosave + gon.autocomplete = Rails.application.secrets.autocomplete end def setup_tracking diff --git a/app/controllers/instructeurs/dossiers_controller.rb b/app/controllers/instructeurs/dossiers_controller.rb index 06c6e5ac4..a7a7d9c4c 100644 --- a/app/controllers/instructeurs/dossiers_controller.rb +++ b/app/controllers/instructeurs/dossiers_controller.rb @@ -27,6 +27,14 @@ module Instructeurs def show @demande_seen_at = current_instructeur.follows.find_by(dossier: dossier)&.demande_seen_at + + respond_to do |format| + format.pdf do + @include_infos_administration = true + render(file: 'dossiers/show', formats: [:pdf]) + end + format.all + end end def messagerie diff --git a/app/controllers/manager/administrateurs_controller.rb b/app/controllers/manager/administrateurs_controller.rb index f976f3cf6..10dec19d5 100644 --- a/app/controllers/manager/administrateurs_controller.rb +++ b/app/controllers/manager/administrateurs_controller.rb @@ -22,11 +22,7 @@ module Manager def delete administrateur = Administrateur.find(params[:id]) - if !administrateur.can_be_deleted? - fail "Impossible de supprimer cet administrateur car il a des dossiers ou des procédures" - end - administrateur.dossiers.each(&:delete_and_keep_track) - administrateur.destroy + administrateur.delete_and_transfer_services logger.info("L'administrateur #{administrateur.id} est supprimé par #{current_administration.id}") flash[:notice] = "L'administrateur #{administrateur.id} est supprimé" diff --git a/app/controllers/manager/instructeurs_controller.rb b/app/controllers/manager/instructeurs_controller.rb index 82b1e11eb..ed2107c45 100644 --- a/app/controllers/manager/instructeurs_controller.rb +++ b/app/controllers/manager/instructeurs_controller.rb @@ -6,5 +6,19 @@ module Manager flash[:notice] = "Instructeur réinvité." redirect_to manager_instructeur_path(instructeur) end + + def delete + instructeur = Instructeur.find(params[:id]) + + if !instructeur.can_be_deleted? + fail "Impossible de supprimer cet instructeur car il est administrateur ou il est le seul instructeur sur une démarche" + end + instructeur.destroy! + + logger.info("L'instructeur #{instructeur.id} est supprimé par #{current_administration.id}") + flash[:notice] = "L'instructeur #{instructeur.id} est supprimé" + + redirect_to manager_instructeurs_path + end end end diff --git a/app/controllers/manager/users_controller.rb b/app/controllers/manager/users_controller.rb index 9e1a9fc54..eb2c9e169 100644 --- a/app/controllers/manager/users_controller.rb +++ b/app/controllers/manager/users_controller.rb @@ -24,7 +24,7 @@ module Manager def delete user = User.find(params[:id]) if !user.can_be_deleted? - fail "Impossible de supprimer cet utilisateur car il a des dossiers en instruction" + fail "Impossible de supprimer cet utilisateur. Il a des dossiers en instruction ou il est administrateur." end user.delete_and_keep_track_dossiers(current_administration) diff --git a/app/controllers/new_administrateur/groupe_instructeurs_controller.rb b/app/controllers/new_administrateur/groupe_instructeurs_controller.rb index fe1adc93a..cf024df3d 100644 --- a/app/controllers/new_administrateur/groupe_instructeurs_controller.rb +++ b/app/controllers/new_administrateur/groupe_instructeurs_controller.rb @@ -48,6 +48,33 @@ module NewAdministrateur end end + def destroy + if !groupe_instructeur.dossiers.empty? + flash[:alert] = "Impossible de supprimer un groupe avec des dossiers. Il faut le réaffecter avant" + elsif procedure.groupe_instructeurs.one? + flash[:alert] = "Suppression impossible : il doit y avoir au moins un groupe instructeur sur chaque procédure" + else + label = groupe_instructeur.label + groupe_instructeur.destroy! + flash[:notice] = "le groupe « #{label} » a été supprimé." + end + redirect_to procedure_groupe_instructeurs_path(procedure) + end + + def reaffecter_dossiers + @procedure = procedure + @groupe_instructeur = groupe_instructeur + @groupes_instructeurs = paginated_groupe_instructeurs + .without_group(@groupe_instructeur) + end + + def reaffecter + target_group = procedure.groupe_instructeurs.find(params[:target_group]) + groupe_instructeur.dossiers.update_all(groupe_instructeur_id: target_group.id) + flash[:notice] = "Les dossiers du groupe « #{groupe_instructeur.label} » ont été réaffectés au groupe « #{target_group.label} »." + redirect_to procedure_groupe_instructeurs_path(procedure) + end + def add_instructeur emails = params['emails'].presence || [] emails = emails.map(&:strip).map(&:downcase) diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index df9204e89..f171e94fa 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -36,6 +36,13 @@ module Users end @dossier = dossier + respond_to do |format| + format.pdf do + @include_infos_administration = false + render(file: 'dossiers/show', formats: [:pdf]) + end + format.all + end end def demande diff --git a/app/graphql/schema.graphql b/app/graphql/schema.graphql index 7f6364605..7d6ede50c 100644 --- a/app/graphql/schema.graphql +++ b/app/graphql/schema.graphql @@ -1012,6 +1012,11 @@ enum TypeDeChamp { """ civilite + """ + Communes + """ + communes + """ Date """ diff --git a/app/javascript/new_design/select2.js b/app/javascript/new_design/select2.js index a9fa8506c..8de34648d 100644 --- a/app/javascript/new_design/select2.js +++ b/app/javascript/new_design/select2.js @@ -1,46 +1,158 @@ import $ from 'jquery'; import 'select2'; -const optionTemplate = email => +const { api_geo_url, api_adresse_url } = gon.autocomplete || {}; + +const language = { + errorLoading: function() { + return 'Les résultats ne peuvent pas être chargés.'; + }, + inputTooLong: function(args) { + var overChars = args.input.length - args.maximum; + + return 'Supprimez ' + overChars + ' caractère' + (overChars > 1 ? 's' : ''); + }, + inputTooShort: function(args) { + var remainingChars = args.minimum - args.input.length; + + return ( + 'Saisissez au moins ' + + remainingChars + + ' caractère' + + (remainingChars > 1 ? 's' : '') + ); + }, + loadingMore: function() { + return 'Chargement de résultats supplémentaires…'; + }, + maximumSelected: function(args) { + return ( + 'Vous pouvez seulement sélectionner ' + + args.maximum + + ' élément' + + (args.maximum > 1 ? 's' : '') + ); + }, + noResults: function() { + return 'Aucun résultat trouvé'; + }, + searching: function() { + return 'Recherche en cours…'; + }, + removeAllItems: function() { + return 'Supprimer tous les éléments'; + } +}; + +const baseOptions = { + language, + width: '100%' +}; + +const baseAjaxOptions = { + delay: 250, + cache: true, + data({ term: nom }) { + return { + nom, + fields: 'nom,code' + }; + }, + processResults(data) { + return { + results: data.map(({ nom }) => ({ id: nom, text: nom })) + }; + } +}; + +const regionsOptions = { + ...baseOptions, + minimumInputLength: 2, + ajax: { url: `${api_geo_url}/regions`, ...baseAjaxOptions } +}; + +const communesOptions = { + ...baseOptions, + minimumInputLength: 2, + ajax: { url: `${api_geo_url}/communes`, ...baseAjaxOptions } +}; + +const etranger99 = { id: '99 - Étranger', text: '99 - Étranger' }; +const departementsOptions = { + ...baseOptions, + minimumInputLength: 2, + ajax: { + ...baseAjaxOptions, + url: `${api_geo_url}/departements`, + processResults(data) { + return { + results: data + .map(({ nom, code }) => ({ + id: `${code} - ${nom}`, + text: `${code} - ${nom}` + })) + .concat([etranger99]) + }; + } + } +}; + +const adresseOptions = { + ...baseOptions, + minimumInputLength: 2, + ajax: { + ...baseAjaxOptions, + url: `${api_adresse_url}/search`, + data({ term: q }) { + return { + q, + limit: 5 + }; + }, + processResults(data) { + return { + results: data.features.map(({ properties: { label }, geometry }) => ({ + id: label, + text: label, + geometry + })) + }; + } + } +}; + +const templateOption = ({ text }) => $( - '' + - email.text + - '' + `${text}` ); addEventListener('ds:page:update', () => { - $('select.select2').select2({ - language: 'fr', - width: '100%' - }); + $('select.select2').select2(baseOptions); + $('select.select2.departements').select2(departementsOptions); + $('select.select2.regions').select2(regionsOptions); + $('select.select2.communes').select2(communesOptions); + $('select.select2.adresse').select2(adresseOptions); $('.columns-form select.select2-limited').select2({ - language: 'fr', + width: '300px', placeholder: 'Sélectionnez des colonnes', - maximumSelectionLength: '5', - width: '300px' + maximumSelectionLength: '5' }); $('.recipients-form select.select2-limited').select2({ - language: 'fr', + language, + width: '300px', placeholder: 'Sélectionnez des instructeurs', - maximumSelectionLength: '30', - width: '300px' + maximumSelectionLength: '30' }); $('select.select2-limited.select-instructeurs').select2({ - language: 'fr', + language, dropdownParent: $('.instructeur-wrapper'), placeholder: 'Saisir l’adresse email de l’instructeur', tags: true, tokenSeparators: [',', ' '], - templateResult: optionTemplate, - templateSelection: function(email) { - return $( - '' + - email.text + - '' - ); - } + templateResult: templateOption, + templateSelection: templateOption }); }); diff --git a/app/javascript/packs/application-old.js b/app/javascript/packs/application-old.js index 180698e4b..a10e991fb 100644 --- a/app/javascript/packs/application-old.js +++ b/app/javascript/packs/application-old.js @@ -8,7 +8,6 @@ import '../shared/page-update-event'; import '../shared/activestorage/ujs'; import '../shared/rails-ujs-fix'; import '../shared/safari-11-file-xhr-workaround'; -import '../shared/autocomplete'; import '../shared/remote-input'; import '../shared/franceconnect'; diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 5a7e995cf..0e567c58b 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -13,7 +13,6 @@ import '../shared/activestorage/ujs'; import '../shared/activestorage/attachment-checker'; import '../shared/rails-ujs-fix'; import '../shared/safari-11-file-xhr-workaround'; -import '../shared/autocomplete'; import '../shared/remote-input'; import '../shared/franceconnect'; import '../shared/toggle-target'; diff --git a/app/javascript/shared/autocomplete.js b/app/javascript/shared/autocomplete.js deleted file mode 100644 index e37dec4dc..000000000 --- a/app/javascript/shared/autocomplete.js +++ /dev/null @@ -1,51 +0,0 @@ -import autocomplete from 'autocomplete.js'; -import { getJSON, fire } from '@utils'; - -const sources = [ - { - type: 'address', - url: '/address/suggestions' - } -]; - -const options = { - autoselect: true, - minLength: 1 -}; - -function selector(type) { - return `[data-autocomplete=${type}]`; -} - -function source(url) { - return { - source(query, callback) { - getJSON(url, { request: query }).then(callback); - }, - templates: { - suggestion({ label, mine }) { - const mineClass = `path-mine-${mine ? 'true' : 'false'}`; - const openTag = `
`; - return autocomplete.escapeHighlightedString(label, openTag, '
'); - } - }, - debounce: 300 - }; -} - -addEventListener('ds:page:update', function() { - for (let { type, url } of sources) { - for (let element of document.querySelectorAll(selector(type))) { - element.removeAttribute('data-autocomplete'); - autocompleteInitializeElement(element, url); - } - } -}); - -function autocompleteInitializeElement(element, url) { - const select = autocomplete(element, options, [source(url)]); - select.on('autocomplete:selected', ({ target }, suggestion) => { - fire(target, 'autocomplete:select', suggestion); - select.autocomplete.setVal(suggestion.label); - }); -} diff --git a/app/javascript/shared/carte.js b/app/javascript/shared/carte.js index b6f76a27a..a01a36c78 100644 --- a/app/javascript/shared/carte.js +++ b/app/javascript/shared/carte.js @@ -1,5 +1,6 @@ /* globals FreeDraw L */ -import { fire, getJSON, delegate } from '@utils'; +import { fire, delegate } from '@utils'; +import $ from 'jquery'; import polygonArea from './polygon_area'; @@ -113,14 +114,6 @@ function drawParcellesAgricoles(map, { parcellesAgricoles }, editable = false) { ); } -function geocodeAddress(map, query) { - getJSON('/address/geocode', { request: query }).then(data => { - if (data.lat !== null) { - map.setView(new L.LatLng(data.lat, data.lon), data.zoom); - } - }); -} - function getCurrentMap(element) { if (!element.matches('.carte')) { const closestCarteElement = element.closest('.carte'); @@ -246,10 +239,12 @@ delegate('click', '.toolbar .new-area', event => { } }); -delegate('autocomplete:select', '.toolbar [data-address]', event => { +$(document).on('select2:select', 'select[data-address]', event => { const map = getCurrentMap(event.target); + const { geometry } = event.params.data; - if (map) { - geocodeAddress(map, event.detail.label); + if (map && geometry && geometry.type === 'Point') { + const [lon, lat] = geometry.coordinates; + map.setView(new L.LatLng(lat, lon), 14); } }); diff --git a/app/lib/api_adresse/adapter.rb b/app/lib/api_adresse/adapter.rb deleted file mode 100644 index cd012357b..000000000 --- a/app/lib/api_adresse/adapter.rb +++ /dev/null @@ -1,33 +0,0 @@ -class ApiAdresse::Adapter - private - - def initialize(address, limit, blank_return) - @address = address - @limit = limit - @blank_return = blank_return - end - - def features - @features ||= get_features - end - - def get_features - response = ApiAdresse::API.call(@address, @limit) - result = JSON.parse(response) - result['features'] - rescue RestClient::Exception, JSON::ParserError, TypeError - @blank_return - end - - def handle_result - if features.present? - process_features - else - @blank_return - end - end - - def process_features - raise NoMethodError - end -end diff --git a/app/lib/api_adresse/address_adapter.rb b/app/lib/api_adresse/address_adapter.rb deleted file mode 100644 index 68e737cfe..000000000 --- a/app/lib/api_adresse/address_adapter.rb +++ /dev/null @@ -1,17 +0,0 @@ -class ApiAdresse::AddressAdapter < ApiAdresse::Adapter - def initialize(address) - super(address, 5, []) - end - - def get_suggestions - handle_result - end - - private - - def process_features - features.map do |feature| - feature['properties']['label'] - end - end -end diff --git a/app/lib/api_adresse/api.rb b/app/lib/api_adresse/api.rb deleted file mode 100644 index 4470d9008..000000000 --- a/app/lib/api_adresse/api.rb +++ /dev/null @@ -1,12 +0,0 @@ -class ApiAdresse::API - def self.call(address, limit = 1) - search_url = [API_ADRESSE_URL, "search"].join("/") - - RestClient::Request.execute(method: :get, - url: search_url, - timeout: 5, - headers: { params: { q: address, limit: limit } }) - rescue RestClient::ServiceUnavailable - nil - end -end diff --git a/app/lib/api_adresse/point_adapter.rb b/app/lib/api_adresse/point_adapter.rb deleted file mode 100644 index ea54079e6..000000000 --- a/app/lib/api_adresse/point_adapter.rb +++ /dev/null @@ -1,15 +0,0 @@ -class ApiAdresse::PointAdapter < ApiAdresse::Adapter - def initialize(address) - super(address, 1, nil) - end - - def geocode - handle_result - end - - private - - def process_features - RGeo::GeoJSON.decode(features[0]['geometry'], json_parser: :json) - end -end diff --git a/app/lib/api_geo/api.rb b/app/lib/api_geo/api.rb deleted file mode 100644 index fabafff90..000000000 --- a/app/lib/api_geo/api.rb +++ /dev/null @@ -1,57 +0,0 @@ -class ApiGeo::API - TIMEOUT = 15 - CACHE_DURATION = 1.day - - def self.regions - url = [API_GEO_URL, "regions"].join("/") - call(url, { fields: :nom }) - end - - def self.departements - url = [API_GEO_URL, "departements"].join("/") - call(url, { fields: :nom }) - end - - def self.pays - parse(File.open('app/lib/api_geo/pays.json').read) - end - - def self.search_rpg(geojson) - url = [API_GEO_SANDBOX_URL, "rpg", "parcelles", "search"].join("/") - call(url, geojson, :post) - end - - private - - def self.parse(body) - JSON.parse(body, symbolize_names: true) - end - - def self.call(url, body, method = :get) - # The cache engine is stored, because as of Typhoeus 1.3.1 the cache engine instance - # is included in the computed `cache_key`. - # (Which means that when the cache instance changes, the cache is invalidated.) - @typhoeus_cache ||= Typhoeus::Cache::SuccessfulRequestsRailsCache.new - - response = Typhoeus::Request.new( - url, - method: method, - params: method == :get ? body : nil, - body: method == :post ? body : nil, - timeout: TIMEOUT, - accept_encoding: 'gzip', - headers: { - 'Accept' => 'application/json', - 'Accept-Encoding' => 'gzip, deflate' - }.merge(method == :post ? { 'Content-Type' => 'application/json' } : {}), - cache: @typhoeus_cache, - cache_ttl: CACHE_DURATION - ).run - - if response.success? - parse(response.body) - else - nil - end - end -end diff --git a/app/lib/api_geo/rpg_adapter.rb b/app/lib/api_geo/rpg_adapter.rb deleted file mode 100644 index 095825d6c..000000000 --- a/app/lib/api_geo/rpg_adapter.rb +++ /dev/null @@ -1,27 +0,0 @@ -class ApiGeo::RPGAdapter - def initialize(coordinates) - @coordinates = GeojsonService.to_json_polygon_for_rpg(coordinates) - end - - def data_source - @data_source ||= ApiGeo::API.search_rpg(@coordinates) - end - - def results - data_source[:features].map do |feature| - feature[:properties] - .stringify_keys - .transform_keys(&:underscore) - .symbolize_keys - .slice( - :culture, - :code_culture, - :surface, - :bio - ).merge({ - geometry: feature[:geometry], - geo_reference_id: feature[:properties][:id] - }) - end - end -end diff --git a/app/models/administrateur.rb b/app/models/administrateur.rb index 4d4b84d4a..1174d4b60 100644 --- a/app/models/administrateur.rb +++ b/app/models/administrateur.rb @@ -70,4 +70,17 @@ class Administrateur < ApplicationRecord def can_be_deleted? dossiers.state_instruction_commencee.none? && procedures.all? { |p| p.administrateurs.count > 1 } end + + def delete_and_transfer_services + if !can_be_deleted? + fail "Impossible de supprimer cet administrateur car il a des dossiers ou des procédures" + end + dossiers.each(&:delete_and_keep_track) + + procedures.each do |procedure| + next_administrateur = procedure.administrateurs.where.not(id: self.id).first + procedure.service.update(administrateur: next_administrateur) + end + destroy + end end diff --git a/app/models/champs/commune_champ.rb b/app/models/champs/commune_champ.rb new file mode 100644 index 000000000..9db282844 --- /dev/null +++ b/app/models/champs/commune_champ.rb @@ -0,0 +1,2 @@ +class Champs::CommuneChamp < Champs::TextChamp +end diff --git a/app/models/champs/departement_champ.rb b/app/models/champs/departement_champ.rb index f211b421c..ee9803389 100644 --- a/app/models/champs/departement_champ.rb +++ b/app/models/champs/departement_champ.rb @@ -1,5 +1,2 @@ class Champs::DepartementChamp < Champs::TextChamp - def self.departements - ApiGeo::API.departements.map { |liste| "#{liste[:code]} - #{liste[:nom]}" }.push('99 - Étranger') - end end diff --git a/app/models/champs/pays_champ.rb b/app/models/champs/pays_champ.rb index 6f916dbce..1d8f08ed7 100644 --- a/app/models/champs/pays_champ.rb +++ b/app/models/champs/pays_champ.rb @@ -1,6 +1,8 @@ class Champs::PaysChamp < Champs::TextChamp + PAYS = JSON.parse(Rails.root.join('app', 'lib', 'api_geo', 'pays.json').read, symbolize_names: true) + def self.pays - ApiGeo::API.pays.pluck(:nom) + PAYS.pluck(:nom) end def self.disabled_options diff --git a/app/models/champs/region_champ.rb b/app/models/champs/region_champ.rb index 9ecba7173..ba9045087 100644 --- a/app/models/champs/region_champ.rb +++ b/app/models/champs/region_champ.rb @@ -1,5 +1,2 @@ class Champs::RegionChamp < Champs::TextChamp - def self.regions - ApiGeo::API.regions.sort_by { |e| e[:nom] }.pluck(:nom) - end end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 96d2f08c2..a7c237bbc 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -343,7 +343,7 @@ class Dossier < ApplicationRecord def geo_position if etablissement.present? - point = ApiAdresse::PointAdapter.new(etablissement.geo_adresse).geocode + point = Geocoder.search(etablissement.geo_adresse).first end lon = "2.428462" @@ -351,8 +351,7 @@ class Dossier < ApplicationRecord zoom = "13" if point.present? - lon = point.x.to_s - lat = point.y.to_s + lat, lon = point.coordinates.map(&:to_s) end { lon: lon, lat: lat, zoom: zoom } diff --git a/app/models/groupe_instructeur.rb b/app/models/groupe_instructeur.rb index 546e94cb4..ccb84e5f1 100644 --- a/app/models/groupe_instructeur.rb +++ b/app/models/groupe_instructeur.rb @@ -10,4 +10,6 @@ class GroupeInstructeur < ApplicationRecord validates :label, uniqueness: { scope: :procedure, message: 'existe déjà' } before_validation -> { label&.strip! } + + scope :without_group, -> (group) { where.not(id: group) } end diff --git a/app/models/instructeur.rb b/app/models/instructeur.rb index 793e45606..97b2b0a49 100644 --- a/app/models/instructeur.rb +++ b/app/models/instructeur.rb @@ -17,9 +17,9 @@ class Instructeur < ApplicationRecord has_many :previously_followed_dossiers, -> { distinct }, through: :previous_follows, source: :dossier has_many :avis has_many :dossiers_from_avis, through: :avis, source: :dossier - has_many :trusted_device_tokens + has_many :trusted_device_tokens, dependent: :destroy - has_one :user + has_one :user, dependent: :nullify default_scope { eager_load(:user) } @@ -76,7 +76,7 @@ class Instructeur < ApplicationRecord active_procedure_overviews = procedures .publiees - .map { |procedure| procedure.procedure_overview(start_date) } + .map { |procedure| procedure.procedure_overview(start_date, groupe_instructeurs) } .filter(&:had_some_activities?) if active_procedure_overviews.count == 0 @@ -176,6 +176,10 @@ class Instructeur < ApplicationRecord trusted_device_token&.token_young? end + def can_be_deleted? + user.administrateur.nil? && procedures.all? { |p| p.defaut_groupe_instructeur.instructeurs.count > 1 } + end + private def annotations_hash(demande, annotations_privees, avis, messagerie) diff --git a/app/models/procedure.rb b/app/models/procedure.rb index aba66b5d9..8697e3d5d 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -464,8 +464,8 @@ class Procedure < ApplicationRecord export(dossiers).to_ods end - def procedure_overview(start_date) - ProcedureOverview.new(self, start_date) + def procedure_overview(start_date, groups) + ProcedureOverview.new(self, start_date, groups) end def initiated_mail_template diff --git a/app/models/procedure_overview.rb b/app/models/procedure_overview.rb index a514766c5..92db328e4 100644 --- a/app/models/procedure_overview.rb +++ b/app/models/procedure_overview.rb @@ -6,24 +6,23 @@ class ProcedureOverview :dossiers_en_construction_count, :old_dossiers_en_construction - def initialize(procedure, start_date) + def initialize(procedure, start_date, groups) @start_date = start_date @procedure = procedure - @dossiers_en_instruction_count = procedure.dossiers.state_en_instruction.count - @old_dossiers_en_instruction = procedure - .dossiers + dossiers = procedure.dossiers.where(groupe_instructeur: groups) + + @dossiers_en_instruction_count = dossiers.state_en_instruction.count + @old_dossiers_en_instruction = dossiers .state_en_instruction .where('en_instruction_at < ?', 1.week.ago) - @dossiers_en_construction_count = procedure.dossiers.state_en_construction.count - @old_dossiers_en_construction = procedure - .dossiers + @dossiers_en_construction_count = dossiers.state_en_construction.count + @old_dossiers_en_construction = dossiers .state_en_construction .where('en_construction_at < ?', 1.week.ago) - @created_dossiers_count = procedure - .dossiers + @created_dossiers_count = dossiers .where(created_at: start_date..Time.zone.now) .state_not_brouillon .count diff --git a/app/models/type_de_champ.rb b/app/models/type_de_champ.rb index 249c1b6f4..280e957d1 100644 --- a/app/models/type_de_champ.rb +++ b/app/models/type_de_champ.rb @@ -19,6 +19,7 @@ class TypeDeChamp < ApplicationRecord pays: 'pays', regions: 'regions', departements: 'departements', + communes: 'communes', engagement: 'engagement', header_section: 'header_section', explication: 'explication', diff --git a/app/models/types_de_champ/commune_type_de_champ.rb b/app/models/types_de_champ/commune_type_de_champ.rb new file mode 100644 index 000000000..3d7c8a855 --- /dev/null +++ b/app/models/types_de_champ/commune_type_de_champ.rb @@ -0,0 +1,2 @@ +class TypesDeChamp::CommuneTypeDeChamp < TypesDeChamp::TypeDeChampBase +end diff --git a/app/models/user.rb b/app/models/user.rb index 4838da20d..35c0edd5e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -97,7 +97,7 @@ class User < ApplicationRecord end def can_be_deleted? - dossiers.state_instruction_commencee.empty? + administrateur.nil? && instructeur.nil? && dossiers.state_instruction_commencee.empty? end def delete_and_keep_track_dossiers(administration) @@ -105,12 +105,11 @@ class User < ApplicationRecord raise "Cannot delete this user because instruction has started for some dossiers" end - if can_be_deleted? - dossiers.each do |dossier| - dossier.delete_and_keep_track(administration) - end - destroy + dossiers.each do |dossier| + dossier.delete_and_keep_track(administration) end + dossiers.unscoped.destroy_all + destroy! end private diff --git a/app/views/dossiers/show.pdf.prawn b/app/views/dossiers/show.pdf.prawn new file mode 100644 index 000000000..045c9b8bd --- /dev/null +++ b/app/views/dossiers/show.pdf.prawn @@ -0,0 +1,203 @@ +require 'prawn/measurement_extensions' + +def format_in_2_lines(pdf, label, text) + pdf.font 'liberation serif', style: :bold, size: 12 do + pdf.text label + end + pdf.text text + pdf.text "\n" +end + +def format_in_2_columns(pdf, label, text) + pdf.text_box label, width: 200, height: 100, overflow: :expand, at: [0, pdf.cursor] + pdf.text_box ":", width: 10, height: 100, overflow: :expand, at: [100, pdf.cursor] + pdf.text_box text, width: 420, height: 100, overflow: :expand, at: [110, pdf.cursor] + pdf.text "\n" +end + +def add_title(pdf, title) + title_style = {style: :bold, size: 24} + pdf.font 'liberation serif', title_style do + pdf.text title + end + pdf.text "\n" +end + +def format_date(date) + I18n.l(date, format: :message_date_with_year) +end + +def add_identite_individual(pdf, dossier) + format_in_2_columns(pdf, "Civilité", dossier.individual.gender) + format_in_2_columns(pdf, "Nom", dossier.individual.nom) + format_in_2_columns(pdf, "Prénom", dossier.individual.prenom) + + if dossier.individual.birthdate.present? + format_in_2_columns(pdf, "Date de naissance", try_format_date(dossier.individual.birthdate)) + end +end + +def render_siret_info(pdf, etablissement) + pdf.text " - Dénomination : #{raison_sociale_or_name(etablissement)}" + pdf.text " - Forme juridique : #{etablissement.entreprise_forme_juridique}" + if etablissement.entreprise_capital_social.present? + pdf.text " - Capital social : #{pretty_currency(etablissement.entreprise_capital_social)}" + end + pdf.text "\n" +end + +def render_identite_etablissement(pdf, etablissement) + pdf.text " - SIRET : #{etablissement.siret}" + pdf.text " - SIRET du siège social: #{etablissement.entreprise.siret_siege_social}" + pdf.text " - Dénomination : #{raison_sociale_or_name(etablissement)}" + pdf.text " - Forme juridique : #{etablissement.entreprise_forme_juridique}" + if etablissement.entreprise_capital_social.present? + pdf.text " - Capital social : #{pretty_currency(etablissement.entreprise_capital_social)}" + end + pdf.text " - Libellé NAF : #{etablissement.libelle_naf}" + pdf.text " - Code NAF : #{etablissement.naf}" + pdf.text " - Date de création : #{try_format_date(etablissement.entreprise.date_creation)}" + pdf.text " - Effectif de l'organisation : #{effectif(etablissement)}" + pdf.text " - Code effectif : #{etablissement.entreprise.code_effectif_entreprise}" + pdf.text " - Numéro de TVA intracommunautaire : #{etablissement.entreprise.numero_tva_intracommunautaire}" + pdf.text " - Adresse : #{etablissement.adresse}" + if etablissement.association? + pdf.text " - Numéro RNA : #{etablissement.association_rna}" + pdf.text " - Titre : #{etablissement.association_titre}" + pdf.text " - Objet : #{etablissement.association_objet}" + pdf.text " - Date de création : #{try_format_date(etablissement.association_date_creation)}" + pdf.text " - Date de publication : #{try_format_date(etablissement.association_date_publication)}" + pdf.text " - Date de déclaration : #{try_format_date(etablissement.association_date_declaration)}" + end + pdf.text "\n" +end + +def render_single_champ(pdf, champ) + case champ.type + when 'Champs::RepetitionChamp' + raise 'There should not be a RepetitionChamp here !' + when 'Champs::PieceJustificativeChamp' + return + when 'Champs::HeaderSectionChamp' + pdf.font 'liberation serif', style: :bold, size: 18 do + pdf.text champ.libelle + end + pdf.text "\n" + when 'Champs::ExplicationChamp' + format_in_2_lines(pdf, champ.libelle, champ.description) + when 'Champs::CarteChamp' + format_in_2_lines(pdf, champ.libelle, champ.geo_json.to_s) + when 'Champs::SiretChamp' + pdf.font 'liberation serif', style: :bold, size: 12 do + pdf.text champ.libelle + end + pdf.text " - SIRET: #{champ.to_s}" + render_identite_etablissement(pdf, champ.etablissement) if champ.etablissement.present? + pdf.text "\n" + else + value = champ.to_s.empty? ? 'Non communiqué' : champ.to_s + format_in_2_lines(pdf, champ.libelle, value) + end +end + +def add_champs(pdf, champs) + champs.each do |champ| + if champ.type == 'Champs::RepetitionChamp' + champ.rows.each do |row| + row.each do |inner_champ| + render_single_champ(pdf, inner_champ) + end + end + else + render_single_champ(pdf, champ) + end + end +end + +def add_message(pdf, message) + sender = message.redacted_email + if message.sent_by_system? + sender = 'Email automatique' + elsif message.sent_by?(@dossier.user) + sender = @dossier.user.email + end + + pdf.text "#{sender}, #{format_date(message.created_at)}", style: :bold + pdf.text ActionView::Base.full_sanitizer.sanitize(message.body) + pdf.text "\n" +end + +def add_avis(pdf, avis) + pdf.text "Avis de #{avis.email_to_display}", style: :bold + if avis.confidentiel? + pdf.text "(confidentiel)", style: :bold + end + text = avis.answer || 'En attente de réponse' + pdf.text text + pdf.text "\n" +end + +def add_etats_dossier(pdf, dossier) + if dossier.en_construction_at.present? + format_in_2_columns(pdf, "Déposé le", try_format_date(dossier.en_construction_at)) + end + if dossier.en_instruction_at.present? + format_in_2_columns(pdf, "En instruction le", try_format_date(dossier.en_instruction_at)) + end + if dossier.processed_at?.present? + format_in_2_columns(pdf, "Décision le", try_format_date(dossier.processed_at)) + end + + pdf.text "\n" +end + +prawn_document(page_size: "A4") do |pdf| + pdf.font_families.update( 'liberation serif' => { + normal: Rails.root.join('lib/prawn/fonts/liberation_serif/LiberationSerif-Regular.ttf' ), + bold: Rails.root.join('lib/prawn/fonts/liberation_serif/LiberationSerif-Bold.ttf' ), + }) + pdf.font 'liberation serif' + + pdf.svg IO.read("app/assets/images/header/logo-ds-wide.svg"), width: 300, position: :center + pdf.move_down(40) + + format_in_2_columns(pdf, 'Dossier Nº', @dossier.id.to_s) + format_in_2_columns(pdf, 'Démarche', @dossier.procedure.libelle) + format_in_2_columns(pdf, 'Organisme', @dossier.procedure.organisation_name) + pdf.text "\n" + + pdf.text "Ce dossier est #{dossier_display_state(@dossier, lower: true)}.", inline_format: true + pdf.text "\n" + if @dossier.motivation.present? + format_in_2_lines(pdf, "Motif de la décision", @dossier.motivation) + end + add_title(pdf, 'Historique') + add_etats_dossier(pdf, @dossier) + + add_title(pdf, "Identité du demandeur") + + format_in_2_columns(pdf, "Email", @dossier.user.email) + add_identite_individual(pdf, @dossier) if @dossier.individual.present? + render_identite_etablissement(pdf, @dossier.etablissement) if @dossier.etablissement.present? + pdf.text "\n" + + add_title(pdf, 'Formulaire') + add_champs(pdf, @dossier.champs) + + if @include_infos_administration && @dossier.champs_private&.size > 0 + add_title(pdf, "Annotations privées") + add_champs(pdf, @dossier.champs_private) + end + + if @include_infos_administration && @dossier.avis.present? + add_title(pdf, "Avis") + @dossier.avis.each do |avis| + add_avis(pdf, avis) + end + end + + add_title(pdf, 'Messagerie') + @dossier.commentaires.each do |commentaire| + add_message(pdf, commentaire) + end +end diff --git a/app/views/instructeurs/dossiers/_header_actions.html.haml b/app/views/instructeurs/dossiers/_header_actions.html.haml index 57932439b..eaa46936f 100644 --- a/app/views/instructeurs/dossiers/_header_actions.html.haml +++ b/app/views/instructeurs/dossiers/_header_actions.html.haml @@ -6,6 +6,8 @@ = link_to "Tout le dossier", print_instructeur_dossier_path(dossier.procedure, dossier), target: "_blank", rel: "noopener", class: "menu-item menu-link" %li = link_to "Uniquement cet onglet", "#", onclick: "window.print()", class: "menu-item menu-link" + %li + = link_to "Export PDF", instructeur_dossier_path(dossier.procedure, dossier, format: :pdf), target: "_blank", rel: "noopener", class: "menu-item menu-link" - if !PiecesJustificativesService.liste_pieces_justificatives(dossier).empty? %span.dropdown.print-menu-opener diff --git a/app/views/manager/instructeurs/show.html.erb b/app/views/manager/instructeurs/show.html.erb index e423414a9..983e171e9 100644 --- a/app/views/manager/instructeurs/show.html.erb +++ b/app/views/manager/instructeurs/show.html.erb @@ -34,7 +34,10 @@ as well as a link to its edit page.
<%= link_to 'Réinviter', reinvite_manager_instructeur_path(instructeur), method: :post, class: 'button' %> +
+ <%= button_to "Supprimer", delete_manager_instructeur_path(page.resource), method: :delete, disabled: !page.resource.can_be_deleted?, class: "button", data: { confirm: "Confirmez-vous la suppression de l'instructeur ?" }, title: page.resource.can_be_deleted? ? "Supprimer" : "Cet instructeur est administrateur ou a des démarches dont il est le seul instructeur et ne peut être supprimé" %> +
diff --git a/app/views/manager/users/show.html.erb b/app/views/manager/users/show.html.erb index ab51789a6..833e79723 100644 --- a/app/views/manager/users/show.html.erb +++ b/app/views/manager/users/show.html.erb @@ -25,7 +25,7 @@ as well as a link to its edit page.
- <%= button_to "supprimer", delete_manager_user_path(page.resource), method: :delete, disabled: !page.resource.can_be_deleted?, class: "button", data: { confirm: "Confirmez-vous la suppression de l'utilisateur ?" }, title: page.resource.can_be_deleted? ? "Supprimer" : "Cet utilisateur a des dossiers dont l'instruction a commencé et ne peut être supprimé" %> + <%= button_to "supprimer", delete_manager_user_path(page.resource), method: :delete, disabled: !page.resource.can_be_deleted?, class: "button", data: { confirm: "Confirmez-vous la suppression de l'utilisateur ?" }, title: page.resource.can_be_deleted? ? "Supprimer" : "Cet utilisateur ne peut être supprimé. Il a des dossiers dont l'instruction a commencé ou il est administrateur ou instructeur" %>
diff --git a/app/views/new_administrateur/groupe_instructeurs/index.html.haml b/app/views/new_administrateur/groupe_instructeurs/index.html.haml index 28ce3b0c1..0e7923121 100644 --- a/app/views/new_administrateur/groupe_instructeurs/index.html.haml +++ b/app/views/new_administrateur/groupe_instructeurs/index.html.haml @@ -28,11 +28,21 @@ %table.table.mt-2 %thead %tr - %th{ colspan: 2 }= t(".existing_groupe", count: @groupes_instructeurs.count) + %th{ colspan: 2 }= t(".existing_groupe", count: @groupes_instructeurs.total_count) %tbody - @groupes_instructeurs.each do |group| %tr %td= group.label %td.actions= link_to "voir", procedure_groupe_instructeur_path(@procedure, group) - + - if @groupes_instructeurs.many? + - if group.dossiers.empty? + %td.actions + = link_to procedure_groupe_instructeur_path(@procedure, group), { method: :delete, class: 'button', data: { confirm: "Êtes-vous sûr de vouloir supprimer le groupe « #{group.label} » ?" }} do + %span.icon.delete + supprimer ce groupe + - else + %td.actions + = link_to reaffecter_dossiers_procedure_groupe_instructeur_path(@procedure, group), class: 'button', title:'Réaffecter les dossiers à un autre groupe afin de pouvoir le supprimer' do + %span.icon.follow + déplacer les dossiers = paginate @groupes_instructeurs diff --git a/app/views/new_administrateur/groupe_instructeurs/reaffecter_dossiers.html.haml b/app/views/new_administrateur/groupe_instructeurs/reaffecter_dossiers.html.haml new file mode 100644 index 000000000..d62f1e7b3 --- /dev/null +++ b/app/views/new_administrateur/groupe_instructeurs/reaffecter_dossiers.html.haml @@ -0,0 +1,27 @@ += render partial: 'new_administrateur/breadcrumbs', + locals: { steps: [link_to('Démarches', admin_procedures_path), + link_to(@procedure.libelle, admin_procedure_path(@procedure)), + link_to('Groupes d’instructeurs', procedure_groupe_instructeurs_path(@procedure)), + @groupe_instructeur.label] } + +.container.groupe-instructeur + + .card + .card-title Réaffectation des dossiers du groupe « {@groupe_instructeur.label} » + %p + Le groupe « #{@groupe_instructeur.label} » contient des dossiers. Afin de procéder à sa suppression, vous devez réaffecter ses dossiers à un autre groupe instructeur. + + %table.table.mt-2 + %thead + %tr + %th{ colspan: 2 }= t(".existing_groupe", count: @groupes_instructeurs.total_count) + %tbody + - @groupes_instructeurs.each do |group| + %tr + %td= group.label + %td.actions= button_to 'Réaffecter les dossiers à ce groupe', + reaffecter_procedure_groupe_instructeur_path(:target_group => group), + { class: 'button', + data: { confirm: "Êtes-vous sûr de vouloir réaffecter les dossiers du groupe « #{@groupe_instructeur.label} » vers le groupe  « #{group.label} » ?" } } + + = paginate @groupes_instructeurs diff --git a/app/views/shared/dossiers/editable_champs/_address.html.haml b/app/views/shared/dossiers/editable_champs/_address.html.haml index b7ae4e032..95025fc33 100644 --- a/app/views/shared/dossiers/editable_champs/_address.html.haml +++ b/app/views/shared/dossiers/editable_champs/_address.html.haml @@ -1,4 +1,4 @@ -= form.text_field :value, - data: { address: true, autocomplete: 'address' }, - placeholder: champ.libelle, - required: champ.mandatory? += form.select :value, [champ.value].compact, + { include_blank: true }, + required: champ.mandatory?, + class: 'select2 adresse' diff --git a/app/views/shared/dossiers/editable_champs/_carte.html.haml b/app/views/shared/dossiers/editable_champs/_carte.html.haml index e55bebec0..e15e1f6c6 100644 --- a/app/views/shared/dossiers/editable_champs/_carte.html.haml +++ b/app/views/shared/dossiers/editable_champs/_carte.html.haml @@ -1,6 +1,6 @@ .toolbar %button.button.primary.new-area Ajouter une zone - %input.address{ data: { address: true, autocomplete: 'address' }, placeholder: 'Saisissez une adresse ou positionner la carte' } + %select.select2.adresse{ data: { address: true }, placeholder: 'Saisissez une adresse ou positionner la carte' } .carte.edit{ data: { geo: geo_data(champ) }, class: "carte-#{form.index}" } diff --git a/app/views/shared/dossiers/editable_champs/_communes.html.haml b/app/views/shared/dossiers/editable_champs/_communes.html.haml new file mode 100644 index 000000000..0087a725e --- /dev/null +++ b/app/views/shared/dossiers/editable_champs/_communes.html.haml @@ -0,0 +1,4 @@ += form.select :value, [champ.value].compact, + { include_blank: true }, + required: champ.mandatory?, + class: 'select2 communes' diff --git a/app/views/shared/dossiers/editable_champs/_departements.html.haml b/app/views/shared/dossiers/editable_champs/_departements.html.haml index e3084d148..9d0171092 100644 --- a/app/views/shared/dossiers/editable_champs/_departements.html.haml +++ b/app/views/shared/dossiers/editable_champs/_departements.html.haml @@ -1,4 +1,4 @@ -= form.select :value, - Champs::DepartementChamp.departements, - include_blank: true, - required: champ.mandatory? += form.select :value, [champ.value].compact, + { include_blank: true }, + required: champ.mandatory?, + class: 'select2 departements' diff --git a/app/views/shared/dossiers/editable_champs/_pays.html.haml b/app/views/shared/dossiers/editable_champs/_pays.html.haml index 13062d8e5..0e686fee9 100644 --- a/app/views/shared/dossiers/editable_champs/_pays.html.haml +++ b/app/views/shared/dossiers/editable_champs/_pays.html.haml @@ -1,5 +1,5 @@ = form.select :value, Champs::PaysChamp.pays, - disabled: Champs::PaysChamp.disabled_options, - include_blank: true, - required: champ.mandatory? + { disabled: Champs::PaysChamp.disabled_options, include_blank: true }, + required: champ.mandatory?, + class: 'select2 pays' diff --git a/app/views/shared/dossiers/editable_champs/_regions.html.haml b/app/views/shared/dossiers/editable_champs/_regions.html.haml index 8920cfc3c..4b16bea2d 100644 --- a/app/views/shared/dossiers/editable_champs/_regions.html.haml +++ b/app/views/shared/dossiers/editable_champs/_regions.html.haml @@ -1,4 +1,4 @@ -= form.select :value, - Champs::RegionChamp.regions, - include_blank: true, - required: champ.mandatory? += form.select :value, [champ.value].compact, + { include_blank: true }, + required: champ.mandatory?, + class: 'select2 regions' diff --git a/app/views/users/dossiers/show/_header.html.haml b/app/views/users/dossiers/show/_header.html.haml index 51d6a2797..e148b90d7 100644 --- a/app/views/users/dossiers/show/_header.html.haml +++ b/app/views/users/dossiers/show/_header.html.haml @@ -15,6 +15,12 @@ = render partial: 'invites/dropdown', locals: { dossier: dossier } - if dossier.can_be_updated_by_user? && !current_page?(modifier_dossier_path(dossier)) = link_to "Modifier mon dossier", modifier_dossier_path(dossier), class: 'button accepted edit-form', 'title'=> "Vous pouvez modifier votre dossier tant qu'il n'est passé en instruction" + %span.dropdown.print-menu-opener + %button.button.dropdown-button.icon-only{ title: 'imprimer' } + %span.icon.printer + %ul.print-menu.dropdown-content + %li + = link_to "Tout le dossier", dossier_path(dossier, format: :pdf), target: "_blank", rel: "noopener", class: "menu-item menu-link" %ul.tabs = dynamic_tab_item('Résumé', dossier_path(dossier)) diff --git a/config/env.example b/config/env.example index 5d9134cec..608fd59ff 100644 --- a/config/env.example +++ b/config/env.example @@ -105,3 +105,7 @@ LOGRAGE_ENABLED="disabled" # Service externe d'horodatage des changements de statut des dossiers (effectué quotidiennement) UNIVERSIGN_API_URL="" UNIVERSIGN_USERPWD="" + +# API Geo / Adresse +API_ADRESSE_URL="https://api-adresse.data.gouv.fr" +API_GEO_URL="https://geo.api.gouv.fr" diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index ee9bff7b4..1535e55ba 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -5,6 +5,7 @@ Rails.application.config.assets.version = '1.0' # Add additional assets to the asset load path Rails.application.config.assets.paths << Rails.root.join('node_modules', 'trix', 'dist') +Rails.application.config.assets.paths << Rails.root.join('node_modules', 'select2', 'dist', 'css') # Precompile additional assets. # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb index 960c3cd3d..588cd5079 100644 --- a/config/initializers/content_security_policy.rb +++ b/config/initializers/content_security_policy.rb @@ -13,7 +13,7 @@ Rails.application.config.content_security_policy do |policy| # c'est trop compliqué pour être rectifié immédiatement (et sans valeur ajoutée: # c'est hardcodé dans les vues, donc pas injectable). policy.style_src :self, "*.crisp.chat", "crisp.chat", :unsafe_inline - policy.connect_src :self, "wss://*.crisp.chat", "*.crisp.chat", "*.demarches-simplifiees.fr", "in-automate.sendinblue.com", "app.franceconnect.gouv.fr", "sentry.io" + policy.connect_src :self, "wss://*.crisp.chat", "*.crisp.chat", "*.demarches-simplifiees.fr", "in-automate.sendinblue.com", "app.franceconnect.gouv.fr", "sentry.io", "geo.api.gouv.fr", "api-adresse.data.gouv.fr" # Pour tout le reste, par défaut on accepte uniquement ce qui vient de chez nous # et dans la notification on inclue la source de l'erreur policy.default_src :self, :data, :report_sample, "fonts.gstatic.com", "in-automate.sendinblue.com", "player.vimeo.com", "app.franceconnect.gouv.fr", "sentry.io", "static.demarches-simplifiees.fr", "*.crisp.chat", "crisp.chat", "*.crisp.help", "*.sibautomation.com", "sibautomation.com", "data" diff --git a/config/initializers/geocoder.rb b/config/initializers/geocoder.rb new file mode 100644 index 000000000..972505d47 --- /dev/null +++ b/config/initializers/geocoder.rb @@ -0,0 +1 @@ +Geocoder.configure(lookup: :ban_data_gouv_fr) diff --git a/config/initializers/urls.rb b/config/initializers/urls.rb index 53aca743e..00b747247 100644 --- a/config/initializers/urls.rb +++ b/config/initializers/urls.rb @@ -1,9 +1,6 @@ # API URLs -API_ADRESSE_URL = ENV.fetch("API_ADRESSE_URL", "https://api-adresse.data.gouv.fr") API_CARTO_URL = ENV.fetch("API_CARTO_URL", "https://sandbox.geo.api.gouv.fr/apicarto") API_ENTREPRISE_URL = ENV.fetch("API_ENTREPRISE_URL", "https://entreprise.api.gouv.fr/v2") -API_GEO_URL = ENV.fetch("API_GEO_URL", "https://geo.api.gouv.fr") -API_GEO_SANDBOX_URL = ENV.fetch("API_GEO_SANDBOX_URL", "https://sandbox.geo.api.gouv.fr") HELPSCOUT_API_URL = ENV.fetch("HELPSCOUT_API_URL", "https://api.helpscout.net/v2") PIPEDRIVE_API_URL = ENV.fetch("PIPEDRIVE_API_URL", "https://api.pipedrive.com/v1") SENDINBLUE_API_URL = ENV.fetch("SENDINBLUE_API_URL", "https://in-automate.sendinblue.com/api/v2") diff --git a/config/locales/models/type_de_champ/fr.yml b/config/locales/models/type_de_champ/fr.yml index cf97e106f..9d8b878d3 100644 --- a/config/locales/models/type_de_champ/fr.yml +++ b/config/locales/models/type_de_champ/fr.yml @@ -24,6 +24,7 @@ fr: pays: 'Pays' regions: 'Régions' departements: 'Départements' + communes: 'Communes' engagement: 'Engagement' header_section: 'Titre de section' explication: 'Explication' diff --git a/config/locales/views/new_administrateur/groupe_instructeurs/fr.yml b/config/locales/views/new_administrateur/groupe_instructeurs/fr.yml index 4e0ace44d..12f1f652a 100644 --- a/config/locales/views/new_administrateur/groupe_instructeurs/fr.yml +++ b/config/locales/views/new_administrateur/groupe_instructeurs/fr.yml @@ -16,3 +16,7 @@ fr: assignment: one: "L’instructeur %{value} a été affecté au groupe « %{groupe} »." other: "Les instructeurs %{value} ont été affectés au groupe « %{groupe} »." + reaffecter_dossiers: + existing_groupe: + one: "%{count} groupe existe" + other: "%{count} groupes existent" diff --git a/config/routes.rb b/config/routes.rb index ce14ec9ee..e69a44a60 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -32,6 +32,7 @@ Rails.application.routes.draw do resources :instructeurs, only: [:index, :show] do post 'reinvite', on: :member + delete 'delete', on: :member end resources :dossiers, only: [:show] @@ -209,13 +210,6 @@ Rails.application.routes.draw do resources :instructeurs, only: [:index, :create, :destroy] end - # - # Addresses - # - - get 'address/suggestions' => 'address#suggestions' - get 'address/geocode' => 'address#geocode' - resources :invites, only: [:show] do collection do post 'dossier/:dossier_id', to: 'invites#create', as: :dossier @@ -363,10 +357,12 @@ Rails.application.routes.draw do get 'annotations' end - resources :groupe_instructeurs, only: [:index, :show, :create, :update] do + resources :groupe_instructeurs, only: [:index, :show, :create, :update, :destroy] do member do post 'add_instructeur' delete 'remove_instructeur' + get 'reaffecter_dossiers' + post 'reaffecter' end collection do @@ -394,6 +390,14 @@ Rails.application.routes.draw do end end + if Rails.env.test? + scope 'test/api_geo' do + get 'regions' => 'api_geo_test#regions' + get 'communes' => 'api_geo_test#communes' + get 'departements' => 'api_geo_test#departements' + end + end + # # Legacy routes # diff --git a/config/secrets.yml b/config/secrets.yml index 6b0afe9dc..3d7ffa9af 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -68,6 +68,9 @@ defaults: &defaults client_key: <%= ENV['CRISP_CLIENT_KEY'] %> universign: userpwd: <%= ENV['UNIVERSIGN_USERPWD'] %> + autocomplete: + api_geo_url: <%= ENV['API_GEO_URL'] %> + api_adresse_url: <%= ENV['API_ADRESSE_URL'] %> @@ -98,6 +101,8 @@ test: logout_endpoint: https://bidon.com/endpoint universign: userpwd: 'fake:fake' + autocomplete: + api_geo_url: /test/api_geo # Do not keep production secrets in the repository, # instead read values from the environment. diff --git a/package.json b/package.json index 8ccc7fce8..94fe7a75b 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ "@rails/webpacker": "4.0.7", "@sentry/browser": "^5.7.1", "@turf/area": "^6.0.1", - "autocomplete.js": "^0.37.0", "babel-plugin-macros": "^2.6.1", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "chartkick": "^3.1.3", diff --git a/spec/controllers/address_controller_spec.rb b/spec/controllers/address_controller_spec.rb deleted file mode 100644 index 99a2e3218..000000000 --- a/spec/controllers/address_controller_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'spec_helper' - -describe AddressController, type: :controller do - describe '#GET suggestions' do - subject { get :suggestions, params: { request: request } } - - before do - subject - end - - context 'when request return result', vcr: { cassette_name: 'api_adresse_search_paris_2' } do - let (:request) { 'Paris' } - - it { expect(response.status).to eq 200 } - it { expect(response.body).to eq '[{"label":"Paris"},{"label":"Paris 63120 Courpière"},{"label":"PARIS (Vaillac) 46240 Cœur de Causse"},{"label":"Paris 40500 Saint-Sever"},{"label":"Paris Buton 37140 Bourgueil"}]' } - end - - context 'when request return nothing', vcr: { cassette_name: 'api_adresse_search_nothing_2' } do - let (:request) { 'je recherche pas grand chose' } - - it { expect(response.status).to eq 200 } - it { expect(response.body).to eq "[]" } - end - end - - describe '#GET geocode' do - let(:dossier_id) { "1" } - subject { get :geocode, params: { request: request, dossier_id: dossier_id } } - - before do - subject - end - - context 'when request return result', vcr: { cassette_name: 'api_adresse_search_paris' } do - let(:request) { 'Paris' } - - it { expect(response.body).to eq ({ lon: '2.3469', lat: '48.8589', zoom: '14', dossier_id: dossier_id }).to_json } - end - - context 'when request return nothing', vcr: { cassette_name: 'api_adresse_search_nothing' } do - let(:request) { 'je recherche pas grand chose' } - - it { expect(response.body).to eq ({ lon: nil, lat: nil, zoom: '14', dossier_id: dossier_id }).to_json } - end - end -end diff --git a/spec/controllers/instructeurs/dossiers_controller_spec.rb b/spec/controllers/instructeurs/dossiers_controller_spec.rb index 264d68ae3..f080ab548 100644 --- a/spec/controllers/instructeurs/dossiers_controller_spec.rb +++ b/spec/controllers/instructeurs/dossiers_controller_spec.rb @@ -473,6 +473,34 @@ describe Instructeurs::DossiersController, type: :controller do end end + describe "#show" do + context "when the dossier is exported as PDF" do + let(:instructeur) { create(:instructeur) } + let(:dossier) { + create(:dossier, + :accepte, + :with_all_champs, + :with_all_annotations, + :with_motivation, + :with_commentaires, procedure: procedure) +} + let!(:avis) { create(:avis, dossier: dossier, instructeur: instructeur) } + subject do + get :show, params: { + procedure_id: procedure.id, + dossier_id: dossier.id, + format: :pdf + } + end + + before do + subject + end + it { expect(assigns(:include_infos_administration)).to eq(true) } + it { expect(response).to render_template 'dossiers/show' } + end + end + describe "#update_annotations" do let(:champ_multiple_drop_down_list) do create(:type_de_champ_multiple_drop_down_list, :private, libelle: 'libelle').champ.create diff --git a/spec/controllers/manager/instructeurs_controller_spec.rb b/spec/controllers/manager/instructeurs_controller_spec.rb new file mode 100644 index 000000000..3a0164d4b --- /dev/null +++ b/spec/controllers/manager/instructeurs_controller_spec.rb @@ -0,0 +1,17 @@ +describe Manager::InstructeursController, type: :controller do + let(:administration) { create(:administration) } + + describe '#delete' do + let!(:instructeur) { create(:instructeur) } + + before { sign_in administration } + + subject { delete :delete, params: { id: instructeur.id } } + + it 'deletes the instructeur' do + subject + + expect(Instructeur.find_by(id: instructeur.id)).to be_nil + end + end +end diff --git a/spec/controllers/new_administrateur/groupe_instructeurs_controller_spec.rb b/spec/controllers/new_administrateur/groupe_instructeurs_controller_spec.rb index e312b7c84..93d02b5fe 100644 --- a/spec/controllers/new_administrateur/groupe_instructeurs_controller_spec.rb +++ b/spec/controllers/new_administrateur/groupe_instructeurs_controller_spec.rb @@ -59,6 +59,106 @@ describe NewAdministrateur::GroupeInstructeursController, type: :controller do end end + describe '#destroy' do + def delete_group(group) + delete :destroy, + params: { + procedure_id: procedure.id, + id: group.id + } + end + + context 'with only one group' do + before { delete_group gi_1_1 } + + it { expect(flash.alert).to be_present } + it { expect(response).to redirect_to(procedure_groupe_instructeurs_path(procedure)) } + it { expect(procedure.groupe_instructeurs.count).to eq(1) } + end + + context 'with many groups' do + let!(:gi_1_2) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 2') } + + context 'of a group that can be deleted' do + before { delete_group gi_1_2 } + it { expect(flash.notice).to be_present } + it { expect(procedure.groupe_instructeurs.count).to eq(1) } + it { expect(response).to redirect_to(procedure_groupe_instructeurs_path(procedure)) } + end + + context 'of a group with dossiers, that cannot be deleted' do + let!(:dossier12) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction), groupe_instructeur: gi_1_2) } + before { delete_group gi_1_2 } + + it { expect(flash.alert).to be_present } + it { expect(procedure.groupe_instructeurs.count).to eq(2) } + it { expect(response).to redirect_to(procedure_groupe_instructeurs_path(procedure)) } + end + end + end + + describe '#reaffecter_dossiers' do + let!(:gi_1_2) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 2') } + let!(:gi_1_3) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 3') } + + before do + get :reaffecter_dossiers, + params: { + procedure_id: procedure.id, + id: gi_1_2.id + } + end + def reaffecter_url(group) + reaffecter_procedure_groupe_instructeur_path(:id => gi_1_2, + :target_group => group) + end + + it { expect(response).to have_http_status(:ok) } + it { expect(response.body).to include(reaffecter_url(procedure.defaut_groupe_instructeur)) } + it { expect(response.body).not_to include(reaffecter_url(gi_1_2)) } + it { expect(response.body).to include(reaffecter_url(gi_1_3)) } + end + + describe '#reaffecter' do + let!(:gi_1_2) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 2') } + let!(:dossier12) { create(:dossier, procedure: procedure, state: Dossier.states.fetch(:en_construction), groupe_instructeur: gi_1_1) } + + describe 'when the new group is a group of the procedure' do + before do + post :reaffecter, + params: { + procedure_id: procedure.id, + id: gi_1_1.id, + target_group: gi_1_2.id + } + dossier12.reload + end + + it { expect(response).to redirect_to(procedure_groupe_instructeurs_path(procedure)) } + it { expect(gi_1_1.dossiers.count).to be(0) } + it { expect(gi_1_2.dossiers.count).to be(1) } + it { expect(gi_1_2.dossiers.last.id).to be(dossier12.id) } + it { expect(dossier12.groupe_instructeur.id).to be(gi_1_2.id) } + end + + describe 'when the target group is not a possible group' do + subject { + post :reaffecter, + params: + { + procedure_id: procedure.id, + id: gi_1_1.id, + target_group: gi_2_2.id + } + } + before do + dossier12.reload + end + + it { expect { subject }.to raise_error(ActiveRecord::RecordNotFound) } + end + end + describe '#update' do let(:new_name) { 'nouveau nom du groupe' } diff --git a/spec/controllers/users/dossiers_controller_spec.rb b/spec/controllers/users/dossiers_controller_spec.rb index f9fd181b5..3671ae490 100644 --- a/spec/controllers/users/dossiers_controller_spec.rb +++ b/spec/controllers/users/dossiers_controller_spec.rb @@ -731,17 +731,37 @@ describe Users::DossiersController, type: :controller do sign_in(user) end - subject! { get(:show, params: { id: dossier.id }) } + context 'with default output' do + subject! { get(:show, params: { id: dossier.id }) } - context 'when the dossier is a brouillon' do - let(:dossier) { create(:dossier, user: user) } - it { is_expected.to redirect_to(brouillon_dossier_path(dossier)) } + context 'when the dossier is a brouillon' do + let(:dossier) { create(:dossier, user: user) } + it { is_expected.to redirect_to(brouillon_dossier_path(dossier)) } + end + + context 'when the dossier has been submitted' do + let(:dossier) { create(:dossier, :en_construction, user: user) } + it { expect(assigns(:dossier)).to eq(dossier) } + it { is_expected.to render_template(:show) } + end end - context 'when the dossier has been submitted' do - let(:dossier) { create(:dossier, :en_construction, user: user) } - it { expect(assigns(:dossier)).to eq(dossier) } - it { is_expected.to render_template(:show) } + context "with PDF output" do + let(:procedure) { create(:procedure) } + let(:dossier) { + create(:dossier, + :accepte, + :with_all_champs, + :with_motivation, + :with_commentaires, + procedure: procedure, + user: user) +} + + subject! { get(:show, params: { id: dossier.id, format: :pdf }) } + + it { expect(assigns(:include_infos_administration)).to eq(false) } + it { expect(response).to render_template 'dossiers/show' } end end diff --git a/spec/factories/champ.rb b/spec/factories/champ.rb index b9913a536..4b10f249f 100644 --- a/spec/factories/champ.rb +++ b/spec/factories/champ.rb @@ -124,6 +124,11 @@ FactoryBot.define do value { '971 - Guadeloupe' } end + factory :champ_communes, class: 'Champs::CommuneChamp' do + type_de_champ { create(:type_de_champ_communes) } + value { 'Paris' } + end + factory :champ_engagement, class: 'Champs::EngagementChamp' do type_de_champ { create(:type_de_champ_engagement) } value { 'true' } diff --git a/spec/factories/type_de_champ.rb b/spec/factories/type_de_champ.rb index 2bc898b09..597d84c9e 100644 --- a/spec/factories/type_de_champ.rb +++ b/spec/factories/type_de_champ.rb @@ -71,6 +71,9 @@ FactoryBot.define do factory :type_de_champ_departements do type_champ { TypeDeChamp.type_champs.fetch(:departements) } end + factory :type_de_champ_communes do + type_champ { TypeDeChamp.type_champs.fetch(:communes) } + end factory :type_de_champ_engagement do type_champ { TypeDeChamp.type_champs.fetch(:engagement) } end diff --git a/spec/features/users/brouillon_spec.rb b/spec/features/users/brouillon_spec.rb index 90bea2630..f74f6c4f4 100644 --- a/spec/features/users/brouillon_spec.rb +++ b/spec/features/users/brouillon_spec.rb @@ -10,10 +10,7 @@ feature 'The user' do # TODO: check # the order # there are no extraneous input - scenario 'fill a dossier', js: true do - allow(Champs::RegionChamp).to receive(:regions).and_return(['region1', 'region2']).at_least(:once) - allow(Champs::DepartementChamp).to receive(:departements).and_return(['dep1', 'dep2']).at_least(:once) - + scenario 'fill a dossier', js: true, vcr: { cassette_name: 'api_geo_departements_regions_et_communes' } do log_in(user, procedure) fill_individual @@ -33,8 +30,16 @@ feature 'The user' do select('val1', from: form_id_for('multiple_drop_down_list')) select('val3', from: form_id_for('multiple_drop_down_list')) select('AUSTRALIE', from: 'pays') - select('region2', from: 'regions') - select('dep2', from: 'departements') + + select_champ_geo('regions', 'Ma', 'Martinique') + select('Martinique', from: 'regions') + + select_champ_geo('departements', 'Ai', '02 - Aisne') + select('02 - Aisne', from: 'departements') + + select_champ_geo('communes', 'Am', 'Ambléon') + select('Ambléon', from: 'communes') + check('engagement') fill_in('dossier_link', with: '123') find('.editable-champ-piece_justificative input[type=file]').attach_file(Rails.root + 'spec/fixtures/files/file.pdf') @@ -57,8 +62,9 @@ feature 'The user' do expect(champ_value_for('simple_drop_down_list')).to eq('val2') expect(JSON.parse(champ_value_for('multiple_drop_down_list'))).to match(['val1', 'val3']) expect(champ_value_for('pays')).to eq('AUSTRALIE') - expect(champ_value_for('regions')).to eq('region2') - expect(champ_value_for('departements')).to eq('dep2') + expect(champ_value_for('regions')).to eq('Martinique') + expect(champ_value_for('departements')).to eq('02 - Aisne') + expect(champ_value_for('communes')).to eq('Ambléon') expect(champ_value_for('engagement')).to eq('on') expect(champ_value_for('dossier_link')).to eq('123') expect(champ_value_for('piece_justificative')).to be_nil # antivirus hasn't approved the file yet @@ -78,8 +84,9 @@ feature 'The user' do expect(page).to have_selected_value('simple_drop_down_list', selected: 'val2') expect(page).to have_selected_value('multiple_drop_down_list', selected: ['val1', 'val3']) expect(page).to have_selected_value('pays', selected: 'AUSTRALIE') - expect(page).to have_selected_value('regions', selected: 'region2') - expect(page).to have_selected_value('departement', selected: 'dep2') + expect(page).to have_selected_value('regions', selected: 'Martinique') + expect(page).to have_selected_value('departements', selected: '02 - Aisne') + expect(page).to have_selected_value('communes', selected: 'Ambléon') expect(page).to have_checked_field('engagement') expect(page).to have_field('dossier_link', with: '123') expect(page).to have_text('file.pdf') @@ -270,4 +277,12 @@ feature 'The user' do expect(page).to have_selected_value("#{field}_4i", selected: date.strftime('%H')) expect(page).to have_selected_value("#{field}_5i", selected: date.strftime('%M')) end + + def select_champ_geo(champ, fill_with, value) + find(".editable-champ-#{champ} .select2-container").click + id = find('.select2-container--open [role=listbox]')[:id] + find("[aria-controls=#{id}]").fill_in with: fill_with + expect(page).to have_content(value) + find('li', text: value).click + end end diff --git a/spec/features/users/dossier_creation_spec.rb b/spec/features/users/dossier_creation_spec.rb index f6dc8de5b..662d02918 100644 --- a/spec/features/users/dossier_creation_spec.rb +++ b/spec/features/users/dossier_creation_spec.rb @@ -77,7 +77,7 @@ feature 'Creating a new dossier:' do .to_return(status: 404, body: '') end - scenario 'the user can enter the SIRET of its etablissement and create a new draft', vcr: { cassette_name: 'api_adresse_search_paris_3' } do + scenario 'the user can enter the SIRET of its etablissement and create a new draft' do visit commencer_path(path: procedure.path) click_on 'Commencer la démarche' diff --git a/spec/fixtures/cassettes/api_adresse_octo.yml b/spec/fixtures/cassettes/api_adresse_octo.yml deleted file mode 100644 index 28ed2accb..000000000 --- a/spec/fixtures/cassettes/api_adresse_octo.yml +++ /dev/null @@ -1,47 +0,0 @@ ---- -http_interactions: -- request: - method: get - uri: https://api-adresse.data.gouv.fr/search?limit=1&q=50%20av%20des%20champs%20elysees - body: - encoding: US-ASCII - string: '' - headers: - Accept: - - "*/*" - Accept-Encoding: - - gzip, deflate - User-Agent: - - rest-client/2.0.0 (darwin15.6.0 x86_64) ruby/2.3.1p112 - response: - status: - code: 200 - message: OK - headers: - Server: - - nginx/1.11.3 - Date: - - Fri, 16 Dec 2016 16:22:23 GMT - Content-Type: - - application/json; charset=utf-8 - Content-Length: - - '628' - Connection: - - keep-alive - Access-Control-Allow-Origin: - - "*" - Access-Control-Allow-Headers: - - X-Requested-With - body: - encoding: UTF-8 - string: '{"limit": 1, "attribution": "BAN", "version": "draft", "licence": "ODbL - 1.0", "query": "50 av des champs elysees", "type": "FeatureCollection", "features": - [{"geometry": {"type": "Point", "coordinates": [2.306888, 48.870374]}, "properties": - {"citycode": "75108", "postcode": "75008", "name": "50 Avenue des Champs \u00c9lys\u00e9es", - "id": "ADRNIVX_0000000270748251", "type": "housenumber", "context": "75, \u00cele-de-France", - "score": 0.7561038961038961, "label": "50 Avenue des Champs \u00c9lys\u00e9es - 75008 Paris", "city": "Paris", "housenumber": "50", "street": "Avenue des - Champs \u00c9lys\u00e9es"}, "type": "Feature"}]}' - http_version: - recorded_at: Fri, 16 Dec 2016 16:22:23 GMT -recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/cassettes/api_adresse_search_nothing.yml b/spec/fixtures/cassettes/api_adresse_search_nothing.yml deleted file mode 100644 index d951adbfc..000000000 --- a/spec/fixtures/cassettes/api_adresse_search_nothing.yml +++ /dev/null @@ -1,42 +0,0 @@ ---- -http_interactions: -- request: - method: get - uri: https://api-adresse.data.gouv.fr/search?limit=1&q=je%20recherche%20pas%20grand%20chose - body: - encoding: US-ASCII - string: '' - headers: - Accept: - - "*/*" - Accept-Encoding: - - gzip, deflate - User-Agent: - - rest-client/2.0.0 (darwin15.6.0 x86_64) ruby/2.3.1p112 - response: - status: - code: 200 - message: OK - headers: - Server: - - nginx/1.11.3 - Date: - - Fri, 16 Dec 2016 14:17:40 GMT - Content-Type: - - application/json; charset=utf-8 - Content-Length: - - '163' - Connection: - - keep-alive - Access-Control-Allow-Origin: - - "*" - Access-Control-Allow-Headers: - - X-Requested-With - body: - encoding: UTF-8 - string: '{"limit": 1, "attribution": "BAN", "version": "draft", "licence": "ODbL - 1.0", "query": "je recherche pas grand chose", "type": "FeatureCollection", - "features": []}' - http_version: - recorded_at: Fri, 16 Dec 2016 14:17:40 GMT -recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/cassettes/api_adresse_search_nothing_2.yml b/spec/fixtures/cassettes/api_adresse_search_nothing_2.yml deleted file mode 100644 index cbb4a0f66..000000000 --- a/spec/fixtures/cassettes/api_adresse_search_nothing_2.yml +++ /dev/null @@ -1,42 +0,0 @@ ---- -http_interactions: -- request: - method: get - uri: https://api-adresse.data.gouv.fr/search?limit=5&q=je%20recherche%20pas%20grand%20chose - body: - encoding: US-ASCII - string: '' - headers: - Accept: - - "*/*" - Accept-Encoding: - - gzip, deflate - User-Agent: - - rest-client/2.0.0 (darwin15.6.0 x86_64) ruby/2.3.1p112 - response: - status: - code: 200 - message: OK - headers: - Server: - - nginx/1.11.3 - Date: - - Fri, 16 Dec 2016 16:45:53 GMT - Content-Type: - - application/json; charset=utf-8 - Content-Length: - - '163' - Connection: - - keep-alive - Access-Control-Allow-Origin: - - "*" - Access-Control-Allow-Headers: - - X-Requested-With - body: - encoding: UTF-8 - string: '{"limit": 5, "attribution": "BAN", "version": "draft", "licence": "ODbL - 1.0", "query": "je recherche pas grand chose", "type": "FeatureCollection", - "features": []}' - http_version: - recorded_at: Fri, 16 Dec 2016 16:45:53 GMT -recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/cassettes/api_adresse_search_paris.yml b/spec/fixtures/cassettes/api_adresse_search_paris.yml deleted file mode 100644 index ced667e17..000000000 --- a/spec/fixtures/cassettes/api_adresse_search_paris.yml +++ /dev/null @@ -1,45 +0,0 @@ ---- -http_interactions: -- request: - method: get - uri: https://api-adresse.data.gouv.fr/search?limit=1&q=Paris - body: - encoding: US-ASCII - string: '' - headers: - Accept: - - "*/*" - Accept-Encoding: - - gzip, deflate - User-Agent: - - rest-client/2.0.0 (darwin15.6.0 x86_64) ruby/2.3.1p112 - response: - status: - code: 200 - message: OK - headers: - Server: - - nginx/1.11.3 - Date: - - Fri, 16 Dec 2016 14:16:43 GMT - Content-Type: - - application/json; charset=utf-8 - Content-Length: - - '457' - Connection: - - keep-alive - Access-Control-Allow-Origin: - - "*" - Access-Control-Allow-Headers: - - X-Requested-With - body: - encoding: UTF-8 - string: '{"limit": 1, "attribution": "BAN", "version": "draft", "licence": "ODbL - 1.0", "query": "Paris", "type": "FeatureCollection", "features": [{"geometry": - {"type": "Point", "coordinates": [2.3469, 48.8589]}, "properties": {"adm_weight": - "6", "citycode": "75056", "name": "Paris", "city": "Paris", "postcode": "75000", - "context": "75, \u00cele-de-France", "score": 1.0, "label": "Paris", "id": - "75056", "type": "city", "population": "2244"}, "type": "Feature"}]}' - http_version: - recorded_at: Fri, 16 Dec 2016 14:16:43 GMT -recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/cassettes/api_adresse_search_paris_2.yml b/spec/fixtures/cassettes/api_adresse_search_paris_2.yml deleted file mode 100644 index 5faf1cd88..000000000 --- a/spec/fixtures/cassettes/api_adresse_search_paris_2.yml +++ /dev/null @@ -1,63 +0,0 @@ ---- -http_interactions: -- request: - method: get - uri: https://api-adresse.data.gouv.fr/search?limit=5&q=Paris - body: - encoding: US-ASCII - string: '' - headers: - Accept: - - "*/*" - Accept-Encoding: - - gzip, deflate - User-Agent: - - rest-client/2.0.0 (darwin15.6.0 x86_64) ruby/2.3.1p112 - response: - status: - code: 200 - message: OK - headers: - Server: - - nginx/1.11.3 - Date: - - Fri, 16 Dec 2016 16:43:34 GMT - Content-Type: - - application/json; charset=utf-8 - Content-Length: - - '1887' - Connection: - - keep-alive - Access-Control-Allow-Origin: - - "*" - Access-Control-Allow-Headers: - - X-Requested-With - body: - encoding: UTF-8 - string: '{"limit": 5, "attribution": "BAN", "version": "draft", "licence": "ODbL - 1.0", "query": "Paris", "type": "FeatureCollection", "features": [{"geometry": - {"type": "Point", "coordinates": [2.3469, 48.8589]}, "properties": {"adm_weight": - "6", "citycode": "75056", "name": "Paris", "city": "Paris", "postcode": "75000", - "context": "75, \u00cele-de-France", "score": 1.0, "label": "Paris", "id": - "75056", "type": "city", "population": "2244"}, "type": "Feature"}, {"geometry": - {"type": "Point", "coordinates": [3.564293, 45.766413]}, "properties": {"citycode": - "63125", "postcode": "63120", "name": "Paris", "city": "Courpi\u00e8re", "context": - "63, Puy-de-D\u00f4me, Auvergne", "score": 0.8255363636363636, "label": "Paris - 63120 Courpi\u00e8re", "id": "63125_B221_03549b", "type": "locality"}, "type": - "Feature"}, {"geometry": {"type": "Point", "coordinates": [1.550208, 44.673592]}, - "properties": {"citycode": "46138", "postcode": "46240", "name": "PARIS (Vaillac)", - "city": "C\u0153ur de Causse", "context": "46, Lot, Midi-Pyr\u00e9n\u00e9es", - "score": 0.824090909090909, "label": "PARIS (Vaillac) 46240 C\u0153ur de Causse", - "id": "46138_XXXX_6ee4ec", "type": "street"}, "type": "Feature"}, {"geometry": - {"type": "Point", "coordinates": [-0.526884, 43.762253]}, "properties": {"citycode": - "40282", "postcode": "40500", "name": "Paris", "city": "Saint-Sever", "context": - "40, Landes, Aquitaine", "score": 0.8236181818181818, "label": "Paris 40500 - Saint-Sever", "id": "40282_B237_2364e3", "type": "locality"}, "type": "Feature"}, - {"geometry": {"type": "Point", "coordinates": [0.157613, 47.336685]}, "properties": - {"citycode": "37031", "postcode": "37140", "name": "Paris Buton", "city": - "Bourgueil", "context": "37, Indre-et-Loire, Centre Val-de-Loire", "score": - 0.8235454545454545, "label": "Paris Buton 37140 Bourgueil", "id": "37031_X027_0a5e7a", - "type": "locality"}, "type": "Feature"}]}' - http_version: - recorded_at: Fri, 16 Dec 2016 16:43:34 GMT -recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/cassettes/api_adresse_search_paris_3.yml b/spec/fixtures/cassettes/api_adresse_search_paris_3.yml deleted file mode 100644 index 22eca3306..000000000 --- a/spec/fixtures/cassettes/api_adresse_search_paris_3.yml +++ /dev/null @@ -1,93 +0,0 @@ ---- -http_interactions: -- request: - method: get - uri: https://api-adresse.data.gouv.fr/search?limit=1&q=50%20AV%20DES%20CHAMPS%20ELYSEES%20complement_adresse%2075008%20PARIS%208 - body: - encoding: US-ASCII - string: '' - headers: - Accept: - - "*/*" - Accept-Encoding: - - gzip, deflate - User-Agent: - - rest-client/2.0.0 (darwin15.6.0 x86_64) ruby/2.3.1p112 - response: - status: - code: 200 - message: OK - headers: - Server: - - nginx/1.11.3 - Date: - - Tue, 03 Jan 2017 13:00:54 GMT - Content-Type: - - application/json; charset=utf-8 - Content-Length: - - '660' - Connection: - - keep-alive - Access-Control-Allow-Origin: - - "*" - Access-Control-Allow-Headers: - - X-Requested-With - body: - encoding: UTF-8 - string: '{"limit": 1, "attribution": "BAN", "version": "draft", "licence": "ODbL - 1.0", "query": "50 AV DES CHAMPS ELYSEES complement_adresse 75008 PARIS 8", - "type": "FeatureCollection", "features": [{"geometry": {"type": "Point", "coordinates": - [2.306888, 48.870374]}, "properties": {"citycode": "75108", "postcode": "75008", - "name": "50 Avenue des Champs \u00c9lys\u00e9es", "id": "ADRNIVX_0000000270748251", - "type": "housenumber", "context": "75, \u00cele-de-France", "score": 0.596517719568567, - "label": "50 Avenue des Champs \u00c9lys\u00e9es 75008 Paris", "city": "Paris", - "housenumber": "50", "street": "Avenue des Champs \u00c9lys\u00e9es"}, "type": - "Feature"}]}' - http_version: - recorded_at: Tue, 03 Jan 2017 13:00:54 GMT -- request: - method: get - uri: http://api-adresse.data.gouv.fr/search?limit=1&q=50%20AV%20DES%20CHAMPS%20ELYSEES%20complement_adresse%2075008%20PARIS%208 - body: - encoding: US-ASCII - string: '' - headers: - Accept: - - "*/*" - Accept-Encoding: - - gzip, deflate - User-Agent: - - rest-client/2.0.0 (darwin15.6.0 x86_64) ruby/2.3.1p112 - response: - status: - code: 200 - message: OK - headers: - Server: - - nginx/1.11.3 - Date: - - Tue, 03 Jan 2017 13:00:55 GMT - Content-Type: - - application/json; charset=utf-8 - Content-Length: - - '660' - Connection: - - keep-alive - Access-Control-Allow-Origin: - - "*" - Access-Control-Allow-Headers: - - X-Requested-With - body: - encoding: UTF-8 - string: '{"limit": 1, "attribution": "BAN", "version": "draft", "licence": "ODbL - 1.0", "query": "50 AV DES CHAMPS ELYSEES complement_adresse 75008 PARIS 8", - "type": "FeatureCollection", "features": [{"geometry": {"type": "Point", "coordinates": - [2.306888, 48.870374]}, "properties": {"citycode": "75108", "postcode": "75008", - "name": "50 Avenue des Champs \u00c9lys\u00e9es", "id": "ADRNIVX_0000000270748251", - "type": "housenumber", "context": "75, \u00cele-de-France", "score": 0.596517719568567, - "label": "50 Avenue des Champs \u00c9lys\u00e9es 75008 Paris", "city": "Paris", - "housenumber": "50", "street": "Avenue des Champs \u00c9lys\u00e9es"}, "type": - "Feature"}]}' - http_version: - recorded_at: Tue, 03 Jan 2017 13:00:55 GMT -recorded_with: VCR 3.0.3 diff --git a/spec/fixtures/cassettes/api_geo_departements.yml b/spec/fixtures/cassettes/api_geo_departements.yml deleted file mode 100644 index 9a7dcd8b3..000000000 --- a/spec/fixtures/cassettes/api_geo_departements.yml +++ /dev/null @@ -1,51 +0,0 @@ ---- -http_interactions: -- request: - method: get - uri: https://geo.api.gouv.fr/departements?fields=nom - body: - encoding: US-ASCII - string: '' - headers: - User-Agent: - - demarches-simplifiees.fr - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Expect: - - '' - response: - status: - code: 200 - message: OK - headers: - Server: - - nginx/1.10.3 (Ubuntu) - Date: - - Tue, 23 Oct 2018 13:11:36 GMT - Content-Type: - - application/json; charset=utf-8 - Transfer-Encoding: - - chunked - Connection: - - keep-alive - Vary: - - Accept-Encoding - X-Powered-By: - - Express - Access-Control-Allow-Origin: - - "*" - Etag: - - W/"cc1-jlb3C7xpXUEaq56Wojrp9rAkoH8" - Strict-Transport-Security: - - max-age=15552000 - Content-Encoding: - - gzip - body: - encoding: ASCII-8BIT - string: !binary |- - W3sibm9tIjoiQWluIiwiY29kZSI6IjAxIn0seyJub20iOiJBaXNuZSIsImNvZGUiOiIwMiJ9LHsibm9tIjoiQWxsaWVyIiwiY29kZSI6IjAzIn0seyJub20iOiJBbHBlcy1kZS1IYXV0ZS1Qcm92ZW5jZSIsImNvZGUiOiIwNCJ9LHsibm9tIjoiSGF1dGVzLUFscGVzIiwiY29kZSI6IjA1In0seyJub20iOiJBbHBlcy1NYXJpdGltZXMiLCJjb2RlIjoiMDYifSx7Im5vbSI6IkFyZMOoY2hlIiwiY29kZSI6IjA3In0seyJub20iOiJBcmRlbm5lcyIsImNvZGUiOiIwOCJ9LHsibm9tIjoiQXJpw6hnZSIsImNvZGUiOiIwOSJ9LHsibm9tIjoiQXViZSIsImNvZGUiOiIxMCJ9LHsibm9tIjoiQXVkZSIsImNvZGUiOiIxMSJ9LHsibm9tIjoiQXZleXJvbiIsImNvZGUiOiIxMiJ9LHsibm9tIjoiQm91Y2hlcy1kdS1SaMO0bmUiLCJjb2RlIjoiMTMifSx7Im5vbSI6IkNhbHZhZG9zIiwiY29kZSI6IjE0In0seyJub20iOiJDYW50YWwiLCJjb2RlIjoiMTUifSx7Im5vbSI6IkNoYXJlbnRlIiwiY29kZSI6IjE2In0seyJub20iOiJDaGFyZW50ZS1NYXJpdGltZSIsImNvZGUiOiIxNyJ9LHsibm9tIjoiQ2hlciIsImNvZGUiOiIxOCJ9LHsibm9tIjoiQ29ycsOoemUiLCJjb2RlIjoiMTkifSx7Im5vbSI6IkPDtHRlLWQnT3IiLCJjb2RlIjoiMjEifSx7Im5vbSI6IkPDtHRlcy1kJ0FybW9yIiwiY29kZSI6IjIyIn0seyJub20iOiJDcmV1c2UiLCJjb2RlIjoiMjMifSx7Im5vbSI6IkRvcmRvZ25lIiwiY29kZSI6IjI0In0seyJub20iOiJEb3VicyIsImNvZGUiOiIyNSJ9LHsibm9tIjoiRHLDtG1lIiwiY29kZSI6IjI2In0seyJub20iOiJFdXJlIiwiY29kZSI6IjI3In0seyJub20iOiJFdXJlLWV0LUxvaXIiLCJjb2RlIjoiMjgifSx7Im5vbSI6IkZpbmlzdMOocmUiLCJjb2RlIjoiMjkifSx7Im5vbSI6IkNvcnNlLWR1LVN1ZCIsImNvZGUiOiIyQSJ9LHsibm9tIjoiSGF1dGUtQ29yc2UiLCJjb2RlIjoiMkIifSx7Im5vbSI6IkdhcmQiLCJjb2RlIjoiMzAifSx7Im5vbSI6IkhhdXRlLUdhcm9ubmUiLCJjb2RlIjoiMzEifSx7Im5vbSI6IkdlcnMiLCJjb2RlIjoiMzIifSx7Im5vbSI6Ikdpcm9uZGUiLCJjb2RlIjoiMzMifSx7Im5vbSI6IkjDqXJhdWx0IiwiY29kZSI6IjM0In0seyJub20iOiJJbGxlLWV0LVZpbGFpbmUiLCJjb2RlIjoiMzUifSx7Im5vbSI6IkluZHJlIiwiY29kZSI6IjM2In0seyJub20iOiJJbmRyZS1ldC1Mb2lyZSIsImNvZGUiOiIzNyJ9LHsibm9tIjoiSXPDqHJlIiwiY29kZSI6IjM4In0seyJub20iOiJKdXJhIiwiY29kZSI6IjM5In0seyJub20iOiJMYW5kZXMiLCJjb2RlIjoiNDAifSx7Im5vbSI6IkxvaXItZXQtQ2hlciIsImNvZGUiOiI0MSJ9LHsibm9tIjoiTG9pcmUiLCJjb2RlIjoiNDIifSx7Im5vbSI6IkhhdXRlLUxvaXJlIiwiY29kZSI6IjQzIn0seyJub20iOiJMb2lyZS1BdGxhbnRpcXVlIiwiY29kZSI6IjQ0In0seyJub20iOiJMb2lyZXQiLCJjb2RlIjoiNDUifSx7Im5vbSI6IkxvdCIsImNvZGUiOiI0NiJ9LHsibm9tIjoiTG90LWV0LUdhcm9ubmUiLCJjb2RlIjoiNDcifSx7Im5vbSI6IkxvesOocmUiLCJjb2RlIjoiNDgifSx7Im5vbSI6Ik1haW5lLWV0LUxvaXJlIiwiY29kZSI6IjQ5In0seyJub20iOiJNYW5jaGUiLCJjb2RlIjoiNTAifSx7Im5vbSI6Ik1hcm5lIiwiY29kZSI6IjUxIn0seyJub20iOiJIYXV0ZS1NYXJuZSIsImNvZGUiOiI1MiJ9LHsibm9tIjoiTWF5ZW5uZSIsImNvZGUiOiI1MyJ9LHsibm9tIjoiTWV1cnRoZS1ldC1Nb3NlbGxlIiwiY29kZSI6IjU0In0seyJub20iOiJNZXVzZSIsImNvZGUiOiI1NSJ9LHsibm9tIjoiTW9yYmloYW4iLCJjb2RlIjoiNTYifSx7Im5vbSI6Ik1vc2VsbGUiLCJjb2RlIjoiNTcifSx7Im5vbSI6Ik5pw6h2cmUiLCJjb2RlIjoiNTgifSx7Im5vbSI6Ik5vcmQiLCJjb2RlIjoiNTkifSx7Im5vbSI6Ik9pc2UiLCJjb2RlIjoiNjAifSx7Im5vbSI6Ik9ybmUiLCJjb2RlIjoiNjEifSx7Im5vbSI6IlBhcy1kZS1DYWxhaXMiLCJjb2RlIjoiNjIifSx7Im5vbSI6IlB1eS1kZS1Ew7RtZSIsImNvZGUiOiI2MyJ9LHsibm9tIjoiUHlyw6luw6llcy1BdGxhbnRpcXVlcyIsImNvZGUiOiI2NCJ9LHsibm9tIjoiSGF1dGVzLVB5csOpbsOpZXMiLCJjb2RlIjoiNjUifSx7Im5vbSI6IlB5csOpbsOpZXMtT3JpZW50YWxlcyIsImNvZGUiOiI2NiJ9LHsibm9tIjoiQmFzLVJoaW4iLCJjb2RlIjoiNjcifSx7Im5vbSI6IkhhdXQtUmhpbiIsImNvZGUiOiI2OCJ9LHsibm9tIjoiUmjDtG5lIiwiY29kZSI6IjY5In0seyJub20iOiJIYXV0ZS1TYcO0bmUiLCJjb2RlIjoiNzAifSx7Im5vbSI6IlNhw7RuZS1ldC1Mb2lyZSIsImNvZGUiOiI3MSJ9LHsibm9tIjoiU2FydGhlIiwiY29kZSI6IjcyIn0seyJub20iOiJTYXZvaWUiLCJjb2RlIjoiNzMifSx7Im5vbSI6IkhhdXRlLVNhdm9pZSIsImNvZGUiOiI3NCJ9LHsibm9tIjoiUGFyaXMiLCJjb2RlIjoiNzUifSx7Im5vbSI6IlNlaW5lLU1hcml0aW1lIiwiY29kZSI6Ijc2In0seyJub20iOiJTZWluZS1ldC1NYXJuZSIsImNvZGUiOiI3NyJ9LHsibm9tIjoiWXZlbGluZXMiLCJjb2RlIjoiNzgifSx7Im5vbSI6IkRldXgtU8OodnJlcyIsImNvZGUiOiI3OSJ9LHsibm9tIjoiU29tbWUiLCJjb2RlIjoiODAifSx7Im5vbSI6IlRhcm4iLCJjb2RlIjoiODEifSx7Im5vbSI6IlRhcm4tZXQtR2Fyb25uZSIsImNvZGUiOiI4MiJ9LHsibm9tIjoiVmFyIiwiY29kZSI6IjgzIn0seyJub20iOiJWYXVjbHVzZSIsImNvZGUiOiI4NCJ9LHsibm9tIjoiVmVuZMOpZSIsImNvZGUiOiI4NSJ9LHsibm9tIjoiVmllbm5lIiwiY29kZSI6Ijg2In0seyJub20iOiJIYXV0ZS1WaWVubmUiLCJjb2RlIjoiODcifSx7Im5vbSI6IlZvc2dlcyIsImNvZGUiOiI4OCJ9LHsibm9tIjoiWW9ubmUiLCJjb2RlIjoiODkifSx7Im5vbSI6IlRlcnJpdG9pcmUgZGUgQmVsZm9ydCIsImNvZGUiOiI5MCJ9LHsibm9tIjoiRXNzb25uZSIsImNvZGUiOiI5MSJ9LHsibm9tIjoiSGF1dHMtZGUtU2VpbmUiLCJjb2RlIjoiOTIifSx7Im5vbSI6IlNlaW5lLVNhaW50LURlbmlzIiwiY29kZSI6IjkzIn0seyJub20iOiJWYWwtZGUtTWFybmUiLCJjb2RlIjoiOTQifSx7Im5vbSI6IlZhbC1kJ09pc2UiLCJjb2RlIjoiOTUifSx7Im5vbSI6Ikd1YWRlbG91cGUiLCJjb2RlIjoiOTcxIn0seyJub20iOiJNYXJ0aW5pcXVlIiwiY29kZSI6Ijk3MiJ9LHsibm9tIjoiR3V5YW5lIiwiY29kZSI6Ijk3MyJ9LHsibm9tIjoiTGEgUsOpdW5pb24iLCJjb2RlIjoiOTc0In0seyJub20iOiJNYXlvdHRlIiwiY29kZSI6Ijk3NiJ9XQ== - http_version: - recorded_at: Tue, 23 Oct 2018 13:11:36 GMT -recorded_with: VCR 4.0.0 diff --git a/spec/fixtures/cassettes/api_geo_regions.yml b/spec/fixtures/cassettes/api_geo_regions.yml deleted file mode 100644 index 0d1c6a182..000000000 --- a/spec/fixtures/cassettes/api_geo_regions.yml +++ /dev/null @@ -1,51 +0,0 @@ ---- -http_interactions: -- request: - method: get - uri: https://geo.api.gouv.fr/regions?fields=nom - body: - encoding: US-ASCII - string: '' - headers: - User-Agent: - - demarches-simplifiees.fr - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Expect: - - '' - response: - status: - code: 200 - message: OK - headers: - Server: - - nginx/1.10.3 (Ubuntu) - Date: - - Tue, 23 Oct 2018 13:11:36 GMT - Content-Type: - - application/json; charset=utf-8 - Transfer-Encoding: - - chunked - Connection: - - keep-alive - Vary: - - Accept-Encoding - X-Powered-By: - - Express - Access-Control-Allow-Origin: - - "*" - Etag: - - W/"28d-5OgIzgwL+0K2UaO0foKduqMMBrA" - Strict-Transport-Security: - - max-age=15552000 - Content-Encoding: - - gzip - body: - encoding: ASCII-8BIT - string: !binary |- - W3sibm9tIjoiR3VhZGVsb3VwZSIsImNvZGUiOiIwMSJ9LHsibm9tIjoiTWFydGluaXF1ZSIsImNvZGUiOiIwMiJ9LHsibm9tIjoiR3V5YW5lIiwiY29kZSI6IjAzIn0seyJub20iOiJMYSBSw6l1bmlvbiIsImNvZGUiOiIwNCJ9LHsibm9tIjoiTWF5b3R0ZSIsImNvZGUiOiIwNiJ9LHsibm9tIjoiw45sZS1kZS1GcmFuY2UiLCJjb2RlIjoiMTEifSx7Im5vbSI6IkNlbnRyZS1WYWwgZGUgTG9pcmUiLCJjb2RlIjoiMjQifSx7Im5vbSI6IkJvdXJnb2duZS1GcmFuY2hlLUNvbXTDqSIsImNvZGUiOiIyNyJ9LHsibm9tIjoiTm9ybWFuZGllIiwiY29kZSI6IjI4In0seyJub20iOiJIYXV0cy1kZS1GcmFuY2UiLCJjb2RlIjoiMzIifSx7Im5vbSI6IkdyYW5kIEVzdCIsImNvZGUiOiI0NCJ9LHsibm9tIjoiUGF5cyBkZSBsYSBMb2lyZSIsImNvZGUiOiI1MiJ9LHsibm9tIjoiQnJldGFnbmUiLCJjb2RlIjoiNTMifSx7Im5vbSI6Ik5vdXZlbGxlLUFxdWl0YWluZSIsImNvZGUiOiI3NSJ9LHsibm9tIjoiT2NjaXRhbmllIiwiY29kZSI6Ijc2In0seyJub20iOiJBdXZlcmduZS1SaMO0bmUtQWxwZXMiLCJjb2RlIjoiODQifSx7Im5vbSI6IlByb3ZlbmNlLUFscGVzLUPDtHRlIGQnQXp1ciIsImNvZGUiOiI5MyJ9LHsibm9tIjoiQ29yc2UiLCJjb2RlIjoiOTQifV0= - http_version: - recorded_at: Tue, 23 Oct 2018 13:11:36 GMT -recorded_with: VCR 4.0.0 diff --git a/spec/fixtures/cassettes/api_geo_search_rpg.yml b/spec/fixtures/cassettes/api_geo_search_rpg.yml deleted file mode 100644 index 97b72996a..000000000 --- a/spec/fixtures/cassettes/api_geo_search_rpg.yml +++ /dev/null @@ -1,51 +0,0 @@ ---- -http_interactions: -- request: - method: post - uri: https://sandbox.geo.api.gouv.fr/rpg/parcelles/search - body: - encoding: UTF-8 - string: '{"polygonIntersects":{"type":"Polygon","coordinates":[[[2.3945903778076176,46.53312237252731],[2.394933700561524,46.532590956418076],[2.3948478698730473,46.53170525134736],[2.393732070922852,46.530760483351195],[2.3909854888916016,46.5309376286023],[2.391414642333985,46.531232869403546],[2.3913288116455083,46.53253190986272],[2.39278793334961,46.53329951007484],[2.3945903778076176,46.53312237252731]]]}}' - headers: - User-Agent: - - demarches-simplifiees.fr - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Content-Type: - - application/json - Expect: - - '' - response: - status: - code: 200 - message: OK - headers: - Server: - - nginx/1.10.3 (Ubuntu) - Date: - - Tue, 23 Oct 2018 14:47:12 GMT - Content-Type: - - application/json; charset=utf-8 - Transfer-Encoding: - - chunked - Connection: - - keep-alive - Vary: - - Accept-Encoding - X-Powered-By: - - Express - Access-Control-Allow-Origin: - - "*" - Etag: - - W/"fb0-ripeBc2xednIfUlCdOtrk6Vbny4" - Content-Encoding: - - gzip - body: - encoding: ASCII-8BIT - string: !binary |- - eyJ0eXBlIjoiRmVhdHVyZUNvbGxlY3Rpb24iLCJmZWF0dXJlcyI6W3sidHlwZSI6IkZlYXR1cmUiLCJwcm9wZXJ0aWVzIjp7ImlkIjoiODA2MzQzNSIsInN1cmZhY2UiOjExLjgsImJpbyI6ZmFsc2UsImNvZGVDdWx0dXJlIjoiUFBIIiwiY3VsdHVyZSI6IlByYWlyaWUgcGVybWFuZW50ZSAtIGhlcmJlIChyZXNzb3VyY2VzIGZvdXJyYWfDqHJlcyBsaWduZXVzZXMgYWJzZW50ZXMgb3UgcGV1IHByw6lzZW50ZXMpIn0sImdlb21ldHJ5Ijp7InR5cGUiOiJQb2x5Z29uIiwiY29vcmRpbmF0ZXMiOltbWzIuMzk2Mzg4ODA5NzM4NDQ3LDQ2LjUzMjYyMDU0MzY5NzY5Nl0sWzIuMzk2ODU4MDg0NjQyNDgsNDYuNTMyMzE0MDQ0MzY4NTVdLFsyLjM5NzM4MzQ3MzYzNjMzNSw0Ni41MzE5NjEwMjE0MDQ5NDZdLFsyLjM5NzI1NjI1NTk4MjkxNCw0Ni41MzEzODkxMTc2MTI0OV0sWzIuMzk2ODg4NzQ5MDkxNTM4LDQ2LjUzMTE0NDQ1MjU5MzczNl0sWzIuMzk2MzM3NzA4MjcwMjU1LDQ2LjUzMDQzNjIzMDY5MDg0XSxbMi4zOTYzNDMzNzM4MTI2NjQsNDYuNTMwMTQxNDkxMjY3NDFdLFsyLjM5Njk5MDY1OTkxMTQyMyw0Ni41Mjk1ODE4NTQzNTkxM10sWzIuMzk0OTYxNTY4NjUwODI4LDQ2LjUyODI3ODQwMDkxMzE5NF0sWzIuMzk0NDM4MzY3MjUwMTgsNDYuNTI3NTc1MzE3NjY1OF0sWzIuMzkzMzM3MDUwMTU1MTg1LDQ2LjUyODA0MzU4MTM3OTg1XSxbMi4zOTMwNjgxOTg0MTQxNjQsNDYuNTI4MTgyOTQ0MTk3MDhdLFsyLjM5MjY4NTYyOTQzOTc2NCw0Ni41MjgzOTM3ODU1NjM3NF0sWzIuMzkyNTE1MDIzNDM3MzQxLDQ2LjUyODQ5NzQwNjU3MzNdLFsyLjM5MjQzNzUzMTU2MDc3OSw0Ni41Mjg2MDQ1NDc0NDgwN10sWzIuMzkyNDMyNDg3OTE3MTM1LDQ2LjUyODcxNTIzMzY5NzQ1XSxbMi4zOTI2MTMxOTU1MjgyMDgsNDYuNTI5MTMxMTM5NzEzODldLFsyLjM5Mzc1MTEzODE0NTU5OCw0Ni41MzA3NjM3NzY2OTQyNl0sWzIuMzk0MTA5MDExMjkyNjc0LDQ2LjUzMTQ2Mzk5ODkxMjk3XSxbMi4zOTQyNTAzODg3Njc4MzIsNDYuNTMxNjQ0MjU5MTk2MTVdLFsyLjM5NDg0MDExMjU5NTQ4Nyw0Ni41MzIxMDc4ODcyNzY0NzRdLFsyLjM5NjA2OTc0MzI0MzkyLDQ2LjUzMzA1NzQyMDg2MTA4XSxbMi4zOTYzNjI2NTU4MjAxNTUsNDYuNTMyNzQ3MTQyNTA4ODc1XSxbMi4zOTYzODg4MDk3Mzg0NDcsNDYuNTMyNjIwNTQzNjk3Njk2XV1dfX0seyJ0eXBlIjoiRmVhdHVyZSIsInByb3BlcnRpZXMiOnsiaWQiOiI4MDYzNDM5Iiwic3VyZmFjZSI6NTkuMTgsImJpbyI6ZmFsc2UsImNvZGVDdWx0dXJlIjoiUFBIIiwiY3VsdHVyZSI6IlByYWlyaWUgcGVybWFuZW50ZSAtIGhlcmJlIChyZXNzb3VyY2VzIGZvdXJyYWfDqHJlcyBsaWduZXVzZXMgYWJzZW50ZXMgb3UgcGV1IHByw6lzZW50ZXMpIn0sImdlb21ldHJ5Ijp7InR5cGUiOiJQb2x5Z29uIiwiY29vcmRpbmF0ZXMiOltbWzIuMzg5NDk0MDQzODI1MDI3LDQ2LjUyOTI0Nzc5NzQ3NTUwNl0sWzIuMzkwNzkyNTgzNjgyNzc4LDQ2LjUzMDYzMzg1NjIyMjAyNV0sWzIuMzkwNzQ0ODA3MzE0MzUzLDQ2LjUzMDY1NzY1MjUxNDc2XSxbMi4zODk5OTM4MTM3OTE0MjEsNDYuNTMxMDMxNzA0MDU4MjM2XSxbMi4zODg4ODc5NTc3OTMwNzMsNDYuNTI5OTU3MDE5NzA3MTI2XSxbMi4zODg3OTIxMjIwNTIxNzIsNDYuNTI5ODQ0MTM0MjM4NzZdLFsyLjM4ODc0NDIzNjc1OTU3MSw0Ni41Mjk3OTc4MjgyNjE5Nl0sWzIuMzg4MzIyMTM2MjgzMDYsNDYuNTMwMzQ0MzAyMDA4Ml0sWzIuMzg4MDc2NjM2MjY5MTczLDQ2LjUzMDc5Njk4NzQxNTY0XSxbMi4zODc3MzIwNjY0NDgxNjgsNDYuNTMyMjgwNzE2NDkyMzY2XSxbMi4zODcxMTU4ODI1MjM0OTIsNDYuNTMzMjY4NTU1OTE5NTA2XSxbMi4zODY5NjA1NDI4Njc3NTIsNDYuNTM0MTAzNDM3OTc1Mjc2XSxbMi4zODY2OTc1MTgyMzA5Nyw0Ni41MzYwOTEzMDc2NzQxNTVdLFsyLjM4NzIzODY4NDQ2NDE0Miw0Ni41MzYwNjEyNzIzMzg2NV0sWzIuMzg4MDkxNjcwNDM0ODA0LDQ2LjUzNjE0OTUxNzk2NzQ5NV0sWzIuMzg4NzcyOTExNTQzMDA5LDQ2LjUzNjMzNjc5MjE5ODY2XSxbMi4zODg5MzYwNjQ2MjQxNDQsNDYuNTM2NTQ4MDMxNjc4NzM0XSxbMi4zODg4ODQ1OTQ4MjIyMDMsNDYuNTM2ODMwMTA4NDkxNDRdLFsyLjM4ODQ2MTQwMDc2ODM5OCw0Ni41MzY5NzM5OTM5MzM4OF0sWzIuMzg3NDY5OTA3NzI4MzM3LDQ2LjUzNzM4NjY3MTIxNTA2XSxbMi4zODkzNDI2MzkxMjc2NjIsNDYuNTM3MDE1NzkwNzYyMzA0XSxbMi4zOTAzNDc4MDIyNzQyOTcsNDYuNTM2OTg2NTM2NjQ4MzM1XSxbMi4zOTEzODQ1ODYwNDM3NTYsNDYuNTM3MTY5NDkzNzYxMjldLFsyLjM5MjMxMjQxNzE4NTc2Miw0Ni41MzczNjg5NTM3ODk0MjVdLFsyLjM5MzYxOTE5MTc2NDg4MSw0Ni41Mzc0NTYyMTczOTA4OV0sWzIuMzk0MTQ2OTY0Mzk3NTY0LDQ2LjUzNzQ5NTIyOTk4NjgxXSxbMi4zOTUyMzQyMDk1NDAxMjQsNDYuNTM3NTY5NTYxMzk3Nzk1XSxbMi4zOTYyNzM1Mjc3ODcxOTQsNDYuNTM3NjMzMzgyMjQ3OTJdLFsyLjM5NzI0NjE3Njk4Mjc3LDQ2LjUzNzYxNTAxNjc0OTU4NF0sWzIuMzk4MDYzNTQ2OTc2NDEsNDYuNTM3NTM5NjA5ODg3OTZdLFsyLjQwMDMzNDg1MDcyNjM3Miw0Ni41MzczMTIyMzU4MzAyM10sWzIuNDAwNTQ1ODg2NjQ0OTQ5LDQ2LjUzNzI4OTI4Mjg2NjM5XSxbMi40MDExNTY4MjQwMzU5OCw0Ni41MzYzOTQ0NTk0MjMzOF0sWzIuNDAyNjI5Mzg5NTE5MTY2LDQ2LjUzNjI4ODQ0NTUwNTAxXSxbMi40MDQyOTc1OTkwNjgzMTYsNDYuNTM2MTUxNDM3NzA2MjRdLFsyLjQwNDI5NjYyODUxNzg1OCw0Ni41MzUzNzc4NTQzMjQ2XSxbMi40MDAzMjE2MDkwMzM3MjcsNDYuNTM2MTMxMDQ2OTE2Nl0sWzIuMzk5ODU5OTA4NzQ0Njc1LDQ2LjUzNjA4Mzg5MDg2NjQ4Nl0sWzIuMzk5NjU4OTc5NzIxNzU3LDQ2LjUzNTg5MjY4MTgyMDExXSxbMi40MDAyMTIwNzg5NDE1MzUsNDYuNTM1NTQ4OTA0MTU1NTNdLFsyLjM5OTAzMzAyNzMwNzc1Nyw0Ni41MzQwOTc3MDAxNjE3OF0sWzIuMzk2MzY1MzcyOTQ0MzQ5LDQ2LjUzMjc2Njg3ODk2MDI1XSxbMi4zOTYwNjkwMDE3NTk5MjUsNDYuNTMzMDgyNDEzMTIxMjg1XSxbMi4zOTU5MjU2NDI3OTYwMjYsNDYuNTMzMTMyNzU3MTQ3N10sWzIuMzk0MDcwMjI5MTg5MzYxLDQ2LjUzMTYxNTQ3MzQzMzE0XSxbMi4zOTM2OTAxODM3OTk0Myw0Ni41MzA4Njc0NDE5NDE3NDRdLFsyLjM5MzY4MTAwNTA5MzM1MSw0Ni41MzA4NTQ0MTc1MDQxOV0sWzIuMzkxNTMyMTkyOTYxNTYsNDYuNTMxMzQ5NTg3MDg0NjhdLFsyLjM4OTUzNDIxMDI1NTE2Niw0Ni41MjkyMzUzMjQxNTUyMl0sWzIuMzg5NDk0MDQzODI1MDI3LDQ2LjUyOTI0Nzc5NzQ3NTUwNl1dXX19LHsidHlwZSI6IkZlYXR1cmUiLCJwcm9wZXJ0aWVzIjp7ImlkIjoiODA2MzQ0MyIsInN1cmZhY2UiOjQuMjQsImJpbyI6ZmFsc2UsImNvZGVDdWx0dXJlIjoiUFBIIiwiY3VsdHVyZSI6IlByYWlyaWUgcGVybWFuZW50ZSAtIGhlcmJlIChyZXNzb3VyY2VzIGZvdXJyYWfDqHJlcyBsaWduZXVzZXMgYWJzZW50ZXMgb3UgcGV1IHByw6lzZW50ZXMpIn0sImdlb21ldHJ5Ijp7InR5cGUiOiJQb2x5Z29uIiwiY29vcmRpbmF0ZXMiOltbWzIuMzg5NTM0MjEwMjU1MTY2LDQ2LjUyOTIzNTMyNDE1NTIyXSxbMi4zOTE1MzIxOTI5NjE1Niw0Ni41MzEzNDk1ODcwODQ2OF0sWzIuMzkzNjgxMDA1MDkzMzUxLDQ2LjUzMDg1NDQxNzUwNDE5XSxbMi4zOTM2NzAwMDI0ODQyOSw0Ni41MzA4Mzg4MDM1NDg4M10sWzIuMzkyOTg5NTY3MDI4NjE0LDQ2LjUyOTg3MzIzMTQ2MTU5XSxbMi4zOTI0OTMxMDcyODQ3MjYsNDYuNTI5MTAxMzQ3NTE3OTVdLFsyLjM5MjMyMTkzMjkyOTI2NCw0Ni41Mjg2NzA0NDIzNTI4XSxbMi4zOTE0ODkyMDkyNzIyMzUsNDYuNTI4NzcwODEzMTQ0NzhdLFsyLjM5MDkxNTA4ODU5MTExLDQ2LjUyODg1Njc2OTY4NDM5XSxbMi4zOTA0NjUxMzI5MzMxODQsNDYuNTI4OTQ2MjM5NDQ1NzZdLFsyLjM4OTUzNDIxMDI1NTE2Niw0Ni41MjkyMzUzMjQxNTUyMl1dXX19XX0= - http_version: - recorded_at: Tue, 23 Oct 2018 14:47:12 GMT -recorded_with: VCR 4.0.0 diff --git a/spec/lib/api_adresse/address_adapter_spec.rb b/spec/lib/api_adresse/address_adapter_spec.rb deleted file mode 100644 index 31a1d7bc3..000000000 --- a/spec/lib/api_adresse/address_adapter_spec.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'spec_helper' - -describe ApiAdresse::AddressAdapter do - describe '#get_suggestions' do - let(:request) { 'Paris' } - let(:response) { File.open('spec/fixtures/files/api_adresse/search_results.json') } - let(:status) { 200 } - - subject { described_class.new(request).get_suggestions } - - before do - stub_request(:get, "https://api-adresse.data.gouv.fr/search?&q=#{request}&limit=5") - .to_return(status: status, body: response, headers: {}) - end - - context 'when address return a list of address' do - it { expect(subject.size).to eq 5 } - it { is_expected.to be_an_instance_of Array } - end - - context 'when address return an empty list' do - let(:response) { File.open('spec/fixtures/files/api_adresse/search_no_results.json') } - - it { expect(subject.size).to eq 0 } - it { is_expected.to be_an_instance_of Array } - end - - context 'when BAN is unavailable' do - let(:status) { 503 } - let(:response) { '' } - - it { expect(subject.size).to eq 0 } - it { is_expected.to be_an_instance_of Array } - end - - context 'when request is empty' do - let(:response) { 'Missing query' } - let(:request) { '' } - - it { expect(subject.size).to eq 0 } - it { is_expected.to be_an_instance_of Array } - end - end -end diff --git a/spec/lib/api_adresse/point_adapter_spec.rb b/spec/lib/api_adresse/point_adapter_spec.rb deleted file mode 100644 index 9f21c8dd5..000000000 --- a/spec/lib/api_adresse/point_adapter_spec.rb +++ /dev/null @@ -1,31 +0,0 @@ -require 'spec_helper' - -describe ApiAdresse::PointAdapter do - let(:address) { '50 av des champs elysees' } - - describe '.geocode', vcr: { cassette_name: 'api_adresse_octo' } do - it 'return a point' do - expect(described_class.new(address).geocode.class).to eq(RGeo::Cartesian::PointImpl) - end - - context 'when RestClient::Exception' do - before do - allow(ApiAdresse::API).to receive(:call).and_raise(RestClient::Exception) - end - - it 'return nil' do - expect(described_class.new(address).geocode).to be_nil - end - end - - context 'when JSON::ParserError' do - before do - allow(JSON).to receive(:parse).and_raise(JSON::ParserError) - end - - it 'return nil' do - expect(described_class.new(address).geocode).to be_nil - end - end - end -end diff --git a/spec/lib/api_geo/api_spec.rb b/spec/lib/api_geo/api_spec.rb deleted file mode 100644 index 6a046f119..000000000 --- a/spec/lib/api_geo/api_spec.rb +++ /dev/null @@ -1,75 +0,0 @@ -require 'spec_helper' - -describe ApiGeo::API do - describe '.regions', vcr: { cassette_name: 'api_geo_regions' } do - subject { described_class.regions } - - it { expect(subject.size).to eq 18 } - end - - describe '.departements', vcr: { cassette_name: 'api_geo_departements' } do - subject { described_class.departements } - - it { expect(subject.size).to eq 101 } - end - - describe '.pays' do - subject { described_class.pays } - let(:pays) { - JSON.parse(File.open('app/lib/api_geo/pays.json').read, symbolize_names: true) - } - - it { is_expected.to eq pays } - end - - describe '.search_rpg', vcr: { cassette_name: 'api_geo_search_rpg' } do - let(:coordinates) do - [ - [ - 2.3945903778076176, - 46.53312237252731 - ], - [ - 2.394933700561524, - 46.532590956418076 - ], - [ - 2.3948478698730473, - 46.53170525134736 - ], - [ - 2.393732070922852, - 46.530760483351195 - ], - [ - 2.3909854888916016, - 46.5309376286023 - ], - [ - 2.391414642333985, - 46.531232869403546 - ], - [ - 2.3913288116455083, - 46.53253190986272 - ], - [ - 2.39278793334961, - 46.53329951007484 - ], - [ - 2.3945903778076176, - 46.53312237252731 - ] - ] - end - - let(:geo_json) { - GeojsonService.to_json_polygon_for_rpg(coordinates) - } - - subject { described_class.search_rpg(geo_json) } - - it { expect(subject[:features].size).to eq 3 } - end -end diff --git a/spec/lib/api_geo/rpg_adapter_spec.rb b/spec/lib/api_geo/rpg_adapter_spec.rb deleted file mode 100644 index 5c277a15a..000000000 --- a/spec/lib/api_geo/rpg_adapter_spec.rb +++ /dev/null @@ -1,62 +0,0 @@ -require 'spec_helper' - -describe ApiGeo::RPGAdapter do - subject { described_class.new(coordinates).results } - - let(:coordinates) do - [ - [ - 2.3945903778076176, - 46.53312237252731 - ], - [ - 2.394933700561524, - 46.532590956418076 - ], - [ - 2.3948478698730473, - 46.53170525134736 - ], - [ - 2.393732070922852, - 46.530760483351195 - ], - [ - 2.3909854888916016, - 46.5309376286023 - ], - [ - 2.391414642333985, - 46.531232869403546 - ], - [ - 2.3913288116455083, - 46.53253190986272 - ], - [ - 2.39278793334961, - 46.53329951007484 - ], - [ - 2.3945903778076176, - 46.53312237252731 - ] - ] - end - - context 'coordinates are filled', vcr: { cassette_name: 'api_geo_search_rpg' } do - describe 'Attribut filter' do - it { expect(subject.size).to eq(3) } - it do - expect(subject.first.keys).to eq([ - :culture, - :code_culture, - :surface, - :bio, - :geometry, - :geo_reference_id - ]) - end - end - end -end diff --git a/spec/models/administrateur_spec.rb b/spec/models/administrateur_spec.rb index 9f0c30923..3e6864466 100644 --- a/spec/models/administrateur_spec.rb +++ b/spec/models/administrateur_spec.rb @@ -46,6 +46,21 @@ describe Administrateur, type: :model do end end + describe '#delete_and_transfer_services' do + let!(:administrateur) { create(:administrateur) } + let!(:autre_administrateur) { create(:administrateur) } + let!(:procedure) { create(:procedure, :with_service, administrateurs: [administrateur, autre_administrateur]) } + let(:service) { procedure.service } + + it "delete and transfer services to other admin" do + service.update(administrateur: administrateur) + administrateur.delete_and_transfer_services + + expect(Administrateur.find_by(id: administrateur.id)).to be_nil + expect(service.reload.administrateur).to eq(autre_administrateur) + end + end + # describe '#password_complexity' do # let(:email) { 'mail@beta.gouv.fr' } # let(:passwords) { ['pass', '12pass23', 'démarches ', 'démarches-simple', 'démarches-simplifiées-pwd'] } diff --git a/spec/models/champ_shared_example.rb b/spec/models/champ_shared_example.rb index 31ae546fa..d00af9504 100644 --- a/spec/models/champ_shared_example.rb +++ b/spec/models/champ_shared_example.rb @@ -32,12 +32,6 @@ shared_examples 'champ_spec' do end end - describe '.departement', vcr: { cassette_name: 'api_geo_departements' } do - subject { Champs::DepartementChamp.departements } - - it { expect(subject).to include '99 - Étranger' } - end - context "when type_champ=date" do let(:type_de_champ) { create(:type_de_champ_date) } let(:champ) { type_de_champ.champ.create } diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb index 5167b274e..d1ae1d8ca 100644 --- a/spec/models/dossier_spec.rb +++ b/spec/models/dossier_spec.rb @@ -1084,4 +1084,39 @@ describe Dossier do expect { expired_brouillon.reload }.to raise_error(ActiveRecord::RecordNotFound) end end + + describe '#geo_position' do + let(:lat) { "46.538192" } + let(:lon) { "2.428462" } + let(:zoom) { "13" } + + let(:etablissement_geo_adresse_lat) { "40.7143528" } + let(:etablissement_geo_adresse_lon) { "-74.0059731" } + + let(:result) { { lat: lat, lon: lon, zoom: zoom } } + let(:dossier) { create(:dossier) } + + it 'should geolocate' do + expect(dossier.geo_position).to eq(result) + end + + context 'with etablissement' do + before do + Geocoder::Lookup::Test.add_stub( + dossier.etablissement.geo_adresse, [ + { + 'coordinates' => [etablissement_geo_adresse_lat.to_f, etablissement_geo_adresse_lon.to_f] + } + ] + ) + end + + let(:dossier) { create(:dossier, :with_entreprise) } + let(:result) { { lat: etablissement_geo_adresse_lat, lon: etablissement_geo_adresse_lon, zoom: zoom } } + + it 'should geolocate' do + expect(dossier.geo_position).to eq(result) + end + end + end end diff --git a/spec/models/instructeur_spec.rb b/spec/models/instructeur_spec.rb index 37917bf18..78737bf89 100644 --- a/spec/models/instructeur_spec.rb +++ b/spec/models/instructeur_spec.rb @@ -424,9 +424,40 @@ describe Instructeur, type: :model do it { expect(instructeur_a.procedures.all.to_ary).to eq([procedure_a]) } end + describe "#can_be_deleted?" do + subject { instructeur.can_be_deleted? } + + context 'when the instructeur is an administrateur' do + let!(:administrateur) { create(:administrateur) } + let(:instructeur) { administrateur.instructeur } + + it { is_expected.to be false } + end + + context "when the instructeur's procedures have other instructeurs" do + let(:instructeur_not_admin) { create(:instructeur) } + let(:autre_instructeur) { create(:instructeur) } + + it "can be deleted" do + assign(procedure, instructeur_assigne: instructeur_not_admin) + assign(procedure, instructeur_assigne: autre_instructeur) + expect(autre_instructeur.can_be_deleted?).to be_truthy + end + end + + context "when the instructeur's procedures is the only one" do + let(:instructeur_not_admin) { create :instructeur } + let(:autre_procedure) { create :procedure } + it "can be deleted" do + assign(autre_procedure, instructeur_assigne: instructeur_not_admin) + expect(instructeur_not_admin.can_be_deleted?).to be_falsy + end + end + end + private - def assign(procedure_to_assign) - create :assign_to, instructeur: instructeur, procedure: procedure_to_assign, groupe_instructeur: procedure_to_assign.defaut_groupe_instructeur + def assign(procedure_to_assign, instructeur_assigne: instructeur) + create :assign_to, instructeur: instructeur_assigne, procedure: procedure_to_assign, groupe_instructeur: procedure_to_assign.defaut_groupe_instructeur end end diff --git a/spec/models/procedure_overview_spec.rb b/spec/models/procedure_overview_spec.rb index 06f0b291d..62f571e67 100644 --- a/spec/models/procedure_overview_spec.rb +++ b/spec/models/procedure_overview_spec.rb @@ -8,7 +8,7 @@ describe ProcedureOverview, type: :model do before { Timecop.freeze(friday) } after { Timecop.return } - let(:procedure_overview) { ProcedureOverview.new(procedure, monday) } + let(:procedure_overview) { ProcedureOverview.new(procedure, monday, [procedure.defaut_groupe_instructeur]) } describe 'dossiers_en_instruction_count' do let!(:en_instruction_dossier) do @@ -56,6 +56,27 @@ describe ProcedureOverview, type: :model do it { expect(procedure_overview.created_dossiers_count).to eq(1) } end + describe 'with a procedure routee' do + let!(:gi_2) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 2') } + let!(:gi_3) { procedure.groupe_instructeurs.create(label: 'groupe instructeur 3') } + + def create_dossier_in_group(g) + create(:dossier, procedure: procedure, created_at: monday, state: Dossier.states.fetch(:en_instruction), groupe_instructeur: g) + end + + let!(:created_dossier_during_the_week_on_group_2) { create_dossier_in_group(gi_2) } + let!(:created_dossier_during_the_week_on_group_3_a) { create_dossier_in_group(gi_3) } + let!(:created_dossier_during_the_week_on_group_3_b) { create_dossier_in_group(gi_3) } + + let(:procedure_overview_gi_2) { ProcedureOverview.new(procedure, monday, [gi_2]) } + let(:procedure_overview_gi_3) { ProcedureOverview.new(procedure, monday, [gi_3]) } + let(:procedure_overview_default) { ProcedureOverview.new(procedure, monday, [procedure.defaut_groupe_instructeur]) } + + it { expect(procedure_overview_gi_2.created_dossiers_count).to eq(1) } + it { expect(procedure_overview_gi_3.created_dossiers_count).to eq(2) } + it { expect(procedure_overview_default.created_dossiers_count).to eq(0) } + end + describe 'had_some_activities?' do subject { procedure_overview.had_some_activities? } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index b127d35c6..19b317f40 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -224,6 +224,24 @@ describe User, type: :model do context 'when the user has no dossier in instruction' do it { is_expected.to be true } end + + context 'when the user is an administrateur' do + it 'cannot be deleted' do + administrateur = create(:administrateur) + user = administrateur.user + + expect(user.can_be_deleted?).to be_falsy + end + end + + context 'when the user is an instructeur' do + it 'cannot be deleted' do + instructeur = create(:instructeur) + user = instructeur.user + + expect(user.can_be_deleted?).to be_falsy + end + end end describe '#delete_and_keep_track_dossiers' do @@ -241,12 +259,29 @@ describe User, type: :model do let!(:dossier_en_construction) { create(:dossier, :en_construction, user: user) } let!(:dossier_brouillon) { create(:dossier, user: user) } - it "keep track of dossiers and delete user" do - user.delete_and_keep_track_dossiers(administration) + context 'without a hidden dossier' do + it "keep track of dossiers and delete user" do + user.delete_and_keep_track_dossiers(administration) - expect(DeletedDossier.find_by(dossier_id: dossier_en_construction)).to be_present - expect(DeletedDossier.find_by(dossier_id: dossier_brouillon)).to be_present - expect(User.find_by(id: user.id)).to be_nil + expect(DeletedDossier.find_by(dossier_id: dossier_en_construction)).to be_present + expect(DeletedDossier.find_by(dossier_id: dossier_brouillon)).to be_present + expect(User.find_by(id: user.id)).to be_nil + end + end + + context 'with a hidden dossier' do + let!(:dossier_cache) do + create(:dossier, :en_construction, user: user) + end + + it "keep track of dossiers and delete user" do + dossier_cache.delete_and_keep_track(administration) + user.delete_and_keep_track_dossiers(administration) + + expect(DeletedDossier.find_by(dossier_id: dossier_en_construction)).to be_present + expect(DeletedDossier.find_by(dossier_id: dossier_brouillon)).to be_present + expect(User.find_by(id: user.id)).to be_nil + end end end end diff --git a/spec/services/procedure_export_service_spec.rb b/spec/services/procedure_export_service_spec.rb index 8be461c80..eb58aed07 100644 --- a/spec/services/procedure_export_service_spec.rb +++ b/spec/services/procedure_export_service_spec.rb @@ -69,6 +69,7 @@ describe ProcedureExportService do "pays", "regions", "departements", + "communes", "engagement", "dossier_link", "piece_justificative", @@ -142,6 +143,7 @@ describe ProcedureExportService do "pays", "regions", "departements", + "communes", "engagement", "dossier_link", "piece_justificative", @@ -220,6 +222,7 @@ describe ProcedureExportService do "pays", "regions", "departements", + "communes", "engagement", "dossier_link", "piece_justificative", diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 538b95806..f5e6de454 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -152,6 +152,8 @@ RSpec.configure do |config| ActionMailer::Base.deliveries.clear ActiveStorage::Current.host = 'http://test.host' + + Geocoder.configure(lookup: :test) } RSpec::Matchers.define :have_same_attributes_as do |expected, options| diff --git a/yarn.lock b/yarn.lock index ff57a5c76..8a6b6baaa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1521,13 +1521,6 @@ atob@^2.1.1: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autocomplete.js@^0.37.0: - version "0.37.0" - resolved "https://registry.yarnpkg.com/autocomplete.js/-/autocomplete.js-0.37.0.tgz#bcfbfd7bcabe90e90fad4c2b1aaa931379a10c38" - integrity sha512-MxYfNb89sl7IRhNdEJv6z8dSfA7lVeU7Dk6m/+/ih0/tPLsbxIM7uPVsnWEUh4j7dFqJktlM7hCeU7jmx6VC8A== - dependencies: - immediate "^3.2.3" - autoprefixer@^9.4.9: version "9.5.1" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.5.1.tgz#243b1267b67e7e947f28919d786b50d3bb0fb357" @@ -4362,11 +4355,6 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -immediate@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" - integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw= - import-cwd@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"