Merge pull request #7139 from betagouv/main

2022-04-08-01
This commit is contained in:
Paul Chavard 2022-04-08 14:13:45 +02:00 committed by GitHub
commit 57df2fe693
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 418 additions and 173 deletions

View file

@ -139,7 +139,7 @@ module Experts
end end
def telecharger_pjs def telecharger_pjs
files = ActiveStorage::DownloadableFile.create_list_from_dossier(@dossier, true) files = ActiveStorage::DownloadableFile.create_list_from_dossiers(Dossier.where(id: @dossier.id), true)
zipline(files, "dossier-#{@dossier.id}.zip") zipline(files, "dossier-#{@dossier.id}.zip")
end end

View file

@ -20,7 +20,7 @@ module Instructeurs
end end
def extend_conservation def extend_conservation
dossier.update(conservation_extension: dossier.conservation_extension + 1.month) dossier.extend_conservation(1.month)
flash[:notice] = t('views.instructeurs.dossiers.archived_dossier') flash[:notice] = t('views.instructeurs.dossiers.archived_dossier')
redirect_back(fallback_location: instructeur_dossier_path(@dossier.procedure, @dossier)) redirect_back(fallback_location: instructeur_dossier_path(@dossier.procedure, @dossier))
end end
@ -218,7 +218,7 @@ module Instructeurs
end end
def telecharger_pjs def telecharger_pjs
files = ActiveStorage::DownloadableFile.create_list_from_dossier(dossier) files = ActiveStorage::DownloadableFile.create_list_from_dossiers(Dossier.where(id: dossier.id))
zipline(files, "dossier-#{dossier.id}.zip") zipline(files, "dossier-#{dossier.id}.zip")
end end

View file

@ -174,7 +174,7 @@ module Users
end end
def extend_conservation def extend_conservation
dossier.update(conservation_extension: dossier.conservation_extension + dossier.procedure.duree_conservation_dossiers_dans_ds.months) dossier.extend_conservation(dossier.procedure.duree_conservation_dossiers_dans_ds.months)
flash[:notice] = t('views.users.dossiers.archived_dossier', duree_conservation_dossiers_dans_ds: dossier.procedure.duree_conservation_dossiers_dans_ds) flash[:notice] = t('views.users.dossiers.archived_dossier', duree_conservation_dossiers_dans_ds: dossier.procedure.duree_conservation_dossiers_dans_ds)
redirect_back(fallback_location: dossier_path(@dossier)) redirect_back(fallback_location: dossier_path(@dossier))
end end

View file

@ -63,7 +63,7 @@ module Types
def date_expiration def date_expiration
if !object.en_instruction? if !object.en_instruction?
object.expiration_date.presence || object.approximative_expiration_date object.expiration_date
end end
end end

View file

@ -99,8 +99,7 @@ module DossierHelper
end end
def safe_expiration_date(dossier) def safe_expiration_date(dossier)
date = dossier.expiration_date.presence || dossier.approximative_expiration_date l(dossier.expiration_date, format: '%d/%m/%Y')
l(date, format: '%d/%m/%Y')
end end
def annuaire_link(siren) def annuaire_link(siren)

View file

@ -1,24 +1,26 @@
class ActiveStorage::DownloadableFile class ActiveStorage::DownloadableFile
def self.create_list_from_dossier(dossier, for_expert = false) def self.create_list_from_dossiers(dossiers, for_expert = false)
dossier_export = PiecesJustificativesService.generate_dossier_export(dossier) dossiers
pjs = [dossier_export] + PiecesJustificativesService.liste_documents(dossier, for_expert) .map { |d| pj_and_path(d.id, PiecesJustificativesService.generate_dossier_export(d)) } +
pjs.map do |piece_justificative| PiecesJustificativesService.liste_documents(dossiers, for_expert)
[
piece_justificative,
"dossier-#{dossier.id}/#{self.timestamped_filename(piece_justificative)}"
]
end
end
def self.create_list_from_dossiers(dossiers)
dossiers.flat_map do |dossier|
create_list_from_dossier(dossier)
end
end end
private private
def self.bill_and_path(bill)
[
bill,
"bills/#{self.timestamped_filename(bill)}"
]
end
def self.pj_and_path(dossier_id, pj)
[
pj,
"dossier-#{dossier_id}/#{self.timestamped_filename(pj)}"
]
end
def self.timestamped_filename(attachment) def self.timestamped_filename(attachment)
# we pad the original file name with a timestamp # we pad the original file name with a timestamp
# and a short id in order to help identify multiple versions and avoid name collisions # and a short id in order to help identify multiple versions and avoid name collisions
@ -47,8 +49,4 @@ class ActiveStorage::DownloadableFile
'pieces_justificatives/' 'pieces_justificatives/'
end end
end end
def using_local_backend?
[:local, :local_test, :test].include?(Rails.application.config.active_storage.service)
end
end end

