commit
d0026fc887
52 changed files with 2442 additions and 3020 deletions
|
@ -7,25 +7,25 @@ class Champs::SiretController < ApplicationController
|
||||||
find_etablisement
|
find_etablisement
|
||||||
|
|
||||||
if @siret.empty?
|
if @siret.empty?
|
||||||
@champ&.update!(value: '')
|
return clear_siret_and_etablissement
|
||||||
@etablissement&.destroy
|
end
|
||||||
elsif @siret.present? && @siret.length == 14
|
|
||||||
etablissement = find_etablisement_with_siret
|
|
||||||
if etablissement.present?
|
|
||||||
@etablissement = etablissement
|
|
||||||
|
|
||||||
if !@champ.nil?
|
if @siret.present? && @siret.length != 14
|
||||||
@champ.update!(value: etablissement.siret, etablissement: etablissement)
|
return siret_error(:invalid)
|
||||||
end
|
end
|
||||||
else
|
|
||||||
@champ&.update!(value: '')
|
begin
|
||||||
@etablissement&.destroy
|
etablissement = find_etablissement_with_siret
|
||||||
@siret = :not_found
|
rescue RestClient::RequestFailed
|
||||||
end
|
return siret_error(:network_error)
|
||||||
else
|
end
|
||||||
@champ&.update!(value: '')
|
if etablissement.blank?
|
||||||
@etablissement&.destroy
|
return siret_error(:not_found)
|
||||||
@siret = :invalid
|
end
|
||||||
|
|
||||||
|
@etablissement = etablissement
|
||||||
|
if !@champ.nil?
|
||||||
|
@champ.update!(value: etablissement.siret, etablissement: etablissement)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -49,10 +49,20 @@ class Champs::SiretController < ApplicationController
|
||||||
@procedure_id = @champ&.dossier&.procedure_id || 'aperçu'
|
@procedure_id = @champ&.dossier&.procedure_id || 'aperçu'
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_etablisement_with_siret
|
def find_etablissement_with_siret
|
||||||
etablissement_attributes = ApiEntrepriseService.get_etablissement_params_for_siret(@siret, @procedure_id)
|
etablissement_attributes = ApiEntrepriseService.get_etablissement_params_for_siret(@siret, @procedure_id)
|
||||||
if etablissement_attributes.present?
|
if etablissement_attributes.present?
|
||||||
Etablissement.new(etablissement_attributes)
|
Etablissement.new(etablissement_attributes)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def clear_siret_and_etablissement
|
||||||
|
@champ&.update!(value: '')
|
||||||
|
@etablissement&.destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
def siret_error(error)
|
||||||
|
clear_siret_and_etablissement
|
||||||
|
@siret = error
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -91,7 +91,11 @@ module Users
|
||||||
end
|
end
|
||||||
|
|
||||||
sanitized_siret = siret_model.siret
|
sanitized_siret = siret_model.siret
|
||||||
etablissement_attributes = ApiEntrepriseService.get_etablissement_params_for_siret(sanitized_siret, @dossier.procedure.id)
|
begin
|
||||||
|
etablissement_attributes = ApiEntrepriseService.get_etablissement_params_for_siret(sanitized_siret, @dossier.procedure.id)
|
||||||
|
rescue RestClient::RequestFailed
|
||||||
|
return render_siret_error(t('errors.messages.siret_network_error'))
|
||||||
|
end
|
||||||
if etablissement_attributes.blank?
|
if etablissement_attributes.blank?
|
||||||
return render_siret_error(t('errors.messages.siret_unknown'))
|
return render_siret_error(t('errors.messages.siret_unknown'))
|
||||||
end
|
end
|
||||||
|
@ -257,7 +261,7 @@ module Users
|
||||||
|
|
||||||
def show_demarche_en_test_banner
|
def show_demarche_en_test_banner
|
||||||
if @dossier.present? && @dossier.procedure.brouillon?
|
if @dossier.present? && @dossier.procedure.brouillon?
|
||||||
flash.now.notice = "Ce dossier est déposé sur une démarche en test. Il sera supprimé lors de la publication de la démarche."
|
flash.now.alert = "Ce dossier est déposé sur une démarche en test. Il sera supprimé lors de la publication de la démarche et sa soumission n’a pas de valeur juridique."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ class TypesDeChampEditor extends Component {
|
||||||
type_champ: 'text',
|
type_champ: 'text',
|
||||||
types_de_champ: [],
|
types_de_champ: [],
|
||||||
private: props.isAnnotation,
|
private: props.isAnnotation,
|
||||||
|
drop_down_list_value: '--Premier élément du menu--\n',
|
||||||
libelle: `${
|
libelle: `${
|
||||||
props.isAnnotation ? 'Nouvelle annotation' : 'Nouveau champ'
|
props.isAnnotation ? 'Nouvelle annotation' : 'Nouveau champ'
|
||||||
} ${props.typeDeChampsTypes[0][0]}`
|
} ${props.typeDeChampsTypes[0][0]}`
|
||||||
|
|
|
@ -1,64 +1,47 @@
|
||||||
import { CREATE } from 'leaflet-freedraw';
|
import { initMap, drawPolygons, addFreeDrawEvents } from '../../shared/carte';
|
||||||
import { delegate } from '@utils';
|
|
||||||
import {
|
|
||||||
initMap,
|
|
||||||
getCurrentMap,
|
|
||||||
geocodeAddress,
|
|
||||||
drawCadastre,
|
|
||||||
drawQuartiersPrioritaires,
|
|
||||||
drawParcellesAgricoles,
|
|
||||||
drawUserSelection,
|
|
||||||
addFreeDrawEvents
|
|
||||||
} from '../../shared/carte';
|
|
||||||
|
|
||||||
function initialize() {
|
async function initialize() {
|
||||||
for (let element of document.querySelectorAll('.carte')) {
|
const elements = document.querySelectorAll('.carte');
|
||||||
diplayMap(element, null, true);
|
|
||||||
|
if (elements.length) {
|
||||||
|
const editable = [...elements].find(element =>
|
||||||
|
element.classList.contains('edit')
|
||||||
|
);
|
||||||
|
await loadLeaflet(editable);
|
||||||
|
|
||||||
|
for (let element of elements) {
|
||||||
|
diplayMap(element, null, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.DS.drawMapData = (selector, data) => {
|
// We load leaflet dynamically, ramda and freedraw and assign them to globals.
|
||||||
let element = document.querySelector(selector);
|
// Latest freedraw version build needs globals.
|
||||||
diplayMap(element, data);
|
async function loadLeaflet(editable) {
|
||||||
};
|
window.L = await import('leaflet').then(({ default: L }) => L);
|
||||||
|
|
||||||
|
if (editable) {
|
||||||
|
window.R = await import('ramda').then(({ default: R }) => R);
|
||||||
|
await import('leaflet-freedraw/dist/leaflet-freedraw.web.js');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function diplayMap(element, data, initial = false) {
|
function diplayMap(element, data, initial = false) {
|
||||||
data = data || JSON.parse(element.dataset.geo);
|
data = data || JSON.parse(element.dataset.geo);
|
||||||
let editable = element.classList.contains('edit');
|
const editable = element.classList.contains('edit');
|
||||||
|
const map = initMap(element, data.position, editable);
|
||||||
|
|
||||||
let map = initMap(element, data.position, editable);
|
drawPolygons(map, data, { initial, editable });
|
||||||
|
|
||||||
// draw external polygons
|
if (initial && editable) {
|
||||||
drawCadastre(map, data, editable);
|
const input = element.parentElement.querySelector('input[data-remote]');
|
||||||
drawQuartiersPrioritaires(map, data, editable);
|
addFreeDrawEvents(map, input);
|
||||||
drawParcellesAgricoles(map, data, editable);
|
|
||||||
|
|
||||||
// draw user polygon
|
|
||||||
if (initial) {
|
|
||||||
drawUserSelection(map, data, editable);
|
|
||||||
|
|
||||||
if (editable) {
|
|
||||||
let input = element.parentElement.querySelector('input[data-remote]');
|
|
||||||
addFreeDrawEvents(map, input);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addEventListener('turbolinks:load', initialize);
|
addEventListener('turbolinks:load', initialize);
|
||||||
|
|
||||||
delegate('click', '.toolbar .new-area', event => {
|
addEventListener('carte:update', ({ detail: { selector, data } }) => {
|
||||||
event.preventDefault();
|
const element = document.querySelector(selector);
|
||||||
let map = getCurrentMap(event.target);
|
diplayMap(element, data);
|
||||||
|
|
||||||
if (map) {
|
|
||||||
map.freeDraw.mode(CREATE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
delegate('autocomplete:select', '.toolbar [data-address]', event => {
|
|
||||||
let map = getCurrentMap(event.target);
|
|
||||||
|
|
||||||
if (map) {
|
|
||||||
geocodeAddress(map, event.detail.label);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
import L from 'leaflet';
|
/* globals FreeDraw L */
|
||||||
import FreeDraw, { NONE, EDIT, DELETE } from 'leaflet-freedraw';
|
|
||||||
import { fire, getJSON, delegate } from '@utils';
|
import { fire, getJSON, delegate } from '@utils';
|
||||||
|
|
||||||
import polygonArea from './polygon_area';
|
import polygonArea from './polygon_area';
|
||||||
|
|
||||||
const LAYERS = {};
|
|
||||||
const MAPS = new WeakMap();
|
const MAPS = new WeakMap();
|
||||||
|
|
||||||
export function initMap(element, position, editable = false) {
|
export function initMap(element, position, editable = false) {
|
||||||
|
@ -22,7 +20,7 @@ export function initMap(element, position, editable = false) {
|
||||||
|
|
||||||
if (editable) {
|
if (editable) {
|
||||||
const freeDraw = new FreeDraw({
|
const freeDraw = new FreeDraw({
|
||||||
mode: NONE,
|
mode: FreeDraw.NONE,
|
||||||
smoothFactor: 4,
|
smoothFactor: 4,
|
||||||
mergePolygons: false
|
mergePolygons: false
|
||||||
});
|
});
|
||||||
|
@ -35,81 +33,39 @@ export function initMap(element, position, editable = false) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function drawCadastre(map, { cadastres }, editable = false) {
|
export function drawPolygons(map, data, { editable, initial }) {
|
||||||
drawLayer(
|
if (initial) {
|
||||||
map,
|
drawUserSelection(map, data, editable);
|
||||||
cadastres,
|
}
|
||||||
editable ? CADASTRE_POLYGON_STYLE : noEditStyle(CADASTRE_POLYGON_STYLE),
|
clearLayers(map);
|
||||||
'cadastres'
|
drawCadastre(map, data, editable);
|
||||||
);
|
drawQuartiersPrioritaires(map, data, editable);
|
||||||
}
|
drawParcellesAgricoles(map, data, editable);
|
||||||
|
bringToFrontUserSelection(map);
|
||||||
export function drawQuartiersPrioritaires(
|
|
||||||
map,
|
|
||||||
{ quartiersPrioritaires },
|
|
||||||
editable = false
|
|
||||||
) {
|
|
||||||
drawLayer(
|
|
||||||
map,
|
|
||||||
quartiersPrioritaires,
|
|
||||||
editable ? QP_POLYGON_STYLE : noEditStyle(QP_POLYGON_STYLE),
|
|
||||||
'quartiersPrioritaires'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function drawParcellesAgricoles(
|
|
||||||
map,
|
|
||||||
{ parcellesAgricoles },
|
|
||||||
editable = false
|
|
||||||
) {
|
|
||||||
drawLayer(
|
|
||||||
map,
|
|
||||||
parcellesAgricoles,
|
|
||||||
editable ? RPG_POLYGON_STYLE : noEditStyle(RPG_POLYGON_STYLE),
|
|
||||||
'parcellesAgricoles'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function drawUserSelection(map, { selection }, editable = false) {
|
export function drawUserSelection(map, { selection }, editable = false) {
|
||||||
if (selection) {
|
if (selection) {
|
||||||
const coordinates = toLatLngs(selection);
|
const coordinates = toLatLngs(selection);
|
||||||
|
let polygon;
|
||||||
|
|
||||||
if (editable) {
|
if (editable) {
|
||||||
coordinates.forEach(polygon => map.freeDraw.create(polygon));
|
coordinates.forEach(polygon => map.freeDraw.create(polygon));
|
||||||
const polygon = map.freeDraw.all()[0];
|
[polygon] = markFreeDrawLayers(map);
|
||||||
if (polygon) {
|
|
||||||
map.fitBounds(polygon.getBounds());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const polygon = L.polygon(coordinates, {
|
polygon = L.polygon(coordinates, {
|
||||||
color: 'red',
|
color: 'red',
|
||||||
zIndex: 3
|
zIndex: 3
|
||||||
}).addTo(map);
|
});
|
||||||
|
polygon.addTo(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (polygon) {
|
||||||
map.fitBounds(polygon.getBounds());
|
map.fitBounds(polygon.getBounds());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export 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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCurrentMap(input) {
|
|
||||||
let element = input.closest('.toolbar').parentElement.querySelector('.carte');
|
|
||||||
|
|
||||||
if (MAPS.has(element)) {
|
|
||||||
return MAPS.get(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const EMPTY_GEO_JSON = '[]';
|
|
||||||
const ERROR_GEO_JSON = '';
|
|
||||||
|
|
||||||
export function addFreeDrawEvents(map, selector) {
|
export function addFreeDrawEvents(map, selector) {
|
||||||
const input = findInput(selector);
|
const input = findInput(selector);
|
||||||
map.freeDraw.on('markers', ({ latLngs }) => {
|
map.freeDraw.on('markers', ({ latLngs }) => {
|
||||||
|
@ -121,10 +77,65 @@ export function addFreeDrawEvents(map, selector) {
|
||||||
input.value = ERROR_GEO_JSON;
|
input.value = ERROR_GEO_JSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
markFreeDrawLayers(map);
|
||||||
fire(input, 'change');
|
fire(input, 'change');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function drawCadastre(map, { cadastres }, editable = false) {
|
||||||
|
drawLayer(
|
||||||
|
map,
|
||||||
|
cadastres,
|
||||||
|
editable ? CADASTRE_POLYGON_STYLE : noEditStyle(CADASTRE_POLYGON_STYLE)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawQuartiersPrioritaires(
|
||||||
|
map,
|
||||||
|
{ quartiersPrioritaires },
|
||||||
|
editable = false
|
||||||
|
) {
|
||||||
|
drawLayer(
|
||||||
|
map,
|
||||||
|
quartiersPrioritaires,
|
||||||
|
editable ? QP_POLYGON_STYLE : noEditStyle(QP_POLYGON_STYLE)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawParcellesAgricoles(map, { parcellesAgricoles }, editable = false) {
|
||||||
|
drawLayer(
|
||||||
|
map,
|
||||||
|
parcellesAgricoles,
|
||||||
|
editable ? RPG_POLYGON_STYLE : noEditStyle(RPG_POLYGON_STYLE)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
const closestToolbarElement = element.closest('.toolbar');
|
||||||
|
|
||||||
|
element = closestCarteElement
|
||||||
|
? closestCarteElement
|
||||||
|
: closestToolbarElement.parentElement.querySelector('.carte');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MAPS.has(element)) {
|
||||||
|
return MAPS.get(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const EMPTY_GEO_JSON = '[]';
|
||||||
|
const ERROR_GEO_JSON = '';
|
||||||
|
|
||||||
function toLatLngs({ coordinates }) {
|
function toLatLngs({ coordinates }) {
|
||||||
return coordinates.map(polygon =>
|
return coordinates.map(polygon =>
|
||||||
polygon[0].map(point => ({ lng: point[0], lat: point[1] }))
|
polygon[0].map(point => ({ lng: point[0], lat: point[1] }))
|
||||||
|
@ -137,28 +148,40 @@ function findInput(selector) {
|
||||||
: selector;
|
: selector;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createLayer(map, layerName) {
|
function createLayer(map) {
|
||||||
const layer = (LAYERS[layerName] = new L.GeoJSON(undefined, {
|
const layer = new L.GeoJSON(undefined, {
|
||||||
interactive: false
|
interactive: false
|
||||||
}));
|
});
|
||||||
layer.addTo(map);
|
layer.addTo(map);
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeLayer(map, layerName) {
|
function clearLayers(map) {
|
||||||
const layer = LAYERS[layerName];
|
map.eachLayer(layer => {
|
||||||
|
if (layer instanceof L.GeoJSON) {
|
||||||
if (layer) {
|
map.removeLayer(layer);
|
||||||
delete LAYERS[layerName];
|
}
|
||||||
map.removeLayer(layer);
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawLayer(map, data, style, layerName = 'default') {
|
function bringToFrontUserSelection(map) {
|
||||||
removeLayer(map, layerName);
|
map.eachLayer(layer => {
|
||||||
|
if (layer.isFreeDraw) {
|
||||||
|
layer.bringToFront();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function markFreeDrawLayers(map) {
|
||||||
|
return map.freeDraw.all().map(layer => {
|
||||||
|
layer.isFreeDraw = true;
|
||||||
|
return layer;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawLayer(map, data, style) {
|
||||||
if (Array.isArray(data) && data.length > 0) {
|
if (Array.isArray(data) && data.length > 0) {
|
||||||
const layer = createLayer(map, layerName);
|
const layer = createLayer(map);
|
||||||
|
|
||||||
data.forEach(function(item) {
|
data.forEach(function(item) {
|
||||||
layer.addData(item.geometry);
|
layer.addData(item.geometry);
|
||||||
|
@ -197,18 +220,33 @@ const RPG_POLYGON_STYLE = Object.assign({}, POLYGON_STYLE, {
|
||||||
});
|
});
|
||||||
|
|
||||||
delegate('click', '.carte.edit', event => {
|
delegate('click', '.carte.edit', event => {
|
||||||
let element = event.target;
|
const map = getCurrentMap(event.target);
|
||||||
let isPath = element.matches('.leaflet-container g path');
|
|
||||||
let map = element.matches('.carte') ? element : element.closest('.carte');
|
|
||||||
let freeDraw = MAPS.has(map) ? MAPS.get(map).freeDraw : null;
|
|
||||||
|
|
||||||
if (freeDraw) {
|
if (map) {
|
||||||
|
const isPath = event.target.matches('.leaflet-container g path');
|
||||||
if (isPath) {
|
if (isPath) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
freeDraw.mode(EDIT | DELETE);
|
map.freeDraw.mode(FreeDraw.EDIT | FreeDraw.DELETE);
|
||||||
}, 50);
|
}, 50);
|
||||||
} else {
|
} else {
|
||||||
freeDraw.mode(NONE);
|
map.freeDraw.mode(FreeDraw.NONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
delegate('click', '.toolbar .new-area', event => {
|
||||||
|
event.preventDefault();
|
||||||
|
const map = getCurrentMap(event.target);
|
||||||
|
|
||||||
|
if (map) {
|
||||||
|
map.freeDraw.mode(FreeDraw.CREATE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
delegate('autocomplete:select', '.toolbar [data-address]', event => {
|
||||||
|
const map = getCurrentMap(event.target);
|
||||||
|
|
||||||
|
if (map) {
|
||||||
|
geocodeAddress(map, event.detail.label);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
class AntiVirusJob < ApplicationJob
|
|
||||||
include ActiveStorage::Downloading
|
|
||||||
|
|
||||||
attr_reader :blob
|
|
||||||
|
|
||||||
def perform(virus_scan)
|
|
||||||
@blob = ActiveStorage::Blob.find_by(key: virus_scan.blob_key)
|
|
||||||
|
|
||||||
if @blob.present?
|
|
||||||
download_blob_to_tempfile do |file|
|
|
||||||
if ClamavService.safe_file?(file.path)
|
|
||||||
status = VirusScan.statuses.fetch(:safe)
|
|
||||||
else
|
|
||||||
status = VirusScan.statuses.fetch(:infected)
|
|
||||||
end
|
|
||||||
virus_scan.update(scanned_at: Time.zone.now, status: status)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -2,7 +2,11 @@ class EtablissementUpdateJob < ApplicationJob
|
||||||
queue_as :default
|
queue_as :default
|
||||||
|
|
||||||
def perform(dossier, siret)
|
def perform(dossier, siret)
|
||||||
etablissement_attributes = ApiEntrepriseService.get_etablissement_params_for_siret(siret, dossier.procedure_id)
|
begin
|
||||||
|
etablissement_attributes = ApiEntrepriseService.get_etablissement_params_for_siret(siret, dossier.procedure_id)
|
||||||
|
rescue
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if etablissement_attributes.present?
|
if etablissement_attributes.present?
|
||||||
if dossier.etablissement.present?
|
if dossier.etablissement.present?
|
||||||
|
|
10
app/jobs/virus_scanner_job.rb
Normal file
10
app/jobs/virus_scanner_job.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
class VirusScannerJob < ApplicationJob
|
||||||
|
def perform(blob)
|
||||||
|
metadata = extract_metadata_via_virus_scanner(blob)
|
||||||
|
blob.update!(metadata: blob.metadata.merge(metadata))
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_metadata_via_virus_scanner(blob)
|
||||||
|
ActiveStorage::VirusScanner.new(blob).metadata
|
||||||
|
end
|
||||||
|
end
|
46
app/lib/active_storage/virus_scanner.rb
Normal file
46
app/lib/active_storage/virus_scanner.rb
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
class ActiveStorage::VirusScanner
|
||||||
|
include ActiveStorage::Downloading
|
||||||
|
|
||||||
|
def initialize(blob)
|
||||||
|
@blob = blob
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :blob
|
||||||
|
|
||||||
|
PENDING = 'pending'
|
||||||
|
INFECTED = 'infected'
|
||||||
|
SAFE = 'safe'
|
||||||
|
|
||||||
|
def pending?
|
||||||
|
blob.metadata[:virus_scan_result] == PENDING
|
||||||
|
end
|
||||||
|
|
||||||
|
def infected?
|
||||||
|
blob.metadata[:virus_scan_result] == INFECTED
|
||||||
|
end
|
||||||
|
|
||||||
|
def safe?
|
||||||
|
blob.metadata[:virus_scan_result] == SAFE
|
||||||
|
end
|
||||||
|
|
||||||
|
def analyzed?
|
||||||
|
blob.metadata[:virus_scan_result].present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def analyze_later
|
||||||
|
if !analyzed?
|
||||||
|
blob.update!(metadata: blob.metadata.merge(virus_scan_result: PENDING))
|
||||||
|
VirusScannerJob.perform_later(blob)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def metadata
|
||||||
|
download_blob_to_tempfile do |file|
|
||||||
|
if ClamavService.safe_file?(file.path)
|
||||||
|
{ virus_scan_result: SAFE, scanned_at: Time.zone.now }
|
||||||
|
else
|
||||||
|
{ virus_scan_result: INFECTED, scanned_at: Time.zone.now }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,9 +7,11 @@ class ApiEntreprise::Adapter
|
||||||
end
|
end
|
||||||
|
|
||||||
def data_source
|
def data_source
|
||||||
@data_source ||= get_resource
|
begin
|
||||||
rescue
|
@data_source ||= get_resource
|
||||||
@data_source = nil
|
rescue RestClient::ResourceNotFound
|
||||||
|
@data_source = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_params
|
def to_params
|
||||||
|
|
|
@ -34,13 +34,21 @@ class ApiEntreprise::API
|
||||||
|
|
||||||
if response.success?
|
if response.success?
|
||||||
JSON.parse(response.body, symbolize_names: true)
|
JSON.parse(response.body, symbolize_names: true)
|
||||||
else
|
elsif response.code == 404 || response.code == 422
|
||||||
raise RestClient::ResourceNotFound
|
raise RestClient::ResourceNotFound
|
||||||
|
else
|
||||||
|
raise RestClient::RequestFailed
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.url(resource_name, siret_or_siren)
|
def self.url(resource_name, siret_or_siren)
|
||||||
[API_ENTREPRISE_URL, resource_name, siret_or_siren].join("/")
|
base_url = [API_ENTREPRISE_URL, resource_name, siret_or_siren].join("/")
|
||||||
|
|
||||||
|
if Flipflop.insee_api_v3?
|
||||||
|
base_url += "?with_insee_v3=true"
|
||||||
|
end
|
||||||
|
|
||||||
|
base_url
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.params(siret_or_siren, procedure_id)
|
def self.params(siret_or_siren, procedure_id)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
class Avis < ApplicationRecord
|
class Avis < ApplicationRecord
|
||||||
include EmailSanitizableConcern
|
include EmailSanitizableConcern
|
||||||
include VirusScanConcern
|
|
||||||
|
|
||||||
belongs_to :dossier, touch: true
|
belongs_to :dossier, touch: true
|
||||||
belongs_to :gestionnaire
|
belongs_to :gestionnaire
|
||||||
|
@ -22,9 +21,6 @@ class Avis < ApplicationRecord
|
||||||
scope :by_latest, -> { order(updated_at: :desc) }
|
scope :by_latest, -> { order(updated_at: :desc) }
|
||||||
scope :updated_since?, -> (date) { where('avis.updated_at > ?', date) }
|
scope :updated_since?, -> (date) { where('avis.updated_at > ?', date) }
|
||||||
|
|
||||||
after_commit :create_avis_virus_scan
|
|
||||||
after_initialize { add_virus_scan_on(self.piece_justificative_file) }
|
|
||||||
|
|
||||||
# The form allows subtmitting avis requests to several emails at once,
|
# The form allows subtmitting avis requests to several emails at once,
|
||||||
# hence this virtual attribute.
|
# hence this virtual attribute.
|
||||||
attr_accessor :emails
|
attr_accessor :emails
|
||||||
|
@ -41,6 +37,27 @@ class Avis < ApplicationRecord
|
||||||
Avis.find_by(id: avis_id)&.email == email
|
Avis.find_by(id: avis_id)&.email == email
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# FIXME remove this after migrating virus_scan to blob metadata
|
||||||
|
def virus_scan
|
||||||
|
VirusScan.find_by(blob_key: piece_justificative_file.blob.key)
|
||||||
|
end
|
||||||
|
|
||||||
|
def virus_scan_safe?
|
||||||
|
virus_scan&.safe? || piece_justificative_file.virus_scanner.safe?
|
||||||
|
end
|
||||||
|
|
||||||
|
def virus_scan_infected?
|
||||||
|
virus_scan&.infected? || piece_justificative_file.virus_scanner.infected?
|
||||||
|
end
|
||||||
|
|
||||||
|
def virus_scan_pending?
|
||||||
|
virus_scan&.pending? || piece_justificative_file.virus_scanner.pending?
|
||||||
|
end
|
||||||
|
|
||||||
|
def virus_scan_no_scan?
|
||||||
|
virus_scan.blank? && !piece_justificative_file.virus_scanner.analyzed?
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def notify_gestionnaire
|
def notify_gestionnaire
|
||||||
|
@ -54,8 +71,4 @@ class Avis < ApplicationRecord
|
||||||
self.email = nil
|
self.email = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_avis_virus_scan
|
|
||||||
create_virus_scan(self.piece_justificative_file)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
class Champs::PieceJustificativeChamp < Champ
|
class Champs::PieceJustificativeChamp < Champ
|
||||||
after_commit :create_virus_scan
|
|
||||||
|
|
||||||
PIECE_JUSTIFICATIVE_FILE_MAX_SIZE = 200.megabytes
|
PIECE_JUSTIFICATIVE_FILE_MAX_SIZE = 200.megabytes
|
||||||
|
|
||||||
PIECE_JUSTIFICATIVE_FILE_ACCEPTED_FORMATS = [
|
PIECE_JUSTIFICATIVE_FILE_ACCEPTED_FORMATS = [
|
||||||
|
@ -48,20 +46,26 @@ class Champs::PieceJustificativeChamp < Champ
|
||||||
errors
|
errors
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# FIXME remove this after migrating virus_scan to blob metadata
|
||||||
|
def virus_scan_safe?
|
||||||
|
virus_scan&.safe? || piece_justificative_file.virus_scanner.safe?
|
||||||
|
end
|
||||||
|
|
||||||
|
def virus_scan_infected?
|
||||||
|
virus_scan&.infected? || piece_justificative_file.virus_scanner.infected?
|
||||||
|
end
|
||||||
|
|
||||||
|
def virus_scan_pending?
|
||||||
|
virus_scan&.pending? || piece_justificative_file.virus_scanner.pending?
|
||||||
|
end
|
||||||
|
|
||||||
|
def virus_scan_no_scan?
|
||||||
|
virus_scan.blank? && !piece_justificative_file.virus_scanner.analyzed?
|
||||||
|
end
|
||||||
|
|
||||||
def for_api
|
def for_api
|
||||||
if piece_justificative_file.attached? && (virus_scan&.safe? || virus_scan&.pending?)
|
if piece_justificative_file.attached? && (virus_scan_safe? || virus_scan_pending?)
|
||||||
Rails.application.routes.url_helpers.url_for(piece_justificative_file)
|
Rails.application.routes.url_helpers.url_for(piece_justificative_file)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def create_virus_scan
|
|
||||||
if self.piece_justificative_file&.attachment&.blob.present?
|
|
||||||
VirusScan.where(champ: self).where.not(blob_key: self.piece_justificative_file.blob.key).delete_all
|
|
||||||
VirusScan.find_or_create_by!(champ: self, blob_key: self.piece_justificative_file.blob.key) do |virus_scan|
|
|
||||||
virus_scan.status = VirusScan.statuses.fetch(:pending)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
module VirusScanConcern
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
|
|
||||||
attr_reader :attachment_attribute
|
|
||||||
|
|
||||||
def add_virus_scan_on(piece_justificative)
|
|
||||||
@attachment_attribute = piece_justificative
|
|
||||||
end
|
|
||||||
|
|
||||||
def virus_scan
|
|
||||||
VirusScan.find_by(blob_key: self.attachment_attribute.blob.key)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_virus_scan(piece_justificative)
|
|
||||||
if piece_justificative&.attachment&.blob.present?
|
|
||||||
VirusScan.find_or_create_by!(blob_key: piece_justificative.blob.key) do |virus_scan|
|
|
||||||
virus_scan.status = VirusScan.statuses.fetch(:pending)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -346,14 +346,6 @@ class Procedure < ApplicationRecord
|
||||||
percentile_time(:en_construction_at, :processed_at, 90)
|
percentile_time(:en_construction_at, :processed_at, 90)
|
||||||
end
|
end
|
||||||
|
|
||||||
def usual_verification_time
|
|
||||||
percentile_time(:en_construction_at, :en_instruction_at, 90)
|
|
||||||
end
|
|
||||||
|
|
||||||
def usual_instruction_time
|
|
||||||
percentile_time(:en_instruction_at, :processed_at, 90)
|
|
||||||
end
|
|
||||||
|
|
||||||
PATH_AVAILABLE = :available
|
PATH_AVAILABLE = :available
|
||||||
PATH_AVAILABLE_PUBLIEE = :available_publiee
|
PATH_AVAILABLE_PUBLIEE = :available_publiee
|
||||||
PATH_NOT_AVAILABLE = :not_available
|
PATH_NOT_AVAILABLE = :not_available
|
||||||
|
|
|
@ -6,12 +6,4 @@ class VirusScan < ApplicationRecord
|
||||||
safe: 'safe',
|
safe: 'safe',
|
||||||
infected: 'infected'
|
infected: 'infected'
|
||||||
}
|
}
|
||||||
|
|
||||||
validates :champ_id, uniqueness: { scope: :blob_key }
|
|
||||||
|
|
||||||
after_create :perform_scan
|
|
||||||
|
|
||||||
def perform_scan
|
|
||||||
AntiVirusJob.perform_later(self)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
class ApiEntrepriseService
|
class ApiEntrepriseService
|
||||||
|
# Retrieve all informations we can get about a SIRET.
|
||||||
|
#
|
||||||
|
# Returns nil if the SIRET is unknown; and nested params
|
||||||
|
# suitable for being saved into a Etablissement object otherwise.
|
||||||
|
#
|
||||||
|
# Raises a RestClient::RequestFailed exception on transcient errors
|
||||||
|
# (timeout, 5XX HTTP error code, etc.)
|
||||||
def self.get_etablissement_params_for_siret(siret, procedure_id)
|
def self.get_etablissement_params_for_siret(siret, procedure_id)
|
||||||
etablissement_params = ApiEntreprise::EtablissementAdapter.new(siret, procedure_id).to_params
|
etablissement_params = ApiEntreprise::EtablissementAdapter.new(siret, procedure_id).to_params
|
||||||
entreprise_params = ApiEntreprise::EntrepriseAdapter.new(siret, procedure_id).to_params
|
entreprise_params = ApiEntreprise::EntrepriseAdapter.new(siret, procedure_id).to_params
|
||||||
|
|
|
@ -4,4 +4,4 @@
|
||||||
partial: 'shared/champs/carte/geo_areas',
|
partial: 'shared/champs/carte/geo_areas',
|
||||||
locals: { champ: @champ, error: @error }) %>
|
locals: { champ: @champ, error: @error }) %>
|
||||||
|
|
||||||
DS.drawMapData("<%= @selector %>", <%= geo_data(@champ) %>);
|
<%= fire_event('carte:update', { selector: @selector, data: @champ.to_render_data }.to_json) %>
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
= favicon_link_tag(image_url("favicons/32x32.png"), type: "image/png", sizes: "32x32")
|
= favicon_link_tag(image_url("favicons/32x32.png"), type: "image/png", sizes: "32x32")
|
||||||
= favicon_link_tag(image_url("favicons/96x96.png"), type: "image/png", sizes: "96x96")
|
= favicon_link_tag(image_url("favicons/96x96.png"), type: "image/png", sizes: "96x96")
|
||||||
|
|
||||||
= javascript_pack_tag 'application', defer: true, 'data-turbolinks-track': 'reload'
|
- packs = ['application', 'track', administrateur_signed_in? ? 'sendinblue' : nil].compact
|
||||||
|
= javascript_packs_with_chunks_tag *packs, defer: true, 'data-turbolinks-track': 'reload'
|
||||||
= stylesheet_link_tag 'new_design/new_application', media: 'all', 'data-turbolinks-track': 'reload'
|
= stylesheet_link_tag 'new_design/new_application', media: 'all', 'data-turbolinks-track': 'reload'
|
||||||
= stylesheet_link_tag 'new_design/print', media: 'print', 'data-turbolinks-track': 'reload'
|
= stylesheet_link_tag 'new_design/print', media: 'print', 'data-turbolinks-track': 'reload'
|
||||||
|
|
||||||
|
@ -42,7 +43,3 @@
|
||||||
= javascript_include_tag :xray
|
= javascript_include_tag :xray
|
||||||
|
|
||||||
= yield :charts_js
|
= yield :charts_js
|
||||||
|
|
||||||
= javascript_pack_tag 'track', async: true
|
|
||||||
- if administrateur_signed_in?
|
|
||||||
= javascript_pack_tag 'sendinblue', async: true
|
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
|
|
||||||
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': "reload"
|
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': "reload"
|
||||||
= stylesheet_link_tag 'print', media: 'print', 'data-turbolinks-track': "reload"
|
= stylesheet_link_tag 'print', media: 'print', 'data-turbolinks-track': "reload"
|
||||||
= javascript_pack_tag 'application-old', defer: true, 'data-turbolinks-track': 'reload'
|
- packs = ['application-old', 'track', administrateur_signed_in? ? 'sendinblue' : nil].compact
|
||||||
|
= javascript_packs_with_chunks_tag *packs, defer: true, 'data-turbolinks-track': 'reload'
|
||||||
= javascript_include_tag 'application', defer: true, 'data-turbolinks-track': 'reload'
|
= javascript_include_tag 'application', defer: true, 'data-turbolinks-track': 'reload'
|
||||||
= csrf_meta_tags
|
= csrf_meta_tags
|
||||||
= Gon::Base.render_data(camel_case: true, init: true)
|
= Gon::Base.render_data(camel_case: true, init: true)
|
||||||
|
@ -42,6 +43,5 @@
|
||||||
%i.fa.fa-times{ style: 'position: fixed; top: 10; right: 30; color: white;' }
|
%i.fa.fa-times{ style: 'position: fixed; top: 10; right: 30; color: white;' }
|
||||||
|
|
||||||
= render partial: 'layouts/footer', locals: { main_container_size: main_container_size }
|
= render partial: 'layouts/footer', locals: { main_container_size: main_container_size }
|
||||||
= javascript_pack_tag 'track', async: true
|
|
||||||
- if administrateur_signed_in?
|
- if administrateur_signed_in?
|
||||||
= javascript_pack_tag 'sendinblue', async: true
|
= javascript_pack_tag 'sendinblue', async: true
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
- pj = champ.piece_justificative_file
|
|
||||||
|
|
||||||
.pj-link
|
|
||||||
- if champ.virus_scan.blank? || champ.virus_scan.safe?
|
|
||||||
= link_to url_for(pj), target: '_blank', rel: 'noopener', title: "Télécharger la pièce jointe" do
|
|
||||||
%span.icon.attachment
|
|
||||||
= pj.filename.to_s
|
|
||||||
- if champ.virus_scan.blank?
|
|
||||||
(ce fichier n’a pas été analysé par notre antivirus, téléchargez-le avec précaution)
|
|
||||||
|
|
||||||
- else
|
|
||||||
= pj.filename.to_s
|
|
||||||
- if champ.virus_scan.pending?
|
|
||||||
(analyse antivirus en cours
|
|
||||||
= link_to "rafraichir", request.path
|
|
||||||
)
|
|
||||||
- elsif champ.virus_scan.infected?
|
|
||||||
- if user_can_upload
|
|
||||||
(virus détecté, merci d’envoyer un autre fichier)
|
|
||||||
- else
|
|
||||||
(virus détecté, le téléchargement de ce fichier est bloqué)
|
|
|
@ -1,5 +1,5 @@
|
||||||
- pj = champ.piece_justificative_file
|
- pj = champ.piece_justificative_file
|
||||||
- if pj.attached?
|
- if pj.attached?
|
||||||
= render partial: "shared/champs/piece_justificative/pj_link", locals: { champ: champ, user_can_upload: false }
|
= render partial: "shared/piece_jointe/pj_link", locals: { object: champ, pj: pj, user_can_upload: false }
|
||||||
- else
|
- else
|
||||||
Pièce justificative non fournie
|
Pièce justificative non fournie
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
Nous n’avons pas trouvé d’établissement correspondant à ce numéro de SIRET.
|
Nous n’avons pas trouvé d’établissement correspondant à ce numéro de SIRET.
|
||||||
= link_to('Plus d’informations', "https://faq.demarches-simplifiees.fr/article/4-erreur-siret", target: '_blank', rel: 'noopener')
|
= link_to('Plus d’informations', "https://faq.demarches-simplifiees.fr/article/4-erreur-siret", target: '_blank', rel: 'noopener')
|
||||||
|
|
||||||
|
- when :network_error
|
||||||
|
= t('errors.messages.siret_network_error')
|
||||||
|
|
||||||
- else
|
- else
|
||||||
- if siret.present? && siret == etablissement&.siret
|
- if siret.present? && siret == etablissement&.siret
|
||||||
= render partial: 'shared/dossiers/editable_champs/etablissement_titre', locals: { etablissement: etablissement }
|
= render partial: 'shared/dossiers/editable_champs/etablissement_titre', locals: { etablissement: etablissement }
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
- if pj.attached?
|
- if pj.attached?
|
||||||
.piece-justificative-actions{ id: "piece_justificative_#{champ.id}" }
|
.piece-justificative-actions{ id: "piece_justificative_#{champ.id}" }
|
||||||
.piece-justificative-action
|
.piece-justificative-action
|
||||||
= render partial: "shared/champs/piece_justificative/pj_link", locals: { champ: champ, user_can_upload: true }
|
= render partial: "shared/piece_jointe/pj_link", locals: { object: champ, pj: champ.piece_justificative_file, user_can_upload: true }
|
||||||
.piece-justificative-action
|
.piece-justificative-action
|
||||||
- if champ.private?
|
- if champ.private?
|
||||||
= link_to 'Supprimer', gestionnaire_champ_purge_champ_piece_justificative_path(procedure_id: champ.dossier.procedure_id, dossier_id: champ.dossier_id, champ_id: champ.id), remote: true, method: :delete, class: 'button small danger'
|
= link_to 'Supprimer', gestionnaire_champ_purge_champ_piece_justificative_path(procedure_id: champ.dossier.procedure_id, dossier_id: champ.dossier_id, champ_id: champ.id), remote: true, method: :delete, class: 'button small danger'
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
- if pj.attached?
|
- if pj.attached?
|
||||||
.pj-link
|
.pj-link
|
||||||
- if object.virus_scan.blank? || object.virus_scan.safe?
|
- if object.virus_scan_safe? || object.virus_scan_no_scan?
|
||||||
= link_to url_for(pj), target: '_blank', title: "Télécharger la pièce jointe" do
|
= link_to url_for(pj), target: '_blank', rel: 'noopener', title: "Télécharger la pièce jointe" do
|
||||||
%span.icon.attachment
|
%span.icon.attachment
|
||||||
= pj.filename.to_s
|
= pj.filename.to_s
|
||||||
- if object.virus_scan.blank?
|
- if object.virus_scan_no_scan?
|
||||||
(ce fichier n’a pas été analysé par notre antivirus, téléchargez-le avec précaution)
|
(ce fichier n’a pas été analysé par notre antivirus, téléchargez-le avec précaution)
|
||||||
|
|
||||||
- else
|
- else
|
||||||
= pj.filename.to_s
|
= pj.filename.to_s
|
||||||
- if object.virus_scan.pending?
|
- if object.virus_scan_pending?
|
||||||
(analyse antivirus en cours
|
(analyse antivirus en cours
|
||||||
= link_to "rafraichir", request.path
|
= link_to "rafraichir", request.path
|
||||||
)
|
)
|
||||||
- elsif object.virus_scan.infected?
|
- elsif object.virus_scan_infected?
|
||||||
- if user_can_upload
|
- if user_can_upload
|
||||||
(virus détecté, merci d’envoyer un autre fichier)
|
(virus détecté, merci d’envoyer un autre fichier)
|
||||||
- else
|
- else
|
||||||
|
|
9
app/views/users/dossiers/show/_estimated_delay.html.haml
Normal file
9
app/views/users/dossiers/show/_estimated_delay.html.haml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/ FIXME: remove the custom procedure switch at some point
|
||||||
|
- procedure_id_for_which_we_hide_the_estimated_delay = 6547
|
||||||
|
- procedure_path_for_which_we_hide_the_estimated_delay = 'deposer-une-offre-de-stage'
|
||||||
|
- show_time_means = procedure.id != procedure_id_for_which_we_hide_the_estimated_delay && procedure.path != procedure_path_for_which_we_hide_the_estimated_delay
|
||||||
|
|
||||||
|
- cache(procedure.id, expires_in: 1.day) do
|
||||||
|
- if procedure.usual_traitement_time && show_time_means
|
||||||
|
%p
|
||||||
|
Habituellement, les dossiers de cette démarche sont traités dans un délai de #{distance_of_time_in_words(procedure.usual_traitement_time)}.
|
|
@ -1,6 +1,4 @@
|
||||||
- procedure_id_for_which_we_hide_the_time_means = 6547
|
|
||||||
- procedure_path_for_which_we_hide_the_time_means = 'deposer-une-offre-de-stage'
|
|
||||||
- show_time_means = dossier.procedure.id != procedure_id_for_which_we_hide_the_time_means && dossier.procedure.path != procedure_path_for_which_we_hide_the_time_means
|
|
||||||
|
|
||||||
.status-overview
|
.status-overview
|
||||||
- if !dossier.termine?
|
- if !dossier.termine?
|
||||||
|
@ -24,17 +22,11 @@
|
||||||
- elsif dossier.en_construction?
|
- elsif dossier.en_construction?
|
||||||
.en-construction
|
.en-construction
|
||||||
%p Un instructeur de l’administration est en train de vérifier que votre dossier est bien complet. Si des modifications sont nécessaires, vous recevrez un message avec les modifications à effectuer.
|
%p Un instructeur de l’administration est en train de vérifier que votre dossier est bien complet. Si des modifications sont nécessaires, vous recevrez un message avec les modifications à effectuer.
|
||||||
|
|
||||||
%p
|
%p
|
||||||
Sinon,
|
Sinon,
|
||||||
= succeed '.' do
|
= succeed '.' do
|
||||||
%strong votre dossier passera directement en instruction
|
%strong votre dossier passera directement en instruction
|
||||||
|
= render partial: 'users/dossiers/show/estimated_delay', locals: { procedure: dossier.procedure }
|
||||||
/ FIXME: remove the custom procedure switch at some point
|
|
||||||
- if dossier.procedure.usual_verification_time && show_time_means
|
|
||||||
- cache(dossier.procedure, expires_in: 1.week) do
|
|
||||||
%p
|
|
||||||
Habituellement, les dossiers de cette démarche sont vérifiés dans un délai de #{distance_of_time_in_words(dossier.procedure.usual_verification_time)}.
|
|
||||||
|
|
||||||
- elsif dossier.en_instruction?
|
- elsif dossier.en_instruction?
|
||||||
.en-instruction
|
.en-instruction
|
||||||
|
@ -44,12 +36,7 @@
|
||||||
%strong
|
%strong
|
||||||
vous recevrez un email
|
vous recevrez un email
|
||||||
avec le résultat.
|
avec le résultat.
|
||||||
|
= render partial: 'users/dossiers/show/estimated_delay', locals: { procedure: dossier.procedure }
|
||||||
/ FIXME: remove the custom procedure switch at some point
|
|
||||||
- if dossier.procedure.usual_instruction_time && show_time_means
|
|
||||||
- cache(dossier.procedure, expires_in: 1.week) do
|
|
||||||
%p
|
|
||||||
Habituellement, les dossiers de cette démarche sont traités dans un délai de #{distance_of_time_in_words(dossier.procedure.usual_instruction_time)}.
|
|
||||||
|
|
||||||
- elsif dossier.accepte?
|
- elsif dossier.accepte?
|
||||||
.accepte
|
.accepte
|
||||||
|
|
|
@ -20,6 +20,8 @@ Flipflop.configure do
|
||||||
group :production do
|
group :production do
|
||||||
feature :remote_storage,
|
feature :remote_storage,
|
||||||
default: ENV['FOG_ENABLED'] == 'enabled'
|
default: ENV['FOG_ENABLED'] == 'enabled'
|
||||||
|
feature :insee_api_v3,
|
||||||
|
default: true
|
||||||
feature :weekly_overview,
|
feature :weekly_overview,
|
||||||
default: ENV['APP_NAME'] == 'tps'
|
default: ENV['APP_NAME'] == 'tps'
|
||||||
feature :pre_maintenance_mode
|
feature :pre_maintenance_mode
|
||||||
|
|
|
@ -1 +1,29 @@
|
||||||
ActiveStorage::Service.url_expires_in = 1.hour
|
ActiveStorage::Service.url_expires_in = 1.hour
|
||||||
|
|
||||||
|
# We want to run the virus scan on every ActiveStorage attachment,
|
||||||
|
# regardless of its type (user-uploaded document, instructeur-uploaded attestation, form template, etc.)
|
||||||
|
#
|
||||||
|
# To do this, the best place to work on is the ActiveStorage::Attachment
|
||||||
|
# objects themselves.
|
||||||
|
#
|
||||||
|
# We have to monkey patch ActiveStorage in order to always run an analyzer.
|
||||||
|
# The way analyzable blob interface work is by running the first accepted analyzer.
|
||||||
|
# This is not what we want for the virus scan. Using analyzer interface is still beneficial
|
||||||
|
# as it takes care of downloading the blob.
|
||||||
|
ActiveStorage::Attachment.class_eval do
|
||||||
|
after_create_commit :virus_scan
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def virus_scan
|
||||||
|
ActiveStorage::VirusScanner.new(blob).analyze_later
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ActiveStorage::Attached::One.class_eval do
|
||||||
|
def virus_scanner
|
||||||
|
if attached?
|
||||||
|
ActiveStorage::VirusScanner.new(attachment.blob)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -182,7 +182,8 @@ fr:
|
||||||
dossier_map_not_activated: "Le dossier n'a pas accès à la cartographie."
|
dossier_map_not_activated: "Le dossier n'a pas accès à la cartographie."
|
||||||
invalid_siret: "Le siret est incorrect"
|
invalid_siret: "Le siret est incorrect"
|
||||||
procedure_not_found: "La démarche n'existe pas"
|
procedure_not_found: "La démarche n'existe pas"
|
||||||
siret_unknown: 'Désolé, nous n’avons pas trouvé d’établissement enregistré correspondant à ce numéro SIRET'
|
siret_unknown: 'Désolé, nous n’avons pas trouvé d’établissement enregistré correspondant à ce numéro SIRET.'
|
||||||
|
siret_network_error: 'Désolé, la récupération des informations SIRET est temporairement indisponible. Veuillez réessayer dans quelques instants.'
|
||||||
etablissement_fail: 'Désolé, nous n’avons pas réussi à enregistrer l’établissement correspondant à ce numéro SIRET'
|
etablissement_fail: 'Désolé, nous n’avons pas réussi à enregistrer l’établissement correspondant à ce numéro SIRET'
|
||||||
france_connect:
|
france_connect:
|
||||||
connexion: "Erreur lors de la connexion à France Connect."
|
connexion: "Erreur lors de la connexion à France Connect."
|
||||||
|
|
|
@ -7,5 +7,6 @@ const resolve = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
environment.splitChunks();
|
||||||
environment.config.merge({ resolve });
|
environment.config.merge({ resolve });
|
||||||
module.exports = environment;
|
module.exports = environment;
|
||||||
|
|
|
@ -2,8 +2,4 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'production';
|
||||||
|
|
||||||
const environment = require('./environment');
|
const environment = require('./environment');
|
||||||
|
|
||||||
// https://github.com/rails/webpacker/issues/1235
|
|
||||||
environment.config.optimization.minimizer[0].options.uglifyOptions.ecma = 5; // for IE 11 support
|
|
||||||
environment.config.optimization.minimizer[0].options.uglifyOptions.safari10 = true;
|
|
||||||
|
|
||||||
module.exports = environment.toWebpackConfig();
|
module.exports = environment.toWebpackConfig();
|
||||||
|
|
22
lib/tasks/deployment/20190425102459_migrate_virus_scans.rake
Normal file
22
lib/tasks/deployment/20190425102459_migrate_virus_scans.rake
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
namespace :after_party do
|
||||||
|
desc 'Deployment task: migrate_virus_scans'
|
||||||
|
task migrate_virus_scans: :environment do
|
||||||
|
puts "Running deploy task 'migrate_virus_scans'"
|
||||||
|
|
||||||
|
virus_scans = VirusScan.all
|
||||||
|
progress = ProgressReport.new(virus_scans.count)
|
||||||
|
virus_scans.find_each do |virus_scan|
|
||||||
|
blob = ActiveStorage::Blob.find_by(key: virus_scan.blob_key)
|
||||||
|
if blob
|
||||||
|
metadata = { virus_scan_result: virus_scan.status, scanned_at: virus_scan.scanned_at }
|
||||||
|
blob.update_column(:metadata, blob.metadata.merge(metadata))
|
||||||
|
end
|
||||||
|
progress.inc
|
||||||
|
end
|
||||||
|
progress.finish
|
||||||
|
|
||||||
|
# Update task as completed. If you remove the line below, the task will
|
||||||
|
# run with every deploy (or every time you call after_party:run).
|
||||||
|
AfterParty::TaskRecord.create version: '20190425102459'
|
||||||
|
end
|
||||||
|
end
|
46
package.json
46
package.json
|
@ -1,44 +1,44 @@
|
||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.15",
|
"@fortawesome/fontawesome-svg-core": "^1.2.17",
|
||||||
"@fortawesome/free-solid-svg-icons": "^5.7.2",
|
"@fortawesome/free-solid-svg-icons": "^5.8.1",
|
||||||
"@fortawesome/react-fontawesome": "^0.1.4",
|
"@fortawesome/react-fontawesome": "^0.1.4",
|
||||||
"@rails/webpacker": "4.0.0-pre.3",
|
"@rails/webpacker": "4.0.2",
|
||||||
"@sentry/browser": "^4.6.5",
|
"@sentry/browser": "^5.1.0",
|
||||||
"@turf/area": "^6.0.1",
|
"@turf/area": "^6.0.1",
|
||||||
"activestorage": "^5.2.2",
|
"activestorage": "^5.2.3",
|
||||||
"autocomplete.js": "^0.36.0",
|
"autocomplete.js": "^0.36.0",
|
||||||
"babel-plugin-macros": "^2.5.0",
|
"babel-plugin-macros": "^2.5.1",
|
||||||
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
|
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
|
||||||
"chartkick": "^3.0.1",
|
"chartkick": "^3.0.2",
|
||||||
"debounce": "^1.2.0",
|
"debounce": "^1.2.0",
|
||||||
"dom4": "^2.1.3",
|
"dom4": "^2.1.4",
|
||||||
"highcharts": "^6.1.2",
|
"highcharts": "^6.1.2",
|
||||||
"jquery": "^3.3.1",
|
"jquery": "^3.4.0",
|
||||||
"leaflet-freedraw": "^2.9.0",
|
"leaflet": "^1.4.0",
|
||||||
"leaflet": "^1.3.4",
|
"leaflet-freedraw": "^2.10.0",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"rails-ujs": "^5.2.2",
|
"rails-ujs": "^5.2.3",
|
||||||
"ramda": "^0.25.0",
|
"ramda": "=0.24.1",
|
||||||
"react_ujs": "^2.4.4",
|
"react": "^16.8.6",
|
||||||
"react-dom": "^16.8.4",
|
"react-dom": "^16.8.6",
|
||||||
"react-scroll-to-component": "^1.0.2",
|
"react-scroll-to-component": "^1.0.2",
|
||||||
"react-sortable-hoc": "^1.7.1",
|
"react-sortable-hoc": "^1.7.1",
|
||||||
"react": "^16.8.4",
|
"react_ujs": "^2.5.0",
|
||||||
"select2": "^4.0.6-rc.1",
|
"select2": "^4.0.6-rc.1",
|
||||||
"turbolinks": "^5.2.0"
|
"turbolinks": "^5.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-eslint": "^10.0.1",
|
"babel-eslint": "^10.0.1",
|
||||||
"eclint": "^2.8.0",
|
"eclint": "^2.8.1",
|
||||||
"eslint": "^5.9.0",
|
"eslint": "^5.16.0",
|
||||||
"eslint-config-prettier": "^3.3.0",
|
"eslint-config-prettier": "^4.1.0",
|
||||||
"eslint-plugin-prettier": "^3.0.0",
|
"eslint-plugin-prettier": "^3.0.1",
|
||||||
"eslint-plugin-react": "^7.12.4",
|
"eslint-plugin-react": "^7.12.4",
|
||||||
"eslint-plugin-react-hooks": "^1.5.1",
|
"eslint-plugin-react-hooks": "^1.6.0",
|
||||||
"prettier": "^1.15.3",
|
"prettier": "^1.17.0",
|
||||||
"webpack-dev-server": "^3.1.9"
|
"webpack-dev-server": "^3.3.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint:ec": "eclint check $({ git ls-files ; find vendor -type f ; echo 'db/schema.rb' ; } | sort | uniq -u)",
|
"lint:ec": "eclint check $({ git ls-files ; find vendor -type f ; echo 'db/schema.rb' ; } | sort | uniq -u)",
|
||||||
|
|
|
@ -45,7 +45,7 @@ describe Champs::CarteController, type: :controller do
|
||||||
expect(assigns(:error)).to eq(nil)
|
expect(assigns(:error)).to eq(nil)
|
||||||
expect(champ.reload.value).to eq(nil)
|
expect(champ.reload.value).to eq(nil)
|
||||||
expect(champ.reload.geo_areas).to eq([])
|
expect(champ.reload.geo_areas).to eq([])
|
||||||
expect(response.body).to include("DS.drawMapData(\".carte-1\", {\"position\":{\"lon\":\"2.428462\",\"lat\":\"46.538192\",\"zoom\":\"13\"},\"selection\":null,\"quartiersPrioritaires\":[],\"cadastres\":[],\"parcellesAgricoles\":[]});")
|
expect(response.body).to include("DS.fire('carte:update', {\"selector\":\".carte-1\",\"data\":{\"position\":{\"lon\":\"2.428462\",\"lat\":\"46.538192\",\"zoom\":\"13\"},\"selection\":null,\"quartiersPrioritaires\":[],\"cadastres\":[],\"parcellesAgricoles\":[]}});")
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -20,74 +20,105 @@ describe Champs::SiretController, type: :controller do
|
||||||
end
|
end
|
||||||
let(:siret) { '' }
|
let(:siret) { '' }
|
||||||
|
|
||||||
context 'when user is connected' do
|
context 'when the user is signed in' do
|
||||||
render_views
|
render_views
|
||||||
before { sign_in user }
|
before { sign_in user }
|
||||||
|
|
||||||
context 'when siret empty' do
|
context 'when the SIRET is empty' do
|
||||||
before {
|
subject! { get :show, params: params, format: 'js' }
|
||||||
get :show, params: params, format: 'js'
|
|
||||||
}
|
|
||||||
|
|
||||||
it 'empty info message' do
|
it 'clears the etablissement and SIRET on the model' do
|
||||||
|
champ.reload
|
||||||
|
expect(champ.etablissement).to be_nil
|
||||||
|
expect(champ.value).to be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'clears any information or error message' do
|
||||||
expect(response.body).to include('.siret-info-1')
|
expect(response.body).to include('.siret-info-1')
|
||||||
expect(response.body).to include('innerHTML = ""')
|
expect(response.body).to include('innerHTML = ""')
|
||||||
champ.reload
|
|
||||||
expect(champ.etablissement).to be_nil
|
|
||||||
expect(champ.value).to be_empty
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when siret invalid' do
|
context 'when the SIRET is invalid' do
|
||||||
let(:siret) { '1234' }
|
let(:siret) { '1234' }
|
||||||
before {
|
|
||||||
get :show, params: params, format: 'js'
|
|
||||||
}
|
|
||||||
|
|
||||||
it 'invalid error' do
|
subject! { get :show, params: params, format: 'js' }
|
||||||
|
|
||||||
|
it 'clears the etablissement and SIRET on the model' do
|
||||||
|
champ.reload
|
||||||
|
expect(champ.etablissement).to be_nil
|
||||||
|
expect(champ.value).to be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'displays a “SIRET is invalid” error message' do
|
||||||
expect(response.body).to include('Le numéro de SIRET doit comporter exactement 14 chiffres.')
|
expect(response.body).to include('Le numéro de SIRET doit comporter exactement 14 chiffres.')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the API is unavailable' do
|
||||||
|
let(:siret) { '82161143100015' }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(controller).to receive(:find_etablissement_with_siret).and_raise(RestClient::RequestFailed)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject! { get :show, params: params, format: 'js' }
|
||||||
|
|
||||||
|
it 'clears the etablissement and SIRET on the model' do
|
||||||
champ.reload
|
champ.reload
|
||||||
expect(champ.etablissement).to be_nil
|
expect(champ.etablissement).to be_nil
|
||||||
expect(champ.value).to be_empty
|
expect(champ.value).to be_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'displays a “API is unavailable” error message' do
|
||||||
|
expect(response.body).to include(I18n.t('errors.messages.siret_network_error'))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when siret not found' do
|
context 'when the SIRET is valid but unknown' do
|
||||||
let(:siret) { '0' * 14 }
|
let(:siret) { '00000000000000' }
|
||||||
before {
|
|
||||||
expect(subject).to receive(:find_etablisement_with_siret).and_return(false)
|
|
||||||
get :show, params: params, format: 'js'
|
|
||||||
}
|
|
||||||
|
|
||||||
it 'not found error' do
|
before do
|
||||||
|
allow(controller).to receive(:find_etablissement_with_siret).and_return(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject! { get :show, params: params, format: 'js' }
|
||||||
|
|
||||||
|
it 'clears the etablissement and SIRET on the model' do
|
||||||
|
champ.reload
|
||||||
|
expect(champ.etablissement).to be_nil
|
||||||
|
expect(champ.value).to be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'displays a “SIRET not found” error message' do
|
||||||
expect(response.body).to include('Nous n’avons pas trouvé d’établissement correspondant à ce numéro de SIRET.')
|
expect(response.body).to include('Nous n’avons pas trouvé d’établissement correspondant à ce numéro de SIRET.')
|
||||||
champ.reload
|
|
||||||
expect(champ.etablissement).to be_nil
|
|
||||||
expect(champ.value).to be_empty
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when siret found' do
|
context 'when the SIRET informations are retrieved successfully' do
|
||||||
let(:siret) { etablissement.siret }
|
let(:siret) { etablissement.siret }
|
||||||
let(:etablissement) { build(:etablissement) }
|
let(:etablissement) { build(:etablissement) }
|
||||||
before {
|
|
||||||
expect(subject).to receive(:find_etablisement_with_siret).and_return(etablissement)
|
|
||||||
get :show, params: params, format: 'js'
|
|
||||||
}
|
|
||||||
|
|
||||||
it 'etablissement info message' do
|
before do
|
||||||
expect(response.body).to include(etablissement.entreprise_raison_sociale)
|
allow(controller).to receive(:find_etablissement_with_siret).and_return(etablissement)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject! { get :show, params: params, format: 'js' }
|
||||||
|
|
||||||
|
it 'populates the etablissement and SIRET on the model' do
|
||||||
champ.reload
|
champ.reload
|
||||||
expect(champ.value).to eq(etablissement.siret)
|
expect(champ.value).to eq(etablissement.siret)
|
||||||
expect(champ.etablissement.siret).to eq(etablissement.siret)
|
expect(champ.etablissement.siret).to eq(etablissement.siret)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'displays the name of the company' do
|
||||||
|
expect(response.body).to include(etablissement.entreprise_raison_sociale)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when user is not connected' do
|
context 'when user is not signed in' do
|
||||||
before {
|
subject! { get :show, params: { position: '1' }, format: 'js' }
|
||||||
get :show, params: { position: '1' }, format: 'js'
|
|
||||||
}
|
|
||||||
|
|
||||||
it { expect(response.code).to eq('401') }
|
it { expect(response.code).to eq('401') }
|
||||||
end
|
end
|
||||||
|
|
|
@ -278,6 +278,13 @@ describe Users::DossiersController, type: :controller do
|
||||||
context 'with a valid SIRET' do
|
context 'with a valid SIRET' do
|
||||||
let(:params_siret) { '440 117 620 01530' }
|
let(:params_siret) { '440 117 620 01530' }
|
||||||
|
|
||||||
|
context 'When API-Entreprise is down' do
|
||||||
|
let(:api_etablissement_status) { 502 }
|
||||||
|
let(:api_body_status) { File.read('spec/fixtures/files/api_entreprise/exercices_unavailable.json') }
|
||||||
|
|
||||||
|
it_behaves_like 'the request fails with an error', I18n.t('errors.messages.siret_network_error')
|
||||||
|
end
|
||||||
|
|
||||||
context 'when API-Entreprise doesn’t know this SIRET' do
|
context 'when API-Entreprise doesn’t know this SIRET' do
|
||||||
let(:api_etablissement_status) { 404 }
|
let(:api_etablissement_status) { 404 }
|
||||||
let(:api_body_status) { '' }
|
let(:api_body_status) { '' }
|
||||||
|
|
|
@ -167,8 +167,8 @@ feature 'The user' do
|
||||||
expect(page).to have_text('analyse antivirus en cours')
|
expect(page).to have_text('analyse antivirus en cours')
|
||||||
|
|
||||||
# Mark file as scanned and clean
|
# Mark file as scanned and clean
|
||||||
virus_scan = VirusScan.last
|
attachment = ActiveStorage::Attachment.last
|
||||||
virus_scan.update(scanned_at: Time.zone.now, status: VirusScan.statuses.fetch(:safe))
|
attachment.blob.update(metadata: attachment.blob.metadata.merge(scanned_at: Time.zone.now, virus_scan_result: ActiveStorage::VirusScanner::SAFE))
|
||||||
within '.piece-justificative' do
|
within '.piece-justificative' do
|
||||||
click_on 'rafraichir'
|
click_on 'rafraichir'
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,28 +17,24 @@ describe 'Dossier details:' do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "the user can see the mean time they are expected to wait" do
|
describe "the user can see the mean time they are expected to wait" do
|
||||||
context "when the dossier is in construction" do
|
let(:other_dossier) { create(:dossier, :accepte, :for_individual, procedure: procedure, en_construction_at: 10.days.ago, en_instruction_at: 9.days.ago, processed_at: Time.zone.now) }
|
||||||
before do
|
|
||||||
other_dossier = create(:dossier, :accepte, :for_individual, procedure: procedure, en_construction_at: 10.days.ago, en_instruction_at: Time.zone.now)
|
|
||||||
visit dossier_path(dossier)
|
|
||||||
end
|
|
||||||
|
|
||||||
it { expect(page).to have_text("Habituellement, les dossiers de cette démarche sont vérifiés dans un délai de 10 jours.") }
|
context "when the dossier is in construction" do
|
||||||
|
it "displays the estimated wait duration" do
|
||||||
|
other_dossier
|
||||||
|
visit dossier_path(dossier)
|
||||||
|
expect(page).to have_text("Habituellement, les dossiers de cette démarche sont traités dans un délai de 10 jours.")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when the dossier is in instruction" do
|
context "when the dossier is in instruction" do
|
||||||
let(:dossier) { create(:dossier, :en_instruction, :for_individual, :with_commentaires, user: user, procedure: procedure) }
|
let(:dossier) { create(:dossier, :en_instruction, :for_individual, :with_commentaires, user: user, procedure: procedure) }
|
||||||
|
|
||||||
before do
|
it "displays the estimated wait duration" do
|
||||||
Timecop.freeze(Time.zone.local(2012, 12, 20))
|
other_dossier
|
||||||
|
|
||||||
other_dossier = create(:dossier, :accepte, :for_individual, procedure: procedure, en_instruction_at: 60.days.ago, processed_at: Time.zone.now)
|
|
||||||
visit dossier_path(dossier)
|
visit dossier_path(dossier)
|
||||||
|
expect(page).to have_text("Habituellement, les dossiers de cette démarche sont traités dans un délai de 10 jours.")
|
||||||
end
|
end
|
||||||
|
|
||||||
after { Timecop.return }
|
|
||||||
|
|
||||||
it { expect(page).to have_text("Habituellement, les dossiers de cette démarche sont traités dans un délai de 2 mois.") }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
7
spec/fixtures/files/api_entreprise/entreprises_unavailable.json
vendored
Normal file
7
spec/fixtures/files/api_entreprise/entreprises_unavailable.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"errors": [
|
||||||
|
"Erreur interne du serveur",
|
||||||
|
"Le siret ou siren indiqué n'existe pas, n'est pas connu ou ne comporte aucune information pour cet appel"
|
||||||
|
],
|
||||||
|
"gateway_error": true
|
||||||
|
}
|
|
@ -1,12 +1,11 @@
|
||||||
RSpec.describe AntiVirusJob, type: :job do
|
RSpec.describe VirusScannerJob, type: :job do
|
||||||
let(:champ) do
|
let(:champ) do
|
||||||
champ = create(:champ, :piece_justificative)
|
champ = create(:champ, :piece_justificative)
|
||||||
champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain")
|
champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain")
|
||||||
champ
|
champ
|
||||||
end
|
end
|
||||||
let(:virus_scan) { create(:virus_scan, status: VirusScan.statuses.fetch(:pending), champ: champ, blob_key: champ.piece_justificative_file.blob.key) }
|
|
||||||
|
|
||||||
subject { AntiVirusJob.new.perform(virus_scan) }
|
subject { VirusScannerJob.new.perform(champ.piece_justificative_file.blob) }
|
||||||
|
|
||||||
context "when no virus is found" do
|
context "when no virus is found" do
|
||||||
let(:virus_found?) { true }
|
let(:virus_found?) { true }
|
||||||
|
@ -16,7 +15,7 @@ RSpec.describe AntiVirusJob, type: :job do
|
||||||
subject
|
subject
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect(virus_scan.reload.status).to eq(VirusScan.statuses.fetch(:safe)) }
|
it { expect(champ.piece_justificative_file.virus_scanner.safe?).to be_truthy }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when a virus is found" do
|
context "when a virus is found" do
|
||||||
|
@ -27,6 +26,6 @@ RSpec.describe AntiVirusJob, type: :job do
|
||||||
subject
|
subject
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect(virus_scan.reload.status).to eq(VirusScan.statuses.fetch(:infected)) }
|
it { expect(champ.piece_justificative_file.virus_scanner.infected?).to be_truthy }
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -2,21 +2,35 @@ require 'spec_helper'
|
||||||
|
|
||||||
describe ApiEntreprise::API do
|
describe ApiEntreprise::API do
|
||||||
let(:procedure_id) { 12 }
|
let(:procedure_id) { 12 }
|
||||||
|
|
||||||
describe '.entreprise' do
|
describe '.entreprise' do
|
||||||
subject { described_class.entreprise(siren, procedure_id) }
|
subject { described_class.entreprise(siren, procedure_id) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/entreprises\/#{siren}?.*token=/)
|
stub_request(:get, /https:\/\/entreprise.api.gouv.fr\/v2\/entreprises\/#{siren}?.*token=/)
|
||||||
.to_return(status: status, body: body)
|
.to_return(status: status, body: body)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when the service is unavailable' do
|
||||||
|
let(:siren) { '111111111' }
|
||||||
|
let(:status) { 502 }
|
||||||
|
let(:body) { File.read('spec/fixtures/files/api_entreprise/entreprises_unavailable.json') }
|
||||||
|
|
||||||
|
it 'raises RestClient::RequestFailed' do
|
||||||
|
expect { subject }.to raise_error(RestClient::RequestFailed)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when siren does not exist' do
|
context 'when siren does not exist' do
|
||||||
let(:siren) { '111111111' }
|
let(:siren) { '111111111' }
|
||||||
let(:status) { 404 }
|
let(:status) { 404 }
|
||||||
let(:body) { '' }
|
let(:body) { File.read('spec/fixtures/files/api_entreprise/entreprises_not_found.json') }
|
||||||
|
|
||||||
it 'raises RestClient::ResourceNotFound' do
|
it 'raises RestClient::ResourceNotFound' do
|
||||||
expect { subject }.to raise_error(RestClient::ResourceNotFound)
|
expect { subject }.to raise_error(RestClient::ResourceNotFound)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when siret exist' do
|
context 'when siret exist' do
|
||||||
let(:siren) { '418166096' }
|
let(:siren) { '418166096' }
|
||||||
let(:status) { 200 }
|
let(:status) { 200 }
|
||||||
|
|
|
@ -11,7 +11,7 @@ describe ApiEntreprise::EntrepriseAdapter do
|
||||||
.to_return(body: body, status: status)
|
.to_return(body: body, status: status)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when SIRET is OK" do
|
context "when the SIRET is valid" do
|
||||||
let(:body) { File.read('spec/fixtures/files/api_entreprise/entreprises.json') }
|
let(:body) { File.read('spec/fixtures/files/api_entreprise/entreprises.json') }
|
||||||
let(:status) { 200 }
|
let(:status) { 200 }
|
||||||
|
|
||||||
|
@ -70,12 +70,21 @@ describe ApiEntreprise::EntrepriseAdapter do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when SIRET is KO" do
|
context "when the SIRET is unknown" do
|
||||||
let(:body) { File.read('spec/fixtures/files/api_entreprise/entreprises_not_found.json') }
|
let(:body) { File.read('spec/fixtures/files/api_entreprise/entreprises_not_found.json') }
|
||||||
let(:status) { 206 }
|
let(:status) { 404 }
|
||||||
|
|
||||||
it '#to_params class est une Hash ?' do
|
it '#to_params class est une Hash ?' do
|
||||||
expect(subject).to eq({})
|
expect(subject).to eq({})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when the service is unavailable" do
|
||||||
|
let(:body) { File.read('spec/fixtures/files/api_entreprise/entreprises_unavailable.json') }
|
||||||
|
let(:status) { 502 }
|
||||||
|
|
||||||
|
it 'raises an exception' do
|
||||||
|
expect { subject }.to raise_error(RestClient::RequestFailed)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,25 +10,15 @@ describe ApiEntreprise::ExercicesAdapter do
|
||||||
.to_return(body: File.read('spec/fixtures/files/api_entreprise/exercices.json', status: 200))
|
.to_return(body: File.read('spec/fixtures/files/api_entreprise/exercices.json', status: 200))
|
||||||
end
|
end
|
||||||
|
|
||||||
it '#to_params class est un Hash ?' do
|
it { is_expected.to be_an_instance_of(Hash) }
|
||||||
expect(subject).to be_an_instance_of(Hash)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'have 3 exercices' do
|
it 'contains several exercices attributes' do
|
||||||
expect(subject[:exercices_attributes].size).to eq(3)
|
expect(subject[:exercices_attributes].size).to eq(3)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'Attributs Exercices' do
|
it 'contains informations in each exercices_attributes' do
|
||||||
it 'L\'exercice contient bien un ca' do
|
expect(subject[:exercices_attributes][0][:ca]).to eq('21009417')
|
||||||
expect(subject[:exercices_attributes][0][:ca]).to eq('21009417')
|
expect(subject[:exercices_attributes][0][:date_fin_exercice]).to eq("2013-12-31T00:00:00+01:00")
|
||||||
end
|
expect(subject[:exercices_attributes][0][:date_fin_exercice_timestamp]).to eq(1388444400)
|
||||||
|
|
||||||
it 'L\'exercice contient bien une date de fin d\'exercice' do
|
|
||||||
expect(subject[:exercices_attributes][0][:date_fin_exercice]).to eq("2013-12-31T00:00:00+01:00")
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'L\'exercice contient bien une date_fin_exercice_timestamp' do
|
|
||||||
expect(subject[:exercices_attributes][0][:date_fin_exercice_timestamp]).to eq(1388444400)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,7 +17,7 @@ describe ApiEntreprise::RNAAdapter do
|
||||||
context 'when siret is not valid' do
|
context 'when siret is not valid' do
|
||||||
let(:siret) { '234567' }
|
let(:siret) { '234567' }
|
||||||
let(:body) { '' }
|
let(:body) { '' }
|
||||||
let(:status) { '404' }
|
let(:status) { 404 }
|
||||||
|
|
||||||
it { is_expected.to eq({}) }
|
it { is_expected.to eq({}) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -383,21 +383,20 @@ describe Champ do
|
||||||
let(:type_de_champ) { create(:type_de_champ_piece_justificative) }
|
let(:type_de_champ) { create(:type_de_champ_piece_justificative) }
|
||||||
|
|
||||||
context 'and there is a blob' do
|
context 'and there is a blob' do
|
||||||
before { champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain") }
|
before do
|
||||||
|
champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain")
|
||||||
|
champ.save
|
||||||
|
end
|
||||||
|
|
||||||
it { expect { champ.save }.to change(VirusScan, :count).by(1) }
|
it { expect(champ.piece_justificative_file.virus_scanner.analyzed?).to be_truthy }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'and there is no blob' do
|
context 'and there is no blob' do
|
||||||
it { expect { champ.save }.to_not change(VirusScan, :count) }
|
before { champ.save }
|
||||||
|
|
||||||
|
it { expect(champ.piece_justificative_file.virus_scanner).to be_nil }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when type_champ is not type_de_champ_piece_justificative' do
|
|
||||||
let(:type_de_champ) { create(:type_de_champ_textarea) }
|
|
||||||
|
|
||||||
it { expect { champ.save }.to_not change(VirusScan, :count) }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "repetition" do
|
describe "repetition" do
|
||||||
|
|
|
@ -1,23 +1,24 @@
|
||||||
describe Champs::PieceJustificativeChamp do
|
describe Champs::PieceJustificativeChamp do
|
||||||
describe '#for_api' do
|
describe '#for_api' do
|
||||||
let(:champ_pj) { create(:champ_piece_justificative) }
|
let(:champ_pj) { create(:champ_piece_justificative) }
|
||||||
|
let(:metadata) { champ_pj.piece_justificative_file.blob.metadata }
|
||||||
|
|
||||||
before { champ_pj.virus_scan.update(status: status) }
|
before { champ_pj.piece_justificative_file.blob.update(metadata: metadata.merge(virus_scan_result: status)) }
|
||||||
|
|
||||||
subject { champ_pj.for_api }
|
subject { champ_pj.for_api }
|
||||||
|
|
||||||
context 'when file is safe' do
|
context 'when file is safe' do
|
||||||
let(:status) { 'safe' }
|
let(:status) { ActiveStorage::VirusScanner::SAFE }
|
||||||
it { is_expected.to include("/rails/active_storage/blobs/") }
|
it { is_expected.to include("/rails/active_storage/blobs/") }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when file is not scanned' do
|
context 'when file is not scanned' do
|
||||||
let(:status) { 'pending' }
|
let(:status) { ActiveStorage::VirusScanner::PENDING }
|
||||||
it { is_expected.to include("/rails/active_storage/blobs/") }
|
it { is_expected.to include("/rails/active_storage/blobs/") }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when file is infected' do
|
context 'when file is infected' do
|
||||||
let(:status) { 'infected' }
|
let(:status) { ActiveStorage::VirusScanner::INFECTED }
|
||||||
it { is_expected.to be_nil }
|
it { is_expected.to be_nil }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -727,59 +727,17 @@ describe Procedure do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#usual_verification_time' do
|
describe '#usual_traitement_time' do
|
||||||
let(:procedure) { create(:procedure) }
|
let(:procedure) { create(:procedure) }
|
||||||
|
|
||||||
def create_dossier(construction_date:, instruction_date:)
|
def create_dossier(construction_date:, instruction_date:, processed_date:)
|
||||||
dossier = create(:dossier, :en_instruction, procedure: procedure)
|
|
||||||
dossier.update!(en_construction_at: construction_date, en_instruction_at: instruction_date)
|
|
||||||
end
|
|
||||||
|
|
||||||
before do
|
|
||||||
delays.each do |delay|
|
|
||||||
create_dossier(construction_date: 1.week.ago - delay, instruction_date: 1.week.ago)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when there are several dossiers in the time frame' do
|
|
||||||
let(:delays) { [1.day, 2.days, 2.days, 2.days, 2.days, 3.days, 3.days, 3.days, 3.days, 12.days] }
|
|
||||||
|
|
||||||
it 'returns a time representative of the dossier verification delay' do
|
|
||||||
expect(procedure.usual_verification_time).to be_between(3.days, 4.days)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when there are very old dossiers' do
|
|
||||||
let(:delays) { [2.days, 2.days] }
|
|
||||||
let!(:old_dossier) { create_dossier(construction_date: 3.months.ago, instruction_date: 2.months.ago) }
|
|
||||||
|
|
||||||
it 'ignores dossiers older than 1 month' do
|
|
||||||
expect(procedure.usual_verification_time).to be_within(1.hour).of(2.days)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when there is only one dossier in the time frame' do
|
|
||||||
let(:delays) { [1.day] }
|
|
||||||
it { expect(procedure.usual_verification_time).to be_within(1.hour).of(1.day) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'where there are no dossiers' do
|
|
||||||
let(:delays) { [] }
|
|
||||||
it { expect(procedure.usual_verification_time).to be_nil }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#usual_instruction_time' do
|
|
||||||
let(:procedure) { create(:procedure) }
|
|
||||||
|
|
||||||
def create_dossier(instruction_date:, processed_date:)
|
|
||||||
dossier = create(:dossier, :accepte, procedure: procedure)
|
dossier = create(:dossier, :accepte, procedure: procedure)
|
||||||
dossier.update!(en_instruction_at: instruction_date, processed_at: processed_date)
|
dossier.update!(en_construction_at: construction_date, en_instruction_at: instruction_date, processed_at: processed_date)
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
delays.each do |delay|
|
delays.each do |delay|
|
||||||
create_dossier(instruction_date: 1.week.ago - delay, processed_date: 1.week.ago)
|
create_dossier(construction_date: 1.week.ago - delay, instruction_date: 1.week.ago - delay + 12.hours, processed_date: 1.week.ago)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -787,36 +745,36 @@ describe Procedure do
|
||||||
let(:delays) { [1.day, 2.days, 2.days, 2.days, 2.days, 3.days, 3.days, 3.days, 3.days, 12.days] }
|
let(:delays) { [1.day, 2.days, 2.days, 2.days, 2.days, 3.days, 3.days, 3.days, 3.days, 12.days] }
|
||||||
|
|
||||||
it 'returns a time representative of the dossier instruction delay' do
|
it 'returns a time representative of the dossier instruction delay' do
|
||||||
expect(procedure.usual_instruction_time).to be_between(3.days, 4.days)
|
expect(procedure.usual_traitement_time).to be_between(3.days, 4.days)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there are very old dossiers' do
|
context 'when there are very old dossiers' do
|
||||||
let(:delays) { [2.days, 2.days] }
|
let(:delays) { [2.days, 2.days] }
|
||||||
let!(:old_dossier) { create_dossier(instruction_date: 3.months.ago, processed_date: 2.months.ago) }
|
let!(:old_dossier) { create_dossier(construction_date: 3.months.ago, instruction_date: 2.months.ago, processed_date: 2.months.ago) }
|
||||||
|
|
||||||
it 'ignores dossiers older than 1 month' do
|
it 'ignores dossiers older than 1 month' do
|
||||||
expect(procedure.usual_instruction_time).to be_within(1.hour).of(2.days)
|
expect(procedure.usual_traitement_time).to be_within(1.hour).of(2.days)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there is a dossier with bad data' do
|
context 'when there is a dossier with bad data' do
|
||||||
let(:delays) { [2.days, 2.days] }
|
let(:delays) { [2.days, 2.days] }
|
||||||
let!(:bad_dossier) { create_dossier(instruction_date: nil, processed_date: 10.days.ago) }
|
let!(:bad_dossier) { create_dossier(construction_date: nil, instruction_date: nil, processed_date: 10.days.ago) }
|
||||||
|
|
||||||
it 'ignores bad dossiers' do
|
it 'ignores bad dossiers' do
|
||||||
expect(procedure.usual_instruction_time).to be_within(1.hour).of(2.days)
|
expect(procedure.usual_traitement_time).to be_within(1.hour).of(2.days)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when there is only one processed dossier' do
|
context 'when there is only one processed dossier' do
|
||||||
let(:delays) { [1.day] }
|
let(:delays) { [1.day] }
|
||||||
it { expect(procedure.usual_instruction_time).to be_within(1.hour).of(1.day) }
|
it { expect(procedure.usual_traitement_time).to be_within(1.hour).of(1.day) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'where there is no processed dossier' do
|
context 'where there is no processed dossier' do
|
||||||
let(:delays) { [] }
|
let(:delays) { [] }
|
||||||
it { expect(procedure.usual_instruction_time).to be_nil }
|
it { expect(procedure.usual_traitement_time).to be_nil }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ describe ChampSerializer do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
champ.piece_justificative_file.attach({ filename: __FILE__, io: File.open(__FILE__) })
|
champ.piece_justificative_file.attach({ filename: __FILE__, io: File.open(__FILE__) })
|
||||||
champ.reload.virus_scan.safe!
|
champ.piece_justificative_file.virus_scanner.analyze_later
|
||||||
end
|
end
|
||||||
after { champ.piece_justificative_file.purge }
|
after { champ.piece_justificative_file.purge }
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
describe 'shared/champs/piece_justificative/_pj_link.html.haml', type: :view do
|
describe 'shared/piece_jointe/_pj_link.html.haml', type: :view do
|
||||||
let(:champ) { create(:champ, :piece_justificative, :with_piece_justificative_file) }
|
let(:champ) { create(:champ_piece_justificative) }
|
||||||
let(:virus_scan) { nil }
|
let(:virus_scan_result) { nil }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
if virus_scan
|
champ.piece_justificative_file.blob.update(metadata: champ.piece_justificative_file.blob.metadata.merge(virus_scan_result: virus_scan_result))
|
||||||
champ.update(virus_scan: virus_scan)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
subject { render 'shared/champs/piece_justificative/pj_link', champ: champ, user_can_upload: false }
|
subject { render 'shared/piece_jointe/pj_link', object: champ, pj: champ.piece_justificative_file, user_can_upload: false }
|
||||||
|
|
||||||
context 'when there is no anti-virus scan' do
|
context 'when there is no anti-virus scan' do
|
||||||
let(:virus_scan) { nil }
|
let(:virus_scan_result) { nil }
|
||||||
|
|
||||||
it 'allows to download the file' do
|
it 'allows to download the file' do
|
||||||
expect(subject).to have_link(champ.piece_justificative_file.filename.to_s)
|
expect(subject).to have_link(champ.piece_justificative_file.filename.to_s)
|
||||||
|
@ -22,7 +20,7 @@ describe 'shared/champs/piece_justificative/_pj_link.html.haml', type: :view do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the anti-virus scan is pending' do
|
context 'when the anti-virus scan is pending' do
|
||||||
let(:virus_scan) { create(:virus_scan, :pending) }
|
let(:virus_scan_result) { ActiveStorage::VirusScanner::PENDING }
|
||||||
|
|
||||||
it 'displays the filename, but doesn’t allow to download the file' do
|
it 'displays the filename, but doesn’t allow to download the file' do
|
||||||
expect(subject).to have_text(champ.piece_justificative_file.filename.to_s)
|
expect(subject).to have_text(champ.piece_justificative_file.filename.to_s)
|
||||||
|
@ -32,7 +30,7 @@ describe 'shared/champs/piece_justificative/_pj_link.html.haml', type: :view do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the file is scanned and safe' do
|
context 'when the file is scanned and safe' do
|
||||||
let(:virus_scan) { create(:virus_scan, :safe) }
|
let(:virus_scan_result) { ActiveStorage::VirusScanner::SAFE }
|
||||||
|
|
||||||
it 'allows to download the file' do
|
it 'allows to download the file' do
|
||||||
expect(subject).to have_link(champ.piece_justificative_file.filename.to_s)
|
expect(subject).to have_link(champ.piece_justificative_file.filename.to_s)
|
||||||
|
@ -40,7 +38,7 @@ describe 'shared/champs/piece_justificative/_pj_link.html.haml', type: :view do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the file is scanned and infected' do
|
context 'when the file is scanned and infected' do
|
||||||
let(:virus_scan) { create(:virus_scan, :infected) }
|
let(:virus_scan_result) { ActiveStorage::VirusScanner::INFECTED }
|
||||||
|
|
||||||
it 'displays the filename, but doesn’t allow to download the file' do
|
it 'displays the filename, but doesn’t allow to download the file' do
|
||||||
expect(subject).to have_text(champ.piece_justificative_file.filename.to_s)
|
expect(subject).to have_text(champ.piece_justificative_file.filename.to_s)
|
|
@ -1,4 +1,6 @@
|
||||||
describe 'users/dossiers/show/_status_overview.html.haml', type: :view do
|
describe 'users/dossiers/show/_status_overview.html.haml', type: :view do
|
||||||
|
before { allow(dossier.procedure).to receive(:usual_traitement_time).and_return(1.day) }
|
||||||
|
|
||||||
subject! { render 'users/dossiers/show/status_overview.html.haml', dossier: dossier }
|
subject! { render 'users/dossiers/show/status_overview.html.haml', dossier: dossier }
|
||||||
|
|
||||||
matcher :have_timeline_item do |selector|
|
matcher :have_timeline_item do |selector|
|
||||||
|
@ -46,6 +48,7 @@ describe 'users/dossiers/show/_status_overview.html.haml', type: :view do
|
||||||
end
|
end
|
||||||
|
|
||||||
it { is_expected.to have_selector('.status-explanation .en-construction') }
|
it { is_expected.to have_selector('.status-explanation .en-construction') }
|
||||||
|
it { is_expected.to have_text('Habituellement, les dossiers de cette démarche sont traités dans un délai de 1 jour') }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when en instruction' do
|
context 'when en instruction' do
|
||||||
|
@ -59,6 +62,7 @@ describe 'users/dossiers/show/_status_overview.html.haml', type: :view do
|
||||||
end
|
end
|
||||||
|
|
||||||
it { is_expected.to have_selector('.status-explanation .en-instruction') }
|
it { is_expected.to have_selector('.status-explanation .en-instruction') }
|
||||||
|
it { is_expected.to have_text('Habituellement, les dossiers de cette démarche sont traités dans un délai de 1 jour') }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when accepté' do
|
context 'when accepté' do
|
||||||
|
|
Loading…
Reference in a new issue