diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml
index d89fe2a19d5..53b685d2640 100644
--- a/.github/workflows/workflow.yml
+++ b/.github/workflows/workflow.yml
@@ -87,6 +87,7 @@ jobs:
CI: true
REDIS_URL_CACHE: redis://redis:6379/0/cache/
TEST_VACOLS_HOST: facols_db
+ HEADLESS: true
steps:
- uses: actions/checkout@v3
diff --git a/app/models/legacy_tasks/judge_legacy_decision_review_task.rb b/app/models/legacy_tasks/judge_legacy_decision_review_task.rb
index 1451a4de96e..1a794dce88c 100644
--- a/app/models/legacy_tasks/judge_legacy_decision_review_task.rb
+++ b/app/models/legacy_tasks/judge_legacy_decision_review_task.rb
@@ -8,12 +8,18 @@ def review_action
def available_actions(current_user, _role)
# This must check judge_in_vacols? rather than role as judge, otherwise acting
# VLJs cannot check out
- return [] if current_user != assigned_to || !current_user.judge_in_vacols?
+ scm = current_user.can_act_on_behalf_of_judges?
- [
- Constants.TASK_ACTIONS.ADD_ADMIN_ACTION.to_h,
- review_action
- ]
+ actions = []
+
+ if scm && FeatureToggle.enabled?(:legacy_case_movement_vlj_to_atty_for_rewrite)
+ actions << Constants.TASK_ACTIONS.LEGACY_RETURN_TO_ATTORNEY.to_h
+ end
+
+ return actions if current_user != assigned_to || !current_user.judge_in_vacols?
+
+ actions << Constants.TASK_ACTIONS.ADD_ADMIN_ACTION.to_h
+ actions << review_action
end
def label
diff --git a/app/queries/ineligible_judge_list.rb b/app/queries/ineligible_judge_list.rb
index 7c5b7d3dc45..739282331f1 100644
--- a/app/queries/ineligible_judge_list.rb
+++ b/app/queries/ineligible_judge_list.rb
@@ -12,8 +12,6 @@ class IneligibleJudgeList
}.freeze
EMPTY_KEY_VALUE = "No Key Present"
- INACTIVE_VACOLS = CaseDistributionIneligibleJudges.ineligible_vacols_judges
- INACTIVE_CASEFLOW = CaseDistributionIneligibleJudges.ineligible_caseflow_judges
def self.generate_rows(record)
HEADERS.keys.map { |key| record[key] }
@@ -47,9 +45,17 @@ def self.parse_record(record)
}
end
+ def self.inactive_caseflow
+ @inactive_caseflow ||= CaseDistributionIneligibleJudges.ineligible_caseflow_judges
+ end
+
+ def self.inactive_vacols
+ @inactive_vacols ||= CaseDistributionIneligibleJudges.ineligible_vacols_judges
+ end
+
def self.get_reason_for_ineligibility(css_id_value, sdomainid_value)
- inactive_caseflow_user = INACTIVE_CASEFLOW.find { |caseflow_user| caseflow_user[:css_id] == css_id_value }
- inactive_vacols_user = INACTIVE_VACOLS.find { |vacols_user| vacols_user[:sdomainid] == sdomainid_value }
+ inactive_caseflow_user = inactive_caseflow.find { |caseflow_user| caseflow_user[:css_id] == css_id_value }
+ inactive_vacols_user = inactive_vacols.find { |vacols_user| vacols_user[:sdomainid] == sdomainid_value }
@reason = if inactive_caseflow_user && inactive_vacols_user
"BOTH"
diff --git a/app/repositories/task_action_repository.rb b/app/repositories/task_action_repository.rb
index e5171aaf9f0..4f41f02f7c9 100644
--- a/app/repositories/task_action_repository.rb
+++ b/app/repositories/task_action_repository.rb
@@ -200,6 +200,14 @@ def judge_qr_return_to_attorney_data(task, _user = nil)
}
end
+ def legacy_return_to_attorney_data(_task, _user = nil)
+ {
+ selected: nil,
+ options: users_to_options(Attorney.list_all),
+ type: AttorneyLegacyTask.name
+ }
+ end
+
def assign_to_privacy_team_data(_task, _user = nil)
org = PrivacyTeam.singleton
diff --git a/client/app/queue/AssignToView.jsx b/client/app/queue/AssignToView.jsx
index 2b4b481265d..6fc5afd3d25 100644
--- a/client/app/queue/AssignToView.jsx
+++ b/client/app/queue/AssignToView.jsx
@@ -16,7 +16,7 @@ import SearchableDropdown from '../components/SearchableDropdown';
import TextareaField from '../components/TextareaField';
import QueueFlowModal from './components/QueueFlowModal';
-import { requestPatch, requestSave, resetSuccessMessages } from './uiReducer/uiActions';
+import { requestPatch, requestSave, resetSuccessMessages, highlightInvalidFormItems } from './uiReducer/uiActions';
import { taskActionData } from './utils';
@@ -76,6 +76,7 @@ class AssignToView extends React.Component {
};
submit = () => {
+
const { appeal, task, isReassignAction, isTeamAssign } = this.props;
const action = getAction(this.props);
@@ -270,7 +271,7 @@ class AssignToView extends React.Component {
}
render = () => {
- const { assigneeAlreadySelected, task } = this.props;
+ const { assigneeAlreadySelected, highlightFormItems, task } = this.props;
const action = getAction(this.props);
const actionData = taskActionData(this.props);
@@ -327,11 +328,13 @@ class AssignToView extends React.Component {
name="Assign to selector"
searchable
hideLabel={actionData.drop_down_label ? null : true}
+ errorMessage={highlightFormItems && this.state.selectedValue === null ? 'This field is required' : null}
label={this.determineDropDownLabel(actionData)}
placeholder={this.determinePlaceholder(this.props, actionData)}
value={this.state.selectedValue}
onChange={(option) => this.setState({ selectedValue: option ? option.value : null })}
options={this.determineOptions(actionData)}
+ required={this.props.task.type === 'JudgeLegacyDecisionReviewTask'}
/>
)}
{this.isVHAAssignToRegional() &&
@@ -353,6 +356,8 @@ class AssignToView extends React.Component {
onChange={(value) => this.setState({ instructions: value })}
value={this.state.instructions}
optional={actionData.body_optional}
+ errorMessage={highlightFormItems && !validInstructions(this.state.instructions) ? 'Instructions field is required' : null}
+ required={this.props.task.type === 'JudgeLegacyDecisionReviewTask'}
/>
)}
{isPulacCerullo && (
@@ -373,6 +378,8 @@ AssignToView.propTypes = {
veteranFullName: PropTypes.string
}),
assigneeAlreadySelected: PropTypes.bool,
+ highlightInvalidFormItems: PropTypes.func,
+ highlightFormItems: PropTypes.bool,
isReassignAction: PropTypes.bool,
isTeamAssign: PropTypes.bool,
onReceiveAmaTasks: PropTypes.func,
@@ -392,6 +399,7 @@ AssignToView.propTypes = {
const mapStateToProps = (state, ownProps) => {
return {
+ highlightFormItems: state.ui.highlightFormItems,
task: taskById(state, { taskId: ownProps.taskId }),
appeal: appealWithDetailSelector(state, ownProps)
};
@@ -403,6 +411,7 @@ const mapDispatchToProps = (dispatch) =>
requestPatch,
requestSave,
onReceiveAmaTasks,
+ highlightInvalidFormItems,
legacyReassignToJudge,
setOvertime,
resetSuccessMessages
diff --git a/client/app/queue/QueueApp.jsx b/client/app/queue/QueueApp.jsx
index 06d50aa6530..a40a562fbb2 100644
--- a/client/app/queue/QueueApp.jsx
+++ b/client/app/queue/QueueApp.jsx
@@ -946,6 +946,11 @@ class QueueApp extends React.PureComponent {
}`}
render={this.routedAssignToUser}
/>
+
09d", n: @scenario_6_file_number + 1))
+ @scenario_6_file_number += 2000
+ @scenario_6_participant_id += 2000
+ end
+ end
+
+ def initial_scenario_7_id_values
+ @scenario_7_file_number ||= 790_000_000
+ @scenario_7_participant_id ||= 590_000_000
+ while Veteran.find_by(file_number: format("%09d", n: @scenario_7_file_number + 1))
+ @scenario_7_file_number += 2000
+ @scenario_7_participant_id += 2000
+ end
+ end
+
+ def create_scenario_6_veteran(options = {})
+ @scenario_6_file_number += 1
+ @scenario_6_participant_id += 1
+ params = {
+ file_number: format("%09d", n: @scenario_6_file_number),
+ participant_id: format("%09d", n: @scenario_6_participant_id)
+ }
+ create(:veteran, params.merge(options))
+ end
+
+ def create_scenario_7_veteran(options = {})
+ @scenario_7_file_number += 1
+ @scenario_7_participant_id += 1
+ params = {
+ file_number: format("%09d", n: @scenario_7_file_number),
+ participant_id: format("%09d", n: @scenario_7_participant_id)
+ }
+ create(:veteran, params.merge(options))
+ end
+
+ def scenario_6_seeds
+ wqjudge_one = create_scenario_6_veteran(first_name: "Wqjudge", last_name: "One")
+ wqjudge_two = create_scenario_6_veteran(first_name: "Wqjudge", last_name: "Two")
+ wqjudge_three = create_scenario_6_veteran(first_name: "Wqjudge", last_name: "Three")
+ wqjudge_four = create_scenario_6_veteran(first_name: "Wqjudge", last_name: "Four")
+ wqjudge_five = create_scenario_6_veteran(first_name: "Wqjudge", last_name: "Five")
+ wqjudge_six = create_scenario_6_veteran(first_name: "Wqjudge", last_name: "Six")
+ wqjudge_control = create_scenario_6_veteran(first_name: "Wqjudge", last_name: "Control")
+
+ # Case 1: Wqjudge One (Priority, 2 issues)
+ create(:legacy_appeal, :with_veteran, vacols_case: create(
+ :case_with_form_9,
+ :type_cavc_remand,
+ :status_active,
+ :aod,
+ :assigned,
+ user: User.find_by_css_id("BVACOTBJUDGE"),
+ assigner: User.find_by_css_id("BVALSHIELDS"),
+ as_judge_assign_task: false,
+ bfcorlid: "#{wqjudge_one.file_number}S",
+ case_issues: create_list(:case_issue, 2, :compensation)
+ ))
+
+ # Case 2: Wqjudge Two (Nonpriority, 1 issue)
+ create(:legacy_appeal, :with_veteran, vacols_case: create(
+ :case_with_form_9,
+ :type_original,
+ :status_active,
+ :assigned,
+ user: User.find_by_css_id("BVACOTBJUDGE"),
+ assigner: User.find_by_css_id("BVACOTBJUDGE"),
+ as_judge_assign_task: false,
+ bfcorlid: "#{wqjudge_two.file_number}S",
+ case_issues: create_list(:case_issue, 1, :compensation)
+ ))
+
+ # Case 3: Wqjudge Three (Nonpriority, 2 issues)
+ create(:legacy_appeal, :with_veteran, vacols_case: create(
+ :case_with_form_9,
+ :type_original,
+ :status_active,
+ :assigned,
+ user: User.find_by_css_id("BVACOTBJUDGE"),
+ assigner: User.find_by_css_id("BVALSHIELDS"),
+ as_judge_assign_task: false,
+ bfcorlid: "#{wqjudge_three.file_number}S",
+ case_issues: create_list(:case_issue, 2, :compensation)))
+
+ # Case 4: Wqjudge Four (Priority, 1 issue)
+ create(:legacy_appeal, :with_veteran, vacols_case: create(
+ :case_with_form_9,
+ :type_cavc_remand,
+ :status_active,
+ :aod,
+ :assigned,
+ user: User.find_by_css_id("BVAGSPORER"),
+ assigner: User.find_by_css_id("BVAJWEHNER1"),
+ as_judge_assign_task: false,
+ bfcorlid: "#{wqjudge_four.file_number}S",
+ case_issues: create_list(:case_issue, 1, :compensation)
+ ))
+
+ # Case 5: Wqjudge Five (Priority, 2 issues)
+ create(:legacy_appeal, :with_veteran, vacols_case: create(
+ :case_with_form_9,
+ :type_original,
+ :status_active,
+ :aod,
+ :assigned,
+ user: User.find_by_css_id("BVAGSPORER"),
+ assigner: User.find_by_css_id("BVAOTRANTOW"),
+ as_judge_assign_task: false,
+ bfcorlid: "#{wqjudge_five.file_number}S",
+ case_issues: create_list(:case_issue, 2, :compensation)
+ ))
+
+ # Case 6: Wqjudge Six (Nonpriority, 1 issue)
+ create(:legacy_appeal, :with_veteran, vacols_case: create(
+ :case_with_form_9,
+ :type_original,
+ :status_active,
+ :assigned,
+ user: User.find_by_css_id("BVAGSPORER"),
+ assigner: User.find_by_css_id("BVAGSPORER"),
+ as_judge_assign_task: false,
+ bfcorlid: "#{wqjudge_six.file_number}S",
+ case_issues: create_list(:case_issue, 1, :compensation)
+ ))
+
+ # Case 7: Wqjudge Control (priority, 1 issue)
+ create(:legacy_appeal, :with_veteran, vacols_case: create(
+ :case_with_form_9,
+ :type_original,
+ :status_active,
+ :assigned,
+ :aod,
+ bfcorlid: "#{wqjudge_control.file_number}S",
+ user: User.find_by_css_id("BVAOTRANTOW"),
+ assigner: User.find_by_css_id("BVAGSPORER")
+ ))
+ end
+
+ def scenario_7_seeds
+ rewrite_one = create_scenario_7_veteran(first_name: "Rewrite", last_name: "One")
+ rewrite_two = create_scenario_7_veteran(first_name: "Rewrite", last_name: "Two")
+ rewrite_three = create_scenario_7_veteran(first_name: "Rewrite", last_name: "Three")
+ rewrite_four = create_scenario_7_veteran(first_name: "Rewrite", last_name: "Four")
+ rewrite_five = create_scenario_7_veteran(first_name: "Rewrite", last_name: "Five")
+ rewrite_six = create_scenario_7_veteran(first_name: "Rewrite", last_name: "Six")
+ no_rewrite_control = create_scenario_7_veteran(first_name: "NoRewrite ", last_name: "Control")
+
+ # Case 1: Rewrite One (Priority, 2 issues)
+ create(:legacy_appeal, :with_veteran, vacols_case: create(
+ :case_with_form_9,
+ :type_cavc_remand,
+ :status_active,
+ :aod,
+ :assigned,
+ user: User.find_by_css_id("BVACOTBJUDGE"),
+ assigner: User.find_by_css_id("BVALSHIELDS"),
+ as_judge_assign_task: false,
+ bfcorlid: "#{rewrite_one.file_number}S",
+ case_issues: create_list(:case_issue, 2, :compensation)
+ ))
+
+ # Case 2: Rewrite Two (Nonpriority, 1 issue)
+ create(:legacy_appeal, :with_veteran, vacols_case: create(
+ :case_with_form_9,
+ :type_original,
+ :status_active,
+ :assigned,
+ user: User.find_by_css_id("BVACOTBJUDGE"),
+ assigner: User.find_by_css_id("BVACOTBJUDGE"),
+ as_judge_assign_task: false,
+ bfcorlid: "#{rewrite_two.file_number}S",
+ case_issues: create_list(:case_issue, 1, :compensation)
+ ))
+
+ # Case 3: Rewrite Three (Nonpriority, 2 issues)
+ create(:legacy_appeal, :with_veteran, vacols_case: create(
+ :case_with_form_9,
+ :type_original,
+ :status_active,
+ :assigned,
+ user: User.find_by_css_id("BVACOTBJUDGE"),
+ assigner: User.find_by_css_id("BVALSHIELDS"),
+ as_judge_assign_task: false,
+ bfcorlid: "#{rewrite_three.file_number}S",
+ case_issues: create_list(:case_issue, 2, :compensation)
+ ))
+
+ # Case 4: Rewrite Four (Priority, 1 issue)
+ create(:legacy_appeal, :with_veteran, vacols_case: create(
+ :case_with_form_9,
+ :type_cavc_remand,
+ :status_active,
+ :aod,
+ :assigned,
+ user: User.find_by_css_id("BVAGSPORER"),
+ assigner: User.find_by_css_id("BVAJWEHNER1"),
+ as_judge_assign_task: false,
+ bfcorlid: "#{rewrite_four.file_number}S",
+ case_issues: create_list(:case_issue, 1, :compensation)
+ ))
+
+ # Case 5: Rewrite Five (Priority, 2 issues)
+ create(:legacy_appeal, :with_veteran, vacols_case: create(
+ :case_with_form_9,
+ :type_original,
+ :status_active,
+ :aod,
+ :assigned,
+ user: User.find_by_css_id("BVAGSPORER"),
+ assigner: User.find_by_css_id("BVAOTRANTOW"),
+ as_judge_assign_task: false,
+ bfcorlid: "#{rewrite_five.file_number}S",
+ case_issues: create_list(:case_issue, 2, :compensation)
+ ))
+
+ # Case 6: Rewrite Six (Nonpriority, 1 issue)
+ create(:legacy_appeal, :with_veteran, vacols_case: create(
+ :case_with_form_9,
+ :type_original,
+ :status_active,
+ :assigned,
+ user: User.find_by_css_id("BVAGSPORER"),
+ assigner: User.find_by_css_id("BVAGSPORER"),
+ as_judge_assign_task: false,
+ bfcorlid: "#{rewrite_six.file_number}S",
+ case_issues: create_list(:case_issue, 1, :compensation)
+ ))
+
+ # Case 7: NoRewrite Control (priority, 1 issue)
+ create(:legacy_appeal, :with_veteran, vacols_case: create(
+ :case_with_form_9,
+ :type_original,
+ :status_active,
+ :assigned,
+ :aod,
+ assigner: User.find_by_css_id("BVAGSPORER"),
+ bfcorlid: "#{no_rewrite_control.file_number}S",
+ user: User.find_by_css_id("BVAOTRANTOW"),
+ case_issues: create_list(:case_issue, 1, :compensation)
+ ))
+ end
end
end
diff --git a/spec/feature/hearings/virtual_hearings/daily_docket_spec.rb b/spec/feature/hearings/virtual_hearings/daily_docket_spec.rb
index ede34af5705..eaf07f3b392 100644
--- a/spec/feature/hearings/virtual_hearings/daily_docket_spec.rb
+++ b/spec/feature/hearings/virtual_hearings/daily_docket_spec.rb
@@ -40,12 +40,15 @@ def check_email_events(hearing, current_user)
context "Formerly Video Virtual Hearing" do
let(:expected_central_office_time) do
+ time_str = "#{updated_hearing_time} #{hearing.hearing_day.scheduled_for} America/New_York"
+ tz_abbr = Time.zone.parse(time_str).dst? ? "ET" : "EST"
+
Time
.parse(updated_hearing_time)
.strftime("%F %T")
.in_time_zone(regional_office_timezone) # cast the updated hearing time to the ro timezone
.in_time_zone(HearingTimeService::CENTRAL_OFFICE_TIMEZONE) # convert it to the central office timezone
- .strftime("%-l:%M %p ET") # and render it in the format expected in the modal
+ .strftime("%-l:%M %p #{tz_abbr}") # and render it in the format expected in the modal
end
scenario "Virtual hearing time is updated" do
@@ -55,6 +58,7 @@ def check_email_events(hearing, current_user)
click_dropdown(name: "optionalHearingTime0", text: updated_video_hearing_time)
expect(page).to have_content(COPY::VIRTUAL_HEARING_MODAL_CHANGE_HEARING_TIME_TITLE)
expect(page).to have_content(COPY::VIRTUAL_HEARING_MODAL_CHANGE_HEARING_TIME_BUTTON)
+
expect(page).to have_content("Time: #{expected_central_office_time} / #{expected_regional_office_time}")
click_button(COPY::VIRTUAL_HEARING_MODAL_CHANGE_HEARING_TIME_BUTTON)
@@ -180,6 +184,8 @@ def check_email_events(hearing, current_user)
end
context "Updating a hearing's time" do
+ let(:fall_date) { "#{1.year.from_now.year}-11-11" }
+
shared_examples "The hearing time is updated correctly" do
scenario do
visit "hearings/schedule/docket/" + hearing.hearing_day.id.to_s
@@ -195,7 +201,7 @@ def check_email_events(hearing, current_user)
let(:initial_hearing) { create(:legacy_hearing, case_hearing: case_hearing) }
# Ensure that the times are always in standard time.
- before { initial_hearing.hearing_day.update!(scheduled_for: "2024-11-11") }
+ before { initial_hearing.hearing_day.update!(scheduled_for: fall_date) }
context "With a pre-existing scheduled_in_timezone value" do
let(:hearing_time_selection_string) { "10:00 AM Central Time (US & Canada)" }
@@ -203,7 +209,7 @@ def check_email_events(hearing, current_user)
let(:expected_post_update_time) { "10:00 AM CST" }
before do
- hearing.hearing_day.update!(regional_office: "RO30", request_type: "V", scheduled_for: "2024-11-11")
+ hearing.hearing_day.update!(regional_office: "RO30", request_type: "V", scheduled_for: fall_date)
end
include_examples "The hearing time is updated correctly"
@@ -215,7 +221,7 @@ def check_email_events(hearing, current_user)
let(:expected_post_update_time) { "3:00 PM CST" }
before do
- hearing.hearing_day.update!(regional_office: "RO30", request_type: "T", scheduled_for: "2024-11-11")
+ hearing.hearing_day.update!(regional_office: "RO30", request_type: "T", scheduled_for: fall_date)
end
include_examples "The hearing time is updated correctly"
diff --git a/spec/models/hearings/forms/hearing_update_form_spec.rb b/spec/models/hearings/forms/hearing_update_form_spec.rb
index bd5cc15a416..ff8744beb7d 100644
--- a/spec/models/hearings/forms/hearing_update_form_spec.rb
+++ b/spec/models/hearings/forms/hearing_update_form_spec.rb
@@ -114,13 +114,18 @@
end
it "should update scheduled_datetime if it is not null already" do
+ date_str = "#{hearing.hearing_day.scheduled_for} America/New_York"
+ is_dst = Time.zone.parse(date_str).dst?
+
hearing.update(
- scheduled_datetime: "2021-04-23T11:30:00-04:00", scheduled_in_timezone: "America/New_York"
+ scheduled_datetime: "2021-04-23T11:30:00#{is_dst ? '-04:00' : '-05:00'}",
+ scheduled_in_timezone: "America/New_York"
)
subject.update
updated_scheduled_datetime = hearing.scheduled_datetime
+
expect(updated_scheduled_datetime.strftime("%Y-%m-%d %H:%M %z"))
- .to eq "#{hearing.hearing_day.scheduled_for.strftime('%Y-%m-%d')} 21:45 -0400"
+ .to eq "#{hearing.hearing_day.scheduled_for.strftime('%Y-%m-%d')} 21:45 #{is_dst ? '-0400' : '-0500'}"
end
it "should not update scheduled_datetime if it is null already" do
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index 811a8a2d4ff..df5073476f7 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -93,7 +93,7 @@
Capybara.javascript_driver = :logging_selenium_chrome
-Capybara.default_driver = ENV["CI"] ? :sniffybara_headless : :parallel_sniffybara
+Capybara.default_driver = ENV["HEADLESS"] ? :sniffybara_headless : :parallel_sniffybara
# the default default_max_wait_time is 2 seconds
Capybara.default_max_wait_time = 5
# Capybara uses puma by default, but for some reason, some of our tests don't