Merge pull request #4928 from betagouv/dev

2020-03-23-01
This commit is contained in:
Keirua 2020-03-23 15:26:54 +01:00 committed by GitHub
commit 14095864f4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 766 additions and 307 deletions

View file

@ -54,7 +54,7 @@ gem 'puma' # Use Puma as the app server
gem 'pundit'
gem 'rack-attack'
gem 'rack-mini-profiler'
gem 'rails'
gem 'rails', '= 5.2.4.2'
gem 'rails-i18n' # Locales par défaut
gem 'rake-progressbar', require: false
gem 'react-rails'

View file

@ -20,25 +20,25 @@ GEM
specs:
aasm (5.0.1)
concurrent-ruby (~> 1.0)
actioncable (5.2.4.1)
actionpack (= 5.2.4.1)
actioncable (5.2.4.2)
actionpack (= 5.2.4.2)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailer (5.2.4.1)
actionpack (= 5.2.4.1)
actionview (= 5.2.4.1)
activejob (= 5.2.4.1)
actionmailer (5.2.4.2)
actionpack (= 5.2.4.2)
actionview (= 5.2.4.2)
activejob (= 5.2.4.2)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (5.2.4.1)
actionview (= 5.2.4.1)
activesupport (= 5.2.4.1)
actionpack (5.2.4.2)
actionview (= 5.2.4.2)
activesupport (= 5.2.4.2)
rack (~> 2.0, >= 2.0.8)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.2.4.1)
activesupport (= 5.2.4.1)
actionview (5.2.4.2)
activesupport (= 5.2.4.2)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
@ -53,25 +53,25 @@ GEM
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
active_storage_validations (0.8.7)
rails (>= 5.2.0)
activejob (5.2.4.1)
activesupport (= 5.2.4.1)
activejob (5.2.4.2)
activesupport (= 5.2.4.2)
globalid (>= 0.3.6)
activemodel (5.2.4.1)
activesupport (= 5.2.4.1)
activerecord (5.2.4.1)
activemodel (= 5.2.4.1)
activesupport (= 5.2.4.1)
activemodel (5.2.4.2)
activesupport (= 5.2.4.2)
activerecord (5.2.4.2)
activemodel (= 5.2.4.2)
activesupport (= 5.2.4.2)
arel (>= 9.0)
activestorage (5.2.4.1)
actionpack (= 5.2.4.1)
activerecord (= 5.2.4.1)
activestorage (5.2.4.2)
actionpack (= 5.2.4.2)
activerecord (= 5.2.4.2)
marcel (~> 0.3.1)
activestorage-openstack (1.4.1)
fog-openstack (~> 1.0)
marcel
mime-types
rails (>= 5.2.2)
activesupport (5.2.4.1)
activesupport (5.2.4.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
@ -469,18 +469,18 @@ GEM
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (5.2.4.1)
actioncable (= 5.2.4.1)
actionmailer (= 5.2.4.1)
actionpack (= 5.2.4.1)
actionview (= 5.2.4.1)
activejob (= 5.2.4.1)
activemodel (= 5.2.4.1)
activerecord (= 5.2.4.1)
activestorage (= 5.2.4.1)
activesupport (= 5.2.4.1)
rails (5.2.4.2)
actioncable (= 5.2.4.2)
actionmailer (= 5.2.4.2)
actionpack (= 5.2.4.2)
actionview (= 5.2.4.2)
activejob (= 5.2.4.2)
activemodel (= 5.2.4.2)
activerecord (= 5.2.4.2)
activestorage (= 5.2.4.2)
activesupport (= 5.2.4.2)
bundler (>= 1.3.0)
railties (= 5.2.4.1)
railties (= 5.2.4.2)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.4)
actionpack (>= 5.0.1.x)
@ -494,9 +494,9 @@ GEM
rails-i18n (5.1.2)
i18n (>= 0.7, < 2)
railties (>= 5.0, < 6)
railties (5.2.4.1)
actionpack (= 5.2.4.1)
activesupport (= 5.2.4.1)
railties (5.2.4.2)
actionpack (= 5.2.4.2)
activesupport (= 5.2.4.2)
method_source
rake (>= 0.8.7)
thor (>= 0.19.0, < 2.0)
@ -782,7 +782,7 @@ DEPENDENCIES
pundit
rack-attack
rack-mini-profiler
rails
rails (= 5.2.4.2)
rails-controller-testing
rails-i18n
rake-progressbar

View file

@ -1,6 +1,7 @@
class ApplicationController < ActionController::Base
include TrustedDeviceConcern
include Pundit
include Devise::StoreLocationExtension
MAINTENANCE_MESSAGE = 'Le site est actuellement en maintenance. Il sera à nouveau disponible dans un court instant.'
@ -198,7 +199,9 @@ class ApplicationController < ActionController::Base
# return at this location
# after the device is trusted
if get_stored_location_for(:user).blank?
store_location_for(:user, request.fullpath)
end
send_login_token_or_bufferize(current_instructeur)
redirect_to link_sent_path(email: current_instructeur.email)

View file

@ -1,6 +1,4 @@
class InvitesController < ApplicationController
include Devise::StoreLocationExtension
before_action :authenticate_user!, only: [:create]
before_action :store_user_location!, only: [:show]

View file

@ -1,6 +1,5 @@
module Users
class DossiersController < UserController
include Devise::StoreLocationExtension
include DossierHelper
layout 'procedure_context', only: [:identite, :update_identite, :siret, :update_siret]

View file