View file

@ -573,7 +573,7 @@ class Dossier < ApplicationRecord
].any? ].any?
end end
def approximative_expiration_date_reference def expiration_date_reference
if brouillon? if brouillon?
created_at created_at
elsif en_construction? elsif en_construction?
@ -581,24 +581,28 @@ class Dossier < ApplicationRecord
elsif termine? elsif termine?
processed_at processed_at
else else
fail "approximative_expiration_date_reference should not be called in state #{self.state}" fail "expiration_date_reference should not be called in state #{self.state}"
end end
end end
def approximative_expiration_date def expiration_date_with_extention
[ [
approximative_expiration_date_reference, expiration_date_reference,
conservation_extension, conservation_extension,
procedure.duree_conservation_dossiers_dans_ds.months procedure.duree_conservation_dossiers_dans_ds.months
].sum - REMAINING_WEEKS_BEFORE_EXPIRATION.weeks ].sum
end
def expiration_notification_date
expiration_date_with_extention - REMAINING_WEEKS_BEFORE_EXPIRATION.weeks
end end
def close_to_expiration? def close_to_expiration?
return false if en_instruction? return false if en_instruction?
approximative_expiration_date < Time.zone.now expiration_notification_date < Time.zone.now
end end
def expiration_date def after_notification_expiration_date
if brouillon? && brouillon_close_to_expiration_notice_sent_at.present? if brouillon? && brouillon_close_to_expiration_notice_sent_at.present?
brouillon_close_to_expiration_notice_sent_at + duration_after_notice brouillon_close_to_expiration_notice_sent_at + duration_after_notice
elsif en_construction? && en_construction_close_to_expiration_notice_sent_at.present? elsif en_construction? && en_construction_close_to_expiration_notice_sent_at.present?
@ -608,6 +612,10 @@ class Dossier < ApplicationRecord
end end
end end
def expiration_date
after_notification_expiration_date.presence || expiration_date_with_extention
end
def duration_after_notice def duration_after_notice
MONTHS_AFTER_EXPIRATION.month + DAYS_AFTER_EXPIRATION.days MONTHS_AFTER_EXPIRATION.month + DAYS_AFTER_EXPIRATION.days
end end
@ -616,6 +624,13 @@ class Dossier < ApplicationRecord
brouillon? || en_construction? brouillon? || en_construction?
end end
def extend_conservation(conservation_extension)
update(conservation_extension: self.conservation_extension + conservation_extension,
brouillon_close_to_expiration_notice_sent_at: nil,
en_construction_close_to_expiration_notice_sent_at: nil,
termine_close_to_expiration_notice_sent_at: nil)
end
def show_groupe_instructeur_details? def show_groupe_instructeur_details?
procedure.routee? && groupe_instructeur.present? && (!procedure.feature_enabled?(:procedure_routage_api) || !defaut_groupe_instructeur?) procedure.routee? && groupe_instructeur.present? && (!procedure.feature_enabled?(:procedure_routage_api) || !defaut_groupe_instructeur?)
end end

View file

