Skip to content

Commit

Permalink
Merge branch 'refs/heads/main' into CST-2538-update-ab-screens
Browse files Browse the repository at this point in the history
  • Loading branch information
lazydevorg committed May 28, 2024
2 parents eea99bd + c344441 commit 3e9426a
Show file tree
Hide file tree
Showing 18 changed files with 307 additions and 65 deletions.
1 change: 0 additions & 1 deletion app/models/cohort.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ class Cohort < ApplicationRecord
has_paper_trail

NPQ_PLUS_1_YEAR = 2020
OPEN_COHORTS_COUNT = 3

has_many :call_off_contracts
has_many :npq_contracts
Expand Down
19 changes: 8 additions & 11 deletions app/models/participant_profile/ecf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ def self.ransackable_associations(_auth_object = nil)
%w[cohort participant_identity school user teacher_profile induction_records]
end

def self.eligible_to_change_cohort_and_continue_training(in_cohort_start_year:, restrict_to_participant_ids: [])
def self.eligible_to_change_cohort_and_continue_training(restrict_to_participant_ids: [])
billable_states = %w[eligible payable paid].freeze

completed_billable_declarations = ParticipantDeclaration.billable.for_declaration(:completed)
completed_billable_declarations = completed_billable_declarations.where(participant_profile_id: restrict_to_participant_ids) if restrict_to_participant_ids.any?

query = joins(:participant_declarations, schedule: :cohort)
.where(cohorts: { start_year: in_cohort_start_year - Cohort::OPEN_COHORTS_COUNT })
.where.not(cohorts: { payments_frozen_at: nil })
.where("participant_declarations.state IN (?) AND declaration_type != ?", billable_states, "completed")
.where.not(id: completed_billable_declarations.select(:participant_profile_id))
.distinct
Expand All @@ -70,10 +70,7 @@ def self.eligible_to_change_cohort_and_continue_training(in_cohort_start_year:,
query
end

def self.archivable(for_cohort_start_year:, restrict_to_participant_ids: [])
latest_cohort = Cohort.order(start_year: :desc).first
return none unless for_cohort_start_year <= latest_cohort.start_year - Cohort::OPEN_COHORTS_COUNT

def self.archivable(restrict_to_participant_ids: [])
unbillable_states = %i[ineligible voided submitted].freeze

# Find all participants that have no FIP induction records (as finding those with only FIP is more complicated).
Expand All @@ -83,7 +80,7 @@ def self.archivable(for_cohort_start_year:, restrict_to_participant_ids: [])
query = left_joins(:participant_declarations, schedule: :cohort)
# Exclude participants that have any induction records that are not FIP.
.where.not(id: not_fip_induction_records.select(:participant_profile_id))
.where(cohorts: { start_year: for_cohort_start_year })
.where.not(cohorts: { payments_frozen_at: nil })
# Exclude participants that have any billable/submitted declarations, but
# retain participants that have no declarations at all.
.where.not("participant_declarations.id IS NOT NULL AND participant_declarations.state NOT IN (?)", unbillable_states)
Expand All @@ -95,12 +92,12 @@ def self.archivable(for_cohort_start_year:, restrict_to_participant_ids: [])
end

# Instance Methods
def eligible_to_change_cohort_and_continue_training?(in_cohort_start_year:)
self.class.eligible_to_change_cohort_and_continue_training(in_cohort_start_year:, restrict_to_participant_ids: [id]).exists?
def eligible_to_change_cohort_and_continue_training?
self.class.eligible_to_change_cohort_and_continue_training(restrict_to_participant_ids: [id]).exists?
end

def archivable?(for_cohort_start_year:)
self.class.archivable(for_cohort_start_year:, restrict_to_participant_ids: [id]).exists?
def archivable?
self.class.archivable(restrict_to_participant_ids: [id]).exists?
end

def completed_induction?
Expand Down
12 changes: 5 additions & 7 deletions app/models/participant_profile/ect.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ class ParticipantProfile::ECT < ParticipantProfile::ECF
where(induction_start_date: nil).joins(:ecf_participant_eligibility).merge(ECFParticipantEligibility.waiting_for_induction)
}

def self.archivable(for_cohort_start_year:, restrict_to_participant_ids: [])
latest_induction_start_date = Date.new(for_cohort_start_year, 9, 1)

super(for_cohort_start_year:, restrict_to_participant_ids:)
def self.archivable(restrict_to_participant_ids: [])
super(restrict_to_participant_ids:)
.where(induction_completion_date: nil)
.where("induction_start_date IS NULL OR induction_start_date < ?", latest_induction_start_date)
.where("induction_start_date IS NULL OR induction_start_date < make_date(cohorts.start_year, 9, 1)")
end

def ect?
Expand All @@ -30,7 +28,7 @@ def role
"Early career teacher"
end

def self.eligible_to_change_cohort_and_continue_training(in_cohort_start_year:, restrict_to_participant_ids:)
super(in_cohort_start_year:, restrict_to_participant_ids:).where(induction_completion_date: nil)
def self.eligible_to_change_cohort_and_continue_training(restrict_to_participant_ids: [])
super(restrict_to_participant_ids:).where(induction_completion_date: nil)
end
end
8 changes: 4 additions & 4 deletions app/models/participant_profile/mentor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ class ParticipantProfile::Mentor < ParticipantProfile::ECF
started_not_completed: "started_not_completed",
}

def self.archivable(for_cohort_start_year:, restrict_to_participant_ids: [])
super(for_cohort_start_year:, restrict_to_participant_ids:)
def self.archivable(restrict_to_participant_ids: [])
super(restrict_to_participant_ids:)
.where(mentor_completion_date: nil)
.where.not(id: InductionRecord.where.not(mentor_profile_id: nil).select(:mentor_profile_id).distinct)
end
Expand All @@ -49,7 +49,7 @@ def role
"Mentor"
end

def self.eligible_to_change_cohort_and_continue_training(in_cohort_start_year:, restrict_to_participant_ids:)
super(in_cohort_start_year:, restrict_to_participant_ids:).where(mentor_completion_date: nil)
def self.eligible_to_change_cohort_and_continue_training(restrict_to_participant_ids: [])
super(restrict_to_participant_ids:).where(mentor_completion_date: nil)
end
end
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
8 changes: 4 additions & 4 deletions spec/models/participant_profile/ect_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ def create_profile(attrs = {})
end

describe ".archivable" do
subject { described_class.archivable(for_cohort_start_year:) }
subject { described_class.archivable }

it "does not include participants where the induction_start_date is 1/9/<for_cohort_start_year> or later" do
build_profile(cohort: eligible_cohort, induction_start_date: Date.new(for_cohort_start_year, 9, 1))
build_profile(cohort: eligible_cohort, induction_start_date: Date.new(for_cohort_start_year + 1, 3, 1))
it "does not include participants where the induction_start_date is 1/9/<cohort_start_year> or later" do
build_profile(cohort: eligible_cohort, induction_start_date: Date.new(eligible_cohort.start_year, 9, 1))
build_profile(cohort: eligible_cohort, induction_start_date: Date.new(eligible_cohort.start_year + 1, 3, 1))

eligible_participant = build_profile(cohort: eligible_cohort)

Expand Down
2 changes: 1 addition & 1 deletion spec/models/participant_profile/mentor_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def create_profile(attrs = {})
end

describe ".archivable" do
subject { described_class.archivable(for_cohort_start_year:) }
subject { described_class.archivable }

it "does not include participants that have mentees" do
build_profile(cohort: eligible_cohort).tap do |mentor_profile|
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
Loading

0 comments on commit 3e9426a

Please sign in to comment.