diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb new file mode 100644 index 0000000000..86efbdfeb5 --- /dev/null +++ b/app/controllers/admin/reports_controller.rb @@ -0,0 +1,18 @@ +module Admin + class ReportsController < BaseAdminController + before_action :ensure_service_operator + + def index + @reports = Report.all + end + + def show + respond_to do |format| + format.csv { + report = Report.find(params[:id]) + send_data report.csv, filename: "#{report.name.parameterize(separator: "_")}_#{report.created_at.iso8601}.csv" + } + end + end + end +end diff --git a/app/jobs/reports_job.rb b/app/jobs/reports_job.rb new file mode 100644 index 0000000000..ae8a05102e --- /dev/null +++ b/app/jobs/reports_job.rb @@ -0,0 +1,9 @@ +class ReportsJob < CronJob + self.cron_expression = "0 6 * * 2#2" # second Tuesday of the month + + def perform + Rails.logger.info "Generating Ops reports" + + Report.create!(name: Reports::DuplicateClaims::NAME, csv: Reports::DuplicateClaims.new.to_csv) + end +end diff --git a/app/models/report.rb b/app/models/report.rb new file mode 100644 index 0000000000..1dfff22ffa --- /dev/null +++ b/app/models/report.rb @@ -0,0 +1,2 @@ +class Report < ApplicationRecord +end diff --git a/app/models/reports/duplicate_claims.rb b/app/models/reports/duplicate_claims.rb new file mode 100644 index 0000000000..1d3610ad54 --- /dev/null +++ b/app/models/reports/duplicate_claims.rb @@ -0,0 +1,47 @@ +require "csv" +require "excel_utils" + +module Reports + class DuplicateClaims + include Admin::ClaimsHelper + + NAME = "Duplicate Approved Claims" + HEADERS = [ + "Claim reference", + "Teacher reference number", + "Full name", + "Policy name", + "Claim amount", + "Claim status", + "Decision date", + "Decision agent" + ].freeze + + def initialize + @claims = Claim.approved.select { |claim| Claim::MatchingAttributeFinder.new(claim).matching_claims.any? } + end + + def to_csv + CSV.generate(write_headers: true, headers: HEADERS) do |csv| + @claims.each do |claim| + csv << row( + claim.reference, + claim.eligibility.teacher_reference_number, + claim.full_name, + claim.policy, + claim.award_amount, + status(claim), + claim.latest_decision.created_at, + claim.latest_decision.created_by.full_name + ) + end + end + end + + private + + def row(*entries) + entries.map { |entry| ExcelUtils.escape_formulas(entry) } + end + end +end diff --git a/app/models/reports/failed_provider_check_claims.rb b/app/models/reports/failed_provider_check_claims.rb new file mode 100644 index 0000000000..b295ba0a66 --- /dev/null +++ b/app/models/reports/failed_provider_check_claims.rb @@ -0,0 +1,8 @@ +require "csv" +require "excel_utils" + +module Reports + class FailedProviderCheckClaims + NAME = "Claims with failed provider check" + end +end diff --git a/app/models/reports/failed_qualification_claims.rb b/app/models/reports/failed_qualification_claims.rb new file mode 100644 index 0000000000..c5125cf232 --- /dev/null +++ b/app/models/reports/failed_qualification_claims.rb @@ -0,0 +1,39 @@ +require "csv" +require "excel_utils" + +module Reports + class FailedQualificationClaims + NAME = "Claims with failed qualification status" + HEADERS = [ + "Claim reference", + "Teacher reference number", + "Policy name", + "Decision date", + "Decision agent" + ].freeze + + def initialize + @claims = Claim.approved.select { |claim| Claim::MatchingAttributeFinder.new(claim).matching_claims.any? } + end + + def to_csv + CSV.generate(write_headers: true, headers: HEADERS) do |csv| + @claims.each do |claim| + csv << row( + claim.reference, + claim.eligibility.teacher_reference_number, + claim.policy, + claim.latest_decision.created_at, + claim.latest_decision.created_by.full_name + ) + end + end + end + + private + + def row(*entries) + entries.map { |entry| ExcelUtils.escape_formulas(entry) } + end + end +end diff --git a/app/views/admin/reports/index.html.erb b/app/views/admin/reports/index.html.erb new file mode 100644 index 0000000000..1167efca71 --- /dev/null +++ b/app/views/admin/reports/index.html.erb @@ -0,0 +1,30 @@ +<% content_for(:page_title) { page_title("Download Reports") } %> + +<% content_for :back_link do %> + <%= govuk_back_link href: admin_root_path %> +<% end %> + +
+
+