@ -1,10 +1,30 @@
class PiecesJustificativesService class PiecesJustificativesService
def self.liste_documents(dossier, for_expert) def self.liste_documents(dossiers, for_expert)
pjs_champs = pjs_for_champs(dossier, for_expert) bill_ids = []
pjs_commentaires = pjs_for_commentaires(dossier)
pjs_dossier = pjs_for_dossier(dossier, for_expert)
pjs_champs + pjs_commentaires + pjs_dossier docs = dossiers.in_batches.flat_map do |batch|
pjs = pjs_for_champs(batch, for_expert) +
pjs_for_commentaires(batch) +
pjs_for_dossier(batch)
if !for_expert
# some bills are shared among operations
# so first, all the bill_ids are fetched
operation_logs, some_bill_ids = operation_logs_and_signature_ids(batch)
pjs += operation_logs
bill_ids += some_bill_ids
end
pjs
end
if !for_expert
# then the bills are retrieved without duplication
docs += signatures(bill_ids.uniq)
end
docs
end end
def self.serialize_types_de_champ_as_type_pj(revision) def self.serialize_types_de_champ_as_type_pj(revision)
@ -106,82 +126,117 @@ class PiecesJustificativesService
private private
def self.pjs_for_champs(dossier, for_expert = false) def self.pjs_for_champs(dossiers, for_expert = false)
champs = Champ champs = Champ
.joins(:piece_justificative_file_attachment) .joins(:piece_justificative_file_attachment)
.where(type: "Champs::PieceJustificativeChamp", dossier: dossier) .where(type: "Champs::PieceJustificativeChamp", dossier: dossiers)
if for_expert if for_expert
champs = champs.where(private: false) champs = champs.where(private: false)
end end
champ_id_dossier_id = champs
.pluck(:id, :dossier_id)
.to_h
ActiveStorage::Attachment ActiveStorage::Attachment
.includes(:blob) .includes(:blob)
.where(record_type: "Champ", record_id: champs.ids) .where(record_type: "Champ", record_id: champ_id_dossier_id.keys)
.map do |a|
dossier_id = champ_id_dossier_id[a.record_id]
ActiveStorage::DownloadableFile.pj_and_path(dossier_id, a)
end
end end
def self.pjs_for_commentaires(dossier) def self.pjs_for_commentaires(dossiers)
commentaires = Commentaire commentaire_id_dossier_id = Commentaire
.joins(:piece_jointe_attachment) .joins(:piece_jointe_attachment)
.where(dossier: dossier) .where(dossier: dossiers)
.pluck(:id, :dossier_id)
.to_h
ActiveStorage::Attachment ActiveStorage::Attachment
.includes(:blob) .includes(:blob)
.where(record_type: "Commentaire", record_id: commentaires.ids) .where(record_type: "Commentaire", record_id: commentaire_id_dossier_id.keys)
.map do |a|
dossier_id = commentaire_id_dossier_id[a.record_id]
ActiveStorage::DownloadableFile.pj_and_path(dossier_id, a)
end
end end
def self.pjs_for_dossier(dossier, for_expert = false) def self.pjs_for_dossier(dossiers)
pjs = motivation(dossier) + motivations(dossiers) +
attestation(dossier) + attestations(dossiers) +
etablissement(dossier) etablissements(dossiers)
if !for_expert
pjs += operation_logs_and_signatures(dossier)
end end
pjs def self.etablissements(dossiers)
end etablissement_id_dossier_id = Etablissement
.where(dossier: dossiers)
def self.etablissement(dossier) .pluck(:id, :dossier_id)
etablissement = Etablissement.where(dossier: dossier) .to_h
ActiveStorage::Attachment ActiveStorage::Attachment
.includes(:blob) .includes(:blob)
.where(record_type: "Etablissement", record_id: etablissement) .where(record_type: "Etablissement", record_id: etablissement_id_dossier_id.keys)
.map do |a|
dossier_id = etablissement_id_dossier_id[a.record_id]
ActiveStorage::DownloadableFile.pj_and_path(dossier_id, a)
end
end end
def self.motivation(dossier) def self.motivations(dossiers)
ActiveStorage::Attachment ActiveStorage::Attachment
.includes(:blob) .includes(:blob)
.where(record_type: "Dossier", name: "justificatif_motivation", record_id: dossier) .where(record_type: "Dossier", name: "justificatif_motivation", record_id: dossiers)
.map do |a|
dossier_id = a.record_id
ActiveStorage::DownloadableFile.pj_and_path(dossier_id, a)
end
end end
def self.attestation(dossier) def self.attestations(dossiers)
attestation = Attestation attestation_id_dossier_id = Attestation
.joins(:pdf_attachment) .joins(:pdf_attachment)
.where(dossier: dossier) .where(dossier: dossiers)
.pluck(:id, :dossier_id)
.to_h
ActiveStorage::Attachment ActiveStorage::Attachment
.includes(:blob) .includes(:blob)
.where(record_type: "Attestation", record_id: attestation) .where(record_type: "Attestation", record_id: attestation_id_dossier_id.keys)
.map do |a|
dossier_id = attestation_id_dossier_id[a.record_id]
ActiveStorage::DownloadableFile.pj_and_path(dossier_id, a)
end
end end
def self.operation_logs_and_signatures(dossier) def self.operation_logs_and_signature_ids(dossiers)
dol_ids_bill_id = DossierOperationLog dol_id_dossier_id_bill_id = DossierOperationLog
.where(dossier: dossier) .where(dossier: dossiers)
.pluck(:id, :bill_signature_id) .pluck(:id, :dossier_id, :bill_signature_id)
dol_ids = dol_ids_bill_id.map(&:first) dol_id_dossier_id = dol_id_dossier_id_bill_id
bill_ids = dol_ids_bill_id.map(&:second).uniq.compact .map { |dol_id, dossier_id, _| [dol_id, dossier_id] }
.to_h
bill_ids = dol_id_dossier_id_bill_id.map(&:third).uniq.compact
serialized_dols = ActiveStorage::Attachment serialized_dols = ActiveStorage::Attachment
.includes(:blob) .includes(:blob)
.where(record_type: "DossierOperationLog", record_id: dol_ids) .where(record_type: "DossierOperationLog", record_id: dol_id_dossier_id.keys)
.map do |a|
dossier_id = dol_id_dossier_id[a.record_id]
ActiveStorage::DownloadableFile.pj_and_path(dossier_id, a)
end
bill_docs = ActiveStorage::Attachment [serialized_dols, bill_ids]
end
def self.signatures(bill_ids)
ActiveStorage::Attachment
.includes(:blob) .includes(:blob)
.where(record_type: "BillSignature", record_id: bill_ids) .where(record_type: "BillSignature", record_id: bill_ids)
.map { |bill| ActiveStorage::DownloadableFile.bill_and_path(bill) }
serialized_dols + bill_docs
end end
end end

