Skip to content

Commit

Permalink
feat(dossier): enable dossier update by stream
Browse files Browse the repository at this point in the history
  • Loading branch information
tchak committed Jan 25, 2025
1 parent b904e9e commit 84eb1ac
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 17 deletions.
1 change: 1 addition & 0 deletions app/controllers/attachments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def champ?

def champ
@champ ||= if champ?
record.dossier.with_update_stream(current_user)
record.dossier.champ_for_update(record.type_de_champ, row_id: record.row_id, updated_by: current_user.email)
end
end
Expand Down
1 change: 1 addition & 0 deletions app/controllers/champs/champ_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Champs::ChampController < ApplicationController

def find_champ
dossier = policy_scope(Dossier).includes(:champs, revision: [:types_de_champ]).find(params[:dossier_id])
dossier.with_update_stream(current_user)
type_de_champ = dossier.find_type_de_champ_by_stable_id(params[:stable_id])
if type_de_champ.repetition?
dossier.project_champ(type_de_champ)
Expand Down
48 changes: 39 additions & 9 deletions app/controllers/users/dossiers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class DossiersController < UserController
before_action :ensure_dossier_can_be_viewed, only: [:show]
before_action :ensure_editing_brouillon, only: [:brouillon]
before_action :forbid_closed_submission!, only: [:submit_brouillon]
before_action :set_dossier_as_editing_fork, only: [:submit_en_construction]
before_action :set_dossier_as_editing_fork, only: [:submit_en_construction], if: :update_with_fork?
before_action :set_dossier_stream, only: [:modifier, :update, :submit_en_construction, :champ], if: :update_with_stream?
before_action :show_demarche_en_test_banner
before_action :store_user_location!, only: :new

Expand Down Expand Up @@ -253,32 +254,48 @@ def extend_conservation_and_restore

def modifier
@dossier = dossier_with_champs
@dossier_for_editing = dossier.owner_editing_fork

if update_with_stream?
@dossier_for_editing = dossier
else
# TODO remove when all forks are gone
@dossier_for_editing = dossier.owner_editing_fork
end
end

def submit_en_construction
@dossier = dossier_with_champs(pj_template: false)
editing_fork_origin = @dossier.editing_fork_origin
editing_fork_origin = dossier.editing_fork_origin
dossier_en_construction = editing_fork_origin || dossier

if cast_bool(params.dig(:dossier, :pending_correction))
editing_fork_origin.resolve_pending_correction
dossier_en_construction.resolve_pending_correction
end

submit_dossier_and_compute_errors

if dossier.errors.blank? && dossier.can_passer_en_construction?
editing_fork_origin.merge_fork(dossier)
editing_fork_origin.submit_en_construction!
redirect_to dossier_path(editing_fork_origin)
if editing_fork_origin.present?
# TODO remove when all forks are gone
editing_fork_origin.merge_fork(dossier)
else
dossier.reset_user_buffer_stream!
end

dossier_en_construction.submit_en_construction!
redirect_to dossier_path(dossier_en_construction)
else
@dossier_for_editing = dossier
@dossier = editing_fork_origin
if editing_fork_origin.present?
@dossier = editing_fork_origin
end

render :modifier
end
end

def update
@dossier = dossier.en_construction? ? dossier.find_editing_fork(dossier.user) : dossier
@dossier = update_with_fork? ? dossier.find_editing_fork(dossier.user) : dossier
@dossier = dossier_with_champs(pj_template: false)
update_dossier_and_compute_errors

Expand Down Expand Up @@ -536,6 +553,18 @@ def set_dossier_as_editing_fork
redirect_to dossier_path(dossier)
end

def set_dossier_stream
dossier.with_update_stream(current_user)
end

def update_with_stream?
dossier.update_with_stream?
end

def update_with_fork?
dossier.update_with_fork?
end

def update_dossier_and_compute_errors
dossier.update_champs_attributes(champs_public_attributes_params, :public, updated_by: current_user.email)
updated_champs = dossier.champs.filter(&:changed_for_autosave?)
Expand All @@ -561,6 +590,7 @@ def submit_dossier_and_compute_errors
dossier.validate(:champs_public_value)
dossier.check_mandatory_and_visible_champs