@ -3,7 +3,7 @@ import $ from 'jquery';
import debounce from 'debounce';
export { debounce };
export const { fire, ajax } = Rails;
export const { fire } = Rails;
export function show(el) {
el && el.classList.remove('hidden');
@ -45,6 +45,22 @@ export function delegate(eventNames, selector, callback) {
);
}
export function ajax(options) {
return new Promise((resolve, reject) => {
Object.assign(options, {
success: (response, statusText, xhr) => {
resolve({ response, statusText, xhr });
},
error: (response, statusText, xhr) => {
let error = new Error(`Erreur ${xhr.status} : ${statusText}`);
Object.assign(error, { response, statusText, xhr });
reject(error);
}
});
Rails.ajax(options);
});
}
export function getJSON(url, data, method = 'get') {
incrementActiveRequestsCount();
data = method !== 'get' ? JSON.stringify(data) : data;

View file

@ -0,0 +1,7 @@
class ExpiredDossiersDeletionJob < ApplicationJob
queue_as :cron
def perform(*args)
ExpiredDossiersDeletionService.process_expired_dossiers_brouillon
end
end

View file

@ -1,8 +0,0 @@
class SeekAndDestroyExpiredDossiersJob < ApplicationJob
queue_as :cron
def perform(*args)
Dossier.send_brouillon_expiration_notices
Dossier.destroy_brouillons_and_notify
end
end

View file

@ -3,8 +3,7 @@ class WarnExpiringDossiersJob < ApplicationJob
def perform(*args)
expiring, expired = Dossier
.includes(:procedure)
.nearing_end_of_retention
.en_instruction_close_to_expiration
.partition(&:retention_expired?)
AdministrationMailer.dossier_expiration_summary(expiring, expired).deliver_later

View file

@ -29,20 +29,6 @@ class DossierMailer < ApplicationMailer
end
end
def notify_deletion_to_user(deleted_dossier, to_email)
@deleted_dossier = deleted_dossier
subject = "Votre dossier nº #{@deleted_dossier.dossier_id} a bien été supprimé"
mail(to: to_email, subject: subject)
end
def notify_deletion_to_administration(deleted_dossier, to_email)
@deleted_dossier = deleted_dossier
subject = "Le dossier nº #{@deleted_dossier.dossier_id} a été supprimé à la demande de l'usager"
mail(to: to_email, subject: subject)
end
def notify_revert_to_instruction(dossier)
@dossier = dossier
@service = dossier.procedure.service
@ -55,40 +41,60 @@ class DossierMailer < ApplicationMailer
end
end
def notify_brouillon_near_deletion(user, dossiers)
def notify_brouillon_near_deletion(dossiers, to_email)
@subject = default_i18n_subject(count: dossiers.count)
@dossiers = dossiers
mail(to: user.email, subject: @subject)
mail(to: to_email, subject: @subject)
end
def notify_brouillon_deletion(user, dossier_hashes)
def notify_brouillon_deletion(dossier_hashes, to_email)
@subject = default_i18n_subject(count: dossier_hashes.count)
@dossier_hashes = dossier_hashes
mail(to: user.email, subject: @subject)
mail(to: to_email, subject: @subject)
end
def notify_automatic_deletion_to_user(user, dossier_hashes)
@subject = default_i18n_subject(count: dossier_hashes.count)
@dossier_hashes = dossier_hashes
def notify_deletion_to_user(deleted_dossier, to_email)
@subject = default_i18n_subject(dossier_id: deleted_dossier.dossier_id)
@deleted_dossier = deleted_dossier
mail(to: user.email, subject: @subject)
mail(to: to_email, subject: @subject)
end
def notify_automatic_deletion_to_administration(user, dossier_hashes)
@subject = default_i18n_subject(count: dossier_hashes.count)
@dossier_hashes = dossier_hashes
def notify_deletion_to_administration(deleted_dossier, to_email)
@subject = default_i18n_subject(dossier_id: deleted_dossier.dossier_id)
@deleted_dossier = deleted_dossier
mail(to: user.email, subject: @subject)
mail(to: to_email, subject: @subject)
end
def notify_en_construction_near_deletion(user, dossiers, for_user)
def notify_automatic_deletion_to_user(deleted_dossiers, to_email)
@subject = default_i18n_subject(count: deleted_dossiers.count)
@deleted_dossiers = deleted_dossiers
mail(to: to_email, subject: @subject)
end
def notify_automatic_deletion_to_administration(deleted_dossiers, to_email)
@subject = default_i18n_subject(count: deleted_dossiers.count)
@deleted_dossiers = deleted_dossiers
mail(to: to_email, subject: @subject)
end
def notify_en_construction_near_deletion_to_user(dossiers, to_email)
@subject = default_i18n_subject(count: dossiers.count)
@dossiers = dossiers
@for_user = for_user
mail(to: user.email, subject: @subject)
mail(to: to_email, subject: @subject)
end
def notify_en_construction_near_deletion_to_administration(dossiers, to_email)
@subject = default_i18n_subject(count: dossiers.count)
@dossiers = dossiers
mail(to: to_email, subject: @subject)
end
def notify_groupe_instructeur_changed(instructeur, dossier)

View file

@ -10,9 +10,8 @@ class AttestationTemplate < ApplicationRecord
has_one_attached :signature
validates :footer, length: { maximum: 190 }
validates :logo, content_type: [:png, :jpg, :jpeg]
validates :signature, content_type: [:png, :jpg, :jpeg]
validates :logo, content_type: ['image/png', 'image/jpg', 'image/jpeg'], size: { less_than: 1.megabytes }
validates :signature, content_type: ['image/png', 'image/jpg', 'image/jpeg'], size: { less_than: 1.megabytes }
DOSSIER_STATE = Dossier.states.fetch(:accepte)

View file

@ -10,6 +10,8 @@ class Avis < ApplicationRecord
validates :email, format: { with: Devise.email_regexp, message: "n'est pas valide" }, allow_nil: true
validates :claimant, presence: true
validates :piece_justificative_file, size: { less_than: 20.megabytes }
validates :introduction_file, size: { less_than: 20.megabytes }
before_validation -> { sanitize_email(:email) }
before_create :try_to_assign_instructeur

View file

@ -11,6 +11,7 @@ class Commentaire < ApplicationRecord
has_one_attached :piece_jointe
validates :body, presence: { message: "ne peut être vide" }
validates :piece_jointe, size: { less_than: 20.megabytes }
default_scope { order(created_at: :asc) }
scope :updated_since?, -> (date) { where('commentaires.updated_at > ?', date) }

View file

@ -22,8 +22,6 @@ class Dossier < ApplicationRecord
TAILLE_MAX_ZIP = 50.megabytes
DRAFT_EXPIRATION = 1.month + 5.days
has_one :etablissement, dependent: :destroy
has_one :individual, validate: false, dependent: :destroy
has_one :attestation, dependent: :destroy
@ -140,7 +138,6 @@ class Dossier < ApplicationRecord
scope :en_cours, -> { not_archived.state_en_construction_ou_instruction }
scope :without_followers, -> { left_outer_joins(:follows).where(follows: { id: nil }) }
scope :with_champs, -> { includes(champs: :type_de_champ) }
scope :nearing_end_of_retention, -> (duration = '1 month') { joins(:procedure).where("en_instruction_at + (duree_conservation_dossiers_dans_ds * interval '1 month') - now() < interval ?", duration) }
scope :for_api, -> {
includes(commentaires: { piece_jointe_attachment: :blob },
champs: [
@ -170,10 +167,24 @@ class Dossier < ApplicationRecord
scope :brouillon_close_to_expiration, -> do
brouillon
.joins(:procedure)
.where("dossiers.created_at + (duree_conservation_dossiers_dans_ds * interval '1 month') - (1 * interval '1 month') <= now()")
.where("dossiers.created_at + (duree_conservation_dossiers_dans_ds * interval '1 month') - INTERVAL '1 month' <= now()")
end
scope :expired_brouillon, -> { brouillon.where("brouillon_close_to_expiration_notice_sent_at < ?", (Time.zone.now - (DRAFT_EXPIRATION))) }
scope :without_notice_sent, -> { where(brouillon_close_to_expiration_notice_sent_at: nil) }
scope :en_construction_close_to_expiration, -> do
en_construction
.joins(:procedure)
.where("dossiers.en_construction_at + (duree_conservation_dossiers_dans_ds * interval '1 month') - INTERVAL '1 month' <= now()")
end
scope :en_instruction_close_to_expiration, -> do
en_instruction
.joins(:procedure)
.where("dossiers.en_instruction_at + (duree_conservation_dossiers_dans_ds * interval '1 month') - INTERVAL '1 month' <= now()")
end
scope :brouillon_expired, -> { brouillon.where("brouillon_close_to_expiration_notice_sent_at < (now() - INTERVAL '1 month 5 days')") }
scope :en_construction_expired, -> { en_construction.where("en_construction_close_to_expiration_notice_sent_at < (now() - INTERVAL '1 month 5 days')") }
scope :without_brouillon_expiration_notice_sent, -> { where(brouillon_close_to_expiration_notice_sent_at: nil) }
scope :without_en_construction_expiration_notice_sent, -> { where(en_construction_close_to_expiration_notice_sent_at: nil) }
scope :for_procedure, -> (procedure) { includes(:user, :groupe_instructeur).where(groupe_instructeurs: { procedure: procedure }) }
scope :for_api_v2, -> { includes(procedure: [:administrateurs], etablissement: [], individual: []) }
@ -669,36 +680,4 @@ class Dossier < ApplicationRecord
end
end
end
def self.send_brouillon_expiration_notices
brouillons = Dossier
.brouillon_close_to_expiration
.without_notice_sent
brouillons
.includes(:user)
.group_by(&:user)
.each do |(user, dossiers)|
DossierMailer.notify_brouillon_near_deletion(user, dossiers).deliver_later
end
brouillons.update_all(brouillon_close_to_expiration_notice_sent_at: Time.zone.now)
end
def self.destroy_brouillons_and_notify
expired_brouillons = Dossier.expired_brouillon
expired_brouillons
.includes(:procedure, :user)
.group_by(&:user)
.each do |(user, dossiers)|
dossier_hashes = dossiers.map(&:hash_for_deletion_mail)
DossierMailer.notify_brouillon_deletion(user, dossier_hashes).deliver_later
dossiers.each do |dossier|
DeletedDossier.create_from_dossier(dossier)
dossier.destroy
end
end
end
end

View file

@ -163,13 +163,15 @@ class Instructeur < ApplicationRecord
acc << h
end
if h[:nb_en_instruction] > 0 || h[:nb_accepted] > 0
[["en_instruction", h[:nb_en_instruction]], ["accepte", h[:nb_accepted]]].each do |state, count|
if procedure.declarative_with_state == state && count > 0
if procedure&.declarative_with_state == state && count > 0
h[:procedure_id] = procedure.id
h[:procedure_libelle] = procedure.libelle
acc << h
end
end
end
acc
end

View file

@ -84,6 +84,26 @@ class Procedure < ApplicationRecord
validates :duree_conservation_dossiers_dans_ds, allow_nil: true, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: MAX_DUREE_CONSERVATION }, unless: :durees_conservation_required
validates :duree_conservation_dossiers_hors_ds, allow_nil: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, unless: :durees_conservation_required
validates_with MonAvisEmbedValidator
validates :notice, content_type: [
"application/msword",
"application/pdf",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/vnd.oasis.opendocument.text",
"application/vnd.oasis.opendocument.presentation",
"text/plain"
], size: { less_than: 20.megabytes }
validates :deliberation, content_type: [
"application/msword",
"application/pdf",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"text/plain",
"application/vnd.oasis.opendocument.text"
], size: { less_than: 20.megabytes }
validates :logo, content_type: ['image/png', 'image/jpg', 'image/jpeg'], size: { less_than: 5.megabytes }
before_save :update_juridique_required
before_save :update_durees_conservation_required
after_initialize :ensure_path_exists

View file

@ -0,0 +1,110 @@
class ExpiredDossiersDeletionService
def self.process_expired_dossiers_brouillon
send_brouillon_expiration_notices
delete_expired_brouillons_and_notify
end
def self.process_expired_dossiers_en_construction
send_en_construction_expiration_notices
delete_expired_en_construction_and_notify
end
def self.send_brouillon_expiration_notices
dossiers_close_to_expiration = Dossier
.brouillon_close_to_expiration
.without_brouillon_expiration_notice_sent
dossiers_close_to_expiration
.includes(:user, :procedure)
.group_by(&:user)
.each do |(user, dossiers)|
DossierMailer.notify_brouillon_near_deletion(
dossiers,
user.email
).deliver_later
end
dossiers_close_to_expiration.update_all(brouillon_close_to_expiration_notice_sent_at: Time.zone.now)
end
def self.send_en_construction_expiration_notices
dossiers_close_to_expiration = Dossier
.en_construction_close_to_expiration
.without_en_construction_expiration_notice_sent
dossiers_close_to_expiration
.includes(:user)
.group_by(&:user)
.each do |(user, dossiers)|
DossierMailer.notify_en_construction_near_deletion_to_user(
dossiers,
user.email
).deliver_later
end
group_by_fonctionnaire_email(dossiers_close_to_expiration).each do |(email, dossiers)|
DossierMailer.notify_en_construction_near_deletion_to_administration(
dossiers,
email
).deliver_later
end
dossiers_close_to_expiration.update_all(en_construction_close_to_expiration_notice_sent_at: Time.zone.now)
end
def self.delete_expired_brouillons_and_notify
dossiers_to_remove = Dossier.brouillon_expired
dossiers_to_remove
.includes(:user, :procedure)
.group_by(&:user)
.each do |(user, dossiers)|
DossierMailer.notify_brouillon_deletion(
dossiers.map(&:hash_for_deletion_mail),
user.email
).deliver_later
end
dossiers_to_remove.each do |dossier|
DeletedDossier.create_from_dossier(dossier)
dossier.destroy
end
end
def self.delete_expired_en_construction_and_notify
dossiers_to_remove = Dossier.en_construction_expired
dossiers_to_remove.each do |dossier|
DeletedDossier.create_from_dossier(dossier)
end
dossiers_to_remove
.includes(:user)
.group_by(&:user)
.each do |(user, dossiers)|
DossierMailer.notify_automatic_deletion_to_user(
DeletedDossier.where(dossier_id: dossiers.map(&:id)),
user.email
).deliver_later
end
self.group_by_fonctionnaire_email(dossiers_to_remove).each do |(email, dossiers)|
DossierMailer.notify_automatic_deletion_to_administration(
DeletedDossier.where(dossier_id: dossiers.map(&:id)),
email
).deliver_later
end
dossiers_to_remove.destroy_all
end
private
def self.group_by_fonctionnaire_email(dossiers)
dossiers
.includes(:followers_instructeurs, procedure: [:administrateurs])
.each_with_object(Hash.new { |h, k| h[k] = Set.new }) do |dossier, h|
(dossier.followers_instructeurs + dossier.procedure.administrateurs).each { |destinataire| h[destinataire.email] << dossier }
end
end
end

View file

@ -3,10 +3,10 @@
%p
Bonjour,
%p= t('.automatic_dossier_deletion', count: @dossier_hashes.count)
%ul
- @dossier_hashes.each do |d|
%li= "n° #{d[:id]} (#{d[:procedure_libelle]})"
%p
= t('.header', count: @deleted_dossiers.count)
%ul
- @deleted_dossiers.each do |d|
%li n° #{d.dossier_id} (#{d.procedure.libelle})
= render partial: "layouts/mailers/signature"

View file

@ -3,12 +3,12 @@
%p
Bonjour,
%p= t('.automatic_dossier_deletion', count: @dossier_hashes.count)
%p
= t('.header', count: @deleted_dossiers.count)
%ul
- @deleted_dossiers.each do |d|
%li n° #{d.dossier_id} (#{d.procedure.libelle})
%ul
- @dossier_hashes.each do |d|
%li= link_to("n° #{d[:id]} (#{d[:procedure_libelle]})", dossier_url(d))
%p= t('.dossier_will_not_be_processed', count: @dossier_hashes.count)
%p= t('.footer', count: @deleted_dossiers.count)
= render partial: "layouts/mailers/signature"

View file

@ -3,9 +3,9 @@
%p
Bonjour,
%p= t('.automatic_dossier_deletion', count: @dossier_hashes.count)
%ul
%p
= t('.header', count: @dossier_hashes.count)
%ul
- @dossier_hashes.each do |d|
%li n° #{d[:id]} (#{d[:procedure_libelle]})

View file

@ -4,13 +4,11 @@
Bonjour,
%p
Afin de limiter la conservation de vos données personnelles,
= t('.automatic_dossier_deletion', count: @dossiers.count)
= t('.header', count: @dossiers.count)
%ul
- @dossiers.each do |d|
%li= link_to("n° #{d.id} (#{d.procedure.libelle})", dossier_url(d))
%p
#{sanitize(t('.send_your_draft', count: @dossiers.count))}. Et sinon, vous n'avez rien à faire.
%p= sanitize(t('.footer', count: @dossiers.count))
= render partial: "layouts/mailers/signature"

View file

@ -1,11 +1,9 @@
- content_for(:title, "Suppression du dossier n° #{@deleted_dossier.dossier_id}")
- content_for(:title, "#{@subject}")
%p
Bonjour,
%p
À la demande de l'usager, le dossier n° #{@deleted_dossier.dossier_id}
(sur la démarche «&nbsp;#{@deleted_dossier.procedure.libelle}&nbsp;»)
a été supprimé.
= t('.body', dossier_id: @deleted_dossier.dossier_id, procedure: @deleted_dossier.procedure.libelle)
= render partial: "layouts/mailers/signature"

View file

@ -1,11 +1,9 @@
- content_for(:title, "Suppression du dossier n° #{@deleted_dossier.dossier_id}")
- content_for(:title, "#{@subject}")
%p
Bonjour,
%p
Votre dossier n° #{@deleted_dossier.dossier_id}
(«&nbsp;#{@deleted_dossier.procedure.libelle}&nbsp;») a bien été supprimé.
Une trace anonyme de ce traitement sera conservée pour ladministration.
= t('.body', dossier_id: @deleted_dossier.dossier_id, procedure: @deleted_dossier.procedure.libelle)
= render partial: "layouts/mailers/signature"

View file

@ -1,27 +0,0 @@
- content_for(:title, "#{@subject}")
%p
Bonjour,
%p
- if !@for_user
Afin de limiter la conservation de vos données personnelles,
= t('.automatic_dossier_deletion', count: @dossiers.count)
%ul
- @dossiers.each do |d|
- if !@for_user
%li
#{link_to("n° #{d.id} (#{d.procedure.libelle})", dossier_url(d))}. Retrouvez le dossier au format #{link_to("PDF", instructeur_dossier_url(d.procedure, d, format: :pdf))}
- else
%li
#{link_to("n° #{d.id} (#{d.procedure.libelle})", dossier_url(d))}. Retrouvez le dossier au format #{link_to("PDF", dossier_url(d, format: :pdf))}
%p
- if @for_user
= sanitize(t('.send_user_draft', count: @dossiers.count))
- else
= sanitize(t('.send_other_draft', count: @dossiers.count))
= render partial: "layouts/mailers/signature"

View file

@ -0,0 +1,16 @@
- content_for(:title, "#{@subject}")
%p
Bonjour,
%p
= t('.header', count: @dossiers.count)
%ul
- @dossiers.each do |d|
%li
#{link_to("n° #{d.id} (#{d.procedure.libelle})", dossier_url(d))}. Retrouvez le dossier au format #{link_to("PDF", instructeur_dossier_url(d.procedure, d, format: :pdf))}
%p
= sanitize(t('.footer', count: @dossiers.count))
= render partial: "layouts/mailers/signature"

View file

@ -0,0 +1,16 @@
- content_for(:title, "#{@subject}")
%p
Bonjour,
%p
= t('.header', count: @dossiers.count)
%ul
- @dossiers.each do |d|
%li
#{link_to("n° #{d.id} (#{d.procedure.libelle})", dossier_url(d))}. Retrouvez le dossier au format #{link_to("PDF", dossier_url(d, format: :pdf))}
%p
= sanitize(t('.footer', count: @dossiers.count))
= render partial: "layouts/mailers/signature"

View file

@ -70,6 +70,8 @@
Une notice explicative est un document destiné à guider lusager dans sa démarche. Cest un document que vous avez élaboré et qui peut prendre la forme dun fichier doc, dun pdf ou encore de diapositives. Le bouton pour télécharger cette notice apparaît en haut du formulaire pour lusager.
= f.label :notice, 'Notice'
%p.notice
Formats acceptés : .doc, .odt, .pdf, .ppt, .pptx
- notice = @procedure.notice
= render 'shared/attachment/edit',
{ form: f,

View file

@ -4,6 +4,6 @@ fr:
subject:
one: "Un dossier a été supprimé automatiquement"
other: "Des dossiers ont été supprimés automatiquement"
automatic_dossier_deletion:
header:
one: "Le délai maximum de conservation du dossier suivant a été atteint, il a donc été supprimé :"
other: "Le délai maximum de conservation des dossiers suivants a été atteint, ils ont donc été supprimés :"

View file

@ -4,9 +4,9 @@ fr:
subject:
one: "Un dossier a été supprimé automatiquement"
other: "Des dossiers ont été supprimés automatiquement"
automatic_dossier_deletion:
header:
one: "Le délai maximum de conservation du dossier suivant a été atteint, il a donc été supprimé :"
other: "Le délai maximum de conservation des dossiers suivants a été atteint, ils ont donc été supprimés :"
dossier_will_not_be_processed:
footer:
one: "Le dossier ne sera pas traité, nous nous excusons de la gène occasionnée."
other: "Les dossiers ne seront pas traités, nous nous excusons de la gène occasionnée."

View file

@ -4,6 +4,6 @@ fr:
subject:
one: "Un dossier en brouillon a été supprimé automatiquement"
other: "Des dossiers en brouillon ont été supprimés automatiquement"
automatic_dossier_deletion:
header:
one: "Le délai maximum de conservation du dossier en brouillon suivant a été atteint, il a donc été supprimé :"
other: "Le délai maximum de conservation des dossiers en brouillon suivants a été atteint, ils ont donc été supprimés :"

View file

@ -4,9 +4,9 @@ fr:
subject:
one: Un dossier en brouillon va bientôt être supprimé
other: Des dossiers en brouillon vont bientôt être supprimés
automatic_dossier_deletion:
one: "le dossier en brouillon suivant sera bientôt automatiquement supprimé :"
other: "les dossiers en brouillon suivant seront bientôt automatiquement supprimés :"
send_your_draft:
one: "Si vous souhaitez toujours déposer ce dossier, vous pouvez retrouver votre brouillon pendant encore <b>un mois</b>"
other: "Si vous souhaitez toujours déposer ces dossiers, vous pouvez retrouver vos brouillons pendant encore <b>un mois</b>"
header:
one: "Afin de limiter la conservation de vos données personnelles, le dossier en brouillon suivant sera bientôt automatiquement supprimé :"
other: "Afin de limiter la conservation de vos données personnelles, les dossiers en brouillon suivant seront bientôt automatiquement supprimés :"
footer:
one: "Si vous souhaitez toujours déposer ce dossier, vous pouvez retrouver votre brouillon pendant encore <b>un mois</b>. Et sinon, vous navez rien à faire."
other: "Si vous souhaitez toujours déposer ces dossiers, vous pouvez retrouver vos brouillons pendant encore <b>un mois</b>. Et sinon, vous navez rien à faire."

View file

@ -0,0 +1,5 @@
fr:
dossier_mailer:
notify_deletion_to_administration:
subject: Le dossier nº %{dossier_id} a été supprimé à la demande de lusager
body: À la demande de lusager, le dossier n° %{dossier_id} (%{procedure}) a été supprimé.

View file

@ -0,0 +1,5 @@
fr:
dossier_mailer:
notify_deletion_to_user:
subject: Votre dossier nº %{dossier_id} a bien été supprimé
body: Votre dossier n° %{dossier_id} (%{procedure}) a bien été supprimé. Une trace de ce traitement sera conservée pour ladministration.

View file

@ -1,15 +0,0 @@
fr:
dossier_mailer:
notify_en_construction_near_deletion:
subject:
one: Un dossier en construction va bientôt être supprimé
other: Des dossiers en construction vont bientôt être supprimés
automatic_dossier_deletion:
one: "le dossier en construction suivant sera bientôt automatiquement supprimé :"
other: "les dossiers en construction suivant seront bientôt automatiquement supprimés :"
send_user_draft:
one: "Vous pouvez retrouver votre dossier pendant encore <b>un mois</b>. Vous n'avez rien à faire."
other: "Vous pouvez retrouver vos dossiers pendant encore <b>un mois</b>. Vous n'avez rien à faire."
send_other_draft:
one: "Vous avez <b>un mois</b> pour traiter le dossier."
other: "Vous avez <b>un mois</b> pour traiter les dossiers."

View file

@ -0,0 +1,12 @@
fr:
dossier_mailer:
notify_en_construction_near_deletion_to_administration:
subject:
one: Un dossier en construction va bientôt être supprimé
other: Des dossiers en construction vont bientôt être supprimés
header:
one: "Le dossier en construction suivant sera bientôt automatiquement supprimé :"
other: "Les dossiers en construction suivant seront bientôt automatiquement supprimés :"
footer:
one: "Vous avez <b>un mois</b> pour commencer l'instruction du dossier."
other: "Vous avez <b>un mois</b> pour commencer l'instruction des dossiers."

View file

@ -0,0 +1,12 @@
fr:
dossier_mailer:
notify_en_construction_near_deletion_to_user:
subject:
one: Un dossier en construction va bientôt être supprimé
other: Des dossiers en construction vont bientôt être supprimés
header:
one: "Afin de limiter la conservation de vos données personnelles, le dossier en construction suivant sera bientôt automatiquement supprimé :"
other: "Afin de limiter la conservation de vos données personnelles, les dossiers en construction suivant seront bientôt automatiquement supprimés :"
footer:
one: "Vous pouvez retrouver votre dossier pendant encore <b>un mois</b>. Vous navez rien à faire."
other: "Vous pouvez retrouver vos dossiers pendant encore <b>un mois</b>. Vous navez rien à faire."

View file

@ -0,0 +1,5 @@
class AddEnConstructionCloseToExpirationToDossiers < ActiveRecord::Migration[5.2]
def change
add_column :dossiers, :en_construction_close_to_expiration_notice_sent_at, :datetime
end
end

View file

@ -250,9 +250,10 @@ ActiveRecord::Schema.define(version: 2020_03_04_155418) do
t.text "private_search_terms"
t.bigint "groupe_instructeur_id"
t.datetime "brouillon_close_to_expiration_notice_sent_at"
t.datetime "groupe_instructeur_updated_at"
t.datetime "en_construction_close_to_expiration_notice_sent_at"
t.index "to_tsvector('french'::regconfig, (search_terms || private_search_terms))", name: "index_dossiers_on_search_terms_private_search_terms", using: :gin
t.index "to_tsvector('french'::regconfig, search_terms)", name: "index_dossiers_on_search_terms", using: :gin
t.datetime "groupe_instructeur_updated_at"
t.index ["archived"], name: "index_dossiers_on_archived"
t.index ["groupe_instructeur_id"], name: "index_dossiers_on_groupe_instructeur_id"
t.index ["hidden_at"], name: "index_dossiers_on_hidden_at"

View file

@ -164,6 +164,7 @@ describe ApplicationController, type: :controller do
allow(@controller).to receive(:instructeur_signed_in?).and_return(instructeur_signed_in)
allow(@controller).to receive(:sensitive_path).and_return(sensitive_path)
allow(@controller).to receive(:send_login_token_or_bufferize)
allow(@controller).to receive(:get_stored_location_for).and_return(nil)
allow(@controller).to receive(:store_location_for)
allow(IPService).to receive(:ip_trusted?).and_return(ip_trusted)
end

View file

@ -110,4 +110,16 @@ feature 'Creating a new dossier:' do
end
end
end
context 'when the user is not signed in' do
let(:instructeur) { create(:instructeur) }
let(:procedure) { create(:procedure, :published) }
scenario 'the user is an instructeur with untrusted device' do
visit commencer_path(path: procedure.path)
click_on "Jai déjà un compte"
sign_in_with(instructeur.email, instructeur.user.password, true)
expect(page).to have_current_path(commencer_path(path: procedure.path))
end
end
end

View file

@ -55,8 +55,8 @@ RSpec.describe DossierMailer, type: :mailer do
subject { described_class.notify_deletion_to_administration(deleted_dossier, to_email) }
it { expect(subject.subject).to eq("Le dossier nº #{deleted_dossier.dossier_id} a été supprimé à la demande de l'usager") }
it { expect(subject.body).to include("À la demande de l'usager") }
it { expect(subject.subject).to eq("Le dossier nº #{deleted_dossier.dossier_id} a été supprimé à la demande de lusager") }
it { expect(subject.body).to include("À la demande de lusager") }
it { expect(subject.body).to include(deleted_dossier.dossier_id) }
it { expect(subject.body).to include(deleted_dossier.procedure.libelle) }
end
@ -81,7 +81,7 @@ RSpec.describe DossierMailer, type: :mailer do
@date_suppression = dossier.created_at + duree.months
end
subject { described_class.notify_brouillon_near_deletion(dossier.user, [dossier]) }
subject { described_class.notify_brouillon_near_deletion([dossier], dossier.user.email) }
it { expect(subject.body).to include(" #{dossier.id} ") }
it { expect(subject.body).to include(dossier.procedure.libelle) }
@ -90,7 +90,7 @@ RSpec.describe DossierMailer, type: :mailer do
describe '.notify_brouillon_deletion' do
let(:dossier) { create(:dossier) }
subject { described_class.notify_brouillon_deletion(dossier.user, [dossier.hash_for_deletion_mail]) }
subject { described_class.notify_brouillon_deletion([dossier.hash_for_deletion_mail], dossier.user.email) }
it { expect(subject.subject).to eq("Un dossier en brouillon a été supprimé automatiquement") }
it { expect(subject.body).to include(" #{dossier.id} (#{dossier.procedure.libelle})") }
@ -98,14 +98,16 @@ RSpec.describe DossierMailer, type: :mailer do
describe '.notify_automatic_deletion_to_user' do
let(:dossier) { create(:dossier) }
let(:deleted_dossier) { DeletedDossier.create_from_dossier(dossier) }
before do
duree = dossier.procedure.duree_conservation_dossiers_dans_ds
@date_suppression = dossier.created_at + duree.months
end
subject { described_class.notify_automatic_deletion_to_user(dossier.user, [dossier.hash_for_deletion_mail]) }
subject { described_class.notify_automatic_deletion_to_user([deleted_dossier], dossier.user.email) }
it { expect(subject.to).to eq([dossier.user.email]) }
it { expect(subject.subject).to eq("Un dossier a été supprimé automatiquement") }
it { expect(subject.body).to include("#{dossier.id} ") }
it { expect(subject.body).to include(dossier.procedure.libelle) }
@ -114,8 +116,9 @@ RSpec.describe DossierMailer, type: :mailer do
describe '.notify_automatic_deletion_to_administration' do
let(:dossier) { create(:dossier) }
let(:deleted_dossier) { DeletedDossier.create_from_dossier(dossier) }
subject { described_class.notify_automatic_deletion_to_administration(dossier.user, [dossier.hash_for_deletion_mail]) }
subject { described_class.notify_automatic_deletion_to_administration([deleted_dossier], dossier.user.email) }
it { expect(subject.subject).to eq("Un dossier a été supprimé automatiquement") }
it { expect(subject.body).to include("#{dossier.id} (#{dossier.procedure.libelle})") }
@ -129,13 +132,13 @@ RSpec.describe DossierMailer, type: :mailer do
@date_suppression = dossier.created_at + duree.months
end
subject { described_class.notify_en_construction_near_deletion(dossier.user, [dossier], true) }
subject { described_class.notify_en_construction_near_deletion_to_administration([dossier], dossier.user.email) }
it { expect(subject.subject).to eq("Un dossier en construction va bientôt être supprimé") }
it { expect(subject.body).to include("#{dossier.id} ") }
it { expect(subject.body).to include(dossier.procedure.libelle) }
it { expect(subject.body).to include("PDF") }
it { expect(subject.body).to include("Vous pouvez retrouver votre dossier pendant encore <b>un mois</b>. Vous n'avez rien à faire.") }
it { expect(subject.body).to include("Vous avez <b>un mois</b> pour commencer l'instruction du dossier.") }
end
describe '.notify_en_construction_near_deletion_to_user' do
@ -146,13 +149,14 @@ RSpec.describe DossierMailer, type: :mailer do
@date_suppression = dossier.created_at + duree.months
end
subject { described_class.notify_en_construction_near_deletion(dossier.user, [dossier], false) }
subject { described_class.notify_en_construction_near_deletion_to_user([dossier], dossier.user.email) }
it { expect(subject.to).to eq([dossier.user.email]) }
it { expect(subject.subject).to eq("Un dossier en construction va bientôt être supprimé") }
it { expect(subject.body).to include("#{dossier.id} ") }
it { expect(subject.body).to include(dossier.procedure.libelle) }
it { expect(subject.body).to include("PDF") }
it { expect(subject.body).to include("Vous avez <b>un mois</b> pour traiter le dossier.") }
it { expect(subject.body).to include("Vous pouvez retrouver votre dossier pendant encore <b>un mois</b>. Vous navez rien à faire.") }
end
describe '.notify_groupe_instructeur_changed_to_instructeur' do

View file

@ -8,37 +8,61 @@ class DossierMailerPreview < ActionMailer::Preview
DossierMailer.notify_new_answer(dossier)
end
def notify_deletion_to_user
DossierMailer.notify_deletion_to_user(deleted_dossier, "user@ds.fr")
end
def notify_deletion_to_administration
DossierMailer.notify_deletion_to_administration(deleted_dossier, "admin@ds.fr")
end
def notify_revert_to_instruction
DossierMailer.notify_revert_to_instruction(dossier)
end
def notify_brouillon_near_deletion
DossierMailer.notify_brouillon_near_deletion(User.new(email: "usager@example.com"), [dossier])
DossierMailer.notify_brouillon_near_deletion([dossier], usager_email)
end
def notify_brouillons_near_deletion
DossierMailer.notify_brouillon_near_deletion(User.new(email: "usager@example.com"), [dossier, dossier])
DossierMailer.notify_brouillon_near_deletion([dossier, dossier], usager_email)
end
def notify_en_construction_near_deletion_to_user
DossierMailer.notify_en_construction_near_deletion_to_user([dossier], usager_email)
end
def notify_en_construction_near_deletion_to_administration
DossierMailer.notify_en_construction_near_deletion_to_administration([dossier, dossier], administration_email)
end
def notify_brouillon_deletion
DossierMailer.notify_brouillon_deletion(User.new(email: "usager@example.com"), [dossier.hash_for_deletion_mail])
DossierMailer.notify_brouillon_deletion([dossier.hash_for_deletion_mail], usager_email)
end
def notify_brouillons_deletion
dossier_hashes = [dossier, dossier].map(&:hash_for_deletion_mail)
DossierMailer.notify_brouillon_deletion(User.new(email: "usager@example.com"), dossier_hashes)
DossierMailer.notify_brouillon_deletion(dossier_hashes, usager_email)
end
def notify_deletion_to_user
DossierMailer.notify_deletion_to_user(deleted_dossier, usager_email)
end
def notify_deletion_to_administration
DossierMailer.notify_deletion_to_administration(deleted_dossier, administration_email)
end
def notify_automatic_deletion_to_user
DossierMailer.notify_automatic_deletion_to_user([deleted_dossier, deleted_dossier], usager_email)
end
def notify_automatic_deletion_to_administration
DossierMailer.notify_automatic_deletion_to_administration([deleted_dossier, deleted_dossier], administration_email)
end
private
def usager_email
"usager@example.com"
end
def administration_email
"administration@example.com"
end
def deleted_dossier
DeletedDossier.new(dossier_id: 1, procedure: procedure)
end
@ -52,7 +76,7 @@ class DossierMailerPreview < ActionMailer::Preview
end
def procedure
Procedure.new(libelle: 'Dotation dÉquipement des Territoires Ruraux - Exercice 2019', service: service, logo: Rack::Test::UploadedFile.new("./spec/fixtures/files/logo_test_procedure.png", 'image/png'))
Procedure.new(id: 1234, libelle: 'Dotation dÉquipement des Territoires Ruraux - Exercice 2019', service: service, logo: Rack::Test::UploadedFile.new("./spec/fixtures/files/logo_test_procedure.png", 'image/png'))
end
def service

View file

@ -45,29 +45,54 @@ describe Dossier do
end
end
describe 'nearing_end_of_retention' do
describe 'brouillon_close_to_expiration' do
let(:procedure) { create(:procedure, duree_conservation_dossiers_dans_ds: 6) }
let!(:young_dossier) { create(:dossier, :en_construction, procedure: procedure) }
let!(:expiring_dossier) { create(:dossier, created_at: 170.days.ago, procedure: procedure) }
let!(:just_expired_dossier) { create(:dossier, created_at: (6.months + 1.hour + 10.seconds).ago, procedure: procedure) }
let!(:long_expired_dossier) { create(:dossier, created_at: 1.year.ago, procedure: procedure) }
subject { Dossier.brouillon_close_to_expiration }
it do
is_expected.not_to include(young_dossier)
is_expected.to include(expiring_dossier)
is_expected.to include(just_expired_dossier)
is_expected.to include(long_expired_dossier)
end
end
describe 'en_construction_close_to_expiration' do
let(:procedure) { create(:procedure, duree_conservation_dossiers_dans_ds: 6) }
let!(:young_dossier) { create(:dossier, procedure: procedure) }
let!(:expiring_dossier) { create(:dossier, :en_construction, en_construction_at: 170.days.ago, procedure: procedure) }
let!(:just_expired_dossier) { create(:dossier, :en_construction, en_construction_at: (6.months + 1.hour + 10.seconds).ago, procedure: procedure) }
let!(:long_expired_dossier) { create(:dossier, :en_construction, en_construction_at: 1.year.ago, procedure: procedure) }
subject { Dossier.en_construction_close_to_expiration }
it do
is_expected.not_to include(young_dossier)
is_expected.to include(expiring_dossier)
is_expected.to include(just_expired_dossier)
is_expected.to include(long_expired_dossier)
end
end
describe 'en_instruction_close_to_expiration' do
let(:procedure) { create(:procedure, duree_conservation_dossiers_dans_ds: 6) }
let!(:young_dossier) { create(:dossier, procedure: procedure) }
let!(:expiring_dossier) { create(:dossier, :en_instruction, en_instruction_at: 170.days.ago, procedure: procedure) }
let!(:just_expired_dossier) { create(:dossier, :en_instruction, en_instruction_at: (6.months + 1.hour + 10.seconds).ago, procedure: procedure) }
let!(:long_expired_dossier) { create(:dossier, :en_instruction, en_instruction_at: 1.year.ago, procedure: procedure) }
context 'with default delay to end of retention' do
subject { Dossier.nearing_end_of_retention }
subject { Dossier.en_instruction_close_to_expiration }
it { is_expected.not_to include(young_dossier) }
it { is_expected.to include(expiring_dossier) }
it { is_expected.to include(just_expired_dossier) }
it { is_expected.to include(long_expired_dossier) }
end
context 'with custom delay to end of retention' do
subject { Dossier.nearing_end_of_retention('0') }
it { is_expected.not_to include(young_dossier) }
it { is_expected.not_to include(expiring_dossier) }
it { is_expected.to include(just_expired_dossier) }
it { is_expected.to include(long_expired_dossier) }
it do
is_expected.not_to include(young_dossier)
is_expected.to include(expiring_dossier)
is_expected.to include(just_expired_dossier)
is_expected.to include(long_expired_dossier)
end
end
@ -1072,60 +1097,6 @@ describe Dossier do
it { expect(Dossier.for_procedure(procedure_2)).to contain_exactly(dossier_2_1) }
end
describe '#send_brouillon_expiration_notices' do
before { Timecop.freeze(Time.zone.parse('12/12/2012 15:00:00')) }
let!(:procedure) { create(:procedure, duree_conservation_dossiers_dans_ds: 6) }
let!(:date_close_to_expiration) { Time.zone.now - procedure.duree_conservation_dossiers_dans_ds.months + 1.month }
let!(:date_expired) { Time.zone.now - procedure.duree_conservation_dossiers_dans_ds.months - 6.days }
let!(:date_not_expired) { Time.zone.now - procedure.duree_conservation_dossiers_dans_ds.months + 2.months }
after { Timecop.return }
context "Envoi de message pour les dossiers expirant dans - d'un mois" do
let!(:expired_brouillon) { create(:dossier, procedure: procedure, created_at: date_expired) }
let!(:brouillon_close_to_expiration) { create(:dossier, procedure: procedure, created_at: date_close_to_expiration) }
let!(:brouillon_close_but_with_notice_sent) { create(:dossier, procedure: procedure, created_at: date_close_to_expiration, brouillon_close_to_expiration_notice_sent_at: Time.zone.now) }
let!(:valid_brouillon) { create(:dossier, procedure: procedure, created_at: date_not_expired) }
before do
allow(DossierMailer).to receive(:notify_brouillon_near_deletion).and_return(double(deliver_later: nil))
Dossier.send_brouillon_expiration_notices
end
it 'verification de la creation de mail' do
expect(DossierMailer).to have_received(:notify_brouillon_near_deletion).with(brouillon_close_to_expiration.user, [brouillon_close_to_expiration])
expect(DossierMailer).to have_received(:notify_brouillon_near_deletion).with(expired_brouillon.user, [expired_brouillon])
end
it 'Verification du changement d etat du champ' do
expect(brouillon_close_to_expiration.reload.brouillon_close_to_expiration_notice_sent_at).not_to be_nil
end
end
end
describe '#destroy_brouillons_and_notify' do
let!(:today) { Time.zone.now.at_midnight }
let!(:expired_brouillon) { create(:dossier, brouillon_close_to_expiration_notice_sent_at: today - (Dossier::DRAFT_EXPIRATION + 1.day)) }
let!(:other_brouillon) { create(:dossier, brouillon_close_to_expiration_notice_sent_at: today - (Dossier::DRAFT_EXPIRATION - 1.day)) }
before do
allow(DossierMailer).to receive(:notify_brouillon_deletion).and_return(double(deliver_later: nil))
Dossier.destroy_brouillons_and_notify
end
it 'notifies deletion' do
expect(DossierMailer).to have_received(:notify_brouillon_deletion).once
expect(DossierMailer).to have_received(:notify_brouillon_deletion).with(expired_brouillon.user, [expired_brouillon.hash_for_deletion_mail])
end
it 'deletes the expired brouillon' do
expect(DeletedDossier.find_by(dossier_id: expired_brouillon.id)).to be_present
expect { expired_brouillon.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
describe '#geo_position' do
let(:lat) { "46.538192" }
let(:lon) { "2.428462" }

View file

@ -193,12 +193,19 @@ describe Procedure do
context 'when the deliberation is uploaded ' do
before do
allow(procedure).to receive(:deliberation)
.and_return(double('attached?': true))
procedure.deliberation = Rack::Test::UploadedFile.new('spec/fixtures/files/file.pdf', 'application/pdf')
end
it { expect(procedure.valid?).to eq(true) }
end
context 'when the deliberation is uploaded with an unauthorized format' do
before do
procedure.deliberation = Rack::Test::UploadedFile.new('spec/fixtures/files/french-flag.gif', 'image/gif')
end
it { expect(procedure.valid?).to eq(false) }
end
end
end
@ -925,8 +932,9 @@ describe Procedure do
p.reload
expect(p.juridique_required).to be_falsey
allow(p).to receive(:deliberation).and_return(double('attached?': true))
p.save
@deliberation = Rack::Test::UploadedFile.new('spec/fixtures/files/file.pdf', 'application/pdf')
p.update(deliberation: @deliberation)
p.reload
expect(p.juridique_required).to be_truthy
end
end

View file

@ -0,0 +1,280 @@
require 'spec_helper'
describe ExpiredDossiersDeletionService do
describe '#process_expired_dossiers_brouillon' do
let(:draft_expiration) { 1.month + 5.days }
let!(:today) { Time.zone.now.at_midnight }
let!(:procedure) { create(:procedure, duree_conservation_dossiers_dans_ds: 6) }
let!(:date_close_to_expiration) { Date.today - procedure.duree_conservation_dossiers_dans_ds.months + 1.month }
let!(:date_expired) { Date.today - procedure.duree_conservation_dossiers_dans_ds.months - 6.days }
let!(:date_not_expired) { Date.today - procedure.duree_conservation_dossiers_dans_ds.months + 2.months }
context 'send messages for dossiers expiring soon and delete expired' do
let!(:expired_brouillon) { create(:dossier, procedure: procedure, created_at: date_expired, brouillon_close_to_expiration_notice_sent_at: today - (draft_expiration + 1.day)) }
let!(:brouillon_close_to_expiration) { create(:dossier, procedure: procedure, created_at: date_close_to_expiration) }
let!(:brouillon_close_but_with_notice_sent) { create(:dossier, procedure: procedure, created_at: date_close_to_expiration, brouillon_close_to_expiration_notice_sent_at: Time.zone.now) }
let!(:valid_brouillon) { create(:dossier, procedure: procedure, created_at: date_not_expired) }
before do
allow(DossierMailer).to receive(:notify_brouillon_near_deletion).and_return(double(deliver_later: nil))
allow(DossierMailer).to receive(:notify_brouillon_deletion).and_return(double(deliver_later: nil))
ExpiredDossiersDeletionService.process_expired_dossiers_brouillon
end
it 'emails should be sent' do
expect(DossierMailer).to have_received(:notify_brouillon_near_deletion).once
expect(DossierMailer).to have_received(:notify_brouillon_near_deletion).with([brouillon_close_to_expiration], brouillon_close_to_expiration.user.email)
end
it 'dossier state should change' do
expect(brouillon_close_to_expiration.reload.brouillon_close_to_expiration_notice_sent_at).not_to be_nil
end
it 'deletes and notify expired brouillon' do
expect(DossierMailer).to have_received(:notify_brouillon_deletion).once
expect(DossierMailer).to have_received(:notify_brouillon_deletion).with([expired_brouillon.hash_for_deletion_mail], expired_brouillon.user.email)
expect(DeletedDossier.find_by(dossier_id: expired_brouillon.id)).to be_present
expect { expired_brouillon.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
describe '#send_brouillon_expiration_notices' do
let!(:conservation_par_defaut) { 3.months }
before { Timecop.freeze(Time.zone.now) }
after { Timecop.return }
before do
allow(DossierMailer).to receive(:notify_brouillon_near_deletion).and_return(double(deliver_later: nil))
end
context 'with a single dossier' do
let!(:dossier) { create(:dossier, created_at: created_at) }
before { ExpiredDossiersDeletionService.send_brouillon_expiration_notices }
context 'when the dossier is not closed to expiration' do
let(:created_at) { (conservation_par_defaut - 1.month - 1.day).ago }
it { expect(dossier.reload.brouillon_close_to_expiration_notice_sent_at).to be_nil }
it { expect(DossierMailer).not_to have_received(:notify_brouillon_near_deletion) }
end
context 'when the dossier is closed to expiration' do
let(:created_at) { (conservation_par_defaut - 1.month + 1.day).ago }
it { expect(dossier.reload.brouillon_close_to_expiration_notice_sent_at).not_to be_nil }
it { expect(DossierMailer).to have_received(:notify_brouillon_near_deletion).once }
it { expect(DossierMailer).to have_received(:notify_brouillon_near_deletion).with([dossier], dossier.user.email) }
end
end
context 'with 2 dossiers to notice' do
let!(:user) { create(:user) }
let!(:dossier_1) { create(:dossier, user: user, created_at: (conservation_par_defaut - 1.month + 1.day).ago) }
let!(:dossier_2) { create(:dossier, user: user, created_at: (conservation_par_defaut - 1.month + 1.day).ago) }
before { ExpiredDossiersDeletionService.send_brouillon_expiration_notices }
it { expect(DossierMailer).to have_received(:notify_brouillon_near_deletion).once }
it { expect(DossierMailer).to have_received(:notify_brouillon_near_deletion).with(match_array([dossier_1, dossier_2]), user.email) }
end
end
describe '#delete_expired_brouillons_and_notify' do
let!(:warning_period) { 1.month + 5.days }
before { Timecop.freeze(Time.zone.now) }
after { Timecop.return }
before do
allow(DossierMailer).to receive(:notify_brouillon_deletion).and_return(double(deliver_later: nil))
end
context 'with a single dossier' do
let!(:dossier) { create(:dossier, brouillon_close_to_expiration_notice_sent_at: notice_sent_at) }
before { ExpiredDossiersDeletionService.delete_expired_brouillons_and_notify }
context 'when no notice has been sent' do
let(:notice_sent_at) { nil }
it { expect { dossier.reload }.not_to raise_error }
it { expect(DossierMailer).not_to have_received(:notify_brouillon_deletion) }
end
context 'when a notice has been sent not so long ago' do
let(:notice_sent_at) { (warning_period - 1.day).ago }
it { expect { dossier.reload }.not_to raise_error }
it { expect(DossierMailer).not_to have_received(:notify_brouillon_deletion) }
end
context 'when a notice has been sent a long time ago' do
let(:notice_sent_at) { (warning_period + 1.day).ago }
it { expect { dossier.reload }.to raise_error(ActiveRecord::RecordNotFound) }
it { expect(DossierMailer).to have_received(:notify_brouillon_deletion).once }
it { expect(DossierMailer).to have_received(:notify_brouillon_deletion).with([dossier.hash_for_deletion_mail], dossier.user.email) }
end
end
context 'with 2 dossiers to delete' do
let!(:user) { create(:user) }
let!(:dossier_1) { create(:dossier, user: user, brouillon_close_to_expiration_notice_sent_at: (warning_period + 1.day).ago) }
let!(:dossier_2) { create(:dossier, user: user, brouillon_close_to_expiration_notice_sent_at: (warning_period + 1.day).ago) }
before { ExpiredDossiersDeletionService.delete_expired_brouillons_and_notify }
it { expect(DossierMailer).to have_received(:notify_brouillon_deletion).once }
it { expect(DossierMailer).to have_received(:notify_brouillon_deletion).with(match_array([dossier_1.hash_for_deletion_mail, dossier_2.hash_for_deletion_mail]), user.email) }
end
end
describe '#send_en_construction_expiration_notices' do
let!(:conservation_par_defaut) { 3.months }
before { Timecop.freeze(Time.zone.now) }
after { Timecop.return }
before do
allow(DossierMailer).to receive(:notify_en_construction_near_deletion_to_user).and_return(double(deliver_later: nil))
allow(DossierMailer).to receive(:notify_en_construction_near_deletion_to_administration).and_return(double(deliver_later: nil))
end
context 'with a single dossier' do
let!(:dossier) { create(:dossier, :en_construction, :followed, en_construction_at: en_construction_at) }
before { ExpiredDossiersDeletionService.send_en_construction_expiration_notices }
context 'when the dossier is not near deletion' do
let(:en_construction_at) { (conservation_par_defaut - 1.month - 1.day).ago }
it { expect(dossier.reload.en_construction_close_to_expiration_notice_sent_at).to be_nil }
it { expect(DossierMailer).not_to have_received(:notify_en_construction_near_deletion_to_user) }
it { expect(DossierMailer).not_to have_received(:notify_en_construction_near_deletion_to_administration) }
end
context 'when the dossier is near deletion' do
let(:en_construction_at) { (conservation_par_defaut - 1.month + 1.day).ago }
it { expect(dossier.reload.en_construction_close_to_expiration_notice_sent_at).not_to be_nil }
it { expect(DossierMailer).to have_received(:notify_en_construction_near_deletion_to_user).once }
it { expect(DossierMailer).to have_received(:notify_en_construction_near_deletion_to_administration).twice }
it { expect(DossierMailer).to have_received(:notify_en_construction_near_deletion_to_user).with([dossier], dossier.user.email) }
it { expect(DossierMailer).to have_received(:notify_en_construction_near_deletion_to_administration).with([dossier], dossier.procedure.administrateurs.first.email) }
it { expect(DossierMailer).to have_received(:notify_en_construction_near_deletion_to_administration).with([dossier], dossier.followers_instructeurs.first.email) }
end
end
context 'with 2 dossiers to notice' do
let!(:user) { create(:user) }
let!(:dossier_1) { create(:dossier, :en_construction, user: user, en_construction_at: (conservation_par_defaut - 1.month + 1.day).ago) }
let!(:dossier_2) { create(:dossier, :en_construction, user: user, en_construction_at: (conservation_par_defaut - 1.month + 1.day).ago) }
let!(:instructeur) { create(:instructeur) }
before do
instructeur.followed_dossiers << dossier_1 << dossier_2
ExpiredDossiersDeletionService.send_en_construction_expiration_notices
end
it { expect(DossierMailer).to have_received(:notify_en_construction_near_deletion_to_user).once }
it { expect(DossierMailer).to have_received(:notify_en_construction_near_deletion_to_administration).exactly(3).times }
it { expect(DossierMailer).to have_received(:notify_en_construction_near_deletion_to_user).with(match_array([dossier_1, dossier_2]), user.email) }
it { expect(DossierMailer).to have_received(:notify_en_construction_near_deletion_to_administration).with(match_array([dossier_1, dossier_2]), instructeur.email) }
it { expect(DossierMailer).to have_received(:notify_en_construction_near_deletion_to_administration).with([dossier_1], dossier_1.procedure.administrateurs.first.email) }
it { expect(DossierMailer).to have_received(:notify_en_construction_near_deletion_to_administration).with([dossier_2], dossier_2.procedure.administrateurs.first.email) }
end
context 'when an instructeur is also administrateur' do
let!(:procedure) { create(:procedure) }
let!(:administrateur) { procedure.administrateurs.first }
let!(:dossier) { create(:dossier, :en_construction, procedure: procedure, en_construction_at: (conservation_par_defaut - 1.month + 1.day).ago) }
before do
administrateur.instructeur.followed_dossiers << dossier
ExpiredDossiersDeletionService.send_en_construction_expiration_notices
end
it { expect(DossierMailer).to have_received(:notify_en_construction_near_deletion_to_user).once }
it { expect(DossierMailer).to have_received(:notify_en_construction_near_deletion_to_user).with([dossier], dossier.user.email) }
it { expect(DossierMailer).to have_received(:notify_en_construction_near_deletion_to_administration).with([dossier], administrateur.email) }
end
end
describe '#delete_expired_en_construction_and_notify' do
let!(:warning_period) { 1.month + 5.days }
before { Timecop.freeze(Time.zone.now) }
after { Timecop.return }
before do
allow(DossierMailer).to receive(:notify_automatic_deletion_to_user).and_return(double(deliver_later: nil))
allow(DossierMailer).to receive(:notify_automatic_deletion_to_administration).and_return(double(deliver_later: nil))
end
context 'with a single dossier' do
let!(:dossier) { create(:dossier, :en_construction, :followed, en_construction_close_to_expiration_notice_sent_at: notice_sent_at) }
let(:deleted_dossier) { DeletedDossier.find_by(dossier_id: dossier.id) }
before { ExpiredDossiersDeletionService.delete_expired_en_construction_and_notify }
context 'when no notice has been sent' do
let(:notice_sent_at) { nil }
it { expect { dossier.reload }.not_to raise_error }
it { expect(DossierMailer).not_to have_received(:notify_automatic_deletion_to_user) }
it { expect(DossierMailer).not_to have_received(:notify_automatic_deletion_to_administration) }
end
context 'when a notice has been sent not so long ago' do
let(:notice_sent_at) { (warning_period - 1.day).ago }
it { expect { dossier.reload }.not_to raise_error }
it { expect(DossierMailer).not_to have_received(:notify_automatic_deletion_to_user) }
it { expect(DossierMailer).not_to have_received(:notify_automatic_deletion_to_administration) }
end
context 'when a notice has been sent a long time ago' do
let(:notice_sent_at) { (warning_period + 1.day).ago }
it { expect { dossier.reload }.to raise_error(ActiveRecord::RecordNotFound) }
it { expect(DossierMailer).to have_received(:notify_automatic_deletion_to_user).once }
it { expect(DossierMailer).to have_received(:notify_automatic_deletion_to_user).with([deleted_dossier], dossier.user.email) }
it { expect(DossierMailer).to have_received(:notify_automatic_deletion_to_administration).twice }
it { expect(DossierMailer).to have_received(:notify_automatic_deletion_to_administration).with([deleted_dossier], dossier.procedure.administrateurs.first.email) }
it { expect(DossierMailer).to have_received(:notify_automatic_deletion_to_administration).with([deleted_dossier], dossier.followers_instructeurs.first.email) }
end
end
context 'with 2 dossiers to delete' do
let!(:user) { create(:user) }
let!(:dossier_1) { create(:dossier, :en_construction, user: user, en_construction_close_to_expiration_notice_sent_at: (warning_period + 1.day).ago) }
let!(:dossier_2) { create(:dossier, :en_construction, user: user, en_construction_close_to_expiration_notice_sent_at: (warning_period + 1.day).ago) }
let(:deleted_dossier_1) { DeletedDossier.find_by(dossier_id: dossier_1.id) }
let(:deleted_dossier_2) { DeletedDossier.find_by(dossier_id: dossier_2.id) }
let!(:instructeur) { create(:instructeur) }
before do
instructeur.followed_dossiers << dossier_1 << dossier_2
ExpiredDossiersDeletionService.delete_expired_en_construction_and_notify
end
it { expect(DossierMailer).to have_received(:notify_automatic_deletion_to_user).once }
it { expect(DossierMailer).to have_received(:notify_automatic_deletion_to_user).with(match_array([deleted_dossier_1, deleted_dossier_2]), user.email) }
it { expect(DossierMailer).to have_received(:notify_automatic_deletion_to_administration).thrice }
it { expect(DossierMailer).to have_received(:notify_automatic_deletion_to_administration).with(match_array([deleted_dossier_1, deleted_dossier_2]), instructeur.email) }
it { expect(DossierMailer).to have_received(:notify_automatic_deletion_to_administration).with([deleted_dossier_1], dossier_1.procedure.administrateurs.first.email) }
it { expect(DossierMailer).to have_received(:notify_automatic_deletion_to_administration).with([deleted_dossier_2], dossier_2.procedure.administrateurs.first.email) }
end
end
end

View file

@ -44,7 +44,7 @@ Capybara.register_driver :headless_chrome do |app|
options.add_argument('--window-size=1440,900')
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
chromeOptions: { args: ['headless', 'disable-dev-shm-usage', 'disable-software-rasterizer', 'mute-audio', 'window-size=1440,900'] }
chromeOptions: { args: ['disable-dev-shm-usage', 'disable-software-rasterizer', 'mute-audio', 'window-size=1440,900'] }
)
Capybara::Selenium::Driver.new app,