feat(dossier): fork dossier when editing en construction
This commit is contained in:
parent
025bd5beaf
commit
08a2a2c9aa
20 changed files with 193 additions and 99 deletions
|
@ -3,10 +3,10 @@
|
||||||
%span.autosave-explanation-text
|
%span.autosave-explanation-text
|
||||||
- if annotation?
|
- if annotation?
|
||||||
= t('.annotations.explanation')
|
= t('.annotations.explanation')
|
||||||
- elsif dossier.brouillon?
|
- elsif dossier.editing_fork?
|
||||||
= t('.brouillon.explanation')
|
|
||||||
- else
|
|
||||||
= t('.en_construction.explanation')
|
= t('.en_construction.explanation')
|
||||||
|
- else
|
||||||
|
= t('.brouillon.explanation')
|
||||||
- if !annotation?
|
- if !annotation?
|
||||||
= link_to t('.more_information'), t("links.common.faq.autosave_url"), class: 'autosave-more-infos fr-link fr-link--sm', **external_link_attributes
|
= link_to t('.more_information'), t("links.common.faq.autosave_url"), class: 'autosave-more-infos fr-link fr-link--sm', **external_link_attributes
|
||||||
|
|
||||||
|
@ -15,10 +15,10 @@
|
||||||
%span.autosave-label
|
%span.autosave-label
|
||||||
- if annotation?
|
- if annotation?
|
||||||
= t('.annotations.confirmation')
|
= t('.annotations.confirmation')
|
||||||
- elsif dossier.brouillon?
|
- elsif dossier.editing_fork?
|
||||||
= t('.brouillon.confirmation')
|
|
||||||
- else
|
|
||||||
= t('.en_construction.confirmation')
|
= t('.en_construction.confirmation')
|
||||||
|
- else
|
||||||
|
= t('.brouillon.confirmation')
|
||||||
- if !annotation?
|
- if !annotation?
|
||||||
= link_to t('.more_information'), t("links.common.faq.autosave_url"), class: 'autosave-more-infos fr-link fr-link--sm', **external_link_attributes
|
= link_to t('.more_information'), t("links.common.faq.autosave_url"), class: 'autosave-more-infos fr-link fr-link--sm', **external_link_attributes
|
||||||
|
|
||||||
|
@ -27,10 +27,10 @@
|
||||||
%span.autosave-label
|
%span.autosave-label
|
||||||
- if annotation?
|
- if annotation?
|
||||||
= t('.annotations.error')
|
= t('.annotations.error')
|
||||||
- elsif dossier.brouillon?
|
- elsif dossier.editing_fork?
|
||||||
= t('.brouillon.error')
|
|
||||||
- else
|
|
||||||
= t('.en_construction.error')
|
= t('.en_construction.error')
|
||||||
|
- else
|
||||||
|
= t('.brouillon.error')
|
||||||
%button.button.small.autosave-retry{ type: :button, data: { action: 'autosave-status#onClickRetryButton', autosave_status_target: 'retryButton' } }
|
%button.button.small.autosave-retry{ type: :button, data: { action: 'autosave-status#onClickRetryButton', autosave_status_target: 'retryButton' } }
|
||||||
%span.autosave-retry-label réessayer
|
%span.autosave-retry-label réessayer
|
||||||
%span.autosave-retrying-label enregistrement en cours…
|
%span.autosave-retrying-label enregistrement en cours…
|
||||||
|
|
|
@ -14,7 +14,7 @@ class Dossiers::EditFooterComponent < ApplicationComponent
|
||||||
@annotation.present?
|
@annotation.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
def button_options
|
def submit_draft_button_options
|
||||||
{
|
{
|
||||||
class: 'fr-btn fr-btn--sm',
|
class: 'fr-btn fr-btn--sm',
|
||||||
disabled: !owner?,
|
disabled: !owner?,
|
||||||
|
@ -23,6 +23,14 @@ class Dossiers::EditFooterComponent < ApplicationComponent
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def submit_en_construction_button_options
|
||||||
|
{
|
||||||
|
class: 'fr-btn fr-btn--sm',
|
||||||
|
method: :post,
|
||||||
|
data: { 'disable-with': t('.submitting'), controller: 'autosave-submit' }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def render?
|
def render?
|
||||||
!@dossier.for_procedure_preview?
|
!@dossier.for_procedure_preview?
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
en:
|
en:
|
||||||
submit: Submit the file
|
submit: Submit the file
|
||||||
|
submit_changes: Submit file changes
|
||||||
submitting: Submitting…
|
submitting: Submitting…
|
||||||
invite_notice: You are invited to make amendments to this file but only the owner themselves can submit it.
|
invite_notice: You are invited to make amendments to this file but only the owner themselves can submit it.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
fr:
|
fr:
|
||||||
submit: Déposer le dossier
|
submit: Déposer le dossier
|
||||||
|
submit_changes: Déposer les modifications
|
||||||
submitting: Envoi en cours…
|
submitting: Envoi en cours…
|
||||||
invite_notice: En tant qu’invité, vous pouvez remplir ce formulaire – mais le titulaire du dossier doit le déposer lui-même.
|
invite_notice: En tant qu’invité, vous pouvez remplir ce formulaire – mais le titulaire du dossier doit le déposer lui-même.
|
||||||
|
|
|
@ -3,7 +3,10 @@
|
||||||
= render Dossiers::AutosaveFooterComponent.new(dossier: @dossier, annotation: annotation?)
|
= render Dossiers::AutosaveFooterComponent.new(dossier: @dossier, annotation: annotation?)
|
||||||
|
|
||||||
- if !annotation? && @dossier.can_transition_to_en_construction?
|
- if !annotation? && @dossier.can_transition_to_en_construction?
|
||||||
= button_to t('.submit'), brouillon_dossier_url(@dossier), button_options
|
= button_to t('.submit'), brouillon_dossier_url(@dossier), submit_draft_button_options
|
||||||
|
- elsif @dossier.forked_with_changes?
|
||||||
|
= button_to t('.submit_changes'), modifier_dossier_url(@dossier.editing_fork_origin), submit_en_construction_button_options
|
||||||
|
|
||||||
|
|
||||||
- if @dossier.brouillon? && !owner?
|
- if @dossier.brouillon? && !owner?
|
||||||
.send-notice.invite-cannot-submit
|
.send-notice.invite-cannot-submit
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
= @form.label @champ.main_value_name, id: @champ.labelledby_id, for: @champ.input_id do
|
= @form.label @champ.main_value_name, id: @champ.labelledby_id, for: @champ.input_id do
|
||||||
- render EditableChamp::ChampLabelContentComponent.new champ: @champ, seen_at: @seen_at
|
- render EditableChamp::ChampLabelContentComponent.new champ: @champ, seen_at: @seen_at
|
||||||
- else
|
- else
|
||||||
.form-label.mb-4
|
.form-label.mb-4{ id: @champ.labelledby_id }
|
||||||
= render EditableChamp::ChampLabelContentComponent.new champ: @champ, seen_at: @seen_at
|
= render EditableChamp::ChampLabelContentComponent.new champ: @champ, seen_at: @seen_at
|
||||||
|
|
||||||
- if @champ.description.present?
|
- if @champ.description.present?
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
- if @champ.mandatory?
|
- if @champ.mandatory?
|
||||||
%span.mandatory *
|
%span.mandatory *
|
||||||
|
|
||||||
- if @champ.updated_at.present? && @seen_at.present?
|
- if @champ.forked_with_changes?
|
||||||
|
%span.updated-at.highlighted
|
||||||
|
= @champ.updated_at > 1.minutes.ago ? "modifié à l’instant" : "modification à déposer"
|
||||||
|
- elsif @champ.updated_at.present? && @seen_at.present?
|
||||||
%span.updated-at{ class: highlight_if_unseen_class }
|
%span.updated-at{ class: highlight_if_unseen_class }
|
||||||
= "modifié le #{try_format_datetime(@champ.updated_at)}"
|
= "modifié le #{try_format_datetime(@champ.updated_at)}"
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ module TurboChampsConcern
|
||||||
def champs_to_turbo_update(params, champs)
|
def champs_to_turbo_update(params, champs)
|
||||||
champ_ids = params.keys.map(&:to_i)
|
champ_ids = params.keys.map(&:to_i)
|
||||||
|
|
||||||
to_update = champs.filter { _1.id.in?(champ_ids) && _1.refresh_after_update? }
|
to_update = champs.filter { _1.id.in?(champ_ids) && (_1.refresh_after_update? || _1.forked_with_changes?) }
|
||||||
to_show, to_hide = champs.filter(&:conditional?)
|
to_show, to_hide = champs.filter(&:conditional?)
|
||||||
.partition(&:visible?)
|
.partition(&:visible?)
|
||||||
.map { champs_to_one_selector(_1 - to_update) }
|
.map { champs_to_one_selector(_1 - to_update) }
|
||||||
|
|
|
@ -6,12 +6,12 @@ module Users
|
||||||
layout 'procedure_context', only: [:identite, :update_identite, :siret, :update_siret]
|
layout 'procedure_context', only: [:identite, :update_identite, :siret, :update_siret]
|
||||||
|
|
||||||
ACTIONS_ALLOWED_TO_ANY_USER = [:index, :recherche, :new, :transferer_all]
|
ACTIONS_ALLOWED_TO_ANY_USER = [:index, :recherche, :new, :transferer_all]
|
||||||
ACTIONS_ALLOWED_TO_OWNER_OR_INVITE = [:show, :destroy, :demande, :messagerie, :brouillon, :update_brouillon, :submit_brouillon, :modifier, :update, :create_commentaire, :papertrail, :restore]
|
ACTIONS_ALLOWED_TO_OWNER_OR_INVITE = [:show, :destroy, :demande, :messagerie, :brouillon, :submit_brouillon, :submit_en_construction, :modifier, :update, :create_commentaire, :papertrail, :restore]
|
||||||
|
|
||||||
before_action :ensure_ownership!, except: ACTIONS_ALLOWED_TO_ANY_USER + ACTIONS_ALLOWED_TO_OWNER_OR_INVITE
|
before_action :ensure_ownership!, except: ACTIONS_ALLOWED_TO_ANY_USER + ACTIONS_ALLOWED_TO_OWNER_OR_INVITE
|
||||||
before_action :ensure_ownership_or_invitation!, only: ACTIONS_ALLOWED_TO_OWNER_OR_INVITE
|
before_action :ensure_ownership_or_invitation!, only: ACTIONS_ALLOWED_TO_OWNER_OR_INVITE
|
||||||
before_action :ensure_dossier_can_be_updated, only: [:update_identite, :update_siret, :brouillon, :update_brouillon, :submit_brouillon, :modifier, :update]
|
before_action :ensure_dossier_can_be_updated, only: [:update_identite, :update_siret, :brouillon, :submit_brouillon, :submit_en_construction, :modifier, :update]
|
||||||
before_action :ensure_dossier_can_be_filled, only: [:brouillon, :modifier, :update_brouillon, :submit_brouillon, :update]
|
before_action :ensure_dossier_can_be_filled, only: [:brouillon, :modifier, :submit_brouillon, :submit_en_construction, :update]
|
||||||
before_action :ensure_dossier_can_be_viewed, only: [:show]
|
before_action :ensure_dossier_can_be_viewed, only: [:show]
|
||||||
before_action :forbid_invite_submission!, only: [:submit_brouillon]
|
before_action :forbid_invite_submission!, only: [:submit_brouillon]
|
||||||
before_action :forbid_closed_submission!, only: [:submit_brouillon]
|
before_action :forbid_closed_submission!, only: [:submit_brouillon]
|
||||||
|
@ -202,21 +202,30 @@ module Users
|
||||||
@dossier = dossier_with_champs
|
@dossier = dossier_with_champs
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_brouillon
|
def submit_en_construction
|
||||||
@dossier = dossier_with_champs
|
@dossier = dossier.find_editing_fork(dossier.user)
|
||||||
update_dossier_and_compute_errors
|
@dossier = dossier_with_champs(pj_template: false)
|
||||||
|
errors = submit_dossier_and_compute_errors
|
||||||
|
|
||||||
respond_to do |format|
|
if errors.blank?
|
||||||
format.html { render :brouillon }
|
editing_fork_origin = @dossier.editing_fork_origin
|
||||||
format.turbo_stream do
|
editing_fork_origin.merge_fork(@dossier)
|
||||||
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_public_params.fetch(:champs_public_all_attributes), dossier.champs_public_all)
|
redirect_to dossier_path(editing_fork_origin)
|
||||||
|
else
|
||||||
|
flash.now.alert = errors
|
||||||
|
|
||||||
render(:update, layout: false)
|
respond_to do |format|
|
||||||
|
format.html { render :modifier }
|
||||||
|
format.turbo_stream do
|
||||||
|
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_public_params.fetch(:champs_public_all_attributes), dossier.champs_public_all)
|
||||||
|
render :update, layout: false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
|
@dossier = dossier.en_construction? ? dossier.find_editing_fork(dossier.user) : dossier
|
||||||
@dossier = dossier_with_champs(pj_template: false)
|
@dossier = dossier_with_champs(pj_template: false)
|
||||||
errors = update_dossier_and_compute_errors
|
errors = update_dossier_and_compute_errors
|
||||||
|
|
||||||
|
@ -225,9 +234,9 @@ module Users
|
||||||
end
|
end
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { render :modifier }
|
|
||||||
format.turbo_stream do
|
format.turbo_stream do
|
||||||
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_public_params.fetch(:champs_public_all_attributes), dossier.champs_public_all)
|
@to_show, @to_hide, @to_update = champs_to_turbo_update(champs_public_params.fetch(:champs_public_all_attributes), dossier.champs_public_all)
|
||||||
|
render :update, layout: false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -425,8 +434,8 @@ module Users
|
||||||
end
|
end
|
||||||
|
|
||||||
def dossier_scope
|
def dossier_scope
|
||||||
if action_name == 'update_brouillon'
|
if action_name == 'update'
|
||||||
Dossier.visible_by_user.or(Dossier.for_procedure_preview)
|
Dossier.visible_by_user.or(Dossier.for_procedure_preview).or(Dossier.for_editing_fork)
|
||||||
elsif action_name == 'restore'
|
elsif action_name == 'restore'
|
||||||
Dossier.hidden_by_user
|
Dossier.hidden_by_user
|
||||||
else
|
else
|
||||||
|
@ -488,10 +497,6 @@ module Users
|
||||||
RoutingEngine.compute(@dossier)
|
RoutingEngine.compute(@dossier)
|
||||||
end
|
end
|
||||||
|
|
||||||
if dossier.en_construction?
|
|
||||||
errors += format_errors(errors: @dossier.check_mandatory_and_visible_champs)
|
|
||||||
end
|
|
||||||
|
|
||||||
errors
|
errors
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -528,9 +533,12 @@ module Users
|
||||||
|
|
||||||
def append_anchor_link(str_error, model)
|
def append_anchor_link(str_error, model)
|
||||||
return str_error.full_message if !model.is_a?(Champ)
|
return str_error.full_message if !model.is_a?(Champ)
|
||||||
|
|
||||||
|
route_helper = @dossier.editing_fork? ? :modifier_dossier_path : :brouillon_dossier_path
|
||||||
|
|
||||||
[
|
[
|
||||||
"Le champ « #{model.libelle.truncate(200)} » #{str_error}",
|
"Le champ « #{model.libelle.truncate(200)} » #{str_error}",
|
||||||
helpers.link_to(t('views.users.dossiers.fix_champ'), brouillon_dossier_path(anchor: model.input_id))
|
helpers.link_to(t('views.users.dossiers.fix_champ'), public_send(route_helper, anchor: model.input_id))
|
||||||
].join(", ")
|
].join(", ")
|
||||||
rescue # case of invalid type de champ on champ
|
rescue # case of invalid type de champ on champ
|
||||||
str_error
|
str_error
|
||||||
|
|
|
@ -244,6 +244,10 @@ class Champ < ApplicationRecord
|
||||||
input_id
|
input_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def forked_with_changes?
|
||||||
|
public? && dossier.champ_forked_with_changes?(self)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def html_id
|
def html_id
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
|
- dossier_for_editing = dossier.en_construction? ? dossier.owner_editing_fork : dossier
|
||||||
|
|
||||||
- if dossier.france_connect_information.present?
|
- if dossier.france_connect_information.present?
|
||||||
- content_for(:notice_info) do
|
- content_for(:notice_info) do
|
||||||
= render partial: "shared/dossiers/france_connect_informations_notice", locals: { user_information: dossier.france_connect_information }
|
= render partial: "shared/dossiers/france_connect_informations_notice", locals: { user_information: dossier.france_connect_information }
|
||||||
|
|
||||||
.dossier-edit.container.counter-start-header-section
|
.dossier-edit.container.counter-start-header-section
|
||||||
= render partial: "shared/dossiers/submit_is_over", locals: { dossier: dossier }
|
= render partial: "shared/dossiers/submit_is_over", locals: { dossier: dossier }
|
||||||
|
|
||||||
- if dossier.brouillon?
|
|
||||||
- form_options = { url: brouillon_dossier_url(dossier), method: :patch }
|
|
||||||
- else
|
|
||||||
- form_options = { url: modifier_dossier_url(dossier), method: :patch }
|
|
||||||
= render NestedForms::FormOwnerComponent.new
|
= render NestedForms::FormOwnerComponent.new
|
||||||
= form_for dossier, form_options.merge({ html: { id: 'dossier-edit-form', class: 'form', multipart: true, novalidate: 'novalidate' } }) do |f|
|
= form_for dossier_for_editing, url: brouillon_dossier_url(dossier), method: :patch, html: { id: 'dossier-edit-form', class: 'form', multipart: true, novalidate: 'novalidate' } do |f|
|
||||||
|
|
||||||
%header.mb-6
|
%header.mb-6
|
||||||
.fr-highlight
|
.fr-highlight
|
||||||
%p.fr-text--sm
|
%p.fr-text--sm
|
||||||
|
@ -42,5 +38,7 @@
|
||||||
= f.select :groupe_instructeur_id,
|
= f.select :groupe_instructeur_id,
|
||||||
dossier.procedure.groupe_instructeurs.active.map { |gi| [gi.label, gi.id] },
|
dossier.procedure.groupe_instructeurs.active.map { |gi| [gi.label, gi.id] },
|
||||||
{ include_blank: dossier.brouillon? }
|
{ include_blank: dossier.brouillon? }
|
||||||
= render EditableChamp::SectionComponent.new(champs: dossier.champs_public)
|
|
||||||
= render Dossiers::EditFooterComponent.new(dossier: dossier, annotation: false)
|
|
||||||
|
= render EditableChamp::SectionComponent.new(champs: dossier_for_editing.champs_public)
|
||||||
|
= render Dossiers::EditFooterComponent.new(dossier: dossier_for_editing, annotation: false)
|
||||||
|
|
|
@ -4,8 +4,14 @@
|
||||||
= turbo_stream.hide_all(@to_hide)
|
= turbo_stream.hide_all(@to_hide)
|
||||||
- @to_update.each do |champ|
|
- @to_update.each do |champ|
|
||||||
= fields_for champ.input_name, champ do |form|
|
= fields_for champ.input_name, champ do |form|
|
||||||
= turbo_stream.replace champ.input_group_id do
|
- if champ.refresh_after_update?
|
||||||
= render EditableChamp::EditableChampComponent.new champ:, form:
|
= turbo_stream.replace champ.input_group_id do
|
||||||
|
= render EditableChamp::EditableChampComponent.new champ:, form:
|
||||||
|
- else
|
||||||
|
= turbo_stream.update champ.labelledby_id do
|
||||||
|
= render EditableChamp::ChampLabelContentComponent.new champ:
|
||||||
|
|
||||||
= turbo_stream.remove_all(".editable-champ .spinner-removable");
|
= turbo_stream.remove_all(".editable-champ .spinner-removable");
|
||||||
= turbo_stream.hide_all(".editable-champ .spinner");
|
= turbo_stream.hide_all(".editable-champ .spinner");
|
||||||
|
= turbo_stream.replace_all '.dossier-edit-sticky-footer' do
|
||||||
|
= render Dossiers::EditFooterComponent.new(dossier: @dossier, annotation: false)
|
||||||
|
|
|
@ -315,10 +315,10 @@ Rails.application.routes.draw do
|
||||||
post 'siret', to: 'dossiers#update_siret'
|
post 'siret', to: 'dossiers#update_siret'
|
||||||
get 'etablissement'
|
get 'etablissement'
|
||||||
get 'brouillon'
|
get 'brouillon'
|
||||||
patch 'brouillon', to: 'dossiers#update_brouillon'
|
patch 'brouillon', to: 'dossiers#update'
|
||||||
post 'brouillon', to: 'dossiers#submit_brouillon'
|
post 'brouillon', to: 'dossiers#submit_brouillon'
|
||||||
get 'modifier', to: 'dossiers#modifier'
|
get 'modifier', to: 'dossiers#modifier'
|
||||||
patch 'modifier', to: 'dossiers#update'
|
post 'modifier', to: 'dossiers#submit_en_construction'
|
||||||
get 'merci'
|
get 'merci'
|
||||||
get 'demande'
|
get 'demande'
|
||||||
get 'messagerie'
|
get 'messagerie'
|
||||||
|
|
|
@ -445,11 +445,76 @@ describe Users::DossiersController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#update_brouillon' do
|
describe '#submit_en_construction' do
|
||||||
before { sign_in(user) }
|
before { sign_in(user) }
|
||||||
|
|
||||||
let(:procedure) { create(:procedure, :published, :with_type_de_champ, :with_piece_justificative) }
|
let!(:dossier) { create(:dossier, :en_construction, user: user) }
|
||||||
let!(:dossier) { create(:dossier, user: user, procedure: procedure) }
|
let(:first_champ) { dossier.owner_editing_fork.champs_public.first }
|
||||||
|
let(:anchor_to_first_champ) { controller.helpers.link_to I18n.t('views.users.dossiers.fix_champ'), modifier_dossier_path(anchor: first_champ.input_id) }
|
||||||
|
let(:value) { 'beautiful value' }
|
||||||
|
let(:now) { Time.zone.parse('01/01/2100') }
|
||||||
|
let(:payload) { { id: dossier.id } }
|
||||||
|
|
||||||
|
before { dossier.owner_editing_fork }
|
||||||
|
|
||||||
|
subject do
|
||||||
|
Timecop.freeze(now) do
|
||||||
|
post :submit_en_construction, params: payload
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the dossier cannot be updated by the user' do
|
||||||
|
let!(:dossier) { create(:dossier, :en_instruction, user: user) }
|
||||||
|
|
||||||
|
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 the update fails' do
|
||||||
|
before do
|
||||||
|
expect_any_instance_of(Dossier).to receive(:valid?).and_return(false)
|
||||||
|
expect_any_instance_of(Dossier).to receive(:errors).and_return(
|
||||||
|
[double(class: ActiveModel::Error, full_message: 'nop', base: first_champ)]
|
||||||
|
)
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(response).to render_template(:modifier) }
|
||||||
|
it { expect(flash.alert).to eq(["Le champ « #{first_champ.libelle} » nop, #{anchor_to_first_champ}"]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a mandatory champ is missing' do
|
||||||
|
let(:value) { nil }
|
||||||
|
|
||||||
|
before do
|
||||||
|
first_champ.type_de_champ.update(mandatory: true, libelle: 'l')
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(response).to render_template(:modifier) }
|
||||||
|
it { expect(flash.alert).to eq(["Le champ « l » doit être rempli, #{anchor_to_first_champ}"]) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when dossier has no champ' do
|
||||||
|
let(:submit_payload) { { id: dossier.id } }
|
||||||
|
|
||||||
|
it 'does not raise any errors' do
|
||||||
|
subject
|
||||||
|
|
||||||
|
expect(response).to redirect_to(dossier_path(dossier))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#update brouillon' do
|
||||||
|
before { sign_in(user) }
|
||||||
|
|
||||||
|
let(:procedure) { create(:procedure, :published, types_de_champ_public: [{}, { type: :piece_justificative }]) }
|
||||||
|
let(:dossier) { create(:dossier, user:, procedure:) }
|
||||||
let(:first_champ) { dossier.champs_public.first }
|
let(:first_champ) { dossier.champs_public.first }
|
||||||
let(:piece_justificative_champ) { dossier.champs_public.last }
|
let(:piece_justificative_champ) { dossier.champs_public.last }
|
||||||
let(:value) { 'beautiful value' }
|
let(:value) { 'beautiful value' }
|
||||||
|
@ -461,16 +526,16 @@ describe Users::DossiersController, type: :controller do
|
||||||
id: dossier.id,
|
id: dossier.id,
|
||||||
dossier: {
|
dossier: {
|
||||||
groupe_instructeur_id: dossier.groupe_instructeur_id,
|
groupe_instructeur_id: dossier.groupe_instructeur_id,
|
||||||
champs_public_attributes: [
|
champs_public_attributes: {
|
||||||
{
|
first_champ.id => {
|
||||||
id: first_champ.id,
|
id: first_champ.id,
|
||||||
value: value
|
value: value
|
||||||
},
|
},
|
||||||
{
|
piece_justificative_champ.id => {
|
||||||
id: piece_justificative_champ.id,
|
id: piece_justificative_champ.id,
|
||||||
piece_justificative_file: file
|
piece_justificative_file: file
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -478,12 +543,12 @@ describe Users::DossiersController, type: :controller do
|
||||||
|
|
||||||
subject do
|
subject do
|
||||||
Timecop.freeze(now) do
|
Timecop.freeze(now) do
|
||||||
patch :update_brouillon, params: payload
|
patch :update, params: payload, format: :turbo_stream
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the dossier cannot be updated by the user' do
|
context 'when the dossier cannot be updated by the user' do
|
||||||
let!(:dossier) { create(:dossier, :en_instruction, user: user) }
|
let(:dossier) { create(:dossier, :en_instruction, user:, procedure:) }
|
||||||
|
|
||||||
it 'redirects to the dossiers list' do
|
it 'redirects to the dossiers list' do
|
||||||
subject
|
subject
|
||||||
|
@ -507,7 +572,7 @@ describe Users::DossiersController, type: :controller do
|
||||||
{
|
{
|
||||||
id: dossier.id,
|
id: dossier.id,
|
||||||
dossier: {
|
dossier: {
|
||||||
champs_public_attributes: [{ value: '' }]
|
champs_public_attributes: { first_champ.id => { id: first_champ.id } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -520,7 +585,7 @@ describe Users::DossiersController, type: :controller do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the user has an invitation but is not the owner' do
|
context 'when the user has an invitation but is not the owner' do
|
||||||
let(:dossier) { create(:dossier) }
|
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||||
let!(:invite) { create(:invite, dossier: dossier, user: user) }
|
let!(:invite) { create(:invite, dossier: dossier, user: user) }
|
||||||
|
|
||||||
before { subject }
|
before { subject }
|
||||||
|
@ -530,11 +595,11 @@ describe Users::DossiersController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#update' do
|
describe '#update en_construction' do
|
||||||
before { sign_in(user) }
|
before { sign_in(user) }
|
||||||
|
|
||||||
let(:procedure) { create(:procedure, :published, :with_type_de_champ, :with_piece_justificative) }
|
let(:procedure) { create(:procedure, :published, types_de_champ_public: [{}, { type: :piece_justificative }]) }
|
||||||
let!(:dossier) { create(:dossier, :en_construction, user: user, procedure: procedure) }
|
let!(:dossier) { create(:dossier, :en_construction, user:, procedure:) }
|
||||||
let(:first_champ) { dossier.champs_public.first }
|
let(:first_champ) { dossier.champs_public.first }
|
||||||
let(:anchor_to_first_champ) { controller.helpers.link_to I18n.t('views.users.dossiers.fix_champ'), brouillon_dossier_path(anchor: first_champ.input_id) }
|
let(:anchor_to_first_champ) { controller.helpers.link_to I18n.t('views.users.dossiers.fix_champ'), brouillon_dossier_path(anchor: first_champ.input_id) }
|
||||||
let(:piece_justificative_champ) { dossier.champs_public.last }
|
let(:piece_justificative_champ) { dossier.champs_public.last }
|
||||||
|
@ -547,16 +612,16 @@ describe Users::DossiersController, type: :controller do
|
||||||
id: dossier.id,
|
id: dossier.id,
|
||||||
dossier: {
|
dossier: {
|
||||||
groupe_instructeur_id: dossier.groupe_instructeur_id,
|
groupe_instructeur_id: dossier.groupe_instructeur_id,
|
||||||
champs_public_attributes: [
|
champs_public_attributes: {
|
||||||
{
|
first_champ.id => {
|
||||||
id: first_champ.id,
|
id: first_champ.id,
|
||||||
value: value
|
value: value
|
||||||
},
|
},
|
||||||
{
|
piece_justificative_champ.id => {
|
||||||
id: piece_justificative_champ.id,
|
id: piece_justificative_champ.id,
|
||||||
piece_justificative_file: file
|
piece_justificative_file: file
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -564,12 +629,12 @@ describe Users::DossiersController, type: :controller do
|
||||||
|
|
||||||
subject do
|
subject do
|
||||||
Timecop.freeze(now) do
|
Timecop.freeze(now) do
|
||||||
patch :update, params: payload
|
patch :update, params: payload, format: :turbo_stream
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the dossier cannot be updated by the user' do
|
context 'when the dossier cannot be updated by the user' do
|
||||||
let!(:dossier) { create(:dossier, :en_instruction, user: user) }
|
let!(:dossier) { create(:dossier, :en_instruction, user:, procedure:) }
|
||||||
|
|
||||||
it 'redirects to the dossiers list' do
|
it 'redirects to the dossiers list' do
|
||||||
subject
|
subject
|
||||||
|
@ -612,12 +677,12 @@ describe Users::DossiersController, type: :controller do
|
||||||
{
|
{
|
||||||
id: dossier.id,
|
id: dossier.id,
|
||||||
dossier: {
|
dossier: {
|
||||||
champs_public_attributes: [
|
champs_public_attributes: {
|
||||||
{
|
piece_justificative_champ.id => {
|
||||||
id: piece_justificative_champ.id,
|
id: piece_justificative_champ.id,
|
||||||
piece_justificative_file: file
|
piece_justificative_file: file
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -640,7 +705,7 @@ describe Users::DossiersController, type: :controller do
|
||||||
subject
|
subject
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect(response).to render_template(:modifier) }
|
it { expect(response).to render_template(:update) }
|
||||||
it { expect(flash.alert).to eq(["Le champ « #{first_champ.libelle} » nop, #{anchor_to_first_champ}"]) }
|
it { expect(flash.alert).to eq(["Le champ « #{first_champ.libelle} » nop, #{anchor_to_first_champ}"]) }
|
||||||
|
|
||||||
it 'does not update the dossier timestamps' do
|
it 'does not update the dossier timestamps' do
|
||||||
|
@ -656,18 +721,6 @@ describe Users::DossiersController, type: :controller do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when a mandatory champ is missing' do
|
|
||||||
let(:value) { nil }
|
|
||||||
|
|
||||||
before do
|
|
||||||
first_champ.type_de_champ.update(mandatory: true, libelle: 'l')
|
|
||||||
subject
|
|
||||||
end
|
|
||||||
|
|
||||||
it { expect(response).to render_template(:modifier) }
|
|
||||||
it { expect(flash.alert).to eq(["Le champ « l » doit être rempli, #{anchor_to_first_champ}"]) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when a champ validation fails' do
|
context 'when a champ validation fails' do
|
||||||
let(:value) { 'abc' }
|
let(:value) { 'abc' }
|
||||||
|
|
||||||
|
@ -682,8 +735,8 @@ describe Users::DossiersController, type: :controller do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the user has an invitation but is not the owner' do
|
context 'when the user has an invitation but is not the owner' do
|
||||||
let(:dossier) { create(:dossier, :en_construction) }
|
let(:dossier) { create(:dossier, :en_construction, procedure:) }
|
||||||
let!(:invite) { create(:invite, dossier: dossier, user: user) }
|
let!(:invite) { create(:invite, dossier:, user:) }
|
||||||
|
|
||||||
before { subject }
|
before { subject }
|
||||||
|
|
||||||
|
@ -692,9 +745,9 @@ describe Users::DossiersController, type: :controller do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the dossier is followed by an instructeur' do
|
context 'when the dossier is followed by an instructeur' do
|
||||||
let(:dossier) { create(:dossier) }
|
let(:dossier) { create(:dossier, procedure:) }
|
||||||
let(:instructeur) { create(:instructeur) }
|
let(:instructeur) { create(:instructeur) }
|
||||||
let!(:invite) { create(:invite, dossier: dossier, user: user) }
|
let!(:invite) { create(:invite, dossier:, user:) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
instructeur.follow(dossier)
|
instructeur.follow(dossier)
|
||||||
|
@ -708,8 +761,8 @@ describe Users::DossiersController, type: :controller do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the champ is a phone number' do
|
context 'when the champ is a phone number' do
|
||||||
let(:procedure) { create(:procedure, :published, :with_phone) }
|
let(:procedure) { create(:procedure, :published, types_de_champ_public: [{ type: :phone }]) }
|
||||||
let!(:dossier) { create(:dossier, :en_construction, user: user, procedure: procedure) }
|
let!(:dossier) { create(:dossier, :en_construction, user:, procedure:) }
|
||||||
let(:first_champ) { dossier.champs_public.first }
|
let(:first_champ) { dossier.champs_public.first }
|
||||||
let(:now) { Time.zone.parse('01/01/2100') }
|
let(:now) { Time.zone.parse('01/01/2100') }
|
||||||
|
|
||||||
|
@ -717,12 +770,12 @@ describe Users::DossiersController, type: :controller do
|
||||||
{
|
{
|
||||||
id: dossier.id,
|
id: dossier.id,
|
||||||
dossier: {
|
dossier: {
|
||||||
champs_public_attributes: [
|
champs_public_attributes: {
|
||||||
{
|
first_champ.id => {
|
||||||
id: first_champ.id,
|
id: first_champ.id,
|
||||||
value: value
|
value: value
|
||||||
}
|
}
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -119,6 +119,8 @@ describe 'The routing', js: true do
|
||||||
fill_in litteraire_user.dossiers.first.champs_public.first.libelle, with: 'some value'
|
fill_in litteraire_user.dossiers.first.champs_public.first.libelle, with: 'some value'
|
||||||
wait_for_autosave(false)
|
wait_for_autosave(false)
|
||||||
|
|
||||||
|
click_on 'Déposer les modifications'
|
||||||
|
|
||||||
log_out
|
log_out
|
||||||
|
|
||||||
# the litteraires instructeurs should have a notification
|
# the litteraires instructeurs should have a notification
|
||||||
|
@ -217,6 +219,8 @@ describe 'The routing', js: true do
|
||||||
|
|
||||||
expect(page).to have_text(new_group)
|
expect(page).to have_text(new_group)
|
||||||
|
|
||||||
|
click_on 'Déposer les modifications'
|
||||||
|
|
||||||
log_out
|
log_out
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -146,6 +146,8 @@ describe 'The routing with rules', js: true do
|
||||||
fill_in litteraire_user.dossiers.first.champs_public.first.libelle, with: 'some value'
|
fill_in litteraire_user.dossiers.first.champs_public.first.libelle, with: 'some value'
|
||||||
wait_for_autosave(false)
|
wait_for_autosave(false)
|
||||||
|
|
||||||
|
click_on 'Déposer les modifications'
|
||||||
|
|
||||||
log_out
|
log_out
|
||||||
|
|
||||||
# the litteraires instructeurs should have a notification
|
# the litteraires instructeurs should have a notification
|
||||||
|
@ -245,6 +247,8 @@ describe 'The routing with rules', js: true do
|
||||||
|
|
||||||
expect(page).to have_text(new_group)
|
expect(page).to have_text(new_group)
|
||||||
|
|
||||||
|
click_on 'Déposer les modifications'
|
||||||
|
|
||||||
log_out
|
log_out
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -483,13 +483,13 @@ describe 'The user' do
|
||||||
fill_individual
|
fill_individual
|
||||||
|
|
||||||
# Test autosave failure
|
# Test autosave failure
|
||||||
allow_any_instance_of(Users::DossiersController).to receive(:update_brouillon).and_raise("Server is busy")
|
allow_any_instance_of(Users::DossiersController).to receive(:update).and_raise("Server is busy")
|
||||||
fill_in('texte obligatoire', with: 'a valid user input')
|
fill_in('texte obligatoire', with: 'a valid user input')
|
||||||
blur
|
blur
|
||||||
expect(page).to have_css('span', text: 'Impossible d’enregistrer le brouillon', visible: true)
|
expect(page).to have_css('span', text: 'Impossible d’enregistrer le brouillon', visible: true)
|
||||||
|
|
||||||
# Test that retrying after a failure works
|
# Test that retrying after a failure works
|
||||||
allow_any_instance_of(Users::DossiersController).to receive(:update_brouillon).and_call_original
|
allow_any_instance_of(Users::DossiersController).to receive(:update).and_call_original
|
||||||
click_on 'réessayer'
|
click_on 'réessayer'
|
||||||
wait_for_autosave
|
wait_for_autosave
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ RSpec.shared_examples 'the user can edit the submitted demande' do
|
||||||
fill_in('Texte obligatoire', with: 'Nouveau texte')
|
fill_in('Texte obligatoire', with: 'Nouveau texte')
|
||||||
wait_for_autosave(false)
|
wait_for_autosave(false)
|
||||||
|
|
||||||
|
click_on 'Déposer les modifications'
|
||||||
click_on 'Demande'
|
click_on 'Demande'
|
||||||
expect(page).to have_current_path(demande_dossier_path(dossier))
|
expect(page).to have_current_path(demande_dossier_path(dossier))
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ describe "Dossier en_construction" do
|
||||||
}
|
}
|
||||||
|
|
||||||
let(:champ) {
|
let(:champ) {
|
||||||
dossier.champs_public.find { _1.type_de_champ_id == tdc.id }
|
dossier.find_editing_fork(dossier.user).champs_public.find { _1.type_de_champ_id == tdc.id }
|
||||||
}
|
}
|
||||||
|
|
||||||
scenario 'delete a non mandatory piece justificative', js: true do
|
scenario 'delete a non mandatory piece justificative', js: true do
|
||||||
|
@ -28,7 +28,7 @@ describe "Dossier en_construction" do
|
||||||
scenario 'remplace a mandatory piece justificative', js: true do
|
scenario 'remplace a mandatory piece justificative', js: true do
|
||||||
visit_dossier(dossier)
|
visit_dossier(dossier)
|
||||||
|
|
||||||
click_on "Remplacer le fichier toto.txt"
|
click_on "Supprimer le fichier toto.txt"
|
||||||
|
|
||||||
input_selector = "#attachment-multiple-empty-#{champ.id}"
|
input_selector = "#attachment-multiple-empty-#{champ.id}"
|
||||||
expect(page).to have_selector(input_selector)
|
expect(page).to have_selector(input_selector)
|
||||||
|
@ -51,9 +51,9 @@ describe "Dossier en_construction" do
|
||||||
scenario 'remplace a mandatory titre identite', js: true do
|
scenario 'remplace a mandatory titre identite', js: true do
|
||||||
visit_dossier(dossier)
|
visit_dossier(dossier)
|
||||||
|
|
||||||
click_on "Remplacer le fichier toto.png"
|
click_on "Supprimer le fichier toto.png"
|
||||||
|
|
||||||
input_selector = ".attachment-input-#{champ.piece_justificative_file.attachments.first.id}"
|
input_selector = "##{champ.input_id}"
|
||||||
expect(page).to have_selector(input_selector)
|
expect(page).to have_selector(input_selector)
|
||||||
find(input_selector).attach_file(Rails.root.join('spec/fixtures/files/file.pdf'))
|
find(input_selector).attach_file(Rails.root.join('spec/fixtures/files/file.pdf'))
|
||||||
|
|
||||||
|
|
|
@ -115,8 +115,8 @@ describe 'shared/dossiers/edit', type: :view do
|
||||||
let(:dossier) { create(:dossier, :en_construction) }
|
let(:dossier) { create(:dossier, :en_construction) }
|
||||||
before { dossier.champs_public << champ }
|
before { dossier.champs_public << champ }
|
||||||
|
|
||||||
it 'cannot delete a piece justificative' do
|
it 'can delete a piece justificative' do
|
||||||
expect(subject).not_to have_selector("[title='Supprimer le fichier #{champ.piece_justificative_file.attachments[0].filename}']")
|
expect(subject).to have_selector("[title='Supprimer le fichier #{champ.piece_justificative_file.attachments[0].filename}']")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue