diff --git a/.rubocop.yml b/.rubocop.yml
index 9e23adcb..02fd138c 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -18,6 +18,7 @@ AllCops:
- 'bin/*'
- 'db/schema.rb'
- 'node_modules/**/*'
+ - 'spec/models/reports/applications_spec.rb'
Style/MethodCallWithArgsParentheses:
AllowParenthesesInMultilineCall: true
@@ -75,6 +76,7 @@ RSpec/ExampleLength:
Max: 10
Exclude:
- 'spec/features/*'
+ - 'spec/models/reports/home_office_spec.rb'
RSpec/NoExpectationExample:
Exclude:
diff --git a/Gemfile b/Gemfile
index 2b53e0ea..5c4afb4f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -10,7 +10,7 @@ gem "foreman"
gem "jbuilder"
gem "okcomputer"
gem "pg", "~> 1.5"
-gem "puma", "~> 6.3"
+gem "puma", "~> 6.4"
gem "rails", "~> 7.0.8"
gem "sprockets-rails"
gem "tzinfo-data", platforms: %i[mingw mswin x64_mingw jruby]
@@ -36,6 +36,9 @@ gem "audited", "~> 5.3", ">= 5.3.3"
gem "config", "~> 4.2"
gem "devise", "~> 4.9"
gem "dfe-analytics", github: "DFE-Digital/dfe-analytics", tag: "v1.10.1"
+gem "flipper"
+gem "flipper-active_record"
+gem "flipper-ui"
gem "httparty", "~> 0.21"
gem "invisible_captcha"
gem "omniauth-azure-activedirectory-v2"
diff --git a/Gemfile.lock b/Gemfile.lock
index e4ad37de..3e6a1beb 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -92,6 +92,7 @@ GEM
bootsnap (1.16.0)
msgpack (~> 1.2)
brakeman (6.0.1)
+ brow (0.4.1)
builder (3.2.4)
bundler-audit (0.9.1)
bundler (>= 1.2.0, < 3)
@@ -179,6 +180,18 @@ GEM
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.2)
+ flipper (1.0.0)
+ brow (~> 0.4.1)
+ concurrent-ruby (< 2)
+ flipper-active_record (1.0.0)
+ activerecord (>= 4.2, < 8)
+ flipper (~> 1.0.0)
+ flipper-ui (1.0.0)
+ erubi (>= 1.0.0, < 2.0.0)
+ flipper (~> 1.0.0)
+ rack (>= 1.4, < 4)
+ rack-protection (>= 1.5.3, <= 4.0.0)
+ sanitize (< 7)
foreman (0.87.2)
fugit (1.8.1)
et-orbi (~> 1, >= 1.2.7)
@@ -317,7 +330,7 @@ GEM
byebug (~> 11.0)
pry (>= 0.13, < 0.15)
public_suffix (5.0.1)
- puma (6.3.1)
+ puma (6.4.0)
nio4r (~> 2.0)
raabro (1.4.0)
racc (1.7.1)
@@ -430,6 +443,9 @@ GEM
rubocop-factory_bot (~> 2.22)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
+ sanitize (6.1.0)
+ crass (~> 1.0.2)
+ nokogiri (>= 1.12.0)
scenic (1.7.0)
activerecord (>= 4.0.0)
railties (>= 4.0.0)
@@ -521,6 +537,9 @@ DEPENDENCIES
dotenv-rails
factory_bot_rails
faker
+ flipper
+ flipper-active_record
+ flipper-ui
foreman
govuk-components (= 4.1.1)
govuk_design_system_formbuilder
@@ -536,7 +555,7 @@ DEPENDENCIES
pg (~> 1.5)
phonelib
pry-byebug
- puma (~> 6.3)
+ puma (~> 6.4)
rails (~> 7.0.8)
rspec-rails
rubocop-govuk
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 9e1d6897..7a7f7eef 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -7,37 +7,6 @@ class ApplicationController < ActionController::Base
before_action :check_service_open!
- delegate :application_route, to: :current_application
- helper_method :application_route
-
- def check_application!
- return if session["application_id"]
-
- redirect_to(root_path)
- end
-
- def current_application
- Application.in_progress.find(session["application_id"])
- end
- helper_method :current_application
-
- def current_applicant
- current_application.applicant
- end
- helper_method :current_applicant
-
- def check_teacher!
- return unless application_route != "teacher"
-
- redirect_to(new_applicants_application_route_path)
- end
-
- def check_trainee!
- return unless application_route != "salaried_trainee"
-
- redirect_to(new_applicants_application_route_path)
- end
-
def check_service_open!
return if request.path == destroy_user_session_path # skip this for log out page
return if Gatekeeper.application_open?
diff --git a/app/controllers/system_admin/applicants_controller.rb b/app/controllers/system_admin/applicants_controller.rb
index 9a868b41..06bbca36 100644
--- a/app/controllers/system_admin/applicants_controller.rb
+++ b/app/controllers/system_admin/applicants_controller.rb
@@ -9,7 +9,7 @@ class ApplicantsController < AdminController
include Pagy::Backend
def index
- results = Application.submitted
+ results = Application.all
.search(params[:search])
.filter_by_status(params[:status])
.order(created_at: :desc)
@@ -18,20 +18,7 @@ def index
@pagy, @applications = pagy_array(results)
session[:filter_status] = params[:status]
- session[:application_ids] = @applications.map(&:id)
- end
-
- def download_qa_csv
- status = session[:filter_status]
- application_ids = session[:application_ids]
-
- applications = Application.where(id: application_ids).reject(&:qa?)
-
- applications.each(&:mark_as_qa!)
-
- report = Reports::QaReport.new(applications, status)
- create_audit(action: "Downloaded QA CSV report (#{status.humanize})")
- send_data(report.csv, filename: report.name)
+ session[:application_ids] = results.map(&:id)
end
def duplicates
diff --git a/app/controllers/system_admin/reports_controller.rb b/app/controllers/system_admin/reports_controller.rb
index e4187e58..b09876d9 100644
--- a/app/controllers/system_admin/reports_controller.rb
+++ b/app/controllers/system_admin/reports_controller.rb
@@ -3,20 +3,16 @@ class ReportsController < AdminController
def index; end
def show
- report = find_report(params[:id])
- create_audit(action: "Downloaded #{report.class.to_s.capitalize} report")
+ service = Report.call(params[:id], **report_params)
+ create_audit(action: "Downloaded #{service.report_name} report")
- send_data(report.csv, filename: report.name)
+ send_data(service.data, filename: service.filename)
end
private
- def find_report(report_id)
- {
- home_office: Reports::HomeOffice.new,
- standing_data: Reports::StandingData.new,
- payroll: Reports::Payroll.new,
- }.with_indifferent_access.fetch(report_id)
+ def report_params
+ params.permit(:id, :status)
end
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index dd7e9147..e5e2f9cd 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -20,4 +20,18 @@ def mailto_irp_express_interest
def banner_feedback_form
govuk_link_to("feedback", "https://forms.office.com/e/p45Wm1Vmxg", target: "_blank")
end
+
+ def application_statuses
+ ApplicationProgress
+ .statuses
+ .keys
+ .map { |status| [status.humanize, status] }
+ end
+
+ def application_statuses_options(selected: nil, all_statuses: false)
+ statuses = application_statuses
+ statuses = application_statuses.unshift(["All statuses", ""]) if all_statuses
+
+ options_for_select(statuses, selected:)
+ end
end
diff --git a/app/javascript/packs/stylesheets/custom.scss b/app/javascript/packs/stylesheets/custom.scss
index 5d0ffa4d..46279d85 100644
--- a/app/javascript/packs/stylesheets/custom.scss
+++ b/app/javascript/packs/stylesheets/custom.scss
@@ -68,6 +68,11 @@
padding: 5px;
font-size: 20px;
}
+
+ a {
+ color: #fff;
+ text-decoration: none;
+ }
}
}
diff --git a/app/models/application.rb b/app/models/application.rb
index 358f13ab..79e54f6e 100644
--- a/app/models/application.rb
+++ b/app/models/application.rb
@@ -30,10 +30,6 @@ class Application < ApplicationRecord
delegate :sla_breached?, to: :application_progress
delegate :status, to: :application_progress, allow_nil: false
- default_scope { submitted }
- scope :submitted, -> { where.not(urn: nil) }
- scope :in_progress, -> { unscoped.where(urn: nil) }
-
scope :search, lambda { |term|
return if term.blank?
@@ -64,22 +60,11 @@ def mark_as_qa!
unique_by: %i[application_id status])
end
- with_options if: :submitted? do
- validates(:application_date, presence: true)
- validates(:application_route, presence: true)
- validates(:date_of_entry, presence: true)
- validates(:start_date, presence: true)
- validates(:subject, presence: true)
- validates(:visa_type, presence: true)
- validates(:urn, presence: true)
- validates(:applicant, presence: true)
- end
-
- def assign_urn!
- update!(urn: Urn.generate(application_route))
- end
-
- def submitted?
- urn.present?
- end
+ validates(:application_date, presence: true)
+ validates(:application_route, presence: true)
+ validates(:date_of_entry, presence: true)
+ validates(:start_date, presence: true)
+ validates(:subject, presence: true)
+ validates(:visa_type, presence: true)
+ validates(:applicant, presence: true)
end
diff --git a/app/models/kpis.rb b/app/models/kpis.rb
index 10ac1596..227a536b 100644
--- a/app/models/kpis.rb
+++ b/app/models/kpis.rb
@@ -4,7 +4,7 @@ def initialize
end
def total_applications
- Application.submitted.count
+ Application.count
end
def total_rejections
@@ -62,4 +62,8 @@ def time_to_banking_approval
def time_to_payment_confirmation
TimeToPaymentConfirmationQuery.new.call
end
+
+ def status_breakdown
+ StatusBreakdownQuery.call
+ end
end
diff --git a/app/models/policies/contract_start_date.rb b/app/models/policies/contract_start_date.rb
deleted file mode 100644
index 925282eb..00000000
--- a/app/models/policies/contract_start_date.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-# frozen_string_literal: true
-
-# The `ContractStartDate` class defines the policy for contract start dates.
-#
-# This policy determines whether a given start date is eligible based on the
-# rule that employment start dates from the first Monday in July are considered
-# eligible.
-#
-# It calculates the first Monday in July of the current year and compares
-# it with the given start date to determine eligibility.
-#
-# @example
-# Policies::ContractStartDate.eligible?(Date.new(2023, 7, 3)) #=> true
-#
-# @example
-# Policies::ContractStartDate.eligible?(Date.new(2023, 6, 30)) #=> false
-#
-module Policies
- class ContractStartDate
- def self.eligible?(start_date)
- current_year = Date.current.year
- first_monday_in_july = Date.new(current_year, 7, 1)
- .beginning_of_month
- .next_occurring(:monday)
-
- start_date >= first_monday_in_july
- end
- end
-end
diff --git a/app/models/policies/entry_date.rb b/app/models/policies/entry_date.rb
deleted file mode 100644
index 6837f009..00000000
--- a/app/models/policies/entry_date.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-# The `EntryDate` class defines the policy for contract start dates.
-#
-# This Policy determines whether a given entry date is eligible based on the
-# start date of the contract. If the applicant entered the country 3 months
-# or less before the start of the contract, then they are eligible.
-#
-# @example
-# Policies::ContractStartDate.eligible?(
-# Date.new(2023, 1, 2),
-# Date.new(2023, 4, 2)
-# ) #=> true
-#
-module Policies
- class EntryDate
- def self.eligible?(entry_date, start_date)
- entry_date >= start_date - 3.months
- end
- end
-end
diff --git a/app/models/reports/applications.rb b/app/models/reports/applications.rb
new file mode 100644
index 00000000..870ce2eb
--- /dev/null
+++ b/app/models/reports/applications.rb
@@ -0,0 +1,79 @@
+module Reports
+ class Applications
+ def name
+ current_time = Time.zone.now.strftime("%Y%m%d-%H%M%S")
+
+ "Applications-Report-#{current_time}.csv"
+ end
+
+ def csv
+ CSV.generate do |csv|
+ csv << header
+ rows.find_each(batch_size: 50) { |row| csv << columns(row) }
+ end
+ end
+
+ private
+
+ def header
+ %i[
+ ip_address
+ given_name
+ middle_name
+ family_name
+ email_address
+ phone_number
+ date_of_birth
+ sex
+ passport_number
+ nationality
+ student_loan
+ address_line_1
+ address_line_2
+ city
+ postcode
+ application_date
+ application_route
+ status
+ date_of_entry
+ start_date
+ subject
+ urn
+ visa_type
+ ].map { _1.to_s.titleize }
+ end
+
+ def columns(application)
+ applicant = application.applicant
+ [
+ applicant.ip_address,
+ applicant.given_name,
+ applicant.middle_name,
+ applicant.family_name,
+ applicant.email_address,
+ applicant.phone_number,
+ applicant.date_of_birth,
+ applicant.sex,
+ applicant.passport_number,
+ applicant.nationality,
+ applicant.student_loan,
+ applicant.address.address_line_1,
+ applicant.address.address_line_2,
+ applicant.address.city,
+ applicant.address.postcode,
+ application.application_date,
+ application.application_route,
+ application.status,
+ application.date_of_entry,
+ application.start_date,
+ application.subject,
+ application.urn,
+ application.visa_type,
+ ]
+ end
+
+ def rows
+ Application.includes(:applicant, :application_progress, applicant: :address)
+ end
+ end
+end
diff --git a/app/models/reports/home_office.rb b/app/models/reports/home_office.rb
index e54f2db8..b8aad595 100644
--- a/app/models/reports/home_office.rb
+++ b/app/models/reports/home_office.rb
@@ -25,10 +25,16 @@ def rows
application.urn,
application.applicant.full_name,
application.applicant.date_of_birth,
+ nil,
application.applicant.nationality,
+ nil,
application.applicant.passport_number,
nil,
nil,
+ nil,
+ nil,
+ nil,
+ nil,
]
end
end
@@ -49,13 +55,19 @@ def applications
def header
[
- "URN",
+ "ID",
"Full Name",
"DOB",
+ "Gender",
"Nationality",
+ "Place of Birth",
"Passport Number",
- "Visa Type",
- "Date of UK entry",
+ "National Insurance Number",
+ "Address",
+ "Postcode",
+ "Email",
+ "Telephone",
+ "Reference",
]
end
end
diff --git a/app/queries/average_age_query.rb b/app/queries/average_age_query.rb
index 08240ad1..383d827d 100644
--- a/app/queries/average_age_query.rb
+++ b/app/queries/average_age_query.rb
@@ -1,6 +1,6 @@
class AverageAgeQuery
def initialize(relation = Applicant.all)
- @relation = relation.joins(:application).merge(Application.submitted)
+ @relation = relation.joins(:application).merge(Application.all)
end
def call
diff --git a/app/queries/gender_breakdown_query.rb b/app/queries/gender_breakdown_query.rb
index 73b354bf..98340ba9 100644
--- a/app/queries/gender_breakdown_query.rb
+++ b/app/queries/gender_breakdown_query.rb
@@ -1,6 +1,6 @@
class GenderBreakdownQuery
def initialize(relation = Applicant.all)
- @relation = relation.joins(:application).merge(Application.submitted)
+ @relation = relation.joins(:application).merge(Application.all)
end
def call
diff --git a/app/queries/nationality_breakdown_query.rb b/app/queries/nationality_breakdown_query.rb
index b22f006d..6b075920 100644
--- a/app/queries/nationality_breakdown_query.rb
+++ b/app/queries/nationality_breakdown_query.rb
@@ -1,6 +1,6 @@
class NationalityBreakdownQuery
def initialize(relation = Applicant.all)
- @relation = relation.joins(:application).merge(Application.submitted)
+ @relation = relation.joins(:application).merge(Application.all)
end
def call
diff --git a/app/queries/rejection_reason_breakdown_query.rb b/app/queries/rejection_reason_breakdown_query.rb
index a9d5aea9..ee8a6a07 100644
--- a/app/queries/rejection_reason_breakdown_query.rb
+++ b/app/queries/rejection_reason_breakdown_query.rb
@@ -1,6 +1,6 @@
class RejectionReasonBreakdownQuery
def initialize(relation = ApplicationProgress.all)
- @relation = relation.joins(:application).merge(Application.submitted).where.not(rejection_reason: nil)
+ @relation = relation.joins(:application).merge(Application.all).where.not(rejection_reason: nil)
end
def call
diff --git a/app/queries/route_breakdown_query.rb b/app/queries/route_breakdown_query.rb
index 2b865dc7..2df254ba 100644
--- a/app/queries/route_breakdown_query.rb
+++ b/app/queries/route_breakdown_query.rb
@@ -1,6 +1,6 @@
class RouteBreakdownQuery
def initialize(relation = Application.all)
- @relation = relation.submitted
+ @relation = relation
end
def call
diff --git a/app/queries/status_breakdown_query.rb b/app/queries/status_breakdown_query.rb
new file mode 100644
index 00000000..2b08f888
--- /dev/null
+++ b/app/queries/status_breakdown_query.rb
@@ -0,0 +1,22 @@
+class StatusBreakdownQuery
+ def self.call
+ new.execute
+ end
+
+ def execute
+ ordered_with_defaults(status_breakdown)
+ end
+
+private
+
+ def status_breakdown
+ ApplicationProgress.group(:status).count
+ end
+
+ def ordered_with_defaults(breakdown)
+ ApplicationProgress.statuses.each_with_object({}) do |(key, _), hsh|
+ hsh[key] = breakdown.fetch(key, 0)
+ hsh
+ end
+ end
+end
diff --git a/app/queries/subject_breakdown_query.rb b/app/queries/subject_breakdown_query.rb
index ea0aa1db..19559899 100644
--- a/app/queries/subject_breakdown_query.rb
+++ b/app/queries/subject_breakdown_query.rb
@@ -1,6 +1,6 @@
class SubjectBreakdownQuery
def initialize(relation = Application.all)
- @relation = relation.submitted
+ @relation = relation
end
def call
diff --git a/app/queries/visa_breakdown_query.rb b/app/queries/visa_breakdown_query.rb
index 80d815b7..a55afdf1 100644
--- a/app/queries/visa_breakdown_query.rb
+++ b/app/queries/visa_breakdown_query.rb
@@ -1,6 +1,6 @@
class VisaBreakdownQuery
def initialize(relation = Application.all)
- @relation = relation.submitted
+ @relation = relation
end
def call
diff --git a/app/services/report.rb b/app/services/report.rb
new file mode 100644
index 00000000..dbfb50b4
--- /dev/null
+++ b/app/services/report.rb
@@ -0,0 +1,61 @@
+class Report
+ REGISTERED_REPORTS = {
+ home_office: Reports::HomeOffice,
+ standing_data: Reports::StandingData,
+ payroll: Reports::Payroll,
+ applications: Reports::Applications,
+ qa: Reports::QaReport,
+ }.freeze
+
+ def self.call(...)
+ service = new(...)
+ service.data
+ service
+ end
+
+ def initialize(report_id, **kwargs)
+ @kwargs = kwargs&.symbolize_keys || {}
+ @report_class = REGISTERED_REPORTS.with_indifferent_access.fetch(report_id)
+ rescue KeyError
+ raise(ArgumentError, "Invalid report id #{report_id}")
+ end
+
+ def report_name
+ report_class.to_s.capitalize
+ end
+
+ def filename
+ report.name
+ end
+
+ def data
+ report.csv
+ end
+
+private
+
+ attr_reader :report_class, :kwargs
+
+ def report
+ return @report if @report
+ return @report = report_class.new(*report_args) if report_args
+
+ @report = report_class.new
+ end
+
+ def report_args
+ return qa_report_args if report_class == Reports::QaReport
+
+ nil
+ end
+
+ def qa_report_args
+ return @qa_report_args if @qa_report_args
+
+ status = kwargs.fetch(:status)
+ applications = Application.filter_by_status(status).reject(&:qa?)
+ applications.each(&:mark_as_qa!)
+
+ @qa_report_args = [applications, status]
+ end
+end
diff --git a/app/services/submit_form.rb b/app/services/submit_form.rb
index 84f9a20e..939f2756 100644
--- a/app/services/submit_form.rb
+++ b/app/services/submit_form.rb
@@ -36,15 +36,15 @@ def submit_form!
def create_application_records
ActiveRecord::Base.transaction do
- school = create_school
- applicant = create_applicant(school)
- @application = create_application(applicant)
+ create_school
+ create_applicant
+ create_application
delete_form
end
end
def create_school
- School.create!(
+ @school = School.create!(
name: form.school_name,
headteacher_name: form.school_headteacher_name,
address_attributes: {
@@ -56,8 +56,8 @@ def create_school
)
end
- def create_applicant(school)
- Applicant.create!(
+ def create_applicant
+ @applicant = Applicant.create!(
ip_address: ip_address,
given_name: form.given_name,
middle_name: form.middle_name,
@@ -75,13 +75,13 @@ def create_applicant(school)
city: form.city,
postcode: form.postcode,
},
- school: school,
+ school: @school,
)
end
- def create_application(applicant)
- Application.create!(
- applicant: applicant,
+ def create_application
+ @application = Application.create!(
+ applicant: @applicant,
application_date: Date.current.to_s,
application_route: form.application_route,
application_progress: ApplicationProgress.new,
@@ -100,7 +100,7 @@ def delete_form
def send_applicant_email
GovukNotifyMailer
.with(
- email: application.applicant.email_address,
+ email: @applicant.email_address,
urn: application.urn,
)
.application_submission
diff --git a/app/steps/personal_details_step.rb b/app/steps/personal_details_step.rb
index e792cd85..524e9a49 100644
--- a/app/steps/personal_details_step.rb
+++ b/app/steps/personal_details_step.rb
@@ -80,10 +80,10 @@ def minimum_age
return unless date_of_birth.present?
# rubocop:enable Rails/Blank
- errors.add(:date_of_birth, :below_min_age) if date_of_birth > MIN_AGE.years.ago.to_date
+ errors.add(:date_of_birth, :below_min_age) unless date_of_birth <= MIN_AGE.years.ago.to_date
end
MAX_AGE = 80
- MIN_AGE = 22
+ MIN_AGE = 21
private_constant :MAX_AGE, :MIN_AGE
end
diff --git a/app/views/pages/closed.html.erb b/app/views/pages/closed.html.erb
index b8680206..50934ec0 100644
--- a/app/views/pages/closed.html.erb
+++ b/app/views/pages/closed.html.erb
@@ -39,11 +39,11 @@
Check your eligibility
- <%= govuk_link_to("Check full eligibility details for teachers.", "https://getintoteaching.education.gov.uk/non-uk-teachers/get-an-international-relocation-payment", { target: "_blank" })%>
+ <%= govuk_link_to("Get an international relocation payment: criteria for teachers", "https://getintoteaching.education.gov.uk/non-uk-teachers/get-an-international-relocation-payment/#criteria-for-teachers", { target: "_blank" })%>
- <%= govuk_link_to("Check full eligibility details for trainees.", "https://getintoteaching.education.gov.uk/non-uk-teachers/get-an-international-relocation-payment", { target: "_blank" })%>
+ <%= govuk_link_to("Get an international relocation payment: criteria for trainee teachers", "https://getintoteaching.education.gov.uk/non-uk-teachers/get-an-international-relocation-payment/#criteria-for-teachers", { target: "_blank" })%>
Get help
diff --git a/app/views/system_admin/applicants/duplicates.html.erb b/app/views/system_admin/applicants/duplicates.html.erb
index 031436f1..d7aa9301 100644
--- a/app/views/system_admin/applicants/duplicates.html.erb
+++ b/app/views/system_admin/applicants/duplicates.html.erb
@@ -10,6 +10,7 @@
row.with_cell(text: 'Email')
row.with_cell(text: 'Phone number')
row.with_cell(text: 'Passport')
+ row.with_cell(text: 'IP Address')
row.with_cell(text: 'Status')
end
end
@@ -37,8 +38,15 @@
row.with_cell(text: duplicate.passport_number)
end
+ if duplicate.applicant.ip_address.present?
+ row.with_cell(text: govuk_link_to(duplicate.applicant.ip_address, duplicates_path(search: duplicate.applicant.ip_address)))
+ else
+ row.with_cell(text: duplicate.applicant.ip_address)
+ end
row.with_cell(text: duplicate.status.humanize)
end
end
end
end %>
+
+<%= govuk_pagination(pagy: @pagy) %>
diff --git a/app/views/system_admin/applicants/index.html.erb b/app/views/system_admin/applicants/index.html.erb
index dc6637ab..5aba9eb6 100644
--- a/app/views/system_admin/applicants/index.html.erb
+++ b/app/views/system_admin/applicants/index.html.erb
@@ -1,8 +1,3 @@
-<% statuses = options_for_select(
- ApplicationProgress.statuses.keys.map { |status| [status.humanize, status] }.unshift(['All statuses', '']),
- selected: params[:status]
- )
-%>
<%= form_with(url: applicants_path, method: :get, id: :search) do |f| %>
<%= f.govuk_text_field :search, value: params[:search], label: { text: 'Search' }, hint: { text: 'Search by name, email, passport number or unique reference number (URN)' } %>
@@ -10,7 +5,7 @@
- <%= f.govuk_select :status, statuses, label: { text: "Filter by application status" } %>
+ <%= f.govuk_select :status, application_statuses_options(selected: params[:status], all_statuses: true), label: { text: "Filter by application status" } %>
@@ -25,9 +20,6 @@
<%= f.govuk_submit 'Search', secondary: true %>
- <% if session[:filter_status].present? %>
- <%= link_to "Download QA CSV", download_qa_csv_applicants_path, class: "govuk-button" %>
- <% end %>
<% end %>
<%= govuk_table(classes: "applicants-table") do |table|
diff --git a/app/views/system_admin/dashboard/show.html.erb b/app/views/system_admin/dashboard/show.html.erb
index 4b160375..f2f37d87 100644
--- a/app/views/system_admin/dashboard/show.html.erb
+++ b/app/views/system_admin/dashboard/show.html.erb
@@ -141,4 +141,17 @@
+
+
+
diff --git a/app/views/system_admin/reports/index.html.erb b/app/views/system_admin/reports/index.html.erb
index 50268e67..5e78d0f7 100644
--- a/app/views/system_admin/reports/index.html.erb
+++ b/app/views/system_admin/reports/index.html.erb
@@ -28,3 +28,33 @@
<%= link_to "Download", report_path(:payroll), class: "govuk-button" %>
+
+
+
Applications Data report
+
+ Download a CSV file of all applications
+
+
+ <%= link_to "Download", report_path(:applications), class: "govuk-button" %>
+
+
+
+
+
QA reports
+
+ Download a QA CSV file
+
+
+
+ <%= form_with(url: report_path(:qa), method: :get) do |f| %>
+
+
+ <%= f.govuk_select :status, application_statuses_options, label: { text: "Filter by application status" } %>
+
+
+ <%= f.govuk_submit 'Download', class: "govuk-button"%>
+
+
+ <% end %>
+
+
diff --git a/config/initializers/flipper.rb b/config/initializers/flipper.rb
new file mode 100644
index 00000000..b75f9716
--- /dev/null
+++ b/config/initializers/flipper.rb
@@ -0,0 +1,6 @@
+class CanAccessFlipperUI
+ def self.matches?(request)
+ current_user = request.env["warden"].user
+ current_user.present?
+ end
+end
diff --git a/config/locales/steps.en.yml b/config/locales/steps.en.yml
index 1b757e40..5f49c753 100644
--- a/config/locales/steps.en.yml
+++ b/config/locales/steps.en.yml
@@ -195,7 +195,7 @@ en:
blank: Enter your date of birth
not_in_future: Date of birth cannot be in the future
over_max_age: Age must be below 80
- below_min_age: Age must be above 22
+ below_min_age: Age must be above 21
sex:
blank: Enter your sex
inclusion: Enter your sex
diff --git a/config/routes.rb b/config/routes.rb
index 25bd141e..ad1fdbab 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -25,17 +25,16 @@
end
scope module: :system_admin, path: "system-admin" do
- resources :applicants, only: %i[index show edit update] do
- collection do
- get :download_qa_csv
- end
- end
-
+ resources :applicants, only: %i[index show edit update]
resources :users, except: %i[show]
resource :settings, only: %i[edit update]
get "/dashboard", to: "dashboard#show"
resources "reports", only: %i[show index]
get "/duplicates", to: "applicants#duplicates"
get "/audits", to: "audits#index"
+
+ constraints CanAccessFlipperUI do
+ mount Flipper::UI.app(Flipper) => "/features"
+ end
end
end
diff --git a/db/migrate/20230915092215_create_flipper_tables.rb b/db/migrate/20230915092215_create_flipper_tables.rb
new file mode 100644
index 00000000..1eae1b6d
--- /dev/null
+++ b/db/migrate/20230915092215_create_flipper_tables.rb
@@ -0,0 +1,22 @@
+class CreateFlipperTables < ActiveRecord::Migration[7.0]
+ def self.up
+ create_table :flipper_features do |t|
+ t.string :key, null: false
+ t.timestamps null: false
+ end
+ add_index :flipper_features, :key, unique: true
+
+ create_table :flipper_gates do |t|
+ t.string :feature_key, null: false
+ t.string :key, null: false
+ t.string :value
+ t.timestamps null: false
+ end
+ add_index :flipper_gates, %i[feature_key key value], unique: true
+ end
+
+ def self.down
+ drop_table :flipper_gates
+ drop_table :flipper_features
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 2873ecf2..2a356c60 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -108,6 +108,22 @@
t.index ["user_id", "user_type"], name: "user_index"
end
+ create_table "flipper_features", force: :cascade do |t|
+ t.string "key", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["key"], name: "index_flipper_features_on_key", unique: true
+ end
+
+ create_table "flipper_gates", force: :cascade do |t|
+ t.string "feature_key", null: false
+ t.string "key", null: false
+ t.string "value"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["feature_key", "key", "value"], name: "index_flipper_gates_on_feature_key_and_key_and_value", unique: true
+ end
+
create_table "forms", force: :cascade do |t|
t.string "given_name"
t.string "middle_name"
diff --git a/spec/factories/applications.rb b/spec/factories/applications.rb
index 08050094..49d86172 100644
--- a/spec/factories/applications.rb
+++ b/spec/factories/applications.rb
@@ -31,7 +31,7 @@
visa_type { VisaStep::VALID_ANSWERS_OPTIONS.reject { _1 == "Other" }.sample }
date_of_entry { Time.zone.today }
start_date { 1.month.from_now.to_date }
- submitted
+ urn { Urn.generate(application_route) }
factory :teacher_application do
application_route { "teacher" }
@@ -45,14 +45,6 @@
applicant strategy: :create, factory: :applicant
end
- trait :submitted do
- urn { Urn.generate(application_route) }
- end
-
- trait :not_submitted do
- urn { nil }
- end
-
trait :with_initial_checks_completed do
application_progress strategy: :build, factory: %i[application_progress initial_checks_completed]
end
diff --git a/spec/features/admin_console/applications_list_spec.rb b/spec/features/admin_console/applications_list_spec.rb
index d00ffb07..85a51dee 100644
--- a/spec/features/admin_console/applications_list_spec.rb
+++ b/spec/features/admin_console/applications_list_spec.rb
@@ -60,13 +60,13 @@
def given_there_are_few_applications
# Create 2 specific applications for search tests
unique_applicant = create(:applicant, given_name: "Unique Given Name", middle_name: "Unique Middle Name", family_name: "Unique Family Name", email_address: "unique@example.com")
- create(:application, :submitted, applicant: unique_applicant, urn: "Unique Urn 1")
+ create(:application, applicant: unique_applicant, urn: "Unique Urn 1")
another_applicant = create(:applicant, given_name: "Another Given Name", middle_name: "Another Middle Name", family_name: "Another Family Name", email_address: "another@example.com")
- create(:application, :submitted, applicant: another_applicant, urn: "Unique Urn 2")
+ create(:application, applicant: another_applicant, urn: "Unique Urn 2")
# Create 19 more applications for pagination test
- create_list(:application, 19, :submitted)
+ create_list(:application, 19)
end
def given_there_is_an_application_that_breached_sla
diff --git a/spec/features/admin_console/download_qa_csv_file_spec.rb b/spec/features/admin_console/download_qa_csv_file_spec.rb
deleted file mode 100644
index 3c7e1548..00000000
--- a/spec/features/admin_console/download_qa_csv_file_spec.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-require "rails_helper"
-
-describe "Download QA CSV functionality" do
- include AdminHelpers
-
- it "shows the 'Download QA CSV' button only if a status filter is applied" do
- given_there_are_few_applications
- given_i_am_signed_as_an_admin
- when_i_am_in_the_applications_list_page
- then_i_should_not_see_the_download_button
- when_i_filter_by_a_status
- then_i_should_see_the_download_button
- end
-
- it "marks applications as qa before generating the csv" do
- given_there_are_few_applications
- given_i_am_signed_as_an_admin
- when_i_am_in_the_applications_list_page
- when_i_filter_by_a_status
- when_i_download_the_csv
-
- then_all_applications_should_be_marked_as_qa
- end
-
- it "generates an empty csv when downloaded twice consecutively" do
- given_there_are_few_applications
- given_i_am_signed_as_an_admin
- when_i_am_in_the_applications_list_page
- when_i_filter_by_a_status
- when_i_download_the_csv
- when_i_am_in_the_applications_list_page
- when_i_filter_by_a_status
- when_i_download_the_csv_again
-
- then_csv_should_be_empty
- end
-
- def given_there_are_few_applications
- create_list(:application, 5, :submitted)
- end
-
- def when_i_am_in_the_applications_list_page
- visit(applicants_path)
- end
-
- def when_i_filter_by_a_status
- select "Initial checks", from: "status"
- click_button "Search"
- end
-
- def then_i_should_not_see_the_download_button
- expect(page).not_to have_link("Download QA CSV")
- end
-
- def then_i_should_see_the_download_button
- expect(page).to have_link("Download QA CSV")
- end
-
- def when_i_download_the_csv
- click_link "Download QA CSV"
- end
-
- def when_i_download_the_csv_again
- when_i_download_the_csv
- end
-
- def then_all_applications_should_be_marked_as_qa
- Application.all.each do |app|
- expect(app.reload.qa?).to be(true)
- end
- end
-
- def then_csv_should_be_empty
- expect(page.body.lines.count).to eq(1)
- end
-end
diff --git a/spec/features/admin_console/duplicates_search_spec.rb b/spec/features/admin_console/duplicates_search_spec.rb
index 9d21f2a2..c4f64ce3 100644
--- a/spec/features/admin_console/duplicates_search_spec.rb
+++ b/spec/features/admin_console/duplicates_search_spec.rb
@@ -10,10 +10,10 @@
let!(:applicant_four) { build(:applicant, email_address: "test2@example.com", passport_number: "123456", phone_number: "999999999") }
before do
- create(:application, :submitted, applicant: applicant_one)
- create(:application, :submitted, applicant: applicant_two)
- create(:application, :submitted, applicant: applicant_three)
- create(:application, :submitted, applicant: applicant_four)
+ create(:application, applicant: applicant_one)
+ create(:application, applicant: applicant_two)
+ create(:application, applicant: applicant_three)
+ create(:application, applicant: applicant_four)
end
it "Admin can search for duplicates by email" do
@@ -34,20 +34,6 @@
then_i_see_matching_duplicates_by_passport_number
end
- it "the view renders even where there are 'in progress' applications" do
- given_i_am_signed_as_an_admin
- when_there_are_in_progress_applications
- when_i_visit_the_duplicates_page
- then_i_see_the_in_progress_applications
- end
-
- it "shows duplicates only once even if they match multiple times" do
- given_i_am_signed_as_an_admin
- when_there_are_in_progress_applications
- when_i_visit_the_duplicates_page
- then_i_see_the_in_progress_applications_only_once
- end
-
def when_i_search_for_a_duplicate_by(type)
visit duplicates_path
case type
@@ -60,18 +46,10 @@ def when_i_search_for_a_duplicate_by(type)
end
end
- def when_there_are_in_progress_applications
- create(:application, applicant: nil, urn: nil, application_progress: nil)
- end
-
def when_i_visit_the_duplicates_page
visit duplicates_path
end
- def then_i_see_the_in_progress_applications
- expect(page).to have_content("Duplicated applications")
- end
-
def then_i_see_matching_duplicates
expect(page).to have_content("test@example.com")
expect(page).to have_content("123456")
diff --git a/spec/features/admin_console/reports_spec.rb b/spec/features/admin_console/reports_spec.rb
index 41e5aec7..f01b734e 100644
--- a/spec/features/admin_console/reports_spec.rb
+++ b/spec/features/admin_console/reports_spec.rb
@@ -29,6 +29,22 @@
then_the_payroll_data_csv_report_is_downloaded
end
+ it "exports Application CSV" do
+ given_i_am_signed_as_an_admin
+ when_i_am_in_the_reports_page
+ and_i_click_on_the_applications_csv_link
+
+ then_the_applications_csv_report_is_downloaded
+ end
+
+ it "exports Qa report CSV" do
+ given_i_am_signed_as_an_admin
+ when_i_am_in_the_reports_page
+ and_i_click_on_the_qa_report_csv_button
+
+ then_the_qa_report_csv_report_is_downloaded
+ end
+
private
def then_the_standing_data_csv_report_is_downloaded
@@ -49,6 +65,18 @@ def then_the_payroll_data_csv_report_is_downloaded
expect(page.response_headers["Content-Disposition"]).to match(/filename="Payroll-Report.*/)
end
+ def then_the_applications_csv_report_is_downloaded
+ expect(page.response_headers["Content-Type"]).to match(/text\/csv/)
+ expect(page.response_headers["Content-Disposition"]).to include "attachment"
+ expect(page.response_headers["Content-Disposition"]).to match(/filename="Applications-Report.*/)
+ end
+
+ def then_the_qa_report_csv_report_is_downloaded
+ expect(page.response_headers["Content-Type"]).to match(/text\/csv/)
+ expect(page.response_headers["Content-Disposition"]).to include "attachment"
+ expect(page.response_headers["Content-Disposition"]).to match(/filename="QA-Report-initial_checks*/)
+ end
+
def and_i_click_on_the_home_office_csv_link
within ".home-office" do
click_on "Download"
@@ -67,6 +95,18 @@ def and_i_click_on_the_payroll_data_csv_link
end
end
+ def and_i_click_on_the_applications_csv_link
+ within ".applications" do
+ click_on "Download"
+ end
+ end
+
+ def and_i_click_on_the_qa_report_csv_button
+ within ".applications-qa" do
+ click_on "Download"
+ end
+ end
+
def when_i_am_in_the_reports_page
visit reports_path
end
diff --git a/spec/models/application_spec.rb b/spec/models/application_spec.rb
index 5ce83fb7..5ed916b0 100644
--- a/spec/models/application_spec.rb
+++ b/spec/models/application_spec.rb
@@ -26,7 +26,7 @@
RSpec.describe Application do
describe "validations" do
context "when the application has been submitted" do
- subject(:application) { build(:teacher_application, :submitted) }
+ subject(:application) { build(:teacher_application) }
it { expect(application).to validate_presence_of(:application_date) }
it { expect(application).to validate_presence_of(:application_route) }
@@ -36,30 +36,9 @@
it { expect(application).to validate_presence_of(:visa_type) }
it { expect(application).to validate_presence_of(:applicant) }
end
-
- context "when the application has not been submitted" do
- it "is valid" do
- expect(described_class.new).to be_valid
- end
- end
end
describe "scopes" do
- describe ".submitted" do
- it "returns applications with a URN" do
- create_list(:application, 2, :submitted)
- create(:application, :not_submitted)
-
- expect(described_class.submitted.count).to eq 2
- end
-
- it "does not return applications without a URN" do
- create(:application, :not_submitted)
-
- expect(described_class.submitted.count).to eq 0
- end
- end
-
describe ".filter_by_status" do
it "returns applications with the specified status" do
initial_checks_application = build(:application)
@@ -95,22 +74,6 @@
end
end
- describe "#assign_urn!" do
- it "matches the required format for a teacher" do
- application = create(:teacher_application)
- application.assign_urn!
-
- expect(application.reload.urn).to match(/^IRPTE[A-Z0-9]{5}$/)
- end
-
- it "matches the required format for a trainee" do
- application = create(:salaried_trainee_application)
- application.assign_urn!
-
- expect(application.reload.urn).to match(/^IRPST[A-Z0-9]{5}$/)
- end
- end
-
describe ".search" do
let(:applicant_john) { create(:applicant, given_name: "John", family_name: "Doe", email_address: "john.doe@example.com", passport_number: "123456") }
let(:application_john) { create(:application, urn: "APP123", applicant: applicant_john) }
diff --git a/spec/models/kpis_spec.rb b/spec/models/kpis_spec.rb
index f14c4142..c1ed016a 100644
--- a/spec/models/kpis_spec.rb
+++ b/spec/models/kpis_spec.rb
@@ -3,7 +3,6 @@
RSpec.describe Kpis do
describe "#total_applications" do
it "returns the total number of applications" do
- create(:application, :not_submitted)
create_list(:application, 5)
kpis = described_class.new
diff --git a/spec/models/policies/contract_start_date_spec.rb b/spec/models/policies/contract_start_date_spec.rb
deleted file mode 100644
index 0f60f3de..00000000
--- a/spec/models/policies/contract_start_date_spec.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-require "rails_helper"
-
-RSpec.describe Policies::ContractStartDate do
- let(:first_monday_july) { Date.new(2023, 7, 3) }
-
- describe "#eligible?" do
- it "returns true when the date is on the first Monday in July" do
- expect(described_class.eligible?(first_monday_july)).to be true
- end
-
- it "returns true when the date is after the first Monday in July" do
- date = first_monday_july.advance(days: 1)
-
- expect(described_class.eligible?(date)).to be true
- end
-
- it "returns false when the date is before the first Monday in July" do
- date = first_monday_july.advance(days: -1)
- expect(described_class.eligible?(date)).to be false
- end
- end
-end
diff --git a/spec/models/policies/entry_date_spec.rb b/spec/models/policies/entry_date_spec.rb
deleted file mode 100644
index b4ced3db..00000000
--- a/spec/models/policies/entry_date_spec.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# frozen_string_literal: true
-
-require "rails_helper"
-
-RSpec.describe Policies::EntryDate do
- subject(:policy) { described_class }
-
- describe "#eligible?" do
- it "returns true if the entry date is more than 3 months since start of contract" do
- entry_date = Date.parse("2020-01-03")
- start_date = Date.parse("2020-04-02")
-
- expect(policy.eligible?(entry_date, start_date)).to be(true)
- end
-
- it "returns true if the entry date is 3 months since start of contract" do
- entry_date = Date.parse("2020-01-02")
- start_date = Date.parse("2020-04-02")
-
- expect(policy.eligible?(entry_date, start_date)).to be(true)
- end
-
- it "returns false if the entry date is less than 3 months since start of contract" do
- entry_date = Date.parse("2020-01-01")
- start_date = Date.parse("2020-04-02")
-
- expect(policy.eligible?(entry_date, start_date)).to be(false)
- end
- end
-end
diff --git a/spec/models/reports/applications_spec.rb b/spec/models/reports/applications_spec.rb
new file mode 100644
index 00000000..1b1928a4
--- /dev/null
+++ b/spec/models/reports/applications_spec.rb
@@ -0,0 +1,83 @@
+require "rails_helper"
+
+module Reports
+ describe Applications do
+ include ActiveSupport::Testing::TimeHelpers
+
+ subject(:report) { described_class.new }
+
+ it "returns the name of the Report" do
+ frozen_time = Time.zone.local(2023, 7, 17, 12, 30, 45)
+ travel_to frozen_time do
+ expected_name = "Applications-Report-20230717-123045.csv"
+
+ report = described_class.new
+ actual_name = report.name
+
+ expect(actual_name).to eq(expected_name)
+ end
+ end
+
+ describe "#csv" do
+ let(:rejected) { create(:application, :with_rejection_completed) }
+ let(:paid) { create(:application, :with_payment_confirmation_completed) }
+ let(:banking) { create(:application, :with_banking_approval_completed) }
+ let(:school) { create(:application, :with_school_checks_completed) }
+ let(:home_office) { create(:application, :with_home_office_checks_completed) }
+ let(:initial) { create(:application, :with_initial_checks_completed) }
+ let(:application) { create(:application) }
+
+ before do
+ rejected
+ paid
+ banking
+ school
+ home_office
+ initial
+ application
+ end
+
+ context "returns file with header" do
+ let(:header) do
+ [
+ "Ip Address",
+ "Given Name",
+ "Middle Name",
+ "Family Name",
+ "Email Address",
+ "Phone Number",
+ "Date Of Birth",
+ "Sex",
+ "Passport Number",
+ "Nationality",
+ "Student Loan",
+ "Address Line 1",
+ "Address Line 2",
+ "City",
+ "Postcode",
+ "Application Date",
+ "Application Route",
+ "Status",
+ "Date Of Entry",
+ "Start Date",
+ "Subject",
+ "Urn",
+ "Visa Type",
+ ].join(",")
+ end
+
+ it { expect(report.csv).to include(header) }
+ end
+
+ context "returns all applications" do
+ it { expect(report.csv).to include(application.urn) }
+ it { expect(report.csv).to include(initial.urn) }
+ it { expect(report.csv).to include(home_office.urn) }
+ it { expect(report.csv).to include(school.urn) }
+ it { expect(report.csv).to include(banking.urn) }
+ it { expect(report.csv).to include(paid.urn) }
+ it { expect(report.csv).to include(rejected.urn) }
+ end
+ end
+ end
+end
diff --git a/spec/models/reports/home_office_spec.rb b/spec/models/reports/home_office_spec.rb
index 88405791..2d413db2 100644
--- a/spec/models/reports/home_office_spec.rb
+++ b/spec/models/reports/home_office_spec.rb
@@ -55,22 +55,34 @@ module Reports
application.urn,
application.applicant.full_name,
application.applicant.date_of_birth,
+ nil,
application.applicant.nationality,
+ nil,
application.applicant.passport_number,
nil,
nil,
+ nil,
+ nil,
+ nil,
+ nil,
].join(","))
end
it "returns the header in CSV format" do
expected_header = [
- "URN",
+ "ID",
"Full Name",
"DOB",
+ "Gender",
"Nationality",
+ "Place of Birth",
"Passport Number",
- "Visa Type",
- "Date of UK entry",
+ "National Insurance Number",
+ "Address",
+ "Postcode",
+ "Email",
+ "Telephone",
+ "Reference",
].join(",")
expect(report.csv).to include(expected_header)
diff --git a/spec/queries/average_age_query_spec.rb b/spec/queries/average_age_query_spec.rb
index 19615279..6960cf71 100644
--- a/spec/queries/average_age_query_spec.rb
+++ b/spec/queries/average_age_query_spec.rb
@@ -4,8 +4,6 @@
describe "#call" do
context "when there are a few applicants" do
before do
- create(:application, :not_submitted, applicant: create(:applicant, date_of_birth: 25.years.ago))
-
create(:application, applicant: create(:applicant, date_of_birth: 35.years.ago))
create(:application, applicant: create(:applicant, date_of_birth: 45.years.ago))
create(:application, applicant: create(:applicant, date_of_birth: 52.years.ago))
diff --git a/spec/queries/gender_breakdown_query_spec.rb b/spec/queries/gender_breakdown_query_spec.rb
index 4584948f..603d7269 100644
--- a/spec/queries/gender_breakdown_query_spec.rb
+++ b/spec/queries/gender_breakdown_query_spec.rb
@@ -4,8 +4,6 @@
describe "#call" do
context "when there are a few applicants" do
before do
- create(:application, :not_submitted, applicant: create(:applicant, sex: "male"))
-
create(:application, applicant: create(:applicant, sex: "male"))
create(:application, applicant: create(:applicant, sex: "female"))
create(:application, applicant: create(:applicant, sex: "male"))
diff --git a/spec/queries/nationality_breakdown_query_spec.rb b/spec/queries/nationality_breakdown_query_spec.rb
index 9d3b5dc2..565fb718 100644
--- a/spec/queries/nationality_breakdown_query_spec.rb
+++ b/spec/queries/nationality_breakdown_query_spec.rb
@@ -4,8 +4,6 @@
describe "#call" do
context "when there are a few applicants" do
before do
- create(:application, :not_submitted, applicant: create(:applicant, nationality: "Nationality 1"))
-
create(:application, applicant: create(:applicant, nationality: "Nationality 1"))
create(:application, applicant: create(:applicant, nationality: "Nationality 3"))
create(:application, applicant: create(:applicant, nationality: "Nationality 4"))
diff --git a/spec/queries/rejection_reason_breakdown_query_spec.rb b/spec/queries/rejection_reason_breakdown_query_spec.rb
index adf14d0c..d6c80bae 100644
--- a/spec/queries/rejection_reason_breakdown_query_spec.rb
+++ b/spec/queries/rejection_reason_breakdown_query_spec.rb
@@ -8,7 +8,6 @@
create(:application_progress, application: create(:application, applicant:), rejection_reason: "suspected_fraud")
create(:application_progress, application: create(:application, applicant:), rejection_reason: "suspected_fraud")
create(:application_progress, application: create(:application, applicant:), rejection_reason: "ineligible_school")
- create(:application_progress, application: create(:application, :not_submitted, applicant:), rejection_reason: "duplicate_submission") # This one should not be included in the count because the associated application is not submitted
end
it "returns the correct rejection reason breakdown" do
diff --git a/spec/queries/route_breakdown_query_spec.rb b/spec/queries/route_breakdown_query_spec.rb
index 7391a312..016a32c6 100644
--- a/spec/queries/route_breakdown_query_spec.rb
+++ b/spec/queries/route_breakdown_query_spec.rb
@@ -4,7 +4,6 @@
describe "#call" do
context "when there are a few applicants" do
before do
- create(:teacher_application, :not_submitted)
create(:teacher_application)
create(:salaried_trainee_application)
create(:teacher_application)
diff --git a/spec/queries/status_breakdown_query_spec.rb b/spec/queries/status_breakdown_query_spec.rb
new file mode 100644
index 00000000..43603ded
--- /dev/null
+++ b/spec/queries/status_breakdown_query_spec.rb
@@ -0,0 +1,45 @@
+require "rails_helper"
+
+RSpec.describe StatusBreakdownQuery, type: :service do
+ subject(:breakdown) { described_class.call }
+
+ describe ".call" do
+ let(:expected_breakdown) do
+ {
+ "initial_checks" => 0,
+ "home_office_checks" => 0,
+ "school_checks" => 0,
+ "bank_approval" => 0,
+ "payment_confirmation" => 0,
+ "paid" => 0,
+ "rejected" => 0,
+ }
+ end
+
+ it "returns the ordered application status breakdown" do
+ expect(breakdown).to include(expected_breakdown)
+ end
+
+ context "with data" do
+ before do
+ create(:application, :with_initial_checks_completed)
+ end
+
+ let(:expected_breakdown) do
+ {
+ "initial_checks" => 0,
+ "home_office_checks" => 1,
+ "school_checks" => 0,
+ "bank_approval" => 0,
+ "payment_confirmation" => 0,
+ "paid" => 0,
+ "rejected" => 0,
+ }
+ end
+
+ it "returns the ordered application status breakdown" do
+ expect(breakdown).to include(expected_breakdown)
+ end
+ end
+ end
+end
diff --git a/spec/queries/subject_breakdown_query_spec.rb b/spec/queries/subject_breakdown_query_spec.rb
index 00e8147d..c9d4408b 100644
--- a/spec/queries/subject_breakdown_query_spec.rb
+++ b/spec/queries/subject_breakdown_query_spec.rb
@@ -4,7 +4,6 @@
describe "#call" do
context "when there are a few applicants" do
before do
- create(:application, :not_submitted)
create(:application, subject: :physics)
create(:application, subject: :languages)
create(:application, subject: :general_science)
diff --git a/spec/queries/visa_breakdown_query_spec.rb b/spec/queries/visa_breakdown_query_spec.rb
index 7280d223..abeaf581 100644
--- a/spec/queries/visa_breakdown_query_spec.rb
+++ b/spec/queries/visa_breakdown_query_spec.rb
@@ -4,7 +4,6 @@
describe "#call" do
context "when there are a few applicants" do
before do
- create(:application, :not_submitted, visa_type: "visa_1")
create(:application, visa_type: "visa_1")
create_list(:application, 2, visa_type: "visa_2")
create_list(:application, 4, visa_type: "visa_3")
diff --git a/spec/services/report_spec.rb b/spec/services/report_spec.rb
new file mode 100644
index 00000000..046ad024
--- /dev/null
+++ b/spec/services/report_spec.rb
@@ -0,0 +1,76 @@
+require "rails_helper"
+
+RSpec.describe Report do
+ subject(:report) { described_class }
+
+ describe "registered reports" do
+ subject(:registered_reports) { described_class::REGISTERED_REPORTS }
+
+ let(:expected_ids) do
+ %i[home_office standing_data payroll applications qa]
+ end
+
+ let(:expected_classes) do
+ [
+ Reports::HomeOffice,
+ Reports::StandingData,
+ Reports::Payroll,
+ Reports::Applications,
+ Reports::QaReport,
+ ]
+ end
+
+ it { expect(registered_reports.keys).to match_array(expected_ids) }
+ it { expect(registered_reports.values).to match_array(expected_classes) }
+ end
+
+ describe ".call" do
+ subject(:service) { described_class.new(report_id, status:) }
+
+ let(:report_id) { "qa" }
+ let(:status) { "initial_checks" }
+
+ context "report_name" do
+ it { expect(service.report_name).to eq("Reports::qareport") }
+ end
+
+ context "filename" do
+ include ActiveSupport::Testing::TimeHelpers
+ it "returns the name of the Report" do
+ frozen_time = Time.zone.local(2023, 7, 17, 12, 30, 45)
+ travel_to frozen_time do
+ expected_name = "QA-Report-initial_checks-2023_07_17-12_30_45.csv"
+
+ expect(service.filename).to eq(expected_name)
+ end
+ end
+ end
+
+ # rubocop:disable RSpec/VerifiedDoubles
+ context "qa report data" do
+ let(:report) { spy(Reports::QaReport) }
+
+ before do
+ allow(Reports::QaReport).to receive(:new).and_return(report)
+ service.data
+ end
+
+ it { expect(Reports::QaReport).to have_received(:new).with(kind_of(Array), status) }
+ it { expect(report).to have_received(:csv) }
+ end
+
+ context "other report data" do
+ let(:report_id) { "home_office" }
+ let(:report) { spy(Reports::HomeOffice) }
+
+ before do
+ allow(Reports::HomeOffice).to receive(:new).and_return(report)
+ service.data
+ end
+
+ it { expect(Reports::HomeOffice).to have_received(:new) }
+ it { expect(report).to have_received(:csv) }
+ end
+ # rubocop:enable RSpec/VerifiedDoubles
+ end
+end
diff --git a/spec/steps/personal_details_step_spec.rb b/spec/steps/personal_details_step_spec.rb
index 08237787..50a11e67 100644
--- a/spec/steps/personal_details_step_spec.rb
+++ b/spec/steps/personal_details_step_spec.rb
@@ -111,7 +111,7 @@
describe "date_of_birth minimum age" do
let(:form) { build(:form, date_of_birth: age) }
let(:error) { step.errors.messages_for(:date_of_birth) }
- let(:minimum_age) { 22 }
+ let(:minimum_age) { 21 }
before { step.valid? }
@@ -189,7 +189,7 @@
it "has MIN_AGE set to 22" do
min_age = described_class.send(:const_get, :MIN_AGE)
- expect(min_age).to eq(22)
+ expect(min_age).to eq(21)
end
end