From dfafc57bb98a757efaa22fdd9fec51a86042f7bb Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 7 Sep 2021 10:26:30 +0200 Subject: [PATCH 1/6] feat(dossier): add dossier_transfers and dossier_transfer_logs --- ...20210721140812_create_dossier_transfers.rb | 11 ++++++++++ ...0721162213_create_dossier_transfer_logs.rb | 11 ++++++++++ db/schema.rb | 22 ++++++++++++++++++- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20210721140812_create_dossier_transfers.rb create mode 100644 db/migrate/20210721162213_create_dossier_transfer_logs.rb diff --git a/db/migrate/20210721140812_create_dossier_transfers.rb b/db/migrate/20210721140812_create_dossier_transfers.rb new file mode 100644 index 000000000..f247319f2 --- /dev/null +++ b/db/migrate/20210721140812_create_dossier_transfers.rb @@ -0,0 +1,11 @@ +class CreateDossierTransfers < ActiveRecord::Migration[6.1] + def change + create_table :dossier_transfers do |t| + t.string :email, null: false, index: true + + t.timestamps + end + + add_reference :dossiers, :dossier_transfer, foreign_key: true, null: true, index: true + end +end diff --git a/db/migrate/20210721162213_create_dossier_transfer_logs.rb b/db/migrate/20210721162213_create_dossier_transfer_logs.rb new file mode 100644 index 000000000..5353ddc5b --- /dev/null +++ b/db/migrate/20210721162213_create_dossier_transfer_logs.rb @@ -0,0 +1,11 @@ +class CreateDossierTransferLogs < ActiveRecord::Migration[6.1] + def change + create_table :dossier_transfer_logs do |t| + t.string :from, null: false + t.string :to, null: false + t.references :dossier, foreign_key: true, null: false, index: true + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 8cc67d824..6a5f1c695 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2021_08_26_161956) do +ActiveRecord::Schema.define(version: 2021_08_27_161956) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -268,6 +268,22 @@ ActiveRecord::Schema.define(version: 2021_08_26_161956) do t.index ["keep_until"], name: "index_dossier_operation_logs_on_keep_until" end + create_table "dossier_transfer_logs", force: :cascade do |t| + t.string "from", null: false + t.string "to", null: false + t.bigint "dossier_id", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["dossier_id"], name: "index_dossier_transfer_logs_on_dossier_id" + end + + create_table "dossier_transfers", force: :cascade do |t| + t.string "email", null: false + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["email"], name: "index_dossier_transfers_on_email" + end + create_table "dossiers", id: :serial, force: :cascade do |t| t.boolean "autorisation_donnees" t.datetime "created_at" @@ -299,7 +315,9 @@ ActiveRecord::Schema.define(version: 2021_08_26_161956) do t.datetime "declarative_triggered_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.bigint "dossier_transfer_id" t.index ["archived"], name: "index_dossiers_on_archived" + t.index ["dossier_transfer_id"], name: "index_dossiers_on_dossier_transfer_id" t.index ["groupe_instructeur_id"], name: "index_dossiers_on_groupe_instructeur_id" t.index ["hidden_at"], name: "index_dossiers_on_hidden_at" t.index ["revision_id"], name: "index_dossiers_on_revision_id" @@ -797,6 +815,8 @@ ActiveRecord::Schema.define(version: 2021_08_26_161956) do add_foreign_key "commentaires", "experts" add_foreign_key "dossier_operation_logs", "bill_signatures" add_foreign_key "dossier_operation_logs", "instructeurs" + add_foreign_key "dossier_transfer_logs", "dossiers" + add_foreign_key "dossiers", "dossier_transfers" add_foreign_key "dossiers", "groupe_instructeurs" add_foreign_key "dossiers", "procedure_revisions", column: "revision_id" add_foreign_key "dossiers", "users" From d6cbdf2a48155b5ebd62f4344e33f5900b3d46ac Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 7 Sep 2021 10:36:09 +0200 Subject: [PATCH 2/6] feat(dossier): add dossier transfer models --- app/mailers/dossier_mailer.rb | 9 ++++ app/models/dossier.rb | 4 ++ app/models/dossier_transfer.rb | 53 +++++++++++++++++++ app/models/dossier_transfer_log.rb | 14 +++++ .../dossier_mailer/notify_transfer.html.haml | 9 ++++ .../dossier_mailer/notify_transfer/fr.yml | 12 +++++ .../previews/dossier_mailer_preview.rb | 8 +++ spec/models/dossier_transfer_spec.rb | 50 +++++++++++++++++ 8 files changed, 159 insertions(+) create mode 100644 app/models/dossier_transfer.rb create mode 100644 app/models/dossier_transfer_log.rb create mode 100644 app/views/dossier_mailer/notify_transfer.html.haml create mode 100644 config/locales/views/dossier_mailer/notify_transfer/fr.yml create mode 100644 spec/models/dossier_transfer_spec.rb diff --git a/app/mailers/dossier_mailer.rb b/app/mailers/dossier_mailer.rb index 4be072ee7..e21e28bfc 100644 --- a/app/mailers/dossier_mailer.rb +++ b/app/mailers/dossier_mailer.rb @@ -157,6 +157,15 @@ class DossierMailer < ApplicationMailer end end + def notify_transfer(transfer) + I18n.with_locale(transfer.user_locale) do + @subject = default_i18n_subject(count: transfer.dossiers.count) + @transfer = transfer + + mail(to: transfer.email, subject: @subject) + end + end + protected # This is an override of `default_i18n_subject` method diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 3a9d86f2a..09b92cbe5 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -27,6 +27,7 @@ # termine_close_to_expiration_notice_sent_at :datetime # created_at :datetime # updated_at :datetime +# dossier_transfer_id :bigint # groupe_instructeur_id :bigint # revision_id :bigint # user_id :integer @@ -122,6 +123,9 @@ class Dossier < ApplicationRecord has_many :types_de_champ, through: :revision has_many :types_de_champ_private, through: :revision + belongs_to :transfer, class_name: 'DossierTransfer', foreign_key: 'dossier_transfer_id', optional: true, inverse_of: :dossiers, dependent: :destroy + has_many :transfer_logs, class_name: 'DossierTransferLog', dependent: :destroy + accepts_nested_attributes_for :champs accepts_nested_attributes_for :champs_private diff --git a/app/models/dossier_transfer.rb b/app/models/dossier_transfer.rb new file mode 100644 index 000000000..731a0ab18 --- /dev/null +++ b/app/models/dossier_transfer.rb @@ -0,0 +1,53 @@ +# == Schema Information +# +# Table name: dossier_transfers +# +# id :bigint not null, primary key +# email :string not null +# created_at :datetime not null +# updated_at :datetime not null +# +class DossierTransfer < ApplicationRecord + has_many :dossiers, dependent: :nullify + + EXPIRATION_LIMIT = 2.weeks + + scope :pending, -> { where('created_at > ?', (Time.zone.now - EXPIRATION_LIMIT)) } + scope :stale, -> { where('created_at < ?', (Time.zone.now - EXPIRATION_LIMIT)) } + scope :with_dossiers, -> { joins(:dossiers) } + + after_create_commit :send_notification + + def self.initiate(email, dossiers) + create(email: email, dossiers: dossiers) + end + + def self.accept(id, current_user) + transfer = pending.find_by(id: id, email: current_user.email) + + if transfer && transfer.dossiers.present? + Invite + .where(dossier: transfer.dossiers, email: transfer.email) + .destroy_all + DossierTransferLog.create(transfer.dossiers.map do |dossier| + { + dossier: dossier, + from: dossier.user.email, + to: transfer.email + } + end) + transfer.dossiers.update_all(user_id: current_user.id) + transfer.destroy + end + end + + def user_locale + User.find_by(email: email)&.locale || I18n.default_locale + end + + private + + def send_notification + DossierMailer.notify_transfer(self).deliver_later + end +end diff --git a/app/models/dossier_transfer_log.rb b/app/models/dossier_transfer_log.rb new file mode 100644 index 000000000..f551b608f --- /dev/null +++ b/app/models/dossier_transfer_log.rb @@ -0,0 +1,14 @@ +# == Schema Information +# +# Table name: dossier_transfer_logs +# +# id :bigint not null, primary key +# from :string not null +# to :string not null +# created_at :datetime not null +# updated_at :datetime not null +# dossier_id :bigint not null +# +class DossierTransferLog < ApplicationRecord + belongs_to :dossier +end diff --git a/app/views/dossier_mailer/notify_transfer.html.haml b/app/views/dossier_mailer/notify_transfer.html.haml new file mode 100644 index 000000000..45887ad34 --- /dev/null +++ b/app/views/dossier_mailer/notify_transfer.html.haml @@ -0,0 +1,9 @@ +- content_for(:title, "#{@subject}") + +%p= t(:hello, scope: [:views, :shared, :greetings]) + +%p + = t('.body', count: @transfer.dossiers.count) + = link_to t('.transfer_link'), dossiers_url(statut: 'dossiers-transferes') + += render partial: "layouts/mailers/signature" diff --git a/config/locales/views/dossier_mailer/notify_transfer/fr.yml b/config/locales/views/dossier_mailer/notify_transfer/fr.yml new file mode 100644 index 000000000..884d1e4d5 --- /dev/null +++ b/config/locales/views/dossier_mailer/notify_transfer/fr.yml @@ -0,0 +1,12 @@ +fr: + dossier_mailer: + notify_transfer: + subject: + one: Une demande de transfert de dossier vous est adressée + other: Une demande de transfert de dossiers vous est adressée + transfer_link: demande de transfer + body: + one: | + Accéder à la demande de transfert du dossier en cliquant sur le lien suivant : + other: | + Accéder à la demande de transfert de %{count} dossiers en cliquant sur le lien suivant : diff --git a/spec/mailers/previews/dossier_mailer_preview.rb b/spec/mailers/previews/dossier_mailer_preview.rb index 85a7d6252..d39aac92e 100644 --- a/spec/mailers/previews/dossier_mailer_preview.rb +++ b/spec/mailers/previews/dossier_mailer_preview.rb @@ -69,6 +69,10 @@ class DossierMailerPreview < ActionMailer::Preview DossierMailer.notify_brouillon_not_submitted(draft) end + def notify_transfer + DossierMailer.notify_transfer(transfer) + end + private def usager_email @@ -111,4 +115,8 @@ class DossierMailerPreview < ActionMailer::Preview horaires: 'Du lundi au vendredi, de 9 h à 18 h' ) end + + def transfer + DossierTransfer.new(email: usager_email, dossiers: [dossier, dossier_accepte]) + end end diff --git a/spec/models/dossier_transfer_spec.rb b/spec/models/dossier_transfer_spec.rb new file mode 100644 index 000000000..161b03b11 --- /dev/null +++ b/spec/models/dossier_transfer_spec.rb @@ -0,0 +1,50 @@ +require 'rails_helper' + +RSpec.describe DossierTransfer, type: :model do + let(:user) { create(:user) } + let(:other_user) { create(:user) } + let(:dossier) { create(:dossier, user: user) } + + describe 'initiate' do + subject { DossierTransfer.initiate(other_user.email, [dossier]) } + + it 'should send transfer request' do + expect(subject.email).to eq(other_user.email) + expect(subject.dossiers).to eq([dossier]) + expect(dossier.transfer).to eq(subject) + expect(dossier.user).to eq(user) + expect(dossier.transfer_logs.count).to eq(0) + end + + describe 'accept' do + let(:transfer_log) { dossier.transfer_logs.first } + + before do + DossierTransfer.accept(subject.id, other_user) + dossier.reload + end + + it 'should transfer dossier' do + expect(DossierTransfer.count).to eq(0) + expect(dossier.transfer).to be_nil + expect(dossier.user).to eq(other_user) + expect(dossier.transfer_logs.count).to eq(1) + expect(transfer_log.dossier).to eq(dossier) + expect(transfer_log.from).to eq(user.email) + expect(transfer_log.to).to eq(other_user.email) + end + end + + describe 'with_dossiers' do + before { subject } + + it { expect(DossierTransfer.with_dossiers.count).to eq(1) } + + context "when dossier discarded" do + before { dossier.discard! } + + it { expect(DossierTransfer.with_dossiers.count).to eq(0) } + end + end + end +end From 46226b7930309ae6f914da244bdd5d0748a24d17 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 7 Sep 2021 15:33:25 +0200 Subject: [PATCH 3/6] fix(i18n): use size instead of count in dossier mailers --- app/mailers/dossier_mailer.rb | 14 +++++++------- ..._automatic_deletion_to_administration.html.haml | 2 +- .../notify_automatic_deletion_to_user.html.haml | 4 ++-- .../notify_brouillon_deletion.html.haml | 2 +- .../notify_brouillon_near_deletion.html.haml | 4 ++-- ...otify_near_deletion_to_administration.html.haml | 8 ++++---- .../notify_near_deletion_to_user.html.haml | 8 ++++---- app/views/dossier_mailer/notify_transfer.html.haml | 2 +- 8 files changed, 22 insertions(+), 22 deletions(-) diff --git a/app/mailers/dossier_mailer.rb b/app/mailers/dossier_mailer.rb index e21e28bfc..fb3dc35a6 100644 --- a/app/mailers/dossier_mailer.rb +++ b/app/mailers/dossier_mailer.rb @@ -67,7 +67,7 @@ class DossierMailer < ApplicationMailer def notify_brouillon_near_deletion(dossiers, to_email) I18n.with_locale(dossiers.first.user_locale) do - @subject = default_i18n_subject(count: dossiers.count) + @subject = default_i18n_subject(count: dossiers.size) @dossiers = dossiers mail(to: to_email, subject: @subject) @@ -75,7 +75,7 @@ class DossierMailer < ApplicationMailer end def notify_brouillon_deletion(dossier_hashes, to_email) - @subject = default_i18n_subject(count: dossier_hashes.count) + @subject = default_i18n_subject(count: dossier_hashes.size) @dossier_hashes = dossier_hashes mail(to: to_email, subject: @subject) @@ -109,7 +109,7 @@ class DossierMailer < ApplicationMailer def notify_automatic_deletion_to_user(deleted_dossiers, to_email) I18n.with_locale(deleted_dossiers.first.user_locale) do @state = deleted_dossiers.first.state - @subject = default_i18n_subject(count: deleted_dossiers.count) + @subject = default_i18n_subject(count: deleted_dossiers.size) @deleted_dossiers = deleted_dossiers mail(to: to_email, subject: @subject) @@ -117,7 +117,7 @@ class DossierMailer < ApplicationMailer end def notify_automatic_deletion_to_administration(deleted_dossiers, to_email) - @subject = default_i18n_subject(count: deleted_dossiers.count) + @subject = default_i18n_subject(count: deleted_dossiers.size) @deleted_dossiers = deleted_dossiers mail(to: to_email, subject: @subject) @@ -126,7 +126,7 @@ class DossierMailer < ApplicationMailer def notify_near_deletion_to_user(dossiers, to_email) I18n.with_locale(dossiers.first.user_locale) do @state = dossiers.first.state - @subject = default_i18n_subject(count: dossiers.count, state: @state) + @subject = default_i18n_subject(count: dossiers.size, state: @state) @dossiers = dossiers mail(to: to_email, subject: @subject) @@ -135,7 +135,7 @@ class DossierMailer < ApplicationMailer def notify_near_deletion_to_administration(dossiers, to_email) @state = dossiers.first.state - @subject = default_i18n_subject(count: dossiers.count, state: @state) + @subject = default_i18n_subject(count: dossiers.size, state: @state) @dossiers = dossiers mail(to: to_email, subject: @subject) @@ -159,7 +159,7 @@ class DossierMailer < ApplicationMailer def notify_transfer(transfer) I18n.with_locale(transfer.user_locale) do - @subject = default_i18n_subject(count: transfer.dossiers.count) + @subject = default_i18n_subject(count: transfer.dossiers.size) @transfer = transfer mail(to: transfer.email, subject: @subject) diff --git a/app/views/dossier_mailer/notify_automatic_deletion_to_administration.html.haml b/app/views/dossier_mailer/notify_automatic_deletion_to_administration.html.haml index 286f30134..7d2cb3e91 100644 --- a/app/views/dossier_mailer/notify_automatic_deletion_to_administration.html.haml +++ b/app/views/dossier_mailer/notify_automatic_deletion_to_administration.html.haml @@ -3,7 +3,7 @@ %p= t(:hello, scope: [:views, :shared, :greetings]) %p - = t('.header', count: @deleted_dossiers.count) + = t('.header', count: @deleted_dossiers.size) %ul - @deleted_dossiers.each do |d| %li n° #{d.dossier_id} (#{d.procedure.libelle}) diff --git a/app/views/dossier_mailer/notify_automatic_deletion_to_user.html.haml b/app/views/dossier_mailer/notify_automatic_deletion_to_user.html.haml index afbfc3fac..8dcb3ea1a 100644 --- a/app/views/dossier_mailer/notify_automatic_deletion_to_user.html.haml +++ b/app/views/dossier_mailer/notify_automatic_deletion_to_user.html.haml @@ -3,12 +3,12 @@ %p= t(:hello, scope: [:views, :shared, :greetings]) %p - = t('.header', count: @deleted_dossiers.count) + = t('.header', count: @deleted_dossiers.size) %ul - @deleted_dossiers.each do |d| %li n° #{d.dossier_id} (#{d.procedure.libelle}) - if @state == Dossier.states.fetch(:en_construction) - %p= t('.footer_en_construction', count: @deleted_dossiers.count) + %p= t('.footer_en_construction', count: @deleted_dossiers.size) = render partial: "layouts/mailers/signature" diff --git a/app/views/dossier_mailer/notify_brouillon_deletion.html.haml b/app/views/dossier_mailer/notify_brouillon_deletion.html.haml index 4461c9c98..a6b1312d1 100644 --- a/app/views/dossier_mailer/notify_brouillon_deletion.html.haml +++ b/app/views/dossier_mailer/notify_brouillon_deletion.html.haml @@ -3,7 +3,7 @@ %p= t(:hello, scope: [:views, :shared, :greetings]) %p - = t('.header', count: @dossier_hashes.count) + = t('.header', count: @dossier_hashes.size) %ul - @dossier_hashes.each do |d| %li n° #{d[:id]} (#{d[:procedure_libelle]}) diff --git a/app/views/dossier_mailer/notify_brouillon_near_deletion.html.haml b/app/views/dossier_mailer/notify_brouillon_near_deletion.html.haml index 450abf898..2b358eab6 100644 --- a/app/views/dossier_mailer/notify_brouillon_near_deletion.html.haml +++ b/app/views/dossier_mailer/notify_brouillon_near_deletion.html.haml @@ -3,11 +3,11 @@ %p= t(:hello, scope: [:views, :shared, :greetings]) %p - = t('.header', count: @dossiers.count) + = t('.header', count: @dossiers.size) %ul - @dossiers.each do |d| %li= link_to("n° #{d.id} (#{d.procedure.libelle})", dossier_url(d)) -%p= sanitize(t('.footer', count: @dossiers.count)) +%p= sanitize(t('.footer', count: @dossiers.size)) = render partial: "layouts/mailers/signature" diff --git a/app/views/dossier_mailer/notify_near_deletion_to_administration.html.haml b/app/views/dossier_mailer/notify_near_deletion_to_administration.html.haml index ff752e469..419aa8e91 100644 --- a/app/views/dossier_mailer/notify_near_deletion_to_administration.html.haml +++ b/app/views/dossier_mailer/notify_near_deletion_to_administration.html.haml @@ -4,9 +4,9 @@ %p - if @state == Dossier.states.fetch(:en_construction) - = t('.header_en_construction', count: @dossiers.count) + = t('.header_en_construction', count: @dossiers.size) - else - = t('.header_termine', count: @dossiers.count) + = t('.header_termine', count: @dossiers.size) %ul - @dossiers.each do |d| %li @@ -14,8 +14,8 @@ %p - if @state == Dossier.states.fetch(:en_construction) - = sanitize(t('.footer_en_construction', count: @dossiers.count)) + = sanitize(t('.footer_en_construction', count: @dossiers.size)) - else - = sanitize(t('.footer_termine', count: @dossiers.count)) + = sanitize(t('.footer_termine', count: @dossiers.size)) = render partial: "layouts/mailers/signature" diff --git a/app/views/dossier_mailer/notify_near_deletion_to_user.html.haml b/app/views/dossier_mailer/notify_near_deletion_to_user.html.haml index d888955fe..f0d0c4c03 100644 --- a/app/views/dossier_mailer/notify_near_deletion_to_user.html.haml +++ b/app/views/dossier_mailer/notify_near_deletion_to_user.html.haml @@ -4,18 +4,18 @@ %p - if @state == Dossier.states.fetch(:en_construction) - = t('.header_en_construction', count: @dossiers.count) + = t('.header_en_construction', count: @dossiers.size) - else - = t('.header_termine', count: @dossiers.count) + = t('.header_termine', count: @dossiers.size) %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)) + = sanitize(t('.footer', count: @dossiers.size)) %p - if @state == Dossier.states.fetch(:en_construction) - = sanitize(t('.footer_en_construction', count: @dossiers.count)) + = sanitize(t('.footer_en_construction', count: @dossiers.size)) = render partial: "layouts/mailers/signature" diff --git a/app/views/dossier_mailer/notify_transfer.html.haml b/app/views/dossier_mailer/notify_transfer.html.haml index 45887ad34..1f322afba 100644 --- a/app/views/dossier_mailer/notify_transfer.html.haml +++ b/app/views/dossier_mailer/notify_transfer.html.haml @@ -3,7 +3,7 @@ %p= t(:hello, scope: [:views, :shared, :greetings]) %p - = t('.body', count: @transfer.dossiers.count) + = t('.body', count: @transfer.dossiers.size) = link_to t('.transfer_link'), dossiers_url(statut: 'dossiers-transferes') = render partial: "layouts/mailers/signature" From 5985755229588ee5af8201023f67988084b13e53 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 7 Sep 2021 10:36:49 +0200 Subject: [PATCH 4/6] feat(dossier): add purge stale dossier transfers job --- app/jobs/cron/purge_stale_transfers_job.rb | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 app/jobs/cron/purge_stale_transfers_job.rb diff --git a/app/jobs/cron/purge_stale_transfers_job.rb b/app/jobs/cron/purge_stale_transfers_job.rb new file mode 100644 index 000000000..c59609506 --- /dev/null +++ b/app/jobs/cron/purge_stale_transfers_job.rb @@ -0,0 +1,7 @@ +class Cron::PurgeStaleTransfersJob < Cron::CronJob + self.schedule_expression = "every day at midnight" + + def perform + DossierTransfer.stale.destroy_all + end +end From 3235f42a63541c6238bf0bac4d09d61b54440016 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Tue, 7 Sep 2021 10:37:42 +0200 Subject: [PATCH 5/6] feat(dossier): add dossier transfers UI --- app/controllers/users/dossiers_controller.rb | 22 ++++++++++--- app/controllers/users/transfers_controller.rb | 31 ++++++++++++++++++ .../users/dossiers/_dossier_actions.html.haml | 14 ++++++-- .../_transfered_dossiers_list.html.haml | 32 +++++++++++++++++++ app/views/users/dossiers/index.html.haml | 8 +++++ app/views/users/dossiers/transferer.html.haml | 9 ++++++ .../users/dossiers/transferer_all.html.haml | 7 ++++ config/locales/en.yml | 5 +++ config/locales/fr.yml | 5 +++ config/routes.rb | 4 +++ .../_dossier_actions.html.haml_spec.rb | 5 ++- .../users/dossiers/index.html.haml_spec.rb | 1 + 12 files changed, 135 insertions(+), 8 deletions(-) create mode 100644 app/controllers/users/transfers_controller.rb create mode 100644 app/views/users/dossiers/_transfered_dossiers_list.html.haml create mode 100644 app/views/users/dossiers/transferer.html.haml create mode 100644 app/views/users/dossiers/transferer_all.html.haml diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index 89576215c..e1bc3af5d 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -4,7 +4,7 @@ module Users layout 'procedure_context', only: [:identite, :update_identite, :siret, :update_siret] - ACTIONS_ALLOWED_TO_ANY_USER = [:index, :recherche, :new] + ACTIONS_ALLOWED_TO_ANY_USER = [:index, :recherche, :new, :transferer_all] ACTIONS_ALLOWED_TO_OWNER_OR_INVITE = [:show, :demande, :messagerie, :brouillon, :update_brouillon, :modifier, :update, :create_commentaire] before_action :ensure_ownership!, except: ACTIONS_ALLOWED_TO_ANY_USER + ACTIONS_ALLOWED_TO_OWNER_OR_INVITE @@ -19,7 +19,12 @@ module Users @user_dossiers = current_user.dossiers.includes(:procedure).order_by_updated_at.page(page) @dossiers_invites = current_user.dossiers_invites.includes(:procedure).order_by_updated_at.page(page) @dossiers_supprimes = current_user.deleted_dossiers.order_by_updated_at.page(page) - @statut = statut(@user_dossiers, @dossiers_invites, @dossiers_supprimes, params[:statut]) + @dossier_transfers = DossierTransfer + .includes(dossiers: :user) + .with_dossiers + .where(email: current_user.email) + .page(page) + @statut = statut(@user_dossiers, @dossiers_invites, @dossiers_supprimes, @dossier_transfers, params[:statut]) end def show @@ -273,16 +278,25 @@ module Users @dossier || (dossier_id.present? && Dossier.find_by(id: dossier_id.to_i)) end + def transferer + @transfer = DossierTransfer.new(dossiers: [dossier]) + end + + def transferer_all + @transfer = DossierTransfer.new(dossiers: current_user.dossiers) + end + private # if the status tab is filled, then this tab # else first filled tab # else mes-dossiers - def statut(mes_dossiers, dossiers_invites, dossiers_supprimes, params_statut) + def statut(mes_dossiers, dossiers_invites, dossiers_supprimes, dossier_transfers, params_statut) tabs = { 'mes-dossiers' => mes_dossiers.present?, 'dossiers-invites' => dossiers_invites.present?, - 'dossiers-supprimes' => dossiers_supprimes.present? + 'dossiers-supprimes' => dossiers_supprimes.present?, + 'dossiers-transferes' => dossier_transfers.present? } if tabs[params_statut] params_statut diff --git a/app/controllers/users/transfers_controller.rb b/app/controllers/users/transfers_controller.rb new file mode 100644 index 000000000..49d7f245b --- /dev/null +++ b/app/controllers/users/transfers_controller.rb @@ -0,0 +1,31 @@ +module Users + class TransfersController < UserController + def create + transfer = DossierTransfer.new(transfer_params) + transfer.save! + redirect_to dossiers_path + end + + def update + DossierTransfer.accept(params[:id], current_user) + redirect_to dossiers_path + end + + def destroy + transfer = DossierTransfer.find_by!(id: params[:id], dossiers: { user: current_user }) + transfer.destroy + redirect_to dossiers_path + end + + private + + def transfer_params + transfer_params = params.require(:dossier_transfer).permit(:email, :dossiers) + if transfer_params[:dossiers].present? + transfer_params.merge(dossiers: [current_user.dossiers.find(transfer_params[:dossiers])]) + else + transfer_params.merge(dossiers: current_user.dossiers) + end + end + end +end diff --git a/app/views/users/dossiers/_dossier_actions.html.haml b/app/views/users/dossiers/_dossier_actions.html.haml index 1b5ee4b89..27f39af37 100644 --- a/app/views/users/dossiers/_dossier_actions.html.haml +++ b/app/views/users/dossiers/_dossier_actions.html.haml @@ -1,7 +1,8 @@ +- has_edit_action = !dossier.read_only? - has_delete_action = dossier.can_be_deleted_by_user? - has_new_dossier_action = dossier.procedure.accepts_new_dossiers? - -- has_actions = has_delete_action || has_new_dossier_action +- has_transfer_action = dossier.user == current_user +- has_actions = has_edit_action || has_delete_action || has_new_dossier_action || has_transfer_action - if has_actions .dropdown.user-dossier-actions @@ -9,7 +10,7 @@ = t('views.users.dossiers.dossier_action.actions') #actions-menu.dropdown-content.fade-in-down %ul.dropdown-items - - if !dossier.read_only? + - if has_edit_action - if dossier.brouillon? %li = link_to(url_for_dossier(dossier)) do @@ -23,6 +24,13 @@ .dropdown-description = t('views.users.dossiers.dossier_action.edit_dossier') + - if has_transfer_action + %li + = link_to transferer_dossier_path(dossier) do + %span.icon.person + .dropdown-description + = t('views.users.dossiers.dossier_action.transfer_dossier') + - if has_new_dossier_action %li = link_to procedure_lien(dossier.procedure) do diff --git a/app/views/users/dossiers/_transfered_dossiers_list.html.haml b/app/views/users/dossiers/_transfered_dossiers_list.html.haml new file mode 100644 index 000000000..9e6503bf3 --- /dev/null +++ b/app/views/users/dossiers/_transfered_dossiers_list.html.haml @@ -0,0 +1,32 @@ +- if dossier_transfers.present? + %ul.dossiers-transfers.mb-2 + - dossier_transfers.each do |transfer| + %li.mb-4 + .transfer-details.mb-2 + Demande de transfert Nº #{transfer.id} envoyé par #{transfer.dossiers.first.user.email} + %table.table.dossiers-table.hoverable + %thead + %tr + %th.number-col= t('views.users.dossiers.dossiers_list.n_dossier') + %th= t('views.users.dossiers.dossiers_list.procedure') + %th= t('views.users.dossiers.dossiers_list.status') + %th Date de dépot + %tbody + - transfer.dossiers.each do |dossier| + %tr{ data: { 'transfer-id': transfer.id } } + %td.number-col + %span.icon.folder + = dossier.id + %td= dossier.procedure.libelle + %td= status_badge(dossier.state) + %td{ style: 'padding: 18px;' }= (dossier.en_construction_at || dossier.created_at).strftime('%d/%m/%Y') + + .transfer-actions.mt-4 + = link_to "Accepter", transfer_path(transfer), class: "button primary", method: :put + = link_to "Rejeter", transfer_path(transfer), class: "button danger", method: :delete + + = paginate(dossier_transfers) + +- else + .blank-tab + %h2.empty-text Aucune demande de transfert de dossiers ne vous a été adressée. diff --git a/app/views/users/dossiers/index.html.haml b/app/views/users/dossiers/index.html.haml index 06762236e..49684211b 100644 --- a/app/views/users/dossiers/index.html.haml +++ b/app/views/users/dossiers/index.html.haml @@ -33,6 +33,11 @@ active: @statut == 'dossiers-supprimes', badge: number_with_html_delimiter(@dossiers_supprimes.count)) + - if @dossier_transfers.count > 0 + = tab_item(t('pluralize.dossiers_transferes', count: @dossier_transfers.count), + dossiers_path(statut: 'dossiers-transferes'), + active: @statut == 'dossiers-transferes', + badge: number_with_html_delimiter(@dossier_transfers.count)) .container - if @statut == "mes-dossiers" @@ -43,3 +48,6 @@ - if @statut == "dossiers-supprimes" = render partial: "deleted_dossiers_list", locals: { deleted_dossiers: @dossiers_supprimes } + + - if @statut == "dossiers-transferes" + = render partial: "transfered_dossiers_list", locals: { dossier_transfers: @dossier_transfers } diff --git a/app/views/users/dossiers/transferer.html.haml b/app/views/users/dossiers/transferer.html.haml new file mode 100644 index 000000000..6966f85c2 --- /dev/null +++ b/app/views/users/dossiers/transferer.html.haml @@ -0,0 +1,9 @@ +.container.mt-4 + - dossier = @transfer.dossiers.first + Transferer le dossier #{dossier_display_state(dossier.state, lower: true)} nº #{@dossier.id} déposé par #{demandeur_dossier(dossier)} le #{try_format_date(dossier.created_at)} vers le compte d‘un autre usager : + + = form_for @transfer, url: transfers_path, html: { class: 'form mt-2' } do |f| + = f.label :email, 'Email du compte destinataire' + = f.email_field :email + = f.hidden_field :dossiers, value: dossier.id + = f.submit "Envoyer la demande de transfert", class: 'button primary' diff --git a/app/views/users/dossiers/transferer_all.html.haml b/app/views/users/dossiers/transferer_all.html.haml new file mode 100644 index 000000000..229a6255e --- /dev/null +++ b/app/views/users/dossiers/transferer_all.html.haml @@ -0,0 +1,7 @@ +.container.mt-4 + Transferer les #{@transfer.dossiers.size} dossiers de votre compte vers le compte d‘un autre usager : + + = form_for @transfer, url: transfers_path, html: { class: 'form mt-2' } do |f| + = f.label :email, 'Email du compte destinataire' + = f.email_field :email + = f.submit "Envoyer la demande de transfert", class: 'button primary' diff --git a/config/locales/en.yml b/config/locales/en.yml index 8597383b1..1a82721e4 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -190,6 +190,7 @@ en: edit_dossier: "Edit the file" start_other_dossier: "Start an other file" delete_dossier: "Delete the file" + transfer_dossier: "Transfer the file" edit_draft: "Edit the draft" actions: "Actions" sessions: @@ -328,6 +329,10 @@ en: zero: deleted file one: deleted file other: deleted files + dossiers_transferes: + zero: transfer request + one: transfer request + other: transfer requests dossier_trouve: zero: 0 file found one: 1 file found diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 4d8bc3e5a..6b28eddc0 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -182,6 +182,7 @@ fr: edit_dossier: "Modifier le dossier" start_other_dossier: "Commencer un autre dossier" delete_dossier: "Supprimer le dossier" + transfer_dossier: "Transferer le dossier" edit_draft: "Modifier le brouillon" actions: "Actions" sessions: @@ -332,6 +333,10 @@ fr: zero: dossier supprimé one: dossier supprimé other: dossiers supprimés + dossiers_transferes: + zero: demande de transfert + one: demande de transfert + other: demandes de transfert dossier_trouve: zero: 0 dossier trouvé one: 1 dossier trouvé diff --git a/config/routes.rb b/config/routes.rb index f1cc0e1a7..377c1c9a2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -264,12 +264,16 @@ Rails.application.routes.draw do post 'commentaire' => 'dossiers#create_commentaire' post 'ask_deletion' get 'attestation' + get 'transferer', to: 'dossiers#transferer' end collection do + get 'transferer', to: 'dossiers#transferer_all' get 'recherche' + resources :transfers, only: [:create, :update, :destroy] end end + resource :feedback, only: [:create] get 'demarches' => 'demarches#index' diff --git a/spec/views/users/dossiers/_dossier_actions.html.haml_spec.rb b/spec/views/users/dossiers/_dossier_actions.html.haml_spec.rb index 73a0f57a4..c4512b41e 100644 --- a/spec/views/users/dossiers/_dossier_actions.html.haml_spec.rb +++ b/spec/views/users/dossiers/_dossier_actions.html.haml_spec.rb @@ -1,11 +1,13 @@ describe 'users/dossiers/dossier_actions.html.haml', type: :view do let(:procedure) { create(:procedure, :published) } let(:dossier) { create(:dossier, :en_construction, procedure: procedure) } + let(:user) { dossier.user } - subject { render 'users/dossiers/dossier_actions.html.haml', dossier: dossier } + subject { render 'users/dossiers/dossier_actions.html.haml', dossier: dossier, current_user: user } it { is_expected.to have_link('Commencer un autre dossier', href: commencer_url(path: procedure.path)) } it { is_expected.to have_link('Supprimer le dossier', href: ask_deletion_dossier_path(dossier)) } + it { is_expected.to have_link('Transferer le dossier', href: transferer_dossier_path(dossier)) } context 'when the dossier cannot be deleted' do let(:dossier) { create(:dossier, :accepte, procedure: procedure) } @@ -20,6 +22,7 @@ describe 'users/dossiers/dossier_actions.html.haml', type: :view do context 'when there are no actions to display' do let(:procedure) { create(:procedure, :closed) } let(:dossier) { create(:dossier, :accepte, procedure: procedure) } + let(:user) { create(:user) } it 'doesn’t render the menu at all' do expect(subject).not_to have_selector('.dropdown') diff --git a/spec/views/users/dossiers/index.html.haml_spec.rb b/spec/views/users/dossiers/index.html.haml_spec.rb index a43e546a9..a62fba1bd 100644 --- a/spec/views/users/dossiers/index.html.haml_spec.rb +++ b/spec/views/users/dossiers/index.html.haml_spec.rb @@ -12,6 +12,7 @@ describe 'users/dossiers/index.html.haml', type: :view do assign(:user_dossiers, Kaminari.paginate_array(user_dossiers).page(1)) assign(:dossiers_invites, Kaminari.paginate_array(dossiers_invites).page(1)) assign(:dossiers_supprimes, Kaminari.paginate_array(user_dossiers).page(1)) + assign(:dossier_transfers, Kaminari.paginate_array([]).page(1)) assign(:statut, statut) render end From 00c7c7c41979565eb26bacc05ada1869afbef207 Mon Sep 17 00:00:00 2001 From: Paul Chavard Date: Wed, 8 Sep 2021 15:49:04 +0200 Subject: [PATCH 6/6] test(dossier): test dossier transfer --- spec/features/users/transfer_dossier_spec.rb | 32 ++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 spec/features/users/transfer_dossier_spec.rb diff --git a/spec/features/users/transfer_dossier_spec.rb b/spec/features/users/transfer_dossier_spec.rb new file mode 100644 index 000000000..622c4a681 --- /dev/null +++ b/spec/features/users/transfer_dossier_spec.rb @@ -0,0 +1,32 @@ +describe 'Transfer dossier:' do + let(:user) { create(:user) } + let(:other_user) { create(:user) } + let(:procedure) { create(:simple_procedure) } + let(:dossier) { create(:dossier, :en_construction, :with_individual, :with_commentaires, user: user, procedure: procedure) } + + before do + dossier + login_as user, scope: :user + visit dossiers_path + end + + scenario 'the user can transfer dossier to another user' do + within(:css, "tr[data-dossier-id=\"#{dossier.id}\"]") do + click_on 'Actions' + click_on 'Transferer le dossier' + end + + expect(page).to have_current_path(transferer_dossier_path(dossier)) + expect(page).to have_content("Transferer le dossier en construction nº #{dossier.id}") + fill_in 'Email du compte destinataire', with: other_user.email + click_on 'Envoyer la demande de transfert' + + logout + login_as other_user, scope: :user + visit dossiers_path + + expect(page).to have_content("Demande de transfert Nº #{dossier.reload.transfer.id} envoyé par #{user.email}") + click_on 'Accepter' + expect(page).to have_current_path(dossiers_path) + end +end