diff --git a/app/jobs/delete_personal_data_from_old_claims_job.rb b/app/jobs/delete_personal_data_from_old_claims_job.rb index 13760e8d97..8f9fb6e87c 100644 --- a/app/jobs/delete_personal_data_from_old_claims_job.rb +++ b/app/jobs/delete_personal_data_from_old_claims_job.rb @@ -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 diff --git a/app/models/claim/personal_data_scrubber.rb b/app/models/claim/personal_data_scrubber.rb index 881fb7d7cc..06bac41b1f 100644 --- a/app/models/claim/personal_data_scrubber.rb +++ b/app/models/claim/personal_data_scrubber.rb @@ -3,6 +3,9 @@ # 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 @@ -29,7 +32,7 @@ class PersonalDataScrubber ] def scrub_completed_claims - Claim.transaction do + ApplicationRecord.transaction do scrub_claims(old_rejected_claims) scrub_claims(old_paid_claims) end @@ -37,6 +40,14 @@ def scrub_completed_claims private + def policy + self.class.module_parent + end + + def claim_scope + Claim.by_policy(policy) + end + def scrub_claims(claims) claims.includes(:amendments).each do |claim| scrub_amendments_personal_data(claim) @@ -46,13 +57,13 @@ def scrub_claims(claims) end def attribute_values_to_set - PERSONAL_DATA_ATTRIBUTES_TO_DELETE.map { |attr| [attr, nil] }.to_h.merge( + self.class::PERSONAL_DATA_ATTRIBUTES_TO_DELETE.map { |attr| [attr, nil] }.to_h.merge( personal_data_removed_at: Time.zone.now ) end def old_rejected_claims - Claim.joins(:decisions) + claim_scope.joins(:decisions) .where(personal_data_removed_at: nil) .where( "(decisions.undone = false AND decisions.result = :rejected AND decisions.created_at < :minimum_time)", @@ -65,7 +76,7 @@ def old_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]) + claim_scope.approved.joins(payments: [:payroll_run]) .where(personal_data_removed_at: nil) .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) @@ -86,7 +97,7 @@ def scrub_amendments_personal_data(claim) end def scrub_amendment_personal_data(amendment) - attributes_to_scrub = PERSONAL_DATA_ATTRIBUTES_TO_DELETE.map(&:to_s) & amendment.claim_changes.keys + attributes_to_scrub = self.class::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) diff --git a/app/models/policies/early_career_payments/claim_personal_data_scrubber.rb b/app/models/policies/early_career_payments/claim_personal_data_scrubber.rb new file mode 100644 index 0000000000..8c26ee459c --- /dev/null +++ b/app/models/policies/early_career_payments/claim_personal_data_scrubber.rb @@ -0,0 +1,6 @@ +module Policies + module EarlyCareerPayments + class ClaimPersonalDataScrubber < Claim::PersonalDataScrubber + end + end +end diff --git a/app/models/policies/further_education_payments.rb b/app/models/policies/further_education_payments.rb index 2e526c6f07..66bbfe295f 100644 --- a/app/models/policies/further_education_payments.rb +++ b/app/models/policies/further_education_payments.rb @@ -1,5 +1,7 @@ module Policies module FurtherEducationPayments + include BasePolicy + extend self # Percentage of claims to QA MIN_QA_THRESHOLD = 10 end diff --git a/app/models/policies/further_education_payments/claim_personal_data_scrubber.rb b/app/models/policies/further_education_payments/claim_personal_data_scrubber.rb new file mode 100644 index 0000000000..83aa091371 --- /dev/null +++ b/app/models/policies/further_education_payments/claim_personal_data_scrubber.rb @@ -0,0 +1,10 @@ +module Policies + module FurtherEducationPayments + class ClaimPersonalDataScrubber < Claim::PersonalDataScrubber + def scrub_completed_claims + # FIXME RL: temp NOOP until business decsion around whether TRN is + # required in the payment claim matching has been resolved + end + end + end +end diff --git a/app/models/policies/international_relocation_payments/claim_personal_data_scrubber.rb b/app/models/policies/international_relocation_payments/claim_personal_data_scrubber.rb new file mode 100644 index 0000000000..f3c8978bd5 --- /dev/null +++ b/app/models/policies/international_relocation_payments/claim_personal_data_scrubber.rb @@ -0,0 +1,10 @@ +module Policies + module InternationalRelocationPayments + class ClaimPersonalDataScrubber < Claim::PersonalDataScrubber + def scrub_completed_claims + # FIXME RL: temp NOOP until business decsion around whether TRN is + # required in the payment claim matching has been resolved + end + end + end +end diff --git a/app/models/policies/levelling_up_premium_payments/claim_personal_data_scrubber.rb b/app/models/policies/levelling_up_premium_payments/claim_personal_data_scrubber.rb new file mode 100644 index 0000000000..84dfc286c3 --- /dev/null +++ b/app/models/policies/levelling_up_premium_payments/claim_personal_data_scrubber.rb @@ -0,0 +1,6 @@ +module Policies + module LevellingUpPremiumPayments + class ClaimPersonalDataScrubber < Claim::PersonalDataScrubber + end + end +end diff --git a/app/models/policies/student_loans/claim_personal_data_scrubber.rb b/app/models/policies/student_loans/claim_personal_data_scrubber.rb new file mode 100644 index 0000000000..5b50d9d32e --- /dev/null +++ b/app/models/policies/student_loans/claim_personal_data_scrubber.rb @@ -0,0 +1,6 @@ +module Policies + module StudentLoans + class ClaimPersonalDataScrubber < Claim::PersonalDataScrubber + end + end +end diff --git a/config/analytics.yml b/config/analytics.yml index c690b55c19..1371f0917f 100644 --- a/config/analytics.yml +++ b/config/analytics.yml @@ -201,6 +201,7 @@ shared: - date_of_entry - nationality - current_school_id + - award_amount :schools: - id - urn @@ -270,3 +271,4 @@ shared: - id - created_at - updated_at + - award_amount diff --git a/db/migrate/20240701143154_add_award_amount_to_international_relocation_payments_eligibilities.rb b/db/migrate/20240701143154_add_award_amount_to_international_relocation_payments_eligibilities.rb new file mode 100644 index 0000000000..8f318d409b --- /dev/null +++ b/db/migrate/20240701143154_add_award_amount_to_international_relocation_payments_eligibilities.rb @@ -0,0 +1,5 @@ +class AddAwardAmountToInternationalRelocationPaymentsEligibilities < ActiveRecord::Migration[7.0] + def change + add_column :international_relocation_payments_eligibilities, :award_amount, :decimal, precision: 7, scale: 2 + end +end diff --git a/db/migrate/20240702105328_add_award_amount_to_further_education_payments_eligibilities.rb b/db/migrate/20240702105328_add_award_amount_to_further_education_payments_eligibilities.rb new file mode 100644 index 0000000000..0c1fd81385 --- /dev/null +++ b/db/migrate/20240702105328_add_award_amount_to_further_education_payments_eligibilities.rb @@ -0,0 +1,5 @@ +class AddAwardAmountToFurtherEducationPaymentsEligibilities < ActiveRecord::Migration[7.0] + def change + add_column :further_education_payments_eligibilities, :award_amount, :decimal, precision: 7, scale: 2 + end +end diff --git a/db/schema.rb b/db/schema.rb index ff00b13637..96d92f0c84 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_06_27_145713) do +ActiveRecord::Schema[7.0].define(version: 2024_07_02_105328) do # These are extensions that must be enabled in order to support this database enable_extension "pg_trgm" enable_extension "pgcrypto" @@ -185,6 +185,7 @@ create_table "further_education_payments_eligibilities", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.decimal "award_amount", precision: 7, scale: 2 end create_table "international_relocation_payments_eligibilities", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| @@ -201,6 +202,7 @@ t.string "passport_number" t.string "school_headteacher_name" t.uuid "current_school_id" + t.decimal "award_amount", precision: 7, scale: 2 t.index ["current_school_id"], name: "index_irb_eligibilities_on_current_school_id" end diff --git a/spec/factories/policies/further_education_payments/eligibilities.rb b/spec/factories/policies/further_education_payments/eligibilities.rb new file mode 100644 index 0000000000..5867f6554f --- /dev/null +++ b/spec/factories/policies/further_education_payments/eligibilities.rb @@ -0,0 +1,6 @@ +FactoryBot.define do + factory :further_education_payments_eligibility, class: "Policies::FurtherEducationPayments::Eligibility" do + trait :eligible do + end + end +end diff --git a/spec/factories/policies/international_relocation_payments/eligibilities.rb b/spec/factories/policies/international_relocation_payments/eligibilities.rb index 305c19a952..f104e77618 100644 --- a/spec/factories/policies/international_relocation_payments/eligibilities.rb +++ b/spec/factories/policies/international_relocation_payments/eligibilities.rb @@ -8,6 +8,14 @@ trait :eligible do eligible_home_office eligible_school + + application_route { "teacher" } + state_funded_secondary_school { true } + one_year { true } + start_date { Date.tomorrow } + subject { "physics" } + visa_type { "British National (Overseas) visa" } + date_of_entry { start_date - 1.week } end trait :eligible_school do diff --git a/spec/models/claim/personal_data_scrubber_spec.rb b/spec/models/claim/personal_data_scrubber_spec.rb deleted file mode 100644 index da2101dfc8..0000000000 --- a/spec/models/claim/personal_data_scrubber_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -require "rails_helper" - -RSpec.describe Claim::PersonalDataScrubber, type: :model do - it_behaves_like( - "a claim personal data scrubber", - Policies::LevellingUpPremiumPayments - ) -end diff --git a/spec/models/policies/early_career_payments/claim_personal_data_scrubber_spec.rb b/spec/models/policies/early_career_payments/claim_personal_data_scrubber_spec.rb new file mode 100644 index 0000000000..50897dcda2 --- /dev/null +++ b/spec/models/policies/early_career_payments/claim_personal_data_scrubber_spec.rb @@ -0,0 +1,8 @@ +require "rails_helper" + +RSpec.describe Policies::EarlyCareerPayments::ClaimPersonalDataScrubber do + it_behaves_like( + "a claim personal data scrubber", + Policies::EarlyCareerPayments + ) +end diff --git a/spec/models/policies/further_education_payments/claim_personal_data_scrubber_spec.rb b/spec/models/policies/further_education_payments/claim_personal_data_scrubber_spec.rb new file mode 100644 index 0000000000..e5be32114f --- /dev/null +++ b/spec/models/policies/further_education_payments/claim_personal_data_scrubber_spec.rb @@ -0,0 +1,10 @@ +require "rails_helper" + +RSpec.describe Policies::FurtherEducationPayments::ClaimPersonalDataScrubber do + # FIXME RL: temp disabled until business decsion around whether TRN is + # required in the payment claim matching has been resolved + # it_behaves_like( + # "a claim personal data scrubber", + # Policies::FurtherEducationPayments + # ) +end diff --git a/spec/models/policies/international_relocation_payments/claim_personal_data_scrubber_spec.rb b/spec/models/policies/international_relocation_payments/claim_personal_data_scrubber_spec.rb new file mode 100644 index 0000000000..ed45b7fc64 --- /dev/null +++ b/spec/models/policies/international_relocation_payments/claim_personal_data_scrubber_spec.rb @@ -0,0 +1,10 @@ +require "rails_helper" + +RSpec.describe Policies::InternationalRelocationPayments::ClaimPersonalDataScrubber do + # FIXME RL: temp disabled until business decsion around whether TRN is + # required in the payment claim matching has been resolved + # it_behaves_like( + # "a claim personal data scrubber", + # Policies::InternationalRelocationPayments + # ) +end diff --git a/spec/models/policies/levelling_up_premium_payments/claim_personal_data_scrubber_spec.rb b/spec/models/policies/levelling_up_premium_payments/claim_personal_data_scrubber_spec.rb new file mode 100644 index 0000000000..e02f020f92 --- /dev/null +++ b/spec/models/policies/levelling_up_premium_payments/claim_personal_data_scrubber_spec.rb @@ -0,0 +1,40 @@ +require "rails_helper" + +RSpec.describe Policies::LevellingUpPremiumPayments::ClaimPersonalDataScrubber do + it_behaves_like( + "a claim personal data scrubber", + Policies::LevellingUpPremiumPayments + ) + + subject(:personal_data_scrubber) { described_class.new.scrub_completed_claims } + let!(:journey_configuration) { create(:journey_configuration, :additional_payments) } + let(:current_academic_year) { AcademicYear.current } + let(:last_academic_year) { Time.zone.local(current_academic_year.start_year, 8, 1) } + let(:user) { create(:dfe_signin_user) } + + it "does not delete details from a claim that has a payment, but has a payrollable topup" do + eligibility = create(:levelling_up_premium_payments_eligibility, :eligible, award_amount: 1500.0) + + claim = create(:claim, :approved, policy: Policies::LevellingUpPremiumPayments, eligibility: eligibility) + + create(:payment, :confirmed, :with_figures, claims: [claim], scheduled_payment_date: last_academic_year) + create(:topup, payment: nil, claim: claim, award_amount: 500, created_by: user) + + expect { personal_data_scrubber }.not_to change { claim.reload.attributes } + end + + it "does not delete details from a claim that has a payment, but has a payrolled topup without payment confirmation" do + claim = nil + + travel_to 2.months.ago do + eligibility = create(:levelling_up_premium_payments_eligibility, :eligible, award_amount: 1500.0) + claim = create(:claim, :approved, policy: Policies::LevellingUpPremiumPayments, eligibility: eligibility) + create(:payment, :confirmed, :with_figures, claims: [claim], scheduled_payment_date: last_academic_year) + end + + payment2 = create(:payment, :with_figures, claims: [claim], scheduled_payment_date: nil) + create(:topup, payment: payment2, claim: claim, award_amount: 500, created_by: user) + + expect { personal_data_scrubber }.not_to change { claim.reload.attributes } + end +end diff --git a/spec/models/policies/student_loans/claim_personal_data_scrubber_spec.rb b/spec/models/policies/student_loans/claim_personal_data_scrubber_spec.rb new file mode 100644 index 0000000000..1bc1df2647 --- /dev/null +++ b/spec/models/policies/student_loans/claim_personal_data_scrubber_spec.rb @@ -0,0 +1,8 @@ +require "rails_helper" + +RSpec.describe Policies::StudentLoans::ClaimPersonalDataScrubber do + it_behaves_like( + "a claim personal data scrubber", + Policies::StudentLoans + ) +end diff --git a/spec/support/claim_personal_data_scrubber_shared_examples.rb b/spec/support/claim_personal_data_scrubber_shared_examples.rb index c61348d2fb..1f13a44a15 100644 --- a/spec/support/claim_personal_data_scrubber_shared_examples.rb +++ b/spec/support/claim_personal_data_scrubber_shared_examples.rb @@ -75,45 +75,37 @@ expect { personal_data_scrubber }.not_to change { claim.reload.attributes } end - it "does not delete details from a claim that has a payment, but has a payrollable topup" do - eligibility = create(eligibility_factory, :eligible, award_amount: 1500.0) - - claim = create(:claim, :approved, policy: policy, eligibility: eligibility) - - create(:payment, :confirmed, :with_figures, claims: [claim], scheduled_payment_date: last_academic_year) - create(:topup, payment: nil, claim: claim, award_amount: 500, created_by: user) - - expect { personal_data_scrubber }.not_to change { claim.reload.attributes } - end - - it "does not delete details from a claim that has a payment, but has a payrolled topup without payment confirmation" do + it "deletes expected details from a claim with multiple payments all of which have been confirmed" do claim = nil travel_to 2.months.ago do - eligibility = create(eligibility_factory, :eligible, award_amount: 1500.0) + eligibility = build(eligibility_factory, :eligible) + # Student loans don't have a settable award amount + if eligibility.has_attribute?(:award_amount) + eligibility.award_amount = 1500.0 + end + eligibility.save! claim = create(:claim, :approved, policy: policy, eligibility: eligibility) create(:payment, :confirmed, :with_figures, claims: [claim], scheduled_payment_date: last_academic_year) end - payment2 = create(:payment, :with_figures, claims: [claim], scheduled_payment_date: nil) - create(:topup, payment: payment2, claim: claim, award_amount: 500, created_by: user) + payment2 = create(:payment, :confirmed, :with_figures, claims: [claim], scheduled_payment_date: last_academic_year) - expect { personal_data_scrubber }.not_to change { claim.reload.attributes } - end + if claim.topupable? + create(:topup, payment: payment2, claim: claim, award_amount: 500, created_by: user) + end - it "deletes expected details from a claim with multiple payments all of which have been confirmed" do - claim = nil + expect { personal_data_scrubber }.to change { claim.reload.attributes } + end - travel_to 2.months.ago do - eligibility = create(eligibility_factory, :eligible, award_amount: 1500.0) - claim = create(:claim, :approved, policy: policy, eligibility: eligibility) - create(:payment, :confirmed, :with_figures, claims: [claim], scheduled_payment_date: last_academic_year) - end + it "does not delete expected details from a claim for a different policy" do + other_policy = Policies::POLICIES.detect { |p| p != policy } - payment2 = create(:payment, :confirmed, :with_figures, claims: [claim], scheduled_payment_date: last_academic_year) - create(:topup, payment: payment2, claim: claim, award_amount: 500, created_by: user) + claim = create(:claim, :submitted, policy: other_policy) + create(:decision, :rejected, claim: claim, created_at: last_academic_year) + claim.update_attribute :hmrc_bank_validation_responses, ["test"] - expect { personal_data_scrubber }.to change { claim.reload.attributes } + expect { personal_data_scrubber }.not_to change { claim.reload.attributes } end it "deletes expected details from an old rejected claim, setting a personal_data_removed_at timestamp" do @@ -182,13 +174,13 @@ it "only scrubs claims from the previous academic year" do # Initialise the scrubber, and create a claim - scrubber = Claim::PersonalDataScrubber.new + scrubber = described_class.new claim = create(:claim, :submitted, policy: policy) create(:decision, :rejected, claim: claim) travel_to(last_academic_year) do - claim = create(:claim, :submitted) + claim = create(:claim, :submitted, policy: policy) create(:decision, :rejected, claim: claim) end @@ -204,15 +196,20 @@ claim, amendment = nil travel_to last_academic_year - 1.week do claim = create(:claim, :submitted, policy: policy) - amendment = create(:amendment, claim: claim, claim_changes: { - "teacher_reference_number" => [generate(:teacher_reference_number).to_s, claim.eligibility.teacher_reference_number], + claim_changes = { "payroll_gender" => ["male", claim.payroll_gender], "date_of_birth" => [25.years.ago.to_date, claim.date_of_birth], "student_loan_plan" => ["plan_1", claim.student_loan_plan], "bank_sort_code" => ["457288", claim.bank_sort_code], "bank_account_number" => ["84818482", claim.bank_account_number], "building_society_roll_number" => ["123456789/ABCD", claim.building_society_roll_number] - }) + } + + if claim.eligibility.has_attribute?(:teacher_reference_number) + claim_changes["teacher_reference_number"] = [generate(:teacher_reference_number).to_s, claim.eligibility.teacher_reference_number] + end + + amendment = create(:amendment, claim: claim, claim_changes: claim_changes) create(:decision, :approved, claim: claim, created_at: last_academic_year) create(:payment, :confirmed, :with_figures, claims: [claim], scheduled_payment_date: last_academic_year) end