Skip to content

Commit

Permalink
[CPDNQT-2152] Resend outcome to api (#1822)
Browse files Browse the repository at this point in the history
* [CPDNPQ-2152] Schedule resending outcomes to QTA

QTA = Qualified Teachers API

* [CPDNPQ-2152] Controller to resend api requests

* [CPDNPQ-2152] Only allow resending latest outcome

* [CPDNPQ-2152] Add resend link to outcomes table component

* [CPDNPQ-2152] Feature spec for resending to api

* [CPDNPQ-2152] Specs to check fields are nilified

* [CPDNPQ-2152] Swap 'resend' action to http `POST`

* [CPDNPQ-2152] Add caption text into outcomes component

* [CPDNPQ-2152] Remove N+1 query

Previously used when finding if declaration is latest
  • Loading branch information
jebw authored Oct 21, 2024
1 parent 554627a commit 5ae5745
Show file tree
Hide file tree
Showing 10 changed files with 471 additions and 13 deletions.
70 changes: 62 additions & 8 deletions app/components/npq_separation/admin/outcomes_table_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ class OutcomesTableComponent < ViewComponent::Base
attr_reader :outcomes

def initialize(outcomes)
@outcomes = outcomes
@outcomes = outcomes.sort_by(&:created_at).reverse
end

def call
render GovukComponent::TableComponent.new(head:, rows:)
render GovukComponent::TableComponent.new(head:, rows:) do |table|
table.with_caption(size: "s", text: caption_text, classes: "govuk-heading-s")
end
end

private
Expand All @@ -19,33 +21,85 @@ def head
"Completion date",
"Submitted by provider",
"Sent to TRA API",
"Recorded by TRA API",
"Recorded by API",
"",
]
end

def rows
outcomes.sort_by(&:created_at).reverse.map do |outcome|
outcomes.map do |outcome|
[
outcome.state.humanize,
outcome.completion_date.to_fs(:govuk_short),
outcome.created_at.to_date.to_fs(:govuk),
outcome.sent_to_qualified_teachers_api_at.try(:to_fs, :govuk_short).presence || "N/A",
outcome.created_at.to_date.to_fs(:govuk_short),
outcome.sent_to_qualified_teachers_api_at&.to_date&.to_fs(:govuk_short) ||
t("participant_outcomes.na"),
recorded_by_tra_api(outcome),
tra_api_resend_link(outcome),
]
end
end

def recorded_by_tra_api(outcome)
if outcome.qualified_teachers_api_request_successful.nil?
if outcome.latest_for_declaration?
tag.strong("Pending", class: "govuk-tag govuk-tag--blue")
tag.strong(t("participant_outcomes.pending"), class: "govuk-tag govuk-tag--blue")
else
tag.strong("N/A", class: "govuk-tag govuk-tag--yellow")
tag.strong(t("participant_outcomes.na"), class: "govuk-tag govuk-tag--yellow")
end
else
helpers.boolean_red_green_tag(outcome.qualified_teachers_api_request_successful)
end
end

def tra_api_resend_link(outcome)
return "" unless outcome.allow_resending_to_qualified_teachers_api?

govuk_button_to(t("participant_outcomes.resend"),
resend_npq_separation_admin_participant_outcome_path(outcome),
class: "govuk-!-margin-bottom-0")
end

def caption_text
title = t("participant_outcomes.declaration_outcomes")
latest = outcomes.first

return title if latest.nil?

outcome_description = if latest.has_passed?
success_sent_state_message(latest)
elsif latest.has_failed?
failed_sent_state_message(latest)
else
latest.state.capitalize
end

"#{title}: #{outcome_description}"
end

def success_sent_state_message(outcome)
if outcome.not_sent?
t("participant_outcomes.passed")
elsif outcome.sent_and_recorded?
t("participant_outcomes.passed_and_recorded")
elsif outcome.sent_but_not_recorded?
t("participant_outcomes.passed_but_not_recorded")
else
outcome.state.capitalize
end
end

