-
Notifications
You must be signed in to change notification settings - Fork 17
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
Changes from all commits
21d4afe
2fb2c08
2f6a333
0baf7d0
42012c9
78ff311
35402df
7946f18
55089f2
4a9eeba
2edb095
5036629
e8ac307
c3074d8
454d539
dde776a
fa79cca
71df9b7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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? | ||
|
||
enum status: { | ||
Constants.TASK_STATUSES.assigned.to_sym => Constants.TASK_STATUSES.assigned, | ||
|
@@ -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) | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed because this function was never called? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
@@ -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 | ||
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.