View file

@ -26,18 +26,18 @@ shared_dirs = [
'vendor/bundle' 'vendor/bundle'
] ]
set :domain, ENV.fetch('domain') set :domain, ENV.fetch('DOMAINS')
set :deploy_to, deploy_to set :deploy_to, deploy_to
# rubocop:disable DS/ApplicationName # rubocop:disable DS/ApplicationName
set :repository, 'https://github.com/betagouv/demarches-simplifiees.fr.git' set :repository, 'https://github.com/betagouv/demarches-simplifiees.fr.git'
# rubocop:enable DS/ApplicationName # rubocop:enable DS/ApplicationName
set :branch, ENV.fetch('branch') set :branch, ENV.fetch('BRANCH')
set :forward_agent, true set :forward_agent, true
set :user, 'ds' set :user, 'ds'
set :shared_dirs, shared_dirs set :shared_dirs, shared_dirs
set :rbenv_path, "/home/ds/.rbenv/bin/rbenv" set :rbenv_path, "/home/ds/.rbenv/bin/rbenv"
puts "Deploy to #{ENV.fetch('domain')}, branch: #{ENV.fetch('branch')}" puts "Deploy to #{ENV.fetch('DOMAINS')}, branch: #{ENV.fetch('BRANCH')}"
# This task is the environment that is loaded for most commands, such as # This task is the environment that is loaded for most commands, such as
# `mina deploy` or `mina rake`. # `mina deploy` or `mina rake`.

View file

@ -0,0 +1,51 @@
namespace :after_party do
desc 'Deployment task: fix_dossiers_expiration_dates'
task fix_dossiers_expiration_dates: :environment do
puts "Running deploy task 'fix_dossiers_expiration_dates'"
duree_conservation = "procedures.duree_conservation_dossiers_dans_ds * INTERVAL '1 month'"
expiration_after_notice_brouillon = "dossiers.brouillon_close_to_expiration_notice_sent_at + INTERVAL '#{Dossier::INTERVAL_EXPIRATION}'"
expiration_with_extention_brouillon = "dossiers.created_at + dossiers.conservation_extension + (#{duree_conservation}) - INTERVAL '#{Dossier::INTERVAL_BEFORE_EXPIRATION}'"
dossiers_brouillon = Dossier
.joins(:procedure)
.state_brouillon
.visible_by_user
.where.not(brouillon_close_to_expiration_notice_sent_at: nil)
.where.not(conservation_extension: 0.seconds)
.where("(#{expiration_after_notice_brouillon}) < (#{expiration_with_extention_brouillon})")
expiration_after_notice_en_construction = "dossiers.en_construction_close_to_expiration_notice_sent_at + INTERVAL '#{Dossier::INTERVAL_EXPIRATION}'"
expiration_with_extention_en_construction = "dossiers.en_construction_at + dossiers.conservation_extension + (#{duree_conservation}) - INTERVAL '#{Dossier::INTERVAL_BEFORE_EXPIRATION}'"
dossiers_en_construction = Dossier
.joins(:procedure)
.state_en_construction
.visible_by_user_or_administration
.where.not(en_construction_close_to_expiration_notice_sent_at: nil)
.where.not(conservation_extension: 0.seconds)
.where("(#{expiration_after_notice_en_construction}) < (#{expiration_with_extention_en_construction})")
expiration_after_notice_termine = "dossiers.termine_close_to_expiration_notice_sent_at + INTERVAL '#{Dossier::INTERVAL_EXPIRATION}'"
expiration_with_extention_termine = "dossiers.processed_at + dossiers.conservation_extension + (#{duree_conservation}) - INTERVAL '#{Dossier::INTERVAL_BEFORE_EXPIRATION}'"
dossiers_termine = Dossier
.joins(:procedure)
.state_termine
.visible_by_user_or_administration
.where.not(termine_close_to_expiration_notice_sent_at: nil)
.where.not(conservation_extension: 0.seconds)
.where("(#{expiration_after_notice_termine}) < (#{expiration_with_extention_termine})")
rake_puts "brouillon: #{dossiers_brouillon.count}"
rake_puts "en_construction: #{dossiers_en_construction.count}"
rake_puts "termine: #{dossiers_termine.count}"
dossiers_brouillon.update_all(brouillon_close_to_expiration_notice_sent_at: nil)
dossiers_en_construction.update_all(en_construction_close_to_expiration_notice_sent_at: nil)
dossiers_termine.update_all(termine_close_to_expiration_notice_sent_at: nil)
# 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: AfterParty::TaskRecorder.new(__FILE__).timestamp
end
end

