Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure vacols location isn't set incorrectly when user requests disposition change #15082

Merged
merged 8 commits into from
Sep 9, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/models/tasks/hearing_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ def verify_org_task_unique
def when_child_task_completed(child_task)
super

# do not move forward to change location or create ihp if there are
# other open hearing tasks
return unless appeal.tasks.open.where(type: HearingTask.name).empty?

if appeal.is_a?(LegacyAppeal)
Expand Down
39 changes: 31 additions & 8 deletions app/models/tasks/schedule_hearing_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ class ScheduleHearingTask < Task
before_create :create_parent_hearing_task
delegate :hearing, to: :parent, allow_nil: true

# error to capture any instances where expect the parent HearingTask to have no
# open children tasks but it does
class HearingTaskHasOpenChildren < StandardError; end

def self.label
"Schedule hearing"
end
Expand Down Expand Up @@ -68,16 +72,22 @@ def create_change_hearing_disposition_task(instructions = nil)
end

multi_transaction do
# cancel my children, myself, and my hearing task ancestor
children.open.update_all(status: Constants.TASK_STATUSES.cancelled, closed_at: Time.zone.now)
update!(status: Constants.TASK_STATUSES.cancelled, closed_at: Time.zone.now)
ancestor_task_of_type(HearingTask)&.update!(
status: Constants.TASK_STATUSES.cancelled,
closed_at: Time.zone.now
)

# cancel the old HearingTask and create a new one associated with the same hearing
# NOTE: We need to first create new hearing task so there is at least one open hearing task for
# when_child_task_completed in HearingTask to prevent triggering of location change for legacy appeals
# with update below
new_hearing_task = hearing_task.cancel_and_recreate

# cancel my children, myself, and possibly my hearing task ancestor
# NOTE: possibly because cancellation depends on whether or not the tasks are assigned to BVA org
# and all the children tasks of HearingTask have been cancelled
cancel_task_and_child_subtasks

parent = ancestor_task_of_type(HearingTask)

cancel_parent_task(parent) if parent

# create the association for new hearing task
HearingTaskAssociation.create!(hearing: hearing_task.hearing, hearing_task: new_hearing_task)

# create a ChangeHearingDispositionTask on the new HearingTask
Expand All @@ -102,6 +112,19 @@ def available_actions(user)

private

def cancel_parent_task(parent)
# if parent HearingTask does not have any open children tasks, cancel it
if parent.children.open.empty?
parent.update!(status: Constants.TASK_STATUSES.cancelled, closed_at: Time.zone.now)
else # otherwise don't cancel it and capture error in sentry
Raven.capture_exception(
HearingTaskHasOpenChildren.new(
"Hearing Task with id #{parent&.id} could not be cancelled because it has open children tasks."
)
)
end
end

