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