diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb
new file mode 100644
index 0000000000..5081b148e4
--- /dev/null
+++ b/app/controllers/admin/reports_controller.rb
@@ -0,0 +1,27 @@
+module Admin
+ class ReportsController < BaseAdminController
+ before_action :ensure_service_operator
+
+ def index
+ end
+
+ def show
+ respond_to do |format|
+ format.csv do
+ send_data(report.to_csv, filename: report.filename)
+ end
+ end
+ end
+
+ private
+
+ def report
+ @report ||= case params[:name]
+ when "fe-approved-claims-with-failing-provider-verification"
+ Reports::FeApprovedClaimsWithFailingProviderVerification.new
+ else
+ raise ActiveRecord::RecordNotFound
+ end
+ end
+ end
+end
diff --git a/app/views/admin/claims/index.html.erb b/app/views/admin/claims/index.html.erb
index 64f5c251ff..d872447b07 100644
--- a/app/views/admin/claims/index.html.erb
+++ b/app/views/admin/claims/index.html.erb
@@ -16,6 +16,7 @@
<%= link_to "Upload TPS data", new_admin_tps_data_upload_path, class: "govuk-button govuk-button--secondary", data: { module: "govuk-button" }, role: :button %>
<%= link_to "Upload SLC data", new_admin_student_loans_data_upload_path, class: "govuk-button govuk-button--secondary", data: { module: "govuk-button" }, role: :button %>
<%= link_to "Upload fraud prevention data", new_admin_fraud_risk_csv_upload_path, class: "govuk-button govuk-button--secondary", data: { module: "govuk-button" }, role: :button %>
+ <%= link_to "Reports", admin_reports_path, class: "govuk-button govuk-button--secondary", data: { module: "govuk-button" }, role: :button %>
<%= render "allocations_form" %>
diff --git a/app/views/admin/reports/index.html.erb b/app/views/admin/reports/index.html.erb
new file mode 100644
index 0000000000..8a9d58e73e
--- /dev/null
+++ b/app/views/admin/reports/index.html.erb
@@ -0,0 +1,17 @@
+<% content_for :back_link do %>
+ <%= govuk_back_link href: admin_claims_path %>
+<% end %>
+
+
+
+
+ Reports
+
+
+ <%= govuk_button_link_to(
+ "FE TRI approved claims whereby the provider check status is 'failed'",
+ admin_report_path("fe-approved-claims-with-failing-provider-verification", format: :csv),
+ secondary: true
+ ) %>
+
+
diff --git a/config/routes.rb b/config/routes.rb
index 3d62b88694..0b94fe93ec 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -133,6 +133,7 @@ def matches?(request)
resources :tps_data_uploads, only: [:new, :create]
resources :fraud_risk_csv_uploads, only: [:new, :create]
resource :fraud_risk_csv_download, only: :show
+ resources :reports, only: [:index, :show], param: :name
resources :payroll_runs, only: [:index, :new, :create, :show, :destroy] do
resources :payment_confirmation_report_uploads, only: [:new, :create]
diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb
new file mode 100644
index 0000000000..93ba372d41
--- /dev/null
+++ b/spec/features/admin/reports_spec.rb
@@ -0,0 +1,102 @@
+require "rails_helper"
+
+RSpec.describe "Admin reports" do
+ around do |example|
+ travel_to Date.new(2024, 12, 6) do
+ example.run
+ end
+ end
+
+ describe "Approved FE claims with failing provider verification" do
+ it "returns a CSV report" do
+ claim = create(
+ :claim,
+ :approved,
+ policy: Policies::FurtherEducationPayments,
+ first_name: "Elizabeth",
+ surname: "Hoover",
+ qa_required: true,
+ eligibility_attributes: {
+ award_amount: 2_000,
+ contract_type: "permanent",
+ verification: {
+ assertions: [
+ {
+ name: "contract_type",
+ outcome: true
+ },
+ {
+ name: "teaching_responsibilities",
+ outcome: true
+ },
+ {
+ name: "further_education_teaching_start_year",
+ outcome: true
+ },
+ {
+ name: "teaching_hours_per_week",
+ outcome: true
+ },
+ {
+ name: "half_teaching_hours",
+ outcome: false
+ },
+ {
+ name: "subjects_taught",
+ outcome: false
+ },
+ {
+ name: "subject_to_formal_performance_action",
+ outcome: true
+ },
+ {
+ name: "subject_to_disciplinary_action",
+ outcome: true
+ }
+ ]
+ }
+ }
+ )
+
+ create(
+ :task,
+ :failed,
+ name: "provider_verification",
+ claim: claim
+ )
+
+ sign_in_as_service_operator
+
+ visit admin_claims_path
+
+ click_on "Reports"
+
+ click_on(
+ "FE TRI approved claims whereby the provider check status is 'failed'"
+ )
+
+ csv_data = page.body
+
+ csv = CSV.parse(csv_data, headers: true)
+ row = csv.first
+
+ expect(row.fetch("Claim reference")).to eq(claim.reference)
+ expect(row.fetch("Full name")).to eq("Elizabeth Hoover")
+ expect(row.fetch("Claim amount")).to eq("£2,000")
+ expect(row.fetch("Claim status")).to eq("Approved awaiting QA")
+ expect(row.fetch("Decision date")).to eq("06/12/2024")
+ expect(row.fetch("Decision agent")).to eq("Aaron Admin")
+ expect(row.fetch("Contract of employment")).to eq("Yes")
+ expect(row.fetch("Teaching responsibilities")).to eq("Yes")
+ expect(row.fetch("First 5 years of teaching")).to eq("Yes")
+ expect(row.fetch("One full term")).to eq("N/A")
+ expect(row.fetch("Timetabled teaching hours")).to eq("Yes")
+ expect(row.fetch("Age range taught")).to eq("No")
+ expect(row.fetch("Subject")).to eq("No")
+ expect(row.fetch("Course")).to eq("??")
+ expect(row.fetch("2.5 hours weekly teaching")).to eq("N/A")
+ expect(row.fetch("Performance")).to eq("Yes")
+ expect(row.fetch("Disciplinary")).to eq("Yes")
+ end
+ end
+end