Skip to content

Commit

Permalink
Merge pull request #4762 from DFE-Digital/CPDLP-2951-npq-capping-add-…
Browse files Browse the repository at this point in the history
…validations-logic-for-moving-npq-applications-forward-from-non-funded-cap-cohorts

Defaults funded_place attribute when changing cohorts
  • Loading branch information
pmanrubia authored May 28, 2024
2 parents 08401f0 + 6a83004 commit 23eba39
Show file tree
Hide file tree
Showing 10 changed files with 260 additions and 13 deletions.
35 changes: 26 additions & 9 deletions app/serializers/api/v1/npq_participant_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@ class NPQParticipantSerializer
attribute(:participant_id, &:id)

attribute(:npq_courses) do |object, params|
scope = object.npq_profiles
scope = scope.includes(npq_application: [:npq_course])
npq_profiles = npq_profiles(object, params)

if params[:cpd_lead_provider]
scope = scope.joins(npq_application: { npq_lead_provider: [:cpd_lead_provider] })
scope = scope.where(npq_applications: { npq_lead_providers: { cpd_lead_provider: params[:cpd_lead_provider] } })
else
scope = ParticipantProfile::NPQ.none
end
npq_profiles.map { |npq_profile| npq_profile.npq_application.npq_course.identifier }
end

scope.map { |npq_profile| npq_profile.npq_application.npq_course.identifier }
attribute :funded_places, if: -> { FeatureFlag.active?(:npq_capping) } do |object, params|
npq_profiles = npq_profiles(object, params)

npq_profiles.map do |npq_profile|
{
"npq_course": npq_profile.npq_application.npq_course.identifier,
"funded_place:": npq_profile.npq_application.funded_place,
"npq_application_id": npq_profile.npq_application.id,
}
end
end

attribute(:teacher_reference_number) do |object|
Expand All @@ -38,6 +42,19 @@ class NPQParticipantSerializer
attribute(:updated_at) do |object|
object.updated_at.rfc3339
end

def self.npq_profiles(object, params)
scope = object.npq_profiles
scope = scope.includes(npq_application: [:npq_course])

if params[:cpd_lead_provider]
scope = scope.joins(npq_application: { npq_lead_provider: [:cpd_lead_provider] })
scope = scope.where(npq_applications: { npq_lead_providers: { cpd_lead_provider: params[:cpd_lead_provider] } })
else
scope = ParticipantProfile::NPQ.none
end
scope
end
end
end
end
4 changes: 3 additions & 1 deletion app/serializers/api/v2/npq_participant_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ class NPQParticipantSerializer
training_status: profile.training_status,
school_urn: profile.school_urn,
targeted_delivery_funding_eligibility: profile.npq_application.targeted_delivery_funding_eligibility,
}
}.tap do |hash|
hash.merge!(funded_place: profile.npq_application.funded_place) if FeatureFlag.active?("npq_capping")
end
end
end
end
Expand Down
4 changes: 3 additions & 1 deletion app/serializers/api/v3/npq_participant_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ def deferral(profile:, cpd_lead_provider:)
withdrawal: withdrawal(profile:, cpd_lead_provider: params[:cpd_lead_provider]),
deferral: deferral(profile:, cpd_lead_provider: params[:cpd_lead_provider]),
created_at: profile.created_at.rfc3339,
}
}.tap do |hash|
hash.merge!(funded_place: profile.npq_application.funded_place) if FeatureFlag.active?("npq_capping")
end
}.compact
end

Expand Down
26 changes: 26 additions & 0 deletions app/services/change_schedule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,35 @@ def update_schedule!
def update_npq_application_cohort!
return unless participant_profile.npq_application.cohort != new_schedule.cohort

update_funded_place!
participant_profile.npq_application.update!(cohort: new_schedule.cohort)
end

def update_funded_place!
return unless FeatureFlag.active?(:npq_capping)
return if npq_contract.funding_cap&.positive? && target_npq_contract.funding_cap&.positive?
return unless target_npq_contract.funding_cap&.positive?

application = participant_profile.npq_application
application.update!(funded_place: application.eligible_for_funding)
end

