Skip to content

Commit

Permalink
Merge pull request #2961 from DFE-Digital/CAPT-1527/keep-irp-national…
Browse files Browse the repository at this point in the history
…-insurance-and-name-for-2-years

Capt 1527/keep irp national insurance and name for 2 years
  • Loading branch information
rjlynch authored Aug 1, 2024
2 parents 4dc7151 + f96cb79 commit 2ffc884
Show file tree
Hide file tree
Showing 32 changed files with 696 additions and 181 deletions.
4 changes: 3 additions & 1 deletion app/jobs/delete_personal_data_from_old_claims_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ class DeletePersonalDataFromOldClaimsJob < CronJob

def perform
Rails.logger.info "Deleting personal data from old claims which have been rejected or paid"
Claim::PersonalDataScrubber.new.scrub_completed_claims
Policies::POLICIES.each do |policy|
policy::ClaimPersonalDataScrubber.new.scrub_completed_claims
end
end
end
26 changes: 21 additions & 5 deletions app/models/claim.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ class Claim < ApplicationRecord
}.freeze
DECISION_DEADLINE = 12.weeks
DECISION_DEADLINE_WARNING_POINT = 2.weeks
CLAIMANT_MATCHING_ATTRIBUTES = %i[
national_insurance_number
]

# Use AcademicYear as custom ActiveRecord attribute type
attribute :academic_year, AcademicYear::Type.new
Expand Down Expand Up @@ -146,11 +149,11 @@ 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_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?
before_save :normalise_first_name, if: :first_name_changed?
before_save :normalise_surname, if: :surname_changed?
before_save :normalise_ni_number, if: %i[national_insurance_number national_insurance_number_changed?]
before_save :normalise_bank_account_number, if: %i[bank_account_number bank_account_number_changed?]
before_save :normalise_bank_sort_code, if: %i[bank_sort_code bank_sort_code_changed?]
before_save :normalise_first_name, if: %i[first_name first_name_changed?]
before_save :normalise_surname, if: %i[surname surname_changed?]

scope :unsubmitted, -> { where(submitted_at: nil) }
scope :submitted, -> { where.not(submitted_at: nil) }
Expand All @@ -177,6 +180,13 @@ class Claim < ApplicationRecord
scope :unassigned, -> { where(assigned_to_id: nil) }
scope :current_academic_year, -> { by_academic_year(AcademicYear.current) }
scope :failed_bank_validation, -> { where(hmrc_bank_validation_succeeded: false) }
scope :unscrubbed, -> { where(personal_data_removed_at: nil) }

scope :with_same_claimant, ->(claim) do
CLAIMANT_MATCHING_ATTRIBUTES.reduce(where.not(id: claim.id)) do |scope, attr|
scope.where(attr => claim.public_send(attr))
end
end

delegate :award_amount, to: :eligibility

Expand Down Expand Up @@ -401,6 +411,12 @@ def set_a_reminder?
)
end

def same_claimant?(other_claim)
CLAIMANT_MATCHING_ATTRIBUTES.all? do |attr|
public_send(attr) == other_claim.public_send(attr)
end
end

private

def normalise_ni_number
Expand Down
17 changes: 3 additions & 14 deletions app/models/claim/claims_preventing_payment_finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,19 @@ 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
return [] if claim.policy == Policies::InternationalRelocationPayments

eligibility_ids = claim.policy.policies_claimable.map { |policy|
policy::Eligibility.where(teacher_reference_number: claim.eligibility.teacher_reference_number)
}.flatten.map(&:id)
return [] if claim.personal_data_removed?

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

payrollable_topup_claims_from_same_claimant = Topup.includes(:claim).payrollable
.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)
.select { |t| claim.same_claimant?(t.claim) }.map(&:claim)

[payrollable_claims_from_same_claimant, payrollable_topup_claims_from_same_claimant].reduce([], :concat).select do |other_claim|
Payment::PERSONAL_CLAIM_DETAILS_ATTRIBUTES_FORBIDDING_DISCREPANCIES.any? do |attribute|
Expand Down
47 changes: 47 additions & 0 deletions app/models/claim/scrubber.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Removes attributes from a claim and its amendments
class Claim
class Scrubber
def self.scrub!(claim, attributes_to_delete)
new(claim, attributes_to_delete).scrub!
end

attr_reader :claim, :attributes_to_delete

def initialize(claim, attributes_to_delete)
@claim = claim
@attributes_to_delete = attributes_to_delete.map(&:to_s)
end

def scrub!
ApplicationRecord.transaction do
claim.amendments.each { |amendment| scrub_amendment!(amendment) }
scrub_claim!
scrub_session!
end
end

