diff --git a/app/jobs/file_importer_job.rb b/app/jobs/file_importer_job.rb index 1c881b3e09..d27ee78778 100644 --- a/app/jobs/file_importer_job.rb +++ b/app/jobs/file_importer_job.rb @@ -34,11 +34,12 @@ def perform(file_upload_id) raise ActiveRecord::RecordNotFound unless FileUpload.exists?(id: file_upload_id) self.file_upload_id = file_upload_id - self.uploaded_by_email = find_user_email + uploaded_by = FileUpload.find(file_upload_id).uploaded_by + self.uploaded_by_email = uploaded_by.email ingest! send_success_email if uploaded_by_email - post_import_block&.call + post_import_block&.call(uploaded_by) rescue => e Rollbar.error(e) rescue_with_lambda&.call @@ -59,10 +60,6 @@ def ingest! FileUpload.delete(file_upload_id) end - def find_user_email - FileUpload.select(:uploaded_by_id).find_by(id: file_upload_id)&.uploaded_by&.email - end - def send_success_email return unless notify_with_mailer && success_mailer_method diff --git a/app/jobs/import_student_loans_data_job.rb b/app/jobs/import_student_loans_data_job.rb index b26f672823..abc6c3029b 100644 --- a/app/jobs/import_student_loans_data_job.rb +++ b/app/jobs/import_student_loans_data_job.rb @@ -1,9 +1,9 @@ class ImportStudentLoansDataJob < FileImporterJob - import_with StudentLoansDataImporter do + import_with StudentLoansDataImporter do |uploaded_by| Rails.logger.info "SLC data imported; student loan verifiers will re-run where necessary" - StudentLoanAmountCheckJob.perform_later - StudentLoanPlanCheckJob.perform_later + StudentLoanAmountCheckJob.perform_later(uploaded_by) + StudentLoanPlanCheckJob.perform_later(uploaded_by) end rescue_with -> do StudentLoansData.delete_all diff --git a/app/jobs/student_loan_amount_check_job.rb b/app/jobs/student_loan_amount_check_job.rb index 6aabe721f1..6edb39859a 100644 --- a/app/jobs/student_loan_amount_check_job.rb +++ b/app/jobs/student_loan_amount_check_job.rb @@ -1,10 +1,10 @@ class StudentLoanAmountCheckJob < ApplicationJob - def perform + def perform(admin) delete_no_data_student_loan_amount_tasks claims = current_year_tslr_claims_awaiting_decision.awaiting_task("student_loan_amount") claims.each do |claim| - ClaimStudentLoanDetailsUpdater.call(claim) + ClaimStudentLoanDetailsUpdater.call(claim, admin) AutomatedChecks::ClaimVerifiers::StudentLoanAmount.new(claim:).perform rescue => e # If something goes wrong, log the error and continue diff --git a/app/jobs/student_loan_plan_check_job.rb b/app/jobs/student_loan_plan_check_job.rb index 5f002f8a08..8fbfd3c700 100644 --- a/app/jobs/student_loan_plan_check_job.rb +++ b/app/jobs/student_loan_plan_check_job.rb @@ -6,11 +6,11 @@ class StudentLoanPlanCheckJob < ApplicationJob Policies::EarlyYearsPayments ].freeze - def perform + def perform(admin) delete_no_data_student_loan_plan_tasks claims = current_year_claims_awaiting_decision.awaiting_task("student_loan_plan") claims.each do |claim| - ClaimStudentLoanDetailsUpdater.call(claim) + ClaimStudentLoanDetailsUpdater.call(claim, admin) AutomatedChecks::ClaimVerifiers::StudentLoanPlan.new(claim:).perform rescue => e # If something goes wrong, log the error and continue diff --git a/app/models/claim.rb b/app/models/claim.rb index 3258a6d86f..31d75cd73e 100644 --- a/app/models/claim.rb +++ b/app/models/claim.rb @@ -8,6 +8,7 @@ class Claim < ApplicationRecord national_insurance_number date_of_birth student_loan_plan + has_student_loan bank_sort_code bank_account_number building_society_roll_number diff --git a/app/models/claim_student_loan_details_updater.rb b/app/models/claim_student_loan_details_updater.rb index 0467b825db..05021c8787 100644 --- a/app/models/claim_student_loan_details_updater.rb +++ b/app/models/claim_student_loan_details_updater.rb @@ -1,25 +1,38 @@ class ClaimStudentLoanDetailsUpdater - def self.call(claim) - new(claim).update_claim_with_latest_data + class StudentLoanUpdateError < StandardError; end + + def self.call(claim, admin) + new(claim, admin).update_claim_with_latest_data end - def initialize(claim) + def initialize(claim, admin) @claim = claim + @admin = admin end def update_claim_with_latest_data - claim.transaction do - eligibility.update!(eligibility_student_loan_attributes) if claim.has_tslr_policy? + claim_changes = {} + + if claim.has_student_loan != student_loans_data.has_student_loan? + claim_changes[:has_student_loan] = student_loans_data.has_student_loan? + end - claim.assign_attributes(claim_student_loan_attributes) + if claim.student_loan_plan != student_loans_data.student_loan_plan + claim_changes[:student_loan_plan] = student_loans_data.student_loan_plan + end - claim.save!(context: :"student-loan") + if student_loan_repayment_amount_changed? + claim_changes[:eligibility_attributes] = { + student_loan_repayment_amount: student_loans_data.total_repayment_amount + } end + + amend_claim(claim_changes) if claim_changes.present? end private - attr_reader :claim + attr_reader :claim, :admin delegate :eligibility, to: :claim delegate :national_insurance_number, :date_of_birth, to: :claim @@ -41,4 +54,31 @@ def student_loans_data date_of_birth: date_of_birth ) end + + def student_loan_repayment_amount_changed? + return false unless claim.has_tslr_policy? + + claim.eligibility.student_loan_repayment_amount != student_loans_data.total_repayment_amount + end + + def amend_claim(claim_changes) + amendment = Amendment.amend_claim( + claim, + claim_changes, + { + notes: "Student loan details updated from SLC data", + created_by: admin + } + ) + + if amendment.errors.any? + msg = [ + "Failed to update claim #{claim.id} student loan data.", + "amendment_error: \"#{amendment.errors.full_messages.to_sentence}\"", + "SLC data: #{claim_changes}" + ].join(" ") + + raise StudentLoanUpdateError, msg + end + end end diff --git a/app/models/policies/student_loans/eligibility.rb b/app/models/policies/student_loans/eligibility.rb index 7ea8cec68d..21a9b4e622 100644 --- a/app/models/policies/student_loans/eligibility.rb +++ b/app/models/policies/student_loans/eligibility.rb @@ -43,7 +43,7 @@ class Eligibility < ApplicationRecord validates :had_leadership_position, on: [:submit], inclusion: {in: [true, false], message: "Select yes if you were employed in a leadership position"} validates :mostly_performed_leadership_duties, on: [:submit], inclusion: {in: [true, false], message: "Select yes if you spent more than half your working hours on leadership duties"}, if: :had_leadership_position? validates_numericality_of :student_loan_repayment_amount, message: "Enter a valid monetary amount", allow_nil: true, greater_than_or_equal_to: 0, less_than_or_equal_to: 99999 - validates :student_loan_repayment_amount, on: :amendment, award_range: {max: 5_000} + validates :student_loan_repayment_amount, on: :amendment, award_range: {max: 5_000}, if: :student_loan_repayment_amount_changed? validates :teacher_reference_number, on: :amendment, presence: {message: "Enter your teacher reference number"} validate :validate_teacher_reference_number_length diff --git a/app/views/admin/amendments/index.html.erb b/app/views/admin/amendments/index.html.erb index 972f0ba899..1d3dd8af72 100644 --- a/app/views/admin/amendments/index.html.erb +++ b/app/views/admin/amendments/index.html.erb @@ -32,7 +32,7 @@ <% admin_amendment_details(amendment).each do |details| %>
changed from <%= details[1] %> to <%= details[2] %>
+changed from <%= details[1].presence || "[no details]" %> to <%= details[2].presence || "[no details]" %>
<% else %>changed
<% end %> diff --git a/spec/jobs/student_loan_amount_check_job_spec.rb b/spec/jobs/student_loan_amount_check_job_spec.rb index 12490eabde..4d103e7c78 100644 --- a/spec/jobs/student_loan_amount_check_job_spec.rb +++ b/spec/jobs/student_loan_amount_check_job_spec.rb @@ -1,7 +1,8 @@ require "rails_helper" RSpec.describe StudentLoanAmountCheckJob do - subject(:perform_job) { described_class.new.perform } + let(:admin) { create(:dfe_signin_user) } + subject(:perform_job) { described_class.new.perform(admin) } let!(:claim) { create(:claim, claim_status, academic_year:, policy: Policies::StudentLoans) } let(:claim_status) { :submitted } @@ -21,7 +22,7 @@ end it "excludes the claim from the check", :aggregate_failures do - expect(ClaimStudentLoanDetailsUpdater).not_to receive(:call).with(claim) + expect(ClaimStudentLoanDetailsUpdater).not_to receive(:call).with(claim, admin) expect(AutomatedChecks::ClaimVerifiers::StudentLoanAmount).not_to receive(:new).with(claim: claim) perform_job end @@ -49,7 +50,7 @@ context "when the student loan amount check did not run before" do it "updates the student loan details" do - expect(ClaimStudentLoanDetailsUpdater).to receive(:call).with(claim) + expect(ClaimStudentLoanDetailsUpdater).to receive(:call).with(claim, admin) perform_job end @@ -64,7 +65,7 @@ let!(:previous_task) { create(:task, claim: claim, name: "student_loan_amount", claim_verifier_match: nil, manual: false) } it "updates the student loan details" do - expect(ClaimStudentLoanDetailsUpdater).to receive(:call).with(claim) + expect(ClaimStudentLoanDetailsUpdater).to receive(:call).with(claim, admin) perform_job end @@ -119,7 +120,7 @@ nino: claim.national_insurance_number, date_of_birth: claim.date_of_birth ) - allow_any_instance_of(Claim).to receive(:save!) { raise(exception) } + allow_any_instance_of(Claim).to receive(:save) { raise(exception) } allow(Rollbar).to receive(:error) end diff --git a/spec/jobs/student_loan_plan_check_job_spec.rb b/spec/jobs/student_loan_plan_check_job_spec.rb index 97accbf41a..65716d6c63 100644 --- a/spec/jobs/student_loan_plan_check_job_spec.rb +++ b/spec/jobs/student_loan_plan_check_job_spec.rb @@ -1,7 +1,8 @@ require "rails_helper" RSpec.describe StudentLoanPlanCheckJob do - subject(:perform_job) { described_class.new.perform } + let(:admin) { create(:dfe_signin_user) } + subject(:perform_job) { described_class.new.perform(admin) } before do create(:journey_configuration, :further_education_payments) @@ -59,7 +60,7 @@ end it "updates the student loan details" do - expect(ClaimStudentLoanDetailsUpdater).to receive(:call).with(claim) + expect(ClaimStudentLoanDetailsUpdater).to receive(:call).with(claim, admin) perform_job end @@ -78,7 +79,7 @@ let(:claim) { create(:claim, claim_status, academic_year:, policy: Policies::FurtherEducationPayments) } it "updates the student loan details" do - expect(ClaimStudentLoanDetailsUpdater).to receive(:call).with(claim) + expect(ClaimStudentLoanDetailsUpdater).to receive(:call).with(claim, admin) perform_job end @@ -90,7 +91,7 @@ context "when the student loan plan check did not run before" do it "updates the student loan details" do - expect(ClaimStudentLoanDetailsUpdater).to receive(:call).with(claim) + expect(ClaimStudentLoanDetailsUpdater).to receive(:call).with(claim, admin) perform_job end @@ -104,7 +105,7 @@ let!(:previous_task) { create(:task, claim: claim, name: "student_loan_plan", claim_verifier_match: nil, manual: false) } it "updates the student loan details" do - expect(ClaimStudentLoanDetailsUpdater).to receive(:call).with(claim) + expect(ClaimStudentLoanDetailsUpdater).to receive(:call).with(claim, admin) perform_job end @@ -134,9 +135,10 @@ :student_loans_data, claim_reference: claim.reference, nino: claim.national_insurance_number, - date_of_birth: claim.date_of_birth + date_of_birth: claim.date_of_birth, + plan_type_of_deduction: 2 ) - allow_any_instance_of(Claim).to receive(:save!) { raise(exception) } + allow_any_instance_of(Claim).to receive(:save) { raise(exception) } allow(Rollbar).to receive(:error) end diff --git a/spec/models/claim_student_loan_details_updater_spec-old.rb b/spec/models/claim_student_loan_details_updater_spec-old.rb new file mode 100644 index 0000000000..55b599c27e --- /dev/null +++ b/spec/models/claim_student_loan_details_updater_spec-old.rb @@ -0,0 +1,236 @@ +require "rails_helper" + +RSpec.describe ClaimStudentLoanDetailsUpdater do + let(:updater) { described_class.new(claim, admin) } + let(:claim) { create(:claim, policy:) } + let(:policy) { Policies::StudentLoans } + let(:admin) { create(:dfe_signin_user) } + + describe ".call" do + let(:updater_mock) { instance_double(described_class) } + + before do + allow(described_class).to receive(:new).with(claim, admin).and_return(updater_mock) + end + + it "invokes the `update_claim_with_latest_data` instance method" do + expect(updater_mock).to receive(:update_claim_with_latest_data) + described_class.call(claim, admin) + end + end + + describe "#update_claim_with_latest_data" do + subject(:call) { updater.update_claim_with_latest_data } + + context "when no existing SLC data is found for the claimant" do + it "returns true" do + expect(call).to eq(true) + end + + context "when the policy is StudentLoans" do + let(:policy) { Policies::StudentLoans } + + it "does not update the claim student plan and zero repayment total" do + expect { call }.not_to change { claim.reload.has_student_loan } + end + + it "keeps the `submitted_using_slc_data` flag to `false` (default)" do + expect { call }.not_to change { claim.submitted_using_slc_data }.from(false) + end + + it "does not create an amendment" do + expect { call }.not_to change { claim.amendments.count } + end + end + + [Policies::EarlyCareerPayments, Policies::LevellingUpPremiumPayments, Policies::FurtherEducationPayments].each do |policy| + context "when the policy is #{policy}" do + let(:policy) { policy } + + it "does not update the claim" do + expect { call }.not_to change { claim.reload } + end + + it "does not create an amendment" do + expect { call }.not_to change { claim.amendments.count } + end + end + end + end + + context "when SLC data is found with student loan information for the claimant" do + before do + create(:student_loans_data, nino: claim.national_insurance_number, date_of_birth: claim.date_of_birth, plan_type_of_deduction: 1, amount: 50) + create(:student_loans_data, nino: claim.national_insurance_number, date_of_birth: claim.date_of_birth, plan_type_of_deduction: 2, amount: 60) + end + + it "returns true" do + expect(call).to eq(true) + end + + context "when the policy is StudentLoans" do + it "updates the claim with the student plan and the repayment total" do + expect { call }.to change { claim.reload.has_student_loan }.to(true) + .and change { claim.student_loan_plan }.to(StudentLoan::PLAN_1_AND_2) + .and change { claim.eligibility.student_loan_repayment_amount }.to(110) + end + + it "creates an amendment" do + expect { call }.to change { claim.amendments.count }.by(1) + + amendment = claim.amendments.last + + expect(amendment.claim_changes).to eq({ + "has_student_loan" => [false, true], + "student_loan_plan" => [Claim::NO_STUDENT_LOAN, StudentLoan::PLAN_1_AND_2], + "student_loan_repayment_amount" => [0, 110] + }) + + expect(amendment.notes).to eq( + "Student loan details updated from SLC data" + ) + + expect(amendment.created_by).to eq(admin) + end + end + + [Policies::EarlyCareerPayments, Policies::LevellingUpPremiumPayments, Policies::FurtherEducationPayments].each do |policy| + context "when the policy is #{policy}" do + let(:policy) { policy } + + it "updates the claim with the student plan only" do + expect { call }.to change { claim.reload.has_student_loan }.to(true) + .and change { claim.student_loan_plan }.to(StudentLoan::PLAN_1_AND_2) + end + + it "creates an amendment" do + expect { call }.to change { claim.amendments.count }.by(1) + + amendment = claim.amendments.last + + expect(amendment.claim_changes).to eq({ + "has_student_loan" => [false, true], + "student_loan_plan" => [Claim::NO_STUDENT_LOAN, StudentLoan::PLAN_1_AND_2] + }) + + expect(amendment.notes).to eq( + "Student loan details updated from SLC data" + ) + + expect(amendment.created_by).to eq(admin) + end + end + end + end + + context "when SLC data is found with no student loan information for the claimant" do + before do + create(:student_loans_data, nino: claim.national_insurance_number, date_of_birth: claim.date_of_birth, plan_type_of_deduction: nil, amount: nil) + end + + it "returns true" do + expect(call).to eq(true) + end + + context "when the policy is StudentLoans" do + it "updates the claim with the student plan and the repayment total" do + expect { call }.to change { claim.reload.has_student_loan }.to(false) + .and change { claim.student_loan_plan }.to(Claim::NO_STUDENT_LOAN) + .and change { claim.eligibility.student_loan_repayment_amount }.to(0) + end + + it "creates an amendment with the student plan change" do + expect { call }.to change { claim.amendments.count }.by(1) + + amendment = claim.amendments.last + + expect(amendment.claim_changes).to eq({ + "has_student_loan" => [true, false], + "student_loan_plan" => [StudentLoan::PLAN_1_AND_2, Claim::NO_STUDENT_LOAN], + "student_loan_repayment_amount" => [110, 0] + }) + + expect(amendment.notes).to eq( + "Student loan details updated from SLC data" + ) + + expect(amendment.created_by).to eq(admin) + end + end + + [Policies::EarlyCareerPayments, Policies::LevellingUpPremiumPayments].each do |policy| + context "when the policy is #{policy}" do + let(:policy) { policy } + + it "updates the claim with the student plan only" do + expect { call }.to change { claim.reload.has_student_loan }.to(false) + .and change { claim.student_loan_plan }.to(Claim::NO_STUDENT_LOAN) + end + + it "creates an amendment with the student plan change" do + expect { call }.to change { claim.amendments.count }.by(1) + + amendment = claim.amendments.last + + expect(amendment.claim_changes).to eq({ + "has_student_loan" => [true, false], + "student_loan_plan" => [StudentLoan::PLAN_1_AND_2, Claim::NO_STUDENT_LOAN] + }) + + expect(amendment.notes).to eq( + "Student loan details updated from SLC data" + ) + + expect(amendment.created_by).to eq(admin) + end + end + end + end + + context "when updating a claim after submission" do + let(:claim) do + create( + :claim, + :submitted, + :with_no_student_loan, + policy:, + eligibility_attributes: { + student_loan_repayment_amount: 0 + } + ) + end + + before do + create(:student_loans_data, nino: claim.national_insurance_number, date_of_birth: claim.date_of_birth, plan_type_of_deduction: 1, amount: 50) + end + + it "updates the claim with the student plan and the repayment total" do + expect { call }.to change { claim.reload.has_student_loan }.to(true) + .and change { claim.student_loan_plan }.to(StudentLoan::PLAN_1) + .and change { claim.eligibility.student_loan_repayment_amount }.to(50) + end + + it "creates an amendment" do + expect { call }.to change { claim.amendments.count }.by(1) + + amendment = claim.amendments.last + + expect(amendment.claim_changes).to eq({ + "has_student_loan" => [false, true], + "student_loan_plan" => [nil, StudentLoan::PLAN_1], + "student_loan_repayment_amount" => [0, 50] + }) + + expect(amendment.notes).to eq( + "Student loan details updated from SLC data" + ) + + expect(amendment.created_by).to eq(admin) + end + + it "does not change the `submitted_using_slc_data` flag" do + expect { call }.to not_change { claim.submitted_using_slc_data } + end + end + end +end diff --git a/spec/models/claim_student_loan_details_updater_spec.rb b/spec/models/claim_student_loan_details_updater_spec.rb index 61e02b57db..cbf4f36100 100644 --- a/spec/models/claim_student_loan_details_updater_spec.rb +++ b/spec/models/claim_student_loan_details_updater_spec.rb @@ -1,128 +1,427 @@ require "rails_helper" RSpec.describe ClaimStudentLoanDetailsUpdater do - let(:updater) { described_class.new(claim) } - let(:claim) { create(:claim, policy:) } - let(:policy) { Policies::StudentLoans } + let(:admin) { create(:dfe_signin_user) } describe ".call" do + let(:updater) { described_class.new(claim, admin) } + let(:claim) { create(:claim, policy:) } + let(:policy) { Policies::StudentLoans } + let(:updater_mock) { instance_double(described_class) } before do - allow(described_class).to receive(:new).with(claim).and_return(updater_mock) + allow(described_class).to receive(:new).with(claim, admin).and_return(updater_mock) end it "invokes the `update_claim_with_latest_data` instance method" do expect(updater_mock).to receive(:update_claim_with_latest_data) - described_class.call(claim) + described_class.call(claim, admin) end end describe "#update_claim_with_latest_data" do - subject(:call) { updater.update_claim_with_latest_data } + before { claim.reload } - context "when no existing SLC data is found for the claimant" do - it "returns true" do - expect(call).to eq(true) + context "when the claim has no student loan data" do + let(:claim) do + create( + :claim, + :submitted, + has_student_loan: nil, + student_loan_plan: nil, + **policy_attributes + ) end - context "when the policy is StudentLoans" do - let(:policy) { Policies::StudentLoans } + context "when no student loan data is found" do + context "when the claim is a tslr claim" do + let(:policy_attributes) do + { + policy: Policies::StudentLoans, + eligibility_attributes: {student_loan_repayment_amount: 0} + } + end + + it "doesn't change the claim attributes" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to not_change { claim.reload.has_student_loan } + .and not_change { claim.student_loan_plan } + .and not_change { claim.eligibility.student_loan_repayment_amount } + end - it "does not update the claim student plan and zero repayment total" do - expect { call }.not_to change { claim.reload.has_student_loan } + it "doesn't create an ammendment" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .not_to change { claim.amendments.count } + end end - it "keeps the `submitted_using_slc_data` flag to `false` (default)" do - expect { call }.not_to change { claim.submitted_using_slc_data }.from(false) + context "when the claim is not a tslr claim" do + let(:policy_attributes) do + { + policy: Policies::FurtherEducationPayments + } + end + + it "doesn't change the claim attributes" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to not_change { claim.reload.has_student_loan } + .and not_change { claim.student_loan_plan } + end + + it "doesn't create an ammendment" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .not_to change { claim.amendments.count } + end end end - [Policies::EarlyCareerPayments, Policies::LevellingUpPremiumPayments, Policies::FurtherEducationPayments].each do |policy| - context "when the policy is #{policy}" do - let(:policy) { policy } + context "when student loan data is found" do + before do + create( + :student_loans_data, + nino: claim.national_insurance_number, + date_of_birth: claim.date_of_birth, + plan_type_of_deduction: 1, + amount: 50 + ) + end + + context "when the claim is a tslr claim" do + let(:policy_attributes) do + { + policy: Policies::StudentLoans, + eligibility_attributes: {student_loan_repayment_amount: 0} + } + end + + it "updates the claim's attributes" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to change { claim.reload.has_student_loan }.from(nil).to(true) + .and change { claim.student_loan_plan }.from(nil).to(StudentLoan::PLAN_1) + .and change { claim.eligibility.student_loan_repayment_amount }.from(0).to(50) + end + + it "creates an ammendment" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to change { claim.amendments.count }.by(1) + + amendment = claim.amendments.last + + expect(amendment.claim_changes).to eq({ + "has_student_loan" => [nil, true], + "student_loan_plan" => [nil, StudentLoan::PLAN_1], + "student_loan_repayment_amount" => [0, 50] + }) + + expect(amendment.notes).to eq( + "Student loan details updated from SLC data" + ) + + expect(amendment.created_by).to eq(admin) + end + end + + context "when the claim is not a tslr claim" do + let(:policy_attributes) do + { + policy: Policies::LevellingUpPremiumPayments + } + end - it "does not update the claim" do - expect { call }.not_to change { claim.reload } + it "updates the claim's attributes" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to change { claim.reload.has_student_loan }.from(nil).to(true) + .and change { claim.student_loan_plan }.from(nil).to(StudentLoan::PLAN_1) + end + + it "creates an ammendment" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to change { claim.amendments.count }.by(1) + + amendment = claim.amendments.last + + expect(amendment.claim_changes).to eq({ + "has_student_loan" => [nil, true], + "student_loan_plan" => [nil, StudentLoan::PLAN_1] + }) + + expect(amendment.notes).to eq( + "Student loan details updated from SLC data" + ) + + expect(amendment.created_by).to eq(admin) end end end end - context "when SLC data is found with student loan information for the claimant" do - before do - create(:student_loans_data, nino: claim.national_insurance_number, date_of_birth: claim.date_of_birth, plan_type_of_deduction: 1, amount: 50) - create(:student_loans_data, nino: claim.national_insurance_number, date_of_birth: claim.date_of_birth, plan_type_of_deduction: 2, amount: 60) + context "when the claim has student loan data" do + let(:claim) do + create( + :claim, + :submitted, + has_student_loan: true, + student_loan_plan: StudentLoan::PLAN_1, + **policy_attributes + ) end - it "returns true" do - expect(call).to eq(true) - end + context "when student loan data is found" do + before do + create( + :student_loans_data, + nino: claim.national_insurance_number, + date_of_birth: claim.date_of_birth, + plan_type_of_deduction: 2, + amount: 100 + ) + end - context "when the policy is StudentLoans" do - it "updates the claim with the student plan and the repayment total" do - expect { call }.to change { claim.reload.has_student_loan }.to(true) - .and change { claim.student_loan_plan }.to(StudentLoan::PLAN_1_AND_2) - .and change { claim.eligibility.student_loan_repayment_amount }.to(110) + context "when the claim is a tslr claim" do + let(:policy_attributes) do + { + policy: Policies::StudentLoans, + eligibility_attributes: {student_loan_repayment_amount: 50} + } + end + + it "replaces the student loan attributes with the latest values" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to not_change { claim.reload.has_student_loan } + .and change { claim.student_loan_plan }.from(StudentLoan::PLAN_1).to(StudentLoan::PLAN_2) + .and change { claim.eligibility.student_loan_repayment_amount }.from(50).to(100) + end + + it "creates an ammendment" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to change { claim.amendments.count }.by(1) + + amendment = claim.amendments.last + + expect(amendment.claim_changes).to eq({ + "student_loan_plan" => [StudentLoan::PLAN_1, StudentLoan::PLAN_2], + "student_loan_repayment_amount" => [50, 100] + }) + + expect(amendment.notes).to eq( + "Student loan details updated from SLC data" + ) + + expect(amendment.created_by).to eq(admin) + end end - end - [Policies::EarlyCareerPayments, Policies::LevellingUpPremiumPayments, Policies::FurtherEducationPayments].each do |policy| - context "when the policy is #{policy}" do - let(:policy) { policy } + context "when the claim is not a tslr claim" do + let(:policy_attributes) do + { + policy: Policies::FurtherEducationPayments + } + end + + it "replaces the student loan attributes with the latest values" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to not_change { claim.reload.has_student_loan } + .and change { claim.student_loan_plan }.from(StudentLoan::PLAN_1).to(StudentLoan::PLAN_2) + end + + it "creates an ammendment" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to change { claim.amendments.count }.by(1) + + amendment = claim.amendments.last + + expect(amendment.claim_changes).to eq({ + "student_loan_plan" => [StudentLoan::PLAN_1, StudentLoan::PLAN_2] + }) + + expect(amendment.notes).to eq( + "Student loan details updated from SLC data" + ) - it "updates the claim with the student plan only" do - expect { call }.to change { claim.reload.has_student_loan }.to(true) - .and change { claim.student_loan_plan }.to(StudentLoan::PLAN_1_AND_2) + expect(amendment.created_by).to eq(admin) end end end - end - context "when SLC data is found with no student loan information for the claimant" do - before do - create(:student_loans_data, nino: claim.national_insurance_number, date_of_birth: claim.date_of_birth, plan_type_of_deduction: nil, amount: nil) - end + context "when multiple student loan data is found" do + before do + create( + :student_loans_data, + nino: claim.national_insurance_number, + date_of_birth: claim.date_of_birth, + plan_type_of_deduction: 1, + amount: 100 + ) - it "returns true" do - expect(call).to eq(true) - end + create( + :student_loans_data, + nino: claim.national_insurance_number, + date_of_birth: claim.date_of_birth, + plan_type_of_deduction: 2, + amount: 200 + ) + end + + context "when the claim is a tslr claim" do + let(:policy_attributes) do + { + policy: Policies::StudentLoans, + eligibility_attributes: {student_loan_repayment_amount: 50} + } + end + + it "combines the data and replaces the existing claim attributes" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to not_change { claim.reload.has_student_loan } + .and change { claim.student_loan_plan }.from(StudentLoan::PLAN_1).to(StudentLoan::PLAN_1_AND_2) + .and change { claim.eligibility.student_loan_repayment_amount }.from(50).to(300) + end + + it "creates an ammendment" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to change { claim.amendments.count }.by(1) + + amendment = claim.amendments.last + + expect(amendment.claim_changes).to eq({ + "student_loan_plan" => [StudentLoan::PLAN_1, StudentLoan::PLAN_1_AND_2], + "student_loan_repayment_amount" => [50, 300] + }) + + expect(amendment.notes).to eq( + "Student loan details updated from SLC data" + ) + + expect(amendment.created_by).to eq(admin) + end + end - context "when the policy is StudentLoans" do - it "updates the claim with the student plan and the repayment total" do - expect { call }.to change { claim.reload.has_student_loan }.to(false) - .and change { claim.student_loan_plan }.to(Claim::NO_STUDENT_LOAN) - .and change { claim.eligibility.student_loan_repayment_amount }.to(0) + context "when the claim is not a tslr claim" do + let(:policy_attributes) do + { + policy: Policies::FurtherEducationPayments + } + end + + it "combines the data and replaces the existing claim attributes" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to not_change { claim.reload.has_student_loan } + .and change { claim.student_loan_plan }.from(StudentLoan::PLAN_1).to(StudentLoan::PLAN_1_AND_2) + end + + it "creates an ammendment" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to change { claim.amendments.count }.by(1) + + amendment = claim.amendments.last + + expect(amendment.claim_changes).to eq({ + "student_loan_plan" => [StudentLoan::PLAN_1, StudentLoan::PLAN_1_AND_2] + }) + + expect(amendment.notes).to eq( + "Student loan details updated from SLC data" + ) + + expect(amendment.created_by).to eq(admin) + end end end - [Policies::EarlyCareerPayments, Policies::LevellingUpPremiumPayments].each do |policy| - context "when the policy is #{policy}" do - let(:policy) { policy } + # This class should only be called with claims that are awaiting the + # either the student_loan_plan or student_loan_amount task. + # StudentLoans::Eligibility#student_loan_repayment_amount is validated + # when an SLC claim is amended. This test is documenting the current + # behaviour, we may want to do something else like return early if + # attempting to amend the `student_loan_repayment_amount` from some + # amount to £0. + context "when no student loan data is found" do + context "when the claim is a tslr claim" do + let(:policy_attributes) do + { + policy: Policies::StudentLoans, + eligibility_attributes: {student_loan_repayment_amount: 100} + } + end + + it "raises an error" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to raise_error(described_class::StudentLoanUpdateError).with_message( + a_string_including( + "Eligibility student loan repayment amount Enter a positive amount up to £5,000.00" + ) + ) + .and not_change { claim.reload.has_student_loan } + .and not_change { claim.student_loan_plan } + .and not_change { claim.eligibility.student_loan_repayment_amount } + end + end + + context "when the claim is not a tslr claim" do + let(:policy_attributes) do + { + policy: Policies::EarlyYearsPayments + } + end - it "updates the claim with the student plan only" do - expect { call }.to change { claim.reload.has_student_loan }.to(false) - .and change { claim.student_loan_plan }.to(Claim::NO_STUDENT_LOAN) + it "resets the student loan attributes" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to change { claim.reload.has_student_loan }.from(true).to(nil) + .and change { claim.student_loan_plan }.from(StudentLoan::PLAN_1).to(nil) + end + + it "creates an ammendment" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to change { claim.amendments.count }.by(1) + + amendment = claim.amendments.last + + expect(amendment.claim_changes).to eq({ + "has_student_loan" => [true, nil], + "student_loan_plan" => [StudentLoan::PLAN_1, nil] + }) + + expect(amendment.notes).to eq( + "Student loan details updated from SLC data" + ) + + expect(amendment.created_by).to eq(admin) end end end end - context "when updating a claim after submission" do - let(:claim) { create(:claim, :submitted, :with_no_student_loan, policy:) } - + context "when creating the amendment fails" do before do - create(:student_loans_data, nino: claim.national_insurance_number, date_of_birth: claim.date_of_birth, plan_type_of_deduction: 1, amount: 50) + create( + :student_loans_data, + nino: claim.national_insurance_number, + date_of_birth: claim.date_of_birth, + plan_type_of_deduction: 2, + amount: 200 + ) end - it "updates the claim with the student plan and the repayment total" do - expect { call }.to change { claim.reload.has_student_loan }.to(true) - .and change { claim.student_loan_plan }.to(StudentLoan::PLAN_1) - .and change { claim.eligibility.student_loan_repayment_amount }.to(50) + let(:claim) do + create( + :claim, + :submitted, + has_student_loan: true, + student_loan_plan: StudentLoan::PLAN_1, + policy: Policies::StudentLoans, + eligibility_attributes: {student_loan_repayment_amount: 50}, + personal_data_removed_at: DateTime.now # make the claim unaamendable + ) end - it "does not change the `submitted_using_slc_data` flag" do - expect { call }.to not_change { claim.submitted_using_slc_data } + it "raises an error and doesn't update the claim" do + expect { described_class.new(claim, admin).update_claim_with_latest_data } + .to raise_error(described_class::StudentLoanUpdateError).with_message( + "Failed to update claim #{claim.id} student loan data. " \ + "amendment_error: \"Claim must be amendable\" " \ + "SLC data: {student_loan_plan: \"plan_2\", eligibility_attributes: {student_loan_repayment_amount: 200.0}}" + ) end end end