From 3c322cb51bedaf80422e30d3a9d7e277aff1ec62 Mon Sep 17 00:00:00 2001 From: Robert Travis Pierce Date: Thu, 26 Sep 2024 10:11:54 -0500 Subject: [PATCH] Merge feature/APPEALS-41559 into release/FY24Q4.6.0 (#22979) * Start feature work tracking PR with a TODO comment * Remove TODO comment for CodeClimate * SeanC/APPEALS-42315 | Create separate Remand table, model and seed data (#22220) * added migration file for adding type column to supplemental claims table * created stub for remand model * added type to supplemental claim factory and new remand trait * added remand sc's to vha seeds * updated migration name and fixed typo * Changed migration to inherit from Caseflow::Mirgration * schema changes * reverting schema * changed the setting of the type column * fixed schema and renamed migration file * fixed migration and schema * updated the remands factory and seeds * fixed comment in schema --------- Co-authored-by: Robert Travis Pierce * TYLERB/APPEALS-42455: Display new and migrated Remands in Decision Review Queue tabs (#22206) * Added support for remands to the business_line.rb model and the index.js util file that parses the decision review type filters for the decision review queue. * Updated the business line spec file, the decision reviews controller spec tests, and the reviews spec feature test to support remands. * Added a decision review polymorphic helper to DRY up some of the polymorphic appeal concerns. * Overrode the remand model association to request issues so active record joins will work correctly. * Added another helper for sti polymorphic relationships. Fixed filtering for STI decision review types for remands. Updated tests and added factories for testing. * Code climate fixes and test fixes. * Fixed more test failures. Removed the remand request issues relationship since it was incorrectly implemented. Added a scope to supplemental claims for remands. Fixed an issue where board grant effectuation tasks without request issues were missing from the new task type count method. * Changed the UNION ALL to a UNION in the new task_type_count method to prevent duplicate row counts. Updated tests and fixed an issue where the board grant effectuation tasks were being counted for pending task queries. * JHoang/APPEALS-42318 (#22270) * some preliminary remand & sc model updates * typo * some preliminary hlr checks? * undo seed change * decision issue and sc updates * attempt code climate fixes * another code climate fix * testing changes * useless variable.. * fix conditional in decision issue * fix undefined find_by method * attempt rspec fix * rspec fix attempt #2 * supplemental_claim_spec fix? * remove comma * code climate fix * added comment for transparency on remand creation, attempt fix for remands route * updated specs for decision_issue, hlr_spec, appeal_spec to account for remand creation type * fix lint error * fix lint error #2 --------- Co-authored-by: Jonathan Hoang * APPEALS-42458 (#22365) * disable edit issues link for remands * add banners explaining that remands are not editable also makes sure that remands are not editable on the edit issues page * TYLERB/APPEALS-44922: Update Generate task report page UI to include Remand in conditions (#22356) * Initial commit. Changed the DecisionReviewType condition filter to a searchable dropdown and added the remands option to it as well. Updated the report page filter parsing to work with the searchable dropdown instead of checkboxes for the decision review type condition filter. * Updated the ReportPage jest test and the report page feature test to work with the decision review type filter as a searchable dropdown instead of checkboxes. * SeanC/APPEALS-42317 | Migrate existing Supplemental Claim Remands to Remand records via script (#22359) * initial verison of update script * remanded migration file * updated sc spec with type * schema * fixed a few issues in the migration * small fix to migration file * moved the type column check into a seperate it block * fixed wrong column in migration file * created a new migration for creating the view * updated the backfill migration file --------- Co-authored-by: Brandon Lee Dorner * J hoang/appeals 44940 (#22428) * initial * some factory and spec updates * change history query update and spec fixes * code climate/lint fix * change @claim_type identifier * fix claim_history_event_spec.rb * test coverage for change history with Remand claim type * spec lint fixes * refactor claim_type filtering to account for remand subtype * business_line_spec fix * decision_reviews_controller_spec update for remand claim type * lint fixes for decision_reviews_controller_spec.rb * comment updates in business_line.rb * renamed mock data in change_history_reporter_spec * trigger GA * refactor claim type filter for less conditional handling * business line spec fix --------- Co-authored-by: Jonathan Hoang * Initial commit for the brakeman sql injection warning fixes. (#22598) * Updated the business line reporter class to properly display Remands as a seperate type than SupplementalClaims. Also added additional testing logic for the business line reporter for hlr and scs since it was only covering appeals. (#22664) Co-authored-by: Robert Travis Pierce * Fixed a bug where the frontend would error due to calling .toLowerCase() on a null or undefined value which could happen sometimes if the api filter options are in an incorrect state. (#22796) Co-authored-by: Robert Travis Pierce * Updated the remand migration files to work with the rails 6 upgrade and the changes to Caseflow::Migration. (#22825) * added fix for failing test in decision_reviews_controller_spec (#22829) --------- Co-authored-by: Robert Travis Pierce Co-authored-by: Sean Craig <110493538+seancva@users.noreply.github.com> Co-authored-by: Tyler Broyles <109369527+TylerBroyles@users.noreply.github.com> Co-authored-by: jonathanh-va <111081469+jonathanh-va@users.noreply.github.com> Co-authored-by: Jonathan Hoang Co-authored-by: Brandon Lee Dorner Co-authored-by: Clay Sheppard --- ...e_belongs_to_polymorphic_appeal_concern.rb | 14 +- ...w_belongs_to_polymorphic_appeal_concern.rb | 14 +- ...s_belongs_to_polymorphic_appeal_concern.rb | 14 +- ...t_belongs_to_polymorphic_appeal_concern.rb | 24 +- ...t_belongs_to_polymorphic_appeal_concern.rb | 14 +- ...e_belongs_to_polymorphic_appeal_concern.rb | 21 +- .../decision_review_polymorphic_helper.rb | 30 ++ .../decision_review_polymorphic_sti_helper.rb | 29 ++ ...t_belongs_to_polymorphic_appeal_concern.rb | 14 +- ...w_belongs_to_polymorphic_appeal_concern.rb | 14 +- ..._belongs_to_polymorphic_hearing_concern.rb | 14 +- ...t_belongs_to_polymorphic_appeal_concern.rb | 14 +- ...k_belongs_to_polymorphic_appeal_concern.rb | 26 +- ...t_belongs_to_polymorphic_appeal_concern.rb | 14 +- app/models/decision_issue.rb | 33 ++- app/models/organizations/business_line.rb | 125 ++++++-- app/models/remand.rb | 4 + app/models/supplemental_claim.rb | 2 + app/services/business_line_reporter.rb | 2 +- .../claim_history_event.rb | 5 +- app/views/supplemental_claims/edit.html.erb | 1 + client/COPY.json | 4 +- client/app/components/TableFilter.jsx | 6 +- .../app/intake/pages/addIssues/addIssues.jsx | 5 + .../app/intakeEdit/components/EditButtons.jsx | 5 +- client/app/intakeEdit/reducers/index.js | 2 + client/app/nonComp/components/Disposition.jsx | 19 +- .../Conditions/DecisionReviewType.jsx | 69 ++--- client/app/nonComp/pages/ReportPage.jsx | 4 +- client/app/nonComp/util/index.js | 5 + .../test/app/nonComp/pages/ReportPage.test.js | 30 +- config/routes.rb | 1 + ...plemental_claims_for_remand_inheritance.rb | 11 + ...ackfill_supplemental_claims_type_column.rb | 20 ++ .../20240805154526_create_remands_view.rb | 20 ++ db/schema.rb | 2 + db/seeds/veterans_health_administration.rb | 11 + .../decision_reviews_controller_spec.rb | 122 ++++++-- spec/factories/supplemental_claim.rb | 11 + spec/factories/task.rb | 18 ++ .../intake/supplemental_claim/edit_spec.rb | 20 ++ spec/feature/non_comp/dispositions_spec.rb | 22 ++ spec/feature/non_comp/report_page_spec.rb | 12 +- spec/feature/non_comp/reviews_spec.rb | 84 ++++-- spec/lib/helpers/association_wrapper_spec.rb | 9 +- spec/models/appeal_spec.rb | 6 +- spec/models/business_line_spec.rb | 140 +++++++-- spec/models/decision_issue_spec.rb | 24 ++ spec/models/higher_level_review_spec.rb | 2 + spec/models/supplemental_claim_spec.rb | 27 ++ spec/services/business_line_reporter_spec.rb | 23 +- .../change_history_reporter_spec.rb | 279 +++++++++++++++++- .../claim_history_event_spec.rb | 11 +- 53 files changed, 1092 insertions(+), 360 deletions(-) create mode 100644 app/models/concerns/decision_review_polymorphic_helper.rb create mode 100644 app/models/concerns/decision_review_polymorphic_sti_helper.rb create mode 100644 app/models/remand.rb create mode 100644 db/migrate/20240716143816_add_type_column_and_index_to_supplemental_claims_for_remand_inheritance.rb create mode 100644 db/migrate/20240723152034_backfill_supplemental_claims_type_column.rb create mode 100644 db/migrate/20240805154526_create_remands_view.rb diff --git a/app/models/concerns/appeal_state_belongs_to_polymorphic_appeal_concern.rb b/app/models/concerns/appeal_state_belongs_to_polymorphic_appeal_concern.rb index 963b6e26371..7d3a575c7b7 100644 --- a/app/models/concerns/appeal_state_belongs_to_polymorphic_appeal_concern.rb +++ b/app/models/concerns/appeal_state_belongs_to_polymorphic_appeal_concern.rb @@ -2,19 +2,9 @@ module AppealStateBelongsToPolymorphicAppealConcern extend ActiveSupport::Concern + include DecisionReviewPolymorphicHelper included do - belongs_to :appeal, polymorphic: true - - belongs_to :ama_appeal, - -> { where(appeal_states: { appeal_type: "Appeal" }) }, - class_name: "Appeal", foreign_key: "appeal_id", optional: true - - belongs_to :legacy_appeal, - -> { where(appeal_states: { appeal_type: "LegacyAppeal" }) }, - class_name: "LegacyAppeal", foreign_key: "appeal_id", optional: true - - scope :ama, -> { where(appeal_type: "Appeal") } - scope :legacy, -> { where(appeal_type: "LegacyAppeal") } + define_polymorphic_decision_review_associations(:appeal, :appeal_states, %w[Appeal LegacyAppeal]) end end diff --git a/app/models/concerns/attorney_case_review_belongs_to_polymorphic_appeal_concern.rb b/app/models/concerns/attorney_case_review_belongs_to_polymorphic_appeal_concern.rb index 6ee39b146d2..70039fbc321 100644 --- a/app/models/concerns/attorney_case_review_belongs_to_polymorphic_appeal_concern.rb +++ b/app/models/concerns/attorney_case_review_belongs_to_polymorphic_appeal_concern.rb @@ -2,19 +2,9 @@ module AttorneyCaseReviewBelongsToPolymorphicAppealConcern extend ActiveSupport::Concern + include DecisionReviewPolymorphicHelper included do - belongs_to :appeal, polymorphic: true - - belongs_to :ama_appeal, - -> { where(attorney_case_reviews: { appeal_type: "Appeal" }) }, - class_name: "Appeal", foreign_key: "appeal_id", optional: true - - belongs_to :legacy_appeal, - -> { where(attorney_case_reviews: { appeal_type: "LegacyAppeal" }) }, - class_name: "LegacyAppeal", foreign_key: "appeal_id", optional: true - - scope :ama, -> { where(appeal_type: "Appeal") } - scope :legacy, -> { where(appeal_type: "LegacyAppeal") } + define_polymorphic_decision_review_associations(:appeal, :attorney_case_reviews, %w[Appeal LegacyAppeal]) end end diff --git a/app/models/concerns/available_hearing_locations_belongs_to_polymorphic_appeal_concern.rb b/app/models/concerns/available_hearing_locations_belongs_to_polymorphic_appeal_concern.rb index 901c94706de..926a75fba65 100644 --- a/app/models/concerns/available_hearing_locations_belongs_to_polymorphic_appeal_concern.rb +++ b/app/models/concerns/available_hearing_locations_belongs_to_polymorphic_appeal_concern.rb @@ -2,19 +2,9 @@ module AvailableHearingLocationsBelongsToPolymorphicAppealConcern extend ActiveSupport::Concern + include DecisionReviewPolymorphicHelper included do - belongs_to :appeal, polymorphic: true - - belongs_to :ama_appeal, - -> { where(available_hearing_locations: { appeal_type: "Appeal" }) }, - class_name: "Appeal", foreign_key: "appeal_id", optional: true - - belongs_to :legacy_appeal, - -> { where(available_hearing_locations: { appeal_type: "LegacyAppeal" }) }, - class_name: "LegacyAppeal", foreign_key: "appeal_id", optional: true - - scope :ama, -> { where(appeal_type: "Appeal") } - scope :legacy, -> { where(appeal_type: "LegacyAppeal") } + define_polymorphic_decision_review_associations(:appeal, :available_hearing_locations, %w[Appeal LegacyAppeal]) end end diff --git a/app/models/concerns/claimant_belongs_to_polymorphic_appeal_concern.rb b/app/models/concerns/claimant_belongs_to_polymorphic_appeal_concern.rb index dd7de661815..b32a1b9b521 100644 --- a/app/models/concerns/claimant_belongs_to_polymorphic_appeal_concern.rb +++ b/app/models/concerns/claimant_belongs_to_polymorphic_appeal_concern.rb @@ -2,29 +2,9 @@ module ClaimantBelongsToPolymorphicAppealConcern extend ActiveSupport::Concern + include DecisionReviewPolymorphicHelper included do - belongs_to :decision_review, polymorphic: true - - belongs_to :ama_appeal, - -> { where(claimants: { decision_review_type: "Appeal" }) }, - class_name: "Appeal", foreign_key: "decision_review_id", optional: true - - belongs_to :legacy_appeal, - -> { where(claimants: { decision_review_type: "LegacyAppeal" }) }, - class_name: "LegacyAppeal", foreign_key: "decision_review_id", optional: true - - belongs_to :higher_level_review, - -> { where(claimants: { decision_review_type: "HigherLevelReview" }) }, - class_name: "HigherLevelReview", foreign_key: "decision_review_id", optional: true - - belongs_to :supplemental_claim, - -> { where(claimants: { decision_review_type: "SupplementalClaim" }) }, - class_name: "SupplementalClaim", foreign_key: "decision_review_id", optional: true - - scope :ama, -> { where(decision_review_type: "Appeal") } - scope :legacy, -> { where(decision_review_type: "LegacyAppeal") } - scope :higher_level_review, -> { where(decision_review_type: "HigherLevelReview") } - scope :supplemental_claim, -> { where(decision_review_type: "SupplementalClaim") } + define_polymorphic_decision_review_associations(:decision_review, :claimants) end end diff --git a/app/models/concerns/decision_document_belongs_to_polymorphic_appeal_concern.rb b/app/models/concerns/decision_document_belongs_to_polymorphic_appeal_concern.rb index 264029a5212..92f07fa0516 100644 --- a/app/models/concerns/decision_document_belongs_to_polymorphic_appeal_concern.rb +++ b/app/models/concerns/decision_document_belongs_to_polymorphic_appeal_concern.rb @@ -2,19 +2,9 @@ module DecisionDocumentBelongsToPolymorphicAppealConcern extend ActiveSupport::Concern + include DecisionReviewPolymorphicHelper included do - belongs_to :appeal, polymorphic: true - - belongs_to :ama_appeal, - -> { where(decision_documents: { appeal_type: "Appeal" }) }, - class_name: "Appeal", foreign_key: "appeal_id", optional: true - - belongs_to :legacy_appeal, - -> { where(decision_documents: { appeal_type: "LegacyAppeal" }) }, - class_name: "LegacyAppeal", foreign_key: "appeal_id", optional: true - - scope :ama, -> { where(appeal_type: "Appeal") } - scope :legacy, -> { where(appeal_type: "LegacyAppeal") } + define_polymorphic_decision_review_associations(:appeal, :decision_documents, %w[Appeal LegacyAppeal]) end end diff --git a/app/models/concerns/decision_issue_belongs_to_polymorphic_appeal_concern.rb b/app/models/concerns/decision_issue_belongs_to_polymorphic_appeal_concern.rb index ae4cd6ba075..f4355794a27 100644 --- a/app/models/concerns/decision_issue_belongs_to_polymorphic_appeal_concern.rb +++ b/app/models/concerns/decision_issue_belongs_to_polymorphic_appeal_concern.rb @@ -2,24 +2,11 @@ module DecisionIssueBelongsToPolymorphicAppealConcern extend ActiveSupport::Concern + include DecisionReviewPolymorphicHelper included do - belongs_to :decision_review, polymorphic: true - - belongs_to :ama_appeal, - -> { where(decision_issues: { decision_review_type: "Appeal" }) }, - class_name: "Appeal", foreign_key: "decision_review_id", optional: true - - belongs_to :higher_level_review, - -> { where(decision_issues: { decision_review_type: "HigherLevelReview" }) }, - class_name: "HigherLevelReview", foreign_key: "decision_review_id", optional: true - - belongs_to :supplemental_claim, - -> { where(decision_issues: { decision_review_type: "SupplementalClaim" }) }, - class_name: "SupplementalClaim", foreign_key: "decision_review_id", optional: true - - scope :ama, -> { where(decision_review_type: "Appeal") } - scope :higher_level_review, -> { where(decision_review_type: "HigherLevelReview") } - scope :supplemental_claim, -> { where(decision_review_type: "SupplementalClaim") } + define_polymorphic_decision_review_associations(:decision_review, + :decision_issues, + %w[Appeal HigherLevelReview SupplementalClaim]) end end diff --git a/app/models/concerns/decision_review_polymorphic_helper.rb b/app/models/concerns/decision_review_polymorphic_helper.rb new file mode 100644 index 00000000000..4082a2314de --- /dev/null +++ b/app/models/concerns/decision_review_polymorphic_helper.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module DecisionReviewPolymorphicHelper + extend ActiveSupport::Concern + + class_methods do + def define_polymorphic_decision_review_associations(association_name, from_association_name, types = nil) + belongs_to association_name, polymorphic: true + + # Specific association mappings that are uniquely different from the calculated class name to underscored symbol + association_name_mapping = { "Appeal" => :ama_appeal, "Hearing" => :ama_hearing } + scope_mapping = { "Appeal" => :ama, "LegacyAppeal" => :legacy, "LegacyHearing" => :legacy, "Hearing" => :ama } + + # LegacyAppeals + all of the non abstract subtypes of DecisionReview not incuding child types for STI + types ||= %w[Appeal LegacyAppeal HigherLevelReview SupplementalClaim] + + types.each do |type| + type_symbol = type.underscore.to_sym + belongs_to_association_name = association_name_mapping[type] || type_symbol + scope_name = scope_mapping[type] || type_symbol + + belongs_to belongs_to_association_name, + -> { where(from_association_name => { "#{association_name}_type": type }) }, + class_name: type, foreign_key: "#{association_name}_id", optional: true + + scope scope_name.to_sym, -> { where("#{association_name}_type": type) } + end + end + end +end diff --git a/app/models/concerns/decision_review_polymorphic_sti_helper.rb b/app/models/concerns/decision_review_polymorphic_sti_helper.rb new file mode 100644 index 00000000000..b7a1af23778 --- /dev/null +++ b/app/models/concerns/decision_review_polymorphic_sti_helper.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module DecisionReviewPolymorphicSTIHelper + extend ActiveSupport::Concern + + class_methods do + def define_polymorphic_decision_review_sti_associations(association_name, from_association_name, types = nil) + # Mappings between STI types and their associated parent type and parent database table + sti_table_mapping = { "Remand" => :supplemental_claims } + sti_type_mapping = { "Remand" => "SupplementalClaim" } + + types ||= %w[Remand] + + types.each do |type| + type_symbol = type.underscore.to_sym + belongs_to_association_name = type_symbol + sti_type = sti_type_mapping[type] || type + sti_table_name = sti_table_mapping[type] || association_name + + belongs_to belongs_to_association_name, + lambda { + where(from_association_name => { "#{association_name}_type": sti_type }) + .where(Arel::Table.new(sti_table_name)[:type].eq(type)) + }, + class_name: type, foreign_key: "#{association_name}_id", optional: true + end + end + end +end diff --git a/app/models/concerns/hearing_email_recipient_belongs_to_polymorphic_appeal_concern.rb b/app/models/concerns/hearing_email_recipient_belongs_to_polymorphic_appeal_concern.rb index 772a47edb85..213b3edb166 100644 --- a/app/models/concerns/hearing_email_recipient_belongs_to_polymorphic_appeal_concern.rb +++ b/app/models/concerns/hearing_email_recipient_belongs_to_polymorphic_appeal_concern.rb @@ -2,19 +2,9 @@ module HearingEmailRecipientBelongsToPolymorphicAppealConcern extend ActiveSupport::Concern + include DecisionReviewPolymorphicHelper included do - belongs_to :appeal, polymorphic: true - - belongs_to :ama_appeal, - -> { where(hearing_email_recipients: { appeal_type: "Appeal" }) }, - class_name: "Appeal", foreign_key: "appeal_id", optional: true - - belongs_to :legacy_appeal, - -> { where(hearing_email_recipients: { appeal_type: "LegacyAppeal" }) }, - class_name: "LegacyAppeal", foreign_key: "appeal_id", optional: true - - scope :ama, -> { where(appeal_type: "Appeal") } - scope :legacy, -> { where(appeal_type: "LegacyAppeal") } + define_polymorphic_decision_review_associations(:appeal, :hearing_email_recipients, %w[Appeal LegacyAppeal]) end end diff --git a/app/models/concerns/judge_case_review_belongs_to_polymorphic_appeal_concern.rb b/app/models/concerns/judge_case_review_belongs_to_polymorphic_appeal_concern.rb index 9400c88b5f1..8fae1ccf999 100644 --- a/app/models/concerns/judge_case_review_belongs_to_polymorphic_appeal_concern.rb +++ b/app/models/concerns/judge_case_review_belongs_to_polymorphic_appeal_concern.rb @@ -2,19 +2,9 @@ module JudgeCaseReviewBelongsToPolymorphicAppealConcern extend ActiveSupport::Concern + include DecisionReviewPolymorphicHelper included do - belongs_to :appeal, polymorphic: true - - belongs_to :ama_appeal, - -> { where(judge_case_reviews: { appeal_type: "Appeal" }) }, - class_name: "Appeal", foreign_key: "appeal_id", optional: true - - belongs_to :legacy_appeal, - -> { where(judge_case_reviews: { appeal_type: "LegacyAppeal" }) }, - class_name: "LegacyAppeal", foreign_key: "appeal_id", optional: true - - scope :ama, -> { where(appeal_type: "Appeal") } - scope :legacy, -> { where(appeal_type: "LegacyAppeal") } + define_polymorphic_decision_review_associations(:appeal, :judge_case_reviews, %w[Appeal LegacyAppeal]) end end diff --git a/app/models/concerns/sent_hearing_email_event_belongs_to_polymorphic_hearing_concern.rb b/app/models/concerns/sent_hearing_email_event_belongs_to_polymorphic_hearing_concern.rb index d0cc44c4cb9..3c3f3453cf7 100644 --- a/app/models/concerns/sent_hearing_email_event_belongs_to_polymorphic_hearing_concern.rb +++ b/app/models/concerns/sent_hearing_email_event_belongs_to_polymorphic_hearing_concern.rb @@ -2,19 +2,9 @@ module SentHearingEmailEventBelongsToPolymorphicHearingConcern extend ActiveSupport::Concern + include DecisionReviewPolymorphicHelper included do - belongs_to :hearing, polymorphic: true - - belongs_to :ama_hearing, - -> { where(sent_hearing_email_events: { hearing_type: "Hearing" }) }, - class_name: "Hearing", foreign_key: "hearing_id", optional: true - - belongs_to :legacy_hearing, - -> { where(sent_hearing_email_events: { hearing_type: "LegacyHearing" }) }, - class_name: "LegacyHearing", foreign_key: "hearing_id", optional: true - - scope :ama, -> { where(hearing_type: "Hearing") } - scope :legacy, -> { where(hearing_type: "LegacyHearing") } + define_polymorphic_decision_review_associations(:hearing, :sent_hearing_email_events, %w[Hearing LegacyHearing]) end end diff --git a/app/models/concerns/special_issue_list_belongs_to_polymorphic_appeal_concern.rb b/app/models/concerns/special_issue_list_belongs_to_polymorphic_appeal_concern.rb index ed19b084975..44bf31e835a 100644 --- a/app/models/concerns/special_issue_list_belongs_to_polymorphic_appeal_concern.rb +++ b/app/models/concerns/special_issue_list_belongs_to_polymorphic_appeal_concern.rb @@ -2,19 +2,9 @@ module SpecialIssueListBelongsToPolymorphicAppealConcern extend ActiveSupport::Concern + include DecisionReviewPolymorphicHelper included do - belongs_to :appeal, polymorphic: true - - belongs_to :ama_appeal, - -> { where(special_issue_lists: { appeal_type: "Appeal" }) }, - class_name: "Appeal", foreign_key: "appeal_id", optional: true - - belongs_to :legacy_appeal, - -> { where(special_issue_lists: { appeal_type: "LegacyAppeal" }) }, - class_name: "LegacyAppeal", foreign_key: "appeal_id", optional: true - - scope :ama, -> { where(appeal_type: "Appeal") } - scope :legacy, -> { where(appeal_type: "LegacyAppeal") } + define_polymorphic_decision_review_associations(:appeal, :special_issue_lists, %w[Appeal LegacyAppeal]) end end diff --git a/app/models/concerns/task_belongs_to_polymorphic_appeal_concern.rb b/app/models/concerns/task_belongs_to_polymorphic_appeal_concern.rb index 56a321d80e4..d1666b1ff93 100644 --- a/app/models/concerns/task_belongs_to_polymorphic_appeal_concern.rb +++ b/app/models/concerns/task_belongs_to_polymorphic_appeal_concern.rb @@ -2,29 +2,11 @@ module TaskBelongsToPolymorphicAppealConcern extend ActiveSupport::Concern + include DecisionReviewPolymorphicHelper + include DecisionReviewPolymorphicSTIHelper included do - belongs_to :appeal, polymorphic: true - - belongs_to :ama_appeal, - -> { where(tasks: { appeal_type: "Appeal" }) }, - class_name: "Appeal", foreign_key: "appeal_id", optional: true - - belongs_to :legacy_appeal, - -> { where(tasks: { appeal_type: "LegacyAppeal" }) }, - class_name: "LegacyAppeal", foreign_key: "appeal_id", optional: true - - belongs_to :higher_level_review, - -> { where(tasks: { appeal_type: "HigherLevelReview" }) }, - class_name: "HigherLevelReview", foreign_key: "appeal_id", optional: true - - belongs_to :supplemental_claim, - -> { where(tasks: { appeal_type: "SupplementalClaim" }) }, - class_name: "SupplementalClaim", foreign_key: "appeal_id", optional: true - - scope :ama, -> { where(appeal_type: "Appeal") } - scope :legacy, -> { where(appeal_type: "LegacyAppeal") } - scope :higher_level_review, -> { where(appeal_type: "HigherLevelReview") } - scope :supplemental_claim, -> { where(appeal_type: "SupplementalClaim") } + define_polymorphic_decision_review_associations(:appeal, :tasks) + define_polymorphic_decision_review_sti_associations(:appeal, :tasks) end end diff --git a/app/models/concerns/vbms_uploaded_document_belongs_to_polymorphic_appeal_concern.rb b/app/models/concerns/vbms_uploaded_document_belongs_to_polymorphic_appeal_concern.rb index 6e332469f35..e2f89793d40 100644 --- a/app/models/concerns/vbms_uploaded_document_belongs_to_polymorphic_appeal_concern.rb +++ b/app/models/concerns/vbms_uploaded_document_belongs_to_polymorphic_appeal_concern.rb @@ -2,19 +2,9 @@ module VbmsUploadedDocumentBelongsToPolymorphicAppealConcern extend ActiveSupport::Concern + include DecisionReviewPolymorphicHelper included do - belongs_to :appeal, polymorphic: true - - belongs_to :ama_appeal, - -> { where(vbms_uploaded_documents: { appeal_type: "Appeal" }) }, - class_name: "Appeal", foreign_key: "appeal_id", optional: true - - belongs_to :legacy_appeal, - -> { where(vbms_uploaded_documents: { appeal_type: "LegacyAppeal" }) }, - class_name: "LegacyAppeal", foreign_key: "appeal_id", optional: true - - scope :ama, -> { where(appeal_type: "Appeal") } - scope :legacy, -> { where(appeal_type: "LegacyAppeal") } + define_polymorphic_decision_review_associations(:appeal, :vbms_uploaded_documents, %w[Appeal LegacyAppeal]) end end diff --git a/app/models/decision_issue.rb b/app/models/decision_issue.rb index 95ef1f19422..7dff27d5f0f 100644 --- a/app/models/decision_issue.rb +++ b/app/models/decision_issue.rb @@ -250,14 +250,7 @@ def create_remand_supplemental_claim! # Checking our assumption that approx_decision_date will always be populated for Decision Issues fail "approx_decision_date is required to create a DTA Supplemental Claim" unless approx_decision_date - sc = SupplementalClaim.create!( - veteran_file_number: veteran_file_number, - decision_review_remanded: decision_review, - benefit_type: benefit_type, - legacy_opt_in_approved: decision_review.legacy_opt_in_approved, - veteran_is_not_claimant: decision_review.veteran_is_not_claimant, - receipt_date: approx_decision_date - ) + sc = determine_remand_creation_type fail AppealDTAPayeeCodeError, decision_review.id unless dta_payee_code sc.create_claimant!( @@ -273,4 +266,28 @@ def create_remand_supplemental_claim! decision_review.update_error!("DTA SC creation failed") raise end + + # Create a Remand if the decision review is an Appeal. + # HLRs will still create remands as SupplementalClaims as of (APPEALS-41559) + def determine_remand_creation_type + if decision_review_type == Appeal.name + Remand.create!( + veteran_file_number: veteran_file_number, + decision_review_remanded: decision_review, + benefit_type: benefit_type, + legacy_opt_in_approved: decision_review.legacy_opt_in_approved, + veteran_is_not_claimant: decision_review.veteran_is_not_claimant, + receipt_date: approx_decision_date + ) + else + SupplementalClaim.create!( + veteran_file_number: veteran_file_number, + decision_review_remanded: decision_review, + benefit_type: benefit_type, + legacy_opt_in_approved: decision_review.legacy_opt_in_approved, + veteran_is_not_claimant: decision_review.veteran_is_not_claimant, + receipt_date: approx_decision_date + ) + end + end end diff --git a/app/models/organizations/business_line.rb b/app/models/organizations/business_line.rb index 5b43066bce2..02a881a48af 100644 --- a/app/models/organizations/business_line.rb +++ b/app/models/organizations/business_line.rb @@ -106,6 +106,9 @@ class QueryBuilder "SupplementalClaim" => Task.arel_table[:appeal_type] .eq("SupplementalClaim") .and(Task.arel_table[:type].eq(DecisionReviewTask.name)) + .and(Arel.sql("sub_type").not_eq("Remand")), + "Remand" => Arel.sql("sub_type").eq("Remand") + .and(Task.arel_table[:type].eq(DecisionReviewTask.name)) }.freeze DEFAULT_ORDERING_HASH = { @@ -140,35 +143,69 @@ def build_query .order(order_clause) end - def task_type_count - Task.select(Task.arel_table[:type]) - .from(combined_decision_review_tasks_query) - .group(Task.arel_table[:type], Task.arel_table[:appeal_type]) - .count - end - - # rubocop:disable Metrics/MethodLength - # rubocop:disable Metrics/AbcSize - def issue_type_count - shared_select_statement = "tasks.id as tasks_id, request_issues.nonrating_issue_category as issue_category" - appeals_query = Task.send(parent.tasks_query_type[query_type]) - .select(shared_select_statement) - .joins(ama_appeal: :request_issues) + def task_type_query_helper(join_association) + Task.send(parent.tasks_query_type[query_type]) + .select("tasks.id AS task_id, tasks.type AS task_type") + .joins(join_association) .joins(issue_modification_request_join) .where(query_constraints) .where(issue_modification_request_filter) - hlr_query = Task.send(parent.tasks_query_type[query_type]) - .select(shared_select_statement) - .joins(supplemental_claim: :request_issues) + end + + def task_type_board_grant_helper + Task.send(parent.tasks_query_type[query_type]) + .select("tasks.id AS task_id, tasks.type AS task_type, 'Appeal' AS decision_review_type") + .joins(board_grant_effectuation_task_appeals_requests_join) .joins(issue_modification_request_join) - .where(query_constraints) + .where(board_grant_effectuation_task_constraints) .where(issue_modification_request_filter) - sc_query = Task.send(parent.tasks_query_type[query_type]) - .select(shared_select_statement) - .joins(higher_level_review: :request_issues) + end + + def issue_type_query_helper(join_association) + Task.send(parent.tasks_query_type[query_type]) + .select("tasks.id as task_id, request_issues.nonrating_issue_category AS issue_category") + .joins(join_association) .joins(issue_modification_request_join) .where(query_constraints) .where(issue_modification_request_filter) + end + + # rubocop:disable Metrics/MethodLength + def task_type_count + appeals_query = task_type_query_helper(ama_appeal: :request_issues) + .select("'Appeal' AS decision_review_type") + hlr_query = task_type_query_helper(higher_level_review: :request_issues) + .select("'HigherLevelReview' AS decision_review_type") + sc_query = task_type_query_helper(supplemental_claim: :request_issues) + .select("supplemental_claims.type AS decision_review_type") + board_grant_query = task_type_board_grant_helper + + task_count = ActiveRecord::Base.connection.execute <<-SQL + WITH task_review_issues AS ( + #{hlr_query.to_sql} + UNION + #{sc_query.to_sql} + UNION + #{appeals_query.to_sql} + UNION + #{board_grant_query.to_sql} + ) + SELECT task_type, decision_review_type, COUNT(1) + FROM task_review_issues + GROUP BY task_type, decision_review_type; + SQL + + task_count.reduce({}) do |acc, item| + key = [item["task_type"], item["decision_review_type"]] + acc[key] = (acc[key] || 0) + item["count"] + acc + end + end + + def issue_type_count + appeals_query = issue_type_query_helper(ama_appeal: :request_issues) + hlr_query = issue_type_query_helper(higher_level_review: :request_issues) + sc_query = issue_type_query_helper(supplemental_claim: :request_issues) nonrating_issue_count = ActiveRecord::Base.connection.execute <<-SQL WITH task_review_issues AS ( @@ -197,12 +234,12 @@ def issue_type_count issue_count_options end - # rubocop:enable Metrics/AbcSize def change_history_rows # Generate all of the filter queries to be used in both the HLR and SC block sql = Arel.sql(change_history_sql_filter_array.join(" ")) sanitized_filters = ActiveRecord::Base.sanitize_sql_array([sql]) + sc_type_clauses = ActiveRecord::Base.sanitize_sql_array([sc_type_filter]) change_history_sql_block = <<-SQL WITH versions_agg AS NOT MATERIALIZED ( @@ -246,7 +283,7 @@ def change_history_rows NULLIF(CONCAT(unrecognized_party_details.name, ' ', unrecognized_party_details.last_name), ' '), NULLIF(CONCAT(people.first_name, ' ', people.last_name), ' '), bgs_attorneys.name - ) AS claimant_name + ) AS claimant_name, 'HigherLevelReview' AS type_classifier FROM tasks INNER JOIN request_issues ON request_issues.decision_review_type = tasks.appeal_type AND request_issues.decision_review_id = tasks.appeal_id @@ -298,7 +335,7 @@ def change_history_rows NULLIF(CONCAT(unrecognized_party_details.name, ' ', unrecognized_party_details.last_name), ' '), NULLIF(CONCAT(people.first_name, ' ', people.last_name), ' '), bgs_attorneys.name - ) AS claimant_name + ) AS claimant_name, supplemental_claims.type AS type_classifier FROM tasks INNER JOIN request_issues ON request_issues.decision_review_type = tasks.appeal_type AND request_issues.decision_review_id = tasks.appeal_id @@ -326,6 +363,7 @@ def change_history_rows AND tasks.assigned_to_type = 'Organization' AND tasks.assigned_to_id = '#{parent.id.to_i}' #{sanitized_filters} + #{sc_type_clauses} SQL ActiveRecord::Base.transaction do @@ -365,9 +403,27 @@ def task_status_filter def claim_type_filter if query_params[:claim_type].present? - " AND #{where_clause_from_array(Task, :appeal_type, query_params[:claim_type]).to_sql}" + temp_claim_types = query_params[:claim_type].dup + + if query_params[:claim_type].include?(Remand.name) + temp_claim_types.push "SupplementalClaim" + end + + " AND #{where_clause_from_array(Task, :appeal_type, temp_claim_types).to_sql} " + else + " AND tasks.appeal_type IN ('HigherLevelReview', 'SupplementalClaim' ) " + end + end + + def sc_type_filter + if query_params[:claim_type].present? + if query_params[:claim_type].include?(Remand.name) || query_params[:claim_type].include?(SupplementalClaim.name) + " AND #{where_clause_from_array(SupplementalClaim, :type, query_params[:claim_type]).to_sql} " + else + "" + end else - " AND tasks.appeal_type IN ('HigherLevelReview', 'SupplementalClaim') " + "" end end @@ -628,7 +684,7 @@ def search_all_clause def group_by_columns "tasks.id, uuid, veterans.participant_id, veterans.ssn, veterans.first_name, veterans.last_name, "\ "unrecognized_party_details.name, unrecognized_party_details.last_name, people.first_name, people.last_name, "\ - "veteran_is_not_claimant, bgs_attorneys.name" + "veteran_is_not_claimant, bgs_attorneys.name, sub_type" end # Uses an array to insert the searched text into all of the searchable fields since it's the same text for all @@ -638,21 +694,26 @@ def search_values end def higher_level_reviews_on_request_issues - decision_reviews_on_request_issues(higher_level_review: :request_issues) + sub_type_alias = "'HigherLevelReview' AS sub_type" + decision_reviews_on_request_issues({ higher_level_review: :request_issues }, sub_type_alias) end def supplemental_claims_on_request_issues - decision_reviews_on_request_issues(supplemental_claim: :request_issues) + sub_type_alias = "supplemental_claims.type AS sub_type" + decision_reviews_on_request_issues({ supplemental_claim: :request_issues }, sub_type_alias) end def appeals_on_request_issues - decision_reviews_on_request_issues(ama_appeal: :request_issues) + sub_type_alias = "'Appeal' as sub_type" + decision_reviews_on_request_issues({ ama_appeal: :request_issues }, sub_type_alias) end # Specific case for BoardEffectuationGrantTasks to include them in the result set # if the :board_grant_effectuation_task FeatureToggle is enabled for the current user. def board_grant_effectuation_tasks + sub_type_alias = "'Appeal' as sub_type" decision_reviews_on_request_issues(board_grant_effectuation_task_appeals_requests_join, + sub_type_alias, board_grant_effectuation_task_constraints) end @@ -667,8 +728,8 @@ def ama_appeals_query appeals_on_request_issues end - def decision_reviews_on_request_issues(join_constraint, where_constraints = query_constraints) - Task.select(union_select_statements) + def decision_reviews_on_request_issues(join_constraint, subclass_type_alias, where_constraints = query_constraints) + Task.select(union_select_statements.append(subclass_type_alias)) .send(parent.tasks_query_type[query_type]) .joins(join_constraint) .joins(*union_query_join_clauses) diff --git a/app/models/remand.rb b/app/models/remand.rb new file mode 100644 index 00000000000..987220c7c3e --- /dev/null +++ b/app/models/remand.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class Remand < SupplementalClaim +end diff --git a/app/models/supplemental_claim.rb b/app/models/supplemental_claim.rb index 014dea14a0b..ab9312d8758 100644 --- a/app/models/supplemental_claim.rb +++ b/app/models/supplemental_claim.rb @@ -12,6 +12,8 @@ class SupplementalClaim < ClaimReview .where("#{table_name}.decision_review_remanded_type='Appeal'") } + scope :remand, -> { where(type: Remand.name) } + attr_accessor :appeal_split_process def ui_hash diff --git a/app/services/business_line_reporter.rb b/app/services/business_line_reporter.rb index 1ac8490b042..92637539d58 100644 --- a/app/services/business_line_reporter.rb +++ b/app/services/business_line_reporter.rb @@ -26,7 +26,7 @@ def as_csv csv << [ business_line.name, task.appeal_id, - task.appeal_type, + task.appeal.class.review_title, task.appeal.claimant&.name, task.appeal.request_issues.size, task.appeal.decision_issues.size, diff --git a/app/services/claim_change_history/claim_history_event.rb b/app/services/claim_change_history/claim_history_event.rb index 0cae36dc208..010273a561b 100644 --- a/app/services/claim_change_history/claim_history_event.rb +++ b/app/services/claim_change_history/claim_history_event.rb @@ -378,7 +378,8 @@ def readable_task_status def readable_claim_type { "HigherLevelReview" => "Higher-Level Review", - "SupplementalClaim" => "Supplemental Claim" + "SupplementalClaim" => "Supplemental Claim", + "Remand" => "Remand" }[claim_type] end @@ -456,7 +457,7 @@ def set_attributes_from_change_history_data(new_event_type, change_data) def parse_task_attributes(change_data) @task_id = change_data["task_id"] @task_status = change_data["task_status"] - @claim_type = change_data["appeal_type"] + @claim_type = change_data["type_classifier"] @assigned_at = change_data["assigned_at"] @days_waiting = change_data["days_waiting"] end diff --git a/app/views/supplemental_claims/edit.html.erb b/app/views/supplemental_claims/edit.html.erb index 5419b2d10ab..3c0200e7e13 100644 --- a/app/views/supplemental_claims/edit.html.erb +++ b/app/views/supplemental_claims/edit.html.erb @@ -7,6 +7,7 @@ userCanEditIntakeIssues: current_user.can_edit_intake_issues?, userCanRequestIssueUpdates: current_user.can_request_for_issue_updates?, userIsVhaAdmin: current_user.vha_business_line_admin_user?, + isRemand: supplemental_claim.is_a?(Remand), dropdownUrls: dropdown_urls, applicationUrls: application_urls, feedbackUrl: feedback_url, diff --git a/client/COPY.json b/client/COPY.json index bbc5dc80ad9..05191f7d8b0 100644 --- a/client/COPY.json +++ b/client/COPY.json @@ -1,4 +1,3 @@ - { "CASE_SEARCH_HOME_PAGE_HEADING": "Veteran Case Search", "CASE_SEARCH_INPUT_PLACEHOLDER": "Enter a file number, SSN, or AMA docket number", @@ -1516,5 +1515,6 @@ } }, "VHA_BANNER_DISPOSITIONS_CANNOT_BE_UPDATED_NON_ADMIN": "Requests for issue modifications have been submitted for this case. Dispositions cannot be made until a VHA admin completes review of the requested changes.", - "VHA_BANNER_DISPOSITIONS_CANNOT_BE_UPDATED_ADMIN": "Requests for issue modifications have been submitted for this case. Dispositions cannot be made until a VHA admin completes review of the requested changes. Click the \"Edit issues\" button above to review the issue modification requests." + "VHA_BANNER_DISPOSITIONS_CANNOT_BE_UPDATED_ADMIN": "Requests for issue modifications have been submitted for this case. Dispositions cannot be made until a VHA admin completes review of the requested changes. Click the \"Edit issues\" button above to review the issue modification requests.", + "REMANDS_NOT_EDITABLE": "Remands can not be edited." } diff --git a/client/app/components/TableFilter.jsx b/client/app/components/TableFilter.jsx index bccd70ce974..248903e4fb4 100644 --- a/client/app/components/TableFilter.jsx +++ b/client/app/components/TableFilter.jsx @@ -75,7 +75,11 @@ class TableFilter extends React.PureComponent { }); // Case insensitive ordering for the filter options - return _.orderBy(filterOptionsFromApi, [(option) => option.displayText.toLowerCase()], ['asc']); + return _.orderBy( + filterOptionsFromApi.filter((option) => option.displayText), + [(option) => option.displayText.toLowerCase()], + ['asc'] + ); } const columnValues = tableDataByRow.map((obj) => { diff --git a/client/app/intake/pages/addIssues/addIssues.jsx b/client/app/intake/pages/addIssues/addIssues.jsx index d887038ef76..1b567e54753 100644 --- a/client/app/intake/pages/addIssues/addIssues.jsx +++ b/client/app/intake/pages/addIssues/addIssues.jsx @@ -330,6 +330,7 @@ class AddIssuesPage extends React.Component { userIsVhaAdmin, userCanSplitAppeal, userCanRequestIssueUpdates, + isRemand, isLegacy, pendingIssueModificationRequests, intakeFromVbms @@ -426,6 +427,7 @@ class AddIssuesPage extends React.Component { const showRequestIssueUpdateOptions = editPage && userCanRequestIssueUpdates && !originalIssuesHaveNoDecisionDate() && + !isRemand && intakeData.benefitType === 'vha'; const disableIssueActions = editPage && @@ -868,6 +870,8 @@ class AddIssuesPage extends React.Component { {editPage && this.establishmentCredits()} + {editPage && isRemand ? : null} + {!_.isEmpty(issuesPendingWithdrawal) && ( @@ -971,6 +975,7 @@ export const EditAddIssuesPage = connect( userIsVhaAdmin: state.userIsVhaAdmin, userCanSplitAppeal: state.userCanSplitAppeal, userCanRequestIssueUpdates: state.userCanRequestIssueUpdates, + isRemand: state.isRemand, isLegacy: state.isLegacy, intakeFromVbms: state.intakeFromVbms }), diff --git a/client/app/intakeEdit/components/EditButtons.jsx b/client/app/intakeEdit/components/EditButtons.jsx index a624f79f56b..f97d0c5cac6 100644 --- a/client/app/intakeEdit/components/EditButtons.jsx +++ b/client/app/intakeEdit/components/EditButtons.jsx @@ -164,6 +164,7 @@ class SaveButtonUnconnected extends React.Component { benefitType, pendingIssueModificationRequests, originalPendingIssueModificationRequests, + isRemand, openIssueModificationRequests } = this.props; @@ -190,7 +191,7 @@ class SaveButtonUnconnected extends React.Component { const saveDisabled = (_.isEqual(addedIssues, originalIssues) && _.isEqual(pendingIssueModificationRequests, originalPendingIssueModificationRequests)) || invalidVeteran || - !withdrawDateValid || hasPendingAdditionRequests; + !withdrawDateValid || hasPendingAdditionRequests || isRemand; let saveButtonText; @@ -301,6 +302,7 @@ SaveButtonUnconnected.propTypes = { specialtyCaseTeamDistribution: PropTypes.bool, pendingIssueModificationRequests: PropTypes.array, originalPendingIssueModificationRequests: PropTypes.array, + isRemand: PropTypes.bool, openIssueModificationRequests: PropTypes.array, state: PropTypes.shape({ addedIssues: PropTypes.array @@ -325,6 +327,7 @@ const SaveButton = connect( specialtyCaseTeamDistribution: state.featureToggles.specialtyCaseTeamDistribution, pendingIssueModificationRequests: state.pendingIssueModificationRequests, openIssueModificationRequests: getOpenPendingIssueModificationRequests(state), + isRemand: state.isRemand, originalPendingIssueModificationRequests: state.originalPendingIssueModificationRequests, state }), diff --git a/client/app/intakeEdit/reducers/index.js b/client/app/intakeEdit/reducers/index.js index fbcb6a7c6c1..4580d78ffbc 100644 --- a/client/app/intakeEdit/reducers/index.js +++ b/client/app/intakeEdit/reducers/index.js @@ -17,6 +17,7 @@ export const mapDataToInitialState = function(props = {}) { userIsVhaAdmin, userCanSplitAppeal, userCanRequestIssueUpdates, + isRemand, userFullName, userCssId, isLegacy, @@ -48,6 +49,7 @@ export const mapDataToInitialState = function(props = {}) { userIsVhaAdmin, userCanSplitAppeal, userCanRequestIssueUpdates, + isRemand, userCssId, userFullName, isLegacy, diff --git a/client/app/nonComp/components/Disposition.jsx b/client/app/nonComp/components/Disposition.jsx index 4bb4e015ff2..6ad77dbdbd9 100644 --- a/client/app/nonComp/components/Disposition.jsx +++ b/client/app/nonComp/components/Disposition.jsx @@ -177,6 +177,8 @@ class NonCompDispositions extends React.PureComponent { } let editIssuesLink = null; + const editIssuesDisabled = task.type === 'Remand'; + const editIssuesButtonType = editIssuesDisabled ? 'disabled' : 'secondary'; const displayPOAComponent = task.business_line === 'vha'; const displayRequestIssueModification = (!displayPOAComponent || isBusinessLineAdmin); @@ -194,9 +196,17 @@ class NonCompDispositions extends React.PureComponent { ; editIssuesLink = (displayRequestIssueModification) ? - Edit Issues + + Edit Issues + : - Request issue modification + + Request issue modification + ; } @@ -237,6 +247,11 @@ class NonCompDispositions extends React.PureComponent { {isBusinessLineAdmin && decisionHasPendingRequestIssues ? null :
{decisionHeaderText}
} + {editIssuesDisabled ? +
+ + +
: null} {decisionHasPendingRequestIssues ?
diff --git a/client/app/nonComp/components/ReportPage/Conditions/DecisionReviewType.jsx b/client/app/nonComp/components/ReportPage/Conditions/DecisionReviewType.jsx index 0cf4879d68c..85ba8461da3 100644 --- a/client/app/nonComp/components/ReportPage/Conditions/DecisionReviewType.jsx +++ b/client/app/nonComp/components/ReportPage/Conditions/DecisionReviewType.jsx @@ -1,69 +1,58 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { useFormContext } from 'react-hook-form'; -import Checkbox from 'app/components/Checkbox'; +import { Controller, useFormContext } from 'react-hook-form'; import * as yup from 'yup'; import { get } from 'lodash'; +import SearchableDropdown from 'app/components/SearchableDropdown'; import { AT_LEAST_ONE_OPTION } from 'constants/REPORT_PAGE_VALIDATION_ERRORS'; -const CHECKBOX_OPTIONS = [ +const DROPDOWN_OPTIONS = [ { label: 'Higher-Level Reviews', - name: 'HigherLevelReview' + value: 'HigherLevelReview' }, { label: 'Supplemental Claims', - name: 'SupplementalClaim' + value: 'SupplementalClaim' + }, + { + label: 'Remands', + value: 'Remand' } ]; export const decisionReviewTypeSchema = yup.object({ - HigherLevelReview: yup.boolean(), - SupplementalClaim: yup.boolean(), -}).test('at-least-one-true', AT_LEAST_ONE_OPTION, (obj) => { - const atLeastOneTrue = Object.values(obj).some((value) => value === true); - - if (!atLeastOneTrue) { - return false; - } - - return true; + decisionReviewTypes: yup.array().min(1, AT_LEAST_ONE_OPTION) }); -export const DecisionReviewType = ({ field, name, register }) => { +export const DecisionReviewType = ({ control, field, name }) => { const { errors } = useFormContext(); - const hasFormErrors = get(errors, name); - - const classNames = hasFormErrors ? - 'decisionReviewTypeContainer decisionReviewTypeContainerError' : - 'decisionReviewTypeContainer'; - - const errorMessage = get(errors, name)?.options?.message; + const nameDecisionReviewTypes = `${name}.options.decisionReviewTypes`; return ( -
- {hasFormErrors ? -
{errorMessage}
: - null - } -
- {CHECKBOX_OPTIONS.map((checkbox) => ( - + ( + - ))} -
+ )} + />
); }; DecisionReviewType.propTypes = { + control: PropTypes.object, field: PropTypes.object, name: PropTypes.string, - register: PropTypes.func }; diff --git a/client/app/nonComp/pages/ReportPage.jsx b/client/app/nonComp/pages/ReportPage.jsx index 3e3a7ba89a8..84417cc4335 100644 --- a/client/app/nonComp/pages/ReportPage.jsx +++ b/client/app/nonComp/pages/ReportPage.jsx @@ -247,14 +247,12 @@ const ReportPage = ({ history }) => { let formattedOptions; switch (condition) { - case 'decisionReviewType': - formattedOptions = Object.keys(options).filter((key) => options[key]); - break; // Multi select conditions case 'personnel': case 'facility': case 'issueDisposition': case 'issueType': + case 'decisionReviewType': formattedOptions = Object.values(options)[0].map((item) => item.value); break; // Else it is probably already an object, so it just pass the existing options diff --git a/client/app/nonComp/util/index.js b/client/app/nonComp/util/index.js index e0f14eb7587..b554aca56b5 100644 --- a/client/app/nonComp/util/index.js +++ b/client/app/nonComp/util/index.js @@ -109,6 +109,11 @@ const parseDecisionReviewTypeFilterOptions = (taskCounts, enabledFilters) => value: 'BoardGrantEffectuationTask', displayText: `Board Grant (${taskCount})` }; + } else if (key.includes('Remand')) { + taskInfo = { + value: 'Remand', + displayText: `Remand (${taskCount})` + }; } return { ...taskInfo, checked: enabledFilters?.includes(taskInfo.value) || false }; diff --git a/client/test/app/nonComp/pages/ReportPage.test.js b/client/test/app/nonComp/pages/ReportPage.test.js index 502d9832392..2df071c4a73 100644 --- a/client/test/app/nonComp/pages/ReportPage.test.js +++ b/client/test/app/nonComp/pages/ReportPage.test.js @@ -195,28 +195,34 @@ describe('ReportPage', () => { it('shows the correct checkbox fields', async () => { await navigateToConditionInput('Decision Review Type'); + const dropdown = screen.getByLabelText('Decision Review Type'); + await selectEvent.select(dropdown, ['Higher-Level Reviews']); expect(screen.getByText('Higher-Level Reviews')).toBeInTheDocument(); + + await selectEvent.select(dropdown, ['Supplemental Claims']); expect(screen.getByText('Supplemental Claims')).toBeInTheDocument(); + + await selectEvent.select(dropdown, ['Remands']); + expect(screen.getByText('Higher-Level Reviews')).toBeInTheDocument(); + expect(screen.getByText('Supplemental Claims')).toBeInTheDocument(); + expect(screen.getByText('Remands')).toBeInTheDocument(); }); - it('clicking the checkbox should toggle the checked status', async () => { + it('renders an error if no selection is made', async () => { await navigateToConditionInput('Decision Review Type'); - const checkbox = screen.getByLabelText('Higher-Level Reviews'); - - await userEvent.click(checkbox); - expect(checkbox.checked).toEqual(true); + const generateTaskReport = screen.getByRole('button', { name: 'Generate task report' }); - await userEvent.click(checkbox); - expect(checkbox.checked).toEqual(false); + expect(generateTaskReport).not.toHaveClass('usa-button-disabled'); - }); + // Wait for the validation text to appear before making assertions + await fireEvent.click(generateTaskReport); + await waitFor(() => { + const validationText = screen.getByText('Please select at least one option'); - it('should render an error if no checkbox is checked', async () => { - await navigateToConditionInput('Decision Review Type'); - expect(screen.getByText('Higher-Level Reviews')).toBeInTheDocument(); - await checkForValidationText('Please select at least one option'); + expect(validationText).toBeInTheDocument(); + }); }); }); diff --git a/config/routes.rb b/config/routes.rb index 3b14fac4202..e0f15ef5319 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -302,6 +302,7 @@ post 'edit_ep', on: :member end match '/supplemental_claims/:claim_id/edit/:any' => 'supplemental_claims#edit', via: [:get] + get '/remands(/*path)', to: redirect('/supplemental_claims/%{path}') resources :decision_reviews, param: :business_line_slug do resources :tasks, controller: :decision_reviews, param: :task_id, only: [:show, :update] do diff --git a/db/migrate/20240716143816_add_type_column_and_index_to_supplemental_claims_for_remand_inheritance.rb b/db/migrate/20240716143816_add_type_column_and_index_to_supplemental_claims_for_remand_inheritance.rb new file mode 100644 index 00000000000..f5e08799f29 --- /dev/null +++ b/db/migrate/20240716143816_add_type_column_and_index_to_supplemental_claims_for_remand_inheritance.rb @@ -0,0 +1,11 @@ +class AddTypeColumnAndIndexToSupplementalClaimsForRemandInheritance < ActiveRecord::Migration[6.1] + include Caseflow::Migrations::AddIndexConcurrently + + def change + safety_assured do + add_column :supplemental_claims, :type, :string, default: "SupplementalClaim", null: false, comment: "The class name for the single table inheritance type of Supplemental Claim for example Remand" + end + + add_safe_index :supplemental_claims, [:type], name: "index_supplemental_claims_on_type" + end +end diff --git a/db/migrate/20240723152034_backfill_supplemental_claims_type_column.rb b/db/migrate/20240723152034_backfill_supplemental_claims_type_column.rb new file mode 100644 index 00000000000..c72349eab79 --- /dev/null +++ b/db/migrate/20240723152034_backfill_supplemental_claims_type_column.rb @@ -0,0 +1,20 @@ +class BackfillSupplementalClaimsTypeColumn < ActiveRecord::Migration[6.1] + disable_ddl_transaction! + + def up + SupplementalClaim.in_batches do |batch| + batch.update_all( + <<-SQL + type = CASE + WHEN decision_review_remanded_id IS NOT NULL AND decision_review_remanded_type = 'Appeal' THEN 'Remand' + ELSE 'SupplementalClaim' + END + SQL + ) + end + end + + def down + SupplementalClaim.in_batches.update_all type: 'SupplementalClaim' + end +end diff --git a/db/migrate/20240805154526_create_remands_view.rb b/db/migrate/20240805154526_create_remands_view.rb new file mode 100644 index 00000000000..80922efa246 --- /dev/null +++ b/db/migrate/20240805154526_create_remands_view.rb @@ -0,0 +1,20 @@ +class CreateRemandsView < ActiveRecord::Migration[6.1] + def up + safety_assured do + execute <<-SQL + CREATE VIEW remands AS + SELECT * + FROM supplemental_claims + WHERE type = 'Remand'; + SQL + end + end + + def down + safety_assured do + execute <<-SQL + DROP VIEW remands; + SQL + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 3afc92fdb92..fb88838b8ac 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1877,11 +1877,13 @@ t.boolean "filed_by_va_gov", comment: "Indicates whether or not this form came from VA.gov" t.boolean "legacy_opt_in_approved", comment: "Indicates whether a Veteran opted to withdraw their Supplemental Claim request issues from the legacy system if a matching issue is found. If there is a matching legacy issue and it is not withdrawn, then that issue is ineligible to be a new request issue and a contention will not be created for it." t.date "receipt_date", comment: "The date that the Supplemental Claim form was received by central mail. Only issues decided prior to the receipt date will show up as contestable issues. It is also the claim date for any associated end products that are established. Supplemental Claims do not have the same timeliness restriction on contestable issues as Appeals and Higher Level Reviews." + t.string "type", default: "SupplementalClaim", null: false, comment: "The class name for the single table inheritance type of Supplemental Claim for example Remand" t.datetime "updated_at" t.uuid "uuid", default: -> { "uuid_generate_v4()" }, null: false, comment: "The universally unique identifier for the Supplemental Claim. Can be used to link to the claim after it is completed." t.string "veteran_file_number", null: false, comment: "PII. The file number of the Veteran that the Supplemental Claim is for." t.boolean "veteran_is_not_claimant", comment: "Indicates whether the Veteran is the claimant on the Supplemental Claim form, or if the claimant is someone else like a spouse or a child. Must be TRUE if the Veteran is deceased." t.index ["decision_review_remanded_type", "decision_review_remanded_id"], name: "index_decision_issues_on_decision_review_remanded" + t.index ["type"], name: "index_supplemental_claims_on_type" t.index ["updated_at"], name: "index_supplemental_claims_on_updated_at" t.index ["uuid"], name: "index_supplemental_claims_on_uuid" t.index ["veteran_file_number"], name: "index_supplemental_claims_on_veteran_file_number" diff --git a/db/seeds/veterans_health_administration.rb b/db/seeds/veterans_health_administration.rb index 9183c93352e..f99c4192da9 100644 --- a/db/seeds/veterans_health_administration.rb +++ b/db/seeds/veterans_health_administration.rb @@ -54,6 +54,7 @@ def create_supplemental_claims 3.times do CLAIMANT_TYPES.each do |claimant_type| create_sc_with_claimant(benefit_type, claimant_type) + create_sc_remand(benefit_type, claimant_type) end end end @@ -83,6 +84,16 @@ def create_sc_with_claimant(benefit_type, claimant_type) sc.create_business_line_tasks! end + def create_sc_remand(benefit_type, claimant_type) + sc = create( + :remand, + benefit_type: benefit_type, + claimant_type: claimant_type, + number_of_claimants: 1 + ) + sc.create_business_line_tasks! + end + # :reek:NestedIterators # this method is creating most of the data, but we can't get around it because of how many PO/VISN combos there are def create_vha_visn_pre_docket_queue diff --git a/spec/controllers/decision_reviews_controller_spec.rb b/spec/controllers/decision_reviews_controller_spec.rb index 4280faa4987..2b511bac1a1 100644 --- a/spec/controllers/decision_reviews_controller_spec.rb +++ b/spec/controllers/decision_reviews_controller_spec.rb @@ -79,7 +79,7 @@ expect(response.status).to eq 200 expect(response.headers["Content-Type"]).to include "text/csv" expect(response.body).to start_with("business_line") - expect(response.body.match?(task.appeal_type)).to eq true + expect(response.body.match?(task.appeal.class.review_title)).to eq true end end end @@ -306,6 +306,23 @@ end end + let!(:in_progress_remand_tasks) do + (0...10).map do |task_num| + task = create( + :remand_task, + assigned_to: non_comp_org, + assigned_at: task_num.minutes.ago + 1.minute.ago + ) + task.appeal.update!(veteran_file_number: veteran.file_number) + create(:request_issue, :nonrating, decision_review: task.appeal, benefit_type: non_comp_org.url) + + # Generate some random request issues for testing issue type filters + generate_request_issues(task, non_comp_org) + + task + end + end + let!(:completed_hlr_tasks) do (1..20).map do |task_num| task = create( @@ -352,6 +369,29 @@ end end + let!(:completed_remand_tasks) do + (1..10).map do |task_num| + task = create( + :remand_task, + assigned_to: non_comp_org, + assigned_at: task_num.days.ago, + closed_at: (2 * task_num).hours.ago + ) + task.completed! + # Explicitly set the closed_at time again to try to avoid test flakiness + task.closed_at = Time.zone.now - (2 * task_num).hours + task.appeal.update!(veteran_file_number: veteran.file_number) + create(:request_issue, :nonrating, decision_review: task.appeal, benefit_type: non_comp_org.url) + + # Generate some random request issues for testing issue type filters + generate_request_issues(task, non_comp_org) + + task.save + # Attempt to reload after save to avoid potential test flakiness + task.reload + end + end + before { non_comp_org.add_user(user) } subject { get :index, params: query_params, format: :json } @@ -390,6 +430,23 @@ end ).to be true end + + it "Only Remand Tasks are shown when filtered" do + get :index, + params: query_params.merge( + filter: ["col=decisionReviewType&val=Remand"], + page: 1 + ), + format: :json + + response_body = JSON.parse(response.body) + + expect( + response_body["tasks"]["data"].all? do |task| + task["type"] == "decision_review_task" && task["attributes"]["type"] == "Remand" + end + ).to be true + end end shared_examples "issue type query filtering" do @@ -438,7 +495,9 @@ } end - let(:in_progress_tasks) { in_progress_hlr_tasks + on_hold_hlr_tasks + in_progress_sc_tasks } + let(:in_progress_tasks) do + in_progress_hlr_tasks + on_hold_hlr_tasks + in_progress_sc_tasks + in_progress_remand_tasks + end include_examples "task query filtering" include_examples "issue type query filtering" @@ -451,30 +510,30 @@ expect(response.status).to eq(200) response_body = JSON.parse(response.body) - expect(response_body["total_task_count"]).to eq 84 + expect(response_body["total_task_count"]).to eq 94 expect(response_body["tasks_per_page"]).to eq 15 - expect(response_body["task_page_count"]).to eq 6 + expect(response_body["task_page_count"]).to eq 7 expect( task_ids_from_response_body(response_body) ).to match_array task_ids_from_seed(in_progress_tasks, (0...15), :assigned_at) end - it "page 6 displays last 9 tasks" do - query_params[:page] = 6 + it "page 7 displays last 4 tasks" do + query_params[:page] = 7 subject expect(response.status).to eq(200) response_body = JSON.parse(response.body) - expect(response_body["total_task_count"]).to eq 84 + expect(response_body["total_task_count"]).to eq 94 expect(response_body["tasks_per_page"]).to eq 15 - expect(response_body["task_page_count"]).to eq 6 + expect(response_body["task_page_count"]).to eq 7 expect( task_ids_from_response_body(response_body) - ).to match_array task_ids_from_seed(in_progress_tasks, (-9..in_progress_tasks.size), :assigned_at) + ).to match_array task_ids_from_seed(in_progress_tasks, (-4..in_progress_tasks.size), :assigned_at) end end @@ -486,7 +545,7 @@ } end - let(:completed_tasks) { completed_sc_tasks + completed_hlr_tasks } + let(:completed_tasks) { completed_sc_tasks + completed_hlr_tasks + completed_remand_tasks } include_examples "task query filtering" include_examples "issue type query filtering" @@ -499,34 +558,34 @@ expect(response.status).to eq(200) response_body = JSON.parse(response.body) - expect(response_body["total_task_count"]).to eq 40 + expect(response_body["total_task_count"]).to eq 50 expect(response_body["tasks_per_page"]).to eq 15 - expect(response_body["task_page_count"]).to eq 3 + expect(response_body["task_page_count"]).to eq 4 expect( task_ids_from_response_body(response_body) ).to match_array task_ids_from_seed(completed_tasks, (0...15), :closed_at) end - it "page 3 displays last 10 tasks" do - query_params[:page] = 3 + it "page 4 displays last 5 tasks" do + query_params[:page] = 4 subject expect(response.status).to eq(200) response_body = JSON.parse(response.body) - expect(response_body["total_task_count"]).to eq 40 + expect(response_body["total_task_count"]).to eq 50 expect(response_body["tasks_per_page"]).to eq 15 - expect(response_body["task_page_count"]).to eq 3 + expect(response_body["task_page_count"]).to eq 4 expect( task_ids_from_response_body(response_body) - ).to match_array task_ids_from_seed(completed_tasks, (-10..completed_tasks.size), :closed_at) + ).to match_array task_ids_from_seed(completed_tasks, (-5..completed_tasks.size), :closed_at) end end - context "vha org incomplete_tasks" do + context "vha org with incomplete tasks" do let(:non_comp_org) { VhaBusinessLine.singleton } context "incomplete_tasks" do @@ -604,7 +663,7 @@ end # The Vha Businessline in_progress should not include on_hold since it uses active for the tasks query - let(:in_progress_tasks) { in_progress_hlr_tasks + in_progress_sc_tasks } + let(:in_progress_tasks) { in_progress_hlr_tasks + in_progress_sc_tasks + in_progress_remand_tasks } it "page 1 displays first 15 tasks" do query_params[:page] = 1 @@ -614,7 +673,7 @@ expect(response.status).to eq(200) response_body = JSON.parse(response.body) - expect(response_body["total_task_count"]).to eq 64 + expect(response_body["total_task_count"]).to eq 74 expect(response_body["tasks_per_page"]).to eq 15 expect(response_body["task_page_count"]).to eq 5 @@ -824,6 +883,10 @@ create(:higher_level_review_vha_task_with_decision) end + let!(:remand_task_event) do + create(:remand_vha_task) + end + it "should return task details" do get :history, params: { task_id: task_event.id, decision_review_business_line_slug: vha_org.url }, format: :json @@ -846,6 +909,25 @@ ) end end + + it "should return remand task details" do + get :history, params: { task_id: remand_task_event.id, decision_review_business_line_slug: vha_org.url }, + format: :json + + expect(response.status).to eq 200 + + res = JSON.parse(response.body) + + expected_events = [ + { "taskID" => remand_task_event.id, "eventType" => "added_issue", "claimType" => "Remand" } + ] + + expected_events.each do |expected_attributes| + expect(res).to include( + a_hash_including("attributes" => a_hash_including(expected_attributes)) + ) + end + end end end diff --git a/spec/factories/supplemental_claim.rb b/spec/factories/supplemental_claim.rb index 39ed9b6df54..2ed4c7ef5a2 100644 --- a/spec/factories/supplemental_claim.rb +++ b/spec/factories/supplemental_claim.rb @@ -282,5 +282,16 @@ decision_review: sc) end end + + factory :remand, class: Remand do + type { Remand.name } + decision_review_remanded do + create( + :appeal, + :with_decision_issue, + disposition: "remanded" + ) + end + end end end diff --git a/spec/factories/task.rb b/spec/factories/task.rb index 3478613c729..bf6787b7f1b 100644 --- a/spec/factories/task.rb +++ b/spec/factories/task.rb @@ -431,6 +431,11 @@ def self.find_first_task_or_create(appeal, task_type, **kwargs) assigned_by { nil } end + factory :remand_task, class: DecisionReviewTask do + appeal { create(:remand, benefit_type: "nca") } + assigned_by { nil } + end + factory :supplemental_claim_poa_task, class: DecisionReviewTask do appeal do create(:supplemental_claim, @@ -511,6 +516,19 @@ def self.find_first_task_or_create(appeal, task_type, **kwargs) assigned_to { VhaBusinessLine.singleton } end + factory :remand_vha_task, class: DecisionReviewTask do + appeal do + create( + :remand, + :with_vha_issue, + benefit_type: "vha", + claimant_type: :veteran_claimant + ) + end + assigned_by { nil } + assigned_to { VhaBusinessLine.singleton } + end + factory :supplemental_claim_vha_task_incomplete, class: DecisionReviewTask do appeal do create( diff --git a/spec/feature/intake/supplemental_claim/edit_spec.rb b/spec/feature/intake/supplemental_claim/edit_spec.rb index 3f2df22e705..1aa698d7434 100644 --- a/spec/feature/intake/supplemental_claim/edit_spec.rb +++ b/spec/feature/intake/supplemental_claim/edit_spec.rb @@ -930,5 +930,25 @@ def click_cancel(visit_page) safe_click "#decision-date" expect(page).to have_button("Add this issue", disabled: true) end + + context "with a remand" do + let(:remand) { create(:remand_vha_task, assigned_at: 1.minute.ago) } + + before do + remand.appeal.establish! + end + + let(:edit_url) do + "/supplemental_claims/#{remand.appeal.uuid}/edit" + end + + it "should not allow editing" do + visit edit_url + + expect(page).to have_content(COPY::REMANDS_NOT_EDITABLE) + expect(page).not_to have_css(".cf-select__control") + expect(page).to have_button("Establish", disabled: true) + end + end end end diff --git a/spec/feature/non_comp/dispositions_spec.rb b/spec/feature/non_comp/dispositions_spec.rb index 36fb413bc45..7d270821654 100644 --- a/spec/feature/non_comp/dispositions_spec.rb +++ b/spec/feature/non_comp/dispositions_spec.rb @@ -502,6 +502,28 @@ def find_disabled_disposition(disposition, description = nil) end end end + + context "viewing a remand" do + let(:in_progress_remand_task) do + create(:remand_vha_task, assigned_at: 1.minute.ago) + end + + let(:dispositions_url) { "#{business_line_url}/tasks/#{in_progress_remand_task.id}" } + + it "should disable the request issue modification button" do + visit dispositions_url + + expect(page).to have_css(".usa-button-disabled", text: "Request issue modification") + expect(page).to have_content(COPY::REMANDS_NOT_EDITABLE) + end + + it "should disable the edit issues button" do + User.authenticate!(user: admin_user) + visit dispositions_url + + expect(page).to have_css(".usa-button-disabled", text: "Edit Issues") + end + end end def enable_feature_flag_and_redirect_to_disposition diff --git a/spec/feature/non_comp/report_page_spec.rb b/spec/feature/non_comp/report_page_spec.rb index b714a8d328e..984769f8da2 100644 --- a/spec/feature/non_comp/report_page_spec.rb +++ b/spec/feature/non_comp/report_page_spec.rb @@ -56,19 +56,11 @@ expect(page).to have_button("Generate task report", disabled: false) click_button "Generate task report" - # This might happen too fast for capybara - # expect(page).to have_button("Generate task report", disabled: true) - # expect(page).to have_content("Generating CSV...") - # Check the csv to make sure it returns the filter row, the column header row, and all 15 event rows csv_file = change_history_csv_file number_of_rows = CSV.read(csv_file).length expect(number_of_rows).to eq(17) - # CSV.foreach(csv_file) do |row| - # puts "Row: #{row}" - # end - # Add in some specific event filters now fill_in_specific_event_filters(["Added issue", "Completed disposition"]) @@ -306,7 +298,7 @@ def add_personnel_condition_with_values(values) def add_decision_review_condition_with_values(values) add_condition("Decision Review Type") - fill_in_decision_review_type(values) + fill_in_multi_select_condition(values, "Decision Review Type", ".decision-review-types") end def add_days_waiting_with_values(time_range, num_days, end_days = nil) @@ -321,7 +313,7 @@ def add_issue_disposition_with_values(values) def add_issue_type_with_values(values) add_condition("Issue Type") - fill_in_multi_select_condition(values, "Issue Type", "issue-types") + fill_in_multi_select_condition(values, "Issue Type", ".issue-types") end def clear_filters diff --git a/spec/feature/non_comp/reviews_spec.rb b/spec/feature/non_comp/reviews_spec.rb index f250c84ca98..1cf36ba917c 100644 --- a/spec/feature/non_comp/reviews_spec.rb +++ b/spec/feature/non_comp/reviews_spec.rb @@ -10,6 +10,7 @@ let(:veteran_b_on_hold) { create(:veteran, first_name: "Gaius", participant_id: "601172", ssn: "191039395") } let(:veteran_a_pending) { create(:veteran, first_name: "Dave", participant_id: "55667788", ssn: "123456789") } let(:veteran_c) { create(:veteran, first_name: "Ccc", participant_id: "1002345", ssn: "128455943") } + let(:veteran_remand) { create(:veteran, first_name: "B. Stark", participant_id: "100234567", ssn: "128455999") } let(:claimant_type) { :veteran_claimant } let(:hlr_a_on_hold) do create(:higher_level_review, veteran_file_number: veteran_a_on_hold.file_number, claimant_type: claimant_type) @@ -24,6 +25,7 @@ let(:hlr_b) { create(:higher_level_review, veteran_file_number: veteran_b.file_number, claimant_type: claimant_type) } let(:hlr_c) { create(:higher_level_review, veteran_file_number: veteran_c.file_number, claimant_type: claimant_type) } let(:appeal) { create(:appeal, veteran: veteran_c) } + let(:remand) { create(:remand, veteran_file_number: veteran_remand.file_number, claimant_type: claimant_type) } let!(:request_issue_a) do create(:request_issue, :nonrating, nonrating_issue_category: "Caregiver | Other", decision_review: hlr_a) @@ -57,6 +59,10 @@ create(:request_issue, :nonrating, nonrating_issue_category: "Other", decision_review: hlr_a_pending) end + let!(:remand_request_issue) do + create(:request_issue, :nonrating, nonrating_issue_category: "Beneficiary Travel", decision_review: remand) + end + let!(:modification_request_a) do create(:issue_modification_request, decision_review: hlr_a_pending, requestor: user) end @@ -107,7 +113,12 @@ :in_progress, appeal: appeal, assigned_to: non_comp_org, - assigned_at: 1.day.ago) + assigned_at: 1.day.ago), + create(:higher_level_review_task, + :in_progress, + appeal: remand, + assigned_to: non_comp_org, + assigned_at: today) ] end @@ -188,9 +199,9 @@ def current_table_rows scenario "displays tasks page with decision_review_queue_ssn_column feature toggle disabled" do visit BASE_URL expect(page).to have_content("Veterans Health Administration") - expect(page).to have_content("Incomplete Tasks") - expect(page).to have_content("Pending Tasks") - expect(page).to have_content("In Progress Tasks") + expect(page).to have_content("Incomplete Tasks (2)") + expect(page).to have_content("Pending Tasks (1)") + expect(page).to have_content("In Progress Tasks (4)") expect(page).to have_content("Completed Tasks") # default is the in progress page if no tab is specified in the url @@ -198,21 +209,24 @@ def current_table_rows expect(page).to have_content("Issues") expect(page).to have_content("Issue Type") expect(page).to have_content("Higher-Level Review", count: 2) + expect(page).to have_content("Remand", count: 1) expect(page).to have_content("Board Grant") expect(page).to have_content(veteran_a.name) expect(page).to have_content(veteran_b.name) expect(page).to have_content(veteran_c.name) + expect(page).to have_content(veteran_remand.name) expect(page).to_not have_content(veteran_a_on_hold.name) expect(page).to_not have_content(veteran_b_on_hold.name) expect(page).to have_content(vet_id_column_header) expect(page).to have_content(vet_a_id_column_value) expect(page).to have_content(vet_b_id_column_value) expect(page).to have_content(vet_c_id_column_value) + expect(page).to have_no_content(search_box_label) # ordered by assigned_at descending expect(page).to have_content( - /#{veteran_b.name}.+\s#{veteran_c.name}.+\s#{veteran_a.name}/ + /#{veteran_b.name}.+\s#{veteran_remand.name}.+\s#{veteran_c.name}.+\s#{veteran_a.name}/ ) click_on "Incomplete Tasks" @@ -241,7 +255,6 @@ def current_table_rows expect(page).to have_content("Higher-Level Review", count: 2) expect(page).to have_content("Date Completed") - # decision_date = hlr_b.request_issues.first.decision_date.strftime("%m\/%d\/%y") decision_date = hlr_b.tasks.first.closed_at.strftime("%m/%d/%y") # ordered by closed_at descending expect(page).to have_content( @@ -262,9 +275,9 @@ def current_table_rows scenario "displays tasks page" do visit BASE_URL expect(page).to have_content("Veterans Health Administration") - expect(page).to have_content("Incomplete Tasks") - expect(page).to have_content("Pending Tasks") - expect(page).to have_content("In Progress Tasks") + expect(page).to have_content("Incomplete Tasks (2)") + expect(page).to have_content("Pending Tasks (1)") + expect(page).to have_content("In Progress Tasks (4)") expect(page).to have_content("Completed Tasks") # default is the in progress page if no tab is specified in the url @@ -374,7 +387,7 @@ def current_table_rows table_rows = current_table_rows expect(table_rows.last.include?("Caregiver | Other\nCHAMPVA\n")).to eq true - expect(table_rows.first.include?(" Camp Lejune Family Member ")).to eq true + expect(table_rows.first.include?(" Beneficiary Travel ")).to eq true # Issue Types desc order_buttons[:issues_type].click @@ -383,7 +396,7 @@ def current_table_rows ) table_rows = current_table_rows - expect(table_rows.last.include?(" Camp Lejune Family Member ")).to eq true + expect(table_rows.last.include?(" Beneficiary Travel ")).to eq true expect(table_rows.first.include?("Caregiver | Other\nCHAMPVA")).to eq true # Days waiting asc @@ -515,22 +528,30 @@ def current_table_rows end end - scenario "filtering reviews by appeal type", skip: true do + scenario "filtering reviews by appeal type" do visit BASE_URL + + expect(page).to have_content("Higher-Level Review") + expect(page).to have_content("Board Grant") + expect(page).to have_content("Remand") + find("[aria-label='Filter by type']").click # Check that task counts are being transmitted correctly from backend expect(page).to have_content("Board Grant (1)") expect(page).to have_content("Higher-Level Review (2)") + expect(page).to have_content("Remand (1)") find("label", text: "Higher-Level Review").click - expect(page).to have_content("Higher-Level Review") + expect(page).to have_content("Higher-Level Review", count: 2) expect(page).to_not have_content("Board Grant") + expect(page).to_not have_content("Remand") find(".cf-clear-filters-link").click expect(page).to have_content("Board Grant") + expect(page).to have_content("Remand") end - scenario "filtering reviews by issue type", skip: true do + scenario "filtering reviews by issue type" do visit BASE_URL find("[aria-label='Filter by issue type']").click @@ -538,6 +559,7 @@ def current_table_rows expect(page).to have_content("Caregiver | Other (1)") expect(page).to have_content("Camp Lejune Family Member (1)") expect(page).to have_content("CHAMPVA (1)") + expect(page).to have_content("Beneficiary Travel (1)") find("label", text: "Caregiver | Other").click expect(page).to have_content("Caregiver | Other") @@ -547,16 +569,19 @@ def current_table_rows # Verify the filter counts for the incomplete tab click_on "Incomplete Tasks" + expect(page).to have_content(COPY::VHA_INCOMPLETE_TAB_DESCRIPTION) find("[aria-label='Filter by issue type']").click expect(page).to have_content("Clothing Allowance (1)") expect(page).to have_content("Other (1)") click_on "Pending Tasks" + expect(page).to have_content(COPY::VHA_PENDING_REQUESTS_TAB_DESCRIPTION) find("[aria-label='Filter by issue type']").click expect(page).to have_content("Other (1)") # Verify the filter counts for the completed tab click_on "Completed Tasks" + expect(page).to have_content(COPY::QUEUE_PAGE_COMPLETE_LAST_SEVEN_DAYS_TASKS_DESCRIPTION) find("[aria-label='Filter by issue type']").click expect(page).to have_content("Apportionment (1)") expect(page).to have_content("Camp Lejune Family Member (1)") @@ -750,17 +775,17 @@ def current_table_rows assigned_at: last_week) ] end - scenario "Duplicate issue types like Beneficiary Travel should be removed from the visible list of issue types" do - visit BASE_URL - hlr_c_regex = /#{veteran_c.name} #{veteran_c.ssn} 3\nBeneficiary Travel\nEligibility for Dental Treatment\n6 days Higher-Level Review/ # rubocop:disable Layout/LineLength - expect(page).to have_content( - hlr_c_regex - ) - end scenario "Ordering issue types should ignore duplicates when ordering" do visit BASE_URL + step "Duplicate issue types like Beneficiary Travel should be removed from the visible list of issue types" do + hlr_c_regex = /#{veteran_c.name} #{veteran_c.ssn} 3\nBeneficiary Travel\nEligibility for Dental Treatment\n6 days Higher-Level Review/ # rubocop:disable Layout/LineLength + expect(page).to have_content( + hlr_c_regex + ) + end + issues_type_sort_button = find(:xpath, '//*[@id="case-table-description"]/thead/tr/th[4]/span/span[2]') issues_type_sort_button.click @@ -788,7 +813,7 @@ def current_table_rows expect(table_rows.first.include?("B Veteran")).to eq true end - scenario "The Issue type column should orderable and filterable at the same time", skip: true do + scenario "The Issue type column should orderable and filterable at the same time" do visit BASE_URL issues_type_sort_button = find(:xpath, '//*[@id="case-table-description"]/thead/tr/th[4]/span/span[2]') @@ -816,6 +841,9 @@ def current_table_rows table_rows = current_table_rows + sort_header = find('th[aria-labelledby="header-appealIssueTypes"]') + expect(sort_header[:'aria-sort']).to eq("ascending") + expect(table_rows.first.include?("A Veteran")).to eq true expect(table_rows.last.include?("D Veteran")).to eq true @@ -823,6 +851,9 @@ def current_table_rows table_rows = current_table_rows + sort_header = find('th[aria-labelledby="header-appealIssueTypes"]') + expect(sort_header[:'aria-sort']).to eq("descending") + expect(table_rows.first.include?("D Veteran")).to eq true expect(table_rows.last.include?("A Veteran")).to eq true @@ -830,6 +861,9 @@ def current_table_rows expect(page).to have_content("Spina Bifida Treatment (Non-Compensation)") table_rows = current_table_rows + + sort_header = find('th[aria-labelledby="header-appealIssueTypes"]') + expect(sort_header[:'aria-sort']).to eq("descending") expect(table_rows.first.include?("B Veteran")).to eq true expect(table_rows.last.include?("A Veteran")).to eq true end @@ -965,7 +999,7 @@ def current_table_rows pagination = page.find(class: "cf-pagination-pages", match: :first) pagination.find("Button", text: "2", match: :first).click - expect(page).to have_content("Viewing 16-30 of 33 total") + expect(page).to have_content("Viewing 16-30 of 34 total") # Navigate to another tab click_button("Incomplete Tasks") @@ -997,9 +1031,11 @@ def current_table_rows expect(page).to have_content("Issue Type") expect(page).to have_content("Higher-Level Review", count: 4) expect(page).to have_content("Board Grant") + expect(page).to have_content("Remand") expect(page).to have_content(veteran_a.name) expect(page).to have_content(veteran_b.name) expect(page).to have_content(veteran_c.name) + expect(page).to have_content(veteran_remand.name) expect(page).to have_content(veteran_a_on_hold.name) expect(page).to have_content(veteran_b_on_hold.name) expect(page).to have_content(vet_id_column_header) @@ -1010,7 +1046,7 @@ def current_table_rows # ordered by assigned_at descending expect(page).to have_content( - /#{veteran_b.name}.+\s#{veteran_c.name}.+\s#{veteran_a.name}/ + /#{veteran_b.name}.+\s#{veteran_remand.name}.+\s#{veteran_c.name}.+\s#{veteran_a.name}/ ) click_on "Completed Tasks" diff --git a/spec/lib/helpers/association_wrapper_spec.rb b/spec/lib/helpers/association_wrapper_spec.rb index 86e157dc91b..4c8140a921a 100644 --- a/spec/lib/helpers/association_wrapper_spec.rb +++ b/spec/lib/helpers/association_wrapper_spec.rb @@ -26,6 +26,7 @@ [:legacy_appeal, "LegacyAppeal", "LegacyAppeal", nil, nil], [:supplemental_claim, "SupplementalClaim", "SupplementalClaim", nil, nil], [:higher_level_review, "HigherLevelReview", "HigherLevelReview", nil, nil], + [:remand, "Remand", "Remand", nil, nil], [:attorney_case_reviews, "AttorneyCaseReview", nil, nil, nil], [:task_timers, "TaskTimer", nil, nil, nil], [:cached_appeal, "CachedAppeal", nil, nil, nil] @@ -45,7 +46,8 @@ [:ama_appeal, nil], [:legacy_appeal, nil], [:higher_level_review, nil], - [:supplemental_claim, nil] + [:supplemental_claim, nil], + [:remand, nil] ] expect(subject.select_associations.map { |assoc| [assoc.name, assoc.options[:foreign_key]] }).to match_array [ [:versions, nil], @@ -62,7 +64,8 @@ [:ama_appeal, "appeal_id"], [:legacy_appeal, "appeal_id"], [:higher_level_review, "appeal_id"], - [:supplemental_claim, "appeal_id"] + [:supplemental_claim, "appeal_id"], + [:remand, "appeal_id"] ] map_foreign_keys = lambda { |assoc| @@ -86,6 +89,8 @@ [:legacy_appeal, true, false, "appeal_id", "legacy_appeal_id", nil], [:higher_level_review, true, false, "appeal_id", "higher_level_review_id", nil], [:supplemental_claim, true, false, "appeal_id", "supplemental_claim_id", nil], + # Polymorphic `belongs_to :appeal`-related STI associations + [:remand, true, false, "appeal_id", "remand_id", nil], # has_many declared in Task # Note: JudgeCaseReview is not listed; that `belongs_to` association can be traced from JudgeCaseReview [:attorney_case_reviews, false, false, "task_id", "attorney_case_review_id", nil], diff --git a/spec/models/appeal_spec.rb b/spec/models/appeal_spec.rb index 48f10bb8329..038ba98ee74 100644 --- a/spec/models/appeal_spec.rb +++ b/spec/models/appeal_spec.rb @@ -278,14 +278,15 @@ let!(:not_remanded_decision_issue) { create(:decision_issue, decision_review: appeal) } - it "creates supplemental claim, request issues, and starts processing" do + it "creates remand, request issues, and starts processing" do subject - remanded_supplemental_claims = SupplementalClaim.where(decision_review_remanded: appeal) + remanded_supplemental_claims = Remand.where(decision_review_remanded: appeal) expect(remanded_supplemental_claims.count).to eq(2) vbms_remand = remanded_supplemental_claims.find_by(benefit_type: "compensation") + expect(vbms_remand.type).to eq(Remand.name) expect(vbms_remand).to have_attributes( receipt_date: decision_date.to_date ) @@ -297,6 +298,7 @@ expect(vbms_remand.tasks).to be_empty caseflow_remand = remanded_supplemental_claims.find_by(benefit_type: "nca") + expect(caseflow_remand.type).to eq(Remand.name) expect(caseflow_remand).to have_attributes( receipt_date: decision_date.to_date ) diff --git a/spec/models/business_line_spec.rb b/spec/models/business_line_spec.rb index f9e382d4742..1684d7228db 100644 --- a/spec/models/business_line_spec.rb +++ b/spec/models/business_line_spec.rb @@ -29,6 +29,18 @@ end end + context "Remand tasks" do + let!(:task_filters) { ["col=decisionReviewType&val=Remand"] } + + it "Returning only Remand tasks" do + expect( + subject.all? do |task| + task.type == DecisionReviewTask.name && task.appeal.type == Remand.name + end + ).to eq true + end + end + context "Veteran Record Request tasks" do let!(:task_filters) { ["col=decisionReviewType&val=VeteranRecordRequest"] } @@ -55,7 +67,6 @@ it "Attempting to return only Board Grant Effectuation tasks amounts to either only completed tasks or any empty result" do tasks = subject - expect(tasks.empty? || tasks.all?(&:completed?)).to eq true end end @@ -127,6 +138,10 @@ create_list(:higher_level_review_task, 5, assigned_to: business_line) end + let!(:remand_tasks_on_active_decision_reviews) do + create_list(:remand_vha_task, 5, assigned_to: business_line) + end + let!(:board_grant_effectuation_tasks) do tasks = create_list(:board_grant_effectuation_task, 5, assigned_to: business_line) @@ -165,12 +180,13 @@ after { FeatureToggle.disable!(:board_grant_effectuation_task) } it "All tasks associated with active decision reviews and BoardGrantEffectuationTasks are included" do - expect(subject.size).to eq 20 + expect(subject.size).to eq 25 expect(subject.map(&:id)).to match_array( (veteran_record_request_on_active_appeals + board_grant_effectuation_tasks + hlr_tasks_on_active_decision_reviews + - sc_tasks_on_active_decision_reviews + sc_tasks_on_active_decision_reviews + + remand_tasks_on_active_decision_reviews ).pluck(:id) ) end @@ -182,11 +198,12 @@ before { FeatureToggle.disable!(:board_grant_effectuation_task) } it "All tasks associated with active decision reviews are included, but not BoardGrantEffectuationTasks" do - expect(subject.size).to eq 15 + expect(subject.size).to eq 20 expect(subject.map(&:id)).to match_array( (veteran_record_request_on_active_appeals + hlr_tasks_on_active_decision_reviews + - sc_tasks_on_active_decision_reviews + sc_tasks_on_active_decision_reviews + + remand_tasks_on_active_decision_reviews ).pluck(:id) ) end @@ -245,6 +262,14 @@ ) end + let!(:completed_remand_tasks) do + add_veteran_and_request_issues_to_decision_reviews( + complete_all_tasks( + create_list(:remand_task, 5, assigned_to: business_line) + ) + ) + end + let!(:open_sc_tasks) do add_veteran_and_request_issues_to_decision_reviews( create_list(:supplemental_claim_task, 5, assigned_to: business_line) @@ -295,12 +320,13 @@ let!(:task_filters) { nil } it "All completed tasks are included in results" do - expect(subject.size).to eq 20 + expect(subject.size).to eq 25 expect(subject.map(&:id)).to match_array( (completed_hlr_tasks + completed_sc_tasks + completed_board_grant_effectuation_tasks + - completed_veteran_record_requests + completed_veteran_record_requests + + completed_remand_tasks ).pluck(:id) ) end @@ -394,6 +420,10 @@ create_list(:supplemental_claim_vha_task, 5, assigned_to: business_line) end + let!(:remand_tasks_on_active_decision_reviews) do + create_list(:remand_vha_task, 5, assigned_to: business_line) + end + # Set some on hold tasks as well let!(:on_hold_sc_tasks_on_active_decision_reviews) do tasks = create_list(:supplemental_claim_vha_task, 5, assigned_to: business_line) @@ -443,13 +473,14 @@ after { FeatureToggle.disable!(:board_grant_effectuation_task) } it "All tasks associated with active decision reviews and BoardGrantEffectuationTasks are included" do - expect(subject.size).to eq 25 + expect(subject.size).to eq 30 expect(subject.map(&:id)).to match_array( (veteran_record_request_on_active_appeals + board_grant_effectuation_tasks + hlr_tasks_on_active_decision_reviews + sc_tasks_on_active_decision_reviews + - on_hold_sc_tasks_on_active_decision_reviews + on_hold_sc_tasks_on_active_decision_reviews + + remand_tasks_on_active_decision_reviews ).pluck(:id) ) end @@ -461,12 +492,13 @@ before { FeatureToggle.disable!(:board_grant_effectuation_task) } it "All tasks associated with active decision reviews are included, but not BoardGrantEffectuationTasks" do - expect(subject.size).to eq 20 + expect(subject.size).to eq 25 expect(subject.map(&:id)).to match_array( (veteran_record_request_on_active_appeals + hlr_tasks_on_active_decision_reviews + sc_tasks_on_active_decision_reviews + - on_hold_sc_tasks_on_active_decision_reviews + on_hold_sc_tasks_on_active_decision_reviews + + remand_tasks_on_active_decision_reviews ).pluck(:id) ) end @@ -486,6 +518,13 @@ benefit_type: "vha", claimant_type: :dependent_claimant)) end + let!(:remand_task) do + create(:remand_vha_task, + appeal: create(:remand, + benefit_type: "vha", + claimant_type: :dependent_claimant)) + end + let(:decision_issue) { create(:decision_issue, disposition: "denied", benefit_type: hlr_task.appeal.benefit_type) } let(:intake_user) { create(:user, full_name: "Alexander Dewitt", css_id: "ALEXVHA", station_id: "103") } let(:decision_user) { create(:user, full_name: "Gaius Baelsar", css_id: "GAIUSVHA", station_id: "104") } @@ -586,6 +625,25 @@ "days_waiting" => (Time.zone.today - Date.parse(sc_task.assigned_at.iso8601)).to_i ) end + let(:remand_task_1_ri_1_expectation) do + a_hash_including( + "nonrating_issue_category" => "Clothing Allowance", + "nonrating_issue_description" => "This is a Clothing Allowance issue", + "task_id" => remand_task.id, + "veteran_file_number" => remand_task.appeal.veteran_file_number, + "intake_user_name" => nil, + "intake_user_css_id" => nil, + "intake_user_station_id" => nil, + "disposition" => nil, + "decision_user_name" => nil, + "decision_user_css_id" => nil, + "decision_user_station_id" => nil, + "claimant_name" => remand_task.appeal.claimant.name, + "task_status" => remand_task.status, + "request_issue_benefit_type" => "vha", + "days_waiting" => (Time.zone.today - Date.parse(remand_task.assigned_at.iso8601)).to_i + ) + end let(:all_expectations) do [ @@ -593,7 +651,8 @@ hlr_task_1_ri_2_expectation, hlr_task_2_ri_1_expectation, hlr_task_2_ri_2_expectation, - sc_task_1_ri_1_expectation + sc_task_1_ri_1_expectation, + remand_task_1_ri_1_expectation ] end @@ -606,8 +665,16 @@ nonrating_issue_category: "Camp Lejune Family Member", nonrating_issue_description: "This is a Camp Lejune issue", benefit_type: "vha") + remand_issue = create(:request_issue, + nonrating_issue_category: "Clothing Allowance", + nonrating_issue_description: "This is a Clothing Allowance issue", + benefit_type: "vha", + decision_review: remand_task.appeal) hlr_task.appeal.request_issues << issue hlr_task2.appeal.request_issues << issue2 + remand_task.appeal.request_issues << remand_issue + remand_task.save + remand_task.reload # Add a different intake user to the second hlr task for data differences second_intake = hlr_task2.appeal.intake @@ -641,21 +708,22 @@ context "without filters" do it "should return all rows" do - expect(subject.count).to eq 5 + expect(subject.count).to eq 6 expect(subject.entries).to include(*all_expectations) end end context "with task_id filter" do context "with multiple task ids" do - let(:change_history_filters) { { task_id: [hlr_task.id, sc_task.id] } } + let(:change_history_filters) { { task_id: [hlr_task.id, sc_task.id, remand_task.id] } } it "should return rows for all matching ids" do - expect(subject.entries.count).to eq(3) + expect(subject.entries.count).to eq(4) expect(subject.entries).to include( hlr_task_1_ri_1_expectation, hlr_task_1_ri_2_expectation, - sc_task_1_ri_1_expectation + sc_task_1_ri_1_expectation, + remand_task_1_ri_1_expectation ) end end @@ -686,7 +754,18 @@ it "should only return rows for the filtered claim type" do expect(subject.entries.count).to eq(4) - expect(subject.entries).to include(*(all_expectations - [sc_task_1_ri_1_expectation])) + expect(subject.entries).to include( + *(all_expectations - [sc_task_1_ri_1_expectation] - [remand_task_1_ri_1_expectation]) + ) + end + end + + context "Remand claim filter" do + let(:change_history_filters) { { claim_type: ["Remand"] } } + + it "should only return rows for the filtered claim type" do + expect(subject.entries.count).to eq(1) + expect(subject.entries).to include(remand_task_1_ri_1_expectation) end end end @@ -729,11 +808,12 @@ let(:change_history_filters) { { dispositions: ["Blank"] } } it "should return rows that do not have a disposition" do - expect(subject.entries.count).to eq(3) + expect(subject.entries.count).to eq(4) expect(subject.entries).to include( hlr_task_2_ri_1_expectation, hlr_task_2_ri_2_expectation, - sc_task_1_ri_1_expectation + sc_task_1_ri_1_expectation, + remand_task_1_ri_1_expectation ) end @@ -741,12 +821,13 @@ let(:change_history_filters) { { dispositions: %w[denied Blank] } } it "should return rows that match denied or have no disposition" do - expect(subject.entries.count).to eq(4) + expect(subject.entries.count).to eq(5) expect(subject.entries).to include( hlr_task_1_ri_2_expectation, hlr_task_2_ri_1_expectation, hlr_task_2_ri_2_expectation, - sc_task_1_ri_1_expectation + sc_task_1_ri_1_expectation, + remand_task_1_ri_1_expectation ) end end @@ -796,9 +877,10 @@ let(:change_history_filters) { { days_waiting: { number_of_days: 11, operator: ">" } } } it "should only return rows that are over the filtered days waiting value" do - expect(subject.entries.count).to eq(1) + expect(subject.entries.count).to eq(2) expect(subject.entries).to include( - sc_task_1_ri_1_expectation + sc_task_1_ri_1_expectation, + remand_task_1_ri_1_expectation ) end end @@ -820,7 +902,9 @@ it "should only return rows that are between the number of days and end of days" do expect(subject.entries.count).to eq(4) - expect(subject.entries).to include(*(all_expectations - [sc_task_1_ri_1_expectation])) + expect(subject.entries).to include( + *(all_expectations - [sc_task_1_ri_1_expectation] - [remand_task_1_ri_1_expectation]) + ) end end end @@ -840,7 +924,9 @@ it "only return rows where either an intake, decisions, or updates user matches the station id" do expect(subject.entries.count).to eq(4) - expect(subject.entries).to include(*(all_expectations - [sc_task_1_ri_1_expectation])) + expect(subject.entries).to include( + *(all_expectations - [sc_task_1_ri_1_expectation] - [remand_task_1_ri_1_expectation]) + ) end end @@ -873,7 +959,9 @@ it "only return rows where either an intake, decisions, or updates user matches the css_ids" do expect(subject.entries.count).to eq(4) - expect(subject.entries).to include(*(all_expectations - [sc_task_1_ri_1_expectation])) + expect(subject.entries).to include( + *(all_expectations - [sc_task_1_ri_1_expectation] - [remand_task_1_ri_1_expectation]) + ) end end diff --git a/spec/models/decision_issue_spec.rb b/spec/models/decision_issue_spec.rb index 3ed5a1fb89e..4013a9ded31 100644 --- a/spec/models/decision_issue_spec.rb +++ b/spec/models/decision_issue_spec.rb @@ -419,6 +419,30 @@ end end end + context "decision review type" do + context "when type is Appeal" do + let(:decision_review) { create(:appeal) } + + it "creates a Remand class" do + expect(subject.type).to eq(Remand.name) + end + end + + context "when type is HLR" do + let(:decision_review) { create(:higher_level_review) } + before do + decision_review.create_claimant!( + participant_id: "98765", + payee_code: "00", + type: "VeteranClaimant" + ) + end + + it "creates a SupplementalClaim class" do + expect(subject.type).to eq(SupplementalClaim.name) + end + end + end end end end diff --git a/spec/models/higher_level_review_spec.rb b/spec/models/higher_level_review_spec.rb index 9182f62a383..031fb9ae2c0 100644 --- a/spec/models/higher_level_review_spec.rb +++ b/spec/models/higher_level_review_spec.rb @@ -264,6 +264,7 @@ expect(supplemental_claim).to_not be_nil expect(supplemental_claim.establishment_submitted_at).to_not be_nil expect(supplemental_claim.request_issues.count).to eq(2) + expect(supplemental_claim.type).to eq(SupplementalClaim.name) first_dta_request_issue = RequestIssue.find_by( decision_review: supplemental_claim, @@ -332,6 +333,7 @@ caseflow_decision_date.to_date + vbms_offset - SupplementalClaim.processing_retry_interval_hours.hours + 1.minute ) + expect(dta_sc.type).to eq(SupplementalClaim.name) expect do subject end.to_not have_enqueued_job(DecisionReviewProcessJob) diff --git a/spec/models/supplemental_claim_spec.rb b/spec/models/supplemental_claim_spec.rb index 96360af191f..702e7d57197 100644 --- a/spec/models/supplemental_claim_spec.rb +++ b/spec/models/supplemental_claim_spec.rb @@ -25,6 +25,17 @@ ) end + let(:remand) do + Remand.new( + veteran_file_number: veteran_file_number, + receipt_date: receipt_date, + benefit_type: benefit_type, + legacy_opt_in_approved: legacy_opt_in_approved, + veteran_is_not_claimant: veteran_is_not_claimant, + decision_review_remanded: decision_review_remanded + ) + end + let!(:intake) do create(:intake, user: current_user, detail: supplemental_claim, veteran_file_number: veteran_file_number) end @@ -44,6 +55,11 @@ let(:legacy_opt_in_approved) { false } let(:receipt_date) { 1.day.ago } + it "sets the type column correctly" do + expect(supplemental_claim.type).to eq(SupplementalClaim.name) + expect(remand.type).to eq(Remand.name) + end + it "is valid" do is_expected.to be true end @@ -120,6 +136,17 @@ end context "create_remand_issues!" do + let(:classifier) { Remand } + let(:supplemental_claim) do + classifier.new( + veteran_file_number: veteran_file_number, + receipt_date: receipt_date, + benefit_type: benefit_type, + legacy_opt_in_approved: legacy_opt_in_approved, + veteran_is_not_claimant: veteran_is_not_claimant, + decision_review_remanded: decision_review_remanded + ) + end subject { supplemental_claim.create_remand_issues! } let(:decision_review_remanded) { create(:appeal) } diff --git a/spec/services/business_line_reporter_spec.rb b/spec/services/business_line_reporter_spec.rb index ffbf5bf373e..1a5604e3501 100644 --- a/spec/services/business_line_reporter_spec.rb +++ b/spec/services/business_line_reporter_spec.rb @@ -14,17 +14,35 @@ let(:third_appeal) { create(:appeal, :with_post_intake_tasks) } let(:third_ama_task) { create(:ama_task, appeal: third_appeal, assigned_to: business_line) } + let(:remand) { create(:remand, benefit_type: business_line.url, claimant_type: :veteran_claimant) } + let(:remand_task) do + DecisionReviewTask.create!(appeal: remand, assigned_at: Time.zone.now, assigned_to: business_line) + end + + let(:hlr) { create(:higher_level_review, benefit_type: business_line.url, claimant_type: :veteran_claimant) } + let(:hlr_task) { DecisionReviewTask.create!(appeal: hlr, assigned_at: Time.zone.now, assigned_to: business_line) } + + let(:supplemental_claim) do + create(:supplemental_claim, benefit_type: business_line.url, claimant_type: :veteran_claimant) + end + let(:sc_task) do + DecisionReviewTask.create!(appeal: supplemental_claim, assigned_at: Time.zone.now, assigned_to: business_line) + end + before do Timecop.freeze(Time.utc(2020, 1, 1, 19, 0, 0)) first_ama_task.completed! second_ama_task.completed! + remand_task.completed! + hlr_task.completed! + sc_task.completed! end describe "#tasks" do subject { BusinessLineReporter.new(business_line).tasks } it "returns the completed tasks" do - expect(subject).to include(first_ama_task, second_ama_task) + expect(subject).to include(first_ama_task, second_ama_task, remand_task, hlr_task, sc_task) end it "does not return an open task" do @@ -41,6 +59,9 @@ def expected_csv business_line,appeal_id,appeal_type,claimant_name,request_issues_count,decision_issues_count,veteran_file_number,intake_user_id,task_type,task_id,tasks_url,task_assigned_to,created_at,closed_at #{business_line.name},#{first_appeal.id},Appeal,#{first_appeal.claimant.name},0,0,#{first_appeal.veteran.file_number},,Task,#{first_ama_task.id},https://appeals.cf.ds.va.gov#{business_line.tasks_url}/tasks/#{first_ama_task.id},#{first_ama_task.assigned_to.name},2020-01-01,2020-01-01 #{business_line.name},#{second_appeal.id},Appeal,#{second_appeal.claimant.name},0,0,#{second_appeal.veteran.file_number},,Task,#{second_ama_task.id},https://appeals.cf.ds.va.gov#{business_line.tasks_url}/tasks/#{second_ama_task.id},#{second_ama_task.assigned_to.name},2020-01-01,2020-01-01 + #{business_line.name},#{remand.id},Remand,#{remand.claimant.name},0,0,#{remand.veteran.file_number},,DecisionReviewTask,#{remand_task.id},https://appeals.cf.ds.va.gov#{business_line.tasks_url}/tasks/#{remand_task.id},#{remand_task.assigned_to.name},2020-01-01,2020-01-01 + #{business_line.name},#{hlr.id},Higher-Level Review,#{hlr.claimant.name},0,0,#{hlr.veteran.file_number},,DecisionReviewTask,#{hlr_task.id},https://appeals.cf.ds.va.gov#{business_line.tasks_url}/tasks/#{hlr_task.id},#{hlr_task.assigned_to.name},2020-01-01,2020-01-01 + #{business_line.name},#{supplemental_claim.id},Supplemental Claim,#{supplemental_claim.claimant.name},0,0,#{supplemental_claim.veteran.file_number},,DecisionReviewTask,#{sc_task.id},https://appeals.cf.ds.va.gov#{business_line.tasks_url}/tasks/#{sc_task.id},#{sc_task.assigned_to.name},2020-01-01,2020-01-01 EOF end # rubocop:enable Metrics/AbcSize diff --git a/spec/services/claim_change_history/change_history_reporter_spec.rb b/spec/services/claim_change_history/change_history_reporter_spec.rb index c5dcc4bbbbd..a18a9782b70 100644 --- a/spec/services/claim_change_history/change_history_reporter_spec.rb +++ b/spec/services/claim_change_history/change_history_reporter_spec.rb @@ -146,6 +146,83 @@ new_event end + let(:remand_claim_creation_event) do + new_event = added_issue_event.clone + new_event.instance_variable_set(:@event_type, :claim_creation) + new_event.instance_variable_set(:@event_user_name, "System") + new_event.instance_variable_set(:@user_facility, nil) + new_event.instance_variable_set(:@claim_type, "Remand") + new_event + end + let(:remand_in_progress_status_event) do + new_event = claim_creation_event.clone + new_event.instance_variable_set(:@event_type, :in_progress) + new_event.instance_variable_set(:@claim_type, "Remand") + new_event + end + let(:remand_cancelled_status_event) do + new_event = in_progress_status_event.clone + new_event.instance_variable_set(:@event_type, :cancelled) + new_event.instance_variable_set(:@task_status, "cancelled") + new_event.instance_variable_set(:@claim_type, "Remand") + new_event + end + let(:remand_completed_status_event) do + new_event = in_progress_status_event.clone + new_event.instance_variable_set(:@event_type, :completed) + new_event.instance_variable_set(:@task_status, "completed") + new_event.instance_variable_set(:@claim_type, "Remand") + new_event + end + let(:remand_incomplete_status_event) do + new_event = in_progress_status_event.clone + new_event.instance_variable_set(:@event_type, :incomplete) + new_event.instance_variable_set(:@task_status, "on_hold") + new_event.instance_variable_set(:@claim_type, "Remand") + new_event + end + let(:remand_added_issue_without_decision_date_event) do + new_event = added_issue_event.clone + new_event.instance_variable_set(:@event_type, :added_issue_without_decision_date) + new_event.instance_variable_set(:@task_status, "on_hold") + new_event.instance_variable_set(:@claim_type, "Remand") + new_event.instance_variable_set(:@decision_date, nil) + new_event + end + let(:remand_completed_disposition_event) do + new_event = added_issue_event.clone + new_event.instance_variable_set(:@event_type, :completed_disposition) + new_event.instance_variable_set(:@task_status, "completed") + new_event.instance_variable_set(:@disposition, "Granted") + new_event.instance_variable_set(:@decision_description, "Decision for CHAMPVA issue") + new_event.instance_variable_set(:@disposition_date, 4.days.ago.iso8601) + new_event.instance_variable_set(:@user_facility, "200") + new_event.instance_variable_set(:@claim_type, "Remand") + new_event + end + let(:remand_added_decision_date_event) do + new_event = completed_disposition_event.clone + new_event.instance_variable_set(:@event_type, :added_decision_date) + new_event.instance_variable_set(:@task_status, "assigned") + new_event.instance_variable_set(:@task_id, 900) + new_event.instance_variable_set(:@claim_type, "Remand") + new_event + end + let(:remand_removed_issue_event) do + new_event = added_decision_date_event.clone + new_event.instance_variable_set(:@event_type, :removed_issue) + new_event.instance_variable_set(:@task_status, "cancelled") + new_event.instance_variable_set(:@claim_type, "Remand") + new_event + end + let(:remand_withdrew_issue_event) do + new_event = removed_issue_event.clone + new_event.instance_variable_set(:@event_type, :withdrew_issue) + new_event.instance_variable_set(:@task_status, "cancelled") + new_event.instance_variable_set(:@claim_type, "Remand") + new_event + end + let(:events) do [ added_issue_event, @@ -158,7 +235,17 @@ completed_disposition_event, added_decision_date_event, removed_issue_event, - withdrew_issue_event + withdrew_issue_event, + remand_claim_creation_event, + remand_in_progress_status_event, + remand_cancelled_status_event, + remand_completed_status_event, + remand_incomplete_status_event, + remand_added_issue_without_decision_date_event, + remand_completed_disposition_event, + remand_added_decision_date_event, + remand_removed_issue_event, + remand_withdrew_issue_event ] end @@ -361,6 +448,186 @@ ] end + let(:remand_claim_creation_event_row) do + [ + "242080004", + "Mason Rodriguez", + "/decision_reviews/vha/tasks/999", + "in progress", + "20", + "Remand", + "", + "System", + remand_claim_creation_event.readable_event_date, + "Claim created", + nil, + "Claim created.", + nil + ] + end + + let(:remand_in_progress_status_event_row) do + [ + "242080004", + "Mason Rodriguez", + "/decision_reviews/vha/tasks/999", + "in progress", + "20", + "Remand", + "", + "System", + remand_in_progress_status_event.readable_event_date, + "Claim status - In progress", + nil, + "Claim can be processed.", + nil + ] + end + + let(:remand_cancelled_status_event_row) do + [ + "242080004", + "Mason Rodriguez", + "/decision_reviews/vha/tasks/999", + "cancelled", + "20", + "Remand", + "", + "System", + remand_cancelled_status_event.readable_event_date, + "Claim closed", + nil, + "Claim closed.", + nil + ] + end + let(:remand_completed_status_event_row) do + [ + "242080004", + "Mason Rodriguez", + "/decision_reviews/vha/tasks/999", + "completed", + "20", + "Remand", + "", + "System", + remand_completed_status_event.readable_event_date, + "Claim closed", + nil, + "Claim closed.", + nil + ] + end + let(:remand_incomplete_status_event_row) do + [ + "242080004", + "Mason Rodriguez", + "/decision_reviews/vha/tasks/999", + "incomplete", + "20", + "Remand", + "", + "System", + remand_incomplete_status_event.readable_event_date, + "Claim status - Incomplete", + nil, + "Claim cannot be processed until decision date is entered.", + nil + ] + end + let(:remand_added_issue_without_decision_date_event_row) do + [ + "242080004", + "Mason Rodriguez", + "/decision_reviews/vha/tasks/999", + "incomplete", + "20", + "Remand", + "VACO (101)", + "E. Thompson", + remand_added_issue_without_decision_date_event.readable_event_date, + "Added issue - No decision date", + "CHAMPVA", + "CHAMPVA issue description", + remand_added_issue_without_decision_date_event.readable_decision_date, + nil + ] + end + let(:remand_completed_disposition_event_row) do + [ + "242080004", + "Mason Rodriguez", + "/decision_reviews/vha/tasks/999", + "completed", + "20", + "Remand", + "Austin AAC (200)", + "E. Thompson", + remand_completed_disposition_event.readable_event_date, + "Completed disposition", + "CHAMPVA", + "CHAMPVA issue description", + remand_completed_disposition_event.readable_decision_date, + "Granted", + "Decision for CHAMPVA issue", + remand_completed_disposition_event.readable_disposition_date + ] + end + let(:remand_added_decision_date_event_row) do + [ + "242080004", + "Mason Rodriguez", + "/decision_reviews/vha/tasks/900", + "in progress", + "20", + "Remand", + "Austin AAC (200)", + "E. Thompson", + remand_added_decision_date_event.readable_event_date, + "Added decision date", + "CHAMPVA", + "CHAMPVA issue description", + remand_added_decision_date_event.readable_decision_date, + nil + ] + end + let(:remand_removed_issue_event_row) do + [ + "242080004", + "Mason Rodriguez", + "/decision_reviews/vha/tasks/900", + "cancelled", + "20", + "Remand", + "Austin AAC (200)", + "E. Thompson", + remand_removed_issue_event.readable_event_date, + "Removed issue", + "CHAMPVA", + "CHAMPVA issue description", + remand_removed_issue_event.readable_decision_date, + nil + ] + end + let(:remand_withdrew_issue_event_row) do + [ + "242080004", + "Mason Rodriguez", + "/decision_reviews/vha/tasks/900", + "cancelled", + "20", + "Remand", + "Austin AAC (200)", + "E. Thompson", + remand_withdrew_issue_event.readable_event_date, + "Withdrew issue", + "CHAMPVA", + "CHAMPVA issue description", + remand_withdrew_issue_event.readable_decision_date, + nil + ] + end + it "returns a csv string with the column headers, filters, and event rows" do rows = CSV.parse(subject) expect(rows.count).to eq(2 + events.length) @@ -377,6 +644,16 @@ expect(rows[10]).to eq(added_decision_date_event_row) expect(rows[11]).to eq(removed_issue_event_row) expect(rows[12]).to eq(withdrew_issue_event_row) + expect(rows[13]).to eq(remand_claim_creation_event_row) + expect(rows[14]).to eq(remand_in_progress_status_event_row) + expect(rows[15]).to eq(remand_cancelled_status_event_row) + expect(rows[16]).to eq(remand_completed_status_event_row) + expect(rows[17]).to eq(remand_incomplete_status_event_row) + expect(rows[18]).to eq(remand_added_issue_without_decision_date_event_row) + expect(rows[19]).to eq(remand_completed_disposition_event_row) + expect(rows[20]).to eq(remand_added_decision_date_event_row) + expect(rows[21]).to eq(remand_removed_issue_event_row) + expect(rows[22]).to eq(remand_withdrew_issue_event_row) end end end diff --git a/spec/services/claim_change_history/claim_history_event_spec.rb b/spec/services/claim_change_history/claim_history_event_spec.rb index 1ee915d9c08..8b4aeeabdb8 100644 --- a/spec/services/claim_change_history/claim_history_event_spec.rb +++ b/spec/services/claim_change_history/claim_history_event_spec.rb @@ -48,7 +48,8 @@ "event_date" => change_data_event_date, "task_versions" => version_changes, "days_waiting" => 25, - "task_closed_at" => "2023-10-19 22:47:16.233187" + "task_closed_at" => "2023-10-19 22:47:16.233187", + "type_classifier" => change_data_claim_type } end @@ -851,6 +852,14 @@ expect(subject).to eq("Supplemental Claim") end end + + context "when the claim type is Remand" do + let(:change_data_claim_type) { "Remand" } + + it "readable claim type of Remand" do + expect(subject).to eq("Remand") + end + end end describe ".readable_user_name" do