Skip to content

Commit

Permalink
Merge pull request #2756 from DFE-Digital/capt-1625
Browse files Browse the repository at this point in the history
CAPT-1625 Move teacher_reference_number from claim to eligibility
  • Loading branch information
kenfodder authored Jun 26, 2024
2 parents 8854c53 + 8348375 commit 59accf5
Show file tree
Hide file tree
Showing 81 changed files with 613 additions and 360 deletions.
8 changes: 4 additions & 4 deletions app/forms/teacher_reference_number_form.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
class TeacherReferenceNumberForm < Form
include TeacherReferenceNumberValidation

attribute :teacher_reference_number

before_validation do
self.teacher_reference_number = teacher_reference_number&.gsub(/\D/, "")
end
before_validation :normalise_teacher_reference_number

validates :teacher_reference_number,
presence: {
Expand All @@ -12,7 +12,7 @@ class TeacherReferenceNumberForm < Form

validates :teacher_reference_number,
length: {
is: 7,
is: TRN_LENGTH,
message: ->(form, _) { form.i18n_errors_path("length") }
}, if: -> { teacher_reference_number.present? }

Expand Down
27 changes: 21 additions & 6 deletions app/helpers/admin/claims_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def personal_data_removed_text

def admin_personal_details(claim)
[
[translate("admin.teacher_reference_number"), claim.teacher_reference_number],
[translate("admin.teacher_reference_number"), claim.eligibility.teacher_reference_number],
[translate("govuk_verify_fields.full_name").capitalize, claim.personal_data_removed? ? personal_data_removed_text : claim.full_name],
[translate("govuk_verify_fields.date_of_birth").capitalize, claim.personal_data_removed? ? personal_data_removed_text : l(claim.date_of_birth, format: :day_month_year)],
[translate("admin.national_insurance_number"), claim.personal_data_removed? ? personal_data_removed_text : claim.national_insurance_number],
Expand Down Expand Up @@ -98,11 +98,19 @@ def claim_route(claim)
end

def matching_attributes(first_claim, second_claim)
first_attributes = matching_attributes_for(first_claim)
second_attributes = matching_attributes_for(second_claim)
first_attributes = matching_attributes_for_claim(first_claim)
second_attributes = matching_attributes_for_claim(second_claim)

first_eligibility_attributes = matching_attributes_for_eligibility(first_claim.eligibility)
second_eligibility_attributes = matching_attributes_for_eligibility(second_claim.eligibility)

matching_attributes = first_attributes & second_attributes
matching_attributes.to_h.compact.keys.map(&:humanize).sort
claim_matches = matching_attributes.to_h.compact.keys.map(&:humanize).sort

matching_eligibility_attributes = first_eligibility_attributes & second_eligibility_attributes
eligibility_matches = matching_eligibility_attributes.to_h.compact.keys.map(&:humanize).sort

claim_matches + eligibility_matches
end

def identity_confirmation_task_claim_verifier_match_status_tag(claim)
Expand Down Expand Up @@ -226,9 +234,16 @@ def no_claims(status)

private

def matching_attributes_for(claim)
def matching_attributes_for_claim(claim)
claim.attributes
.slice(*Claim::MatchingAttributeFinder::ATTRIBUTE_GROUPS_TO_MATCH.flatten)
.slice(*Claim::MatchingAttributeFinder::CLAIM_ATTRIBUTE_GROUPS_TO_MATCH.flatten)
.reject { |_, v| v.blank? }
.to_a
end

def matching_attributes_for_eligibility(eligibility)
eligibility.attributes
.slice(*eligibility.policy.eligibility_matching_attributes.flatten)
.reject { |_, v| v.blank? }
.to_a
end
Expand Down
2 changes: 1 addition & 1 deletion app/jobs/claim_verifier_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ def perform(claim)
AutomatedChecks::ClaimVerifier.new(
claim:,
dqt_teacher_status: claim.has_dqt_record? ? Dqt::Teacher.new(claim.dqt_teacher_status) : Dqt::Client.new.teacher.find(
claim.teacher_reference_number,
claim.eligibility.teacher_reference_number,
birthdate: claim.date_of_birth.to_s,
nino: claim.national_insurance_number
)
Expand Down
2 changes: 1 addition & 1 deletion app/jobs/qualifications_no_match_check_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def perform(filter: nil)
AutomatedChecks::ClaimVerifiers::Qualifications.new(
claim: claim,
dqt_teacher_status: Dqt::Client.new.teacher.find(
claim.teacher_reference_number,
claim.eligibility.teacher_reference_number,
birthdate: claim.date_of_birth.to_s,
nino: claim.national_insurance_number
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def initialize(
)
self.admin_user = admin_user
self.claim = claim
self.school_workforce_census = SchoolWorkforceCensus.where(teacher_reference_number: claim.teacher_reference_number)
self.school_workforce_census = SchoolWorkforceCensus.where(teacher_reference_number: claim.eligibility.teacher_reference_number)
self.school_workforce_census_subjects = school_workforce_census
end

Expand Down
2 changes: 1 addition & 1 deletion app/models/automated_checks/claim_verifiers/employment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class Employment
def initialize(claim:, admin_user: nil)
self.admin_user = admin_user
self.claim = claim
self.teachers_pensions_service = TeachersPensionsService.by_teacher_reference_number(claim.teacher_reference_number)
self.teachers_pensions_service = TeachersPensionsService.by_teacher_reference_number(claim.eligibility.teacher_reference_number)
end

def perform
Expand Down
4 changes: 2 additions & 2 deletions app/models/automated_checks/claim_verifiers/identity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def partial_match
unless trn_matched?
notes << create_field_note(
name: "Teacher reference number",
claimant: claim.teacher_reference_number,
claimant: claim.eligibility.teacher_reference_number,
dqt: dqt_teacher_status.teacher_reference_number
)
end
Expand All @@ -162,7 +162,7 @@ def tasks=(tasks)
end

def trn_matched?
claim.teacher_reference_number == dqt_teacher_status.teacher_reference_number
claim.eligibility.teacher_reference_number == dqt_teacher_status.teacher_reference_number
end
end
end
Expand Down
22 changes: 22 additions & 0 deletions app/models/base_policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,26 @@ def locale_key
def payroll_file_name
to_s
end

def policies_claimable
return [] unless const_defined?(:OTHER_CLAIMABLE_POLICIES)

[self] + self::OTHER_CLAIMABLE_POLICIES
end

def policy_eligibilities_claimable
policies_claimable.map { |p| p::Eligibility }
end

def eligibility_matching_attributes
return [] unless const_defined?(:ELIGIBILITY_MATCHING_ATTRIBUTES)

self::ELIGIBILITY_MATCHING_ATTRIBUTES
end

def searchable_eligibility_attributes
return [] unless const_defined?(:SEARCHABLE_ELIGIBILITY_ATTRIBUTES)

self::SEARCHABLE_ELIGIBILITY_ATTRIBUTES
end
end
22 changes: 2 additions & 20 deletions app/models/claim.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

class Claim < ApplicationRecord
MIN_QA_THRESHOLD = 10
TRN_LENGTH = 7
NO_STUDENT_LOAN = "not_applicable"
STUDENT_LOAN_PLAN_OPTIONS = StudentLoan::PLANS.dup << NO_STUDENT_LOAN
ADDRESS_ATTRIBUTES = %w[address_line_1 address_line_2 address_line_3 address_line_4 postcode].freeze
AMENDABLE_ATTRIBUTES = %i[
teacher_reference_number
national_insurance_number
date_of_birth
student_loan_plan
Expand All @@ -22,7 +20,6 @@ class Claim < ApplicationRecord
address_line_4: true,
postcode: true,
payroll_gender: true,
teacher_reference_number: true,
national_insurance_number: true,
has_student_loan: false,
student_loan_country: false,
Expand Down Expand Up @@ -76,7 +73,8 @@ class Claim < ApplicationRecord
qualifications_details_check: true,
dqt_teacher_status: false,
submitted_using_slc_data: false,
journeys_session_id: false
journeys_session_id: false,
column_to_remove_teacher_reference_number: true
}.freeze
DECISION_DEADLINE = 12.weeks
DECISION_DEADLINE_WARNING_POINT = 2.weeks
Expand Down Expand Up @@ -126,9 +124,6 @@ class Claim < ApplicationRecord

validates :academic_year_before_type_cast, format: {with: AcademicYear::ACADEMIC_YEAR_REGEXP}

validates :teacher_reference_number, on: [:submit, :amendment], presence: {message: "Enter your teacher reference number"}
validate :trn_must_be_seven_digits

validates :has_student_loan, on: [:"student-loan"], inclusion: {in: [true, false]}
validates :student_loan_plan, inclusion: {in: STUDENT_LOAN_PLAN_OPTIONS}, allow_nil: true
validates :student_loan_plan, on: [:"student-loan", :amendment], presence: {message: "Enter a valid student loan plan"}
Expand All @@ -147,7 +142,6 @@ class Claim < ApplicationRecord
validate :building_society_roll_number_must_be_between_one_and_eighteen_digits
validate :building_society_roll_number_must_be_in_a_valid_format

before_save :normalise_trn, if: :teacher_reference_number_changed?
before_save :normalise_ni_number, if: :national_insurance_number_changed?
before_save :normalise_bank_account_number, if: :bank_account_number_changed?
before_save :normalise_bank_sort_code, if: :bank_sort_code_changed?
Expand Down Expand Up @@ -405,18 +399,6 @@ def set_a_reminder?

private

def normalise_trn
self.teacher_reference_number = normalised_trn
end

def normalised_trn
teacher_reference_number.gsub(/\D/, "")
end

def trn_must_be_seven_digits
errors.add(:teacher_reference_number, "Teacher reference number must be 7 digits") if teacher_reference_number.present? && normalised_trn.length != TRN_LENGTH
end

def normalise_ni_number
self.national_insurance_number = normalised_ni_number
end
Expand Down
16 changes: 13 additions & 3 deletions app/models/claim/claims_preventing_payment_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,31 @@ def initialize(claim)
# The returned claims have different payment or tax details to those
# provided by `claim`, and hence `claim` cannot be paid in the same payment
# as the returned claims.
#
# NOTE: This only works for ECP/LUPP and TSLR cross policy as this requires a TRN
# Driven by: Policies.policies_claimable(policy) using OTHER_CLAIMABLE_POLICIES config otherwise this just returns []
def claims_preventing_payment
@claims_preventing_payment ||= find_claims_preventing_payment
end

private

def find_claims_preventing_payment
payrollable_claims_from_same_claimant = Claim.payrollable.where(teacher_reference_number: claim.teacher_reference_number)
eligibility_ids = claim.policy.policies_claimable.map { |policy|
policy::Eligibility.where(teacher_reference_number: claim.eligibility.teacher_reference_number)
}.flatten.map(&:id)

payrollable_claims_from_same_claimant = Claim.payrollable.where(eligibility_id: eligibility_ids)

payrollable_topup_claims_from_same_claimant = Topup.includes(:claim).payrollable
.select { |t| t.claim.teacher_reference_number == claim.teacher_reference_number }
.select { |t|
claim.policy.policy_eligibilities_claimable.map(&:to_s).include?(t.claim.eligibility_type) &&
t.claim.eligibility.teacher_reference_number == claim.eligibility.teacher_reference_number
}
.map(&:claim)

[payrollable_claims_from_same_claimant, payrollable_topup_claims_from_same_claimant].reduce([], :concat).select do |other_claim|
Payment::PERSONAL_DETAILS_ATTRIBUTES_FORBIDDING_DISCREPANCIES.any? do |attribute|
Payment::PERSONAL_CLAIM_DETAILS_ATTRIBUTES_FORBIDDING_DISCREPANCIES.any? do |attribute|
attribute_does_not_match?(other_claim, attribute)
end
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/claim/data_report_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def to_csv
@claims.each do |claim|
csv << [
ExcelUtils.escape_formulas(claim.reference),
ExcelUtils.escape_formulas(claim.teacher_reference_number),
ExcelUtils.escape_formulas(claim.eligibility.teacher_reference_number),
ExcelUtils.escape_formulas(claim.national_insurance_number),
ExcelUtils.escape_formulas(claim.full_name),
ExcelUtils.escape_formulas(claim.email_address),
Expand Down
43 changes: 36 additions & 7 deletions app/models/claim/matching_attribute_finder.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class Claim
class MatchingAttributeFinder
ATTRIBUTE_GROUPS_TO_MATCH = [
["teacher_reference_number"],
# Fields on the claim model to consider a match
CLAIM_ATTRIBUTE_GROUPS_TO_MATCH = [
["email_address"],
["national_insurance_number"],
["bank_account_number", "bank_sort_code", "building_society_roll_number"]
Expand All @@ -13,17 +13,20 @@ def initialize(source_claim)

# Returns a list of claims that could potentially be from the same applicant
# because they either share a same single attribute with the source claim,
# (for example, the same TRN, or email), or they share a group of attributes
# (for example, the same national_insurance_number, email, etc...), or they share a group of attributes
# with the source claim (for example bank sort code, account number and roll
# number).
#
# The associated eligibility fields can be used as well
#
# This may not necessarily mean the claim cannot be approved, but means it
# warrants a degree of caution and further investigation.
def matching_claims
match_queries = Claim.none

ATTRIBUTE_GROUPS_TO_MATCH.each do |attributes|
vals = values_for_attributes(attributes)
# Claim attributes
CLAIM_ATTRIBUTE_GROUPS_TO_MATCH.each do |attributes|
vals = values_for_attributes(@source_claim, attributes)

next if vals.blank?

Expand All @@ -33,20 +36,46 @@ def matching_claims
match_queries = match_queries.or(query)
end

# Eligibility attributes
eligibility_ids = eligibility_attributes_groups_to_match.map { |attributes|
vals = values_for_attributes(@source_claim.eligibility, attributes)
concatenated_columns = "CONCAT(#{attributes.join(",")})"

policies_to_find_matches.map { |policy|
policy::Eligibility.where("LOWER(#{concatenated_columns}) = LOWER(?)", vals.join)
}
}.flatten.map(&:id)

eligibility_match_query = Claim.where(eligibility_id: eligibility_ids)
match_queries = match_queries.or(eligibility_match_query)

claims_to_compare.merge(match_queries)
end

private

def policies_to_find_matches
@source_claim.policy.policies_claimable
end

def eligibility_attributes_groups_to_match
@source_claim.policy.eligibility_matching_attributes
end

def policies_to_find_matches_eligibility_types
policies_to_find_matches.map { |policy| policy::Eligibility.to_s }
end

def claims_to_compare
Claim.submitted
.by_academic_year(@source_claim.academic_year)
.by_policies(policies_to_find_matches)
.where.not(id: @source_claim.id)
end

def values_for_attributes(attributes)
def values_for_attributes(object, attributes)
attributes.map { |attribute|
@source_claim.read_attribute(attribute)
object.read_attribute(attribute)
}.reject(&:blank?)
end
end
Expand Down
17 changes: 12 additions & 5 deletions app/models/claim/search.rb
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
class Claim
# Accepts a search term and returns all Claims that match against any of the
# attributes defined in the `SEARCHABLE_ATTRIBUTES` constant. Both subject
# attributes defined in the `SEARCHABLE_ATTRIBUTES` or `policy::SEARCHABLE_ELIGIBILITY_ATTRIBUTES` constant. Both subject
# and attribute are downcased, so the search is case-insensitive.
class Search
attr_accessor :search_term

SEARCHABLE_ATTRIBUTES = %w[
SEARCHABLE_CLAIM_ATTRIBUTES = %w[
reference
email_address
surname
teacher_reference_number
]

def initialize(search_term)
@search_term = search_term
end

def claims
SEARCHABLE_ATTRIBUTES.inject(Claim.none) do |relation, attribute|
claim_match_query = SEARCHABLE_CLAIM_ATTRIBUTES.inject(Claim.none) { |relation, attribute|
relation.or(search_by(attribute))
end
}

eligibility_ids = Policies::POLICIES.map { |policy|
policy.searchable_eligibility_attributes.map { |attribute|
policy::Eligibility.where("LOWER(#{attribute}) = LOWER(?)", search_term)
}
}.flatten.map(&:id)

claim_match_query.or(Claim.where(eligibility_id: eligibility_ids))
end

private
Expand Down
Loading

0 comments on commit 59accf5

Please sign in to comment.