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

End holds set with timed hold tasks #10687

Merged
merged 18 commits into from
May 10, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
17 changes: 17 additions & 0 deletions app/controllers/tasks/end_hold_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

class Tasks::EndHoldController < TasksController
def create
task.cancel_timed_hold

render json: { tasks: json_tasks(task.appeal.tasks.includes(*task_includes)) }
rescue ActiveRecord::RecordInvalid => error
invalid_record_error(error.record)
end

private

def task
@task ||= Task.find(params[:task_id])
end
end
24 changes: 3 additions & 21 deletions app/models/legacy_tasks/legacy_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,10 @@ def available_actions_unwrapper(user, role)
end

def build_action_hash(action, user)
{ label: action[:label], value: action[:value], data: action[:func] ? send(action[:func], user) : nil }
end

def add_admin_action_data(_user)
{
redirect_after: "/queue",
selected: nil,
options: Constants::CO_LOCATED_ADMIN_ACTIONS.map do |key, value|
{
label: value,
value: key
}
end,
type: ColocatedTask.name
}
end

def assign_to_attorney_data(_user)
{
selected: nil,
options: nil,
type: AttorneyLegacyTask.name
label: action[:label],
value: action[:value],
data: action[:func] ? TaskActionRepository.send(action[:func], self, user) : nil
}
end

Expand Down
10 changes: 8 additions & 2 deletions app/models/serializers/work_queue/task_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ class WorkQueue::TaskSerializer
attribute :started_at
attribute :created_at
attribute :closed_at
attribute :placed_on_hold_at
attribute :on_hold_duration
attribute :instructions
attribute :appeal_type
attribute :timeline_title
Expand All @@ -41,6 +39,14 @@ class WorkQueue::TaskSerializer
}
end

attribute :placed_on_hold_at do |object|
object.placed_on_hold_at || object.calculated_placed_on_hold_at
end

attribute :on_hold_duration do |object|
object.on_hold_duration || object.calculated_on_hold_duration
end

attribute :docket_name do |object|
object.appeal.try(:docket_name)
end
Expand Down
174 changes: 37 additions & 137 deletions app/models/task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Task < ApplicationRecord
before_update :set_timestamps
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_timed_hold, unless: :task_just_placed_on_hold?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we create a child task for a task that is on a timed hold, will we cancel the existing timed hold? If not, I think it is fine to defer that work to a follow on PR since this looks good (and big!) as is and we aren't creating timed holds yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will; I added a test in this commit.


enum status: {
Constants.TASK_STATUSES.assigned.to_sym => Constants.TASK_STATUSES.assigned,
Expand Down Expand Up @@ -65,12 +66,17 @@ def active_with_no_children?
active? && children.empty?
end

# available_actions() returns an array of options from selected by the subclass
# from TASK_ACTIONS that looks something like:
# available_actions() returns an array of options selected by
# the subclass from TASK_ACTIONS that looks something like:
# [ { "label": "Assign to person", "value": "modal/assign_to_person", "func": "assignable_users" }, ... ]
def available_actions_unwrapper(user)
actions = actions_available?(user) ? available_actions(user).map { |action| build_action_hash(action, user) } : []

# Add the cancel timed hold option to the set of actions here.
if actions.any? && on_timed_hold?
actions.push(build_action_hash(Constants.TASK_ACTIONS.END_TIMED_HOLD.to_h, user))
end

# Make sure each task action has a unique URL so we can determine which action we are selecting on the frontend.
if actions.length > actions.pluck(:value).uniq.length
fail Caseflow::Error::DuplicateTaskActionPaths, task_id: id, user_id: user.id, labels: actions.pluck(:label)
Expand All @@ -80,12 +86,16 @@ def available_actions_unwrapper(user)
end

def build_action_hash(action, user)
{ label: action[:label], value: action[:value], data: action[:func] ? send(action[:func], user) : nil }
{
label: action[:label],
value: action[:value],
data: action[:func] ? TaskActionRepository.send(action[:func], self, user) : nil
}
end

# A wrapper around actions_allowable that also disallows doing actions to on_hold tasks.
def actions_available?(user)
return false if status == Constants.TASK_STATUSES.on_hold
return false if status == Constants.TASK_STATUSES.on_hold && !on_timed_hold?

actions_allowable?(user)
end
Expand All @@ -111,6 +121,27 @@ def children_attorney_tasks
children.where(type: AttorneyTask.name)
end

def on_timed_hold?
!active_child_timed_hold_task.nil?
end

def active_child_timed_hold_task
children.active.find_by(type: TimedHoldTask.name)
end

def cancel_timed_hold
active_child_timed_hold_task&.update!(status: Constants.TASK_STATUSES.cancelled)
end

def calculated_placed_on_hold_at
active_child_timed_hold_task&.timer_start_time
end

def calculated_on_hold_duration
timed_hold_task = active_child_timed_hold_task
(timed_hold_task&.timer_end_time&.to_date &.- timed_hold_task&.timer_start_time&.to_date)&.to_i
end

def self.recently_closed
inactive.where(closed_at: (Time.zone.now - 2.weeks)..Time.zone.now)
end
Expand Down Expand Up @@ -295,136 +326,10 @@ def cancel_task_and_child_subtasks
)
end