def target_npq_contract
NPQContract.find_latest_by(
npq_lead_provider: participant_profile.npq_application.npq_lead_provider,
npq_course: participant_profile.npq_application.npq_course,
cohort:,
)
end

def npq_contract
NPQContract.find_latest_by(
npq_lead_provider: participant_profile.npq_application.npq_lead_provider,
npq_course: participant_profile.npq_application.npq_course,
cohort: participant_profile.npq_application.cohort,
)
end

def update_npq_records!
return unless participant_profile&.npq?

Expand Down
2 changes: 1 addition & 1 deletion spec/factories/services/npq/npq_application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@

trait :accepted do
after :create do |npq_application|
NPQ::Application::Accept.new(npq_application:).call
NPQ::Application::Accept.new(npq_application:, funded_place: npq_application.funded_place).call
npq_application.reload
end
end
Expand Down
30 changes: 30 additions & 0 deletions spec/serializers/api/v1/npq_participant_serializer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,39 @@ module V1

it "does not leak course info when given a provider param" do
result = NPQParticipantSerializer.new(participant, params: { cpd_lead_provider: provider_one.cpd_lead_provider }).serializable_hash

expect(result[:data][:attributes][:npq_courses]).to eq %w[npq-headship]
end

describe "funded places" do
context "when feature flag `npq_capping` is inactive" do
before { FeatureFlag.deactivate(:npq_capping) }

it "does not return the funded places" do
result = NPQParticipantSerializer.new(participant, params: { cpd_lead_provider: provider_one.cpd_lead_provider }).serializable_hash

expect(result[:data][:attributes]).not_to include(:funded_places)
end
end

context "when feature flag `npq_capping` is active" do
before { FeatureFlag.activate(:npq_capping) }

it "returns the funded places" do
application_one.update!(funded_place: true)
result = NPQParticipantSerializer.new(participant, params: { cpd_lead_provider: provider_one.cpd_lead_provider }).serializable_hash

expect(result[:data][:attributes][:funded_places]).to eq [
{
"npq_course": "npq-headship",
"funded_place:": true,
"npq_application_id": application_one.id,
},
]
end
end
end

it "does not leak course info when given no provider param" do
result = NPQParticipantSerializer.new(participant).serializable_hash
expect(result[:data][:attributes][:npq_courses]).to eq []
Expand Down
45 changes: 45 additions & 0 deletions spec/serializers/api/v2/npq_participant_serializer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,51 @@ module V2
expect(result[:data][:attributes][:npq_enrolments][0][:targeted_delivery_funding_eligibility]).to eql(profile.npq_application.targeted_delivery_funding_eligibility)
end

describe "funded place attribute" do
context "when feature flag `npq_capping` is disabled" do
before { FeatureFlag.deactivate(:npq_capping) }

it "does not include the `funded_place` attribute" do
result = NPQParticipantSerializer.new(user).serializable_hash

expect(result[:data][:attributes][:npq_enrolments][0].keys).not_to include(:funded_place)
end
end

context "when feature flag `npq_capping` is enabled" do
let(:npq_course) { create(:npq_leadership_course, identifier: "npq-senior-leadership") }
let(:npq_lead_provider) { create(:npq_lead_provider) }
let(:statement) do
create(
:npq_statement,
:next_output_fee,
cpd_lead_provider: npq_lead_provider.cpd_lead_provider,
cohort: profile.npq_application.cohort,
)
end
let(:funding_cap) { 10 }
let!(:npq_contract) do
create(
:npq_contract,
npq_lead_provider:,
cohort: statement.cohort,
course_identifier: npq_course.identifier,
version: statement.contract_version,
funding_cap:,
)
end

before { FeatureFlag.activate(:npq_capping) }

it "includes the `funding_cap` attribute" do
profile.npq_application.update!(funded_place: true)
result = NPQParticipantSerializer.new(user).serializable_hash

expect(result[:data][:attributes][:npq_enrolments][0][:funded_place]).to be_truthy
end
end
end

context "when there are multiple providers involved" do
let(:second_profile) { create(:npq_participant_profile, user:) }

Expand Down
45 changes: 45 additions & 0 deletions spec/serializers/api/v3/npq_participant_serializer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,51 @@ module V3
])
end