private

def scrub_amendment!(amendment)
amendment_data_to_scrub = attributes_to_delete & amendment.claim_changes.keys.map(&:to_s)
personal_data_mask = amendment_data_to_scrub.to_h { |attr| [attr, nil] }
amendment.claim_changes.merge!(personal_data_mask)
amendment.personal_data_removed_at = Time.zone.now
amendment.save!
end

def scrub_claim!
personal_data_mask = attributes_to_delete.to_h { |attr| [attr, nil] }
attributes_to_set = personal_data_mask.merge(
personal_data_removed_at: Time.zone.now
)
claim.update!(attributes_to_set)
end

def scrub_session!
return unless claim.journey_session

claim.journey_session.update!(answers: {})
end
end
end
14 changes: 2 additions & 12 deletions app/models/payment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,14 @@ class Payment < ApplicationRecord
postcode
has_student_loan
banking_name
national_insurance_number
]
PERSONAL_CLAIM_DETAILS_ATTRIBUTES_FORBIDDING_DISCREPANCIES = %i[
date_of_birth
student_loan_plan
bank_sort_code
bank_account_number
building_society_roll_number
]

PERSONAL_ELIGIBILITY_DETAILS_ATTRIBUTES_FORBIDDING_DISCREPANCIES = %i[
teacher_reference_number
national_insurance_number
]

delegate(*(PERSONAL_CLAIM_DETAILS_ATTRIBUTES_PERMITTING_DISCREPANCIES + PERSONAL_CLAIM_DETAILS_ATTRIBUTES_FORBIDDING_DISCREPANCIES), to: :claim_for_personal_details)
Expand All @@ -61,15 +57,9 @@ def personal_details_must_be_consistent
attribute_values.uniq.count > 1 && !attribute_values.all?(&:blank?)
}

mismatching_eligibility_attributes = PERSONAL_ELIGIBILITY_DETAILS_ATTRIBUTES_FORBIDDING_DISCREPANCIES.select { |attribute|
attribute_values = claims.map(&:eligibility).map(&attribute)
attribute_values.uniq.count > 1 && !attribute_values.all?(&:blank?)
}

if mismatching_attributes.any? || mismatching_eligibility_attributes.any?
if mismatching_attributes.any?
claims_sentence = claims.map(&:reference).to_sentence
attributes_list = mismatching_attributes.map { |attribute| Claim.human_attribute_name(attribute).downcase }
attributes_list += mismatching_eligibility_attributes.map { |attribute| claims.first.eligibility.class.human_attribute_name(attribute).downcase }
attributes_sentence = attributes_list.to_sentence

errors.add(:claims, "#{claims_sentence} have different values for #{attributes_sentence}")
Expand Down
2 changes: 1 addition & 1 deletion app/models/payroll_run.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def self.create_with_claims!(claims, topups, attrs = {})
end

def self.group_by_field(obj)
obj.is_a?(Claim) ? obj.eligibility.teacher_reference_number : obj.teacher_reference_number
obj.national_insurance_number
end

def download_triggered?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
# was scheduled to be paid more than two months ago.
#
# Attributes are set to nil, and personal_data_removed_at is set to the current timestamp.
#
# Inherit policy specifc data scrubbers from this class
# `app/models/policy/{some_policy}/claim_personal_data_scrubber.rb`

class Claim
class PersonalDataScrubber
module Policies
class ClaimPersonalDataScrubber
PERSONAL_DATA_ATTRIBUTES_TO_DELETE = [
:first_name,
:middle_name,
Expand All @@ -29,46 +32,65 @@ class PersonalDataScrubber
]

def scrub_completed_claims
Claim.transaction do
scrub_claims(old_rejected_claims)
scrub_claims(old_paid_claims)
old_rejected_claims
.unscrubbed
.includes(:amendments, :journey_session).each do |claim|
Claim::Scrubber.scrub!(claim, self.class::PERSONAL_DATA_ATTRIBUTES_TO_DELETE)
end

old_paid_claims
.unscrubbed
.includes(:amendments, :journey_session).each do |claim|
Claim::Scrubber.scrub!(claim, self.class::PERSONAL_DATA_ATTRIBUTES_TO_DELETE)
end
end

private

def scrub_claims(claims)
claims.includes(:amendments).each do |claim|
scrub_amendments_personal_data(claim)
end
def policy
self.class.module_parent
end

claims.update_all(attribute_values_to_set)
def claim_scope
Claim.by_policy(policy)
end