def assign_to_organization_data(_user = nil)
organizations = Organization.assignable(self).map do |organization|
{
label: organization.name,
value: organization.id
}
end

{
selected: nil,
options: organizations,
type: GenericTask.name
}
end

def mail_assign_to_organization_data(_user = nil)
{ options: MailTask.subclass_routing_options }
end

def cancel_task_data(_user = nil)
{
modal_title: COPY::CANCEL_TASK_MODAL_TITLE,
modal_body: COPY::CANCEL_TASK_MODAL_DETAIL,
message_title: format(COPY::CANCEL_TASK_CONFIRMATION, appeal.veteran_full_name),
message_detail: format(COPY::MARK_TASK_COMPLETE_CONFIRMATION_DETAIL, assigned_by&.full_name || "the assigner")
}
end

def assign_to_user_data(user = nil)
users = if assigned_to.is_a?(Organization)
assigned_to.users
elsif parent&.assigned_to.is_a?(Organization)
parent.assigned_to.users.reject { |u| u == assigned_to }
else
[]
end

{
selected: user,
options: users_to_options(users),
type: type
}
end

def assign_to_judge_data(_user = nil)
{
selected: root_task.children.find { |task| task.is_a?(JudgeTask) }&.assigned_to,
options: users_to_options(Judge.list_all),
type: JudgeQualityReviewTask.name
}
end

def assign_to_attorney_data(_user = nil)
{
selected: nil,
options: nil,
type: AttorneyTask.name
}
end

def assign_to_privacy_team_data(_user = nil)
org = PrivacyTeam.singleton

{
selected: org,
options: [{ label: org.name, value: org.id }],
type: PrivacyActTask.name
}
end

def assign_to_translation_team_data(_user = nil)
org = Translation.singleton

{
selected: org,
options: [{ label: org.name, value: org.id }],
type: TranslationTask.name
}
end

def add_admin_action_data(_user = nil)
{
redirect_after: "/queue",
selected: nil,
options: Constants::CO_LOCATED_ADMIN_ACTIONS.map do |key, value|
{
label: value,
value: key
}
end,
type: ColocatedTask.name
}
end

def complete_data(_user = nil)
{
modal_body: COPY::MARK_TASK_COMPLETE_COPY
}
end

def schedule_veteran_data(_user = nil)
{
selected: nil,
options: nil,
type: ScheduleHearingTask.name
}
end

