diff --git a/app/assets/stylesheets/components/authorization-request-events.css b/app/assets/stylesheets/components/authorization-request-events.css index 3db30bada..c13a0ef3a 100644 --- a/app/assets/stylesheets/components/authorization-request-events.css +++ b/app/assets/stylesheets/components/authorization-request-events.css @@ -3,15 +3,12 @@ } ul.authorization-request-events li { - display: block; - position: relative; padding-bottom: var(--authorization-request-event-padding); padding-top: var(--authorization-request-event-padding); - border-bottom: 1px solid rgba(0,0,0,0.1); + border-top: 1px solid rgba(0,0,0,0.1); } -ul.authorization-request-events li time { - position: absolute; - right: 0; - top: var(--authorization-request-event-padding); -} + +ul.authorization-request-events li:last-of-type { + padding-bottom: 0; +} \ No newline at end of file diff --git a/app/assets/stylesheets/components/authorization_row.css b/app/assets/stylesheets/components/authorization_row.css new file mode 100644 index 000000000..c44f66029 --- /dev/null +++ b/app/assets/stylesheets/components/authorization_row.css @@ -0,0 +1,4 @@ +ul li.authorization-row { + display: block; + border-bottom: 1px solid rgba(0,0,0,0.1); +} \ No newline at end of file diff --git a/app/controllers/instruction/authorizations_controller.rb b/app/controllers/instruction/authorizations_controller.rb new file mode 100644 index 000000000..750610979 --- /dev/null +++ b/app/controllers/instruction/authorizations_controller.rb @@ -0,0 +1,17 @@ +class Instruction::AuthorizationsController < Instruction::AbstractAuthorizationRequestsController + def index + authorize [:instruction, @authorization_request], :show? + + @authorizations = AuthorizationRequest + .find(params[:authorization_request_id]) + .authorizations + .includes(:approving_instructor) + .order(created_at: :desc) + end + + private + + def layout_name + 'instruction/authorization_request' + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7af7471ce..a47589119 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -52,15 +52,23 @@ def authorization_request_reopening_badge(extra_css_class: nil) end def authorization_request_stage_badge(authorization_request, css_class: nil) + stage_badge(authorization_request.definition.stage.type, css_class: css_class) + end + + def authorization_stage_badge(authorization, css_class: nil) + stage_badge(authorization.definition.stage.type, css_class: css_class) + end + + def stage_badge(stage_type, css_class: nil) content_tag( :span, - t("authorization_request.stage.#{authorization_request.definition.stage.type}"), - class: ['fr-badge', 'fr-badge--no-icon', authorization_request_stage_badge_class(authorization_request), css_class], + t("authorization_request.stage.#{stage_type}"), + class: ['fr-badge', 'fr-badge--no-icon', stage_badge_class(stage_type), css_class], ) end - def authorization_request_stage_badge_class(authorization_request) - case authorization_request.definition.stage.type + def stage_badge_class(stage_type) + case stage_type when 'sandbox' 'fr-badge--brown-caramel' when 'production' diff --git a/app/models/authorization.rb b/app/models/authorization.rb index 893fe983b..1bc59f7eb 100644 --- a/app/models/authorization.rb +++ b/app/models/authorization.rb @@ -22,6 +22,20 @@ class Authorization < ApplicationRecord inverse_of: :authorization, dependent: :destroy + has_many :authorization_request_events, + as: :entity, + dependent: :nullify + + has_one :approve_authorization_request_event, + -> { where(name: 'approve').order(created_at: :desc).limit(1) }, + dependent: :nullify, + class_name: 'AuthorizationRequestEvent', + inverse_of: :entity + + has_one :approving_instructor, + through: :approve_authorization_request_event, + source: :user + scope :validated, -> { joins(:request).where(authorization_requests: { state: 'validated' }) } delegate :name, :kind, to: :request @@ -45,7 +59,11 @@ def request_as_validated # rubocop:enable Metrics/AbcSize def latest? - request.latest_authorization == self + if definition.stage.exists? + request.latest_authorization_of_class(authorization_request_class) == self + else + request.latest_authorization == self + end end def latest @@ -56,6 +74,10 @@ def authorization_request request end + def definition + authorization_request_class.constantize.definition + end + private def affect_snapshot_documents(request_as_validated) diff --git a/app/models/authorization_request.rb b/app/models/authorization_request.rb index 7d48fbcb4..c6bd73696 100644 --- a/app/models/authorization_request.rb +++ b/app/models/authorization_request.rb @@ -91,6 +91,10 @@ def latest_authorization authorizations.order(created_at: :desc).limit(1).first 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 + def events @events ||= AuthorizationRequestEventsQuery.new(self).perform end diff --git a/app/models/authorization_request_event.rb b/app/models/authorization_request_event.rb index d0018d943..a3ac3f40b 100644 --- a/app/models/authorization_request_event.rb +++ b/app/models/authorization_request_event.rb @@ -60,4 +60,10 @@ def authorization_request rescue NoMethodError entity end + + def authorization + entity.authorization + rescue NoMethodError + entity + end end diff --git a/app/policies/authorization_request_policy.rb b/app/policies/authorization_request_policy.rb index 11737b5ab..81bff840a 100644 --- a/app/policies/authorization_request_policy.rb +++ b/app/policies/authorization_request_policy.rb @@ -72,7 +72,8 @@ def transfer? end def start_next_stage? - record.definition.next_stage? && + same_user_and_organization? && + record.definition.next_stage? && record.validated? end diff --git a/app/views/authorization_request_forms/build/_header.html.erb b/app/views/authorization_request_forms/build/_header.html.erb index 2cbf8266c..805bb5e81 100644 --- a/app/views/authorization_request_forms/build/_header.html.erb +++ b/app/views/authorization_request_forms/build/_header.html.erb @@ -29,6 +29,17 @@ <% end %> + + <% if @authorization && policy([:instruction, @authorization_request]).show? %> +
<%= t(".text.#{type}") %>
- <%= link_to t('.link.text', authorization_id: @authorization_request.latest_authorization.id), latest_authorization_path(@authorization_request), class:"fr-btn" %> + <%= link_to t('.link.text', authorization_created_at: @authorization_request.latest_authorization.created_at.to_date), latest_authorization_path(@authorization_request), class:"fr-btn" %> diff --git a/app/views/authorization_requests/shared/_title.html.erb b/app/views/authorization_requests/shared/_title.html.erb index 116fb1500..80fbb6472 100644 --- a/app/views/authorization_requests/shared/_title.html.erb +++ b/app/views/authorization_requests/shared/_title.html.erb @@ -1,4 +1,9 @@ -<% if @authorization_request.reopening? && @authorization.blank? %> +<% if @authorization.present? %> ++ %{user_full_name} a soumis la demande +
Les données suivantes ont été modifiées par rapport aux informations pré-remplies du formulaire : %{text} submit_with_changes: text: | - %{user_full_name} a soumis la demande. - -+ %{user_full_name} a soumis la demande. +
La liste des changements depuis la dernière modération sont les suivants : %{text} @@ -699,6 +703,7 @@ fr: text: "%{user_full_name} a approuvé la demande" icon: checkbox-line color: success + view_authorization: Consulter l'habilitation request_changes: text: | %{user_full_name} a demandé des modifications sur la demande: @@ -792,6 +797,10 @@ fr: system_import: text: La demande d'habilitation a été importé depuis une autre source + authorizations: + index: + title: Historique des habilitations liées à cette demande + show_cta: Consulter l'habilitation cancel_authorization_reopenings: new: diff --git a/config/routes.rb b/config/routes.rb index 6ec5a7c5e..2b55ae196 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -84,6 +84,7 @@ resources :cancel_authorization_reopenings, only: %w[new create], path: 'annuler_reouverture', as: :cancel_reopening resources :authorization_request_events, only: :index, path: 'historique', as: :events + resources :authorizations, only: :index, path: 'habilitations' resources :messages, only: %w[index create], path: 'messages' end diff --git a/features/consultation_habilitation.feature b/features/consultation_habilitation.feature index 4ce5ed00d..d43b0a8f1 100644 --- a/features/consultation_habilitation.feature +++ b/features/consultation_habilitation.feature @@ -128,3 +128,7 @@ Fonctionnalité: Consultation d'une demande d'habilitation Et que je me rends sur l'habilitation validée du 01/01/2024 Alors la page contient "Attention, vous consultez une version ancienne de cette habilitation" + Scénario: Je consulte une habilitation sandbox validée dont la demande a obtenu une habilitation production + Quand j'ai 1 demande d'habilitation "API Impôt Particulier" validée + Et que je me rends sur la première habilitation validée + Alors la page ne contient pas "Attention, vous consultez une version ancienne de cette habilitation" \ No newline at end of file diff --git a/features/instructeurs/consultation_habilitation.feature b/features/instructeurs/consultation_habilitation.feature index 0d934b57d..04e9c0fd1 100644 --- a/features/instructeurs/consultation_habilitation.feature +++ b/features/instructeurs/consultation_habilitation.feature @@ -1,10 +1,11 @@ # language: fr -Fonctionnalité: Instruction: consultation d'une habilitation - Un instructeur peut consulter une habilitation et y voir des informations relatives aux entités +Fonctionnalité: Instruction: consultation d'une demande d'habilitation + Un instructeur peut consulter une demande d'habilitation et y voir des informations relatives aux entités Contexte: Sachant que je suis un instructeur "API Entreprise" + Et que je suis un instructeur "API Impôt Particulier" Et que je me connecte Scénario: Je vois les informations sur l'organisation et le demandeur @@ -19,3 +20,37 @@ Fonctionnalité: Instruction: consultation d'une habilitation Quand je me rends sur une demande d'habilitation "API Entreprise" réouverte Alors il y a un bouton "Consulter l'habilitation" + Scénario: Je ne vois pas d'onglet pour consulter les habilitations d'une demande sans habilitation + Quand je me rends sur une demande d'habilitation "API Entreprise" en brouillon + Alors la page ne contient pas "Toutes les habilitations" + + Scénario: Je vois un onglet pour consulter les habilitations d'une demande validée + Quand je me rends sur une demande d'habilitation "API Entreprise" validée + Et que je clique sur "Toutes les habilitations" + Alors la page contient "Historique des habilitations liées à cette demande" + + Scénario: Je peux naviguer d'une demande validée à une habilitation et revenir sur la demande + Quand je me rends sur une demande d'habilitation "API Entreprise" validée + Et que je clique sur "Toutes les habilitations" + Et que je clique sur "Consulter l'habilitation" + Alors la page contient "Cette habilitation est liée à la demande N°" + Et que je clique sur "demande N°" + Alors la page contient "Toutes les habilitations" + + Scénario: Je vois les habilitations de sandbox et production d'une demande DGFiP validée + Quand je me rends sur une demande d'habilitation "API Impôt Particulier" validée + Et que je clique sur "Toutes les habilitations" + Alors la page contient "Bac à sable" + Et la page contient "Production" + + Scénario: Je ne vois pas de mention de production dans le titre d'une habilitation sandbox + Quand je me rends sur une demande d'habilitation "API Impôt Particulier" validée + Et que je clique sur "Toutes les habilitations" + Et que je clique sur le dernier "Consulter l'habilitation" + Alors la page ne contient pas "Production" + + Scénario: Je ne vois pas de bouton "Démarrer ma demande d’habilitation en production" sur une habilitation sandbox + Quand je me rends sur une demande d'habilitation "API Impôt Particulier" validée + Et que je clique sur "Toutes les habilitations" + Et que je clique sur le dernier "Consulter l'habilitation" + Et la page ne contient pas "Démarrer ma demande d’habilitation en production" \ No newline at end of file diff --git a/features/instructeurs/historique_habilitation.feature b/features/instructeurs/historique_habilitation.feature index acc58525d..b1bd2552c 100644 --- a/features/instructeurs/historique_habilitation.feature +++ b/features/instructeurs/historique_habilitation.feature @@ -92,3 +92,11 @@ Fonctionnalité: Instruction: historique habilitation Et que je clique sur "Historique" Alors la page contient "Les données suivantes ont été modifiées par rapport aux informations pré-remplies du formulaire" + @DisableBullet + Scénario: Je vois un lien vers l'habilitation sur l'évènement de validation + Quand il y a 1 demande d'habilitation "Solution Portail des aides" soumise + Et que cette demande a été "validée" + Et que je vais sur la page instruction + Et que je clique sur "Consulter" + Et que je clique sur "Historique" + Alors la page contient un lien vers "demandes/.+/habilitations/.+" diff --git "a/features/instructeurs/r\303\251vocation_habilitation.feature" "b/features/instructeurs/r\303\251vocation_habilitation.feature" index 64cbc0586..e1ced112c 100644 --- "a/features/instructeurs/r\303\251vocation_habilitation.feature" +++ "b/features/instructeurs/r\303\251vocation_habilitation.feature" @@ -10,7 +10,7 @@ Fonctionnalité: Instruction: révocation d'habilitation @AvecCourriels @DisableBullet Scénario: Je révoque une habilitation avec un message valide - Quand je me rends sur une habilitation "API Service National" validée + Quand je me rends sur une demande d'habilitation "API Service National" validée Et je clique sur "Révoquer" Et que je remplis "Indiquez les motifs de révocation" avec "Une nouvelle demande a été validée" Et que je clique sur "Révoquer l'habilitation" diff --git a/features/step_definitions/authorization_requests_steps.rb b/features/step_definitions/authorization_requests_steps.rb index 0770f8f1a..079fa555f 100644 --- a/features/step_definitions/authorization_requests_steps.rb +++ b/features/step_definitions/authorization_requests_steps.rb @@ -382,7 +382,7 @@ end # https://rubular.com/r/eAlfvtPiXB46Ec -Quand(/je me rends sur une (?:demande d')?habilitation "([^"]+)"(?: de l'organisation "([^"]+)")?(?: (?:en|à))? ?(.+)?/) do |type, organization_name, status| +Quand(/je me rends sur une demande d'habilitation "([^"]+)"(?: de l'organisation "([^"]+)")?(?: (?:en|à))? ?(.+)?/) do |type, organization_name, status| attributes = {} attributes[:organization] = find_or_create_organization_by_name(organization_name) if organization_name.present? diff --git a/spec/factories/authorization_requests.rb b/spec/factories/authorization_requests.rb index dd8a457f4..4cfcf2402 100644 --- a/spec/factories/authorization_requests.rb +++ b/spec/factories/authorization_requests.rb @@ -177,11 +177,19 @@ trait :has_previous_authorization_validated do after(:create) do |authorization_request| + if authorization_request.authorizations.empty? + previous_authorization_created_at = authorization_request.created_at + else + date_offset = (authorization_request.latest_authorization.created_at - authorization_request.created_at) / 2 + previous_authorization_created_at = authorization_request.created_at + date_offset + end + 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, data: authorization_request.data.presence || { 'what' => 'ever' }, + created_at: previous_authorization_created_at ) end end diff --git a/spec/models/authorization_request_spec.rb b/spec/models/authorization_request_spec.rb index 04874073c..89c8a4415 100644 --- a/spec/models/authorization_request_spec.rb +++ b/spec/models/authorization_request_spec.rb @@ -352,4 +352,30 @@ it { is_expected.to contain_exactly(valid_authorization_request_with_one_scope, valid_authorization_request_with_more_scopes) } end + + describe '#latest_authorization_of_class' do + subject { authorization_request.latest_authorization_of_class(request_class) } + + 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') } + + context 'with production class' do + let(:request_class) { 'AuthorizationRequest::APIImpotParticulier' } + + it { is_expected.to eq(production_authorization) } + end + + context 'with sandbox class' do + let(:request_class) { 'AuthorizationRequest::APIImpotParticulierSandbox' } + + it { is_expected.to eq(sandbox_authorization) } + + context 'when there is a newer sandbox authorization' do + let!(:new_sandbox_authorization) { create(:authorization, request: authorization_request, authorization_request_class: request_class, created_at: Date.tomorrow) } + + it { is_expected.to eq(new_sandbox_authorization) } + end + end + end end diff --git a/spec/models/authorization_spec.rb b/spec/models/authorization_spec.rb index f2e011f58..e038ecb4c 100644 --- a/spec/models/authorization_spec.rb +++ b/spec/models/authorization_spec.rb @@ -62,4 +62,64 @@ end end end + + describe '#latest?' do + subject { authorization.latest? } + + let!(:authorization) { authorization_request.latest_authorization } + let!(:authorization_request) { create(:authorization_request, :validated) } + + it { is_expected.to be true } + + context 'when there is a newer authorization' do + before do + create(:authorization, request: authorization_request) + end + + it { is_expected.to be false } + end + + context 'when the definition has stages and the authorization is sandbox' do + let!(:authorization_request) { create(:authorization_request, :api_impot_particulier_production, :validated) } + let!(:authorization) { authorization_request.authorizations.order(created_at: :asc).first } + + context 'when a newer production authorization exists' do + it { is_expected.to be true } + end + + context 'when a newer sandbox authorization exists' do + before do + create(:authorization, request: authorization_request, authorization_request_class: authorization.authorization_request_class, created_at: Date.tomorrow) + end + + it { is_expected.to be false } + end + end + end + + describe '#approving_instructor' do + subject { authorization.approving_instructor } + + let!(:authorization) { create(:authorization) } + + context 'when there is no approving instructor' do + it { is_expected.to be_nil } + end + + context 'when there is an approve event' do + let!(:event) { create(:authorization_request_event, entity: authorization, name: 'approve') } + + it { is_expected.to eq(event.user) } + end + + context 'when there is several approve events' do + let!(:events) do + create_list(:authorization_request_event, 3, entity: authorization, name: 'approve') do |event, index| + event.update(created_at: Time.zone.now - index.days) + end + end + + it { is_expected.to eq(events.first.user) } + end + end end