View file

@ -1,74 +1,12 @@
describe ActiveStorage::DownloadableFile do describe ActiveStorage::DownloadableFile do
let(:dossier) { create(:dossier, :en_construction) } let(:dossier) { create(:dossier, :en_construction) }
subject(:list) { ActiveStorage::DownloadableFile.create_list_from_dossier(dossier) } subject(:list) { ActiveStorage::DownloadableFile.create_list_from_dossiers(Dossier.where(id: dossier.id)) }
describe 'create_list_from_dossier' do describe 'create_list_from_dossiers' do
context 'when no piece_justificative is present' do context 'when no piece_justificative is present' do
it { expect(list.length).to eq 1 } it { expect(list.length).to eq 1 }
it { expect(list.first[0].name).to eq "pdf_export_for_instructeur" } it { expect(list.first[0].name).to eq "pdf_export_for_instructeur" }
end end
context 'when there is a piece_justificative' do
before do
dossier.champs << create(:champ_piece_justificative, :with_piece_justificative_file, dossier: dossier)
end
it { expect(list.length).to eq 2 }
end
context 'when there is a private piece_justificative' do
before do
dossier.champs_private << create(:champ_piece_justificative, :with_piece_justificative_file, private: true, dossier: dossier)
end
it { expect(list.length).to eq 2 }
end
context 'when there is a repetition bloc' do
before do
dossier.champs << create(:champ_repetition_with_piece_jointe, dossier: dossier)
end
it 'should have 4 piece_justificatives' do
expect(list.size).to eq 5
end
end
context 'when there is a message with no attachment' do
before do
dossier.commentaires << create(:commentaire, dossier: dossier)
end
it { expect(list.length).to eq 1 }
end
context 'when there is a message with an attachment' do
before do
dossier.commentaires << create(:commentaire, :with_file, dossier: dossier)
end
it { expect(list.length).to eq 2 }
end
context 'when the files are asked by an expert with piece justificative and private piece justificative' do
let(:expert) { create(:expert) }
let(:instructeur) { create(:instructeur) }
let(:procedure) { create(:procedure, :published, :with_piece_justificative, instructeurs: [instructeur]) }
let(:experts_procedure) { create(:experts_procedure, expert: expert, procedure: procedure) }
let(:dossier) { create(:dossier, :en_construction, :with_dossier_link, procedure: procedure) }
let(:champ) { dossier.champs.first }
let(:avis) { create(:avis, dossier: dossier, claimant: instructeur, experts_procedure: experts_procedure, confidentiel: true) }
subject(:list) { ActiveStorage::DownloadableFile.create_list_from_dossier(dossier, true) }
before do
dossier.champs_private << create(:champ_piece_justificative, :with_piece_justificative_file, private: true, dossier: dossier)
dossier.champs << create(:champ_piece_justificative, :with_piece_justificative_file, dossier: dossier)
end
it { expect(list.length).to eq 2 }
end
end end
end end

View file