describe "funded place attribute" do
context "when feature flag `npq_capping` is disabled" do
before { FeatureFlag.deactivate(:npq_capping) }

it "does not include the `funded_place` attribute" do
result = subject.serializable_hash

puts result
expect(result[:data][0][:attributes][:npq_enrolments][0].keys).not_to include(:funded_place)
end
end

context "when feature flag `npq_capping` is enabled" do
let(:npq_course) { create(:npq_leadership_course, identifier: "npq-senior-leadership") }
let(:statement) do
create(
:npq_statement,
:next_output_fee,
cpd_lead_provider: npq_lead_provider.cpd_lead_provider,
cohort: profile.npq_application.cohort,
)
end
let(:funding_cap) { 10 }
let!(:npq_contract) do
create(
:npq_contract,
npq_lead_provider:,
cohort: statement.cohort,
course_identifier: npq_course.identifier,
version: statement.contract_version,
funding_cap:,
)
end

before { FeatureFlag.activate(:npq_capping) }

it "includes the `funding_cap` attribute" do
profile.npq_application.update!(funded_place: true)
result = subject.serializable_hash

expect(result[:data][0][:attributes][:npq_enrolments][0][:funded_place]).to be_truthy
end
end
end

describe "npq_enrolments" do
context "when there are multiple providers involved" do
let(:another_cpd_lead_provider) { create(:cpd_lead_provider, :with_npq_lead_provider) }
Expand Down
81 changes: 81 additions & 0 deletions spec/services/change_schedule_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,87 @@
service.call
expect(participant_profile.npq_application.cohort).to eq(new_cohort)
end

context "when moving from a previous cohort" do
let!(:statement) do
create(
:npq_statement,
:next_output_fee,
cpd_lead_provider: npq_lead_provider.cpd_lead_provider,
)
end

let!(:new_statement) do
create(
:npq_statement,
:next_output_fee,
cpd_lead_provider: npq_lead_provider.cpd_lead_provider,
cohort: new_cohort,
)
end

it "updates the cohort on the npq application" do
service.call
expect(participant_profile.npq_application.cohort).to eq(new_cohort)
end

context "when moving from funding cohort to funding cohort" do
before { FeatureFlag.activate(:npq_capping) }

let(:participant_profile) { create(:npq_participant_profile, npq_lead_provider:, npq_course:, schedule:, user:, npq_application:) }
let(:npq_application) { create(:npq_application, :accepted, npq_lead_provider:, npq_course:, user:, funded_place: false) }

before do
npq_contract.update!(funding_cap: 10)
npq_contract_new_cohort.update!(funding_cap: 10)
end

it "does not change funding place if original contract has a funded place" do
participant_profile.npq_application.update!(funded_place: false, eligible_for_funding: true)

service.call

expect(participant_profile.reload.npq_application.reload.funded_place).to be_falsey
end
end

context "when moving from non funding cohort to funding cohort" do
before do
npq_contract.update!(funding_cap: 0)
npq_contract_new_cohort.update!(funding_cap: 10)
end

context "when feature flag `npq_capping` is disabled" do
before { FeatureFlag.deactivate(:npq_capping) }

it "does not change funding place" do
expect { service.call }.not_to change { participant_profile.reload.npq_application.reload.funded_place }
end
end

context "when feature flag `npq_capping` is enabled" do
before do
FeatureFlag.activate(:npq_capping)
end

it "sets funding place to `true` if `eligible_for_funding` is true" do
participant_profile.npq_application.update!(eligible_for_funding: true, funded_place: nil)

service.call

expect(participant_profile.reload.npq_application.reload.funded_place).to be_truthy
end

it "sets funding place to `false` if `eligible_for_funding` is false" do
participant_profile.npq_application.update!(eligible_for_funding: false, funded_place: nil)

service.call

expect(participant_profile.reload.npq_application.reload.funded_place).to be_falsey
end
end
end
end
end
end
end
Expand Down
1 change: 0 additions & 1 deletion spec/services/npq/application/change_funded_place_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@

it "is invalid if funded_place is `nil`" do
service.call

expect(service.errors.messages_for(:npq_application)).to include("The entered '#/funded_place' is missing from your request. Check details and try again.")
end
end
Expand Down

0 comments on commit 23eba39

Please sign in to comment.