# TODO remove when all forks are gone
if dossier.editing_fork_origin&.pending_correction?
dossier.editing_fork_origin.validate(:champs_public_value)
dossier.editing_fork_origin.errors.where(:pending_correction).each do |error|
Expand Down
199 changes: 191 additions & 8 deletions spec/controllers/users/dossiers_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -594,10 +594,7 @@
end

context 'when dossier was already submitted' do
before do
expect_any_instance_of(Dossier).to receive(:remove_not_visible_or_empty_champs!)
post :submit_en_construction, params: payload
end
before { post :submit_en_construction, params: payload }

it 'redirects to the dossier' do
subject
Expand Down Expand Up @@ -1037,8 +1034,192 @@
it { expect(response).to have_http_status(:ok) }
end

context 'when the champ is a phone number' do
let(:types_de_champ_public) { [{ type: :phone }] }
let(:now) { Time.zone.parse('01/01/2100') }

let(:submit_payload) do
{
id: dossier.id,
dossier: {
champs_public_attributes: {
first_champ.public_id => {
value: value
}
}
}
}
end

context 'with a valid value sent as string' do
let(:value) { '0612345678' }
it 'updates the value' do
subject
expect(first_champ.reload.value).to eq('0612345678')
end
end

context 'with a valid value sent as number' do
let(:value) { '45187272'.to_i }
it 'updates the value' do
subject
expect(first_champ.reload.value).to eq('45187272')
end
end
end
end

describe '#update en_construction (stream)' do
before { sign_in(user) }

let(:types_de_champ_public) { [{}, { type: :piece_justificative }] }
let(:procedure) { create(:procedure, :published, types_de_champ_public:).tap { Flipper.enable(:user_buffer_stream, _1) } }
let!(:dossier) { create(:dossier, :en_construction, user:, procedure:) }
let(:first_champ) { dossier.project_champs_public.first }
let(:first_champ_user_buffer) { dossier.with_update_stream(dossier.user) { dossier.project_champs_public.first } }
let(:piece_justificative_champ) { dossier.project_champs_public.last }
let(:piece_justificative_champ_user_buffer) { dossier.with_update_stream(dossier.user) { dossier.project_champs_public.last } }
let(:anchor_to_first_champ) { controller.helpers.link_to I18n.t('views.users.dossiers.fix_champ'), brouillon_dossier_path(anchor: first_champ.labelledby_id), class: 'error-anchor' }
let(:value) { 'beautiful value' }
let(:file) { fixture_file_upload('spec/fixtures/files/piece_justificative_0.pdf', 'application/pdf') }
let(:now) { Time.zone.parse('01/01/2100') }

let(:submit_payload) do
{
id: dossier.id,
dossier: {
groupe_instructeur_id: dossier.groupe_instructeur_id,
champs_public_attributes: {
first_champ.public_id => {
value: value
},
piece_justificative_champ.public_id => {
piece_justificative_file: file
}
}
}
}
end
let(:payload) { submit_payload }

subject do
Timecop.freeze(now) do
patch :update, params: payload, format: :turbo_stream
end
end

context 'when the dossier cannot be updated by the user' do
let!(:dossier) { create(:dossier, :en_instruction, user:, procedure:) }

it 'redirects to the dossiers list' do
subject
expect(response).to redirect_to(dossier_path(dossier))
expect(flash.alert).to eq('Votre dossier ne peut plus être modifié')
end
end

context 'when dossier can be updated by the owner' do
it 'updates the champs' do
subject
dossier.reload
expect(dossier.user_buffer_changes?).to be_truthy
expect(first_champ_user_buffer.stream).to eq(Champ::USER_BUFFER_STREAM)
expect(first_champ_user_buffer.value).to eq('beautiful value')
expect(first_champ_user_buffer.updated_at).to eq(now)
expect(piece_justificative_champ_user_buffer.stream).to eq(Champ::USER_BUFFER_STREAM)
expect(piece_justificative_champ_user_buffer.piece_justificative_file).to be_attached
end

