Skip to content

Commit

Permalink
Block invalid policy combinations
Browse files Browse the repository at this point in the history
The business rules state that certain policy combinations aren't
permitted, eg if a claimant has made an ECP claim they can't make a
second claim for FE in the same academic year. In such an invalid claim
scenario the requirement is to disable the approve button so admins
can't approve the claim.
  • Loading branch information
rjlynch committed Dec 23, 2024
1 parent 815b7d4 commit 278d3d7
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 14 deletions.
23 changes: 21 additions & 2 deletions app/models/claim.rb
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,12 @@ def approvable?
(!decision_made? || awaiting_qa?) &&
!payment_prevented_by_other_claims? &&
attributes_flagged_by_risk_indicator.none? &&
policy.approvable?(self)
policy.approvable?(self) &&
!precluded_by_previous_claim?
end

def approved?
decision_made? && latest_decision.approved?
end

def rejectable?
Expand All @@ -296,7 +301,7 @@ def holdable?
end

def flaggable_for_qa?
decision_made? && latest_decision.approved? && below_min_qa_threshold? && !awaiting_qa? && !qa_completed?
approved? && below_min_qa_threshold? && !awaiting_qa? && !qa_completed?
end

# This method's intention is to help make a decision on whether a claim should
Expand Down Expand Up @@ -570,4 +575,18 @@ def submittable_mobile_details?
def submittable_email_details?
email_address.present? && email_verified == true
end

def claims_from_same_claimant
@claims_from_same_claimant ||= MatchingAttributeFinder.new(self).matching_claims
end

def precluded_by_previous_claim?
approved_claims = claims_from_same_claimant.select(&:approved?)

other_claimed_policies = approved_claims.map(&:policy)

policies_claimed = (other_claimed_policies + [policy]).uniq

Policies.prohibited_policy_combination?(policies_claimed)
end
end
19 changes: 19 additions & 0 deletions app/models/policies.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,23 @@ def self.constantize(policy)
def self.with_attribute(attr)
POLICIES.select { |policy| policy::Eligibility.has_attribute?(attr) }
end

# Claimants can't claim for these policy combinations in the same academic
# year
INVALID_POLICY_COMBINATIONS = [
[EarlyCareerPayments, EarlyYearsPayments],
[EarlyCareerPayments, FurtherEducationPayments],
[EarlyCareerPayments, LevellingUpPremiumPayments],
[EarlyYearsPayments, FurtherEducationPayments],
[EarlyYearsPayments, LevellingUpPremiumPayments],
[FurtherEducationPayments, LevellingUpPremiumPayments],
[FurtherEducationPayments, StudentLoans],
[FurtherEducationPayments, InternationalRelocationPayments]
]

def self.prohibited_policy_combination?(policies)
policies.combination(2).any? do |policy1, policy2|
INVALID_POLICY_COMBINATIONS.include?([policy1, policy2].sort_by(&:to_s))
end
end
end
9 changes: 9 additions & 0 deletions spec/factories/claims.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
sequence(:email_address) { |n| "person#{n}@example.com" }
sequence(:teacher_reference_number, 1000000) { |n| n }
sequence(:national_insurance_number, 100000) { |n| "QQ#{n}C" }
sequence(:name) { |n| "Name #{n}" }

factory :claim do
started_at { Time.zone.now }
Expand Down Expand Up @@ -67,6 +68,14 @@
national_insurance_number { generate(:national_insurance_number) }
end

trait :random_personal_details do
email_address { generate(:email_address) }
first_name { generate(:name) }
surname { generate(:name) }
date_of_birth { rand(18..65).years.ago.to_date }
national_insurance_number { generate(:national_insurance_number) }
end

trait :eligible do
eligibility_trait { :eligible }
end
Expand Down
17 changes: 10 additions & 7 deletions spec/factories/payments.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@
end

claims do
personal_details = {
national_insurance_number: generate(:national_insurance_number),
email_address: "[email protected]",
bank_sort_code: "220011",
bank_account_number: "12345678",
eligibility_attributes: {teacher_reference_number: generate(:teacher_reference_number)}
}
personal_details = attributes_for(
:claim,
:random_personal_details,
:with_bank_details
).except(:reference).merge(
eligibility_attributes: {
teacher_reference_number: generate(:teacher_reference_number)
}
)
claim_policies.map do |policy|
association(:claim, :approved, personal_details.merge(policy: policy))
end
end

association(:payroll_run, factory: :payroll_run)

