Skip to content

Commit

Permalink
Merge pull request #5239 from DFE-Digital/CST-2578-closing-2021-archi…
Browse files Browse the repository at this point in the history
…ve-2021-participants-that-didnt-start-training

Bug fix: do not archive participants in a closing cohort with archiva…
  • Loading branch information
ltello authored Oct 17, 2024
2 parents f5381c8 + 49239aa commit 17736ba
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 30 deletions.
6 changes: 6 additions & 0 deletions app/models/participant_declaration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
class ParticipantDeclaration < ApplicationRecord
self.ignored_columns = %w[statement_type statement_id voided_at]

ARCHIVABLE_STATES = %w[ineligible voided submitted].freeze

belongs_to :cpd_lead_provider
belongs_to :user
belongs_to :cohort
Expand Down Expand Up @@ -112,6 +114,10 @@ class ParticipantDeclaration < ApplicationRecord

before_create :build_initial_declaration_state

def self.non_archivable_states
states.keys.excluding(ARCHIVABLE_STATES)
end

def voidable?
%w[submitted eligible payable ineligible].include?(state)
end
Expand Down
21 changes: 0 additions & 21 deletions app/models/participant_profile/ecf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,27 +80,6 @@ def eligible_to_change_cohort_back_to_their_payments_frozen_original?(cohort:, c
participant_declarations.billable.where(cohort:).exists?
end

def self.archivable_from_frozen_cohort(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).
not_fip_induction_records = InductionRecord.left_joins(:induction_programme).where.not(induction_programme: { training_programme: :full_induction_programme })
not_fip_induction_records = not_fip_induction_records.where(participant_profile_id: restrict_to_participant_ids) if restrict_to_participant_ids.any?

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.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)
.distinct

query = query.where(id: restrict_to_participant_ids) if restrict_to_participant_ids.any?

query
end

# Instance Methods
def eligible_to_change_cohort_and_continue_training?(cohort:)
self.class.eligible_to_change_cohort_and_continue_training(cohort:, restrict_to_participant_ids: [id]).exists?
Expand Down
33 changes: 30 additions & 3 deletions app/models/participant_profile/ect.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,36 @@ class ParticipantProfile::ECT < ParticipantProfile::ECF
}

def self.archivable_from_frozen_cohort(restrict_to_participant_ids: [])
super(restrict_to_participant_ids:)
.where(induction_completion_date: nil)
.where("induction_start_date IS NULL OR induction_start_date < make_date(cohorts.start_year, 9, 1)")
# ECTs that have no FIP induction records
not_fip = InductionRecord.joins(:induction_programme, participant_profile: { schedule: :cohort })
.where.not(cohorts: { payments_frozen_at: nil })
.where(participant_profiles: { induction_completion_date: nil })
.where(participant_profiles: { type: "ParticipantProfile::ECT" })
.where("participant_profiles.induction_start_date IS NULL OR participant_profiles.induction_start_date < make_date(cohorts.start_year, 9, 1)")
.where.not(induction_programme: { training_programme: :full_induction_programme })
not_fip = not_fip.where(participant_profile_id: restrict_to_participant_ids) if restrict_to_participant_ids.any?
not_fip_ids = not_fip.pluck(:participant_profile_id).uniq

# ECTs that have at least one non-archivable declaration
with_unarchivable_declaration = joins(:participant_declarations, schedule: :cohort)
.where.not(cohorts: { payments_frozen_at: nil })
.where(induction_completion_date: nil)
.where("induction_start_date IS NULL OR induction_start_date < make_date(cohorts.start_year, 9, 1)")
.where.not(id: not_fip_ids)
.where(participant_declarations: { state: ParticipantDeclaration.non_archivable_states })
.distinct
with_unarchivable_declaration = with_unarchivable_declaration.where(id: restrict_to_participant_ids) if restrict_to_participant_ids.any?
with_unarchivable_declaration_ids = with_unarchivable_declaration.pluck(:id)

