diff --git a/app/controllers/api/docs/v3/decision_reviews.yaml b/app/controllers/api/docs/v3/decision_reviews.yaml index abae92dba7f..7040bb12ac4 100644 --- a/app/controllers/api/docs/v3/decision_reviews.yaml +++ b/app/controllers/api/docs/v3/decision_reviews.yaml @@ -914,10 +914,14 @@ paths: parameters: - name: X-VA-SSN in: header - required: true - description: The SSN of the veteran. + description: The SSN of the veteran. Either SSN or file number must be passed. schema: type : string + - name: X-VA-File-Number + in: header + description: The file number of the veteran. Either SSN or file number must be passed. + schema: + type: string - name: X-VA-Receipt-Date in: header required: true diff --git a/app/controllers/api/v3/base_controller.rb b/app/controllers/api/v3/base_controller.rb index fc4216988dc..49b88fe6c6b 100644 --- a/app/controllers/api/v3/base_controller.rb +++ b/app/controllers/api/v3/base_controller.rb @@ -10,22 +10,6 @@ def status_from_errors(errors) end protect_from_forgery with: :null_session - before_action :api_released? - - def api_released? - return true if FeatureToggle.enabled?(:api_v3) - - render json: { - errors: [ - { - status: "501", - title: "Not Implemented", - detail: "This endpoint is not yet supported." - } - ] - }, - status: :not_implemented - end def render_errors(errors) errors = Array.wrap(errors) diff --git a/app/controllers/api/v3/decision_reviews/appeals/contestable_issues_controller.rb b/app/controllers/api/v3/decision_reviews/appeals/contestable_issues_controller.rb index f8ca2785544..b5c5193d38a 100644 --- a/app/controllers/api/v3/decision_reviews/appeals/contestable_issues_controller.rb +++ b/app/controllers/api/v3/decision_reviews/appeals/contestable_issues_controller.rb @@ -5,6 +5,12 @@ module V3 module DecisionReviews module Appeals class ContestableIssuesController < BaseContestableIssuesController + include ApiV3FeatureToggleConcern + + before_action only: [:index] do + api_released?(:api_v3_appeals_contestable_issues) + end + private def standin_decision_review diff --git a/app/controllers/api/v3/decision_reviews/appeals_controller.rb b/app/controllers/api/v3/decision_reviews/appeals_controller.rb index fb80236b4a6..51b48ee4efb 100644 --- a/app/controllers/api/v3/decision_reviews/appeals_controller.rb +++ b/app/controllers/api/v3/decision_reviews/appeals_controller.rb @@ -1,6 +1,12 @@ # frozen_string_literal: true class Api::V3::DecisionReviews::AppealsController < Api::V3::BaseController + include ApiV3FeatureToggleConcern + + before_action do + api_released?(:api_v3_appeals) + end + # stub def create render json: {}, status: :not_found diff --git a/app/controllers/api/v3/decision_reviews/base_contestable_issues_controller.rb b/app/controllers/api/v3/decision_reviews/base_contestable_issues_controller.rb index 37d3f0ef4be..f95f71b939b 100644 --- a/app/controllers/api/v3/decision_reviews/base_contestable_issues_controller.rb +++ b/app/controllers/api/v3/decision_reviews/base_contestable_issues_controller.rb @@ -41,17 +41,32 @@ def validate_params end def veteran_valid? - unless veteran_ssn_is_formatted_correctly? + if veteran_ssn.present? && !veteran_ssn_is_formatted_correctly? render_invalid_veteran_ssn return false end - unless veteran + if veteran_identifier.nil? + render_missing_headers + return false + end + unless veteran.present? && veteran_matches render_veteran_not_found return false end true end + def veteran_matches + if veteran_ssn.present? && veteran_ssn != veteran.ssn + return false + end + if veteran_file_number.present? && veteran_file_number != veteran.file_number + return false + end + + true + end + def receipt_date_valid? unless receipt_date.is_a? Date render_invalid_receipt_date @@ -69,11 +84,19 @@ def receipt_date_valid? end def veteran - @veteran ||= VeteranFinder.find_best_match veteran_ssn + @veteran ||= VeteranFinder.find_best_match veteran_identifier end def veteran_ssn - @veteran_ssn ||= request.headers["X-VA-SSN"].to_s.strip + @veteran_ssn ||= request.headers["X-VA-SSN"].presence&.strip + end + + def veteran_file_number + @veteran_file_number ||= request.headers["X-VA-File-Number"].presence&.strip + end + + def veteran_identifier + veteran_file_number || veteran_ssn end def veteran_ssn_is_formatted_correctly? @@ -97,6 +120,14 @@ def render_veteran_not_found ) end + def render_missing_headers + render_errors( + status: 422, + code: :missing_identifying_headers, + title: "Veteran file number or SSN header is required" + ) + end + def receipt_date @receipt_date ||= begin Date.iso8601 receipt_date_header diff --git a/app/controllers/api/v3/decision_reviews/higher_level_reviews/contestable_issues_controller.rb b/app/controllers/api/v3/decision_reviews/higher_level_reviews/contestable_issues_controller.rb index bd8ec14fd7b..213fd515306 100644 --- a/app/controllers/api/v3/decision_reviews/higher_level_reviews/contestable_issues_controller.rb +++ b/app/controllers/api/v3/decision_reviews/higher_level_reviews/contestable_issues_controller.rb @@ -5,6 +5,12 @@ module V3 module DecisionReviews module HigherLevelReviews class ContestableIssuesController < BaseContestableIssuesController + include ApiV3FeatureToggleConcern + + before_action only: [:index] do + api_released?(:api_v3_higher_level_reviews_contestable_issues) + end + private def standin_decision_review diff --git a/app/controllers/api/v3/decision_reviews/higher_level_reviews_controller.rb b/app/controllers/api/v3/decision_reviews/higher_level_reviews_controller.rb index 0f6988f8eed..75c6d4db304 100644 --- a/app/controllers/api/v3/decision_reviews/higher_level_reviews_controller.rb +++ b/app/controllers/api/v3/decision_reviews/higher_level_reviews_controller.rb @@ -1,6 +1,12 @@ # frozen_string_literal: true class Api::V3::DecisionReviews::HigherLevelReviewsController < Api::V3::BaseController + include ApiV3FeatureToggleConcern + + before_action do + api_released?(:api_v3_higher_level_reviews) + end + SUCCESSFUL_CREATION_HTTP_STATUS = 202 def create diff --git a/app/controllers/concerns/api_v3_feature_toggle_concern.rb b/app/controllers/concerns/api_v3_feature_toggle_concern.rb new file mode 100644 index 00000000000..a8533d5b28a --- /dev/null +++ b/app/controllers/concerns/api_v3_feature_toggle_concern.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module ApiV3FeatureToggleConcern + extend ActiveSupport::Concern + + def api_released?(feature) + return true if FeatureToggle.enabled?(feature) + + render json: { + errors: [ + { + status: "501", + title: "Not Implemented", + detail: "This endpoint is not yet supported." + } + ] + }, + status: :not_implemented + end +end diff --git a/app/controllers/organizations/users_controller.rb b/app/controllers/organizations/users_controller.rb index acc0bad5e77..fe4d8d132f2 100644 --- a/app/controllers/organizations/users_controller.rb +++ b/app/controllers/organizations/users_controller.rb @@ -9,7 +9,7 @@ def index render json: { organization_name: organization.name, - judge_team: FeatureToggle.enabled?(:judge_admin_scm) && organization.type == JudgeTeam.name, + judge_team: organization.type == JudgeTeam.name, dvc_team: organization.type == DvcTeam.name, organization_users: json_administered_users(organization_users) } diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb index f9a37b74511..41474e510a8 100644 --- a/app/controllers/tasks_controller.rb +++ b/app/controllers/tasks_controller.rb @@ -17,6 +17,8 @@ class TasksController < ApplicationController BlockedSpecialCaseMovementTask: BlockedSpecialCaseMovementTask, ChangeHearingDispositionTask: ChangeHearingDispositionTask, ColocatedTask: ColocatedTask, + DocketSwitchDeniedTask: DocketSwitchDeniedTask, + DocketSwitchGrantedTask: DocketSwitchGrantedTask, EvidenceSubmissionWindowTask: EvidenceSubmissionWindowTask, FoiaTask: FoiaTask, HearingAdminActionTask: HearingAdminActionTask, @@ -109,12 +111,13 @@ def update rescue ActiveRecord::RecordInvalid => error invalid_record_error(error.record) rescue AssignHearingDispositionTask::HearingAssociationMissing => error - Raven.capture_exception(error) + Raven.capture_exception(error, extra: { application: "hearings" }) + render json: { "errors": ["title": "Missing Associated Hearing", "detail": error] }, status: :bad_request rescue Caseflow::Error::VirtualHearingConversionFailed => error - Raven.capture_exception(error) + Raven.capture_exception(error, extra: { application: "hearings" }) render json: { "errors": ["title": COPY::FAILED_HEARING_UPDATE, "message": error.message, "code": error.code] diff --git a/app/jobs/update_cached_appeals_attributes_job.rb b/app/jobs/update_cached_appeals_attributes_job.rb index b3332104108..31b07ac548f 100644 --- a/app/jobs/update_cached_appeals_attributes_job.rb +++ b/app/jobs/update_cached_appeals_attributes_job.rb @@ -286,20 +286,36 @@ def case_fields_for_vacols_ids(vacols_ids) # ... # } VACOLS::Case.where(bfkey: vacols_ids).map do |vacols_case| - legacy_appeal = AppealRepository.build_appeal(vacols_case) # build non-persisting legacy appeal object + original_request = original_hearing_request_type_for_vacols_case(vacols_case) + changed_request = changed_hearing_request_type_for_all_vacols_ids[vacols_case.bfkey] + # Replicates LegacyAppeal#current_hearing_request_type + current_request = HearingDay::REQUEST_TYPES.key(changed_request)&.to_sym || original_request [ vacols_case.bfkey, { location: vacols_case.bfcurloc, status: VACOLS::Case::TYPES[vacols_case.bfac], - hearing_request_type: legacy_appeal.current_hearing_request_type(readable: true), - former_travel: former_travel?(legacy_appeal) + hearing_request_type: LegacyAppeal::READABLE_HEARING_REQUEST_TYPES[current_request], + former_travel: original_request == :travel_board && current_request != :travel_board } ] end.to_h end + # Gets the symbolic representation of the original type of hearing requested for a vacols case record + # Replicates logic in LegacyAppeal#original_hearing_request_type + def original_hearing_request_type_for_vacols_case(vacols_case) + request_type = VACOLS::Case::HEARING_REQUEST_TYPES[vacols_case.bfhr] + + (request_type == :travel_board && vacols_case.bfdocind == "V") ? :video : request_type + end + + # Maps vacols ids to their leagcy appeal's changed hearing request type + def changed_hearing_request_type_for_all_vacols_ids + @changed_hearing_request_type_for_all_vacols_ids ||= LegacyAppeal.pluck(:vacols_id, :changed_request_type).to_h + end + def issues_counts_for_vacols_folders(vacols_ids) VACOLS::CaseIssue.where(isskey: vacols_ids).group(:isskey).count end @@ -322,15 +338,4 @@ def veteran_names_for_file_numbers(veteran_file_numbers) [veteran.file_number, "#{veteran.last_name&.split(' ')&.last}, #{veteran.first_name}"] end.to_h end - - # checks to see if the hearing request type was former_travel - def former_travel?(legacy_appeal) - # the current request type is travel - if legacy_appeal.current_hearing_request_type == :travel_board - return false - end - - # otherwise check if og request type was travel - legacy_appeal.original_hearing_request_type == :travel_board - end end diff --git a/app/models/cavc_remand.rb b/app/models/cavc_remand.rb index 26bb1b25f37..0ae809a9da7 100644 --- a/app/models/cavc_remand.rb +++ b/app/models/cavc_remand.rb @@ -3,11 +3,12 @@ # Model to store information captured when processing a form for an appeal remanded by CAVC class CavcRemand < CaseflowRecord + include UpdatedByUserConcern + belongs_to :created_by, class_name: "User" - belongs_to :updated_by, class_name: "User" belongs_to :appeal - validates :created_by, :updated_by, :appeal, :cavc_docket_number, :represented_by_attorney, :cavc_judge_full_name, + validates :created_by, :appeal, :cavc_docket_number, :represented_by_attorney, :cavc_judge_full_name, :cavc_decision_type, :decision_date, :decision_issue_ids, :instructions, presence: true validates :remand_subtype, presence: true, if: :remand? validates :judgement_date, :mandate_date, presence: true, unless: :mdr? diff --git a/app/models/docket.rb b/app/models/docket.rb index bfa2cc42ea6..de4cbfc0c80 100644 --- a/app/models/docket.rb +++ b/app/models/docket.rb @@ -44,7 +44,7 @@ def age_of_n_oldest_genpop_priority_appeals(num) end def age_of_oldest_priority_appeal - @age_of_oldest_priority_appeal ||= appeals(priority: true, ready: true).limit(1).first.ready_for_distribution_at + @age_of_oldest_priority_appeal ||= appeals(priority: true, ready: true).limit(1).first&.ready_for_distribution_at end def oldest_priority_appeal_days_waiting diff --git a/app/models/end_product_establishment.rb b/app/models/end_product_establishment.rb index 4f4a71ae7a3..222e8e1f518 100644 --- a/app/models/end_product_establishment.rb +++ b/app/models/end_product_establishment.rb @@ -312,9 +312,10 @@ def on_decision_issue_sync_processed(processing_request_issue) end def status + ep_code = Constants::EP_CLAIM_TYPES[code] if committed? { - ep_code: "#{modifier} #{Constants::EP_CLAIM_TYPES[code]['offical_label']}", + ep_code: "#{modifier} #{ep_code ? ep_code['offical_label'] : 'Unknown'}", ep_status: [status_type, sync_status].compact.join(", ") } else diff --git a/app/models/hearings/forms/base_hearing_update_form.rb b/app/models/hearings/forms/base_hearing_update_form.rb index 48c27f4e1a6..6bb326a8a90 100644 --- a/app/models/hearings/forms/base_hearing_update_form.rb +++ b/app/models/hearings/forms/base_hearing_update_form.rb @@ -14,7 +14,7 @@ class BaseHearingUpdateForm def update virtual_hearing_changed = false - ActiveRecord::Base.transaction do + ActiveRecord::Base.multi_transaction do update_hearing add_update_hearing_alert if show_update_alert? if should_create_or_update_virtual_hearing? diff --git a/app/models/legacy_appeal.rb b/app/models/legacy_appeal.rb index ebc35a1ad09..32fa4554d68 100644 --- a/app/models/legacy_appeal.rb +++ b/app/models/legacy_appeal.rb @@ -419,6 +419,7 @@ def sanitized_changed_request_type(changed_request_type) # values. Also, `hearing_request_type` alone can't disambiguate a video hearing # from a travel board hearing # This method cleans all of these issues up to return a sanitized version of the original type requested by Appellant. + # Replicated in UpdateCachedAppealAttributesJob#original_hearing_request_type_for_vacols_case def original_hearing_request_type(readable: false) original_hearing_request_type = case hearing_request_type when :central_office @@ -435,6 +436,7 @@ def original_hearing_request_type(readable: false) # This method captures if a travel board hearing request type was overridden in Caseflow. # In general, this method returns the current hearing request type which could be dervied # from `change_request_type` or VACOLS `hearing_request_type` + # Replicated in UpdateCachedAppealAttributesJob#case_fields_for_vacols_ids def current_hearing_request_type(readable: false) current_hearing_request_type = if changed_request_type.present? sanitized_changed_request_type(changed_request_type) diff --git a/app/models/promulgated_rating.rb b/app/models/promulgated_rating.rb index fcaaf7dbea5..cea50950a9b 100644 --- a/app/models/promulgated_rating.rb +++ b/app/models/promulgated_rating.rb @@ -82,6 +82,9 @@ def retry_fetching_rating_profile ) matching_rating = ratings_at_issue.find { |rating| profile_date_matches(rating.profile_date) } matching_rating.present? ? matching_rating.rating_profile : {} + rescue BGS::ShareError, Rating::NilRatingProfileListError => error + Raven.capture_exception(error) + {} end # The profile date is used as a key when fetching a rating by profile date. diff --git a/app/models/tasks/assign_hearing_disposition_task.rb b/app/models/tasks/assign_hearing_disposition_task.rb index 700bf74d49f..d905b99efec 100644 --- a/app/models/tasks/assign_hearing_disposition_task.rb +++ b/app/models/tasks/assign_hearing_disposition_task.rb @@ -69,9 +69,9 @@ def update_from_params(params, user) payload_values = params.delete(:business_payloads)&.dig(:values) if params[:status] == Constants.TASK_STATUSES.cancelled && payload_values[:disposition].present? - update_hearing_and_self(params: params, payload_values: payload_values) + created_tasks = update_hearing_and_self(params: params, payload_values: payload_values) - [self] + [self] + created_tasks else super(params, user) end @@ -82,15 +82,17 @@ def cancel! fail HearingDispositionNotCanceled end - if appeal.is_a? Appeal - EvidenceSubmissionWindowTask.find_or_create_by!( - appeal: appeal, - parent: hearing_task.parent, - assigned_to: MailTeam.singleton - ) - end + evidence_task = if appeal.is_a? Appeal + EvidenceSubmissionWindowTask.find_or_create_by!( + appeal: appeal, + parent: hearing_task.parent, + assigned_to: MailTeam.singleton + ) + end update!(status: Constants.TASK_STATUSES.cancelled, closed_at: Time.zone.now) + + [evidence_task].compact end def postpone! @@ -106,7 +108,7 @@ def no_show! fail HearingDispositionNotNoShow end - NoShowHearingTask.create_with_hold(self) + [NoShowHearingTask.create_with_hold(self)] end def hold! @@ -116,6 +118,8 @@ def hold! if appeal.is_a?(LegacyAppeal) update!(status: Constants.TASK_STATUSES.completed) + + [] # Not creating any tasks, just updating self else create_transcription_and_maybe_evidence_submission_window_tasks end @@ -135,21 +139,25 @@ def cascade_closure_from_child_task?(_child_task) end def update_hearing_and_self(params:, payload_values:) - case payload_values[:disposition] - when Constants.HEARING_DISPOSITION_TYPES.cancelled - mark_hearing_cancelled - when Constants.HEARING_DISPOSITION_TYPES.held - mark_hearing_held - when Constants.HEARING_DISPOSITION_TYPES.no_show - mark_hearing_no_show - when Constants.HEARING_DISPOSITION_TYPES.postponed - mark_hearing_postponed( - instructions: params["instructions"], - after_disposition_update: payload_values[:after_disposition_update] - ) - end + created_tasks = case payload_values[:disposition] + when Constants.HEARING_DISPOSITION_TYPES.cancelled + mark_hearing_cancelled + when Constants.HEARING_DISPOSITION_TYPES.held + mark_hearing_held + when Constants.HEARING_DISPOSITION_TYPES.no_show + mark_hearing_no_show + when Constants.HEARING_DISPOSITION_TYPES.postponed + mark_hearing_postponed( + instructions: params["instructions"], + after_disposition_update: payload_values[:after_disposition_update] + ) + else + fail ArgumentError, "unknown disposition" + end update_with_instructions(instructions: params[:instructions]) if params[:instructions].present? + + created_tasks end def update_hearing_disposition(disposition:) @@ -186,7 +194,7 @@ def reschedule(hearing_day_id:, scheduled_time_string:, hearing_location: nil, v .convert_hearing_to_virtual(new_hearing, virtual_hearing_attributes) end - self.class.create_assign_hearing_disposition_task!(appeal, new_hearing_task, new_hearing) + [new_hearing_task, self.class.create_assign_hearing_disposition_task!(appeal, new_hearing_task, new_hearing)] end end @@ -234,6 +242,8 @@ def reschedule_or_schedule_later(instructions: nil, after_disposition_update:) with_admin_action_klass: after_disposition_update[:with_admin_action_klass], admin_action_instructions: after_disposition_update[:admin_action_instructions] ) + else + fail ArgumentError, "unknown disposition action" end end @@ -245,20 +255,33 @@ def schedule_later(instructions: nil, with_admin_action_klass: nil, admin_action instructions: instructions.present? ? [instructions] : nil, parent: new_hearing_task ) - if with_admin_action_klass.present? - with_admin_action_klass.constantize.create!( - appeal: appeal, - assigned_to: HearingsManagement.singleton, - instructions: admin_action_instructions.present? ? [admin_action_instructions] : nil, - parent: schedule_task - ) - end + admin_action_task = if with_admin_action_klass.present? + with_admin_action_klass.constantize.create!( + appeal: appeal, + assigned_to: HearingsManagement.singleton, + instructions: admin_action_instructions.present? ? [admin_action_instructions] : nil, + parent: schedule_task + ) + end + + [new_hearing_task, schedule_task, admin_action_task].compact end def create_transcription_and_maybe_evidence_submission_window_tasks - TranscriptionTask.create!(appeal: appeal, parent: self, assigned_to: TranscriptionTeam.singleton) - unless hearing&.evidence_window_waived - EvidenceSubmissionWindowTask.create!(appeal: appeal, parent: self, assigned_to: MailTeam.singleton) - end + transcription_task = TranscriptionTask.create!( + appeal: appeal, + parent: self, + assigned_to: TranscriptionTeam.singleton + ) + + evidence_task = unless hearing&.evidence_window_waived + EvidenceSubmissionWindowTask.create!( + appeal: appeal, + parent: self, + assigned_to: MailTeam.singleton + ) + end + + [transcription_task, evidence_task].compact end end diff --git a/app/models/tasks/docket_switch/docket_switch_denied_task.rb b/app/models/tasks/docket_switch/docket_switch_denied_task.rb new file mode 100644 index 00000000000..3a0273ddbb7 --- /dev/null +++ b/app/models/tasks/docket_switch/docket_switch_denied_task.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class DocketSwitchDeniedTask < AttorneyTask + def available_actions(user) + actions = super(user) + + if ClerkOfTheBoard.singleton.user_has_access?(user) + if assigned_to.is_a?(User) && FeatureToggle.enabled?(:docket_change, user: user) + actions.push(Constants.TASK_ACTIONS.DOCKET_SWITCH_DENIED.to_h) + end + end + + actions + end + + class << self + def label + COPY::DOCKET_SWITCH_DENIED_TASK_LABEL + end + end +end diff --git a/app/models/tasks/docket_switch/docket_switch_granted_task.rb b/app/models/tasks/docket_switch/docket_switch_granted_task.rb new file mode 100644 index 00000000000..eedfa5c2a6c --- /dev/null +++ b/app/models/tasks/docket_switch/docket_switch_granted_task.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class DocketSwitchGrantedTask < AttorneyTask + def available_actions(user) + actions = super(user) + + if ClerkOfTheBoard.singleton.user_has_access?(user) + if assigned_to.is_a?(User) && FeatureToggle.enabled?(:docket_change, user: user) + actions.push(Constants.TASK_ACTIONS.DOCKET_SWITCH_GRANTED.to_h) + end + end + + actions + end + + class << self + def label + COPY::DOCKET_SWITCH_GRANTED_TASK_LABEL + end + end +end diff --git a/app/models/tasks/docket_switch_mail_task.rb b/app/models/tasks/docket_switch/docket_switch_mail_task.rb similarity index 100% rename from app/models/tasks/docket_switch_mail_task.rb rename to app/models/tasks/docket_switch/docket_switch_mail_task.rb diff --git a/app/models/tasks/docket_switch_ruling_task.rb b/app/models/tasks/docket_switch/docket_switch_ruling_task.rb similarity index 100% rename from app/models/tasks/docket_switch_ruling_task.rb rename to app/models/tasks/docket_switch/docket_switch_ruling_task.rb diff --git a/app/models/tasks/schedule_hearing_task.rb b/app/models/tasks/schedule_hearing_task.rb index ba2f2e42026..e8afde09797 100644 --- a/app/models/tasks/schedule_hearing_task.rb +++ b/app/models/tasks/schedule_hearing_task.rb @@ -44,24 +44,26 @@ def create_parent_hearing_task def update_from_params(params, current_user) multi_transaction do verify_user_can_update!(current_user) + + created_tasks = [] + if params[:status] == Constants.TASK_STATUSES.completed task_values = params.delete(:business_payloads)[:values] - multi_transaction do - hearing = create_hearing(task_values) + hearing = create_hearing(task_values) - if task_values[:virtual_hearing_attributes].present? - @alerts = VirtualHearings::ConvertToVirtualHearingService - .convert_hearing_to_virtual(hearing, task_values[:virtual_hearing_attributes]) - end - - AssignHearingDispositionTask.create_assign_hearing_disposition_task!(appeal, parent, hearing) + if task_values[:virtual_hearing_attributes].present? + @alerts = VirtualHearings::ConvertToVirtualHearingService + .convert_hearing_to_virtual(hearing, task_values[:virtual_hearing_attributes]) end + + created_tasks << AssignHearingDispositionTask.create_assign_hearing_disposition_task!(appeal, parent, hearing) elsif params[:status] == Constants.TASK_STATUSES.cancelled - withdraw_hearing + created_tasks << withdraw_hearing end - super(params, current_user) # returns [self] + # super returns [self] + super(params, current_user) + created_tasks.compact end end @@ -155,6 +157,7 @@ def withdraw_hearing AppealRepository.withdraw_hearing!(appeal) AppealRepository.update_location!(appeal, location) + nil else EvidenceSubmissionWindowTask.create!( appeal: appeal, diff --git a/app/models/vacols/case_hearing.rb b/app/models/vacols/case_hearing.rb index 26632555a15..46491819d11 100644 --- a/app/models/vacols/case_hearing.rb +++ b/app/models/vacols/case_hearing.rb @@ -3,7 +3,12 @@ class VACOLS::CaseHearing < VACOLS::Record self.table_name = "hearsched" self.primary_key = "hearing_pkseq" - self.sequence_name = "hearsched_pkseq" + + # :autogenerated allows a trigger to set the new sequence value for a primary key. + # + # COMPATIBILITY NOTE: Support for :autogenerated is dropped in Rails 6. + # See Issue: https://github.com/rsim/oracle-enhanced/issues/1643 + self.sequence_name = :autogenerated attribute :hearing_date, :datetime attribute :notes1, :ascii_string @@ -11,6 +16,8 @@ class VACOLS::CaseHearing < VACOLS::Record has_one :staff, foreign_key: :sattyid, primary_key: :board_member has_one :brieff, foreign_key: :bfkey, primary_key: :folder_nr, class_name: "Case" + has_one :folder, foreign_key: :ticknum, primary_key: :folder_nr + has_one :corres, foreign_key: :stafkey, primary_key: :bfcorkey, class_name: "Correspondent" HEARING_TYPE_LOOKUP = { central: "C", @@ -46,6 +53,7 @@ class VACOLS::CaseHearing < VACOLS::Record aod: :aod, transcript_requested: :tranreq, add_on: :addon, + add_time: :addtime, representative_name: :repname, staff_id: :mduser, room: :room, @@ -62,6 +70,9 @@ class VACOLS::CaseHearing < VACOLS::Record after_update :create_or_update_diaries class << self + # Finds all hearings for specific days. + # + # @deprecated Use {#where} def hearings_for_hearing_days(hearing_day_ids) select_hearings .where(vdkey: hearing_day_ids) @@ -69,6 +80,9 @@ def hearings_for_hearing_days(hearing_day_ids) .where.not(folder_nr: nil) end + # Finds all hearings for specific days that are assigned to a judge. + # + # @deprecated Use {#where} def hearings_for_hearing_days_assigned_to_judge(hearing_day_ids, judge) id = connection.quote(judge.css_id.upcase) @@ -79,6 +93,9 @@ def hearings_for_hearing_days_assigned_to_judge(hearing_day_ids, judge) .where.not(folder_nr: nil) end + # Finds all hearings for an appeal. + # + # @deprecated Use {#where} def for_appeal(appeal_vacols_id) select_hearings.where(folder_nr: appeal_vacols_id) end @@ -93,6 +110,9 @@ def for_appeals(vacols_ids) end end + # Finds a hearing by ID. + # + # @deprecated Use {#find}. def load_hearing(pkseq) select_hearings.find_by(hearing_pkseq: pkseq) end @@ -108,6 +128,11 @@ def create_hearing!(hearing_info) private + # Selects hearings and associated fields. + # + # @deprecated Use {#eager_load} when appropriate. This method creates attributes on the model that + # *don't* actually exist on the HEARSCHED table. It is better to load the associated data in a way + # that is idiomatic to Rails. def select_hearings # VACOLS overloads the HEARSCHED table with other types of hearings # that work differently. Filter those out. @@ -115,7 +140,7 @@ def select_hearings :hearing_disp, :hearing_pkseq, :hearing_date, :hearing_type, :notes1, :folder_nr, :vdkey, :aod, :holddays, :tranreq, :transent, - :repname, :addon, :board_member, :mduser, + :repname, :addon, :addtime, :board_member, :mduser, :mdtime, :sattyid, :bfregoff, :bfso, :bfcorkey, :bfddec, :bfdc, :room, :vdbvapoc, "staff.sdomainid as css_id", "brieff.bfac", "staff.slogid", diff --git a/app/repositories/hearing_repository.rb b/app/repositories/hearing_repository.rb index 56f66bc1b09..0296e5c3b5a 100644 --- a/app/repositories/hearing_repository.rb +++ b/app/repositories/hearing_repository.rb @@ -37,7 +37,7 @@ def update_vacols_hearing!(vacols_record, hearing_hash) end def create_vacols_hearing(hearing_day, appeal, scheduled_for, hearing_location_attrs) - VACOLS::CaseHearing.create_hearing!( + vacols_record = VACOLS::CaseHearing.create_hearing!( folder_nr: appeal.vacols_id, hearing_date: VacolsHelper.format_datetime_with_utc_timezone(scheduled_for), vdkey: hearing_day.id, @@ -47,12 +47,14 @@ def create_vacols_hearing(hearing_day, appeal, scheduled_for, hearing_location_a vdbvapoc: hearing_day.bva_poc ) - vacols_record = VACOLS::CaseHearing.for_appeal(appeal.vacols_id).where(vdkey: hearing_day.id) - .order(addtime: :desc).last - hearing = LegacyHearing.assign_or_create_from_vacols_record(vacols_record) - - hearing.update(hearing_location_attributes: hearing_location_attrs) unless hearing_location_attrs.nil? + # Reload the hearing to pull in associated data. + # Note: using `load_hearing` here is necessary to load in associated data that is not declared in + # the table (see `VACOLS::CaseHearing#select_hearings`). + vacols_record = VACOLS::CaseHearing.load_hearing(vacols_record.id) + hearing = LegacyHearing.assign_or_create_from_vacols_record(vacols_record) + hearing.hearing_location_attributes = hearing_location_attrs unless hearing_location_attrs.nil? + hearing.save! hearing end diff --git a/app/repositories/task_action_repository.rb b/app/repositories/task_action_repository.rb index 8053bb4f2db..c6c32b4f647 100644 --- a/app/repositories/task_action_repository.rb +++ b/app/repositories/task_action_repository.rb @@ -174,6 +174,18 @@ def sign_motion_to_vacate_data(_task, _user = nil) {} end + def docket_switch_denied_data(_task, _user = nil) + { + type: DocketSwitchDeniedTask.name + } + end + + def docket_switch_granted_data(_task, _user = nil) + { + type: DocketSwitchGrantedTask.name + } + end + def assign_to_translation_team_data(_task, _user = nil) org = Translation.singleton diff --git a/app/services/external_api/bgs_service.rb b/app/services/external_api/bgs_service.rb index 3a226fc4539..5eee0ee8c18 100644 --- a/app/services/external_api/bgs_service.rb +++ b/app/services/external_api/bgs_service.rb @@ -286,6 +286,8 @@ def bust_fetch_veteran_info_cache(vbms_id) def fetch_ratings_in_range(participant_id:, start_date:, end_date:) DBService.release_db_connections + start_date, end_date = formatted_start_and_end_dates(start_date, end_date) + MetricsService.record("BGS: fetch ratings in range: \ participant_id = #{participant_id}, \ start_date = #{start_date} \ @@ -315,6 +317,8 @@ def fetch_rating_profile(participant_id:, profile_date:) def fetch_rating_profiles_in_range(participant_id:, start_date:, end_date:) DBService.release_db_connections + start_date, end_date = formatted_start_and_end_dates(start_date, end_date) + MetricsService.record("BGS: fetch rating profile in range: \ participant_id = #{participant_id}, \ start_date = #{start_date}, \ @@ -462,5 +466,13 @@ def init_client log: true ) end + + def formatted_start_and_end_dates(start_date, end_date) + # start_date and end_date should be Dates with different values + return_start_date = start_date&.to_date + return_end_date = end_date&.to_date + return_end_date += 1.day if return_end_date.present? && return_end_date == return_start_date + [return_start_date, return_end_date] + end # :nocov: end diff --git a/app/services/hearing_task_tree_initializer.rb b/app/services/hearing_task_tree_initializer.rb index 1b347eca35f..fbed859125d 100644 --- a/app/services/hearing_task_tree_initializer.rb +++ b/app/services/hearing_task_tree_initializer.rb @@ -29,9 +29,9 @@ def for_appeal_with_pending_travel_board_hearing(appeal) create_args = { appeal: appeal, assigned_to: Bva.singleton } root_task = RootTask.find_or_create_by!(**create_args) - hearing_task = HearingTask.find_or_create_by!(**create_args, parent: root_task) - schedule_hearing_task = ScheduleHearingTask.find_or_create_by!(**create_args, parent: hearing_task) - ChangeHearingRequestTypeTask.find_or_create_by!(**create_args, parent: schedule_hearing_task) + hearing_task = HearingTask.create!(**create_args, parent: root_task) + schedule_hearing_task = ScheduleHearingTask.create!(**create_args, parent: hearing_task) + ChangeHearingRequestTypeTask.create!(**create_args, parent: schedule_hearing_task) end appeal.reload diff --git a/app/services/virtual_hearings/convert_to_virtual_hearing_service.rb b/app/services/virtual_hearings/convert_to_virtual_hearing_service.rb index f124589d35a..e15211022ac 100644 --- a/app/services/virtual_hearings/convert_to_virtual_hearing_service.rb +++ b/app/services/virtual_hearings/convert_to_virtual_hearing_service.rb @@ -21,12 +21,27 @@ def convert_hearing_to_virtual(hearing, virtual_hearing_attributes) form.update [{ hearing: form.hearing_alerts }, { virtual_hearing: form.virtual_hearing_alert }] + rescue ActiveRecord::RecordNotUnique => error + # :nocov: + raise wrap_error(error, 1003, COPY::VIRTUAL_HEARING_ALREADY_CREATED) + # :nocov: + rescue ActiveRecord::RecordInvalid => error + raise wrap_error(error, 1002, error.message) rescue StandardError => error - raise( - Caseflow::Error::VirtualHearingConversionFailed, + # :nocov: + raise wrap_error(error, 1099, error.message) + # :nocov: + end + + private + + # Wraps an error in the class `Caseflow::Error::VirtualHearingConversionFailed`, allowing + # errors thrown by this class to be handled with specialized logic. + def wrap_error(error, code, message) + Caseflow::Error::VirtualHearingConversionFailed.new( error_type: error.class, - message: error.message, - code: 1002 + message: message, + code: code ) end end diff --git a/client/.eslintrc.js b/client/.eslintrc.js index 778f98ed00b..71f940b059a 100644 --- a/client/.eslintrc.js +++ b/client/.eslintrc.js @@ -41,6 +41,9 @@ module.exports = { version: '16.12', }, 'import/resolver': { + webpack: { + config: './webpack.config.js' + }, node: { extensions: ['.js', '.jsx', '.json'], }, diff --git a/client/.storybook/main.js b/client/.storybook/main.js index 13a918d073b..db368ec0f23 100644 --- a/client/.storybook/main.js +++ b/client/.storybook/main.js @@ -20,6 +20,13 @@ module.exports = { return { ...config, + resolve: { + ...config.resolve, + alias: { + ...config.resolve.alias, + ...custom.resolve.alias + } + }, module: { ...config.module, rules: [...config.module.rules, ...customRules], diff --git a/client/COPY.json b/client/COPY.json index 2b18a0ceb16..411fdc00d28 100644 --- a/client/COPY.json +++ b/client/COPY.json @@ -579,6 +579,8 @@ "CONTROLLED_CORRESPONDENCE_MAIL_TASK_LABEL": "Controlled correspondence", "DEATH_CERTIFICATE_MAIL_TASK_LABEL": "Death certificate", "DOCKET_SWITCH_MAIL_TASK_LABEL": "Docket Switch", + "DOCKET_SWITCH_DENIED_TASK_LABEL": "Denied Docket Switch", + "DOCKET_SWITCH_GRANTED_TASK_LABEL": "Granted Docket Switch", "EVIDENCE_OR_ARGUMENT_MAIL_TASK_LABEL": "Evidence or argument", "EXTENSION_REQUEST_MAIL_TASK_LABEL": "Extension request", "FOIA_REQUEST_MAIL_TASK_LABEL": "FOIA request", diff --git a/client/app/components/ReduxBase.jsx b/client/app/components/ReduxBase.jsx index 5c6430b2a25..ab2865c9692 100644 --- a/client/app/components/ReduxBase.jsx +++ b/client/app/components/ReduxBase.jsx @@ -1,16 +1,15 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Provider } from 'react-redux'; -import { createStore, applyMiddleware, compose } from 'redux'; +import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'; import perfLogger from 'redux-perf-middleware'; -import thunk from 'redux-thunk'; import { getReduxAnalyticsMiddleware } from '../util/ReduxUtil'; -const setupStore = ({ reducer, initialState, analyticsMiddlewareArgs, enhancers }) => { - // eslint-disable-next-line no-underscore-dangle - const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; - - const middleware = [thunk, getReduxAnalyticsMiddleware(...analyticsMiddlewareArgs)]; +const setupStore = ({ reducer, initialState, analyticsMiddlewareArgs }) => { + const middleware = [ + ...getDefaultMiddleware({ immutableCheck: false }), + getReduxAnalyticsMiddleware(...analyticsMiddlewareArgs), + ]; // Some middleware should be skipped in test scenarios. Normally I wouldn't leave a comment // like this, but we had a bug where we accidentally added essential middleware here and it @@ -20,17 +19,29 @@ const setupStore = ({ reducer, initialState, analyticsMiddlewareArgs, enhancers middleware.push(perfLogger); } - const composedEnhancers = composeEnhancers(applyMiddleware(...middleware), ...enhancers); - - const store = createStore(reducer, initialState, composedEnhancers); + const store = configureStore({ + reducer, + preloadedState: initialState, + middleware, + }); return store; }; export default function ReduxBase(props) { - const { children, reducer, initialState, enhancers, analyticsMiddlewareArgs, getStoreRef } = props; + const { + children, + reducer, + initialState, + analyticsMiddlewareArgs, + getStoreRef, + } = props; - const store = setupStore({ reducer, initialState, enhancers, analyticsMiddlewareArgs }); + const store = setupStore({ + reducer, + initialState, + analyticsMiddlewareArgs, + }); // Dispatch relies on direct access to the store. It would be better to use connect(), // but for now, we will expose this to grant that access. @@ -42,17 +53,18 @@ export default function ReduxBase(props) { } ReduxBase.propTypes = { - children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired, + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + ]).isRequired, reducer: PropTypes.func, initialState: PropTypes.object, - enhancers: PropTypes.array, analyticsMiddlewareArgs: PropTypes.array, - getStoreRef: PropTypes.func + getStoreRef: PropTypes.func, }; ReduxBase.defaultProps = { analyticsMiddlewareArgs: [], // eslint-disable-next-line no-empty-function getStoreRef: () => {}, - enhancers: [] }; diff --git a/client/app/hearings/components/ScheduleVeteran.jsx b/client/app/hearings/components/ScheduleVeteran.jsx index b7807b1a6c0..703824ce093 100644 --- a/client/app/hearings/components/ScheduleVeteran.jsx +++ b/client/app/hearings/components/ScheduleVeteran.jsx @@ -7,6 +7,7 @@ import { withRouter } from 'react-router-dom'; import AppSegment from '@department-of-veterans-affairs/caseflow-frontend-toolkit/components/AppSegment'; import Button from '../../components/Button'; import { sprintf } from 'sprintf-js'; +import { isNil, maxBy, omit, find, get } from 'lodash'; import TASK_STATUSES from '../../../constants/TASK_STATUSES'; import COPY from '../../../COPY'; @@ -17,7 +18,6 @@ import { onReceiveAppealDetails } from '../../queue/QueueActions'; import { formatDateStr } from '../../util/DateUtil'; import Alert from '../../components/Alert'; import { setMargin, marginTop, regionalOfficeSection, saveButton, cancelButton } from './details/style'; -import { find, get } from 'lodash'; import { getAppellantTitle, processAlerts, parseVirtualHearingErrors } from '../utils'; import { onChangeFormData, @@ -100,9 +100,6 @@ export const ScheduleVeteran = ({ // Reset the component state const reset = () => { - // Clear the loading state - setLoading(false); - // Clear any lingering errors setErrors({}); @@ -150,12 +147,19 @@ export const ScheduleVeteran = ({ // Format the payload for the API const getPayload = () => { + // The API can't accept a payload if the field `status` is provided because it is a generated + // (not editable) field. + // + // `omit` returns an empty object if `null` is provided as an argument, so the `isNil` check here + // prevents `omit` from returning an empty object.` + const virtualHearing = isNil(hearing.virtualHearing) ? null : omit(hearing.virtualHearing, ['status']); + // Format the shared hearing values const hearingValues = { scheduled_time_string: hearing.scheduledTimeString, hearing_day_id: hearing.hearingDay.hearingId, hearing_location: hearing.hearingLocation ? ApiUtil.convertToSnakeCase(hearing.hearingLocation) : null, - virtual_hearing_attributes: hearing.virtualHearing ? ApiUtil.convertToSnakeCase(hearing.virtualHearing) : null, + virtual_hearing_attributes: virtualHearing ? ApiUtil.convertToSnakeCase(virtualHearing) : null, }; // Determine whether to send the reschedule payload @@ -212,12 +216,17 @@ export const ScheduleVeteran = ({ // Patch the hearing task with the form data const { body } = await ApiUtil.patch(`/tasks/${taskId}`, payload); - const [task] = body?.tasks?.data?.filter((hearingTask) => hearingTask.id === taskId); + // Find the most recently created AssignHearingDispositionTask. This task will have the ID of the + // most recently created hearing. + const mostRecentTask = maxBy( + body?.tasks?.data?.filter((task) => task.attributes?.type === 'AssignHearingDispositionTask') ?? [], + (task) => task.id + ); const alerts = body?.tasks?.alerts; if (alerts && hearing.virtualHearing) { - processAlerts(alerts, props, () => props.startPollingHearing(task?.attributes?.external_hearing_id)); + processAlerts(alerts, props, () => props.startPollingHearing(mostRecentTask?.attributes?.external_hearing_id)); } else { props.showSuccessMessage(getSuccessMsg()); } @@ -240,14 +249,11 @@ export const ScheduleVeteran = ({ setErrors(errList); - // Remove the loading state on error - setLoading(false); - // Handle errors in the standard format } else if (msg?.title) { props.showErrorMessage({ title: msg?.title, - detail: msg?.detail + detail: msg?.detail ?? msg?.message }); // Handle legacy appeals @@ -266,6 +272,9 @@ export const ScheduleVeteran = ({ 'Please contact the Caseflow Team for assistance.', }); } + } finally { + // Clear the loading state + setLoading(false); } }; diff --git a/client/constants/DIAGNOSTIC_CODE_DESCRIPTIONS.json b/client/constants/DIAGNOSTIC_CODE_DESCRIPTIONS.json index ad50681d8c6..8fb0adf3448 100644 --- a/client/constants/DIAGNOSTIC_CODE_DESCRIPTIONS.json +++ b/client/constants/DIAGNOSTIC_CODE_DESCRIPTIONS.json @@ -131,6 +131,22 @@ "staff_description": "Other musculoskeletal disease", "status_description": "musculoskeletal disease" }, + "5100": { + "staff_description": "Anatomical loss of hands and feet", + "status_description": "Anatomical loss of hands and feet" + }, + "5101": { + "staff_description": "Loss of Use of Hands and Feet", + "status_description": "Loss of Use of Hands and Feet" + }, + "5102": { + "staff_description": "Anatomical loss of both hands and one foot", + "status_description": "Anatomical loss of both hands and one foot" + }, + "5103": { + "staff_description": "Anatomical loss of both feet and one hand", + "status_description": "Anatomical loss of both feet and one hand" + }, "5104": { "staff_description": "Anatomical loss of one hand and loss of use of one foot", "status_description": "loss of hand and loss of use of foot" @@ -359,6 +375,10 @@ "staff_description": "Toes, three or four, amputation of, without metatarsal involvement", "status_description": "amputation of toes" }, + "5174": { + "staff_description": "Hip prosthesis", + "status_description": "Hip prosthesis" + }, "5199": { "staff_description": "Other amputations", "status_description": "amputation" @@ -575,6 +595,10 @@ "staff_description": "Genu recurvatum", "status_description": "knee hyperextension" }, + "5264": { + "staff_description": "Prosthesis for knee", + "status_description": "Prosthesis for knee" + }, "5270": { "staff_description": "Ankle, ankylosis of", "status_description": "ankylosis of ankle" @@ -959,6 +983,70 @@ "staff_description": "Keratoconus", "status_description": "keratoconus" }, + "6036": { + "staff_description": "Status Post Corneal Transplant", + "status_description": "Status Post Corneal Transplant" + }, + "6037": { + "staff_description": "Pinguecula, Spot On White of Eye", + "status_description": "Pinguecula, Spot On White of Eye" + }, + "6040": { + "staff_description": "Diabetic retinopathy", + "status_description": "Diabetic retinopathy" + }, + "6042": { + "staff_description": "Retinal dystrophy", + "status_description": "Retinal dystrophy" + }, + "6046": { + "staff_description": "Post-chiasmal disorders", + "status_description": "Post-chiasmal disorders" + }, + "6050": { + "staff_description": "(Covered by SMC Codes)", + "status_description": "(Covered by SMC Codes)" + }, + "6051": { + "staff_description": "(Covered by SMC Codes)", + "status_description": "(Covered by SMC Codes)" + }, + "6052": { + "staff_description": "(Covered by SMC Codes)", + "status_description": "(Covered by SMC Codes)" + }, + "6053": { + "staff_description": "(Covered by SMC Codes)", + "status_description": "(Covered by SMC Codes)" + }, + "6054": { + "staff_description": "(Covered by SMC Codes)", + "status_description": "(Covered by SMC Codes)" + }, + "6055": { + "staff_description": "(Covered by SMC Codes)", + "status_description": "(Covered by SMC Codes)" + }, + "6056": { + "staff_description": "(Covered by SMC Codes)", + "status_description": "(Covered by SMC Codes)" + }, + "6057": { + "staff_description": "(Covered by SMC Codes)", + "status_description": "(Covered by SMC Codes)" + }, + "6058": { + "staff_description": "(Covered by SMC Codes)", + "status_description": "(Covered by SMC Codes)" + }, + "6059": { + "staff_description": "(Covered by SMC Codes)", + "status_description": "(Covered by SMC Codes)" + }, + "6060": { + "staff_description": "(Covered by SMC Codes)", + "status_description": "(Covered by SMC Codes)" + }, "6061": { "staff_description": "Anatomical loss both eyes", "status_description": "loss of both eyes" @@ -1063,6 +1151,46 @@ "staff_description": "Hearing loss", "status_description": "hearing loss" }, + "6101": { + "staff_description": "Hearing loss", + "status_description": "Hearing loss" + }, + "6102": { + "staff_description": "Hearing loss", + "status_description": "Hearing loss" + }, + "6103": { + "staff_description": "Hearing loss", + "status_description": "Hearing loss" + }, + "6104": { + "staff_description": "Hearing loss", + "status_description": "Hearing loss" + }, + "6105": { + "staff_description": "Hearing loss", + "status_description": "Hearing loss" + }, + "6106": { + "staff_description": "Hearing loss", + "status_description": "Hearing loss" + }, + "6107": { + "staff_description": "Hearing loss", + "status_description": "Hearing loss" + }, + "6108": { + "staff_description": "Hearing loss", + "status_description": "Hearing loss" + }, + "6109": { + "staff_description": "Hearing loss", + "status_description": "Hearing loss" + }, + "6110": { + "staff_description": "Hearing loss", + "status_description": "Hearing loss" + }, "6199": { "staff_description": "Other hearing loss", "status_description": "hearing loss" @@ -1079,13 +1207,21 @@ "staff_description": "Otosclerosis", "status_description": "otosclerosis" }, + "6203": { + "staff_description": "Otitis interna", + "status_description": "Otitis interna" + }, "6204": { "staff_description": "Peripheral vestibular disorders", "status_description": "inner ear or vestibular nerve disorder" }, "6205": { "staff_description": "Meniere's syndrome", - "status_description": "M\u00e9ni\u00e8re's disease" + "status_description": "Ménière's disease" + }, + "6206": { + "staff_description": "Mastoiditis", + "status_description": "Mastoiditis" }, "6207": { "staff_description": "Auricle, loss or deformity", @@ -1107,10 +1243,58 @@ "staff_description": "Tympanic membrane, perforation of", "status_description": "perforation of tympanic membrane" }, + "6250": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6251": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6252": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6253": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6254": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6255": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6256": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6257": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6258": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, "6260": { "staff_description": "Tinnitus, recurrent", "status_description": "tinnitus" }, + "6261": { + "staff_description": "Ear condition", + "status_description": "Ear condition" + }, + "6262": { + "staff_description": "Ear condition", + "status_description": "Ear condition" + }, + "6263": { + "staff_description": "Ear condition", + "status_description": "Ear condition" + }, "6275": { "staff_description": "Loss of sense of smell, complete", "status_description": "loss of sense of smell" @@ -1119,6 +1303,90 @@ "staff_description": "Loss of sense of taste, complete", "status_description": "loss of sense of taste" }, + "6277": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6278": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6279": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6280": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6281": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6282": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6283": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6284": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6285": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6286": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6287": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6288": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6289": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6290": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6291": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6292": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6293": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6294": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6295": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6296": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, + "6297": { + "staff_description": "Impaired hearing", + "status_description": "Impaired hearing" + }, "6299": { "staff_description": "Other sense organ disability", "status_description": "sense organ disability" @@ -1167,6 +1435,10 @@ "staff_description": "Tuberculosis, miliary", "status_description": "miliary tuberculosis" }, + "6312": { + "staff_description": "Nontuberculosis mycobacterium infection", + "status_description": "Nontuberculosis mycobacterium infection" + }, "6313": { "staff_description": "Avitaminosis", "status_description": "avitaminosis" @@ -1199,6 +1471,38 @@ "staff_description": "Parasitic diseases otherwise not specified", "status_description": "parasitic disease" }, + "6325": { + "staff_description": "Disseminated Strongyloidiasis", + "status_description": "Disseminated Strongyloidiasis" + }, + "6326": { + "staff_description": "Schistosomiasis", + "status_description": "Schistosomiasis" + }, + "6329": { + "staff_description": "Dengue", + "status_description": "Dengue" + }, + "6330": { + "staff_description": "Campylobacter jejuni infection", + "status_description": "Campylobacter jejuni infection" + }, + "6331": { + "staff_description": "Coxiella burnetii infection (Q fever)", + "status_description": "Coxiella burnetii infection (Q fever)" + }, + "6333": { + "staff_description": "Nontyphoid Salmonella infections", + "status_description": "Nontyphoid Salmonella infections" + }, + "6334": { + "staff_description": "Shigella infections", + "status_description": "Shigella infections" + }, + "6335": { + "staff_description": "West Nile virus infection", + "status_description": "West Nile virus infection" + }, "6350": { "staff_description": "Lupus erythematosus, systemic (disseminated)", "status_description": "lupus" @@ -1207,6 +1511,14 @@ "staff_description": "HIV-Related Illness", "status_description": "HIV-related illness" }, + "6352": { + "staff_description": "Aids related complex", + "status_description": "Aids related complex" + }, + "6353": { + "staff_description": "Positive blood test for AIDS antibody", + "status_description": "Positive blood test for AIDS antibody" + }, "6354": { "staff_description": "Chronic Fatigue Syndrome (CFS)", "status_description": "chronic fatigue syndrome" @@ -1215,6 +1527,10 @@ "staff_description": "Other infectious disease, immune disorder, or nutritional deficiency", "status_description": "infectious disease immune disorder or nutritional deficiency" }, + "6501": { + "staff_description": "Rhinitis", + "status_description": "Rhinitis" + }, "6502": { "staff_description": "Septum, nasal, deviation of", "status_description": "deviated septum" @@ -1251,6 +1567,10 @@ "staff_description": "Laryngitis, chronic", "status_description": "chronic laryngitis" }, + "6517": { + "staff_description": "Residuals of injured larynx", + "status_description": "Residuals of injured larynx" + }, "6518": { "staff_description": "Laryngectomy, total", "status_description": "laryngectomy" @@ -1323,6 +1643,46 @@ "staff_description": "Tuberculosis, pulmonary, chronic, active, advancement unspecified [entitled 8/19/68]", "status_description": "pulmonary tuberculosis" }, + "6705": { + "staff_description": "Lung Condition", + "status_description": "Lung Condition" + }, + "6706": { + "staff_description": "Lung Condition", + "status_description": "Lung Condition" + }, + "6707": { + "staff_description": "Active Pulmonary Tuberculosis", + "status_description": "Active Pulmonary Tuberculosis" + }, + "6708": { + "staff_description": "Active Pulmonary Tuberculosis", + "status_description": "Active Pulmonary Tuberculosis" + }, + "6709": { + "staff_description": "Active Pulmonary Tuberculosis", + "status_description": "Active Pulmonary Tuberculosis" + }, + "6710": { + "staff_description": "Active Pulmonary Tuberculosis", + "status_description": "Active Pulmonary Tuberculosis" + }, + "6711": { + "staff_description": "Lung condition", + "status_description": "Lung condition" + }, + "6712": { + "staff_description": "Lung condition", + "status_description": "Lung condition" + }, + "6713": { + "staff_description": "Lung condition", + "status_description": "Lung condition" + }, + "6714": { + "staff_description": "Lung condition", + "status_description": "Lung condition" + }, "6721": { "staff_description": "Tuberculosis, pulmonary, chronic, far advanced, inactive [entitled 8/19/68]", "status_description": "pulmonary tuberculosis" @@ -1339,6 +1699,22 @@ "staff_description": "Tuberculosis, pulmonary, chronic, inactive, advancement unspecified [entitled 8/19/68]", "status_description": "pulmonary tuberculosis" }, + "6725": { + "staff_description": "Inactive Pulmonary Tuberculosis", + "status_description": "Inactive Pulmonary Tuberculosis" + }, + "6726": { + "staff_description": "Inactive Pulmonary Tuberculosis", + "status_description": "Inactive Pulmonary Tuberculosis" + }, + "6727": { + "staff_description": "Inactive Pulmonary Tuberculosis", + "status_description": "Inactive Pulmonary Tuberculosis" + }, + "6728": { + "staff_description": "Inactive Pulmonary Tuberculosis", + "status_description": "Inactive Pulmonary Tuberculosis" + }, "6730": { "staff_description": "Tuberculosis, pulmonary, chronic, active [after 8/19/68]", "status_description": "pulmonary tuberculosis" @@ -1355,10 +1731,82 @@ "staff_description": "Other tuberculous disease of lungs and/or pleura", "status_description": "tuberculous disease" }, + "6800": { + "staff_description": "Anthracosis", + "status_description": "Anthracosis" + }, + "6801": { + "staff_description": "Silicosis", + "status_description": "Silicosis" + }, + "6802": { + "staff_description": "Pneumoconiosis, unspecified", + "status_description": "Pneumoconiosis, unspecified" + }, + "6803": { + "staff_description": "Actinomycosis of Lung", + "status_description": "Actinomycosis of Lung" + }, + "6804": { + "staff_description": "Streptotrichosis of Lung", + "status_description": "Streptotrichosis of Lung" + }, + "6805": { + "staff_description": "Blastomycosis of Lung", + "status_description": "Blastomycosis of Lung" + }, + "6806": { + "staff_description": "Sporotrichosis of Lung", + "status_description": "Sporotrichosis of Lung" + }, + "6807": { + "staff_description": "Spergillosis of Lung", + "status_description": "Spergillosis of Lung" + }, + "6808": { + "staff_description": "Mycosis of Lung", + "status_description": "Mycosis of Lung" + }, + "6809": { + "staff_description": "Abcess of Lung", + "status_description": "Abcess of Lung" + }, + "6810": { + "staff_description": "Pleurisy", + "status_description": "Pleurisy" + }, + "6811": { + "staff_description": "Pleurisy", + "status_description": "Pleurisy" + }, + "6812": { + "staff_description": "Fistula of Lung", + "status_description": "Fistula of Lung" + }, + "6813": { + "staff_description": "Collapsed Lung", + "status_description": "Collapsed Lung" + }, + "6814": { + "staff_description": "Spontaneous Collapsed Lung", + "status_description": "Spontaneous Collapsed Lung" + }, + "6815": { + "staff_description": "Removal of Lung", + "status_description": "Removal of Lung" + }, + "6816": { + "staff_description": "Partial Removal of Lung", + "status_description": "Partial Removal of Lung" + }, "6817": { "staff_description": "Pulmonary Vascular Disease", "status_description": "pulmonary vascular disease" }, + "6818": { + "staff_description": "Residuals of lung injury", + "status_description": "Residuals of lung injury" + }, "6819": { "staff_description": "Neoplasms, malignant, any specified part of respiratory system exclusive of skin growths", "status_description": "malignant neoplasm of the respiratory system" @@ -1367,6 +1815,10 @@ "staff_description": "Neoplasms, benign, any specified part of respiratory system", "status_description": "benign neoplasm of the respiratory system" }, + "6821": { + "staff_description": "Infection of the Lung", + "status_description": "Infection of the Lung" + }, "6822": { "staff_description": "Actinomycosis", "status_description": "actinomycosis" @@ -1519,6 +1971,18 @@ "staff_description": "Ventricular arrhythmias (sustained)", "status_description": "ventricular arrhythmias" }, + "7012": { + "staff_description": "Heart Condition", + "status_description": "Heart Condition" + }, + "7013": { + "staff_description": "Heart Condition", + "status_description": "Heart Condition" + }, + "7014": { + "staff_description": "Rapid pulse of the heart", + "status_description": "Rapid pulse of the heart" + }, "7015": { "staff_description": "Atrioventricular block", "status_description": "atrioventricular block" @@ -1547,6 +2011,10 @@ "staff_description": "Other heart disease", "status_description": "heart disease" }, + "7100": { + "staff_description": "Arteriosclerosis", + "status_description": "Arteriosclerosis" + }, "7101": { "staff_description": "Hypertensive vascular disease (hypertension and isolated systolic hypertension)", "status_description": "hypertensive vascular disease" @@ -1575,6 +2043,10 @@ "staff_description": "Thrombo-angiitis obliterans (Buerger's Disease)", "status_description": "Buerger's disease" }, + "7116": { + "staff_description": "Claudication", + "status_description": "Claudication" + }, "7117": { "staff_description": "Raynaud's syndrome", "status_description": "Raynaud's disease" @@ -1639,6 +2111,10 @@ "staff_description": "Peritoneum, adhesions of", "status_description": "peritoneal adhesions" }, + "7302": { + "staff_description": "Ulcer condition", + "status_description": "Ulcer condition" + }, "7304": { "staff_description": "Ulcer, gastric", "status_description": "ulcer" @@ -1703,6 +2179,10 @@ "staff_description": "Irritable colon syndrome (spastic colitis, mucous colitis, etc.)", "status_description": "irritable bowel syndrome" }, + "7320": { + "staff_description": "Intestinal condition", + "status_description": "Intestinal condition" + }, "7321": { "staff_description": "Amebiasis", "status_description": "amebiasis" @@ -1783,6 +2263,10 @@ "staff_description": "Hernia, femoral", "status_description": "femoral hernia" }, + "7341": { + "staff_description": "Residuals of abdominal wounds", + "status_description": "Residuals of abdominal wounds" + }, "7342": { "staff_description": "Visceroptosis, symptomatic, marked", "status_description": "visceroptosis" @@ -1835,6 +2319,10 @@ "staff_description": "Nephritis, chronic", "status_description": "chronic nephritis" }, + "7503": { + "staff_description": "Pyelitis", + "status_description": "Pyelitis" + }, "7504": { "staff_description": "Pyelonephritis, chronic", "status_description": "chronic pyelonephritis" @@ -1843,6 +2331,10 @@ "staff_description": "Kidney, tuberculosis of", "status_description": "tuberculosis of the kidney" }, + "7506": { + "staff_description": "Kidney condition", + "status_description": "Kidney condition" + }, "7507": { "staff_description": "Nephrosclerosis, arteriolar", "status_description": "arteriolar nephrosclerosis" @@ -1867,6 +2359,14 @@ "staff_description": "Cystitis, chronic, includes interstitial and all etiologies, infectious and non-infectious", "status_description": "chornic cystitis" }, + "7513": { + "staff_description": "Cystitis", + "status_description": "Cystitis" + }, + "7514": { + "staff_description": "Tuberculosis of the bladder", + "status_description": "Tuberculosis of the bladder" + }, "7515": { "staff_description": "Bladder, calculus in, with symptoms interfering with function", "status_description": "bladder stone" @@ -1911,6 +2411,10 @@ "staff_description": "Epididymo-orchitis, chronic only", "status_description": "chronic epididymo-orchitis" }, + "7526": { + "staff_description": "Removal of the prostate gland", + "status_description": "Removal of the prostate gland" + }, "7527": { "staff_description": "Prostate gland injuries, infections, hypertrophy, postoperative residuals", "status_description": "prostate gland injury" @@ -2055,6 +2559,14 @@ "staff_description": "Endometriosis", "status_description": "endometriosis" }, + "7630": { + "staff_description": "Malignant neoplasms of the breast", + "status_description": "Malignant neoplasms of the breast" + }, + "7631": { + "staff_description": "Benign neoplasms of the breast", + "status_description": "Benign neoplasms of the breast" + }, "7632": { "staff_description": "Female Sexual Arousal Disorder (FSAD)", "status_description": "female sexual arousal disorder" @@ -2067,6 +2579,10 @@ "staff_description": "Anemia, hypochromic-microcytic and megaloblastic", "status_description": "microcytic or megaloblastic anemia" }, + "7701": { + "staff_description": "Secondary Anemia", + "status_description": "Secondary Anemia" + }, "7702": { "staff_description": "Agranulocytosis, acute", "status_description": "agranulocytosis" @@ -2099,10 +2615,18 @@ "staff_description": "Adenitis, tuberculous, active or inactive", "status_description": "tuberculous lymphadenitis" }, + "7711": { + "staff_description": "Axillary adenitis", + "status_description": "Axillary adenitis" + }, "7712": { "staff_description": "Inguinal adenitis", "status_description": "multiple myeloma" }, + "7713": { + "staff_description": "Secondary adenitis", + "status_description": "Secondary adenitis" + }, "7714": { "staff_description": "Sickle cell anemia", "status_description": "sickle cell anemia" @@ -2115,6 +2639,42 @@ "staff_description": "Aplastic anemia", "status_description": "aplastic anemia" }, + "7717": { + "staff_description": "Primary (AL) amyloidosis", + "status_description": "Primary (AL) amyloidosis" + }, + "7718": { + "staff_description": "Essential thrombocythemia/Primary myelofibrosis", + "status_description": "Essential thrombocythemia/Primary myelofibrosis" + }, + "7719": { + "staff_description": "Chronic Myelogenous Leukemia (CML)", + "status_description": "Chronic Myelogenous Leukemia (CML)" + }, + "7720": { + "staff_description": "Iron Deficiency Anemia", + "status_description": "Iron Deficiency Anemia" + }, + "7721": { + "staff_description": "Folic Acid Deficiency", + "status_description": "Folic Acid Deficiency" + }, + "7722": { + "staff_description": "Pernicious Anemia/Vitamin B12 Deficiency Anemia", + "status_description": "Pernicious Anemia/Vitamin B12 Deficiency Anemia" + }, + "7723": { + "staff_description": "Acquired Hemolytic Anemia", + "status_description": "Acquired Hemolytic Anemia" + }, + "7724": { + "staff_description": "Solitary Plasmacytoma", + "status_description": "Solitary Plasmacytoma" + }, + "7725": { + "staff_description": "Myelodysplastic Syndromes", + "status_description": "Myelodysplastic Syndromes" + }, "7799": { "staff_description": "Other hemic or lymphatic system disability", "status_description": "hemic or lymphatic disability" @@ -2283,6 +2843,10 @@ "staff_description": "Hypoparathyroidism", "status_description": "hypoparathyroidism" }, + "7906": { + "staff_description": "Thyroiditis", + "status_description": "Thyroiditis" + }, "7907": { "staff_description": "Cushing's syndrome", "status_description": "Cushing's syndrome" @@ -2295,6 +2859,10 @@ "staff_description": "Diabetes insipidus", "status_description": "diabetes insipidus" }, + "7910": { + "staff_description": "Adrenal condition", + "status_description": "Adrenal condition" + }, "7911": { "staff_description": "Addison's disease (Adrenal Cortical Hypofunction)", "status_description": "Addison's disease" @@ -2339,6 +2907,10 @@ "staff_description": "Encephalitis, epidemic, chronic", "status_description": "chronic epidemic encephalitis" }, + "8001": { + "staff_description": "Condition of the brain", + "status_description": "Condition of the brain" + }, "8002": { "staff_description": "Brain, new growth of, malignant", "status_description": "malignant brain growth" @@ -2427,6 +2999,10 @@ "staff_description": "Myasthenia gravis", "status_description": "myasthenia gravis" }, + "8026": { + "staff_description": "Brain condition", + "status_description": "Brain condition" + }, "8045": { "staff_description": "Brain disease due to trauma", "status_description": "brain disease due to trauma" @@ -2963,6 +3539,18 @@ "staff_description": "Undiagnosed condition, dental and oral", "status_description": "undiagnosed dental or oral condition" }, + "8900": { + "staff_description": "Seizure disorder", + "status_description": "Seizure disorder" + }, + "8901": { + "staff_description": "- Seizure disorder", + "status_description": "- Seizure disorder" + }, + "8902": { + "staff_description": "- Seizure disorder", + "status_description": "- Seizure disorder" + }, "8910": { "staff_description": "Epilepsy, grand mal", "status_description": "epilepsy" @@ -2987,6 +3575,282 @@ "staff_description": "Other epilepsy", "status_description": "epilepsy" }, + "9000": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9001": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9002": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9003": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9004": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9005": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9006": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9007": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9008": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9009": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9010": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9011": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9012": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9013": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9014": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9015": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9016": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9017": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9018": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9019": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9020": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9021": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9022": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9023": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9024": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9025": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9026": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9027": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9028": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9029": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9030": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9031": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9032": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9033": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9034": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9035": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9036": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9037": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9038": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9039": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9040": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9041": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9042": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9043": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9044": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9045": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9046": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9047": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9048": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9049": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9050": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9051": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9052": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9053": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9054": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9055": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9099": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, + "9100": { + "staff_description": "Neurosis", + "status_description": "Neurosis" + }, + "9101": { + "staff_description": "Neurosis", + "status_description": "Neurosis" + }, + "9102": { + "staff_description": "Neurosis", + "status_description": "Neurosis" + }, + "9103": { + "staff_description": "Neurosis", + "status_description": "Neurosis" + }, + "9104": { + "staff_description": "Neurosis", + "status_description": "Neurosis" + }, + "9105": { + "staff_description": "Neurosis", + "status_description": "Neurosis" + }, + "9106": { + "staff_description": "Neurosis", + "status_description": "Neurosis" + }, + "9110": { + "staff_description": "Psychophysiologic heart disorder", + "status_description": "Psychophysiologic heart disorder" + }, + "9111": { + "staff_description": "Psychophysiologic heart disorder", + "status_description": "Psychophysiologic heart disorder" + }, + "9112": { + "staff_description": "Psychophysiologic stomach disorder", + "status_description": "Psychophysiologic stomach disorder" + }, + "9199": { + "staff_description": "Nervous condition", + "status_description": "Nervous condition" + }, + "9200": { + "staff_description": "Psychosis", + "status_description": "Psychosis" + }, "9201": { "staff_description": "Schizophrenia, disorganized type", "status_description": "schizophrenia" @@ -3007,10 +3871,22 @@ "staff_description": "Schizophrenia, residual type; other and unspecified types", "status_description": "schizophrenia" }, + "9206": { + "staff_description": "Bipolar disorder", + "status_description": "Bipolar disorder" + }, + "9207": { + "staff_description": "Depression with psychotic features", + "status_description": "Depression with psychotic features" + }, "9208": { "staff_description": "Delusional disorder", "status_description": "delusional disorder" }, + "9209": { + "staff_description": "Depression with melancholia", + "status_description": "Depression with melancholia" + }, "9210": { "staff_description": "Psychotic disorder, not otherwise specified (atypical psychosis)", "status_description": "psychotic disorder" @@ -3031,6 +3907,14 @@ "staff_description": "Dementia due to infection (see code for list)", "status_description": "dementia" }, + "9302": { + "staff_description": "Dementia associated with intracranial infections", + "status_description": "Dementia associated with intracranial infections" + }, + "9303": { + "staff_description": "Dementia associated with alcoholism", + "status_description": "Dementia associated with alcoholism" + }, "9304": { "staff_description": "Dementia due to head trauma", "status_description": "dementia" @@ -3039,14 +3923,86 @@ "staff_description": "Vascular dementia", "status_description": "vascular dementia" }, + "9306": { + "staff_description": "Multi-infarct dementia", + "status_description": "Multi-infarct dementia" + }, + "9307": { + "staff_description": "Dementia associated with convulsive disorder", + "status_description": "Dementia associated with convulsive disorder" + }, + "9308": { + "staff_description": "Dementia associated with metabolic disorder", + "status_description": "Dementia associated with metabolic disorder" + }, + "9309": { + "staff_description": "Dementia associated with brain tumor", + "status_description": "Dementia associated with brain tumor" + }, "9310": { "staff_description": "Dementia of unknown etiology", "status_description": "dementia" }, + "9311": { + "staff_description": "Dementia due to undiagnosed cause", + "status_description": "Dementia due to undiagnosed cause" + }, "9312": { "staff_description": "Dementia of the Alzheimer's type", "status_description": "Alzheimer's disease" }, + "9313": { + "staff_description": "Brain syndrome", + "status_description": "Brain syndrome" + }, + "9314": { + "staff_description": "Brain syndrome", + "status_description": "Brain syndrome" + }, + "9315": { + "staff_description": "Dementia associated with epidemic encephalitis", + "status_description": "Dementia associated with epidemic encephalitis" + }, + "9316": { + "staff_description": "Brain syndrome", + "status_description": "Brain syndrome" + }, + "9317": { + "staff_description": "Brain syndrome", + "status_description": "Brain syndrome" + }, + "9318": { + "staff_description": "Brain syndrome", + "status_description": "Brain syndrome" + }, + "9319": { + "staff_description": "Brain syndrome", + "status_description": "Brain syndrome" + }, + "9320": { + "staff_description": "Brain syndrome", + "status_description": "Brain syndrome" + }, + "9321": { + "staff_description": "Brain syndrome", + "status_description": "Brain syndrome" + }, + "9322": { + "staff_description": "Dementia associated with endocrine disorder", + "status_description": "Dementia associated with endocrine disorder" + }, + "9323": { + "staff_description": "Brain syndrome", + "status_description": "Brain syndrome" + }, + "9324": { + "staff_description": "Dementia associated with systemic infection", + "status_description": "Dementia associated with systemic infection" + }, + "9325": { + "staff_description": "Brain Syndrome", + "status_description": "Brain Syndrome" + }, "9326": { "staff_description": "Dementia due to other neurologic or general medical conditions, or substance- induced", "status_description": "dementia" @@ -3055,10 +4011,22 @@ "staff_description": "Organic mental disorder, other (inc. personality change due to a general medical condition)", "status_description": "organic mental disorder" }, + "9399": { + "staff_description": "Cognitive disorders - general", + "status_description": "Cognitive disorders - general" + }, "9400": { "staff_description": "Generalized anxiety disorder", "status_description": "generalized anxiety disorder" }, + "9401": { + "staff_description": "Neurosis", + "status_description": "Neurosis" + }, + "9402": { + "staff_description": "Neurosis", + "status_description": "Neurosis" + }, "9403": { "staff_description": "Specific (simple) phobia; social phobia", "status_description": "specific phobia" @@ -3067,6 +4035,26 @@ "staff_description": "Obsessive compulsive disorder", "status_description": "obsessive compulsive disorder" }, + "9405": { + "staff_description": "Depressive Reaction", + "status_description": "Depressive Reaction" + }, + "9406": { + "staff_description": "Neurosis", + "status_description": "Neurosis" + }, + "9407": { + "staff_description": "Neurosis", + "status_description": "Neurosis" + }, + "9408": { + "staff_description": "Neurosis", + "status_description": "Neurosis" + }, + "9409": { + "staff_description": "Neurosis", + "status_description": "Neurosis" + }, "9410": { "staff_description": "Other and unspecified neurosis", "status_description": "neurosis" @@ -3139,6 +4127,54 @@ "staff_description": "Other nonpsychotic emotional illness", "status_description": "nonpsychotic emotional illness" }, + "9500": { + "staff_description": "Psychological factors affecting skin", + "status_description": "Psychological factors affecting skin" + }, + "9501": { + "staff_description": "Psychological factors affecting cardiovascular", + "status_description": "Psychological factors affecting cardiovascular" + }, + "9502": { + "staff_description": "Psychological factors affecting gastrointestinal", + "status_description": "Psychological factors affecting gastrointestinal" + }, + "9503": { + "staff_description": "Nervous Condition", + "status_description": "Nervous Condition" + }, + "9504": { + "staff_description": "Nervous Condition", + "status_description": "Nervous Condition" + }, + "9505": { + "staff_description": "Psychological factors affecting musculoskeletal", + "status_description": "Psychological factors affecting musculoskeletal" + }, + "9506": { + "staff_description": "Psychological factors affecting respiratory", + "status_description": "Psychological factors affecting respiratory" + }, + "9507": { + "staff_description": "Psychological factors affecting hemic/lymphatic", + "status_description": "Psychological factors affecting hemic/lymphatic" + }, + "9508": { + "staff_description": "Psychological factors affecting genitourinary", + "status_description": "Psychological factors affecting genitourinary" + }, + "9509": { + "staff_description": "Psychological factors affecting endocrine", + "status_description": "Psychological factors affecting endocrine" + }, + "9510": { + "staff_description": "Psychological factors affecting organ of sense", + "status_description": "Psychological factors affecting organ of sense" + }, + "9511": { + "staff_description": "Psychological factors affecting physical condition", + "status_description": "Psychological factors affecting physical condition" + }, "9520": { "staff_description": "Anorexia nervosa", "status_description": "anorexia" @@ -3191,6 +4227,10 @@ "staff_description": "Coronoid process, loss of", "status_description": "partial loss of lower jaw" }, + "9910": { + "staff_description": "Loss of maxilla", + "status_description": "Loss of maxilla" + }, "9911": { "staff_description": "Hard palate, loss of half or more", "status_description": "loss of hard palate" diff --git a/client/constants/EP_CLAIM_TYPES.json b/client/constants/EP_CLAIM_TYPES.json index 1ad87a55fad..0be858210ce 100644 --- a/client/constants/EP_CLAIM_TYPES.json +++ b/client/constants/EP_CLAIM_TYPES.json @@ -158,21 +158,6 @@ "offical_label": "PMC HLR DTA Error - Rating", "disposition_type": "dta_error" }, - "040SCNRPMC": { - "review_type": "supplemental_claim", - "issue_type": "nonrating", - "benefit_type": "pension", - "family": "040", - "offical_label": "PMC Supplemental Claim nonrating", - "disposition_type": "dta_error" - }, - "040SCNR": { - "review_type": "supplemental_claim", - "issue_type": "nonrating", - "benefit_type": "compensation", - "family": "040", - "offical_label": "Supplemental Claim nonrating" - }, "040SCRPMC": { "review_type": "supplemental_claim", "issue_type": "rating", @@ -212,34 +197,6 @@ "offical_label": "PMC Board Grant Rating", "disposition_type": "allowed" }, - "030HLRNR": { - "review_type": "higher_level_review", - "issue_type": "nonrating", - "benefit_type": "compensation", - "family": "030", - "offical_label": "Higher-Level Review nonrating" - }, - "030HLRNRPMC": { - "review_type": "higher_level_review", - "issue_type": "nonrating", - "benefit_type": "pension", - "family": "030", - "offical_label": "PMC Higher-Level Review nonrating" - }, - "030HLRR": { - "review_type": "higher_level_review", - "issue_type": "rating", - "benefit_type": "compensation", - "family": "030", - "offical_label": "Higher-Level Review Rating" - }, - "030HLRRPMC": { - "review_type": "higher_level_review", - "issue_type": "rating", - "benefit_type": "pension", - "family": "030", - "offical_label": "PMC Higher-Level Review Rating" - }, "930AHCNRLPMC": { "review_type": "higher_level_review", "issue_type": "nonrating", diff --git a/client/constants/TASK_ACTIONS.json b/client/constants/TASK_ACTIONS.json index 1b8d92d7ad7..06dcaccf096 100644 --- a/client/constants/TASK_ACTIONS.json +++ b/client/constants/TASK_ACTIONS.json @@ -90,7 +90,17 @@ }, "DOCKET_SWITCH_CHECKOUT": { "label": "Send to Judge", - "value": "docket_switch/checkout/review" + "value": "docket_switch/checkout/review", + "func": "docket_switch_granted_data" + }, + "DOCKET_SWITCH_DENIED": { + "label": "Deny Docket Switch", + "value": "docket_switch/checkout/deny", + "func": "docket_switch_denied_data" + }, + "DOCKET_SWITCH_GRANTED": { + "label": "Grant Docket Switch", + "value": "docket_switch/checkout/grant" }, "CANCEL_FOREIGN_VETERANS_CASE_TASK": { "label": "Cancel hearing request", diff --git a/client/package.json b/client/package.json index 270ff248806..741dfb72411 100644 --- a/client/package.json +++ b/client/package.json @@ -59,6 +59,7 @@ "@department-of-veterans-affairs/caseflow-frontend-toolkit": "https://github.com/department-of-veterans-affairs/caseflow-frontend-toolkit#71ee169", "@fortawesome/fontawesome-free": "^5.3.1", "@hookform/resolvers": "^0.1.1", + "@reduxjs/toolkit": "^1.4.0", "@storybook/addon-a11y": "^6.0.7", "@storybook/addon-actions": "^6.0.7", "@storybook/addon-controls": "^6.0.7", @@ -147,6 +148,7 @@ "enzyme-adapter-react-16": "^1.15.1", "enzyme-to-json": "^3.5.0", "eslint": "^7.0.0", + "eslint-import-resolver-webpack": "^0.13.0", "eslint-plugin-babel": "^5.3.0", "eslint-plugin-import": "^2.20.1", "eslint-plugin-jest": "^23.19.0", diff --git a/client/test/app/hearings/components/ScheduleVeteran.test.js b/client/test/app/hearings/components/ScheduleVeteran.test.js index b067a6ee38c..c1f2f79cb01 100644 --- a/client/test/app/hearings/components/ScheduleVeteran.test.js +++ b/client/test/app/hearings/components/ScheduleVeteran.test.js @@ -1,6 +1,7 @@ import React from 'react'; import { mount } from 'enzyme'; import AppSegment from '@department-of-veterans-affairs/caseflow-frontend-toolkit/components/AppSegment'; +import { omit } from 'lodash'; import COPY from 'COPY'; import ScheduleVeteran from 'app/hearings/components/ScheduleVeteran'; @@ -378,7 +379,9 @@ describe('ScheduleVeteran', () => { description: 'Update Task', values: { ...scheduleHearingDetails.apiFormattedValues, - virtual_hearing_attributes: ApiUtil.convertToSnakeCase(virtualHearing.virtualHearing), + virtual_hearing_attributes: ApiUtil.convertToSnakeCase( + omit(virtualHearing.virtualHearing, ['status']) + ), override_full_hearing_day_validation: false, }, }, diff --git a/client/test/app/hearings/components/__snapshots__/ScheduleVeteran.test.js.snap b/client/test/app/hearings/components/__snapshots__/ScheduleVeteran.test.js.snap index d31059a245a..773b27629d9 100644 --- a/client/test/app/hearings/components/__snapshots__/ScheduleVeteran.test.js.snap +++ b/client/test/app/hearings/components/__snapshots__/ScheduleVeteran.test.js.snap @@ -49877,7 +49877,7 @@ SAN FRANCISCO, CA 94103 ] } linkStyling={false} - loading={true} + loading={false} name="Schedule" onClick={[Function]} type="button" @@ -49885,37 +49885,13 @@ SAN FRANCISCO, CA 94103 > - - - - - @@ -79318,7 +79294,7 @@ SAN FRANCISCO, CA 94103 ] } linkStyling={false} - loading={true} + loading={false} name="Schedule" onClick={[Function]} type="button" @@ -79326,37 +79302,13 @@ SAN FRANCISCO, CA 94103 > - - - - - @@ -168590,7 +168542,7 @@ SAN FRANCISCO, CA 94103 ] } linkStyling={false} - loading={true} + loading={false} name="Schedule" onClick={[Function]} type="button" @@ -168598,37 +168550,13 @@ SAN FRANCISCO, CA 94103 > - - - - - diff --git a/client/webpack.config.js b/client/webpack.config.js index ff2eaa0b9e4..40da81f4835 100644 --- a/client/webpack.config.js +++ b/client/webpack.config.js @@ -20,7 +20,15 @@ const config = { alias: { // This does not actually appear to be necessary, but it does silence // a warning from superagent-no-cache. - ie: 'component-ie' + ie: 'component-ie', + app: path.resolve('app'), + constants: path.resolve('constants'), + layouts: path.resolve('app/2.0/layouts'), + routes: path.resolve('app/2.0/routes'), + store: path.resolve('app/2.0/store'), + screens: path.resolve('app/2.0/screens'), + components: path.resolve('app/2.0/components'), + test: path.resolve('test'), } }, module: { diff --git a/client/yarn.lock b/client/yarn.lock index ede3ec73071..4ea77176e08 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -2554,6 +2554,16 @@ prop-types "^15.6.1" react-lifecycles-compat "^3.0.4" +"@reduxjs/toolkit@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.4.0.tgz#ee2e2384cc3d1d76780d844b9c2da3580d32710d" + integrity sha512-hkxQwVx4BNVRsYdxjNF6cAseRmtrkpSlcgJRr3kLUcHPIAMZAmMJkXmHh/eUEGTMqPzsYpJLM7NN2w9fxQDuGw== + dependencies: + immer "^7.0.3" + redux "^4.0.0" + redux-thunk "^2.3.0" + reselect "^4.0.0" + "@sinonjs/commons@^1.7.0": version "1.7.2" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.2.tgz#505f55c74e0272b43f6c52d81946bed7058fc0e2" @@ -4277,6 +4287,11 @@ array-find-index@^1.0.1: resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= +array-find@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-find/-/array-find-1.0.0.tgz#6c8e286d11ed768327f8e62ecee87353ca3e78b8" + integrity sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg= + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -7368,6 +7383,15 @@ enhanced-resolve@4.1.0: memory-fs "^0.4.0" tapable "^1.0.0" +enhanced-resolve@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e" + integrity sha1-TW5omzcl+GCQknzMhs2fFjW4ni4= + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.2.0" + tapable "^0.1.8" + enhanced-resolve@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" @@ -7645,6 +7669,22 @@ eslint-import-resolver-node@^0.3.2: debug "^2.6.9" resolve "^1.13.1" +eslint-import-resolver-webpack@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.0.tgz#5cb19cf4b6996c8a2514aeb10f909e2c70488dc3" + integrity sha512-hZWGcmjaJZK/WSCYGI/y4+FMGQZT+cwW/1E/P4rDwFj2PbanlQHISViw4ccDJ+2wxAqjgwBfxwy3seABbVKDEw== + dependencies: + array-find "^1.0.0" + debug "^2.6.9" + enhanced-resolve "^0.9.1" + find-root "^1.1.0" + has "^1.0.3" + interpret "^1.2.0" + lodash "^4.17.15" + node-libs-browser "^1.0.0 || ^2.0.0" + resolve "^1.13.1" + semver "^5.7.1" + eslint-module-utils@^2.4.1: version "2.5.2" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.5.2.tgz#7878f7504824e1b857dd2505b59a8e5eda26a708" @@ -9590,6 +9630,11 @@ immer@1.10.0: resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d" integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg== +immer@^7.0.3: + version "7.0.7" + resolved "https://registry.yarnpkg.com/immer/-/immer-7.0.7.tgz#9dfe713d49bf871cc59aedfce59b1992fa37a977" + integrity sha512-Q8yYwVADJXrNfp1ZUAh4XDHkcoE3wpdpb4mC5abDSajs2EbW8+cGdPyAnglMyLnm7EF6ojD2xBFX7L5i4TIytw== + immutability-helper@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/immutability-helper/-/immutability-helper-3.0.1.tgz#4f609c5afbf8d78cb297970e8af2fba8b0eda1d6" @@ -9835,7 +9880,7 @@ interpret@1.2.0: resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== -interpret@^1.0.0: +interpret@^1.0.0, interpret@^1.2.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== @@ -11820,6 +11865,11 @@ memoizerific@^1.11.3: dependencies: map-or-similar "^1.5.0" +memory-fs@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290" + integrity sha1-8rslNovBIeORwlIN6Slpyu4KApA= + memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -12427,7 +12477,7 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-libs-browser@^2.2.1: +"node-libs-browser@^1.0.0 || ^2.0.0", node-libs-browser@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== @@ -14695,7 +14745,7 @@ redux@^3.7.2: loose-envify "^1.1.0" symbol-observable "^1.0.3" -redux@^4.0.5: +redux@^4.0.0, redux@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== @@ -15372,7 +15422,7 @@ selfsigned@^1.10.7: dependencies: node-forge "0.9.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -16398,6 +16448,11 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" +tapable@^0.1.8: + version "0.1.10" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4" + integrity sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q= + tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" diff --git a/db/migrate/20201020135542_cavc_remand_updated_by_nullable.rb b/db/migrate/20201020135542_cavc_remand_updated_by_nullable.rb new file mode 100644 index 00000000000..f69c6e3fef7 --- /dev/null +++ b/db/migrate/20201020135542_cavc_remand_updated_by_nullable.rb @@ -0,0 +1,6 @@ +class CavcRemandUpdatedByNullable < Caseflow::Migration + def change + safety_assured { remove_column :cavc_remands, :updated_by_id, :bigint, null: false, comment: "User that updated this record. For MDR remands, judgement and mandate dates will be added after the record is first created." } + add_column :cavc_remands, :updated_by_id, :bigint, comment: "User that updated this record. For MDR remands, judgement and mandate dates will be added after the record is first created." + end +end diff --git a/db/schema.rb b/db/schema.rb index 87456c5ee1f..4a7de6f9bec 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_10_05_190456) do +ActiveRecord::Schema.define(version: 2020_10_20_135542) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -280,7 +280,7 @@ t.string "remand_subtype", comment: "Type of remand. If the cavc_decision_type is 'remand', expecting one of 'jmp', 'jmpr', or 'mdr'. Otherwise, this can be null." t.boolean "represented_by_attorney", null: false, comment: "Whether or not the appellant was represented by an attorney" t.datetime "updated_at", null: false, comment: "Default timestamps" - t.bigint "updated_by_id", null: false, comment: "User that updated this record. For MDR remands, judgement and mandate dates will be added after the record is first created." + t.bigint "updated_by_id", comment: "User that updated this record. For MDR remands, judgement and mandate dates will be added after the record is first created." t.index ["appeal_id"], name: "index_cavc_remands_on_appeal_id" end diff --git a/docs/schema/caseflow.csv b/docs/schema/caseflow.csv index 86e372985f8..8a16b68a493 100644 --- a/docs/schema/caseflow.csv +++ b/docs/schema/caseflow.csv @@ -194,7 +194,7 @@ cavc_remands,mandate_date,date ∗,x,,,,,Date that CAVC reported the mandate w cavc_remands,remand_subtype,string ∗,x,,,,,"Type of remand. If the cavc_decision_type is 'remand', expecting one of 'jmp', 'jmpr', or 'mdr'. Otherwise, this can be null." cavc_remands,represented_by_attorney,boolean ∗,x,,,,,Whether or not the appellant was represented by an attorney cavc_remands,updated_at,datetime ∗,x,,,,,Default timestamps -cavc_remands,updated_by_id,integer (8) ∗ FK,x,,x,,,"User that updated this record. For MDR remands, judgement and mandate dates will be added after the record is first created." +cavc_remands,updated_by_id,integer (8) FK,,,x,,,"User that updated this record. For MDR remands, judgement and mandate dates will be added after the record is first created." certifications,,,,,,,, certifications,already_certified,boolean,,,,,, certifications,bgs_rep_address_line_1,string,,,,,, @@ -409,16 +409,6 @@ end_product_code_updates,created_at,datetime ∗,x,,,,, end_product_code_updates,end_product_establishment_id,integer (8) ∗ FK,x,,x,,x, end_product_code_updates,id,integer (8) PK,x,x,,,, end_product_code_updates,updated_at,datetime ∗,x,,,,x, -end_product_updates,,,,,,,, "Updates the claim label for end products established from Caseflow." -end_product_updates,user_id,integer,"The ID of the user who makes an end product update." -end_product_updates,end_product_establishment_id,bigint,"The end product establishment id used to track the end product being updated" -end_product_updates,original_decision_review_id,bigint,"The original ID of the decision review that this end product update belongs to; has a non-nil value only if a new decision_review was created." -end_product_updates,original_decision_review_type,string,"The original decision review type that this end product update belongs to" -end_product_updates,status,string,"Status after an attempt to update the end product; expected values: 'success', 'error', ..." -end_product_updates,error,string,"The error message captured from BGS if the end product update failed." -end_product_updates,original_code,string,"The original end product code before the update was submitted" -end_product_updates,new_code,string,"The new end product code the user wants to update to." -end_product_updates,active_request_issue_ids,integer,"A list of active request issue IDs when a user has finished editing a decision review. Used to keep track of which request issues may have been impacted by the update." end_product_establishments,,,,,,,,"Represents end products that have been, or need to be established by Caseflow. Used to track the status of those end products as they are processed in VBMS and/or SHARE." end_product_establishments,benefit_type_code,string,,,,,,"1 if the Veteran is alive, and 2 if the Veteran is deceased. Not to be confused with benefit_type, which is unrelated." end_product_establishments,claim_date,date,,,,,,The claim_date for end product established. @@ -443,6 +433,19 @@ end_product_establishments,synced_status,string,,,,,,"The status of the end prod end_product_establishments,updated_at,datetime,,,,,x, end_product_establishments,user_id,integer FK,,,x,,x,The ID of the user who performed the decision review intake. end_product_establishments,veteran_file_number,string ∗,x,,,,x,The file number of the Veteran submitted when establishing the end product. +end_product_updates,,,,,,,,Updates the claim label for end products established from Caseflow +end_product_updates,active_request_issue_ids,integer (8) ∗,x,,,,,A list of active request issue IDs when a user has finished editing a decision review. Used to keep track of which request issues may have been impacted by the update. +end_product_updates,created_at,datetime ∗,x,,,,, +end_product_updates,end_product_establishment_id,integer (8) ∗ FK,x,,x,,x,The end product establishment id used to track the end product being updated. +end_product_updates,error,string,,,,,,The error message captured from BGS if the end product update failed. +end_product_updates,id,integer (8) PK,x,x,,,, +end_product_updates,new_code,string,,,,,,The new end product code the user wants to update to. +end_product_updates,original_code,string,,,,,,The original end product code before the update was submitted. +end_product_updates,original_decision_review_id,integer (8),,,,,x,The original decision review that this end product update belongs to; has a non-nil value only if a new decision_review was created. +end_product_updates,original_decision_review_type,string,,,,,x,The original decision review type that this end product update belongs to +end_product_updates,status,string,,,,,,"Status after an attempt to update the end product; expected values: 'success', 'error', ..." +end_product_updates,updated_at,datetime ∗,x,,,,, +end_product_updates,user_id,integer (8) ∗,x,,,,x,The ID of the user who makes an end product update. form8s,,,,,,,, form8s,_initial_appellant_name,string,,,,,, form8s,_initial_appellant_relationship,string,,,,,, diff --git a/lib/caseflow/error.rb b/lib/caseflow/error.rb index 68785012755..4c2e7328c21 100644 --- a/lib/caseflow/error.rb +++ b/lib/caseflow/error.rb @@ -364,8 +364,8 @@ class VirtualHearingConversionFailed < SerializableError def initialize(args = {}) @error_type = args[:error_type] - @code = (@error_type == ActiveRecord::RecordNotUnique) ? :conflict : args[:code] - @message = (@error_type == ActiveRecord::RecordNotUnique) ? COPY::VIRTUAL_HEARING_ALREADY_CREATED : args[:message] + @code = args[:code] + @message = args[:message] end end end diff --git a/spec/controllers/api/v3/base_controller_spec.rb b/spec/controllers/api/v3/base_controller_spec.rb deleted file mode 100644 index 9329c490fdf..00000000000 --- a/spec/controllers/api/v3/base_controller_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe Api::V3::BaseController, type: :controller do - describe "#api_released used in before_action" do - controller do - def index - render json: { meta: { text: "This is just a test action." } } - end - end - - before(:each) do - allow(controller).to receive(:verify_authentication_token).and_return(true) - end - - describe "when not enabled" do - it "should return a 501 response" do - get :index - expect(response).to have_http_status(:not_implemented) - end - it "should have a jsonapi error response" do - get :index - expect { JSON.parse(response.body) }.to_not raise_error - parsed_response = JSON.parse(response.body) - expect(parsed_response["errors"]).to be_a Array - expect(parsed_response["errors"].first).to include("status", "title", "detail") - end - end - - describe "when enabled" do - before do - FeatureToggle.enable!(:api_v3) - end - after do - FeatureToggle.disable!(:api_v3) - end - it "should return a 200 response" do - get :index - expect(response).to have_http_status(:ok) - end - end - end -end diff --git a/spec/controllers/tasks_controller_spec.rb b/spec/controllers/tasks_controller_spec.rb index ab3e1683244..3862f22457d 100644 --- a/spec/controllers/tasks_controller_spec.rb +++ b/spec/controllers/tasks_controller_spec.rb @@ -860,6 +860,18 @@ include_examples "returns alerts" it_behaves_like "request with invalid attributes" + + # See https://github.com/department-of-veterans-affairs/caseflow/issues/15430 + context "when virtual hearing payload includes virtual hearing status" do + let(:virtual_hearing_attributes) do + { + appellant_email: "valid@caseflow.va.gov", + status: "pending" + } + end + + it_behaves_like "request with invalid attributes" + end end context "when task is ChangeHearingRequestTypeTask" do diff --git a/spec/factories/vacols/case_hearing.rb b/spec/factories/vacols/case_hearing.rb index 25d75e8e547..729bcf38e37 100644 --- a/spec/factories/vacols/case_hearing.rb +++ b/spec/factories/vacols/case_hearing.rb @@ -31,13 +31,6 @@ hearing_disp { "N" } end - after(:create) do |hearing, _evaluator| - # For some reason the returned record's sequence is one less than what is actually saved. - # We need to reload the correct record before trying to modify it. - hearing.hearing_pkseq = hearing.hearing_pkseq + 1 - hearing.reload - end - after(:build) do |hearing, evaluator| # Build Caseflow hearing day and associate with legacy hearing. if hearing.vdkey.nil? diff --git a/spec/feature/hearings/change_hearing_disposition_spec.rb b/spec/feature/hearings/change_hearing_disposition_spec.rb index 366a9bc86d3..4fa71cb5f14 100644 --- a/spec/feature/hearings/change_hearing_disposition_spec.rb +++ b/spec/feature/hearings/change_hearing_disposition_spec.rb @@ -125,7 +125,9 @@ step "change the hearing disposition to cancelled" do expect(Raven).to receive(:capture_exception) - .with(AssignHearingDispositionTask::HearingAssociationMissing) { @raven_called = true } + .with(AssignHearingDispositionTask::HearingAssociationMissing, any_args) do + @raven_called = true + end click_dropdown(prompt: "Select an action", text: "Change hearing disposition") click_dropdown({ prompt: "Select", text: "Cancelled" }, find(".cf-modal-body")) diff --git a/spec/feature/hearings/postpone_hearing_spec.rb b/spec/feature/hearings/postpone_hearing_spec.rb index 81f0b80cef5..94326b2f8ac 100644 --- a/spec/feature/hearings/postpone_hearing_spec.rb +++ b/spec/feature/hearings/postpone_hearing_spec.rb @@ -143,6 +143,8 @@ expect(Hearing.where(hearing_day: hearing_day_earlier).count).to eq 1 expect(Hearing.find_by(hearing_day: hearing_day_earlier).hearing_location.facility_id).to eq "vba_339" expect(Hearing.first.disposition).to eq "postponed" + expect(Hearing.second.disposition).to be_nil + expect(Hearing.second.uuid).to_not eq Hearing.first.uuid expect(HearingTask.count).to eq 2 end end @@ -166,6 +168,8 @@ expect(page).to have_content("You have successfully assigned") expect(LegacyHearing.second.hearing_day.id).to eq hearing_day_earlier.id expect(LegacyHearing.first.disposition).to eq "postponed" + expect(LegacyHearing.second.disposition).to be_nil + expect(LegacyHearing.second.vacols_id).to_not eq LegacyHearing.first.vacols_id expect(HearingTask.first.hearing.id).to eq legacy_hearing.id expect(HearingTask.second.hearing.id).to eq LegacyHearing.second.id end @@ -290,6 +294,8 @@ expect(page).to have_content("You have successfully assigned") expect(LegacyHearing.second.hearing_day.id).to eq hearing_day_earlier.id expect(LegacyHearing.first.disposition).to eq "postponed" + expect(LegacyHearing.second.disposition).to be_nil + expect(LegacyHearing.second.vacols_id).to_not eq LegacyHearing.first.vacols_id expect(HearingTask.first.hearing.id).to eq legacy_hearing.id expect(HearingTask.second.hearing.id).to eq LegacyHearing.second.id end diff --git a/spec/models/docket_spec.rb b/spec/models/docket_spec.rb index 604bc658ef7..7ea1db538a0 100644 --- a/spec/models/docket_spec.rb +++ b/spec/models/docket_spec.rb @@ -207,11 +207,21 @@ end context "age_of_oldest_priority_appeal" do - subject { DirectReviewDocket.new.age_of_oldest_priority_appeal } + let(:docket) { DirectReviewDocket.new } + + subject { docket.age_of_oldest_priority_appeal } it "returns the 'ready at' field of the oldest priority appeal that is ready for distribution" do expect(subject).to eq(aod_age_appeal.ready_for_distribution_at) end + + context "when there are no ready priority appeals" do + let(:docket) { EvidenceSubmissionDocket.new } + + it "returns nil" do + expect(subject.nil?).to be true + end + end end context "distribute_appeals" do diff --git a/spec/models/end_product_establishment_spec.rb b/spec/models/end_product_establishment_spec.rb index c9c6720cf7c..52ece6036d4 100644 --- a/spec/models/end_product_establishment_spec.rb +++ b/spec/models/end_product_establishment_spec.rb @@ -1372,6 +1372,12 @@ context "if there is no modifier, shows unknown" do it { is_expected.to eq(ep_code: " Higher-Level Review Rating", ep_status: "") } end + + context "if the epe code is not found" do + before { epe.update!(code: "NOTFOUND") } + + it { is_expected.to eq(ep_code: " Unknown", ep_status: "") } + end end context "if there is not an end product" do diff --git a/spec/models/promulgated_rating_spec.rb b/spec/models/promulgated_rating_spec.rb index b7ff6ba11f5..7d0cb9d22b9 100644 --- a/spec/models/promulgated_rating_spec.rb +++ b/spec/models/promulgated_rating_spec.rb @@ -111,22 +111,59 @@ end context "#rating_profile" do - subject { rating.rating_profile } + let(:bgs) { Fakes::BGSService.new } + + before do + allow(Fakes::BGSService).to receive(:new).and_return(bgs) + end - context "when BGS throws a Share Error" do - let(:bgs) { Fakes::BGSService.new } + subject { rating.rating_profile } + context "BGS throws a Share Error on fetch_rating_profile" do before do - allow(Fakes::BGSService).to receive(:new).and_return(bgs) allow(bgs).to receive(:fetch_rating_profile) .and_raise(BGS::ShareError, "Veteran does not meet the minimum disability requirements") - allow(bgs).to receive(:fetch_rating_profiles_in_range).and_call_original end - it "Fetches the rating profile using RatingAtIssue" do - expect(subject.present?) - expect { rating.issues }.to_not raise_error - expect(bgs).to have_received(:fetch_rating_profiles_in_range) + context "BGS returns a successful response on fetch_rating_profiles_in_range" do + before do + allow(bgs).to receive(:fetch_rating_profiles_in_range).and_call_original + end + + it "Fetches the rating profile using RatingAtIssue" do + expect(subject.present?) + expect { rating.issues }.to_not raise_error + expect(bgs).to have_received(:fetch_rating_profiles_in_range) + end + end + + context "an error is raised on fetch_rating_profiles_in_range" do + let(:error) { nil } + let(:message) { "" } + + before do + allow(bgs).to receive(:fetch_rating_profiles_in_range) + .and_raise(error, message) + end + + context "a share error is raised on fetch_rating_profiles_in_range" do + let(:error) { BGS::ShareError } + let(:message) { "Veteran does not meet the minimum disability requirements" } + + it "captures the exception and returns an empty object" do + expect(Raven).to receive(:capture_exception).with error + expect(subject).to eq({}) + end + end + + context "a nil rating profile list error is raised on fetch_rating_profiles_in_range" do + let(:error) { Rating::NilRatingProfileListError } + + it "captures the exception returns an empty object" do + expect(Raven).to receive(:capture_exception).with error + expect(subject).to eq({}) + end + end end end end diff --git a/spec/models/tasks/assign_hearing_disposition_task_spec.rb b/spec/models/tasks/assign_hearing_disposition_task_spec.rb index bba43cf1ccf..49114adae8c 100644 --- a/spec/models/tasks/assign_hearing_disposition_task_spec.rb +++ b/spec/models/tasks/assign_hearing_disposition_task_spec.rb @@ -57,10 +57,9 @@ end it "sets the hearing disposition and calls hold!" do - expect(disposition_task).to receive(:hold!).exactly(1).times - - subject + expect(disposition_task).to receive(:hold!).exactly(1).times.and_call_original + expect(subject.count).to eq 3 expect(Hearing.count).to eq 1 expect(hearing.disposition).to eq Constants.HEARING_DISPOSITION_TYPES.held end @@ -79,10 +78,9 @@ end it "sets the hearing disposition and calls no_show!" do - expect(disposition_task).to receive(:no_show!).exactly(1).times - - subject + expect(disposition_task).to receive(:no_show!).exactly(1).times.and_call_original + expect(subject.count).to eq(2) expect(Hearing.count).to eq 1 expect(hearing.disposition).to eq Constants.HEARING_DISPOSITION_TYPES.no_show end diff --git a/spec/models/tasks/docket_switch_denied_task_spec.rb b/spec/models/tasks/docket_switch_denied_task_spec.rb new file mode 100644 index 00000000000..83dfe09813b --- /dev/null +++ b/spec/models/tasks/docket_switch_denied_task_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +describe DocketSwitchDeniedTask, :postgres do + let(:cotb_team) { ClerkOfTheBoard.singleton } + let(:root_task) { create(:root_task) } + let(:task_class) { DocketSwitchDeniedTask } + let(:judge) { create(:user, :with_vacols_judge_record, full_name: "Judge the First", css_id: "JUDGE_1") } + + let(:attorney) { create(:user, :with_vacols_attorney_record) } + + let(:task_actions) do + [Constants.TASK_ACTIONS.REVIEW_DECISION_DRAFT.to_h, + Constants.TASK_ACTIONS.ADD_ADMIN_ACTION.to_h, + Constants.TASK_ACTIONS.CANCEL_AND_RETURN_TASK.to_h] + end + + before do + cotb_team.add_user(attorney) + end + + describe ".available_actions" do + let(:attonery_task) do + task_class.create!( + appeal: root_task.appeal, + parent_id: root_task.id, + assigned_to: attorney, + assigned_by: judge + ) + end + + subject { attonery_task.available_actions(attorney) } + + context "when the current user is not a member of the Clerk of the Board team" do + before { allow_any_instance_of(ClerkOfTheBoard).to receive(:user_has_access?).and_return(false) } + context "without docket_change feature toggle" do + it "returns the correct label" do + expect(DocketSwitchDeniedTask.new.label).to eq( + COPY::DOCKET_SWITCH_DENIED_TASK_LABEL + ) + end + end + + context "with docket_change feature toggle" do + before { FeatureToggle.enable!(:docket_change) } + after { FeatureToggle.disable!(:docket_change) } + + it "returns the available_actions as defined by Task" do + expect(subject).to eq(task_actions) + end + end + end + + context "when the current user is a member of the Clerk of the Board team" do + context "without docket_change feature toggle" do + it "returns the available_actions as defined by Task" do + expect(subject).to eq(task_actions) + end + end + + context "with docket_change feature toggle" do + before { FeatureToggle.enable!(:docket_change) } + after { FeatureToggle.disable!(:docket_change) } + + it "returns the available_actions as defined by Task" do + expect(subject).to eq(task_actions + [Constants.TASK_ACTIONS.DOCKET_SWITCH_DENIED.to_h]) + end + end + end + end +end diff --git a/spec/models/tasks/docket_switch_granted_task_spec.rb b/spec/models/tasks/docket_switch_granted_task_spec.rb new file mode 100644 index 00000000000..2fe28adfeac --- /dev/null +++ b/spec/models/tasks/docket_switch_granted_task_spec.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +describe DocketSwitchGrantedTask, :postgres do + let(:cotb_team) { ClerkOfTheBoard.singleton } + let(:root_task) { create(:root_task) } + let(:task_class) { DocketSwitchGrantedTask } + let(:judge) { create(:user, :with_vacols_judge_record, full_name: "Judge the First", css_id: "JUDGE_1") } + + let(:attorney) { create(:user, :with_vacols_attorney_record) } + + let(:task_actions) do + [Constants.TASK_ACTIONS.REVIEW_DECISION_DRAFT.to_h, + Constants.TASK_ACTIONS.ADD_ADMIN_ACTION.to_h, + Constants.TASK_ACTIONS.CANCEL_AND_RETURN_TASK.to_h] + end + + before do + cotb_team.add_user(attorney) + end + + describe ".available_actions" do + let(:attonery_task) do + task_class.create!( + appeal: root_task.appeal, + parent_id: root_task.id, + assigned_to: attorney, + assigned_by: judge + ) + end + + subject { attonery_task.available_actions(attorney) } + + context "when the current user is not a member of the Clerk of the Board team" do + before { allow_any_instance_of(ClerkOfTheBoard).to receive(:user_has_access?).and_return(false) } + context "without docket_change feature toggle" do + it "returns the correct label" do + expect(DocketSwitchGrantedTask.new.label).to eq( + COPY::DOCKET_SWITCH_GRANTED_TASK_LABEL + ) + end + end + + context "with docket_change feature toggle" do + before { FeatureToggle.enable!(:docket_change) } + after { FeatureToggle.disable!(:docket_change) } + + it "returns the available_actions as defined by Task" do + expect(subject).to eq(task_actions) + end + end + end + + context "when the current user is a member of the Clerk of the Board team" do + context "without docket_change feature toggle" do + it "returns the available_actions as defined by Task" do + expect(subject).to eq(task_actions) + end + end + + context "with docket_change feature toggle" do + before { FeatureToggle.enable!(:docket_change) } + after { FeatureToggle.disable!(:docket_change) } + + it "returns the available_actions as defined by Task" do + expect(subject).to eq(task_actions + [Constants.TASK_ACTIONS.DOCKET_SWITCH_GRANTED.to_h]) + end + end + end + end +end diff --git a/spec/models/tasks/schedule_hearing_task_spec.rb b/spec/models/tasks/schedule_hearing_task_spec.rb index 57e89fa5cd8..e42a3db8892 100644 --- a/spec/models/tasks/schedule_hearing_task_spec.rb +++ b/spec/models/tasks/schedule_hearing_task_spec.rb @@ -75,6 +75,8 @@ end describe "#update_from_params" do + subject { schedule_hearing_task.update_from_params(update_params, hearings_management_user) } + context "AMA appeal" do let(:hearing_day) do create(:hearing_day, @@ -95,19 +97,15 @@ } end - subject { schedule_hearing_task.update_from_params(update_params, hearings_management_user) } - it "associates a caseflow hearing with the hearing day" do - subject - + expect(subject.count).to eq(2) expect(Hearing.count).to eq(1) expect(Hearing.first.hearing_day).to eq(hearing_day) expect(Hearing.first.appeal).to eq(schedule_hearing_task.appeal) end it "creates a AssignHearingDispositionTask and associated object" do - subject - + expect(subject.count).to eq(2) expect(AssignHearingDispositionTask.count).to eq(1) expect(AssignHearingDispositionTask.first.appeal).to eq(schedule_hearing_task.appeal) expect(HearingTaskAssociation.count).to eq(1) @@ -128,8 +126,7 @@ end it "converts hearing to virtual hearing", :aggregate_failures do - subject - + expect(subject.count).to eq(2) expect(Hearing.count).to eq(1) expect(Hearing.first.virtual_hearing).not_to eq(nil) expect(Hearing.first.virtual?).to eq(true) @@ -169,8 +166,7 @@ context "with no VSO" do it "completes the task and updates the location to case storage" do - schedule_hearing_task.update_from_params(update_params, hearings_management_user) - + expect(subject.count).to eq(1) expect(schedule_hearing_task.status).to eq(Constants.TASK_STATUSES.cancelled) expect(schedule_hearing_task.closed_at).to_not be_nil expect(vacols_case.reload.bfcurloc).to eq(LegacyAppeal::LOCATION_CODES[:case_storage]) @@ -199,8 +195,7 @@ end it "completes the task and updates the location to service organization" do - schedule_hearing_task.update_from_params(update_params, hearings_management_user) - + expect(subject.count).to eq(1) expect(schedule_hearing_task.status).to eq(Constants.TASK_STATUSES.cancelled) expect(vacols_case.reload.bfcurloc).to eq(LegacyAppeal::LOCATION_CODES[:service_organization]) expect(vacols_case.bfha).to eq("5") @@ -216,8 +211,7 @@ end it "completes the task and creates an EvidenceSubmissionWindowTask" do - schedule_hearing_task.update_from_params(update_params, hearings_management_user) - + expect(subject.count).to eq(2) expect(schedule_hearing_task.status).to eq(Constants.TASK_STATUSES.cancelled) expect(appeal.tasks.where(type: EvidenceSubmissionWindowTask.name).count).to eq(1) end diff --git a/spec/requests/api/v3/decision_reviews/appeals/contestable_issues_controller_spec.rb b/spec/requests/api/v3/decision_reviews/appeals/contestable_issues_controller_spec.rb index 7cc61ac497a..3f1926f7823 100644 --- a/spec/requests/api/v3/decision_reviews/appeals/contestable_issues_controller_spec.rb +++ b/spec/requests/api/v3/decision_reviews/appeals/contestable_issues_controller_spec.rb @@ -1,6 +1,16 @@ # frozen_string_literal: true describe Api::V3::DecisionReviews::Appeals::ContestableIssuesController, :postgres, type: :request do + include IntakeHelpers + + before do + FeatureToggle.enable!(:api_v3_appeals_contestable_issues) + + Timecop.freeze(post_ama_start_date) + end + + after { FeatureToggle.disable!(:api_v3_appeals_contestable_issues) } + let(:decision_review_type) { :appeal } let(:source) do appeal = create(decision_review_type, veteran_file_number: veteran.file_number) diff --git a/spec/requests/api/v3/decision_reviews/higher_level_reviews/contestable_issues_controller_spec.rb b/spec/requests/api/v3/decision_reviews/higher_level_reviews/contestable_issues_controller_spec.rb index 814e69773e6..c643c2c50df 100644 --- a/spec/requests/api/v3/decision_reviews/higher_level_reviews/contestable_issues_controller_spec.rb +++ b/spec/requests/api/v3/decision_reviews/higher_level_reviews/contestable_issues_controller_spec.rb @@ -5,6 +5,16 @@ let(:source) { create(:higher_level_review, veteran_file_number: veteran.file_number, same_office: false) } let(:benefit_type) { "compensation" } + include IntakeHelpers + + before do + FeatureToggle.enable!(:api_v3_higher_level_reviews_contestable_issues) + + Timecop.freeze(post_ama_start_date) + end + + after { FeatureToggle.disable!(:api_v3_higher_level_reviews_contestable_issues) } + include_examples "contestable issues index requests" describe "#index" do @@ -34,5 +44,22 @@ expect(response).to have_http_status(:unprocessable_entity) end end + + context "when feature toggle is not enabled" do + before { FeatureToggle.disable!(:api_v3_higher_level_reviews_contestable_issues) } + + it "should return a 501 response" do + get_issues + expect(response).to have_http_status(:not_implemented) + end + + it "should have a jsonapi error response" do + get_issues + expect { JSON.parse(response.body) }.to_not raise_error + parsed_response = JSON.parse(response.body) + expect(parsed_response["errors"]).to be_a Array + expect(parsed_response["errors"].first).to include("status", "title", "detail") + end + end end end diff --git a/spec/requests/api/v3/decision_reviews/higher_level_reviews_controller_spec.rb b/spec/requests/api/v3/decision_reviews/higher_level_reviews_controller_spec.rb index 4772a2ba409..58ba16ac9fe 100644 --- a/spec/requests/api/v3/decision_reviews/higher_level_reviews_controller_spec.rb +++ b/spec/requests/api/v3/decision_reviews/higher_level_reviews_controller_spec.rb @@ -6,12 +6,12 @@ include IntakeHelpers before do - FeatureToggle.enable!(:api_v3) + FeatureToggle.enable!(:api_v3_higher_level_reviews) Timecop.freeze(post_ama_start_date) end - after { FeatureToggle.disable!(:api_v3) } + after { FeatureToggle.disable!(:api_v3_higher_level_reviews) } let!(:rating) do promulgation_date = receipt_date - 10.days @@ -109,6 +109,25 @@ def post_create(parameters = params) let(:expected_error_json) { expected_error_render_hash[:json].as_json } let(:expected_error_status) { expected_error_render_hash[:status] } + context "when feature toggle is not enabled" do + before { FeatureToggle.disable!(:api_v3_higher_level_reviews) } + + it "should return a 501 response" do + allow_any_instance_of(HigherLevelReview).to receive(:asyncable_status) { :submitted } + post_create + expect(response).to have_http_status(:not_implemented) + end + + it "should have a jsonapi error response" do + allow_any_instance_of(HigherLevelReview).to receive(:asyncable_status) { :submitted } + post_create + expect { JSON.parse(response.body) }.to_not raise_error + parsed_response = JSON.parse(response.body) + expect(parsed_response["errors"]).to be_a Array + expect(parsed_response["errors"].first).to include("status", "title", "detail") + end + end + context "good request" do it "should return a 202 on success" do allow_any_instance_of(HigherLevelReview).to receive(:asyncable_status) { :submitted } diff --git a/spec/services/external_api/bgs_service_spec.rb b/spec/services/external_api/bgs_service_spec.rb index b445cbe0c9d..b048ad158b8 100644 --- a/spec/services/external_api/bgs_service_spec.rb +++ b/spec/services/external_api/bgs_service_spec.rb @@ -287,4 +287,45 @@ end end end + + describe "fetch_ratings_in_range" do + let!(:veteran) { create(:veteran) } + let!(:rating) { double("rating") } + let(:start_date) { Time.zone.now - 5.years } + let(:start_date_formatted) { start_date.to_date.to_datetime.iso8601 } + let(:end_date) { start_date } + + before do + allow(bgs_client).to receive(:rating).and_return rating + end + + subject do + bgs.fetch_ratings_in_range(participant_id: veteran.participant_id, start_date: start_date, end_date: end_date) + end + + context "the start and end dates are the same" do + let(:end_date_formatted) { (start_date + 1.day).to_date.to_datetime.iso8601 } + + it "formats dates correctly" do + expect(rating) + .to receive(:find_by_participant_id_and_date_range) + .with(veteran.participant_id, start_date_formatted, end_date_formatted) + + subject + end + end + + context "the start and end dates are different" do + let(:end_date) { Time.zone.now } + let(:end_date_formatted) { end_date.to_date.to_datetime.iso8601 } + + it "formats dates correctly" do + expect(rating) + .to receive(:find_by_participant_id_and_date_range) + .with(veteran.participant_id, start_date_formatted, end_date_formatted) + + subject + end + end + end end diff --git a/spec/services/hearing_task_tree_initializer_spec.rb b/spec/services/hearing_task_tree_initializer_spec.rb index f92e2a29e98..eb0663be1ad 100644 --- a/spec/services/hearing_task_tree_initializer_spec.rb +++ b/spec/services/hearing_task_tree_initializer_spec.rb @@ -13,7 +13,7 @@ subject { described_class.for_appeal_with_pending_travel_board_hearing(appeal) } - context "if task tree does not already exist" do + context "the task tree does not already exist" do it "it creates the expected tasks" do expect(ChangeHearingRequestTypeTask.count).to eq(0) expect(ScheduleHearingTask.count).to eq(0) @@ -35,7 +35,7 @@ end end - context "if an open hearing task already exists" do + context "an open hearing task already exists" do let(:root_task) { create(:root_task, appeal: appeal) } let(:hearing_task) { create(:hearing_task, appeal: appeal, parent: root_task) } let!(:schedule_hearing_task) { create(:schedule_hearing_task, appeal: appeal, parent: hearing_task) } @@ -47,6 +47,25 @@ expect(HearingTask.count).to eq(1) end end + + context "there's a closed hearing task on the appeal" do + let(:root_task) { create(:root_task, appeal: appeal) } + let!(:hearing_task) do + create(:hearing_task, appeal: appeal, parent: root_task) + end + + before do + hearing_task.update!(status: Constants.TASK_STATUSES.completed) + end + + it "creates a new hearing task parent" do + expect(HearingTask.count).to eq(1) + subject + expect(ChangeHearingRequestTypeTask.count).to eq(1) + expect(ScheduleHearingTask.count).to eq(1) + expect(HearingTask.count).to eq(2) + end + end end context "#create_schedule_hearing_tasks" do diff --git a/spec/support/shared_context/requests/api/v3/decision_reviews/shared_context_contestable_issues.rb b/spec/support/shared_context/requests/api/v3/decision_reviews/shared_context_contestable_issues.rb index fabdfad4bd1..0d6f7b9363e 100644 --- a/spec/support/shared_context/requests/api/v3/decision_reviews/shared_context_contestable_issues.rb +++ b/spec/support/shared_context/requests/api/v3/decision_reviews/shared_context_contestable_issues.rb @@ -14,18 +14,27 @@ let!(:api_key) { ApiKey.create!(consumer_name: "ApiV3 Test Consumer").key_string } let(:veteran) { create(:veteran).unload_bgs_record } let(:ssn) { veteran.ssn } + let(:file_number) { nil } let(:response_data) { JSON.parse(response.body)["data"] } let(:receipt_date) { Time.zone.today } let(:get_issues) do benefit_type_url_string = benefit_type ? "/#{benefit_type}" : "" + + headers = { + "Authorization" => "Token #{api_key}", + "X-VA-Receipt-Date" => receipt_date.try(:strftime, "%F") || receipt_date + } + + if file_number.present? + headers["X-VA-File-Number"] = file_number + elsif ssn.present? + headers["X-VA-SSN"] = ssn + end + get( "/api/v3/decision_reviews/#{decision_review_type}s/contestable_issues#{benefit_type_url_string}", - headers: { - "Authorization" => "Token #{api_key}", - "X-VA-SSN" => ssn, - "X-VA-Receipt-Date" => receipt_date.try(:strftime, "%F") || receipt_date - } + headers: headers ) end end diff --git a/spec/support/shared_examples/requests/api/v3/decision_reviews/shared_examples_contestable_issues.rb b/spec/support/shared_examples/requests/api/v3/decision_reviews/shared_examples_contestable_issues.rb index 55c543200f3..023e8312546 100644 --- a/spec/support/shared_examples/requests/api/v3/decision_reviews/shared_examples_contestable_issues.rb +++ b/spec/support/shared_examples/requests/api/v3/decision_reviews/shared_examples_contestable_issues.rb @@ -6,19 +6,45 @@ describe "#index" do include_context "contestable issues request index context", include_shared: true - it "should return a 200 OK" do - get_issues - expect(response).to have_http_status(:ok) - end - - it "should return a list of issues in JSON:API format" do + let(:promulgated_ratings) do Generators::PromulgatedRating.build( participant_id: veteran.ptcpnt_id, profile_date: Time.zone.today - 10.days # must be before receipt_date ) - get_issues - expect(response_data).to be_an Array - expect(response_data).not_to be_empty + end + + context "when SSN is used" do + let(:file_number) { nil } + let(:ssn) { veteran.ssn } + + it "should return 200 OK" do + get_issues + expect(response).to have_http_status(:ok) + end + + it "should return a list of issues in JSON:API format" do + promulgated_ratings + get_issues + expect(response_data).to be_an Array + expect(response_data).not_to be_empty + end + end + + context "when file_number is used" do + let(:file_number) { veteran.file_number } + let(:ssn) { nil } + + it "should return 200 OK" do + get_issues + expect(response).to have_http_status(:ok) + end + + it "should return a list of issues in JSON:API format" do + promulgated_ratings + get_issues + expect(response_data).to be_an Array + expect(response_data).not_to be_empty + end end context "returned issues" do @@ -276,5 +302,15 @@ expect(response).to have_http_status(:unprocessable_entity) end end + + context "when no ssn or file_number is present" do + let(:ssn) { nil } + let(:file_number) { nil } + + it "should return a 422" do + get_issues + expect(response).to have_http_status(:unprocessable_entity) + end + end end end