def attribute_values_to_set
PERSONAL_DATA_ATTRIBUTES_TO_DELETE.map { |attr| [attr, nil] }.to_h.merge(
personal_data_removed_at: Time.zone.now
def old_rejected_claims
claims_rejected_before(minimum_time)
end

def claims_rejected_before(date)
rejected_claims.where(
"decisions.created_at < :minimum_time",
minimum_time: date
)
end

def old_rejected_claims
Claim.joins(:decisions)
.where(personal_data_removed_at: nil)
def rejected_claims
claim_scope.joins(:decisions)
.where(
"(decisions.undone = false AND decisions.result = :rejected AND decisions.created_at < :minimum_time)",
minimum_time: minimum_time,
"(decisions.undone = false AND decisions.result = :rejected)",
rejected: Decision.results.fetch(:rejected)
)
end

def old_paid_claims
claims_paid_before(minimum_time)
end

def claims_paid_before(date)
paid_claims.where(
"payments.scheduled_payment_date < :minimum_time",
minimum_time: date
)
end

def paid_claims
claim_ids_with_payrollable_topups = Topup.payrollable.pluck(:claim_id)
claim_ids_with_payrolled_topups_without_payment_confirmation = Topup.joins(payment: [:payroll_run]).where(payments: {scheduled_payment_date: nil}).pluck(:claim_id)

Claim.approved.joins(payments: [:payroll_run])
.where(personal_data_removed_at: nil)
claim_scope.approved.joins(payments: [:payroll_run])
.where.not(id: claim_ids_with_payrollable_topups + claim_ids_with_payrolled_topups_without_payment_confirmation)
.where("payments.scheduled_payment_date < :minimum_time", minimum_time: minimum_time)
end

def minimum_time
Expand All @@ -78,21 +100,5 @@ def minimum_time
def current_academic_year
AcademicYear.current
end

def scrub_amendments_personal_data(claim)
claim.amendments.each do |amendment|
scrub_amendment_personal_data(amendment)
end
end

def scrub_amendment_personal_data(amendment)
attributes_to_scrub = PERSONAL_DATA_ATTRIBUTES_TO_DELETE.map(&:to_s) & amendment.claim_changes.keys
personal_data_mask = attributes_to_scrub.to_h { |attribute| [attribute, nil] }
amendment.claim_changes.merge!(personal_data_mask)

amendment.personal_data_removed_at = Time.zone.now

amendment.save!
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Policies
module EarlyCareerPayments
class ClaimPersonalDataScrubber < Policies::ClaimPersonalDataScrubber
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Policies
module FurtherEducationPayments
class ClaimPersonalDataScrubber < Policies::ClaimPersonalDataScrubber
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
module Policies
module InternationalRelocationPayments
class ClaimPersonalDataScrubber < Policies::ClaimPersonalDataScrubber
PERSONAL_DATA_ATTRIBUTES_TO_DELETE = [
:date_of_birth,
:address_line_1,
:address_line_2,
:address_line_3,
:address_line_4,
:postcode,
:payroll_gender,
:bank_sort_code,
:bank_account_number,
:building_society_roll_number,
:banking_name,
:hmrc_bank_validation_responses,
:mobile_number,
:teacher_id_user_info,
:dqt_teacher_status
]

PERSONAL_DATA_ATTRIBUTES_TO_RETAIN_FOR_EXTENDED_PERIOD = [
:first_name,
:middle_name,
:surname,
:national_insurance_number
]

ANY_NON_NULL_EXTENDED_PERIOD_ATTRIBUTES =
PERSONAL_DATA_ATTRIBUTES_TO_RETAIN_FOR_EXTENDED_PERIOD.map do |attr|
"#{attr} IS NOT NULL"
end.join(" OR ")

def scrub_completed_claims
super

claims_rejected_before(extended_period_end_date).where(
ANY_NON_NULL_EXTENDED_PERIOD_ATTRIBUTES
).each do |claim|
Claim::Scrubber.scrub!(
claim,
PERSONAL_DATA_ATTRIBUTES_TO_RETAIN_FOR_EXTENDED_PERIOD
)
end

claims_paid_before(extended_period_end_date).where(
ANY_NON_NULL_EXTENDED_PERIOD_ATTRIBUTES
).each do |claim|
Claim::Scrubber.scrub!(
claim,
PERSONAL_DATA_ATTRIBUTES_TO_RETAIN_FOR_EXTENDED_PERIOD
)
end
end

def extended_period_end_date
minimum_time - 2.years
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Policies
module LevellingUpPremiumPayments
class ClaimPersonalDataScrubber < Policies::ClaimPersonalDataScrubber
end
end
end
Loading

0 comments on commit 2ffc884

Please sign in to comment.