def failed_sent_state_message(outcome)
if outcome.not_sent?
t("participant_outcomes.failed")
elsif outcome.sent_and_recorded?
t("participant_outcomes.failed_and_recorded")
elsif outcome.sent_but_not_recorded?
t("participant_outcomes.failed_but_not_recorded")
else
outcome.state.capitalize
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class NpqSeparation::Admin::ParticipantOutcomesController < NpqSeparation::AdminController
def resend
if participant_outcome.resend_to_qualified_teachers_api!
flash[:success] = "Rescheduled for delivery to Teacher Services"
else
flash[:error] = "Not suitable for resending to Teacher Services"
end

redirect_to npq_separation_admin_application_path(participant_outcome.application_id)
end

private

def participant_outcome
@participant_outcome ||= ParticipantOutcome.find(params[:id])
end
end
33 changes: 31 additions & 2 deletions app/models/participant_outcome.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class ParticipantOutcome < ApplicationRecord
validates :completion_date, presence: true
validate :completion_date_not_in_the_future

delegate :user, :lead_provider, :course, to: :declaration
delegate :user, :lead_provider, :course, :application_id, to: :declaration

enum state: {
passed: "passed",
Expand Down Expand Up @@ -59,8 +59,37 @@ def has_passed?
passed_state?
end

def has_failed?
return nil if voided_state?

failed_state?
end

def not_sent?
sent_to_qualified_teachers_api_at.nil?
end

def sent_and_recorded?
sent_to_qualified_teachers_api_at? && qualified_teachers_api_request_successful?
end

def sent_but_not_recorded?
sent_to_qualified_teachers_api_at? && qualified_teachers_api_request_successful == false
end

def latest_for_declaration?
declaration.participant_outcomes.latest == self
self == declaration.participant_outcomes.max_by(&:created_at)
end

def allow_resending_to_qualified_teachers_api?
sent_but_not_recorded? && latest_for_declaration?
end

def resend_to_qualified_teachers_api!
return false unless allow_resending_to_qualified_teachers_api?

update!(qualified_teachers_api_request_successful: nil,
sent_to_qualified_teachers_api_at: nil)
end

private
Expand Down
12 changes: 12 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,18 @@ en:
blank: "The '#/%{parameterized_attribute}' is missing from your request. Please include a 'passed' or 'failed' value and try again."
inclusion: "The attribute '#/%{parameterized_attribute}' can only include 'passed' or 'failed' values. If you need to void an outcome, you will need to void the associated 'completed' declaration."

participant_outcomes:
na: "N/A"
declaration_outcomes: "Declaration Outcomes"
passed: "Passed"
failed: "Failed"
passed_and_recorded: "Passed and recorded"
failed_and_recorded: "Failed and recorded"
passed_but_not_recorded: "Passed but not recorded"
failed_but_not_recorded: "Failed but not recorded"
pending: "Pending"
resend: "Resend"

time:
formats:
admin: "%R on %d/%m/%Y"
Expand Down
4 changes: 4 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@
resources :courses, only: %i[index show]
resources :users, only: %i[index show]

resources :participant_outcomes, only: %i[] do
member { post :resend }
end

namespace :finance do
resources :statements, only: %i[index show] do
collection do
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
require "rails_helper"

RSpec.describe NpqSeparation::Admin::OutcomesTableComponent, type: :component do
subject { page }

before do
render_inline(described_class.new(outcomes))
end
Expand All @@ -20,6 +22,56 @@
end
end

describe "Table caption text" do
context "when passed but not sent" do
let :outcomes do
create_list(:participant_outcome, 1, :passed)
end

it { is_expected.to have_css "caption", text: "Declaration Outcomes: Passed" }
end

context "when failed but not sent" do
let :outcomes do
create_list(:participant_outcome, 1, :failed)
end

it { is_expected.to have_css "caption", text: "Declaration Outcomes: Failed" }
end

context "when passed and sent and recorded" do
let :outcomes do
create_list(:participant_outcome, 1, :passed, :successfully_sent_to_qualified_teachers_api)
end

it { is_expected.to have_css "caption", text: "Declaration Outcomes: Passed and recorded" }
end

context "when failed and sent and recorded" do
let :outcomes do
create_list(:participant_outcome, 1, :failed, :successfully_sent_to_qualified_teachers_api)
end

it { is_expected.to have_css "caption", text: "Declaration Outcomes: Failed and recorded" }
end

context "when passed and sent but not recorded" do
let :outcomes do
create_list(:participant_outcome, 1, :passed, :unsuccessfully_sent_to_qualified_teachers_api)
end

it { is_expected.to have_css "caption", text: "Declaration Outcomes: Passed but not recorded" }
end

context "when failed and sent but not recored" do
let :outcomes do
create_list(:participant_outcome, 1, :failed, :unsuccessfully_sent_to_qualified_teachers_api)
end

it { is_expected.to have_css "caption", text: "Declaration Outcomes: Failed but not recorded" }
end
end

describe "'Sent to TRA API' column" do
let(:cell_text) { page.find("tbody tr td:nth-child(4)").text }

Expand All @@ -35,14 +87,14 @@
let(:outcomes) { [create(:participant_outcome, :sent_to_qualified_teachers_api)] }

it "renders the timestamp" do
expected = outcomes.first.sent_to_qualified_teachers_api_at.to_fs(:govuk_short)
expected = outcomes.first.sent_to_qualified_teachers_api_at.to_date.to_fs(:govuk_short)

expect(Time.zone.parse(cell_text)).to eq(expected)
end
end
end

describe "'Recorded by TRA API' column" do
describe "'Recorded by API' column" do
let(:cell_text) { page.find("tbody tr td:nth-child(5)").text }

context "when the outcome has not been sent to TRA" do
Expand Down Expand Up @@ -77,4 +129,37 @@
end
end
end

describe "Resend column" do
let(:resend_cell) { page.find("tbody tr td:nth-child(6)") }

context "when the outcome can be resent" do
let :outcomes do
create_list(:participant_outcome, 1, :unsuccessfully_sent_to_qualified_teachers_api)
end

let :resend_path do
Rails.application
.routes
.url_helpers
.resend_npq_separation_admin_participant_outcome_path(outcomes.first)
end

it "renders a resend button" do
within(%(tbody tr td:nth-child(6) form[action="#{resend_path}")) do |form|
expect(form).to have_button("Resend")
end
end
end

context "when the outcome cannot be resent" do
let :outcomes do
create_list(:participant_outcome, 1, :unsuccessfully_sent_to_qualified_teachers_api)
end

it "renders a blank cell" do
expect(resend_cell).to have_text("")
end
end
end
end
2 changes: 1 addition & 1 deletion spec/factories/participant_outcomes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
end

trait :sent_to_qualified_teachers_api do
sent_to_qualified_teachers_api_at { Time.zone.now - 1.hour }
sent_to_qualified_teachers_api_at { 1.hour.ago }
end

trait :not_sent_to_qualified_teachers_api do
Expand Down
17 changes: 17 additions & 0 deletions spec/features/npq_separation/admin/applications_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,21 @@

expect(page).to have_css("h1", text: school.name)
end

scenario "resending outcome to qualified teachers api" do
outcome = create(:participant_outcome, :unsuccessfully_sent_to_qualified_teachers_api)

visit npq_separation_admin_application_path(outcome.application_id)

expect(page).to have_css("h1", text: "Application for #{outcome.user.full_name}")

within(".govuk-table tbody tr:first-of-type td:last-of-type") do |action_cell|
expect(action_cell).to have_button("Resend")

click_button("Resend")
end

expect(page).to have_css("h1", text: "Application for #{outcome.user.full_name}")
expect(page).to have_css(".govuk-notification-banner--success", text: /rescheduled/i)
end
end
Loading

0 comments on commit 5ae5745

Please sign in to comment.