@ -40,6 +40,7 @@ describe Dossier do
let(:procedure) { create(:procedure, :published, duree_conservation_dossiers_dans_ds: 6) } let(:procedure) { create(:procedure, :published, duree_conservation_dossiers_dans_ds: 6) }
let!(:young_dossier) { create(:dossier, :en_construction, procedure: procedure) } let!(:young_dossier) { create(:dossier, :en_construction, procedure: procedure) }
let!(:expiring_dossier) { create(:dossier, created_at: 175.days.ago, procedure: procedure) } let!(:expiring_dossier) { create(:dossier, created_at: 175.days.ago, procedure: procedure) }
let!(:expiring_dossier_with_notification) { create(:dossier, created_at: 175.days.ago, brouillon_close_to_expiration_notice_sent_at: Time.zone.now, procedure: procedure) }
let!(:just_expired_dossier) { create(:dossier, created_at: (6.months + 1.hour + 10.seconds).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) } let!(:long_expired_dossier) { create(:dossier, created_at: 1.year.ago, procedure: procedure) }
@ -52,13 +53,27 @@ describe Dossier do
is_expected.to include(long_expired_dossier) is_expected.to include(long_expired_dossier)
end end
it do
expect(expiring_dossier.close_to_expiration?).to be_truthy
expect(expiring_dossier_with_notification.close_to_expiration?).to be_truthy
end
context 'does not include an expiring dossier that has been postponed' do context 'does not include an expiring dossier that has been postponed' do
before do before do
expiring_dossier.update(conservation_extension: 1.month) expiring_dossier.extend_conservation(1.month)
expiring_dossier_with_notification.extend_conservation(1.month)
expiring_dossier.reload expiring_dossier.reload
expiring_dossier_with_notification.reload
end end
it { is_expected.not_to include(expiring_dossier) } it { is_expected.not_to include(expiring_dossier) }
it do
expect(expiring_dossier.close_to_expiration?).to be_falsey
expect(expiring_dossier_with_notification.close_to_expiration?).to be_falsey
expect(expiring_dossier.expiration_date).to eq(expiring_dossier.expiration_date_with_extention)
expect(expiring_dossier_with_notification.expiration_date).to eq(expiring_dossier_with_notification.expiration_date_with_extention)
end
end end
context 'when .close_to_expiration' do context 'when .close_to_expiration' do
@ -76,6 +91,7 @@ describe Dossier do
let(:procedure) { create(:procedure, :published, duree_conservation_dossiers_dans_ds: 6) } let(:procedure) { create(:procedure, :published, duree_conservation_dossiers_dans_ds: 6) }
let!(:young_dossier) { create(:dossier, procedure: procedure) } let!(:young_dossier) { create(:dossier, procedure: procedure) }
let!(:expiring_dossier) { create(:dossier, :en_construction, en_construction_at: 175.days.ago, procedure: procedure) } let!(:expiring_dossier) { create(:dossier, :en_construction, en_construction_at: 175.days.ago, procedure: procedure) }
let!(:expiring_dossier_with_notification) { create(:dossier, :en_construction, en_construction_at: 175.days.ago, en_construction_close_to_expiration_notice_sent_at: Time.zone.now, procedure: procedure) }
let!(:just_expired_dossier) { create(:dossier, :en_construction, en_construction_at: (6.months + 1.hour + 10.seconds).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) } let!(:long_expired_dossier) { create(:dossier, :en_construction, en_construction_at: 1.year.ago, procedure: procedure) }
@ -88,13 +104,27 @@ describe Dossier do
is_expected.to include(long_expired_dossier) is_expected.to include(long_expired_dossier)
end end
it do
expect(expiring_dossier.close_to_expiration?).to be_truthy
expect(expiring_dossier_with_notification.close_to_expiration?).to be_truthy
end
context 'does not include an expiring dossier that has been postponed' do context 'does not include an expiring dossier that has been postponed' do
before do before do
expiring_dossier.update(conservation_extension: 1.month) expiring_dossier.extend_conservation(1.month)
expiring_dossier_with_notification.extend_conservation(1.month)
expiring_dossier.reload expiring_dossier.reload
expiring_dossier_with_notification.reload
end end
it { is_expected.not_to include(expiring_dossier) } it { is_expected.not_to include(expiring_dossier) }
it do
expect(expiring_dossier.close_to_expiration?).to be_falsey
expect(expiring_dossier_with_notification.close_to_expiration?).to be_falsey
expect(expiring_dossier.expiration_date).to eq(expiring_dossier.expiration_date_with_extention)
expect(expiring_dossier_with_notification.expiration_date).to eq(expiring_dossier_with_notification.expiration_date_with_extention)
end
end end
context 'when .close_to_expiration' do context 'when .close_to_expiration' do
@ -121,6 +151,7 @@ describe Dossier do
let(:procedure) { create(:procedure, :published, duree_conservation_dossiers_dans_ds: 6, procedure_expires_when_termine_enabled: true) } let(:procedure) { create(:procedure, :published, duree_conservation_dossiers_dans_ds: 6, procedure_expires_when_termine_enabled: true) }
let!(:young_dossier) { create(:dossier, state: :accepte, procedure: procedure, processed_at: 2.days.ago) } let!(:young_dossier) { create(:dossier, state: :accepte, procedure: procedure, processed_at: 2.days.ago) }
let!(:expiring_dossier) { create(:dossier, state: :accepte, procedure: procedure, processed_at: 175.days.ago) } let!(:expiring_dossier) { create(:dossier, state: :accepte, procedure: procedure, processed_at: 175.days.ago) }
let!(:expiring_dossier_with_notification) { create(:dossier, state: :accepte, procedure: procedure, processed_at: 175.days.ago, termine_close_to_expiration_notice_sent_at: Time.zone.now) }
let!(:just_expired_dossier) { create(:dossier, state: :accepte, procedure: procedure, processed_at: (6.months + 1.hour + 10.seconds).ago) } let!(:just_expired_dossier) { create(:dossier, state: :accepte, procedure: procedure, processed_at: (6.months + 1.hour + 10.seconds).ago) }
let!(:long_expired_dossier) { create(:dossier, state: :accepte, procedure: procedure, processed_at: 1.year.ago) } let!(:long_expired_dossier) { create(:dossier, state: :accepte, procedure: procedure, processed_at: 1.year.ago) }
@ -133,6 +164,29 @@ describe Dossier do
is_expected.to include(long_expired_dossier) is_expected.to include(long_expired_dossier)
end end
it do
expect(expiring_dossier.close_to_expiration?).to be_truthy
expect(expiring_dossier_with_notification.close_to_expiration?).to be_truthy
end
context 'does not include an expiring dossier that has been postponed' do
before do
expiring_dossier.extend_conservation(1.month)
expiring_dossier_with_notification.extend_conservation(1.month)
expiring_dossier.reload
expiring_dossier_with_notification.reload
end
it { is_expected.not_to include(expiring_dossier) }
it do
expect(expiring_dossier.close_to_expiration?).to be_falsey
expect(expiring_dossier_with_notification.close_to_expiration?).to be_falsey
expect(expiring_dossier.expiration_date).to eq(expiring_dossier.expiration_date_with_extention)
expect(expiring_dossier_with_notification.expiration_date).to eq(expiring_dossier_with_notification.expiration_date_with_extention)
end
end
context 'when .close_to_expiration' do context 'when .close_to_expiration' do
subject { Dossier.close_to_expiration } subject { Dossier.close_to_expiration }
it do it do

