Skip to content

Commit

Permalink
Teaches BVA Dispatching Tasks to actually block (#15041)
Browse files Browse the repository at this point in the history
* Teaches BVA Dispatching Tasks to actually block

* Renames dispatch parent hook

* Apply suggestions from code review

Co-authored-by: Yoom Lam <[email protected]>

* Missed some names

* Applys code review suggestions

 Co-authored-by: Yoom Lam <[email protected]>

* Unblocks tasks preceeding BVA Dispatch

* Update spec/models/task_spec.rb

Co-authored-by: Yoom Lam <[email protected]>

* Update spec/models/task_spec.rb

Co-authored-by: Yoom Lam <[email protected]>

* Adds safe call

* Teaches BVA Dispatch blocking tasks to invoke Dispatch on close (#15042)

* Teaches BVA Dispatch blocking tasks to invoke Dispatch on close

When a task that blocks BVA Dispatch is closed, it evaluates the state of the appeal and attempts to create a BVA Dispatch Task if the appeal is otherwise ready

Teaches BvaDispatchTask to refuse to create if there open dispatch_blocking? tasks

* Enables the feature

* Updates Privacy Task messaging to VLJ Support

* Adds messaging for FOIA ama task assignment

Co-authored-by: Yoom Lam <[email protected]>
  • Loading branch information
lomky and yoomlam authored Aug 28, 2020
1 parent a48c335 commit a9a6449
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 1 deletion.
30 changes: 30 additions & 0 deletions app/models/task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ class Task < CaseflowRecord

before_create :set_assigned_at
before_create :verify_org_task_unique
before_create :conditionally_set_dispatch_as_parent, if: :blocking_dispatch?

after_create :create_and_auto_assign_child_task, if: :automatically_assign_org_task?
after_create :tell_parent_task_child_task_created

before_save :set_timestamp
after_update :dispatch_if_ready, if: :task_just_closed_and_blocking_dispatch?
after_update :update_parent_status, if: :task_just_closed_and_has_parent?
after_update :update_children_status_after_closed, if: :task_just_closed?
after_update :cancel_task_timers, if: :task_just_closed?
Expand Down Expand Up @@ -609,6 +611,10 @@ def blocking_dispatch?
self.class.blocking_dispatch?
end

def task_just_closed_and_blocking_dispatch?
task_just_closed? && blocking_dispatch?
end

# currently only defined by ScheduleHearingTask and AssignHearingDispositionTask for virtual hearing related updates
def alerts
@alerts ||= []
Expand Down Expand Up @@ -684,6 +690,30 @@ def set_assigned_at
self.assigned_at = created_at unless assigned_at
end

# If there is a BVA Dispatch task open, we want to make sure we block it
# Prefer the provided parent if it already blocks BVA Dispatch, then the User-assigned
# BVA Dispatch task, and fallback to the Organization assigned one.
# If there is no BVA Dispatch task open, set the parent to the root_task unless
# the parent already blocks BVA Dispatch to avoid splicing connected tasks
def conditionally_set_dispatch_as_parent
dispatch_task = BvaDispatchTask.open.find_by(appeal: appeal, assigned_to_type: "User") ||
BvaDispatchTask.open.find_by(appeal: appeal, assigned_to_type: "Organization")

if ! (dispatch_task || self.parent&.blocking_dispatch?)
self.parent = appeal.root_task
end

if dispatch_task&.descendants&.exclude?(parent)
self.parent = dispatch_task
end
end

def dispatch_if_ready
if appeal.ready_for_bva_dispatch?
BvaDispatchTask.create_from_root_task(appeal.root_task)
end
end

STATUS_TIMESTAMPS = {
assigned: :assigned_at,
in_progress: :started_at,
Expand Down
2 changes: 2 additions & 0 deletions app/models/tasks/bva_dispatch_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def task_is_assigned_to_organization_user_administers?(user)

class << self
def create_from_root_task(root_task)
return if root_task.appeal.tasks.open.map(&:blocking_dispatch?).any?

create!(assigned_to: BvaDispatch.singleton, parent_id: root_task.id, appeal: root_task.appeal)
end

Expand Down
1 change: 1 addition & 0 deletions app/repositories/task_action_repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ def assign_to_privacy_team_data(_task, _user = nil)
{
selected: org,
options: [{ label: org.name, value: org.id }],
message_detail: COPY::PRIVACY_ASSIGN_TASK_SUCCESS_MESSAGE_DETAIL,
type: PrivacyActTask.name
}
end
Expand Down
2 changes: 2 additions & 0 deletions client/COPY.json
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@
"ADD_COLOCATED_TASK_CONFIRMATION_DETAIL": "If you need to make any changes, please email your administration action team.",
"ADD_COLOCATED_TASK_ACTION_DUPLICATE_ERROR": "There is already an open %s action on this case with the instructions \"%s\"",
"ADD_HEARING_ADMIN_TASK_CONFIRMATION_DETAIL": "The task has been placed in your teams queue.",
"ADD_FOIA_TASK_CONFIRMATION_DETAIL": "You can continue your work on this case while the FOIA task is worked, case remains in your Assigned queue.",

"CHANGE_TASK_TYPE_SUBHEAD": "Change task type",
"CHANGE_TASK_TYPE_ACTION_LABEL": "Select another task type from the list of available options:",
Expand Down Expand Up @@ -420,6 +421,7 @@
"ASSIGN_TASK_SUCCESS_MESSAGE": "Task assigned to %s",
"REASSIGN_TASK_SUCCESS_MESSAGE": "Task reassigned to %s",
"HEARING_ASSIGN_TASK_SUCCESS_MESSAGE_DETAIL": "You can continue to assign tasks to yourself and others using this queue.",
"PRIVACY_ASSIGN_TASK_SUCCESS_MESSAGE_DETAIL": "You can return this case to the Judge/Attorney or continue to assign tasks to yourself and others using this queue.",

"CREATE_MAIL_TASK_TITLE": "Create new mail task",
"MAIL_TASK_DROPDOWN_TYPE_SELECTOR_LABEL": "Select correspondence type",
Expand Down
5 changes: 4 additions & 1 deletion client/app/queue/colocatedTasks/AddColocatedTaskView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,12 @@ class AddColocatedTaskView extends React.PureComponent {
map((action) => taskActionData(this.props).options.find((option) => option.value === action.type).label).
join(', ');
const msgDisplayCount = this.state.adminActions.length === 1 ? 'an' : this.state.adminActions.length;
const msgDetails = (msgActions === 'FOIA' && !task.isLegacy) ?
COPY.ADD_FOIA_TASK_CONFIRMATION_DETAIL :
taskActionData(this.props).message_detail || COPY.ADD_HEARING_ADMIN_TASK_CONFIRMATION_DETAIL;
const successMsg = {
title: sprintf(msgTitle, msgDisplayCount, msgSubject, msgActions),
detail: taskActionData(this.props).message_detail || COPY.ADD_HEARING_ADMIN_TASK_CONFIRMATION_DETAIL
detail: msgDetails
};

this.props.
Expand Down
197 changes: 197 additions & 0 deletions spec/models/task_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1748,4 +1748,201 @@ def next_assignee(_options = {})
end
end
end

describe ".conditionally_set_dispatch_as_parent" do
let(:user) { create(:user) }
let(:appeal) { create(:appeal) }
let(:root_task) { RootTask.find_or_create_by!(appeal: appeal, assigned_to: Bva.singleton) }
let(:parent_task) { create(:task, parent: root_task) }
before { FeatureToggle.enable!(:block_at_dispatch) }
after { FeatureToggle.disable!(:block_at_dispatch) }
subject { FoiaTask.create!(appeal: appeal, parent: parent_task, assigned_to: create(:user)) }

shared_examples "parent task is descendant of BvaDispatchTask" do
let(:parent_task) { ColocatedTask.find_by(appeal: appeal) }

before do
ColocatedTask.create!(assigned_by: create(:user),
assigned_to: create(:organization),
parent: dispatch_task,
appeal: dispatch_task.appeal)
end
it "uses the descendant task as the parent" do
expect(subject.parent).to eq(parent_task)
end
end

shared_examples "organization-assignee BvaDispatchTask as parent" do
context "that is active" do
before do
BvaDispatchTask.find_by(appeal: appeal,
assigned_to_type: "Organization").update!(status: Constants.TASK_STATUSES.assigned)
end

it "uses that as the parent" do
expect(subject.parent).to eq(dispatch_org_task)
end
end

context "that is on_hold" do
before do
BvaDispatchTask.find_by(appeal: appeal,
assigned_to_type: "Organization").update!(status: Constants.TASK_STATUSES.on_hold)
end

it "uses that as the parent" do
expect(subject.parent).to eq(dispatch_org_task)
end

context "but the parent task is a descendant of BvaDispatchTask" do
let(:dispatch_task) { dispatch_org_task }

include_examples "parent task is descendant of BvaDispatchTask"
end
end

context "that is completed" do
before do
BvaDispatchTask.find_by(appeal: appeal,
assigned_to_type: "Organization").update!(status: Constants.TASK_STATUSES.completed)
end

include_examples "depends on whether parent blocking_dispatch?"
end
end

shared_examples "depends on whether parent blocking_dispatch?" do
context "parent task already blocks dispatch" do
let(:parent_task) { create(:congressional_interest_mail_task, parent: root_task) }
it "doesn't change the parent task" do
expect(subject.parent).to eq(parent_task)
end
end

context "parent task does not block dispatch" do
it "sets the root task as parent" do
expect(subject.parent).to eq(root_task)
end
end
end

context "appeal has user-assignee BvaDispatchTask" do
let(:dispatch_user_task) { BvaDispatchTask.find_by(appeal: appeal, assigned_to_type: "User") }
before do
BvaDispatch.singleton.add_user(user)
BvaDispatchTask.create_from_root_task(root_task)
end

context "that is open" do
context "that is active" do
it "uses that as the parent" do
expect(subject.parent).to eq(dispatch_user_task)
end
end

context "that is on_hold" do
before do
dispatch_user_task.update!(status: Constants.TASK_STATUSES.on_hold)
end

it "uses that as the parent" do
expect(subject.parent).to eq(dispatch_user_task)
end

context "but the parent task is a descendant of BvaDispatchTask" do
let(:dispatch_task) { dispatch_user_task }

include_examples "parent task is descendant of BvaDispatchTask"
end
end
end

context "that is completed" do
before do
BvaDispatchTask.find_by(appeal: appeal,
assigned_to_type: "User").update_column(:status, Constants.TASK_STATUSES.completed)
end

context "but has an organization-assignee BvaDispatchTask" do
let(:dispatch_org_task) { BvaDispatchTask.find_by(appeal: appeal, assigned_to_type: "Organization") }

include_examples "organization-assignee BvaDispatchTask as parent"
end
end
end

context "appeal has no user-assignee BvaDispatchTask" do
before do
BvaDispatch.singleton.add_user(user)
end

context "but has an organization-assignee BvaDispatchTask" do
let(:dispatch_org_task) { BvaDispatchTask.find_by(appeal: appeal, assigned_to_type: "Organization") }

before do
BvaDispatch.singleton.add_user(user)
BvaDispatchTask.create_from_root_task(root_task)
BvaDispatchTask.find_by(appeal: appeal, assigned_to_type: "User").delete
end

include_examples "organization-assignee BvaDispatchTask as parent"
end

context "and no organization-assignee BvaDispatchTask" do
include_examples "depends on whether parent blocking_dispatch?"
end
end
end

describe ".dispatch_if_ready" do
let(:appeal) { create(:appeal) }
let(:user) { create(:user) }
let!(:root_task) { RootTask.find_or_create_by!(appeal: appeal, assigned_to: Bva.singleton) }
let!(:foia_task) { FoiaTask.create!(appeal: appeal, parent: appeal.root_task, assigned_to: create(:user)) }

before { FeatureToggle.enable!(:block_at_dispatch) }
after { FeatureToggle.disable!(:block_at_dispatch) }

subject { foia_task.completed! }

before do
allow(appeal).to receive(:ready_for_bva_dispatch?).and_return(ready)
BvaDispatch.singleton.add_user(user)
end

context "appeal is not ready for BVA Dispatch" do
let(:ready) { false }

it "does not create BVA Dispatch tasks" do
subject

expect(BvaDispatchTask.find_by(appeal: appeal)).to be(nil)
end
end

context "appeal is ready for BVA Dispatch" do
let(:ready) { true }

context "and no other tasks block BVA Dispatch" do
it "creates BVA Dispatch tasks" do
subject

expect(BvaDispatchTask.find_by(appeal: appeal)).to be_a(BvaDispatchTask)
expect(RootTask.find_by(appeal: appeal).open?).to be(true)
end
end

context "but other tasks block BVA Dispatch" do
let!(:privacy_act_task) do
PrivacyActTask.create!(appeal: appeal, parent: appeal.root_task, assigned_to: create(:user))
end

it "does not create BVA Dispatch tasks" do
subject

expect(BvaDispatchTask.find_by(appeal: appeal)).to be(nil)
end
end
end
end
end

0 comments on commit a9a6449

Please sign in to comment.