award_amount { claims.map(&:award_amount).compact.sum }
Expand Down
8 changes: 4 additions & 4 deletions spec/features/admin/admin_payroll_runs_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
scenario "Service operator creates a payroll run" do
click_on "Payroll"

create(:claim, :approved, policy: Policies::StudentLoans)
create(:claim, :approved, policy: Policies::StudentLoans)
create(:claim, :approved, policy: Policies::EarlyCareerPayments)
create(:claim, :approved, policy: Policies::LevellingUpPremiumPayments)
create(:claim, :approved, :random_personal_details, policy: Policies::StudentLoans)
create(:claim, :approved, :random_personal_details, policy: Policies::StudentLoans)
create(:claim, :approved, :random_personal_details, policy: Policies::EarlyCareerPayments)
create(:claim, :approved, :random_personal_details, policy: Policies::LevellingUpPremiumPayments)

paid_lup_claim = nil
travel_to 2.months.ago do
Expand Down
77 changes: 77 additions & 0 deletions spec/features/admin/claim_with_unpermitted_policy_combo_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require "rails_helper"

RSpec.describe "Claim with unpermitted policy combo" do
context "when one of the claims has been approved" do
it "doesn't allow the admin to approve the other claim" do
create(
:claim,
:approved,
:current_academic_year,
policy: Policies::InternationalRelocationPayments,
email_address: "[email protected]"
)

duplicate_claim = create(
:claim,
:submitted,
:current_academic_year,
policy: Policies::FurtherEducationPayments,
email_address: "[email protected]"
)

sign_in_as_service_operator

visit new_admin_claim_decision_path(duplicate_claim)

approve_option = find("input[type=radio][value=approved]")

expect(approve_option).to be_disabled
end
end

context "when neither of the claims have been approved" do
it "allows the admin to approve one of the claims" do
irp_claim = create(
:claim,
:submitted,
:current_academic_year,
policy: Policies::InternationalRelocationPayments,
email_address: "[email protected]"
)

fe_claim = create(
:claim,
:submitted,
:current_academic_year,
policy: Policies::FurtherEducationPayments,
email_address: "[email protected]"
)

sign_in_as_service_operator

visit new_admin_claim_decision_path(irp_claim)

approve_option = find("input[type=radio][value=approved]")

expect(approve_option).not_to be_disabled

visit new_admin_claim_decision_path(fe_claim)

approve_option = find("input[type=radio][value=approved]")

expect(approve_option).not_to be_disabled

choose "Approve"

fill_in "Decision notes", with: "LGTM"

click_on "Confirm decision"

visit new_admin_claim_decision_path(irp_claim)

approve_option = find("input[type=radio][value=approved]")

expect(approve_option).to be_disabled
end
end
end
2 changes: 1 addition & 1 deletion spec/jobs/payroll_run_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

describe "#perform" do
context "when successful" do
let(:claims) { Policies.all.map { |policy| create(:claim, :approved, policy: policy) } }
let(:claims) { Policies.all.map { |policy| create(:claim, :approved, :random_personal_details, policy: policy) } }
let(:topups) { [] }

before do
Expand Down
48 changes: 48 additions & 0 deletions spec/models/claim_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,54 @@
expect(subject).not_to be_approvable
end
end

context "when the claimant has claimed for a policy that precludes them from this policy" do
subject do
create(
:claim,
:submitted,
:current_academic_year,
policy: Policies::FurtherEducationPayments,
email_address: "[email protected]"
)
end

context "when the other claim has been approved" do
it "is not approvable" do
create(
:claim,
:approved,
:current_academic_year,
policy: Policies::EarlyCareerPayments,
email_address: "[email protected]"
)

expect(subject).not_to be_approvable
end
end

context "when the other claim has not been approved" do
it "is approvable" do
create(
:claim,
:submitted,
:current_academic_year,
policy: Policies::EarlyCareerPayments,
email_address: "[email protected]"
)

create(
:claim,
:rejected,
:current_academic_year,
policy: Policies::EarlyCareerPayments,
email_address: "[email protected]"
)

expect(subject).to be_approvable
end
end
end
end

describe "#rejectable?" do
Expand Down
8 changes: 8 additions & 0 deletions spec/models/policies_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,12 @@
end
end
end

describe "::INVALID_POLICY_COMBINATIONS" do
it "contains subarrays that are sorted alphabetically" do
Policies::INVALID_POLICY_COMBINATIONS.each do |subarray|
expect(subarray).to eq(subarray.sort_by(&:to_s))
end
end
end
end

0 comments on commit 278d3d7

Please sign in to comment.