# ECTs in a payments-frozen cohort with no induction start date or prior to Sept 2021 excluding the ones above
query = joins(schedule: :cohort)
.where.not(cohorts: { payments_frozen_at: nil })
.where(induction_completion_date: nil)
.where("induction_start_date IS NULL OR induction_start_date < make_date(cohorts.start_year, 9, 1)")
.where.not(id: not_fip_ids + with_unarchivable_declaration_ids)
query = query.where(id: restrict_to_participant_ids) if restrict_to_participant_ids.any?

query
end

def completed_training?
Expand Down
39 changes: 36 additions & 3 deletions app/models/participant_profile/mentor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,42 @@ class ParticipantProfile::Mentor < ParticipantProfile::ECF
}

def self.archivable_from_frozen_cohort(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)
# Mentors that have no FIP induction records
not_fip = InductionRecord.joins(:induction_programme, participant_profile: { schedule: :cohort })
.where.not(cohorts: { payments_frozen_at: nil })
.where(participant_profiles: { mentor_completion_date: nil })
.where(participant_profiles: { type: "ParticipantProfile::Mentor" })
.where.not(induction_programme: { training_programme: :full_induction_programme })
not_fip = not_fip.where(participant_profile_id: restrict_to_participant_ids) if restrict_to_participant_ids.any?
not_fip_ids = not_fip.pluck(:participant_profile_id).uniq

# Mentors that have at least one non-archivable declaration
with_unarchivable_declaration = joins(:participant_declarations, schedule: :cohort)
.where.not(cohorts: { payments_frozen_at: nil })
.where(mentor_completion_date: nil)
.where.not(id: not_fip_ids)
.where(participant_declarations: { state: ParticipantDeclaration.non_archivable_states })
.distinct
with_unarchivable_declaration = with_unarchivable_declaration.where(id: restrict_to_participant_ids) if restrict_to_participant_ids.any?
with_unarchivable_declaration_ids = with_unarchivable_declaration.pluck(:id)

# Mentors ever assigned an ECT
with_mentee = InductionRecord.joins(:induction_programme, mentor_profile: { schedule: :cohort })
.where.not(mentor_profile_id: nil)
.where.not(mentor_profile_id: not_fip_ids + with_unarchivable_declaration_ids)
.where.not(cohorts: { payments_frozen_at: nil })
.where(participant_profiles: { mentor_completion_date: nil })
with_mentee = with_mentee.where(mentor_profile_id: restrict_to_participant_ids) if restrict_to_participant_ids.any?
with_mentee_ids = with_mentee.pluck(:mentor_profile_id).uniq

# Mentors in a payments frozen cohort with no completion date excluding the ones above
query = joins(schedule: :cohort)
.where.not(cohorts: { payments_frozen_at: nil })
.where(mentor_completion_date: nil)
.where.not(id: not_fip_ids + with_unarchivable_declaration_ids + with_mentee_ids)
query = query.where(id: restrict_to_participant_ids) if restrict_to_participant_ids.any?

query
end

def complete_training!(completion_date:, completion_reason:)
Expand Down
18 changes: 15 additions & 3 deletions spec/support/shared_examples/archivable_support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,21 @@ def build_declaration(attrs = {})
expect(participant_profile).not_to be_archivable_from_frozen_cohort
end

it "returns false if the participant has billable declarations" do
participant_profile = build_declaration(state: :paid, cohort: eligible_cohort).participant_profile
expect(participant_profile).not_to be_archivable_from_frozen_cohort
ParticipantDeclaration.non_archivable_states.each do |declaration_state|
it "returns false if the participant has #{declaration_state} declarations" do
paid_declaration = build_declaration(state: declaration_state, cohort: eligible_cohort)
participant_profile = paid_declaration.participant_profile
declaration_date = participant_profile.schedule.milestones.find_by(declaration_type: "retained-2").milestone_date - 1.day
travel_to declaration_date do
build_declaration(state: :voided,
cohort: eligible_cohort,
participant_profile:,
cpd_lead_provider: paid_declaration.cpd_lead_provider,
declaration_date:,
declaration_type: "retained-2")
expect(participant_profile).not_to be_archivable_from_frozen_cohort
end
end
end

it "returns false if the participant has a CIP induction record" do
Expand Down

0 comments on commit 17736ba

Please sign in to comment.