Skip to content

Commit

Permalink
Reopening an authorization with previous stages gives a choice
Browse files Browse the repository at this point in the history
  • Loading branch information
JeSuisUnCaillou committed Feb 6, 2025
1 parent afb021a commit 13f7cde
Show file tree
Hide file tree
Showing 14 changed files with 157 additions and 11 deletions.
18 changes: 18 additions & 0 deletions app/assets/stylesheets/components/reopen_authorizations.css
Original file line number Diff line number Diff line change
@@ -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%;
}
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions app/models/authorization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion app/models/authorization_definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def name_with_stage
end

def reopenable?
!next_stage?
true
end

def instructors
Expand Down
6 changes: 6 additions & 0 deletions app/models/authorization_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion app/organizers/reopen_authorization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ class ReopenAuthorization < ApplicationOrganizer
context.authorization_request = context.authorization.request
end

organize ExecuteAuthorizationRequestTransitionWithCallbacks
organize TransitionAuthorizationRequestToStageOfAuthorization,
ExecuteAuthorizationRequestTransitionWithCallbacks
end
31 changes: 26 additions & 5 deletions app/views/reopen_authorizations/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
<h1 class="fr-modal__title">
<%= t('.title', authorization_name: @authorization.name) %>
</h1>

<p>
<%= t('.disclaimer').html_safe %>
</p>
Expand All @@ -16,13 +15,35 @@
<%= t('.invalid_request') %>
</p>
<% end %>

<% if @authorization.reopenable_to_another_stage? %>
<div class="fr-alert fr-alert--warning">
<h3 class="fr-alert__title"><%= t('.warning_for_stages.title') %></h3>
<p><%= t('.warning_for_stages.content').html_safe %>
</div>
<% end %>
</div>

<div class="fr-modal__footer">
<div class="fr-btns-group fr-btns-group--right fr-btns-group--inline-reverse fr-btns-group--inline-lg fr-btns-group--icon-left">
<%= link_to t('.cancel'), '#', class: %w(fr-btn fr-btn--secondary), aria: { controls: 'main-modal' } %>
<%= button_to t('.reopen'), url_for(controller: 'reopen_authorizations', action: 'create', authorization_request_id: @authorization.request_id, authorization_id: @authorization.id), class: %w(fr-btn fr-btn--primary fr-icon-success-line fr-btn--icon-left) %>
</div>
<% if @authorization.reopenable_to_another_stage? %>
<div class="reopen-stage-buttons-container">
<div class="reopen-stage-buttons">
<% @authorization.request.latest_authorizations_of_each_stage.each do |authorization| %>
<%= button_to t(".reopen_to_stage.#{authorization.definition.stage.type}"), url_for(controller: 'reopen_authorizations', action: 'create', authorization_request_id: @authorization.request_id, authorization_id: authorization.id), class: "fr-btn fr-btn--primary fr-icon-success-line fr-btn--icon-left reopen-stage-button" %>
<% end %>
</div>

<div class="reopen-stage-cancel">
<%= link_to t('.cancel'), '#', class: %w(fr-btn fr-btn--secondary), aria: { controls: 'main-modal' } %>
</div>
</div>

<% else %>
<div class="fr-btns-group fr-btns-group--right fr-btns-group--inline fr-btns-group--inline-lg fr-btns-group--icon-left">
<%= button_to t('.reopen'), url_for(controller: 'reopen_authorizations', action: 'create', authorization_request_id: @authorization.request_id, authorization_id: @authorization.id), class: %w(fr-btn fr-btn--primary fr-icon-success-line fr-btn--icon-left) %>
<%= link_to t('.cancel'), '#', class: %w(fr-btn fr-btn--secondary), aria: { controls: 'main-modal' } %>
</div>
<% end %>
</div>
</turbo-frame>
</div>
Expand Down
2 changes: 1 addition & 1 deletion config/authorization_definitions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions config/locales/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 : <br /><a class=\"fr-link\" target=\"_blank\" href=\"mailto:[email protected]\">[email protected]</a>"
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
Expand Down
15 changes: 15 additions & 0 deletions features/reouverture_habilitation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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"
7 changes: 5 additions & 2 deletions spec/factories/authorization_requests.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions spec/factories/authorizations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
16 changes: 16 additions & 0 deletions spec/models/authorization_request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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) }

Expand Down
35 changes: 34 additions & 1 deletion spec/organizers/reopen_authorization_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
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
end

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 }
Expand All @@ -29,12 +29,45 @@

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 }

it 'does not change state' do
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

0 comments on commit 13f7cde

Please sign in to comment.