View file

@ -1,43 +1,178 @@
describe PiecesJustificativesService do describe PiecesJustificativesService do
describe '.liste_documents' do
let(:for_expert) { false }
subject do
PiecesJustificativesService
.liste_documents(Dossier.where(id: dossier.id), for_expert)
.map(&:first)
end
context 'with a pj champ' do
let(:procedure) { create(:procedure, :with_piece_justificative) }
let(:dossier) { create(:dossier, procedure: procedure) }
let(:witness) { create(:dossier, procedure: procedure) }
let(:pj_champ) { -> (d) { d.champs.find { |c| c.type == 'Champs::PieceJustificativeChamp' } } }
before do
attach_file_to_champ(pj_champ.call(dossier))
attach_file_to_champ(pj_champ.call(witness))
end
it { expect(subject).to match_array([pj_champ.call(dossier).piece_justificative_file.attachment]) }
end
context 'with a private pj champ' do
let(:procedure) { create(:procedure) }
let(:dossier) { create(:dossier, procedure: procedure) }
let(:witness) { create(:dossier, procedure: procedure) }
let!(:private_pj) { create(:type_de_champ_piece_justificative, procedure: procedure, private: true) }
let(:private_pj_champ) { -> (d) { d.champs_private.find { |c| c.type == 'Champs::PieceJustificativeChamp' } } }
before do
attach_file_to_champ(private_pj_champ.call(dossier))
attach_file_to_champ(private_pj_champ.call(witness))
end
it { expect(subject).to match_array([private_pj_champ.call(dossier).piece_justificative_file.attachment]) }
context 'for expert' do
let(:for_expert) { true }
it { expect(subject).to be_empty }
end
end
context 'with a identite champ pj' do
let(:procedure) { create(:procedure, :with_titre_identite) } let(:procedure) { create(:procedure, :with_titre_identite) }
let(:dossier) { create(:dossier, procedure: procedure) } let(:dossier) { create(:dossier, procedure: procedure) }
let(:witness) { create(:dossier, procedure: procedure) }
let(:champ_identite) { dossier.champs.find { |c| c.type == 'Champs::TitreIdentiteChamp' } } let(:champ_identite) { dossier.champs.find { |c| c.type == 'Champs::TitreIdentiteChamp' } }
before { attach_file_to_champ(champ_identite) }
it "doesn't return sensitive documents like titre_identite" do
expect(champ_identite.piece_justificative_file).to be_attached
expect(subject).to be_empty
end
end
context 'with a pj on an commentaire' do
let(:dossier) { create(:dossier) }
let(:witness) { create(:dossier) }
let!(:commentaire) { create(:commentaire, :with_file, dossier: dossier) }
let!(:witness_commentaire) { create(:commentaire, :with_file, dossier: witness) }
it { expect(subject).to match_array(dossier.commentaires.first.piece_jointe.attachment) }
end
context 'with a motivation' do
let(:dossier) { create(:dossier, :with_justificatif) }
let!(:witness) { create(:dossier, :with_justificatif) }
it { expect(subject).to match_array(dossier.justificatif_motivation.attachment) }
end
context 'with an attestation' do
let(:dossier) { create(:dossier, :with_attestation) }
let!(:witness) { create(:dossier, :with_attestation) }
it { expect(subject).to match_array(dossier.attestation.pdf.attachment) }
end
context 'with an etablissement' do
let(:dossier) { create(:dossier, :with_entreprise) }
let(:attestation_sociale) { dossier.etablissement.entreprise_attestation_sociale }
let(:attestation_fiscale) { dossier.etablissement.entreprise_attestation_fiscale }
let!(:witness) { create(:dossier, :with_entreprise) }
let!(:witness_attestation_sociale) { witness.etablissement.entreprise_attestation_sociale }
let!(:witness_attestation_fiscale) { witness.etablissement.entreprise_attestation_fiscale }
before do
attach_file(attestation_sociale)
attach_file(attestation_fiscale)
end
it { expect(subject).to match_array([attestation_sociale.attachment, attestation_fiscale.attachment]) }
end
context 'with a bill' do
let(:dossier) { create(:dossier) }
let(:witness) { create(:dossier) }
let(:bill_signature) do let(:bill_signature) do
bs = build(:bill_signature, :with_serialized, :with_signature) bs = build(:bill_signature, :with_serialized, :with_signature)
bs.save(validate: false) bs.save(validate: false)
bs bs
end end
let(:witness_bill_signature) do
bs = build(:bill_signature, :with_serialized, :with_signature)
bs.save(validate: false)
bs
end
before do before do
champ_identite
.piece_justificative_file
.attach(io: StringIO.new("toto"), filename: "toto.png", content_type: "image/png")
create(:dossier_operation_log, dossier: dossier, bill_signature: bill_signature) create(:dossier_operation_log, dossier: dossier, bill_signature: bill_signature)
create(:dossier_operation_log, dossier: witness, bill_signature: witness_bill_signature)
end end
describe '.liste_documents' do let(:dossier_bs) { dossier.dossier_operation_logs.first.bill_signature }
subject { PiecesJustificativesService.liste_documents(dossier, false) }
it "doesn't return sensitive documents like titre_identite" do it "returns serialized bill and signature" do
expect(champ_identite.piece_justificative_file).to be_attached expect(subject).to match_array([dossier_bs.serialized.attachment, dossier_bs.signature.attachment])
expect(subject.any? { |piece| piece.name == 'piece_justificative_file' }).to be_falsy
end end
it "returns operation logs of the dossier" do context 'for expert' do
expect(subject.any? { |piece| piece.name == 'serialized' }).to be_truthy let(:for_expert) { true }
it { expect(subject).to be_empty }
end
end
context 'with a dol' do
let(:dossier) { create(:dossier) }
let(:witness) { create(:dossier) }
let(:dol) { create(:dossier_operation_log, dossier: dossier) }
let(:witness_dol) { create(:dossier_operation_log, dossier: witness) }
before do
attach_file(dol.serialized)
attach_file(witness_dol.serialized)
end
it { expect(subject).to match_array(dol.serialized.attachment) }
context 'for expert' do
let(:for_expert) { true }
it { expect(subject).to be_empty }
end
end end
end end
describe '.generate_dossier_export' do describe '.generate_dossier_export' do
let(:dossier) { create(:dossier) }
subject { PiecesJustificativesService.generate_dossier_export(dossier) } subject { PiecesJustificativesService.generate_dossier_export(dossier) }
it "generates pdf export for instructeur" do
subject
end
it "doesn't update dossier" do it "doesn't update dossier" do
before_export = Time.zone.now expect { subject }.not_to change { dossier.updated_at }
subject
expect(dossier.updated_at).to be <= before_export
end end
end end
def attach_file_to_champ(champ)
attach_file(champ.piece_justificative_file)
end
def attach_file(attachable)
attachable
.attach(io: StringIO.new("toto"), filename: "toto.png", content_type: "image/png")
end
end end

View file

@ -106,7 +106,7 @@ describe ProcedureArchiveService do
) )
end end
let(:documents) { [pj, bad_pj] } let(:documents) { [pj, bad_pj].map { |p| ActiveStorage::DownloadableFile.pj_and_path(dossier.id, p) } }
before do before do
allow(PiecesJustificativesService).to receive(:liste_documents).and_return(documents) allow(PiecesJustificativesService).to receive(:liste_documents).and_return(documents)
end end