diff --git a/app/models/tasks/disposition_task.rb b/app/models/tasks/disposition_task.rb index 2df615aa814..7e20aa61329 100644 --- a/app/models/tasks/disposition_task.rb +++ b/app/models/tasks/disposition_task.rb @@ -7,7 +7,10 @@ # The task is marked complete when these children tasks are completed. class DispositionTask < GenericTask before_create :check_parent_type + after_update :update_appeal_location_after_cancel, if: :task_just_canceled_and_has_legacy_appeal? + after_update :create_ihp_tasks_after_cancel, if: :task_just_canceled_and_has_ama_appeal? + class HearingDispositionNotCanceled < StandardError; end class HearingDispositionNotNoShow < StandardError; end class << self @@ -32,8 +35,16 @@ def check_parent_type end end + def cancel! + if hearing_disposition != Constants.HEARING_DISPOSITION_TYPES.cancelled + fail HearingDispositionNotCanceled + end + + update!(status: Constants.TASK_STATUSES.cancelled) + end + def mark_no_show! - if parent&.hearing_task_association&.hearing&.disposition != Constants.HEARING_DISPOSITION_TYPES.no_show + if hearing_disposition != Constants.HEARING_DISPOSITION_TYPES.no_show fail HearingDispositionNotNoShow end @@ -48,4 +59,36 @@ def mark_no_show! on_hold_duration: 25.days ) end + + private + + def task_just_canceled? + saved_change_to_attribute?("status") && cancelled? + end + + def task_just_canceled_and_has_legacy_appeal? + task_just_canceled? && appeal.is_a?(LegacyAppeal) + end + + def task_just_canceled_and_has_ama_appeal? + task_just_canceled? && appeal.is_a?(Appeal) + end + + def create_ihp_tasks_after_cancel + RootTask.create_ihp_tasks!(appeal, parent) + end + + def update_appeal_location_after_cancel + location = if appeal.vsos.empty? + LegacyAppeal::LOCATION_CODES[:case_storage] + else + LegacyAppeal::LOCATION_CODES[:service_organization] + end + + AppealRepository.update_location!(appeal, location) + end + + def hearing_disposition + parent&.hearing_task_association&.hearing&.disposition + end end diff --git a/app/models/tasks/hearing_task.rb b/app/models/tasks/hearing_task.rb index 4ea04630191..2d46e4fde92 100644 --- a/app/models/tasks/hearing_task.rb +++ b/app/models/tasks/hearing_task.rb @@ -7,4 +7,14 @@ class HearingTask < GenericTask has_one :hearing_task_association + + private + + def update_status_if_children_tasks_are_complete + if children.select(&:active?).empty? + return update!(status: :cancelled) if children.select { |c| c.type == DispositionTask.name && c.cancelled? }.any? + end + + super + end end diff --git a/spec/models/tasks/disposition_task_spec.rb b/spec/models/tasks/disposition_task_spec.rb index cf0868bbee3..7946889c9fd 100644 --- a/spec/models/tasks/disposition_task_spec.rb +++ b/spec/models/tasks/disposition_task_spec.rb @@ -36,6 +36,140 @@ end end + describe ".cancel!" do + let(:disposition) { nil } + let(:appeal) { FactoryBot.create(:appeal) } + let(:root_task) { FactoryBot.create(:root_task, appeal: appeal) } + let(:hearing_task) { FactoryBot.create(:hearing_task, parent: root_task, appeal: appeal) } + let(:hearing) { FactoryBot.create(:hearing, appeal: appeal, disposition: disposition) } + let!(:hearing_task_association) do + FactoryBot.create( + :hearing_task_association, + hearing: hearing, + hearing_task: hearing_task + ) + end + let!(:schedule_hearing_task) do + FactoryBot.create( + :schedule_hearing_task, + parent: hearing_task, + appeal: appeal, + assigned_to: HearingsManagement.singleton, + status: Constants.TASK_STATUSES.completed + ) + end + let!(:disposition_task) do + FactoryBot.create( + :ama_disposition_task, + parent: hearing_task, + appeal: appeal, + status: Constants.TASK_STATUSES.in_progress + ) + end + + subject { disposition_task.cancel! } + + context "the appeal is an AMA appeal" do + context "the task's hearing's disposition is canceled" do + let(:disposition) { Constants.HEARING_DISPOSITION_TYPES.cancelled } + + it "cancels the disposition task and its parent hearing task" do + expect(disposition_task.cancelled?).to be_falsey + expect(hearing_task.on_hold?).to be_truthy + + expect { subject }.to_not raise_error + + expect(disposition_task.cancelled?).to be_truthy + expect(hearing_task.cancelled?).to be_truthy + expect(InformalHearingPresentationTask.where(appeal: appeal).length).to eq 0 + end + + context "the appeal has a VSO" do + let(:participant_id_with_pva) { "000000" } + let(:appeal) do + create(:appeal, claimants: [create(:claimant, participant_id: participant_id_with_pva)]) + end + + before do + Vso.create( + name: "Paralyzed Veterans Of America", + role: "VSO", + url: "paralyzed-veterans-of-america", + participant_id: "2452383" + ) + + allow_any_instance_of(BGSService).to receive(:fetch_poas_by_participant_ids) + .with([participant_id_with_pva]).and_return( + participant_id_with_pva => { + representative_name: "PARALYZED VETERANS OF AMERICA, INC.", + representative_type: "POA National Organization", + participant_id: "2452383" + } + ) + end + + it "creates an IHP task" do + expect(InformalHearingPresentationTask.where(appeal: appeal).length).to eq 0 + + subject + + expect(InformalHearingPresentationTask.where(appeal: appeal).length).to eq 1 + end + end + end + + context "the task's hearing's disposition is not canceled" do + let(:disposition) { Constants.HEARING_DISPOSITION_TYPES.postponed } + + it "raises an error" do + expect(disposition_task.cancelled?).to be_falsey + expect { subject }.to raise_error(DispositionTask::HearingDispositionNotCanceled) + expect(disposition_task.cancelled?).to be_falsey + end + end + end + + context "the appeal is a legacy appeal" do + let(:vacols_case) { FactoryBot.create(:case, bfcurloc: LegacyAppeal::LOCATION_CODES[:schedule_hearing]) } + let(:appeal) { create(:legacy_appeal, vacols_case: vacols_case) } + let(:hearing) { create(:legacy_hearing, appeal: appeal, disposition: disposition) } + let(:disposition) { Constants.HEARING_DISPOSITION_TYPES.cancelled } + + context "there's no associated VSO" do + it "updates the case location to case storage (81)" do + subject + + expect(vacols_case.reload.bfcurloc).to eq(LegacyAppeal::LOCATION_CODES[:case_storage]) + end + end + + context "there is an associated VSO" do + let(:participant_id) { "1234" } + let!(:vso) { create(:vso, name: "Gogozim", participant_id: participant_id) } + + before do + allow(BGSService).to receive(:power_of_attorney_records).and_return( + appeal.veteran_file_number => { + file_number: appeal.veteran_file_number, + power_of_attorney: { + legacy_poa_cd: "3QQ", + nm: "Clarence Darrow", + org_type_nm: "POA Attorney", + ptcpnt_id: participant_id + } + } + ) + end + + it "updates the case location to service organization (55)" do + subject + + expect(vacols_case.reload.bfcurloc).to eq(LegacyAppeal::LOCATION_CODES[:service_organization]) + end + end + end + end + describe ".mark_no_show!" do let(:disposition) { nil } let(:appeal) { FactoryBot.create(:appeal) }