def create_hearing(task_values)
HearingRepository.slot_new_hearing(
task_values[:hearing_day_id],
Expand Down
106 changes: 68 additions & 38 deletions spec/models/tasks/schedule_hearing_task_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -226,64 +226,94 @@
end

describe "#create_change_hearing_disposition_task" do
let(:appeal) { create(:appeal) }
let(:root_task) { create(:root_task, appeal: appeal) }
let(:past_hearing_disposition) { Constants.HEARING_DISPOSITION_TYPES.postponed }
let(:hearing) { create(:hearing, appeal: appeal, disposition: past_hearing_disposition) }
let(:hearing_task) { create(:hearing_task, parent: root_task) }
let!(:disposition_task) do
create(:assign_hearing_disposition_task, parent: hearing_task)
end

let!(:association) { create(:hearing_task_association, hearing: hearing, hearing_task: hearing_task) }
let!(:hearing_task_2) { create(:hearing_task, parent: root_task) }
let!(:task) { create(:schedule_hearing_task, parent: hearing_task_2) }
let(:instructions) { "These are my detailed instructions for a schedule hearing task." }

let!(:disposition_task) do
create(:assign_hearing_disposition_task, parent: hearing_task)
end

subject { task.create_change_hearing_disposition_task(instructions) }

before do
[hearing_task, disposition_task].each { |task| task&.update!(status: Constants.TASK_STATUSES.completed) }
create(:hearing_task_association, hearing: hearing, hearing_task: hearing_task_2)
end

subject { task.create_change_hearing_disposition_task(instructions) }

it "creates new hearing and change hearing disposition tasks and cancels unwanted tasks" do
subject
shared_examples "creates new task" do
it "creates new hearing and change hearing disposition tasks and cancels unwanted tasks" do
subject

expect(hearing_task.reload.open?).to be_falsey
expect(hearing_task.closed_at).to_not be_nil
expect(disposition_task.reload.open?).to be_falsey
expect(disposition_task.closed_at).to_not be_nil
expect(hearing_task_2.reload.status).to eq Constants.TASK_STATUSES.cancelled
expect(hearing_task_2.closed_at).to_not be_nil
expect(task.reload.status).to eq Constants.TASK_STATUSES.cancelled
expect(task.closed_at).to_not be_nil
new_hearing_tasks = appeal.tasks.open.where(type: HearingTask.name)
expect(new_hearing_tasks.count).to eq 1
expect(new_hearing_tasks.first.hearing).to eq hearing
new_change_tasks = appeal.tasks.open.where(type: ChangeHearingDispositionTask.name)
expect(new_change_tasks.count).to eq 1
expect(new_change_tasks.first.parent).to eq new_hearing_tasks.first
expect(hearing_task.reload.open?).to be_falsey
expect(hearing_task.closed_at).to_not be_nil
expect(disposition_task.reload.open?).to be_falsey
expect(disposition_task.closed_at).to_not be_nil
expect(hearing_task_2.reload.status).to eq Constants.TASK_STATUSES.cancelled
expect(hearing_task_2.closed_at).to_not be_nil
expect(task.reload.status).to eq Constants.TASK_STATUSES.cancelled
expect(task.closed_at).to_not be_nil
new_hearing_tasks = appeal.tasks.open.where(type: HearingTask.name)
expect(new_hearing_tasks.count).to eq 1
expect(new_hearing_tasks.first.hearing).to eq hearing
new_change_tasks = appeal.tasks.open.where(type: ChangeHearingDispositionTask.name)
expect(new_change_tasks.count).to eq 1
expect(new_change_tasks.first.parent).to eq new_hearing_tasks.first
end
end
context "AMA appeal" do
let(:appeal) { create(:appeal) }
let(:past_hearing_disposition) { Constants.HEARING_DISPOSITION_TYPES.postponed }
let(:hearing) { create(:hearing, appeal: appeal, disposition: past_hearing_disposition) }

include_examples "creates new task"

it "does not create ihp tasks" do
subject

expect(InformalHearingPresentationTask.where(appeal_id: appeal.id).count).to eq(0)
end

context "the past hearing disposition is nil" do
let(:past_hearing_disposition) { nil }

context "the past hearing disposition is nil" do
let(:past_hearing_disposition) { nil }
it "raises an error" do
expect { subject }
.to raise_error(Caseflow::Error::ActionForbiddenError)
.with_message(COPY::REQUEST_HEARING_DISPOSITION_CHANGE_FORBIDDEN_ERROR)
end
end

it "raises an error" do
expect { subject }
.to raise_error(Caseflow::Error::ActionForbiddenError)
.with_message(COPY::REQUEST_HEARING_DISPOSITION_CHANGE_FORBIDDEN_ERROR)
context "there's no past inactive hearing task" do
let(:hearing_task) { nil }
let(:disposition_task) { nil }
let(:association) { nil }

it "raises an error" do
expect { subject }
.to raise_error(Caseflow::Error::ActionForbiddenError)
.with_message(COPY::REQUEST_HEARING_DISPOSITION_CHANGE_FORBIDDEN_ERROR)
end
end
end

context "there's no past inactive hearing task" do
let(:hearing_task) { nil }
let(:disposition_task) { nil }
let(:association) { nil }
context "Legacy Appeal" do
let(:vacols_case) { create(:case, bfcurloc: LegacyAppeal::LOCATION_CODES[:caseflow]) }
let(:veteran_participant_id) { "0000" }
let(:appeal) { create(:legacy_appeal, vacols_case: vacols_case) }
let(:case_hearing_past_disposition) { "P" }
let(:hearing) { create(:legacy_hearing, appeal: appeal, disposition: case_hearing_past_disposition) }

include_examples "creates new task"

it "does not change location" do
tomas-nava marked this conversation as resolved.
Show resolved Hide resolved
subject

it "raises an error" do
expect { subject }
.to raise_error(Caseflow::Error::ActionForbiddenError)
.with_message(COPY::REQUEST_HEARING_DISPOSITION_CHANGE_FORBIDDEN_ERROR)
expect(vacols_case.reload.bfcurloc).to eq(LegacyAppeal::LOCATION_CODES[:caseflow])
end
end
end
Expand Down