diff --git a/app/assets/stylesheets/components/reopen_authorizations.css b/app/assets/stylesheets/components/reopen_authorizations.css new file mode 100644 index 000000000..b9e414fe3 --- /dev/null +++ b/app/assets/stylesheets/components/reopen_authorizations.css @@ -0,0 +1,18 @@ +.reopen-stage-buttons-container { + width: 100%; + display: flex; + align-items: flex-start; +} + +.reopen-stage-buttons { + flex: 1; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.reopen-stage-cancel { + display: flex; + align-items: flex-end; + height: 100%; +} \ No newline at end of file diff --git a/app/interactors/transition_authorization_request_to_stage_of_authorization.rb b/app/interactors/transition_authorization_request_to_stage_of_authorization.rb new file mode 100644 index 000000000..ef935e2f2 --- /dev/null +++ b/app/interactors/transition_authorization_request_to_stage_of_authorization.rb @@ -0,0 +1,18 @@ +class TransitionAuthorizationRequestToStageOfAuthorization < ApplicationInteractor + def call + context.fail! unless context.authorization.reopenable? + + return if context.authorization_request.is_a? context.authorization.authorization_request_class.constantize + + transition_to_authorization_stage + end + + private + + def transition_to_authorization_stage + context.authorization_request.update!( + type: context.authorization.authorization_request_class.to_s, + form_uid: context.authorization.form_uid, + ) + end +end diff --git a/app/models/authorization.rb b/app/models/authorization.rb index 24f5228c5..d5f7ee8c0 100644 --- a/app/models/authorization.rb +++ b/app/models/authorization.rb @@ -58,6 +58,10 @@ def request_as_validated end # rubocop:enable Metrics/AbcSize + def reopenable? + latest? + end + def latest? if definition.stage.exists? request.latest_authorization_of_class(authorization_request_class) == self @@ -78,6 +82,10 @@ def definition authorization_request_class.constantize.definition end + def reopenable_to_another_stage? + authorization_request.latest_authorizations_of_each_stage.count > 1 + end + private def affect_snapshot_documents(request_as_validated) diff --git a/app/models/authorization_definition.rb b/app/models/authorization_definition.rb index 1283d82fa..df00f57fc 100644 --- a/app/models/authorization_definition.rb +++ b/app/models/authorization_definition.rb @@ -58,7 +58,7 @@ def name_with_stage end def reopenable? - !next_stage? + true end def instructors diff --git a/app/models/authorization_request.rb b/app/models/authorization_request.rb index c6bd73696..95a6a9b5a 100644 --- a/app/models/authorization_request.rb +++ b/app/models/authorization_request.rb @@ -91,6 +91,12 @@ def latest_authorization authorizations.order(created_at: :desc).limit(1).first end + def latest_authorizations_of_each_stage + authorizations.group_by(&:authorization_request_class).map do |_, authorizations| + authorizations.max_by(&:created_at) + end + end + def latest_authorization_of_class(authorization_request_class) authorizations.where(authorization_request_class: authorization_request_class).order(created_at: :desc).limit(1).first end diff --git a/app/organizers/reopen_authorization.rb b/app/organizers/reopen_authorization.rb index 800e232a2..70a99a439 100644 --- a/app/organizers/reopen_authorization.rb +++ b/app/organizers/reopen_authorization.rb @@ -5,5 +5,6 @@ class ReopenAuthorization < ApplicationOrganizer context.authorization_request = context.authorization.request end - organize ExecuteAuthorizationRequestTransitionWithCallbacks + organize TransitionAuthorizationRequestToStageOfAuthorization, + ExecuteAuthorizationRequestTransitionWithCallbacks end diff --git a/app/views/reopen_authorizations/new.html.erb b/app/views/reopen_authorizations/new.html.erb index c7151a009..15f0aedb2 100644 --- a/app/views/reopen_authorizations/new.html.erb +++ b/app/views/reopen_authorizations/new.html.erb @@ -6,7 +6,6 @@

<%= t('.title', authorization_name: @authorization.name) %>

-

<%= t('.disclaimer').html_safe %>

@@ -16,13 +15,35 @@ <%= t('.invalid_request') %>

<% end %> + + <% if @authorization.reopenable_to_another_stage? %> +
+

<%= t('.warning_for_stages.title') %>

+

<%= t('.warning_for_stages.content').html_safe %> +

+ <% end %> diff --git a/config/authorization_definitions.yml b/config/authorization_definitions.yml index 791c9aec8..af0e5f05d 100644 --- a/config/authorization_definitions.yml +++ b/config/authorization_definitions.yml @@ -339,7 +339,7 @@ shared: type: production previouses: - id: api_impot_particulier_sandbox - form_id: api-impot-particulier-production + form_id: api-impot-particulier-sandbox blocks: - name: basic_infos - name: personal_data diff --git a/config/locales/fr.yml b/config/locales/fr.yml index caee845f8..d4dd66f44 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -463,8 +463,14 @@ fr: invalid_request: | Attention, vous réouvrez une demande qui possède des données invalides ou manquantes, vous pourrez avoir des erreurs inattendues, si c'était le cas merci de contacter le support + warning_for_stages: + title: Vos mises à jour peuvent concerner votre habilitation bac à sable + content: "Pour savoir si les mises à jour que vous désirez effectuer nécessitent de passer d'abord par une mise à jour de l'habilitation bac à sable, contactez le fournisseur de données :
dtnum.donnees.demande-acces@dgfip.finances.gouv.fr" cancel: Annuler reopen: Mettre à jour l'habilitation + reopen_to_stage: + sandbox: Mettre à jour l'habilitation bac à sable + production: Mettre à jour l'habilitation de production create: success: title: L'habilitation %{name} a bien été réouverte diff --git a/features/reouverture_habilitation.feature b/features/reouverture_habilitation.feature index 4c54f691b..c973d9392 100644 --- a/features/reouverture_habilitation.feature +++ b/features/reouverture_habilitation.feature @@ -114,3 +114,18 @@ Fonctionnalité: Réouverture d'une habilitation validée Alors il y a un badge "Validée" + Scénario: Initialisation d'une réouverture bac à sable d'une demande validée en production + Quand j'ai 1 demande d'habilitation "API Impôt Particulier" validée + Et que je vais sur la page tableau de bord + Et que je clique sur "Mettre à jour" + Et que je clique sur "Mettre à jour l'habilitation bac à sable" + Alors je suis sur la page "Demande libre (Bac à sable) - API Impôt Particulier" + Et il y a un message de succès contenant "a bien été réouverte" + + Scénario: Initialisation d'une réouverture production d'une demande validée en production + Quand j'ai 1 demande d'habilitation "API Impôt Particulier" validée + Et que je vais sur la page tableau de bord + Et que je clique sur "Mettre à jour" + Et que je clique sur "Mettre à jour l'habilitation de production" + Alors je suis sur la page "Demande libre (Production) - API Impôt Particulier" + Et il y a un message de succès contenant "a bien été réouverte" \ No newline at end of file diff --git a/spec/factories/authorization_requests.rb b/spec/factories/authorization_requests.rb index 77893542c..f85522010 100644 --- a/spec/factories/authorization_requests.rb +++ b/spec/factories/authorization_requests.rb @@ -188,12 +188,15 @@ previous_authorization_created_at = authorization_request.created_at + date_offset end + previous_stage = authorization_request.definition.stage.previous_stages[0] + authorization_request.authorizations << Authorization.create!( request: authorization_request, applicant: authorization_request.applicant, - authorization_request_class: authorization_request.definition.stage.previous_stages[0][:definition].authorization_request_class, + authorization_request_class: previous_stage[:definition].authorization_request_class, data: authorization_request.data.presence || { 'what' => 'ever' }, - created_at: previous_authorization_created_at + created_at: previous_authorization_created_at, + form_uid: previous_stage[:form].id ) end end diff --git a/spec/factories/authorizations.rb b/spec/factories/authorizations.rb index 999b0b397..0abb03b28 100644 --- a/spec/factories/authorizations.rb +++ b/spec/factories/authorizations.rb @@ -19,6 +19,7 @@ authorization.data = authorization.request.data.dup authorization.data['what'] = 'ever' if authorization.data.blank? + authorization.form_uid ||= authorization.request.form_uid end end end diff --git a/spec/models/authorization_request_spec.rb b/spec/models/authorization_request_spec.rb index 89c8a4415..023f287ec 100644 --- a/spec/models/authorization_request_spec.rb +++ b/spec/models/authorization_request_spec.rb @@ -353,6 +353,22 @@ it { is_expected.to contain_exactly(valid_authorization_request_with_one_scope, valid_authorization_request_with_more_scopes) } end + describe '#latest_authorizations_of_each_stage' do + subject { authorization_request.latest_authorizations_of_each_stage } + + let(:authorization_request) { create(:authorization_request, :api_impot_particulier_production, :validated) } + let(:sandbox_authorization) { authorization_request.authorizations.find_by(authorization_request_class: 'AuthorizationRequest::APIImpotParticulierSandbox') } + let(:production_authorization) { authorization_request.authorizations.find_by(authorization_request_class: 'AuthorizationRequest::APIImpotParticulier') } + + it { is_expected.to eq([sandbox_authorization, production_authorization]) } + + context 'when there is a newer sandbox authorization' do + let!(:new_sandbox_authorization) { create(:authorization, request: authorization_request, authorization_request_class: 'AuthorizationRequest::APIImpotParticulierSandbox', created_at: Date.tomorrow) } + + it { is_expected.to eq([new_sandbox_authorization, production_authorization]) } + end + end + describe '#latest_authorization_of_class' do subject { authorization_request.latest_authorization_of_class(request_class) } diff --git a/spec/organizers/reopen_authorization_spec.rb b/spec/organizers/reopen_authorization_spec.rb index e2c8341e9..52cb5437a 100644 --- a/spec/organizers/reopen_authorization_spec.rb +++ b/spec/organizers/reopen_authorization_spec.rb @@ -3,7 +3,6 @@ subject(:reopen_authorization_request) { described_class.call(authorization:, user:) } let(:user) { authorization_request.applicant } - let(:authorization) { create(:authorization, request: authorization_request) } before do freeze_time @@ -11,6 +10,7 @@ context 'with authorization request is in validated state' do let!(:authorization_request) { create(:authorization_request, authorization_request_kind, :validated) } + let(:authorization) { authorization_request.latest_authorization } let(:authorization_request_kind) { :api_entreprise } it { is_expected.to be_success } @@ -29,6 +29,7 @@ context 'with authorization request in draft state' do let(:authorization_request) { create(:authorization_request, :hubee_cert_dc, :draft) } + let(:authorization) { create(:authorization, request: authorization_request) } it { is_expected.to be_failure } @@ -36,5 +37,37 @@ expect { reopen_authorization_request }.not_to change { authorization_request.reload.state } end end + + context 'with an authorization of a different stage than the request' do + let(:authorization_request) { create(:authorization_request, :api_impot_particulier, :validated) } + let(:authorization) { authorization_request.latest_authorization_of_class 'AuthorizationRequest::APIImpotParticulierSandbox' } + + it "transitions the authorization request to the authorization's stage" do + expect { reopen_authorization_request }.to change { AuthorizationRequest.find(authorization_request.id).class }.from(AuthorizationRequest::APIImpotParticulier).to(AuthorizationRequest::APIImpotParticulierSandbox) + end + + it "transitions the authorization request's form to the authorization's form" do + expect { reopen_authorization_request }.to change { AuthorizationRequest.find(authorization_request.id).form_uid }.from('api-impot-particulier-production').to('api-impot-particulier-sandbox') + end + + context 'when the authorization_request is from the same stage as the authorisation' do + let(:authorization) { authorization_request.latest_authorization_of_class 'AuthorizationRequest::APIImpotParticulier' } + + it "doesn't transitions the authorization_request class" do + expect { reopen_authorization_request }.not_to change { AuthorizationRequest.find(authorization_request.id).class } + end + + it "doesn't transitions the authorization_request form" do + expect { reopen_authorization_request }.not_to change { AuthorizationRequest.find(authorization_request.id).form_uid } + end + end + + context 'when the authorization_request is not the last of its stage' do + let(:authorization) { authorization_request.authorizations.first } + let!(:more_recent_authorization) { create(:authorization, request: authorization_request, authorization_request_class: 'AuthorizationRequest::APIImpotParticulierSandbox', created_at: Date.tomorrow) } + + it { is_expected.to be_failure } + end + end end end