+ Reports +

+ + + + + + + + + + <% @reports.each do |report| %> + + + + + <% end %> + +
NameCreated At
<%= govuk_link_to report.name, admin_report_path(report, format: :csv) %><%= l(report.created_at) %>
+
+
diff --git a/app/views/application/_admin_menu.html.erb b/app/views/application/_admin_menu.html.erb index 5e70776cf8..a84eb0ee7a 100644 --- a/app/views/application/_admin_menu.html.erb +++ b/app/views/application/_admin_menu.html.erb @@ -14,6 +14,9 @@
  • <%= link_to "Manage services", admin_journey_configurations_path, class: "govuk-header__link" %>
  • +
  • + <%= link_to "Reports", admin_reports_path, class: "govuk-header__link" %> +
  • <% end %>
  • <%= link_to "Sign out", admin_sign_out_path, method: :delete, class: "govuk-header__link" %> diff --git a/config/analytics_blocklist.yml b/config/analytics_blocklist.yml index df4942ffd7..2ce8284395 100644 --- a/config/analytics_blocklist.yml +++ b/config/analytics_blocklist.yml @@ -131,3 +131,9 @@ - verification :journeys_sessions: - answers + :reports: + - id + - name + - csv + - created_at + - updated_at diff --git a/config/routes.rb b/config/routes.rb index b8b19f1d62..fcea29e2da 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -159,6 +159,7 @@ def matches?(request) resources :levelling_up_premium_payments_awards, only: [:index, :create] resource :eligible_ey_providers, only: [:create, :show], path: "eligible-early-years-providers" resource :eligible_fe_providers, only: [:create, :show], path: "eligible-further-education-providers" + resources :reports, only: [:index, :show] get "refresh-session", to: "sessions#refresh", as: :refresh_session diff --git a/db/migrate/20241007141638_create_reports.rb b/db/migrate/20241007141638_create_reports.rb new file mode 100644 index 0000000000..065bc39b5b --- /dev/null +++ b/db/migrate/20241007141638_create_reports.rb @@ -0,0 +1,10 @@ +class CreateReports < ActiveRecord::Migration[7.0] + def change + create_table :reports, id: :uuid do |t| + t.string :name + t.text :csv + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 2aa35382cd..a6c27f2fb4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2024_09_24_113642) do +ActiveRecord::Schema[7.0].define(version: 2024_10_07_141638) do # These are extensions that must be enabled in order to support this database enable_extension "citext" enable_extension "pg_trgm" @@ -111,7 +111,7 @@ t.text "onelogin_idv_first_name" t.text "onelogin_idv_last_name" t.date "onelogin_idv_date_of_birth" - t.datetime "started_at", precision: nil, null: false + t.datetime "started_at", precision: nil t.index ["academic_year"], name: "index_claims_on_academic_year" t.index ["created_at"], name: "index_claims_on_created_at" t.index ["eligibility_type", "eligibility_id"], name: "index_claims_on_eligibility_type_and_eligibility_id" @@ -419,6 +419,13 @@ t.string "itt_subject" end + create_table "reports", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + t.string "name" + t.text "csv" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "school_workforce_censuses", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| t.string "teacher_reference_number" t.datetime "created_at", null: false