diff --git a/app/mailers/instructeur_mailer.rb b/app/mailers/instructeur_mailer.rb index 89c58864c..55332cf38 100644 --- a/app/mailers/instructeur_mailer.rb +++ b/app/mailers/instructeur_mailer.rb @@ -1,5 +1,7 @@ # Preview all emails at http://localhost:3000/rails/mailers/instructeur_mailer class InstructeurMailer < ApplicationMailer + helper MailerHelper + layout 'mailers/layout' def user_to_instructeur(email) @@ -42,4 +44,12 @@ class InstructeurMailer < ApplicationMailer mail(to: instructeur.email, subject: subject) end + + def send_archive(instructeur, procedure, archive) + @archive = archive + @procedure = procedure + subject = "Votre archive est disponible" + + mail(to: instructeur.email, subject: subject) + end end diff --git a/app/models/archive.rb b/app/models/archive.rb index 7be847b3a..082176066 100644 --- a/app/models/archive.rb +++ b/app/models/archive.rb @@ -48,4 +48,12 @@ class Archive < ApplicationRecord def available? status == 'generated' && file.attached? end + + def filename(procedure) + if content_type == 'everything' + "procedure-#{procedure.id}.zip" + else + "procedure-#{procedure.id}-mois-#{I18n.l(month, format: '%Y-%m')}.zip" + end + end end diff --git a/app/models/dossier.rb b/app/models/dossier.rb index a0add0355..8b8a1e60e 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -172,7 +172,12 @@ class Dossier < ApplicationRecord scope :en_construction, -> { not_archived.state_en_construction } scope :en_instruction, -> { not_archived.state_en_instruction } scope :termine, -> { not_archived.state_termine } - scope :downloadable_sorted, -> { + scope :processed_in_month, -> (month) do + state_termine + .joins(:traitements) + .where(traitements: { processed_at: month.beginning_of_month..month.end_of_month }) + end + scope :downloadable_sorted, -> { state_not_brouillon .includes( :user, diff --git a/app/services/procedure_archive_service.rb b/app/services/procedure_archive_service.rb new file mode 100644 index 000000000..66862ff1d --- /dev/null +++ b/app/services/procedure_archive_service.rb @@ -0,0 +1,74 @@ +require 'tempfile' + +class ProcedureArchiveService + def initialize(procedure) + @procedure = procedure + end + + def create_archive(instructeur, type, month = nil) + groupe_instructeurs = instructeur + .groupe_instructeurs + .where(procedure: @procedure) + + if type == 'everything' + dossiers = @procedure.dossiers.state_termine + else + dossiers = @procedure.dossiers.processed_in_month(month) + end + + files = create_list_of_attachments(dossiers) + + archive = Archive.create!( + content_type: type, + month: month, + groupe_instructeurs: groupe_instructeurs + ) + + tmp_file = Tempfile.new(['tc', '.zip']) + + Zip::OutputStream.open(tmp_file) do |zipfile| + files.each do |attachment, pj_filename| + zipfile.put_next_entry(pj_filename) + zipfile.puts(attachment.download) + end + end + + archive.file.attach(io: File.open(tmp_file), filename: archive.filename(@procedure)) + tmp_file.delete + archive.make_available! + InstructeurMailer.send_archive(instructeur, @procedure, archive).deliver_later + end + + def self.procedure_files_size(procedure) + dossiers_files_size(procedure.dossiers) + end + + def self.dossiers_files_size(dossiers) + dossiers.map do |dossier| + liste_pieces_justificatives_for_archive(dossier).sum(&:byte_size) + end.sum + end + + private + + def create_list_of_attachments(dossiers) + dossiers.flat_map do |dossier| + ActiveStorage::DownloadableFile.create_list_from_dossier(dossier) + end + end + + def self.attachments_from_champs_piece_justificative(champs) + champs + .filter { |c| c.type_champ == TypeDeChamp.type_champs.fetch(:piece_justificative) } + .filter { |pj| pj.piece_justificative_file.attached? } + .map(&:piece_justificative_file) + end + + def self.liste_pieces_justificatives_for_archive(dossier) + champs_blocs_repetables = dossier.champs + .filter { |c| c.type_champ == TypeDeChamp.type_champs.fetch(:repetition) } + .flat_map(&:champs) + + attachments_from_champs_piece_justificative(champs_blocs_repetables + dossier.champs) + end +end diff --git a/app/views/instructeur_mailer/send_archive.html.haml b/app/views/instructeur_mailer/send_archive.html.haml new file mode 100644 index 000000000..11857c69f --- /dev/null +++ b/app/views/instructeur_mailer/send_archive.html.haml @@ -0,0 +1,21 @@ +- content_for(:title, 'Votre archive est disponible') + +%p + Bonjour, + +%p + Votre archive pour la démarche + = link_to("#{@procedure.id} − #{@procedure.libelle}", instructeur_procedure_url(@procedure.id)) + est disponible. + Vous pouvez la télécharger dans votre espace de gestion des archives. + +%p + = round_button('Consulter mes archives', instructeur_archives_url(@procedure), :primary) + +%p + Ce fichier est + %b valide une semaine + et peut-être téléchargé + %b plusieurs fois. + += render partial: "layouts/mailers/signature" diff --git a/spec/services/procedure_archive_service_spec.rb b/spec/services/procedure_archive_service_spec.rb new file mode 100644 index 000000000..48dd2ba67 --- /dev/null +++ b/spec/services/procedure_archive_service_spec.rb @@ -0,0 +1,63 @@ +describe ProcedureArchiveService do + describe '#create_archive' do + let(:procedure) { create(:procedure, :published) } + let(:instructeur) { create(:instructeur) } + let(:service) { ProcedureArchiveService.new(procedure) } + let(:year) { 2020 } + let(:month) { 3 } + let(:date_month) { Date.strptime("#{year}-#{month}", "%Y-%m") } + + before do + create_dossier_for_month(year, month) + create_dossier_for_month(2020, month) + end + + after { Timecop.return } + + context 'for a specific month' do + let(:year) { 2021 } + let(:mailer) { double('mailer', deliver_later: true) } + + it 'creates a monthly archive' do + expect(InstructeurMailer).to receive(:send_archive).and_return(mailer) + + service.create_archive(instructeur, 'monthly', date_month) + + archive = Archive.last + archive.file.open do |f| + files = ZipTricks::FileReader.read_zip_structure(io: f) + expect(files.size).to be 2 + expect(files.first.filename).to include("export") + expect(files.last.filename).to include("attestation") + end + expect(archive.content_type).to eq 'monthly' + expect(archive.file.attached?).to be_truthy + end + end + + context 'for all months' do + let(:mailer) { double('mailer', deliver_later: true) } + + it 'creates a everything archive' do + expect(InstructeurMailer).to receive(:send_archive).and_return(mailer) + + service.create_archive(instructeur, 'everything') + + archive = Archive.last + archive.file.open do |f| + files = ZipTricks::FileReader.read_zip_structure(io: f) + expect(files.size).to be 4 + end + expect(archive.content_type).to eq 'everything' + expect(archive.file.attached?).to be_truthy + end + end + end + + private + + def create_dossier_for_month(year, month) + Timecop.freeze(Time.zone.local(year, month, 5)) + create(:dossier, :accepte, :with_attestation, procedure: procedure) + end +end