it 'updates the dossier timestamps' do
subject
dossier.reload
expect(dossier.updated_at).not_to eq(now)
expect(dossier.last_champ_updated_at).to be_nil
end

it { is_expected.to have_http_status(:ok) }

context 'when only a single file champ are modified' do
# A bug in ActiveRecord causes records changed through grand-parent <-> parent <-> child
# relationships do not touch the grand-parent record on change.
# This situation is hit when updating just the attachment of a champ (and not the
# champ itself).
#
# This test ensures that, whatever workaround we wrote for this, it still works properly.
#
# See https://github.com/rails/rails/issues/26726
let(:submit_payload) do
{
id: dossier.id,
dossier: {
champs_public_attributes: {
piece_justificative_champ.public_id => {
piece_justificative_file: file
}
}
}
}
end

it 'updates the dossier timestamps' do
subject
dossier.reload
expect(dossier.updated_at).not_to eq(now)
expect(dossier.last_champ_updated_at).to be_nil
end
end
end

context 'when the update fails' do
render_views

context 'classic error' do
before do
allow_any_instance_of(Dossier).to receive(:save).and_return(false)
allow_any_instance_of(Dossier).to receive(:errors).and_return(
[message: 'nop', inner_error: double(base: first_champ_user_buffer)]
)
subject
end

it { expect(response).to render_template(:update) }

it 'does not update the dossier timestamps' do
dossier.reload
expect(dossier.updated_at).not_to eq(now)
expect(dossier.last_champ_updated_at).to be_nil
end
end

context 'iban error' do
let(:types_de_champ_public) { [{ type: :iban }] }
let(:value) { 'abc' }

before { subject }

it do
dossier.reload
expect(dossier.updated_at).not_to eq(now)
expect(dossier.last_champ_updated_at).to be_nil
expect(response).to have_http_status(:success)
end
end
end

context 'when the user has an invitation but is not the owner' do
let(:dossier) { create(:dossier, :en_construction, procedure:) }
let!(:invite) { create(:invite, dossier:, user:) }

before { subject }

it do
dossier.reload
expect(first_champ_user_buffer.value).to eq('beautiful value')
expect(response).to have_http_status(:ok)
end
end

context 'when the dossier is followed by an instructeur' do
let(:dossier) { create(:dossier, procedure:) }
let(:instructeur) { create(:instructeur) }
let!(:invite) { create(:invite, dossier:, user:) }

Expand All @@ -1049,7 +1230,7 @@
it 'the follower has a notification' do
expect(instructeur.reload.followed_dossiers.with_notifications).to eq([])
subject
expect(instructeur.reload.followed_dossiers.with_notifications).to eq([dossier.reload])
expect(instructeur.reload.followed_dossiers.with_notifications).to eq([])
end
end

Expand All @@ -1074,15 +1255,17 @@
let(:value) { '0612345678' }
it 'updates the value' do
subject
expect(first_champ.reload.value).to eq('0612345678')
dossier.reload
expect(first_champ_user_buffer.value).to eq('0612345678')
end
end

context 'with a valid value sent as number' do
let(:value) { '45187272'.to_i }
it 'updates the value' do
subject
expect(first_champ.reload.value).to eq('45187272')
dossier.reload
expect(first_champ_user_buffer.value).to eq('45187272')
end
end
end
Expand Down
9 changes: 9 additions & 0 deletions spec/views/shared/dossiers/_edit.html.haml_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,15 @@
end
end

context 'when dossier is en construction (stream)' do
let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure:) }
let(:dossier_for_editing) { dossier.with_update_stream(dossier.user) }

it 'can delete a piece justificative' do
expect(subject).to have_selector("[title='Supprimer le fichier #{champ.piece_justificative_file.attachments[0].filename}']")
end
end

context 'when dossier is brouillon' do
it 'can delete a piece justificative' do
expect(subject).to have_selector("[title='Supprimer le fichier #{champ.piece_justificative_file.attachments[0].filename}']")
Expand Down

0 comments on commit 84eb1ac

Please sign in to comment.