def return_to_attorney_data(_user = nil)
assignee = children.select { |t| t.is_a?(AttorneyTask) }.max_by(&:created_at)&.assigned_to
attorneys = JudgeTeam.for_judge(assigned_to)&.attorneys || []
attorneys |= [assignee] if assignee.present?
{
selected: assignee,
options: users_to_options(attorneys),
type: AttorneyRewriteTask.name
}
end

def timeline_title
"#{type} completed"
end

def timeline_details
{
title: timeline_title,
date: closed_at
}
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed because this function was never called?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, it was defined and used in November '18, then the usage was removed in January but the definition never was.


def update_if_hold_expired!
update!(status: Constants.TASK_STATUSES.in_progress) if on_hold_expired?
end
Expand Down Expand Up @@ -472,13 +377,8 @@ def task_just_closed_and_has_parent?
task_just_closed? && parent
end

def users_to_options(users)
users.map do |user|
{
label: user.full_name,
value: user.id
}
end
def task_just_placed_on_hold?
saved_change_to_attribute?(:placed_on_hold_at)
end

def update_status_if_children_tasks_are_complete
Expand Down
11 changes: 0 additions & 11 deletions app/models/tasks/disposition_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,6 @@ def available_actions(user)
end
end

def add_schedule_hearing_task_admin_actions_data(_user)
{
redirect_after: "/queue/appeals/#{appeal.external_id}",
message_detail: COPY::ADD_HEARING_ADMIN_TASK_CONFIRMATION_DETAIL,
selected: nil,
options: HearingAdminActionTask.subclasses.sort_by(&:label).map do |subclass|
{ value: subclass.name, label: subclass.label }
end
}
end

def update_from_params(params, user)
payload_values = params.delete(:business_payloads)&.dig(:values)

Expand Down
13 changes: 0 additions & 13 deletions app/models/tasks/hearing_admin_action_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,6 @@ def actions_allowable?(user)
(HearingsManagement.singleton.user_has_access?(user) || HearingAdmin.singleton.user_has_access?(user)) && super
end

def assign_to_user_data(user = nil)
super(user).merge(
redirect_after: "/organizations/#{HearingAdmin.singleton.url}",
message_detail: COPY::HEARING_ASSIGN_TASK_SUCCESS_MESSAGE_DETAIL
)
end

def complete_data(_user = nil)
{
modal_body: COPY::HEARING_SCHEDULE_COMPLETE_ADMIN_MODAL
}
end

private

def on_hold_duration_is_set
Expand Down
6 changes: 0 additions & 6 deletions app/models/tasks/no_show_hearing_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,6 @@ def reschedule_hearing
end
end

def complete_data(_user = nil)
{
modal_body: COPY::NO_SHOW_HEARING_TASK_COMPLETE_MODAL_BODY
}
end

private

def set_assignee
Expand Down
22 changes: 0 additions & 22 deletions app/models/tasks/schedule_hearing_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,28 +106,6 @@ def available_actions(user)
hearing_admin_actions
end

def add_admin_action_data(_user)
{
redirect_after: "/queue/appeals/#{appeal.external_id}",
message_detail: COPY::ADD_HEARING_ADMIN_TASK_CONFIRMATION_DETAIL,
selected: nil,
options: HearingAdminActionTask.subclasses.sort_by(&:label).map do |subclass|
{ value: subclass.name, label: subclass.label }
end
}
end

def withdraw_hearing_data(_user)
{
redirect_after: "/queue/appeals/#{appeal.external_id}",
modal_title: COPY::WITHDRAW_HEARING_MODAL_TITLE,
modal_body: COPY::WITHDRAW_HEARING_MODAL_BODY,
message_title: format(COPY::WITHDRAW_HEARING_SUCCESS_MESSAGE_TITLE, appeal.veteran_full_name),
message_detail: format(COPY::WITHDRAW_HEARING_SUCCESS_MESSAGE_BODY, appeal.veteran_full_name),
back_to_hearing_schedule: true
}
end

private

def set_assignee
Expand Down
Loading