-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
412 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
### | ||
# Copyright 2016 - 2025 Green River Data Analysis, LLC | ||
# | ||
# License detail: https://github.com/greenriver/hmis-warehouse/blob/production/LICENSE.md | ||
### | ||
|
||
module Mutations | ||
class SplitHousehold < BaseMutation | ||
argument :splitting_enrollment_inputs, [Types::HmisSchema::EnrollmentRelationshipInput], required: true | ||
|
||
field :new_household, Types::HmisSchema::Household, null: false | ||
field :remaining_household, Types::HmisSchema::Household, null: false | ||
|
||
def resolve(splitting_enrollment_inputs:) | ||
map_enrollment_id_to_relationship = splitting_enrollment_inputs.map { |e| [e.enrollment_id, e.relationship_to_hoh] }.to_h | ||
|
||
splitting_enrollment_ids = map_enrollment_id_to_relationship.keys | ||
splitting_enrollments = Hmis::Hud::Enrollment. | ||
where(id: splitting_enrollment_ids). | ||
viewable_by(current_user). | ||
includes(:household, :project) | ||
access_denied! unless splitting_enrollments.count == splitting_enrollment_inputs.count | ||
|
||
project = splitting_enrollments.map(&:project).uniq.sole | ||
access_denied! unless current_permission?(permission: :can_split_households, entity: project) | ||
|
||
donor_household = splitting_enrollments.map(&:household).uniq.sole | ||
remaining_enrollments = Hmis::Hud::Enrollment. | ||
where(household_id: donor_household.household_id). | ||
where.not(id: splitting_enrollment_ids) | ||
remaining_hoh = remaining_enrollments.any? { |enrollment| enrollment.relationship_to_hoh == 1 } | ||
|
||
raise 'Splitting all clients to a new household is invalid' if remaining_enrollments.empty? | ||
raise 'This operation would leave behind a household with no HoH, which is not allowed' unless remaining_hoh | ||
|
||
donor_before_state = Hmis::Hud::Enrollment.snapshot_enrollments([*splitting_enrollments, *remaining_enrollments]) | ||
new_household_id = Hmis::Hud::Base.generate_uuid | ||
|
||
Hmis::Hud::Enrollment.transaction do | ||
splitting_enrollments.each do |enrollment| | ||
enrollment.update!( | ||
household_id: new_household_id, | ||
relationship_to_hoh: map_enrollment_id_to_relationship[enrollment.id.to_s], | ||
) | ||
|
||
enrollment.active_unit_occupancy&.assign_attributes(occupancy_period_attributes: { end_date: Date.current }) | ||
|
||
enrollment.save! | ||
end | ||
|
||
donor_household.reload | ||
|
||
event = Hmis::HouseholdEvent.new | ||
event.user = current_user | ||
event.household = donor_household | ||
event.event_type = Hmis::HouseholdEvent::SPLIT | ||
event.event_details = { | ||
'receivingHouseholdId': new_household_id, | ||
'before': donor_before_state, | ||
'after': Hmis::Hud::Enrollment.snapshot_enrollments(remaining_enrollments), | ||
} | ||
event.save! | ||
|
||
remaining_enrollments.invalidate_processing! | ||
end | ||
|
||
enrollment = splitting_enrollments.first | ||
enrollment.reload | ||
|
||
{ | ||
new_household: enrollment.household, | ||
remaining_household: donor_household, | ||
} | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
182 changes: 182 additions & 0 deletions
182
drivers/hmis/spec/requests/hmis/split_household_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
### | ||
# Copyright 2016 - 2025 Green River Data Analysis, LLC | ||
# | ||
# License detail: https://github.com/greenriver/hmis-warehouse/blob/production/LICENSE.md | ||
### | ||
|
||
require 'rails_helper' | ||
require_relative 'login_and_permissions' | ||
require_relative '../../support/hmis_base_setup' | ||
|
||
RSpec.describe Hmis::GraphqlController, type: :request do | ||
include_context 'hmis base setup' | ||
let!(:access_control) { create_access_control(hmis_user, ds1) } | ||
|
||
let(:mutation) do | ||
<<~GRAPHQL | ||
mutation SplitHousehold($input: SplitHouseholdInput!) { | ||
splitHousehold(input: $input) { | ||
newHousehold { | ||
id | ||
householdSize | ||
householdClients { | ||
client { | ||
id | ||
} | ||
enrollment { | ||
id | ||
} | ||
} | ||
} | ||
remainingHousehold { | ||
id | ||
householdSize | ||
} | ||
} | ||
} | ||
GRAPHQL | ||
end | ||
|
||
let!(:donor_household_id) { Hmis::Hud::Base.generate_uuid } | ||
let!(:remaining) { create :hmis_hud_enrollment, data_source: ds1, project: p1, entry_date: 2.weeks.ago, household_id: donor_household_id } | ||
let!(:new_hoh) { create :hmis_hud_enrollment, data_source: ds1, project: p1, entry_date: 2.weeks.ago, relationship_to_hoh: 3, household_id: donor_household_id } | ||
let!(:child) { create :hmis_hud_enrollment, data_source: ds1, project: p1, entry_date: 2.weeks.ago, relationship_to_hoh: 2, household_id: donor_household_id } | ||
|
||
before(:each) do | ||
hmis_login(user) | ||
remaining.update!(processed_as: 'PROCESSED', processed_hash: 'PROCESSED') | ||
new_hoh.update!(processed_as: 'PROCESSED', processed_hash: 'PROCESSED') | ||
child.update!(processed_as: 'PROCESSED', processed_hash: 'PROCESSED') | ||
Delayed::Job.jobs_for_class('GrdaWarehouse::Tasks::ServiceHistory::Enrollment').delete_all | ||
end | ||
|
||
def perform_mutation( | ||
splitting_enrollment_inputs = [ | ||
{ | ||
enrollment_id: new_hoh.id, | ||
relationship_to_hoh: 'SELF_HEAD_OF_HOUSEHOLD', | ||
}, | ||
{ | ||
enrollment_id: child.id, | ||
relationship_to_hoh: 'CHILD', | ||
}, | ||
] | ||
) | ||
input = { | ||
input: { | ||
splitting_enrollment_inputs: splitting_enrollment_inputs, | ||
}, | ||
} | ||
response, result = post_graphql(input) { mutation } | ||
|
||
expect(response.status).to eq(200), result.inspect | ||
result = result.dig('data', 'splitHousehold') | ||
return result['newHousehold'], result['remainingHousehold'] | ||
end | ||
|
||
it 'should successfully split households' do | ||
expect do | ||
new_household, remaining_household = perform_mutation | ||
expect(new_household.dig('householdSize')).to eq(2) | ||
expect(remaining_household.dig('id')).to eq(donor_household_id) | ||
expect(remaining_household.dig('householdSize')).to eq(1) | ||
remaining.reload | ||
new_hoh.reload | ||
child.reload | ||
end.to change(new_hoh, :household_id). | ||
and change(new_hoh, :processed_as).from('PROCESSED').to(nil). | ||
and change(child, :household_id). | ||
and change(child, :processed_as).from('PROCESSED').to(nil). | ||
and not_change(remaining, :household_id). | ||
and change(remaining, :processed_as).from('PROCESSED').to(nil). # Triggers reprocessing for remaining hh even though no fields have changed | ||
and change(Delayed::Job.jobs_for_class('GrdaWarehouse::Tasks::ServiceHistory::Enrollment'), :count).by(1) | ||
|
||
expect(new_hoh.household_id).to eq(child.household_id) | ||
expect(new_hoh.relationship_to_hoh).to eq(1) # Self | ||
expect(child.relationship_to_hoh).to eq(2) # Child | ||
|
||
split_event = remaining.household.events.sole | ||
expect(split_event.event_type).to eq('split') | ||
dets = split_event.event_details | ||
expect(dets['receivingHouseholdId']).to eq(new_hoh.household_id) | ||
expect(dets['before'].map { |enrollment_snap| enrollment_snap['enrollmentId'] }).to contain_exactly(remaining.id, new_hoh.id, child.id) | ||
expect(dets['after'].map { |enrollment_snap| enrollment_snap['enrollmentId'] }).to contain_exactly(remaining.id) | ||
end | ||
|
||
it 'fails when the user does not have can_split_households permission' do | ||
remove_permissions(access_control, :can_split_households) | ||
input = { | ||
splitting_enrollment_inputs: [ | ||
{ | ||
enrollment_id: new_hoh.id, | ||
relationship_to_hoh: 'SELF_HEAD_OF_HOUSEHOLD', | ||
}, | ||
], | ||
} | ||
expect_access_denied post_graphql(input: input) { mutation } | ||
end | ||
|
||
it 'fails when the given enrollment IDs are invalid' do | ||
input = { | ||
splitting_enrollment_inputs: [ | ||
{ | ||
enrollment_id: 'fake-enrollment', | ||
relationship_to_hoh: 'SELF_HEAD_OF_HOUSEHOLD', | ||
}, | ||
], | ||
} | ||
expect_access_denied post_graphql(input: input) { mutation } | ||
end | ||
|
||
context 'when the given enrollment IDs come from different households' do | ||
let!(:child) { create :hmis_hud_enrollment, data_source: ds1, project: p1, entry_date: 2.weeks.ago, relationship_to_hoh: 2 } | ||
|
||
it 'fails to process' do | ||
input = { | ||
splitting_enrollment_inputs: [ | ||
{ | ||
enrollment_id: new_hoh.id, | ||
relationship_to_hoh: 'SELF_HEAD_OF_HOUSEHOLD', | ||
}, | ||
{ | ||
enrollment_id: child.id, | ||
relationship_to_hoh: 'CHILD', | ||
}, | ||
], | ||
} | ||
expect_gql_error post_graphql(input: input) { mutation } | ||
end | ||
end | ||
|
||
it 'fails when the split would not leave behind any users' do | ||
input = { | ||
splitting_enrollment_inputs: [ | ||
{ | ||
enrollment_id: remaining.id, | ||
relationship_to_hoh: 'SELF_HEAD_OF_HOUSEHOLD', | ||
}, | ||
{ | ||
enrollment_id: new_hoh.id, | ||
relationship_to_hoh: 'SPOUSE_OR_PARTNER', | ||
}, | ||
{ | ||
enrollment_id: child.id, | ||
relationship_to_hoh: 'CHILD', | ||
}, | ||
], | ||
} | ||
expect_gql_error post_graphql(input: input) { mutation }, message: /Splitting all clients to a new household is invalid/ | ||
end | ||
|
||
it 'fails when the split would leave behind a headless household' do | ||
input = { | ||
splitting_enrollment_inputs: [ | ||
{ | ||
enrollment_id: remaining.id, | ||
relationship_to_hoh: 'SELF_HEAD_OF_HOUSEHOLD', | ||
}, | ||
], | ||
} | ||
expect_gql_error post_graphql(input: input) { mutation }, message: /This operation would leave behind a household with no HoH, which is not allowed/ | ||
end | ||
end |
Oops, something went wrong.