diff --git a/app/models/organizations/business_line.rb b/app/models/organizations/business_line.rb index 02a881a48af..23628e730c7 100644 --- a/app/models/organizations/business_line.rb +++ b/app/models/organizations/business_line.rb @@ -126,6 +126,15 @@ class QueryBuilder } }.freeze + USER_TABLE_ALIASES = [ + :intake_users, + :update_users, + :decision_users, + :decision_users_completed_by, + :requestor, + :decider + ].freeze + def initialize(query_type: :in_progress, parent: business_line, query_params: {}) @query_type = query_type @parent = parent @@ -210,9 +219,9 @@ def issue_type_count nonrating_issue_count = ActiveRecord::Base.connection.execute <<-SQL WITH task_review_issues AS ( #{hlr_query.to_sql} - UNION ALL + UNION #{sc_query.to_sql} - UNION ALL + UNION #{appeals_query.to_sql} ) SELECT issue_category, COUNT(1) AS nonrating_issue_count @@ -246,7 +255,7 @@ def change_history_rows SELECT versions.item_id, versions.item_type, - ARRAY_AGG(versions.object_changes ORDER BY versions.id) AS object_changes_array, + STRING_AGG(versions.object_changes, '|||' ORDER BY versions.id) AS object_changes_array, MAX(CASE WHEN versions.object_changes LIKE '%closed_at:%' THEN versions.whodunnit ELSE NULL @@ -259,8 +268,48 @@ def change_history_rows AND tasks.assigned_to_id = '#{parent.id.to_i}' GROUP BY versions.item_id, versions.item_type + ), imr_version_agg AS (SELECT + versions.item_id, + versions.item_type, + STRING_AGG(versions.object, '|||' ORDER BY versions.id) AS object_array, + STRING_AGG(versions.object_changes, '|||' ORDER BY versions.id) AS object_changes_array + FROM + versions + INNER JOIN issue_modification_requests ON issue_modification_requests.id = versions.item_id + WHERE versions.item_type = 'IssueModificationRequest' + GROUP BY + versions.item_id, versions.item_type + ), imr_distinct AS ( + SELECT DISTINCT ON (imr_cte.id) + imr_cte.id, + imr_cte.decided_at, + imr_cte.created_at, + imr_cte.decision_review_type, + imr_cte.decision_review_id, + imr_cte.status, + imr_cte.updated_at + FROM issue_modification_requests imr_cte + ), imr_lead_decided AS ( + SELECT id, + decision_review_id, + LEAD( + CASE + WHEN status = 'cancelled' THEN updated_at + ELSE decided_at + END, + 1, + '9999-12-31 23:59:59' -- Fake value to indicate out of bounds + ) OVER (PARTITION BY decision_review_id, decision_review_type ORDER BY decided_at, created_at DESC) AS next_decided_or_cancelled_at + FROM imr_distinct + ), imr_lead_created AS ( + SELECT id, + LEAD(created_at, 1, '9999-12-31 23:59:59') OVER (PARTITION BY decision_review_id, decision_review_type ORDER BY created_at ASC) AS next_created_at + FROM imr_distinct ) - SELECT tasks.id AS task_id, tasks.status AS task_status, request_issues.id AS request_issue_id, + SELECT tasks.id AS task_id, + check_imr_current_status.is_assigned_present, + tasks.status AS task_status, + request_issues.id AS request_issue_id, request_issues_updates.created_at AS request_issue_update_time, decision_issues.description AS decision_description, request_issues.benefit_type AS request_issue_benefit_type, request_issues_updates.id AS request_issue_update_id, request_issues.created_at AS request_issue_created_at, request_decision_issues.created_at AS request_decision_created_at, @@ -283,7 +332,38 @@ 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, 'HigherLevelReview' AS type_classifier + ) AS claimant_name, + 'HigherLevelReview' AS type_classifier, + imr.id AS issue_modification_request_id, + imr.nonrating_issue_category AS requested_issue_type, + imr.nonrating_issue_description As requested_issue_description, + imr.remove_original_issue, + imr.request_reason AS modification_request_reason, + imr.decision_date AS requested_decision_date, + imr.request_type AS request_type, + imr.status AS issue_modification_request_status, + imr.decision_reason AS decision_reason, + imr.decider_id decider_id, + imr.requestor_id as requestor_id, + CASE WHEN imr.status = 'cancelled' THEN imr.updated_at ELSE imr.decided_at END AS decided_at, + imr.created_at AS issue_modification_request_created_at, + imr.updated_at AS issue_modification_request_updated_at, + imr.edited_at AS issue_modification_request_edited_at, + imr.withdrawal_date AS issue_modification_request_withdrawal_date, + imr.decision_review_id AS decision_review_id, + imr.decision_review_type AS decision_review_type, + requestor.full_name AS requestor, + requestor.station_id AS requestor_station_id, + requestor.css_id AS requestor_css_id, + decider.full_name AS decider, + decider.station_id AS decider_station_id, + decider.css_id AS decider_css_id, + itv.object_changes_array AS imr_versions, + LAG(imr.created_at, 1) OVER (PARTITION BY tasks.id, imr.decision_review_id, imr.decision_review_type ORDER BY imr.created_at) AS previous_imr_created_at, + LAG(CASE WHEN imr.status = 'cancelled' THEN imr.updated_at ELSE imr.decided_at END) OVER (PARTITION BY tasks.id, imr.decision_review_id, imr.decision_review_type ORDER BY CASE WHEN imr.status = 'cancelled' THEN imr.updated_at ELSE imr.decided_at END) AS previous_imr_decided_at, + itv.object_array as previous_state_array, + imr_lead_decided.next_decided_or_cancelled_at, + imr_lead_created.next_created_at FROM tasks INNER JOIN request_issues ON request_issues.decision_review_type = tasks.appeal_type AND request_issues.decision_review_id = tasks.appeal_id @@ -293,6 +373,18 @@ def change_history_rows AND intakes.detail_id = tasks.appeal_id LEFT JOIN request_issues_updates ON request_issues_updates.review_type = tasks.appeal_type AND request_issues_updates.review_id = tasks.appeal_id + LEFT JOIN LATERAL ( + SELECT * + FROM issue_modification_requests imr + WHERE imr.decision_review_id = tasks.appeal_id + AND imr.decision_review_type = 'HigherLevelReview' + AND ( + imr.request_issue_id = request_issues.id + OR imr.request_type = 'addition' + ) + ) imr ON true + LEFT JOIN imr_lead_decided ON imr_lead_decided.id = imr.id + LEFT JOIN imr_lead_created ON imr_lead_created.id = imr.id LEFT JOIN request_decision_issues ON request_decision_issues.request_issue_id = request_issues.id LEFT JOIN decision_issues ON decision_issues.decision_review_id = tasks.appeal_id AND decision_issues.decision_review_type = tasks.appeal_type AND decision_issues.id = request_decision_issues.decision_issue_id @@ -307,63 +399,136 @@ def change_history_rows LEFT JOIN users update_users ON request_issues_updates.user_id = update_users.id LEFT JOIN users decision_users ON decision_users.id = tv.version_closed_by_id::int LEFT JOIN users decision_users_completed_by ON decision_users_completed_by.id = tasks.completed_by_id + LEFT JOIN users requestor ON imr.requestor_id = requestor.id + LEFT JOIN users decider ON imr.decider_id = decider.id + LEFT JOIN imr_version_agg itv ON itv.item_type = 'IssueModificationRequest' AND itv.item_id = imr.id + LEFT JOIN LATERAL ( + SELECT CASE + WHEN EXISTS ( + SELECT 1 + FROM issue_modification_requests imr + WHERE imr.decision_review_id = request_issues.decision_review_id + AND imr.decision_review_type = 'HigherLevelReview' + AND imr.status = 'assigned' + ) THEN true + ELSE false + END AS is_assigned_present + ) check_imr_current_status on true WHERE tasks.type = 'DecisionReviewTask' AND tasks.assigned_to_type = 'Organization' AND tasks.assigned_to_id = '#{parent.id.to_i}' #{sanitized_filters} - UNION ALL - SELECT tasks.id AS task_id, tasks.status AS task_status, request_issues.id AS request_issue_id, - request_issues_updates.created_at AS request_issue_update_time, decision_issues.description AS decision_description, - request_issues.benefit_type AS request_issue_benefit_type, request_issues_updates.id AS request_issue_update_id, - request_issues.created_at AS request_issue_created_at, request_decision_issues.created_at AS request_decision_created_at, - intakes.completed_at AS intake_completed_at, update_users.full_name AS update_user_name, tasks.created_at AS task_created_at, - intake_users.full_name AS intake_user_name, update_users.station_id AS update_user_station_id, tasks.closed_at AS task_closed_at, - intake_users.station_id AS intake_user_station_id, decision_issues.created_at AS decision_created_at, - COALESCE(decision_users.station_id, decision_users_completed_by.station_id) AS decision_user_station_id, - COALESCE(decision_users.full_name, decision_users_completed_by.full_name) AS decision_user_name, - COALESCE(decision_users.css_id, decision_users_completed_by.css_id) AS decision_user_css_id, - intake_users.css_id AS intake_user_css_id, update_users.css_id AS update_user_css_id, - request_issues_updates.before_request_issue_ids, request_issues_updates.after_request_issue_ids, - request_issues_updates.withdrawn_request_issue_ids, request_issues_updates.edited_request_issue_ids, - decision_issues.caseflow_decision_date, request_issues.decision_date_added_at, - tasks.appeal_type, tasks.appeal_id, request_issues.nonrating_issue_category, request_issues.nonrating_issue_description, - request_issues.decision_date, decision_issues.disposition, tasks.assigned_at, request_issues.unidentified_issue_text, - request_decision_issues.decision_issue_id, request_issues.closed_at AS request_issue_closed_at, - tv.object_changes_array AS task_versions, (CURRENT_TIMESTAMP::date - tasks.assigned_at::date) AS days_waiting, - COALESCE(intakes.veteran_file_number, supplemental_claims.veteran_file_number) AS veteran_file_number, - COALESCE( - 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, 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 - INNER JOIN supplemental_claims ON tasks.appeal_type = 'SupplementalClaim' - AND tasks.appeal_id = supplemental_claims.id - LEFT JOIN intakes ON tasks.appeal_type = intakes.detail_type - AND intakes.detail_id = tasks.appeal_id - LEFT JOIN request_issues_updates ON request_issues_updates.review_type = tasks.appeal_type - AND request_issues_updates.review_id = tasks.appeal_id - LEFT JOIN request_decision_issues ON request_decision_issues.request_issue_id = request_issues.id - LEFT JOIN decision_issues ON decision_issues.decision_review_id = tasks.appeal_id - AND decision_issues.decision_review_type = tasks.appeal_type AND decision_issues.id = request_decision_issues.decision_issue_id - LEFT JOIN claimants ON claimants.decision_review_id = tasks.appeal_id - AND claimants.decision_review_type = tasks.appeal_type - LEFT JOIN versions_agg tv ON tv.item_type = 'Task' AND tv.item_id = tasks.id - LEFT JOIN people ON claimants.participant_id = people.participant_id - LEFT JOIN bgs_attorneys ON claimants.participant_id = bgs_attorneys.participant_id - LEFT JOIN unrecognized_appellants ON claimants.id = unrecognized_appellants.claimant_id - LEFT JOIN unrecognized_party_details ON unrecognized_appellants.unrecognized_party_detail_id = unrecognized_party_details.id - LEFT JOIN users intake_users ON intakes.user_id = intake_users.id - LEFT JOIN users update_users ON request_issues_updates.user_id = update_users.id - LEFT JOIN users decision_users ON decision_users.id = tv.version_closed_by_id::int - LEFT JOIN users decision_users_completed_by ON decision_users_completed_by.id = tasks.completed_by_id - WHERE tasks.type = 'DecisionReviewTask' - AND tasks.assigned_to_type = 'Organization' - AND tasks.assigned_to_id = '#{parent.id.to_i}' - #{sanitized_filters} - #{sc_type_clauses} + UNION ALL + SELECT tasks.id AS task_id, check_imr_current_status.is_assigned_present, tasks.status AS task_status, request_issues.id AS request_issue_id, + request_issues_updates.created_at AS request_issue_update_time, decision_issues.description AS decision_description, + request_issues.benefit_type AS request_issue_benefit_type, request_issues_updates.id AS request_issue_update_id, + request_issues.created_at AS request_issue_created_at, request_decision_issues.created_at AS request_decision_created_at, + intakes.completed_at AS intake_completed_at, update_users.full_name AS update_user_name, tasks.created_at AS task_created_at, + intake_users.full_name AS intake_user_name, update_users.station_id AS update_user_station_id, tasks.closed_at AS task_closed_at, + intake_users.station_id AS intake_user_station_id, decision_issues.created_at AS decision_created_at, + COALESCE(decision_users.station_id, decision_users_completed_by.station_id) AS decision_user_station_id, + COALESCE(decision_users.full_name, decision_users_completed_by.full_name) AS decision_user_name, + COALESCE(decision_users.css_id, decision_users_completed_by.css_id) AS decision_user_css_id, + intake_users.css_id AS intake_user_css_id, update_users.css_id AS update_user_css_id, + request_issues_updates.before_request_issue_ids, request_issues_updates.after_request_issue_ids, + request_issues_updates.withdrawn_request_issue_ids, request_issues_updates.edited_request_issue_ids, + decision_issues.caseflow_decision_date, request_issues.decision_date_added_at, + tasks.appeal_type, tasks.appeal_id, request_issues.nonrating_issue_category, request_issues.nonrating_issue_description, + request_issues.decision_date, decision_issues.disposition, tasks.assigned_at, request_issues.unidentified_issue_text, + request_decision_issues.decision_issue_id, request_issues.closed_at AS request_issue_closed_at, + tv.object_changes_array AS task_versions, (CURRENT_TIMESTAMP::date - tasks.assigned_at::date) AS days_waiting, + COALESCE(intakes.veteran_file_number, supplemental_claims.veteran_file_number) AS veteran_file_number, + COALESCE( + 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, + supplemental_claims.type AS type_classifier, + imr.id AS issue_modification_request_id, + imr.nonrating_issue_category AS requested_issue_type, + imr.nonrating_issue_description As requested_issue_description, + imr.remove_original_issue, + imr.request_reason AS modification_request_reason, + imr.decision_date AS requested_decision_date, + imr.request_type AS request_type, + imr.status AS issue_modification_request_status, + imr.decision_reason AS decision_reason, + imr.decider_id AS decider_id, + imr.requestor_id AS requestor_id, + CASE WHEN imr.status = 'cancelled' THEN imr.updated_at ELSE imr.decided_at END AS decided_at, + imr.created_at AS issue_modification_request_created_at, + imr.updated_at AS issue_modification_request_updated_at, + imr.edited_at AS issue_modification_request_edited_at, + imr.withdrawal_date AS issue_modification_request_withdrawal_date, + imr.decision_review_id AS decision_review_id, + imr.decision_review_type AS decision_review_type, + requestor.full_name AS requestor, + requestor.station_id AS requestor_station_id, + requestor.css_id AS requestor_css_id, + decider.full_name AS decider, + decider.station_id AS decider_station_id, + decider.css_id AS decider_css_id, + itv.object_changes_array AS imr_versions, + LAG(imr.created_at, 1) OVER (PARTITION BY tasks.id, imr.decision_review_id, imr.decision_review_type ORDER BY imr.created_at) AS previous_imr_created_at, + LAG(CASE WHEN imr.status = 'cancelled' THEN imr.updated_at ELSE imr.decided_at END) OVER (PARTITION BY tasks.id, imr.decision_review_id, imr.decision_review_type ORDER BY CASE WHEN imr.status = 'cancelled' THEN imr.updated_at ELSE imr.decided_at END) AS previous_imr_decided_at, + itv.object_array as previous_state_array, + imr_lead_decided.next_decided_or_cancelled_at, + imr_lead_created.next_created_at + FROM tasks + INNER JOIN request_issues ON request_issues.decision_review_type = tasks.appeal_type + AND request_issues.decision_review_id = tasks.appeal_id + INNER JOIN supplemental_claims ON tasks.appeal_type = 'SupplementalClaim' + AND tasks.appeal_id = supplemental_claims.id + LEFT JOIN intakes ON tasks.appeal_type = intakes.detail_type + AND intakes.detail_id = tasks.appeal_id + LEFT JOIN request_issues_updates ON request_issues_updates.review_type = tasks.appeal_type + AND request_issues_updates.review_id = tasks.appeal_id + LEFT JOIN LATERAL ( + SELECT * + FROM issue_modification_requests imr + WHERE imr.decision_review_id = tasks.appeal_id + AND imr.decision_review_type = 'SupplementalClaim' + AND ( + imr.request_issue_id = request_issues.id + OR imr.request_type = 'addition' + ) + ) imr ON true + LEFT JOIN imr_lead_decided ON imr_lead_decided.id = imr.id + LEFT JOIN imr_lead_created ON imr_lead_created.id = imr.id + LEFT JOIN request_decision_issues ON request_decision_issues.request_issue_id = request_issues.id + LEFT JOIN decision_issues ON decision_issues.decision_review_id = tasks.appeal_id + AND decision_issues.decision_review_type = tasks.appeal_type AND decision_issues.id = request_decision_issues.decision_issue_id + LEFT JOIN claimants ON claimants.decision_review_id = tasks.appeal_id + AND claimants.decision_review_type = tasks.appeal_type + LEFT JOIN versions_agg tv ON tv.item_type = 'Task' AND tv.item_id = tasks.id + LEFT JOIN people ON claimants.participant_id = people.participant_id + LEFT JOIN bgs_attorneys ON claimants.participant_id = bgs_attorneys.participant_id + LEFT JOIN unrecognized_appellants ON claimants.id = unrecognized_appellants.claimant_id + LEFT JOIN unrecognized_party_details ON unrecognized_appellants.unrecognized_party_detail_id = unrecognized_party_details.id + LEFT JOIN users intake_users ON intakes.user_id = intake_users.id + LEFT JOIN users update_users ON request_issues_updates.user_id = update_users.id + LEFT JOIN users decision_users ON decision_users.id = tv.version_closed_by_id::int + LEFT JOIN users decision_users_completed_by ON decision_users_completed_by.id = tasks.completed_by_id + LEFT JOIN users requestor ON imr.requestor_id = requestor.id + LEFT JOIN users decider ON imr.decider_id = decider.id + LEFT JOIN imr_version_agg itv ON itv.item_type = 'IssueModificationRequest' AND itv.item_id = imr.id + LEFT JOIN LATERAL ( + SELECT CASE + WHEN EXISTS ( + SELECT 1 + FROM issue_modification_requests imr + WHERE imr.decision_review_id = request_issues.decision_review_id + AND imr.decision_review_type = 'SupplementalClaim' + AND imr.status = 'assigned' + ) THEN true + ELSE false + END AS is_assigned_present + ) check_imr_current_status on true + WHERE tasks.type = 'DecisionReviewTask' + 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 @@ -395,12 +560,40 @@ def change_history_sql_filter_array def task_status_filter if query_params[:task_status].present? - " AND #{where_clause_from_array(Task, :status, query_params[:task_status]).to_sql}" + task_specific_status_filter else " AND tasks.status IN ('assigned', 'in_progress', 'on_hold', 'completed', 'cancelled') " end end + def task_specific_status_filter + if query_params[:task_status].include?("pending") + task_status_pending_filter + else + task_status_without_pending_filter + end + end + + def task_status_pending_filter + <<-SQL + AND ( + (imr.id IS NOT NULL AND imr.status = 'assigned') + OR #{where_clause_from_array(Task, :status, query_params[:task_status].uniq).to_sql} + ) + SQL + end + + def task_status_without_pending_filter + <<-SQL + AND NOT EXISTS( + SELECT decision_review_id FROM issue_modification_requests WHERE + issue_modification_requests.status = 'assigned' + AND issue_modification_requests.decision_review_id = tasks.appeal_id + AND tasks.appeal_type = issue_modification_requests.decision_review_type) + AND #{where_clause_from_array(Task, :status, query_params[:task_status].uniq).to_sql} + SQL + end + def claim_type_filter if query_params[:claim_type].present? temp_claim_types = query_params[:claim_type].dup @@ -476,19 +669,16 @@ def days_waiting_filter end end - # rubocop:disable Metrics/AbcSize def station_id_filter if query_params[:facilities].present? + conditions = USER_TABLE_ALIASES.map do |alias_name| + User.arel_table.alias(alias_name)[:station_id].in(query_params[:facilities]).to_sql + end + <<-SQL AND ( - #{User.arel_table.alias(:intake_users)[:station_id].in(query_params[:facilities]).to_sql} - OR - #{User.arel_table.alias(:update_users)[:station_id].in(query_params[:facilities]).to_sql} - OR - #{User.arel_table.alias(:decision_users)[:station_id].in(query_params[:facilities]).to_sql} - OR - #{User.arel_table.alias(:decision_users_completed_by)[:station_id].in(query_params[:facilities]).to_sql} + #{conditions.join(' OR ')} ) SQL end @@ -496,21 +686,18 @@ def station_id_filter def user_css_id_filter if query_params[:personnel].present? + conditions = USER_TABLE_ALIASES.map do |alias_name| + User.arel_table.alias(alias_name)[:css_id].in(query_params[:personnel]).to_sql + end + <<-SQL AND ( - #{User.arel_table.alias(:intake_users)[:css_id].in(query_params[:personnel]).to_sql} - OR - #{User.arel_table.alias(:update_users)[:css_id].in(query_params[:personnel]).to_sql} - OR - #{User.arel_table.alias(:decision_users)[:css_id].in(query_params[:personnel]).to_sql} - OR - #{User.arel_table.alias(:decision_users_completed_by)[:css_id].in(query_params[:personnel]).to_sql} + #{conditions.join(' OR ')} ) SQL end end - # rubocop:enable Metrics/AbcSize #################### End of Change history filter helpers ######################## diff --git a/app/services/claim_change_history/change_history_event_serializer.rb b/app/services/claim_change_history/change_history_event_serializer.rb index 5b8bca631ce..5bd16ea05e9 100644 --- a/app/services/claim_change_history/change_history_event_serializer.rb +++ b/app/services/claim_change_history/change_history_event_serializer.rb @@ -24,4 +24,27 @@ class ChangeHistoryEventSerializer withdrawalRequestDate: object.withdrawal_request_date } end + + attribute :modificationRequestDetails do |object| + { + requestType: object.request_type, + benefitType: object.benefit_type, + newIssueType: object.new_issue_type, + newIssueDescription: object.new_issue_description, + newDecisionDate: object.new_decision_date, + modificationRequestReason: object.modification_request_reason, + issueModificationRequestWithdrawalDate: object.issue_modification_request_withdrawal_date, + removeOriginalIssue: object.remove_original_issue, + issueModificationRequestStatus: object.issue_modification_request_status, + requestor: object.requestor, + decider: object.decider, + decidedAtDate: object.decided_at_date, + decisionReason: object.decision_reason, + previousIssueType: object.previous_issue_type, + previousIssueDescription: object.previous_issue_description, + previousDecisionDate: object.previous_decision_date, + previousModificationRequestReason: object.previous_modification_request_reason, + previousWithdrawalDate: object.previous_withdrawal_date + } + end end diff --git a/app/services/claim_change_history/change_history_filter_parser.rb b/app/services/claim_change_history/change_history_filter_parser.rb index 2e423e43e13..4fbcd6b2826 100644 --- a/app/services/claim_change_history/change_history_filter_parser.rb +++ b/app/services/claim_change_history/change_history_filter_parser.rb @@ -25,6 +25,7 @@ def parse_filters private + # rubocop:disable Metrics/MethodLength def events_filter_helper event_mapping = { "added_decision_date" => :added_decision_date, @@ -34,21 +35,32 @@ def events_filter_helper "claim_closed" => [:completed, :cancelled], "claim_status_incomplete" => :incomplete, "claim_status_inprogress" => :in_progress, + "claim_status_pending" => :pending, "completed_disposition" => :completed_disposition, "removed_issue" => :removed_issue, "withdrew_issue" => :withdrew_issue, - "claim_cancelled" => :cancelled + "claim_cancelled" => :cancelled, + "requested_issue_modification" => :modification, + "requested_issue_addition" => :addition, + "requested_issue_removal" => :removal, + "requested_issue_withdrawal" => :withdrawal, + "approval_of_request" => :request_approved, + "rejection_of_request" => :request_denied, + "cancellation_of_request" => :request_cancelled, + "edit_of_request" => :request_edited } filter_params[:events]&.values&.map { |event_type| event_mapping[event_type] }&.flatten end + # rubocop:enable Metrics/MethodLength def task_status_filter_helper status_mapping = { "incomplete" => "on_hold", "in_progress" => %w[assigned in_progress], "completed" => "completed", - "cancelled" => "cancelled" + "cancelled" => "cancelled", + "pending" => "pending" } filter_params[:statuses]&.values&.map { |task_status| status_mapping[task_status] }&.flatten diff --git a/app/services/claim_change_history/change_history_reporter.rb b/app/services/claim_change_history/change_history_reporter.rb index 68cf043c19c..8025b63d60e 100644 --- a/app/services/claim_change_history/change_history_reporter.rb +++ b/app/services/claim_change_history/change_history_reporter.rb @@ -18,7 +18,12 @@ class ChangeHistoryReporter Edit\ Action Issue\ Type Issue\ Description - Prior\ Decision\ Date + Decision\ Date + New\ Issue\ Type + New\ Issue\ Description + New\ Decision\ Date + Request\ Reason + Reason\ for\ Rejection Disposition Disposition\ Description Disposition\ Date diff --git a/app/services/claim_change_history/claim_history_event.rb b/app/services/claim_change_history/claim_history_event.rb index 010273a561b..e19ac3ed289 100644 --- a/app/services/claim_change_history/claim_history_event.rb +++ b/app/services/claim_change_history/claim_history_event.rb @@ -15,7 +15,12 @@ class ClaimHistoryEvent :benefit_type, :issue_type, :issue_description, :decision_date, :disposition, :decision_description, :withdrawal_request_date, :task_status, :disposition_date, :intake_completed_date, :event_user_name, - :event_user_css_id + :event_user_css_id, :new_issue_type, :new_issue_description, :new_decision_date, + :modification_request_reason, :request_type, :decision_reason, :decided_at_date, + :issue_modification_request_withdrawal_date, :requestor, + :decider, :remove_original_issue, :issue_modification_request_status, + :previous_issue_type, :previous_issue_description, :previous_decision_date, + :previous_modification_request_reason, :previous_withdrawal_date EVENT_TYPES = [ :completed_disposition, @@ -28,7 +33,16 @@ class ClaimHistoryEvent :in_progress, :completed, :incomplete, - :cancelled + :cancelled, + :pending, + :modification, + :addition, + :withdrawal, + :removal, + :request_approved, + :request_denied, + :request_cancelled, + :request_edited ].freeze ISSUE_EVENTS = [ @@ -47,10 +61,31 @@ class ClaimHistoryEvent :added_decision_date ].freeze - STATUS_EVENTS = [:in_progress, :incomplete, :completed, :claim_creation, :cancelled].freeze + STATUS_EVENTS = [ + :completed, + :claim_creation, + :cancelled, + :in_progress, + :incomplete, + :pending + ].freeze + + REQUEST_ISSUE_MODIFICATION_EVENTS = [ + :modification, + :addition, + :withdrawal, + :removal, + :request_approved, + :request_denied, + :request_cancelled, + :request_edited + ].freeze REQUEST_ISSUE_TIME_WINDOW = 15 STATUS_EVENT_TIME_WINDOW = 2 + ISSUE_MODIFICATION_REQUEST_CREATION_WINDOW = 60 + # Used to signal when the database lead function is out of bounds + OUT_OF_BOUNDS_LEAD_TIME = Time.utc(9999, 12, 31, 23, 59, 59) class << self def from_change_data(event_type, change_data) @@ -73,11 +108,242 @@ def create_claim_creation_event(change_data) from_change_data(:claim_creation, change_data.merge(intake_event_hash(change_data))) end + def create_issue_modification_request_event(change_data) + issue_modification_events = [] + request_type = change_data["request_type"] + event_hash = request_issue_modification_event_hash(change_data) + + if change_data["previous_state_array"].present? + first_version = parse_versions(change_data["previous_state_array"])[0] + event_hash.merge!(update_event_hash_data_from_version_object(first_version)) + end + + if request_type == "addition" + change_data = issue_attributes_for_request_type_addition(change_data) + end + + issue_modification_events.push from_change_data(request_type.to_sym, change_data.merge(event_hash)) + end + + def create_edited_request_issue_events(change_data) + edited_events = [] + imr_versions = parse_versions(change_data["imr_versions"]) + previous_version = parse_versions(change_data["previous_state_array"]) + + if imr_versions.present? + *rest_of_versions, last_version = imr_versions + + if last_version["status"].present? + edited_events.push(*create_last_version_events(change_data, last_version)) + else + rest_of_versions.push(last_version) + end + edited_events.push(*create_event_from_rest_of_versions(change_data, rest_of_versions, previous_version)) + else + create_pending_status_event(change_data, change_data["issue_modification_request_updated_at"]) + end + edited_events + end + + def create_event_from_rest_of_versions(change_data, edited_versions, previous_version) + edit_of_request_events = [] + event_type = :request_edited + event_date_hash = {} + edited_versions.map.with_index do |version, index| + event_date_hash = request_issue_modification_event_hash(change_data) + .merge("event_date" => version["updated_at"][1]) + # this create_event_from_version_object updated the previous version fields in change data + # that is being used in the front end to show the original records. + if !previous_version[index].nil? + event_date_hash.merge!(create_event_from_version_object(previous_version[index])) + # this update_event_hash_data_from_version_object updates the change_data values with previous or + # unedited data. since change_data has the final version of the data that was updated. + # this is necessary to preserve the history that is displayed in the frontend. + event_date_hash.merge!(update_event_hash_data_from_version_object(previous_version[index])) + end + + event_date_hash.merge!(update_event_hash_data_from_version(version, 1)) + edit_of_request_events.push(*from_change_data(event_type, change_data.merge(event_date_hash))) + end + edit_of_request_events + end + + def create_last_version_events(change_data, last_version) + edited_events = [] + + last_version["status"].map.with_index do |status, index| + if status == "assigned" + edited_events.push(*create_pending_status_event(change_data, last_version["updated_at"][index])) + else + edited_events.push(*create_request_issue_decision_events( + change_data, last_version["updated_at"][index], status + )) + end + end + edited_events + end + + def create_request_issue_decision_events(change_data, event_date, event) + events = [] + event_user = change_data["decider"] || change_data["requestor"] + + decision_event_hash = pending_system_hash + .merge("event_date" => event_date, + "event_user_name" => event_user, + "user_facility" => change_data["decider_station_id"] || change_data["requestor_station_id"], + "event_user_css_id" => change_data["decider_css_id"] || change_data["requestor_css_id"]) + + change_data = issue_attributes_for_request_type_addition(change_data) if change_data["request_type"] == "addition" + + request_event_type = "request_#{event}" + events.push from_change_data(request_event_type.to_sym, change_data.merge(decision_event_hash)) + + events.push create_imr_in_progress_status_event(change_data) + events + end + + def create_imr_in_progress_status_event(change_data) + in_progress_system_hash_events = pending_system_hash + .merge("event_date" => (change_data["decided_at"] || + change_data["issue_modification_request_updated_at"])) + + # If the imr is not decided, then always skip in progress creation + if imr_decided_or_cancelled?(change_data) && create_imr_in_progress_status_event?(change_data) + from_change_data(:in_progress, change_data.merge(in_progress_system_hash_events)) + end + end + + def create_imr_in_progress_status_event?(change_data) + # If the next imr is already decided in the same transaction, it's not in reverse order, and it's + # not the last imr then defer creation + return false if early_deferral?(change_data) + + if do_not_defer_in_progress_creation?(change_data) + # If it's in reverse order and the creation of the next imr is after the current decision time then generate + # an event since the next imr will start a new pending/in progress loop + # Or + # If the next created by was after the decided_at then, this was an in progress transition so create one + # Or + # If it's the last IMR and the next imr was decided or cancelled in the same transaction then go ahead + # and generate an in progress event since the ordering is odd due to the decided at in the same transaction + true + elsif next_imr_decided_is_out_of_bounds?(change_data) + # If it's the end of the lead rows, then this is the last decided row + # If the next created at is in the same transaction, then defer event creation, otherwise create an in progress + # Or + # If the next imr was created at the same time that the current imr is decided, then defer + create_in_progress_event_for_last_decided_by_imr?(change_data) + elsif defer_in_progress_creation?(change_data) + # If the next imr was in the same transaction and it's also decided, then defer event creation to it. + # Or + # If the next imr was created in the same transaction as the next decided, then defer to the next imr + # Or + # If the next imr was created at the same time that the current imr is decided, then defer + # since it should never leave the current pending loop in that case + false + else + # If nothing else matches and the next one is also decided then go ahead and generate an in progress event + # This may occasionally result in a false positive but it should be right most of the time + change_data["next_decided_or_cancelled_at"].present? + end + end + + def do_not_defer_in_progress_creation?(change_data) + (imr_reverse_order?(change_data) && next_imr_created_by_after_current_decided_at?(change_data)) || + (change_data["next_decided_or_cancelled_at"].nil? && + next_imr_created_by_after_current_decided_at?(change_data)) || + (last_imr?(change_data) && next_imr_decided_or_cancelled_in_same_transaction?(change_data)) + end + + def defer_in_progress_creation?(change_data) + (next_imr_created_in_same_transaction?(change_data) && change_data["next_decided_or_cancelled_at"]) || + next_imr_created_at_and_decided_at_in_same_transaction?(change_data) || + next_imr_created_in_same_transaction_as_decided_at?(change_data) + end + + def imr_decided_or_cancelled?(change_data) + %w[cancelled denied approved].include?(change_data["issue_modification_request_status"]) + end + + def next_imr_decided_or_cancelled_in_same_transaction?(change_data) + timestamp_within_seconds?(change_data["decided_at"], change_data["next_decided_or_cancelled_at"], 2) + end + + def next_imr_created_in_same_transaction?(change_data) + timestamp_within_seconds?(change_data["issue_modification_request_created_at"], + change_data["next_created_at"], + 2) + end + + def next_imr_created_in_same_transaction_as_decided_at?(change_data) + timestamp_within_seconds?(change_data["next_created_at"], + change_data["decided_at"], + 2) + end + + def next_imr_created_by_after_current_decided_at?(change_data) + change_data["next_created_at"] && + change_data["decided_at"] && + !last_imr?(change_data) && + (change_data["next_created_at"].change(usec: 0) > change_data["decided_at"].change(usec: 0)) + end + + def next_imr_created_at_and_decided_at_in_same_transaction?(change_data) + timestamp_within_seconds?(change_data["next_decided_or_cancelled_at"], + change_data["next_created_at"], + 2) + end + + def imr_reverse_order?(change_data) + change_data["previous_imr_decided_at"].nil? || change_data["decided_at"].nil? || + (change_data["previous_imr_decided_at"] > change_data["decided_at"]) + end + + def next_imr_decided_is_out_of_bounds?(change_data) + change_data["next_decided_or_cancelled_at"] == OUT_OF_BOUNDS_LEAD_TIME + end + + def last_imr?(change_data) + change_data["next_created_at"] == OUT_OF_BOUNDS_LEAD_TIME + end + + def create_in_progress_event_for_last_decided_by_imr?(change_data) + if next_imr_created_in_same_transaction?(change_data) || + next_imr_created_in_same_transaction_as_decided_at?(change_data) + false + else + true + end + end + + def early_deferral?(change_data) + next_imr_decided_or_cancelled_in_same_transaction?(change_data) && + !imr_reverse_order?(change_data) && !last_imr?(change_data) + end + + def create_pending_status_event(change_data, event_date) + pending_system_hash_events = pending_system_hash + .merge("event_date" => event_date) + + if change_data["previous_imr_created_at"].nil? + # If this is the first IMR then it will always generate a pending event. + from_change_data(:pending, change_data.merge(pending_system_hash_events)) + elsif timestamp_within_seconds?(change_data["previous_imr_decided_at"], + change_data["issue_modification_request_created_at"], + STATUS_EVENT_TIME_WINDOW) + # If this IMR was created at the same time as the previous decided at then skip pending event creation. + nil + elsif !previous_imr_created_in_same_transaction?(change_data) + # if two imr's are of different transaction and if decision has already been made then we + # want to put pending status since it went back to pending status before it was approved/cancelled or denied. + from_change_data(:pending, change_data.merge(pending_system_hash_events)) + end + end + # rubocop:disable Metrics/MethodLength def create_status_events(change_data) status_events = [] - versions = parse_versions(change_data) - + versions = parse_versions(change_data["task_versions"]) hookless_cancelled_events = handle_hookless_cancelled_status_events(versions, change_data) status_events.push(*hookless_cancelled_events) @@ -99,29 +365,34 @@ def create_status_events(change_data) rest_of_versions.map do |version| status_events.push event_from_version(version, 1, change_data) end + + # If there are no events, then it had versions but none that altered status so create one from current status + status_events.compact! + if status_events.empty? + status_events.push create_status_event_from_current_status(change_data) + end elsif hookless_cancelled_events.empty? # No versions so make an event with the current status - # There is a chance that a task has no intake either through data setup or through a remanded SC - event_date = change_data["intake_completed_at"] || change_data["task_created_at"] - status_events.push from_change_data(task_status_to_event_type(change_data["task_status"]), - change_data.merge("event_date" => event_date, - "event_user_name" => "System")) + status_events.push create_status_event_from_current_status(change_data) end status_events end # rubocop:enable Metrics/MethodLength - def parse_versions(change_data) - versions = change_data["task_versions"] - if versions - # Quite a bit faster but less safe. Should probably be fine since it's coming from the database - # rubocop:disable Security/YAMLLoad - versions[1..-2].split(",").map { |yaml| YAML.load(yaml.gsub(/^"|"$/, "")) } - # versions[1..-2].split(",").map { |yaml| YAML.safe_load(yaml.gsub(/^"|"$/, ""), [Time]) } - # rubocop:enable Security/YAMLLoad + def create_status_event_from_current_status(change_data) + # There is a chance that a task has no intake either through data setup or through a remanded SC + from_change_data(task_status_to_event_type(change_data["task_status"]), + change_data.merge("event_date" => change_data["intake_completed_at"] || + change_data["task_created_at"], + "event_user_name" => "System")) + end - end + def parse_versions(versions) + # Quite a bit faster but less safe. Should probably be fine since it's coming from the database + # rubocop:disable Security/YAMLLoad + versions&.split("|||")&.map { |yaml| YAML.load(yaml.gsub(/^"|"$/, "")) } + # rubocop:enable Security/YAMLLoad end def create_issue_events(change_data) @@ -144,10 +415,32 @@ def create_issue_events(change_data) issue_events end + def issue_attributes_for_request_type_addition(change_data) + # addition should not have issue_type that is pre-existing + issue_data = { + "nonrating_issue_category" => nil, + "nonrating_issue_description" => nil, + "decision_date" => nil + } + + change_data.merge(issue_data) + end + + def previous_imr_created_in_same_transaction?(change_data) + timestamp_within_seconds?(change_data["issue_modification_request_created_at"], + change_data["previous_imr_created_at"] || + change_data["issue_modification_request_created_at"], + ISSUE_MODIFICATION_REQUEST_CREATION_WINDOW) + end + def extract_issue_ids_from_change_data(change_data, key) (change_data[key] || "").scan(/\d+/).map(&:to_i) end + def decider_user_facility(change_data) + change_data["decider_station_id"] || change_data["requestor_station_id"] + end + def process_issue_ids(request_issue_ids, event_type, change_data) created_events = [] @@ -221,10 +514,29 @@ def task_status_to_event_type(task_status) "assigned" => :in_progress, "on_hold" => :incomplete, "completed" => :completed, - "cancelled" => :cancelled + "cancelled" => :cancelled, + "pending" => :pending }[task_status] end + def update_event_hash_data_from_version(version, index) + version_database_field_mapping.each_with_object({}) do |(version_key, db_key), data| + data[db_key] = version[version_key][index] unless version[version_key].nil? + end + end + + def update_event_hash_data_from_version_object(version) + version_database_field_mapping.each_with_object({}) do |(version_key, db_key), data| + data[db_key] = version[version_key] + end + end + + def create_event_from_version_object(version) + previous_version_database_field_mapping.each_with_object({}) do |(version_key, db_key), data| + data[db_key] = version[version_key] + end + end + def event_from_version(changes, index, change_data) # If there is no task status change in the set of papertrail changes, ignore the object if changes["status"] @@ -269,6 +581,28 @@ def update_event_hash(change_data) } end + def version_database_field_mapping + { + "nonrating_issue_category" => "requested_issue_type", + "nonrating_issue_description" => "requested_issue_description", + "remove_original_issue" => "remove_original_issue", + "request_reason" => "modification_request_reason", + "decision_date" => "requested_decision_date", + "decision_reason" => "decision_reason", + "withdrawal_date" => "issue_modification_request_withdrawal_date" + } + end + + def previous_version_database_field_mapping + { + "nonrating_issue_category" => "previous_issue_type", + "nonrating_issue_description" => "previous_issue_description", + "decision_date" => "previous_decision_date", + "request_reason" => "previous_modification_request_reason", + "withdrawal_date" => "previous_withdrawal_date" + } + end + def add_issue_update_event_hash(change_data) # Check the current request issue updates time to see if the issue update is in the correct row # If it is, then do the normal update_event_hash information @@ -301,6 +635,22 @@ def retrieve_issue_update_data(change_data) end end + def request_issue_modification_event_hash(change_data) + { + "event_date" => change_data["issue_modification_request_created_at"], + "event_user_name" => change_data["requestor"], + "user_facility" => change_data["requestor_station_id"], + "event_user_css_id" => change_data["requestor_css_id"] + } + end + + def pending_system_hash + { + "event_user_name" => "System", + "event_type" => "in_progress" + } + end + def timestamp_within_seconds?(first_date, second_date, time_in_seconds) return false unless first_date && second_date @@ -356,7 +706,7 @@ def to_csv_array [ veteran_file_number, claimant_name, task_url, readable_task_status, days_waiting, readable_claim_type, readable_facility_name, readable_user_name, readable_event_date, - readable_event_type, issue_or_status_information, disposition_information + readable_event_type, issue_or_status_information, issue_modification_request_information, disposition_information ] end @@ -371,7 +721,8 @@ def readable_task_status "in_progress" => "in progress", "on_hold" => "incomplete", "completed" => "completed", - "cancelled" => "cancelled" + "cancelled" => "cancelled", + "pending" => "pending" }[task_status] end @@ -399,6 +750,10 @@ def readable_decision_date format_date_string(decision_date) end + def readable_new_decision_date + format_date_string(new_decision_date) + end + def readable_disposition_date format_date_string(disposition_date) end @@ -409,10 +764,12 @@ def readable_facility_name [Constants::BGS_FACILITY_CODES[user_facility], " (", user_facility, ")"].join end + # rubocop:disable Metrics/MethodLength def readable_event_type { in_progress: "Claim status - In progress", incomplete: "Claim status - Incomplete", + pending: "Claim status - Pending", completed: "Claim closed", claim_creation: "Claim created", completed_disposition: "Completed disposition", @@ -421,9 +778,18 @@ def readable_event_type withdrew_issue: "Withdrew issue", removed_issue: "Removed issue", added_decision_date: "Added decision date", - cancelled: "Claim closed" + cancelled: "Claim closed", + addition: "Requested issue addition", + removal: "Requested issue removal", + modification: "Requested issue modification", + withdrawal: "Requested issue withdrawal", + request_approved: "Approval of request - issue #{request_type}", + request_denied: "Rejection of request - issue #{request_type}", + request_cancelled: "Cancellation of request", + request_edited: "Edit of request - issue #{request_type}" }[event_type] end + # rubocop:enable Metrics/MethodLength def issue_event? ISSUE_EVENTS.include?(event_type) @@ -441,6 +807,10 @@ def status_event? STATUS_EVENTS.include?(event_type) end + def event_has_modification_request? + REQUEST_ISSUE_MODIFICATION_EVENTS.include?(event_type) + end + private def set_attributes_from_change_history_data(new_event_type, change_data) @@ -452,23 +822,28 @@ def set_attributes_from_change_history_data(new_event_type, change_data) parse_task_attributes(change_data) parse_issue_attributes(change_data) parse_disposition_attributes(change_data) + parse_request_issue_modification_attributes(change_data) end def parse_task_attributes(change_data) @task_id = change_data["task_id"] - @task_status = change_data["task_status"] + @task_status = derive_task_status(change_data) @claim_type = change_data["type_classifier"] @assigned_at = change_data["assigned_at"] @days_waiting = change_data["days_waiting"] end + def derive_task_status(change_data) + change_data["is_assigned_present"] ? "pending" : change_data["task_status"] + end + def parse_intake_attributes(change_data) @intake_completed_date = change_data["intake_completed_at"] @veteran_file_number = change_data["veteran_file_number"] end def parse_issue_attributes(change_data) - if issue_event? + if issue_event? || event_has_modification_request? @issue_type = change_data["nonrating_issue_category"] @issue_description = change_data["nonrating_issue_description"] || change_data["unidentified_issue_text"] @decision_date = change_data["decision_date"] @@ -492,6 +867,52 @@ def parse_event_attributes(change_data) @event_user_css_id = change_data["event_user_css_id"] end + def parse_request_issue_modification_attributes(change_data) + if event_has_modification_request? + @request_type = change_data["request_type"] + @new_issue_type = change_data["requested_issue_type"] + @new_issue_description = change_data["requested_issue_description"] + @new_decision_date = change_data["requested_decision_date"] + @modification_request_reason = change_data["modification_request_reason"] + @decision_reason = change_data["decision_reason"] + @decided_at_date = change_data["decided_at"] + @issue_modification_request_withdrawal_date = change_data["issue_modification_request_withdrawal_date"] + @remove_original_issue = change_data["remove_original_issue"] + @issue_modification_request_status = change_data["issue_modification_request_status"] + @requestor = change_data["requestor"] + @decider = change_data["decider"] + parse_previous_issue_modification_attributes(change_data) + end + end + + def parse_previous_issue_modification_attributes(change_data) + @previous_issue_type = derive_previous_issue_type(change_data) + @previous_decision_date = derive_previous_decision_date(change_data) + @previous_modification_request_reason = derive_previous_modification_request_reason(change_data) + @previous_issue_description = derive_previous_issue_description(change_data) + @previous_withdrawal_date = derive_previous_withdrawal_date(change_data) + end + + def derive_previous_issue_type(change_data) + change_data["previous_issue_type"] || change_data["requested_issue_type"] + end + + def derive_previous_decision_date(change_data) + change_data["previous_decision_date"] || change_data["requested_decision_date"] + end + + def derive_previous_issue_description(change_data) + change_data["previous_issue_description"] || change_data["requested_issue_description"] + end + + def derive_previous_modification_request_reason(change_data) + change_data["previous_modification_request_reason"] || change_data["modification_request_reason"] + end + + def derive_previous_withdrawal_date(change_data) + change_data["previous_withdrawal_date"] || change_data["issue_modification_request_withdrawal_date"] + end + ############ CSV and Serializer Helpers ############ def abbreviated_user_name(name_string) @@ -500,11 +921,19 @@ def abbreviated_user_name(name_string) end def issue_information - if issue_event? + if issue_event? || event_has_modification_request? [issue_type, issue_description, readable_decision_date] end end + def issue_modification_request_information + if event_has_modification_request? + [new_issue_type, new_issue_description, readable_new_decision_date, modification_request_reason, decision_reason] + else + [nil, nil, nil, nil, nil] + end + end + def disposition_information if disposition_event? [disposition, decision_description, readable_disposition_date] @@ -525,7 +954,8 @@ def status_description incomplete: "Claim cannot be processed until decision date is entered.", completed: "Claim closed.", claim_creation: "Claim created.", - cancelled: "Claim closed." + cancelled: "Claim cancelled.", + pending: "Claim cannot be processed until VHA admin reviews pending requests." }[event_type] end diff --git a/app/services/claim_change_history/claim_history_service.rb b/app/services/claim_change_history/claim_history_service.rb index c778ae4f01b..5ed8065f002 100644 --- a/app/services/claim_change_history/claim_history_service.rb +++ b/app/services/claim_change_history/claim_history_service.rb @@ -4,7 +4,9 @@ class ClaimHistoryService attr_reader :business_line, :processed_task_ids, :processed_request_issue_ids, :processed_request_issue_update_ids, - :processed_decision_issue_ids, :events, :filters + :processed_decision_issue_ids, :processed_issue_modification_request_ids, + :processed_issue_modification_task_ids, + :events, :filters attr_writer :filters TIMING_RANGES = %w[ @@ -23,23 +25,23 @@ def initialize(business_line = VhaBusinessLine.singleton, filters = {}) @processed_request_issue_update_ids = Set.new @processed_decision_issue_ids = Set.new @processed_request_issue_ids = Set.new + @processed_issue_modification_request_ids = Set.new + @processed_issue_modification_task_ids = Set.new @events = [] end def build_events # Reset the instance attributes from the last time build_events was ran reset_processing_attributes - all_data = business_line.change_history_rows(@filters) - all_data.entries.each do |change_data| process_request_issue_update_events(change_data) process_request_issue_events(change_data) process_decision_issue_and_task_events(change_data) + process_request_issue_modification_events(change_data) # Don't process task events outside of decision issues unless there are no decision issues process_task_events(change_data) unless change_data["task_status"] == "completed" end - # Compact and sort in place to reduce garbage collection @events.compact! @events.sort_by! do |event| @@ -64,6 +66,8 @@ def reset_processing_attributes @processed_request_issue_update_ids.clear @processed_decision_issue_ids.clear @processed_request_issue_ids.clear + @processed_issue_modification_request_ids.clear + @processed_issue_modification_task_ids.clear @events.clear end @@ -98,7 +102,9 @@ def matches_filter(new_events) def process_event_filter(new_events) return new_events if @filters[:events].blank? - new_events.select { |event| event && ensure_array(@filters[:events]).include?(event.event_type) } + new_events.select do |event| + event && ensure_array(@filters[:events]).include?(event.event_type) + end end def process_issue_type_filter(new_events) @@ -205,7 +211,6 @@ def process_request_issue_update_events(change_data) def process_task_events(change_data) task_id = change_data["task_id"] - if task_id && !@processed_task_ids.include?(task_id) @processed_task_ids.add(task_id) save_events(ClaimHistoryEvent.create_claim_creation_event(change_data)) @@ -235,6 +240,39 @@ def process_decision_issue_and_task_events(change_data) end end + def process_request_issue_modification_events(change_data) + issue_modification_request_id = change_data["issue_modification_request_id"] + + return unless issue_modification_request_id + + is_processed = @processed_issue_modification_request_ids.include?(issue_modification_request_id) + + if issue_modification_request_id && !is_processed + @processed_issue_modification_request_ids.add(issue_modification_request_id) + save_events(ClaimHistoryEvent.create_issue_modification_request_event(change_data)) + save_events(ClaimHistoryEvent.create_edited_request_issue_events(change_data)) + end + # if imr version doesn't have status it means, imr has been made but no action yet taken from admin + process_pending_status_event(change_data) if !is_processed + end + + def process_pending_status_event(change_data) + task_id = change_data["task_id"] + + # processed_issue_modification_task_id stores all the task id that has already + # been processed so that it prevents the duplicate entry of pending event based on change_data. + if task_id && + !@processed_issue_modification_task_ids.include?(task_id) && + change_data["issue_modification_request_status"] == "assigned" + @processed_issue_modification_task_ids.add(task_id) + + save_events( + ClaimHistoryEvent.create_pending_status_event(change_data, + change_data["issue_modification_request_created_at"]) + ) + end + end + def ensure_array(variable) variable.is_a?(Array) ? variable : [variable] end diff --git a/client/app/nonComp/components/IndividualClaimHistoryTable.jsx b/client/app/nonComp/components/IndividualClaimHistoryTable.jsx index a42bb20078b..78b766a8e4d 100644 --- a/client/app/nonComp/components/IndividualClaimHistoryTable.jsx +++ b/client/app/nonComp/components/IndividualClaimHistoryTable.jsx @@ -1,8 +1,12 @@ -import React from 'react'; +/* eslint-disable max-lines */ +import React, { useState } from 'react'; import QueueTable from '../../queue/QueueTable'; import BENEFIT_TYPES from 'constants/BENEFIT_TYPES'; import { formatDateStr } from 'app/util/DateUtil'; import PropTypes from 'prop-types'; +import StringUtil from 'app/util/StringUtil'; + +const { capitalizeFirst } = StringUtil; const IndividualClaimHistoryTable = (props) => { @@ -34,25 +38,153 @@ const IndividualClaimHistoryTable = (props) => { return Claim can be processed.; }; + const ClaimPendingFragment = () => { + return Claim cannot be processed until VHA admin reviews pending requests.; + }; + const ClaimIncompleteFragment = () => { return Claim cannot be processed until decision date is entered.; }; + const benefitType = (details) => { + return <> + Benefit type: {BENEFIT_TYPES[details.benefitType]}
+ ; + }; + const ClaimClosedFragment = (details) => { - return - Claim closed.
+ const fragment = details.eventType === 'cancelled' ? <> + Claim cancelled. + : <> + Claim closed.
Claim decision date: {formatDecisionDate(details.dispositionDate)} -
; + ; + + return ( +
+ {fragment} +
+ ); }; const AddedIssueFragment = (details) => { return - Benefit type: {BENEFIT_TYPES[details.benefitType]}
+ { benefitType(details) } Issue type: {details.issueType}
Issue description: {details.issueDescription}
; }; + const RequestedIssueFragment = (details) => { + return + { benefitType(details) } + Issue type: {details.newIssueType}
+ Issue description: {details.newIssueDescription}
+ Decision date: {formatDecisionDate(details.newDecisionDate)}
+ {capitalizeFirst(details.requestType)} request reason: {details.modificationRequestReason}
+
; + }; + + const WithdrawalRequestedIssueFragment = (details) => { + return <> + + Withdrawal request date: {formatDecisionDate(details.issueModificationRequestWithdrawalDate)}
+ ; + }; + + const formatLabel = (baseLabel, prefix) => { + if (prefix) { + return `${prefix} ${baseLabel.toLowerCase()}`; + } + + return baseLabel; + }; + + const previousModificationFragment = (details, prefix) => { + return + {formatLabel('Issue type:', prefix)} {details.previousIssueType}
+ {formatLabel('Issue description:', prefix)} {details.previousIssueDescription}
+ {formatLabel('Decision date:', prefix)} {formatDecisionDate(details.previousDecisionDate)}
+ {capitalizeFirst(details.requestType)} request reason: {details.previousModificationRequestReason}
+
; + }; + + const RequestedIssueModificationFragment = (details) => { + return + { benefitType(details) } + Current issue type: {details.issueType}
+ Current issue description: {details.issueDescription}
+ Current decision date: {formatDecisionDate(details.decisionDate)}
+ { previousModificationFragment(details, 'New') } +
; + }; + + const RemoveOriginalIssueFragment = (details) => { + return + Remove original issue: {details.removeOriginalIssue ? 'Yes' : 'No' }
+
; + }; + + const requestDecision = (details) => { + return + Request decision: {details.issueModificationRequestStatus === 'denied' ? 'Rejected' : 'Approved'}
+
; + }; + + const RequestedIssueDecisionFragment = (details) => { + return + {requestDecision(details)} + { details.issueModificationRequestStatus === 'approved' && details.requestType === 'modification' ? + : null + } + { details.issueModificationRequestStatus === 'denied' ? + + Reason for rejection: {details.decisionReason}
+
: null + } + Request originated by: {details.requestor}
+
; + }; + + const modificationRequestReason = (details) => { + return + New {details.requestType} request reason: {details.modificationRequestReason}
+
; + }; + + const EditOfRequestIssueModification = (details) => { + let component = null; + + switch (details.requestType) { + case 'modification': + case 'addition': + component = + New issue type: {details.newIssueType}
+ New issue description: {details.newIssueDescription}
+ New decision date: {formatDecisionDate(details.newDecisionDate)}
+ {modificationRequestReason(details)} +
; + break; + case 'removal': + component = + {modificationRequestReason(details)} + ; + break; + case 'withdrawal': + component = + {modificationRequestReason(details)} + New withdrawal request date: {formatDecisionDate(details.issueModificationRequestWithdrawalDate)}
+
; + break; + default: + return null; + } + + return + {component} + ; + }; + const AddedIssueWithDateFragment = (details) => { return @@ -93,12 +225,74 @@ const IndividualClaimHistoryTable = (props) => { ; }; - const DetailsFragment = (row) => { + const WithdrawnRequestedIssueModificationFragment = (details) => { + return + + Withdrawal request date: {formatDecisionDate(details.previousWithdrawalDate)}
+
; + }; + + const PreviousFragmentWithBenefitType = (details) => { + return + { benefitType(details) } + { previousModificationFragment(details) } + ; + }; + + const OriginalRequestedIssueModificationFragment = (details) => { let component = null; - const { readableEventType, details } = row; + switch (details.requestType) { + case 'modification': + component = ; + break; + case 'addition': + case 'removal': + component = ; + break; + case 'withdrawal': + component = ; + break; + default: + return null; + } + + return ( +
+ {component} +
+ ); + }; + + const OriginalDetailsFragments = (row) => { + const { details, modificationRequestDetails } = row; + const requestModificationDetails = { ...details, ...modificationRequestDetails }; - const detailsExtended = { ...details, eventDate: row.eventDate }; + const [isOpen, setIsOpen] = useState(false); + + const toggle = () => { + setIsOpen(!isOpen); + }; + + return ( +
+ + {isOpen && +
+ +
} +
+ ); + }; + + const DetailsFragment = (row) => { + + let component = null; + const { readableEventType, details, modificationRequestDetails } = row; + const detailsExtended = { ...details, eventDate: row.eventDate, eventType: row.eventType }; + const requestIssueModificationDetails = { ...modificationRequestDetails, ...detailsExtended }; switch (readableEventType) { case 'Claim created': @@ -110,6 +304,9 @@ const IndividualClaimHistoryTable = (props) => { case 'Completed disposition': component = ; break; + case 'Claim status - Pending': + component = ; + break; case 'Claim status - In progress': component = ; break; @@ -131,13 +328,42 @@ const IndividualClaimHistoryTable = (props) => { case 'Removed issue': component = ; break; + case 'Cancellation of request': + component = ; + break; + case 'Requested issue modification': + component = ; + break; + case 'Requested issue addition': + case 'Requested issue removal': + component = ; + break; + case 'Requested issue withdrawal': + component = ; + break; + case `Edit of request - issue ${requestIssueModificationDetails.requestType}`: + component = ; + break; + case `Rejection of request - issue ${requestIssueModificationDetails.requestType}`: + case `Approval of request - issue ${requestIssueModificationDetails.requestType}`: + component = ; + break; default: return null; } - return

- {component} -

; + const chunk = [ + 'request_approved', + 'request_edited', + 'request_denied' + ]; + + return ( +
+

{component}

+ { chunk.includes(requestIssueModificationDetails.eventType) ? : null } +
+ ); }; const dateSort = (row) => { @@ -210,3 +436,4 @@ IndividualClaimHistoryTable.propTypes = { }; export default IndividualClaimHistoryTable; +/* eslint-enable max-lines */ diff --git a/client/app/nonComp/pages/ReportPage.jsx b/client/app/nonComp/pages/ReportPage.jsx index 84417cc4335..d8753e6fa53 100644 --- a/client/app/nonComp/pages/ReportPage.jsx +++ b/client/app/nonComp/pages/ReportPage.jsx @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ import React, { useEffect } from 'react'; import { useController, useForm, FormProvider, useFormContext } from 'react-hook-form'; import { useDispatch, useSelector } from 'react-redux'; @@ -5,6 +6,7 @@ import { downloadReportCSV } from 'app/nonComp/actions/changeHistorySlice'; import { css } from 'glamor'; import PropTypes from 'prop-types'; +import Alert from 'app/components/Alert'; import Button from 'app/components/Button'; import NonCompLayout from '../components/NonCompLayout'; import { conditionsSchema, ReportPageConditions } from '../components/ReportPage/ReportPageConditions'; @@ -27,7 +29,7 @@ import { RADIO_STATUS_OPTIONS, RADIO_STATUS_REPORT_TYPE_OPTIONS, SPECIFIC_STATUS_OPTIONS, - SPECTIFIC_EVENT_OPTIONS + SPECIFIC_EVENT_OPTIONS } from 'constants/REPORT_TYPE_CONSTANTS'; import * as ERRORS from 'constants/REPORT_PAGE_VALIDATION_ERRORS'; @@ -42,6 +44,13 @@ const buttonOuterContainerStyling = css({ marginTop: '4rem', }); +const outerContainerStyling = css({ + display: 'flex', + justifyContent: 'space-between', + paddingLeft: '30px', + gap: '3em', +}); + const specificEventTypeSchema = yup.lazy((value) => { // eslint-disable-next-line no-undefined if (value === undefined) { @@ -52,13 +61,22 @@ const specificEventTypeSchema = yup.lazy((value) => { added_decision_date: yup.boolean(), added_issue: yup.boolean(), added_issue_no_decision_date: yup.boolean(), + removed_issue: yup.boolean(), + withdrew_issue: yup.boolean(), + completed_disposition: yup.boolean(), claim_created: yup.boolean(), claim_closed: yup.boolean(), claim_status_incomplete: yup.boolean(), + claim_status_pending: yup.boolean(), claim_status_inprogress: yup.boolean(), - completed_disposition: yup.boolean(), - removed_issue: yup.boolean(), - withdrew_issue: yup.boolean(), + requested_issue_modification: yup.boolean(), + requested_issue_addition: yup.boolean(), + requested_issue_removal: yup.boolean(), + requested_issue_withdrawal: yup.boolean(), + approval_of_request: yup.boolean(), + rejection_of_request: yup.boolean(), + cancellation_of_request: yup.boolean(), + edit_of_request: yup.boolean(), }).test('at-least-one-true', ERRORS.AT_LEAST_ONE_OPTION, (obj) => { return Object.values(obj).some((val) => val === true); }); @@ -130,28 +148,11 @@ const ReportPageButtons = ({ ); }; -const RHFCheckboxGroup = ({ options, name, control }) => { - const { field } = useController({ - control, - name - }); - - const { errors } = useFormContext(); - - const [value, setValue] = React.useState({}); - - let fieldClasses = 'checkbox'; - - const errorMessage = get(errors, name)?.message; - - if (errorMessage) { - fieldClasses += ' usa-input-error'; - fieldClasses += ' less-error-padding'; - } +const EventCheckboxGroup = ({ header, options, name, onChange }) => { return ( -
- {errorMessage ?
{ errorMessage }
: null} +
+ { header &&

{header}

} {options.map((option) => (
{ key={`${name}.${option.id}`} label={option.label} stronglabel - onChange={(val) => { - value[option.id] = val; - field.onChange(value); - setValue(value); - }} unpadded + onChange={(val) => onChange({ option, val })} />
))} -
+ + ); +}; + +const RHFCheckboxGroup = ({ options, name, control }) => { + const { errors } = useFormContext(); + + let fieldClasses = 'checkbox'; + const errorMessage = get(errors, name)?.message; + + const { field } = useController({ + control, + name + }); + + if (errorMessage) { + fieldClasses += ' usa-input-error'; + fieldClasses += ' less-error-padding'; + } + + const [value, setValue] = React.useState({}); + + const onCheckboxClick = ({ option, val }) => { + value[option.id] = val; + field.onChange(value); + setValue(value); + }; + + const messageStyling = css({ + fontSize: '17px !important', + paddingTop: '2px !important' + }); + + return ( +
+ {name === 'specificEventType' ? +
+ {errorMessage && + + } +
+ + + +
+
: +
+ {errorMessage ?
{ errorMessage }
: null} + +
+ } +
); }; @@ -210,20 +281,30 @@ const ReportPage = ({ history }) => { specificStatus: { incomplete: '', in_progress: '', + pending: '', completed: '', cancelled: '' }, specificEventType: { - added_decision_date: '', - added_issue: '', - added_issue_no_decision_date: '', claim_created: '', claim_closed: '', claim_status_incomplete: '', + claim_status_pending: '', claim_status_inprogress: '', - completed_disposition: '', + added_decision_date: '', + added_issue: '', + added_issue_no_decision_date: '', removed_issue: '', withdrew_issue: '', + completed_disposition: '', + requested_issue_modification: '', + requested_issue_addition: '', + requested_issue_removal: '', + requested_issue_withdrawal: '', + approval_of_request: '', + rejection_of_request: '', + cancellation_of_request: '', + edit_of_request: '', } }; @@ -372,7 +453,7 @@ const ReportPage = ({ history }) => { {watchReportType === 'event_type_action' && watchRadioEventAction === 'specific_events_action' ? ( @@ -409,6 +490,13 @@ RHFCheckboxGroup.propTypes = { errorMessage: PropTypes.string }; +EventCheckboxGroup.propTypes = { + options: PropTypes.array, + onChange: PropTypes.func, + header: PropTypes.string, + name: PropTypes.string +}; + RHFRadioButton.propTypes = { options: PropTypes.array, control: PropTypes.object, @@ -417,3 +505,4 @@ RHFRadioButton.propTypes = { }; export default ReportPage; +/* eslint-enable max-lines */ diff --git a/client/constants/REPORT_PAGE_VALIDATION_ERRORS.json b/client/constants/REPORT_PAGE_VALIDATION_ERRORS.json index 6757f9945d4..17ec7ce7621 100644 --- a/client/constants/REPORT_PAGE_VALIDATION_ERRORS.json +++ b/client/constants/REPORT_PAGE_VALIDATION_ERRORS.json @@ -8,5 +8,6 @@ "END_DATE_SMALL": "End date must be greater than the start date", "DATE_FUTURE": "Date cannot be in the future", "AT_LEAST_ONE_OPTION": "Please select at least one option", + "AT_LEAST_ONE_CHECKBOX_OPTION": "Please select at least one checkbox from one of the sections below.", "MISSING_PERSONNEL": "Please select at least one team member" } diff --git a/client/constants/REPORT_TYPE_CONSTANTS.json b/client/constants/REPORT_TYPE_CONSTANTS.json index f84e99bb1c2..739bf7caf22 100644 --- a/client/constants/REPORT_TYPE_CONSTANTS.json +++ b/client/constants/REPORT_TYPE_CONSTANTS.json @@ -46,6 +46,10 @@ "label": "Incomplete", "id": "incomplete" }, + { + "label": "Pending", + "id": "pending" + }, { "label": "In Progress", "id": "in_progress" @@ -59,46 +63,90 @@ "id": "cancelled" } ], - "SPECTIFIC_EVENT_OPTIONS": [ - { - "label": "Added decision date", - "id": "added_decision_date" - }, - { - "label": "Added issue", - "id": "added_issue" - }, - { - "label": "Added issue - No decision date", - "id":"added_issue_no_decision_date" - }, - { - "label": "Claim created", - "id":"claim_created" - }, - { - "label": "Claim closed", - "id":"claim_closed" - }, - { - "label": "Claim status - Incomplete", - "id":"claim_status_incomplete" - }, - { - "label": "Claim status - In progress", - "id":"claim_status_inprogress" - }, - { - "label": "Completed disposition", - "id":"completed_disposition" - }, - { - "label": "Removed issue", - "id":"removed_issue" - }, - { - "label": "Withdrew issue", - "id":"withdrew_issue" + "SPECIFIC_EVENT_OPTIONS": [ + { + "system": [ + { + "label": "Claim created", + "id":"claim_created" + }, + { + "label": "Claim closed", + "id":"claim_closed" + }, + { + "label": "Claim status - Incomplete", + "id":"claim_status_incomplete" + }, + { + "label": "Claim status - Pending", + "id":"claim_status_pending" + }, + { + "label": "Claim status - In progress", + "id":"claim_status_inprogress" + } + ], + "general": [ + { + "label": "Added decision date", + "id": "added_decision_date" + }, + { + "label": "Added issue", + "id": "added_issue" + }, + { + "label": "Added issue - No decision date", + "id":"added_issue_no_decision_date" + }, + { + "label": "Removed issue", + "id":"removed_issue" + }, + { + "label": "Withdrew issue", + "id":"withdrew_issue" + }, + { + "label": "Completed disposition", + "id":"completed_disposition" + } + ], + "requests": [ + { + "label": "Requested issue modification", + "id":"requested_issue_modification" + }, + { + "label": "Requested issue addition", + "id":"requested_issue_addition" + }, + { + "label": "Requested issue removal", + "id":"requested_issue_removal" + }, + { + "label": "Requested issue withdrawal", + "id":"requested_issue_withdrawal" + }, + { + "label": "Approval of request", + "id":"approval_of_request" + }, + { + "label": "Rejection of request", + "id":"rejection_of_request" + }, + { + "label": "Cancellation of request", + "id":"cancellation_of_request" + }, + { + "label": "Edit of request", + "id":"edit_of_request" + } + ] } ], "TIMING_SPECIFIC_OPTIONS": [ diff --git a/client/test/app/nonComp/pages/ReportPage.test.js b/client/test/app/nonComp/pages/ReportPage.test.js index 2df071c4a73..80bf888ccaf 100644 --- a/client/test/app/nonComp/pages/ReportPage.test.js +++ b/client/test/app/nonComp/pages/ReportPage.test.js @@ -13,6 +13,7 @@ import { getVhaUsers } from 'test/helpers/reportPageHelper'; import CombinedNonCompReducer from 'app/nonComp/reducers'; import REPORT_TYPE_CONSTANTS from 'constants/REPORT_TYPE_CONSTANTS'; +import * as ERRORS from 'constants/REPORT_PAGE_VALIDATION_ERRORS'; describe('ReportPage', () => { const setup = (storeValues = {}) => { @@ -381,7 +382,7 @@ describe('ReportPage', () => { }); - it('should add 10 checkboxes when radio Specific Events/ Actions is clicked', async () => { + it('should add 19 checkboxes when radio Specific Events/ Actions is clicked', async () => { setup(); await selectEvent.select(screen.getByLabelText('Report Type'), ['Status', 'Event / Action']); @@ -392,9 +393,17 @@ describe('ReportPage', () => { expect(specificEvents.length).toBe(1); fireEvent.click(screen.getByLabelText('Specific Events / Actions')); - expect(screen.getAllByRole('checkbox').length).toBe(10); + expect(screen.getAllByRole('checkbox').length).toBe(19); - REPORT_TYPE_CONSTANTS.SPECTIFIC_EVENT_OPTIONS.forEach((option) => { + REPORT_TYPE_CONSTANTS.SPECIFIC_EVENT_OPTIONS[0].system.forEach((option) => { + expect(screen.getAllByText(option.label)).toBeTruthy(); + }); + + REPORT_TYPE_CONSTANTS.SPECIFIC_EVENT_OPTIONS[0].general.forEach((option) => { + expect(screen.getAllByText(option.label)).toBeTruthy(); + }); + + REPORT_TYPE_CONSTANTS.SPECIFIC_EVENT_OPTIONS[0].requests.forEach((option) => { expect(screen.getAllByText(option.label)).toBeTruthy(); }); }); @@ -413,14 +422,14 @@ describe('ReportPage', () => { fireEvent.click(screen.getByLabelText('Specific Events / Actions')); - expect(screen.getAllByRole('checkbox').length).toBe(10); + expect(screen.getAllByRole('checkbox').length).toBe(19); const generateTaskReport = screen.getByRole('button', { name: /Generate task report/i }); await userEvent.click(generateTaskReport); await waitFor(() => { - expect(screen.getAllByText('Please select at least one option').length).toBe(1); + expect(screen.getAllByText(ERRORS.AT_LEAST_ONE_CHECKBOX_OPTION).length).toBe(1); }); }); @@ -436,7 +445,7 @@ describe('ReportPage', () => { expect(specificEvents.length).toBe(1); fireEvent.click(screen.getByLabelText('Specific Status')); - expect(screen.getAllByRole('checkbox').length).toBe(4); + expect(screen.getAllByRole('checkbox').length).toBe(5); REPORT_TYPE_CONSTANTS.SPECIFIC_STATUS_OPTIONS.map((option) => expect(screen.getAllByText(option.label)).toBeTruthy() diff --git a/client/test/app/nonComp/pages/__snapshots__/ClaimHistoryPage.test.js.snap b/client/test/app/nonComp/pages/__snapshots__/ClaimHistoryPage.test.js.snap index da3dc91ccc1..2c211e5097e 100644 --- a/client/test/app/nonComp/pages/__snapshots__/ClaimHistoryPage.test.js.snap +++ b/client/test/app/nonComp/pages/__snapshots__/ClaimHistoryPage.test.js.snap @@ -388,32 +388,34 @@ exports[`ClaimHistoryPage renders correctly for Admin user renders correctly 1`] role="gridcell" tabindex="-1" > -

- - Benefit type: - - Veterans Health Administration -
- - Issue type: - - Other -
- - Issue description: - - issue added after removing unidentified issues -
- - Decision date: - - 09/12/2023 -
- - Removed issue date: - - 02/23/2024 -

+
+

+ + Benefit type: + + Veterans Health Administration +
+ + Issue type: + + Other +
+ + Issue description: + + issue added after removing unidentified issues +
+ + Decision date: + + 09/12/2023 +
+ + Removed issue date: + + 02/23/2024 +

+
-

- - Benefit type: - - Veterans Health Administration -
- - Issue type: - - Beneficiary Travel -
- - Issue description: - - go fly -
- - Decision date: - - 02/23/2024 -
-

+
+

+ + Benefit type: + + Veterans Health Administration +
+ + Issue type: + + Beneficiary Travel +
+ + Issue description: + + go fly +
+ + Decision date: + + 02/23/2024 +
+

+
-

- - Benefit type: - - Veterans Health Administration -
- - Issue type: - -
- - Issue description: - - unidentified issue description -
- - Decision date: - - No decision date -
- - Removed issue date: - - 12/12/2023 -

+
+

+ + Benefit type: + + Veterans Health Administration +
+ + Issue type: + +
+ + Issue description: + + unidentified issue description +
+ + Decision date: + + No decision date +
+ + Removed issue date: + + 12/12/2023 +

+
-

- - Benefit type: - - Veterans Health Administration -
- - Issue type: - - Other -
- - Issue description: - - issue added after removing unidentified issues -
- - Decision date: - - 09/12/2023 -
-

+
+

+ + Benefit type: + + Veterans Health Administration +
+ + Issue type: + + Other +
+ + Issue description: + + issue added after removing unidentified issues +
+ + Decision date: + + 09/12/2023 +
+

+
-

- Claim can be processed. -

+
+

+ Claim can be processed. +

+
-

- - Benefit type: - - Veterans Health Administration -
- - Issue type: - -
- - Issue description: - - unidentified issue description -
- - Decision date: - - No decision date -
-

+
+

+ + Benefit type: + + Veterans Health Administration +
+ + Issue type: + +
+ + Issue description: + + unidentified issue description +
+ + Decision date: + + No decision date +
+

+
-

- Claim cannot be processed until decision date is entered. -

+
+

+ Claim cannot be processed until decision date is entered. +

+
-

- Claim created. -

+
+

+ Claim created. +

+
diff --git a/db/seeds/veterans_health_administration.rb b/db/seeds/veterans_health_administration.rb index f99c4192da9..c3161f82d72 100644 --- a/db/seeds/veterans_health_administration.rb +++ b/db/seeds/veterans_health_administration.rb @@ -63,6 +63,7 @@ def create_supplemental_claims def create_hlr_with_claimant(benefit_type, claimant_type) hlr = create( :higher_level_review, + :with_intake, :with_request_issue, :processed, benefit_type: benefit_type, @@ -75,6 +76,7 @@ def create_hlr_with_claimant(benefit_type, claimant_type) def create_sc_with_claimant(benefit_type, claimant_type) sc = create( :supplemental_claim, + :with_intake, :with_request_issue, :processed, benefit_type: benefit_type, diff --git a/spec/controllers/decision_reviews_controller_spec.rb b/spec/controllers/decision_reviews_controller_spec.rb index 2b511bac1a1..911bad2b8a4 100644 --- a/spec/controllers/decision_reviews_controller_spec.rb +++ b/spec/controllers/decision_reviews_controller_spec.rb @@ -880,7 +880,44 @@ end let!(:task_event) do - create(:higher_level_review_vha_task_with_decision) + create(:issue_modification_request, + :with_higher_level_review_with_decision, + :edit_of_request, + :update_decider, + :with_request_issue, + nonrating_issue_category: "Medical and Dental Care Reimbursement") + end + + let!(:issue_modification_request_modification) do + create(:issue_modification_request, + :with_request_issue, + request_type: "modification", + decision_review: task_event.decision_review) + end + + let!(:issue_modification_request_withdrawal) do + create(:issue_modification_request, + :with_request_issue, + :withdrawal, + decision_review: task_event.decision_review) + end + + let!(:issue_modification_request_removal) do + create(:issue_modification_request, + :with_request_issue, + request_type: "removal", + decision_review: task_event.decision_review) + end + let!(:issue_modification_request_cancel) do + create(:issue_modification_request, + :cancel_of_request, + decision_review: task_event.decision_review) + end + + let(:task_id) { task_event.decision_review.tasks.ids[0] } + + let!(:remand_task_event) do + create(:remand_vha_task) end let!(:remand_task_event) do @@ -888,7 +925,7 @@ end it "should return task details" do - get :history, params: { task_id: task_event.id, decision_review_business_line_slug: vha_org.url }, + get :history, params: { task_id: task_id, decision_review_business_line_slug: vha_org.url }, format: :json expect(response.status).to eq 200 @@ -896,11 +933,38 @@ res = JSON.parse(response.body) expected_events = [ - { "taskID" => task_event.id, "eventType" => "added_issue", "claimType" => "Higher-Level Review", + { "taskID" => task_id, "eventType" => "added_issue", "claimType" => "Higher-Level Review", "readableEventType" => "Added issue" }, { "eventType" => "claim_creation", "readableEventType" => "Claim created" }, { "eventType" => "in_progress", "readableEventType" => "Claim status - In progress" }, - { "eventType" => "completed_disposition", "readableEventType" => "Completed disposition" } + { "eventType" => "completed_disposition", "readableEventType" => "Completed disposition" }, + { "eventType" => "addition", "readableEventType" => "Requested issue addition" }, + { "eventType" => "pending", "readableEventType" => "Claim status - Pending" }, + { "eventType" => "request_approved", "readableEventType" => "Approval of request - issue addition" }, + { "eventType" => "request_edited", "readableEventType" => "Edit of request - issue addition" }, + { "eventType" => "modification", "readableEventType" => "Requested issue modification" }, + { "eventType" => "removal", "readableEventType" => "Requested issue removal" }, + { "eventType" => "request_cancelled", "readableEventType" => "Cancellation of request" }, + { "eventType" => "withdrawal", "readableEventType" => "Requested issue withdrawal" } + ] + + expected_events.each do |expected_attributes| + expect(res).to include( + a_hash_including("attributes" => a_hash_including(expected_attributes)) + ) + 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| diff --git a/spec/factories/appeal.rb b/spec/factories/appeal.rb index c73c2424981..e26ccc43b7c 100644 --- a/spec/factories/appeal.rb +++ b/spec/factories/appeal.rb @@ -686,7 +686,7 @@ create(:request_issue, benefit_type: "vha", nonrating_issue_category: "Caregiver | Other", - nonrating_issue_description: "VHA - Caregiver ", + nonrating_issue_description: "VHA - Caregiver", decision_review: appeal, decision_date: 1.month.ago) appeal.reload diff --git a/spec/factories/higher_level_review.rb b/spec/factories/higher_level_review.rb index 612fdafeab7..8880a175a42 100644 --- a/spec/factories/higher_level_review.rb +++ b/spec/factories/higher_level_review.rb @@ -167,7 +167,7 @@ create(:request_issue, benefit_type: "vha", nonrating_issue_category: "Caregiver | Other", - nonrating_issue_description: "VHA - Caregiver ", + nonrating_issue_description: "VHA - Caregiver", decision_review: higher_level_review, decision_date: 1.month.ago) diff --git a/spec/factories/issue_modification_request.rb b/spec/factories/issue_modification_request.rb index 17ea37cbafc..48fb4d8cb12 100644 --- a/spec/factories/issue_modification_request.rb +++ b/spec/factories/issue_modification_request.rb @@ -6,7 +6,8 @@ request_reason { Faker::Lorem.sentence } benefit_type { "vha" } - nonrating_issue_category {} + nonrating_issue_description { Faker::Lorem.sentence } + nonrating_issue_category { Constants::ISSUE_CATEGORIES["vha"].sample } status { "assigned" } decision_date { Time.zone.today - rand(0..29) } @@ -15,14 +16,36 @@ remove_original_issue { false } requestor_id { create(:user).id } + trait :withdrawal do + request_type { "withdrawal" } + withdrawal_date { Time.zone.today - rand(0..29) } + end + trait :update_decider do after(:create) do |imr, evaluator| - imr.status = evaluator.status || "approved" + imr.status = evaluator.status == "assigned" ? "approved" : evaluator.status imr.decider_id = create(:user).id imr.save! end end + trait :edit_of_request do + after(:create) do |imr, evaluator| + imr.request_reason = "I edited this request." + imr.nonrating_issue_category = evaluator.nonrating_issue_category || + Constants::ISSUE_CATEGORIES[evaluator.benefit_type].sample + imr.status = evaluator.status + imr.save! + end + end + + trait :cancel_of_request do + after(:create) do |imr| + imr.status = "cancelled" + imr.save! + end + end + trait :with_request_issue do request_issue do create(:request_issue, @@ -47,6 +70,7 @@ trait :with_supplemental_claim do decision_review do create(:supplemental_claim, + :with_intake, :with_vha_issue, :update_assigned_at, :processed, @@ -57,8 +81,21 @@ trait :with_higher_level_review do decision_review do create(:higher_level_review, + :with_intake, + :with_vha_issue, + :update_assigned_at, + :processed, + claimant_type: :veteran_claimant) + end + end + + trait :with_higher_level_review_with_decision do + decision_review do + create(:higher_level_review, + :with_intake, :with_vha_issue, :update_assigned_at, + :with_decision, :processed, claimant_type: :veteran_claimant) end diff --git a/spec/feature/non_comp/individual_claim_history_spec.rb b/spec/feature/non_comp/individual_claim_history_spec.rb index f7d5a141752..a5d01b3b835 100644 --- a/spec/feature/non_comp/individual_claim_history_spec.rb +++ b/spec/feature/non_comp/individual_claim_history_spec.rb @@ -10,27 +10,885 @@ let(:non_comp_org) { VhaBusinessLine.singleton } let(:user) { create(:default_user, css_id: "REPORT USER", full_name: "Report User") } let(:veteran) { create(:veteran) } - let!(:task) { create(:higher_level_review_vha_task_with_decision) } - let(:task_history_url) { "/decision_reviews/vha/tasks/#{task.id}/history" } - let(:events) { ClaimHistoryService.new(non_comp_org, task_id: task.id).build_events } before do User.stub = user non_comp_org.add_user(user) OrganizationsUser.make_user_admin(user, non_comp_org) - visit task_history_url end - scenario "display the claim history table" do - number_of_events = events.length + def click_filter_option(filter_text) + sort = find("[aria-label='Filter by Activity']") + sort.click - expect(page).to have_text("Viewing 1-#{number_of_events} of #{number_of_events} total") + dropdown_filter = page.find(class: "cf-dropdown-filter") + dropdown_filter.find("label", text: filter_text, match: :prefer_exact).click + end + + def clear_filter_option(filter_text) + sort = find("[aria-label='Filter by Activity. Filtering by #{filter_text}']") + sort.click + + clear_button_filter = page.find(class: "cf-clear-filter-button-wrapper") + clear_button_filter.click + end + + describe "Check for event hitsoty activity dynamic labels" do + let!(:task_event) do + create(:issue_modification_request, + :with_higher_level_review_with_decision, + nonrating_issue_category: "Medical and Dental Care Reimbursement") + end + + # Edited then approved + let!(:issue_modification_request_modification_edit) do + request = create(:issue_modification_request, + :with_request_issue, + :edit_of_request, + request_type: "modification", + decision_review: task_event.decision_review) + request.update(status: "approved") + request + end + + let!(:issue_modification_request_withdrawal_edit) do + request = create(:issue_modification_request, + :with_request_issue, + :edit_of_request, + :withdrawal, + decision_review: task_event.decision_review) + request.update(status: "approved") + request + end + + let!(:issue_modification_request_addition_edit) do + request = create(:issue_modification_request, + :edit_of_request, + decision_review: task_event.decision_review) + request.update(status: "approved") + request + end + + let!(:issue_modification_request_removal_edit) do + request = create(:issue_modification_request, + :with_request_issue, + :edit_of_request, + request_type: "removal", + decision_review: task_event.decision_review) + request.update(status: "approved") + request + end + + # cancelled + let!(:cancelled_issue_modification_request_modification) do + create(:issue_modification_request, + :with_request_issue, + :cancel_of_request, + request_type: "modification", + decision_review: task_event.decision_review) + end + + let!(:cancelled_issue_modification_request_withdrawal) do + create(:issue_modification_request, + :with_request_issue, + :cancel_of_request, + :withdrawal, + decision_review: task_event.decision_review) + end + + let!(:cancelled_issue_modification_request_addition) do + create(:issue_modification_request, + :cancel_of_request, + decision_review: task_event.decision_review) + end + + let!(:cancelled_issue_modification_request_removal) do + create(:issue_modification_request, + :with_request_issue, + :cancel_of_request, + request_type: "removal", + decision_review: task_event.decision_review) + end + + # Rejected aka Denied + let!(:denied_issue_modification_request_modification) do + request = create(:issue_modification_request, + :with_request_issue, + request_type: "modification", + decision_review: task_event.decision_review) + + request.update( + status: "denied", + decision_reason: "Decision for denial" + ) + request + end + + let!(:denied_issue_modification_request_withdrawal) do + request = create(:issue_modification_request, + :with_request_issue, + :withdrawal, + decision_review: task_event.decision_review) + + request.update( + status: "denied", + decision_reason: "Decision for denial" + ) + request + end + + let!(:denied_issue_modification_request_addition) do + request = create(:issue_modification_request, + decision_review: task_event.decision_review) + + request.update( + status: "denied", + decision_reason: "Decision for denial" + ) + request + end + + let!(:denied_issue_modification_request_removal) do + request = create(:issue_modification_request, + :with_request_issue, + request_type: "removal", + decision_review: task_event.decision_review) + + request.update( + status: "denied", + decision_reason: "Decision for denial" + ) + request + end + + let!(:claim_closed) do + create(:higher_level_review_vha_task_with_decision) + end + + let(:task_id) { task_event.decision_review.tasks.ids[0] } + + let(:task_history_url) { "/decision_reviews/vha/tasks/#{task_id}/history" } + let(:events) { ClaimHistoryService.new(non_comp_org, task_id: task_id).build_events } + + before do + visit task_history_url + end + + let(:event_types) { events.map(&:readable_event_type).uniq.sort! } + + context "Testing all Event Types" do + it "Filtering each event and verifiying row attributes" do + step "display the claim history table count" do + expect(page).to have_text("Viewing 1-15 of #{events.length} total") + + click_filter_option("Approval of request - issue addition (1)") + expect(event_types.include?("Approval of request - issue addition")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + + table_row = table.first('tr[id^="table-row"]') + expect(table_row).to have_content("Approval of request - issue addition") + expect(table_row).to have_content("Request decision:") + expect(table_row).to have_content("Request originated by:") + + expect(table_row).to have_content("View original request") + table_row.first("a", text: "View original request").click + + expect(table_row).to have_content("Hide original request") + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Issue type:") + expect(table_row).to have_content("Issue description:") + expect(table_row).to have_content("Decision date:") + expect(table_row).to have_content("Addition request reason:") + clear_filter_option("Approval of request - issue addition") + end + + step "Checking Approval of request - issue modification" do + click_filter_option("Approval of request - issue modification (1)") + expect(event_types.include?("Approval of request - issue modification")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + + table_row = table.first('tr[id^="table-row"]') + expect(table_row).to have_content("Approval of request - issue modification") + expect(table_row).to have_content("Request decision:") + expect(table_row).to have_content("Remove original issue:") + expect(table_row).to have_content("Request originated by") + expect(table_row).to have_content("View original request") + + first("a", text: "View original request").click + expect(table_row).to have_content("Hide original request") + + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Current issue type:") + expect(table_row).to have_content("Current issue description:") + expect(table_row).to have_content("Current decision date:") + expect(table_row).to have_content("New issue type:") + expect(table_row).to have_content("New issue description:") + expect(table_row).to have_content("New decision date:") + expect(table_row).to have_content("Modification request reason:") + clear_filter_option("Approval of request - issue modification") + end + + step "Checking Approval of request - issue removal" do + click_filter_option("Approval of request - issue removal (1)") + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + expect(event_types.include?("Approval of request - issue removal")).to be_truthy + + table_row = table.first('tr[id^="table-row"]') + expect(table_row).to have_content("Approval of request - issue removal") + expect(table_row).to have_content("Request decision:") + expect(table_row).to have_content("Request originated by") + expect(table_row).to have_content("View original request") + + expect(table_row).to have_content("View original request") + first("a", text: "View original request").click + expect(table_row).to have_content("Hide original request") + + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Issue type:") + expect(table_row).to have_content("Issue description:") + expect(table_row).to have_content("Decision date:") + expect(table_row).to have_content("Removal request reason:") + clear_filter_option("Approval of request - issue removal") + end + + step "Checking Approval of request - issue withdrawal" do + click_filter_option("Approval of request - issue withdrawal (1)") + expect(event_types.include?("Approval of request - issue withdrawal")).to be_truthy + + table = page.find("tbody") + table_row = table.first('tr[id^="table-row"]') + expect(table_row).to have_content("Approval of request - issue withdrawal") + expect(table_row).to have_content("Request decision:") + expect(table_row).to have_content("Request originated by") + expect(table_row).to have_content("View original request") + + expect(table_row).to have_content("View original request") + first("a", text: "View original request").click + expect(table_row).to have_content("Hide original request") + + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Issue type:") + expect(table_row).to have_content("Issue description:") + expect(table_row).to have_content("Decision date:") + expect(table_row).to have_content("Withdrawal request reason:") + expect(table_row).to have_content("Withdrawal request date:") + clear_filter_option("Approval of request - issue withdrawal") + end + + step "Checking Cancellation of request" do + click_filter_option("Cancellation of request") + expect(event_types.include?("Cancellation of request")).to be_truthy + table = page.find("tbody") + + expect(table).to have_selector("tr", count: 4) + clear_filter_option("Cancellation of request") + end + + step "Checking Edit of request - issue addition" do + click_filter_option("Edit of request - issue addition (1)") + expect(event_types.include?("Edit of request - issue addition")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + + table_row = table.first('tr[id^="table-row"]') + + expect(table_row).to have_content("Edit of request - issue addition") + expect(table_row).to have_content("New issue type:") + expect(table_row).to have_content("New issue description:") + expect(table_row).to have_content("New decision date:") + expect(table_row).to have_content("New addition request reason:") + + expect(table_row).to have_content("View original request") + first("a", text: "View original request").click + expect(table_row).to have_content("Hide original request") + + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Issue type:") + expect(table_row).to have_content("Issue description:") + expect(table_row).to have_content("Decision date:") + expect(table_row).to have_content("Addition request reason:") + clear_filter_option("Edit of request - issue addition") + end + + step "Checking Edit of request - issue modification" do + click_filter_option("Edit of request - issue modification (1)") + expect(event_types.include?("Edit of request - issue modification")).to be_truthy + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + + table_row = table.first('tr[id^="table-row"]') + expect(table_row).to have_content("Edit of request - issue modification") + + expect(table_row).to have_content("New issue type:") + expect(table_row).to have_content("New issue description:") + expect(table_row).to have_content("New decision date:") + expect(table_row).to have_content("New modification request reason:") + + expect(table_row).to have_content("View original request") + first("a", text: "View original request").click + expect(table_row).to have_content("Hide original request") + + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Current issue type:") + expect(table_row).to have_content("Current issue description:") + expect(table_row).to have_content("Current decision date:") + expect(table_row).to have_content("New issue type:") + expect(table_row).to have_content("New issue description:") + expect(table_row).to have_content("New decision date:") + expect(table_row).to have_content("Modification request reason:") + + clear_filter_option("Edit of request - issue modification") + end + + step "Checking Edit of request - issue removal" do + click_filter_option("Edit of request - issue removal (4)") + expect(event_types.include?("Edit of request - issue removal")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 4) + + table_row = table.first('tr[id^="table-row"]') + expect(table_row).to have_content("Edit of request - issue removal") + expect(table_row).to have_content("New removal request reason:") + + expect(table_row).to have_content("View original request") + first("a", text: "View original request").click + expect(table_row).to have_content("Hide original request") + + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Issue type:") + expect(table_row).to have_content("Issue description:") + expect(table_row).to have_content("Decision date:") + expect(table_row).to have_content("Removal request reason:") + clear_filter_option("Edit of request - issue removal") + end + + step "Checking Edit of request - issue withdrawal" do + click_filter_option("Edit of request - issue withdrawal (4)") + expect(event_types.include?("Edit of request - issue withdrawal")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 4) + + table_row = table.first("tr") + expect(table_row).to have_content("Edit of request - issue withdrawal") + expect(table_row).to have_content("New withdrawal request reason:") + expect(table_row).to have_content("New withdrawal request date:") + + expect(table_row).to have_content("View original request") + first("a", text: "View original request").click + expect(table_row).to have_content("Hide original request") + + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Issue type:") + expect(table_row).to have_content("Issue description:") + expect(table_row).to have_content("Decision date:") + expect(table_row).to have_content("Withdrawal request reason:") + expect(table_row).to have_content("Withdrawal request date:") + clear_filter_option("Edit of request - issue withdrawal") + end + + step "Checking Rejection of request - issue addition" do + click_filter_option("Rejection of request - issue addition (1)") + expect(event_types.include?("Rejection of request - issue addition")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + + table_row = table.first("tr") + expect(table_row).to have_content("Request decision:") + expect(table_row).to have_content("Reason for rejection:") + expect(table_row).to have_content("Request originated by:") + + expect(table_row).to have_content("View original request") + first("a", text: "View original request").click + expect(table_row).to have_content("Hide original request") + + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Issue type:") + expect(table_row).to have_content("Issue description:") + expect(table_row).to have_content("Decision date:") + expect(table_row).to have_content("Addition request reason:") + + clear_filter_option("Rejection of request - issue addition") + end + + step "Checking Rejection of request - issue modification" do + click_filter_option("Rejection of request - issue modification (1)") + expect(event_types.include?("Rejection of request - issue modification")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + + table_row = table.first("tr") + expect(table_row).to have_content("Request decision:") + expect(table_row).to have_content("Reason for rejection:") + expect(table_row).to have_content("Request originated by:") + + expect(table_row).to have_content("View original request") + first("a", text: "View original request").click + expect(table_row).to have_content("Hide original request") + + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Current issue type:") + expect(table_row).to have_content("Current issue description:") + expect(table_row).to have_content("New issue type:") + expect(table_row).to have_content("New issue description:") + expect(table_row).to have_content("New decision date:") + expect(table_row).to have_content("Modification request reason:") + + clear_filter_option("Rejection of request - issue modification") + end + + step "Checking Rejection of request - issue removal" do + click_filter_option("Rejection of request - issue removal (1)") + expect(event_types.include?("Rejection of request - issue removal")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + + table_row = table.first("tr") + expect(table_row).to have_content("Request decision:") + expect(table_row).to have_content("Reason for rejection:") + expect(table_row).to have_content("Request originated by:") + + expect(table_row).to have_content("View original request") + first("a", text: "View original request").click + expect(table_row).to have_content("Hide original request") + + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Issue type:") + expect(table_row).to have_content("Issue description:") + expect(table_row).to have_content("Issue type:") + expect(table_row).to have_content("Decision date:") + + clear_filter_option("Rejection of request - issue removal") + end + + step "Checking Rejection of request - issue withdrawal" do + click_filter_option("Rejection of request - issue withdrawal (1)") + expect(event_types.include?("Rejection of request - issue withdrawal")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + + table_row = table.first("tr") + expect(table_row).to have_content("Request decision:") + expect(table_row).to have_content("Reason for rejection:") + expect(table_row).to have_content("Request originated by:") + + expect(table_row).to have_content("View original request") + first("a", text: "View original request").click + + expect(table_row).to have_content("Hide original request") + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Issue type:") + expect(table_row).to have_content("Issue description:") + expect(table_row).to have_content("Decision date:") + expect(table_row).to have_content("Withdrawal request reason:") + expect(table_row).to have_content("Withdrawal request date:") + + clear_filter_option("Rejection of request - issue withdrawal") + end + + step "Checking Added issue" do + click_filter_option("Added issue") + expect(event_types.include?("Added issue")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 10) + + table_row = table.first("tr") + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Issue type:") + expect(table_row).to have_content("Issue description:") + expect(table_row).to have_content("Decision date:") + + clear_filter_option("Added issue") + end + + step "checking Requested issue addition" do + click_filter_option("Requested issue addition (4)") + expect(event_types.include?("Requested issue addition")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 4) + + table_row = table.first("tr") + expect(table_row).to have_content("Requested issue addition") + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Issue type:") + expect(table_row).to have_content("Issue description:") + expect(table_row).to have_content("Decision date:") + expect(table_row).to have_content("Addition request reason:") + + clear_filter_option("Requested issue addition") + end + + step "Checking Requested issue modification" do + click_filter_option("Requested issue modification (3)") + expect(event_types.include?("Requested issue modification")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 3) + + table_row = table.first('tr[id^="table-row"]') + expect(table_row).to have_content("Requested issue modification") + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Current issue type:") + expect(table_row).to have_content("Current issue description:") + expect(table_row).to have_content("Current decision date:") + expect(table_row).to have_content("New issue type:") + expect(table_row).to have_content("New issue description:") + expect(table_row).to have_content("New decision date:") + expect(table_row).to have_content("Modification request reason:") + + clear_filter_option("Requested issue modification") + end + + step "Checking Requested issue removal" do + click_filter_option("Requested issue removal (3)") + expect(event_types.include?("Requested issue removal")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 3) + + table_row = table.first('tr[id^="table-row"]') + expect(table_row).to have_content("Requested issue removal") + + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Issue type:") + expect(table_row).to have_content("Issue description:") + expect(table_row).to have_content("Decision date:") + expect(table_row).to have_content("Removal request reason:") + + clear_filter_option("Requested issue removal") + end + + step "Checking Requested issue withdrawal" do + click_filter_option("Requested issue withdrawal (3)") + expect(event_types.include?("Requested issue withdrawal")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 3) + + table_row = table.first("tr") + expect(table_row).to have_content("Requested issue withdrawal") + + expect(table_row).to have_content("Benefit type:") + expect(table_row).to have_content("Issue type:") + expect(table_row).to have_content("Issue description:") + expect(table_row).to have_content("Decision date:") + expect(table_row).to have_content("Withdrawal request reason:") + expect(table_row).to have_content("Withdrawal request date:") + + clear_filter_option("Requested issue withdrawal") + end + + step "Checking Claim status - Pending" do + click_filter_option("Claim status - Pending (1)") + expect(event_types.include?("Claim status - Pending")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + + table_row = table.first("tr") + expect(table_row).to have_content("Claim status - Pending") + expect(table_row).to have_content("Claim cannot be processed until VHA admin reviews pending requests.") + + clear_filter_option("Claim status - Pending") + end + + step "Checking Claim created" do + click_filter_option("Claim created (1)") + expect(event_types.include?("Claim created")).to be_truthy + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + + table_row = table.first("tr") + expect(table_row).to have_content("Claim created") + expect(table_row).to have_content("Claim created.") + end + end + end + + context "should do expected details to show claim close for a claim close" do + before { visit "/decision_reviews/vha/tasks/#{claim_closed.id}/history" } + + it "Claim closed" do + click_filter_option("Claim closed (1)") + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + + table_row = table.first("tr") + expect(table_row).to have_content("Claim closed") + expect(table_row).to have_content("Claim decision date:") + end + end + end + + describe "check for dynamic data coming in" do + let!(:task_event_two) do + create(:issue_modification_request, + :with_higher_level_review_with_decision, + nonrating_issue_category: "Medical and Dental Care Reimbursement") + end + let(:task_event_two_id) { task_event_two.decision_review.tasks.ids[0] } + + let!(:approved_modification_edit) do + create(:issue_modification_request, + request_type: "modification", + decision_review: task_event_two.decision_review, + request_issue: task_event_two.decision_review.request_issues.first, + request_reason: "Initial request reason", + decision_date: 2.days.ago) + end + + let!(:cancelled_issue_modification_request_modification) do + create(:issue_modification_request, + :with_request_issue, + :cancel_of_request, + request_type: "modification", + decision_review: task_event_two.decision_review) + end + + let!(:denied_issue_modification_request_modification) do + request = create(:issue_modification_request, + :with_request_issue, + request_type: "modification", + decision_review: task_event_two.decision_review) + + request.update( + status: "denied", + decision_reason: "Decision for approval" + ) + request + end + + let(:events) { ClaimHistoryService.new(non_comp_org, task_id: task_event_two_id).build_events } + + before do + Timecop.freeze(Time.zone.now) + # approved_modification_edit + Timecop.travel(2.minutes.from_now) + + # Edit the request to create a request edit event + approved_modification_edit.update!(request_reason: "I edited this request.", + nonrating_issue_category: "CHAMPVA", + nonrating_issue_description: "Newly edited issue description") + + Timecop.travel(2.minutes.from_now) + approved_modification_edit.update!(status: "approved") + end + + after do + Timecop.return + end + + context "Check for data output" do + it "check for the correct data for Edited Request Modification" do + visit "/decision_reviews/vha/tasks/#{task_event_two_id}/history" + click_filter_option("Edit of request - issue modification (1)") + + original_modification_request = events.detect { |e| e.event_type == :request_edited } + + new_decision_date = original_modification_request.new_decision_date + request_issue_decision_date = Date.parse(original_modification_request.decision_date) + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + table_row = table.first('tr[id^="table-row"]') + expect(table_row).to have_content("Edit of request - issue modification") + expect(table_row).to have_content("New issue type: #{approved_modification_edit.nonrating_issue_category}") + expect(table_row).to have_content( + "New issue description: #{approved_modification_edit.nonrating_issue_description}" + ) + expect(table_row).to have_content( + "New decision date: #{approved_modification_edit.decision_date.strftime('%m/%d/%Y')}" + ) + expect(table_row).to have_content( + "New modification request reason: #{approved_modification_edit.request_reason}" + ) + + expect(table_row).to have_content("View original request") + first("a", text: "View original request").click + expect(table_row).to have_content("Hide original request") + + expect(table_row).to have_content("Benefit type: Veterans Health Administration") + expect(table_row).to have_content("Current issue type: #{original_modification_request.issue_type}") + expect(table_row).to have_content( + "Current issue description: #{original_modification_request.issue_description}" + ) + expect(table_row).to have_content( + "Current decision date: #{request_issue_decision_date.strftime('%m/%d/%Y')}" + ) + expect(table_row).to have_content("New issue type: #{original_modification_request.new_issue_type}") + expect(table_row).to have_content( + "New issue description: #{original_modification_request.new_issue_description}" + ) + + expect(table_row).to have_content("New decision date: #{new_decision_date.strftime('%m/%d/%Y')}") + expect(table_row).to have_content( + "Modification request reason: #{original_modification_request.previous_modification_request_reason}" + ) + + clear_filter_option("Edit of request - issue modification") + + step "check for the correct data for Approval of request - issue modification" do + click_filter_option("Approval of request - issue modification (1)") + + original_modification_request = events.reverse.find { |e| e.event_type == :request_approved } + new_decision_date = Date.parse(original_modification_request.new_decision_date) + request_issue_decision_date = Date.parse(original_modification_request.decision_date) + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + table_row = table.first('tr[id^="table-row"]') + expect(table_row).to have_content("Approval of request - issue modification") + + expect(table_row).to have_content("Request decision: #{ + original_modification_request.issue_modification_request_status == 'denied' ? 'Rejected' : 'Approved' + }") + + expect(table_row).to have_content("Remove original issue: #{ + original_modification_request.remove_original_issue ? 'Yes' : 'No' + }") + expect(table_row).to have_content("Request originated by: #{original_modification_request.requestor}") + + expect(table_row).to have_content("View original request") + first("a", text: "View original request").click + expect(table_row).to have_content("Hide original request") + + expect(table_row).to have_content("Benefit type: Veterans Health Administration") + expect(table_row).to have_content("Current issue type: #{original_modification_request.issue_type}") + expect(table_row).to have_content( + "Current issue description: #{original_modification_request.issue_description}" + ) + expect(table_row).to have_content( + "Current decision date: #{request_issue_decision_date.strftime('%m/%d/%Y')}" + ) + expect(table_row).to have_content("New issue type: #{original_modification_request.new_issue_type}") + expect(table_row).to have_content( + "New issue description: #{original_modification_request.new_issue_description}" + ) + expect(table_row).to have_content("New decision date: #{new_decision_date.strftime('%m/%d/%Y')}") + expect(table_row).to have_content( + "Modification request reason: #{original_modification_request.modification_request_reason}" + ) + + clear_filter_option("Approval of request - issue modification") + end + + step "check for the correct data for denied request modification" do + click_filter_option("Rejection of request - issue modification (1)") + + original_modification_request = events.detect { |e| e.event_type == :request_denied } + new_decision_date = Date.parse(original_modification_request.new_decision_date) + request_issue_decision_date = Date.parse(original_modification_request.decision_date) + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + table_row = table.first('tr[id^="table-row"]') + expect(table_row).to have_content("Rejection of request - issue modification") + + expect(table_row).to have_content("Request decision: #{ + original_modification_request.issue_modification_request_status == 'denied' ? 'Rejected' : 'Approved' + }") + expect(table_row).to have_content("Request originated by: #{original_modification_request.requestor}") + + expect(table_row).to have_content("View original request") + first("a", text: "View original request").click + expect(table_row).to have_content("Hide original request") + + expect(table_row).to have_content("Benefit type: Veterans Health Administration") + expect(table_row).to have_content("Current issue type: #{original_modification_request.issue_type}") + expect(table_row).to have_content( + "Current issue description: #{original_modification_request.issue_description}" + ) + expect(table_row).to have_content( + "Current decision date: #{request_issue_decision_date.strftime('%m/%d/%Y')}" + ) + expect(table_row).to have_content("New issue type: #{original_modification_request.new_issue_type}") + expect(table_row).to have_content( + "New issue description: #{original_modification_request.new_issue_description}" + ) + expect(table_row).to have_content("New decision date: #{new_decision_date.strftime('%m/%d/%Y')}") + expect(table_row).to have_content( + "Modification request reason: #{original_modification_request.modification_request_reason}" + ) + + clear_filter_option("Rejection of request - issue modification") + end + + step "check for the correct data for Cancellation of request" do + click_filter_option("Cancellation of request (1)") + + original_modification_request = events.detect { |e| e.event_type == :request_cancelled } + + new_decision_date = Date.parse(original_modification_request.new_decision_date) + request_issue_decision_date = Date.parse(original_modification_request.decision_date) + + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) + table_row = table.first('tr[id^="table-row"]') + expect(table_row).to have_content("Cancellation of request") + + expect(table_row).to have_content("Benefit type: Veterans Health Administration") + expect(table_row).to have_content( + "Current issue type: #{original_modification_request.issue_type}" + ) + expect(table_row).to have_content( + "Current issue description: #{original_modification_request.issue_description}" + ) + expect(table_row).to have_content( + "Current decision date: #{request_issue_decision_date.strftime('%m/%d/%Y')}" + ) + expect(table_row).to have_content("New issue type: #{original_modification_request.new_issue_type}") + expect(table_row).to have_content( + "New issue description: #{original_modification_request.new_issue_description}" + ) + expect(table_row).to have_content("New decision date: #{new_decision_date.strftime('%m/%d/%Y')}") + expect(table_row).to have_content( + "Modification request reason: #{original_modification_request.modification_request_reason}" + ) + end + end + end + end + + describe "check for cancelled claim" do + let!(:cancelled_claim) do + create(:supplemental_claim, :with_vha_issue, :with_update_users) + end + + before do + cancelled_claim.establish! + cancelled_claim.request_issues.update(closed_status: "withdrawn", closed_at: Time.zone.now) + cancelled_claim.tasks.update(status: "cancelled") + cancelled_claim.reload + + visit "/decision_reviews/vha/tasks/#{cancelled_claim.tasks[0].id}/history" + end + + context "should do expected details to show claim closed when cancelled" do + it "Claim Cancelled" do + click_filter_option("Claim closed (1)") - table = page.find("tbody") - expect(table).to have_selector("tr", count: number_of_events) + table = page.find("tbody") + expect(table).to have_selector("tr", count: 1) - events.each do |event| - expect(page).to have_text(event.readable_event_type) + table_row = table.first("tr") + expect(table_row).to have_content("Claim closed") + expect(table_row).to have_content("Claim cancelled.") + end end end end diff --git a/spec/models/business_line_spec.rb b/spec/models/business_line_spec.rb index 1684d7228db..8bd3ad8ce93 100644 --- a/spec/models/business_line_spec.rb +++ b/spec/models/business_line_spec.rb @@ -518,6 +518,21 @@ benefit_type: "vha", claimant_type: :dependent_claimant)) end + let!(:hlr_task_with_imr) do + create(:issue_modification_request, + :with_higher_level_review, + :edit_of_request, + nonrating_issue_category: "Medical and Dental Care Reimbursement", + nonrating_issue_description: "Reimbursement note description") + end + + let!(:sc_task_with_imr) do + create(:issue_modification_request, + :with_supplemental_claim, + :edit_of_request, + nonrating_issue_category: "Medical and Dental Care Reimbursement", + nonrating_issue_description: "Reimbursement note description") + end let!(:remand_task) do create(:remand_vha_task, appeal: create(:remand, @@ -533,7 +548,7 @@ let(:hlr_task_1_ri_1_expectation) do a_hash_including( "nonrating_issue_category" => "Caregiver | Other", - "nonrating_issue_description" => "VHA - Caregiver ", + "nonrating_issue_description" => "VHA - Caregiver", "task_id" => hlr_task.id, "veteran_file_number" => hlr_task.appeal.veteran_file_number, "intake_user_name" => hlr_task.appeal.intake.user.full_name, @@ -571,7 +586,7 @@ let(:hlr_task_2_ri_1_expectation) do a_hash_including( "nonrating_issue_category" => "Caregiver | Other", - "nonrating_issue_description" => "VHA - Caregiver ", + "nonrating_issue_description" => "VHA - Caregiver", "task_id" => hlr_task2.id, "veteran_file_number" => hlr_task2.appeal.veteran_file_number, "intake_user_name" => intake_user.full_name, @@ -625,6 +640,29 @@ "days_waiting" => (Time.zone.today - Date.parse(sc_task.assigned_at.iso8601)).to_i ) end + let(:imr_hlr_expectation) do + a_hash_including( + "requested_issue_type" => "Medical and Dental Care Reimbursement", + "requested_issue_description" => "Reimbursement note description", + "remove_original_issue" => false, + "modification_request_reason" => "I edited this request.", + "request_type" => "addition", + "issue_modification_request_status" => "assigned", + "decision_review_type" => "HigherLevelReview" + ) + end + let(:imr_sc_expectation) do + a_hash_including( + "requested_issue_type" => "Medical and Dental Care Reimbursement", + "requested_issue_description" => "Reimbursement note description", + "remove_original_issue" => false, + "modification_request_reason" => "I edited this request.", + "request_type" => "addition", + "issue_modification_request_status" => "assigned", + "decision_reason" => nil, + "decision_review_type" => "SupplementalClaim" + ) + end let(:remand_task_1_ri_1_expectation) do a_hash_including( "nonrating_issue_category" => "Clothing Allowance", @@ -708,6 +746,7 @@ context "without filters" do it "should return all rows" do + expect(subject.count).to eq 8 expect(subject.count).to eq 6 expect(subject.entries).to include(*all_expectations) end @@ -744,7 +783,7 @@ let(:change_history_filters) { { claim_type: "SupplementalClaim" } } it "should only return rows for the filtered claim type" 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) end end @@ -753,7 +792,7 @@ let(:change_history_filters) { { claim_type: "HigherLevelReview" } } it "should only return rows for the filtered claim type" do - expect(subject.entries.count).to eq(4) + expect(subject.entries.count).to eq(5) expect(subject.entries).to include( *(all_expectations - [sc_task_1_ri_1_expectation] - [remand_task_1_ri_1_expectation]) ) @@ -782,6 +821,32 @@ end end + context "with task status filter pending" do + let(:change_history_filters) { { task_status: ["pending"] } } + + it "should only return rows for the filtered status types" do + expect(subject.entries.count).to eq(2) + expect(subject.entries).to include( + imr_hlr_expectation, + imr_sc_expectation + ) + end + end + + context "with task status filter pending and completed" do + let(:change_history_filters) { { task_status: %w[pending completed] } } + + it "should only return rows for the filtered status types" do + expect(subject.entries.count).to eq(4) + expect(subject.entries).to include( + hlr_task_1_ri_1_expectation, + hlr_task_1_ri_2_expectation, + imr_hlr_expectation, + imr_sc_expectation + ) + end + end + context "with dispositions filter" do context "with multiple disposition filters" do let(:change_history_filters) { { dispositions: %w[Granted denied] } } @@ -808,7 +873,7 @@ let(:change_history_filters) { { dispositions: ["Blank"] } } it "should return rows that do not have a disposition" do - expect(subject.entries.count).to eq(4) + expect(subject.entries.count).to eq(6) expect(subject.entries).to include( hlr_task_2_ri_1_expectation, hlr_task_2_ri_2_expectation, @@ -821,7 +886,7 @@ 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(5) + expect(subject.entries.count).to eq(7) expect(subject.entries).to include( hlr_task_1_ri_2_expectation, hlr_task_2_ri_1_expectation, @@ -832,6 +897,14 @@ end end end + + context "with a single disposition filter and task status pending" do + let(:change_history_filters) { { dispositions: ["denied"], task_status: ["pending"] } } + + it "should only not return any row since pending task cannot have disposition" do + expect(subject.entries.count).to eq(0) + end + end end context "with issue types filter" do @@ -839,7 +912,7 @@ let(:change_history_filters) { { issue_types: ["Beneficiary Travel", "CHAMPVA"] } } it "should only return rows for the filtered issue type values" do - expect(subject.entries.count).to eq(2) + expect(subject.entries.count).to eq(3) expect(subject.entries).to include( hlr_task_1_ri_2_expectation, sc_task_1_ri_1_expectation @@ -851,13 +924,22 @@ let(:change_history_filters) { { issue_types: ["Caregiver | Other"] } } it "should only return rows for the filtered issue type values" do - expect(subject.entries.count).to eq(2) + expect(subject.entries.count).to eq(3) expect(subject.entries).to include( hlr_task_1_ri_1_expectation, hlr_task_2_ri_1_expectation ) end end + + context "with a single issue type filter and pending task status" do + let(:change_history_filters) { { issue_types: ["Caregiver | Other"], task_status: ["pending"] } } + + it "should only return rows for the filtered issue type values" do + expect(subject.entries.count).to eq(1) + expect(subject.entries).to include(imr_hlr_expectation) + end + end end context "with days waiting filter" do @@ -865,7 +947,7 @@ let(:change_history_filters) { { days_waiting: { number_of_days: 6, operator: "<" } } } it "should only return rows that are under the filtered days waiting value" do - expect(subject.entries.count).to eq(2) + expect(subject.entries.count).to eq(4) expect(subject.entries).to include( hlr_task_2_ri_1_expectation, hlr_task_2_ri_2_expectation @@ -934,7 +1016,7 @@ let(:change_history_filters) { { facilities: ["101"] } } it "only return rows where either an intake, decisions, or updates user matches the station id" do - expect(subject.entries.count).to eq(3) + expect(subject.entries.count).to eq(5) expect(subject.entries).to include( hlr_task_1_ri_1_expectation, hlr_task_1_ri_2_expectation, @@ -994,10 +1076,21 @@ end it "should only return rows that match both filters" 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) end end + + context "multiple issue types and claim type and task status pending" do + let(:change_history_filters) do + { issue_types: ["Beneficiary Travel", "CHAMPVA"], claim_type: "SupplementalClaim", task_status: ["pending"] } + end + + it "should only return rows that match both filters" do + expect(subject.entries.count).to eq(1) + expect(subject.entries).to include(imr_sc_expectation) + end + end end end diff --git a/spec/services/claim_change_history/change_history_event_serializer_spec.rb b/spec/services/claim_change_history/change_history_event_serializer_spec.rb index 540cc563f2d..5829c4c02de 100644 --- a/spec/services/claim_change_history/change_history_event_serializer_spec.rb +++ b/spec/services/claim_change_history/change_history_event_serializer_spec.rb @@ -36,6 +36,29 @@ ClaimHistoryService.new(vha_org, task_id: vha_task.id).build_events end + let(:modificationRequestDetailsObject) do + { + benefitType: "vha", + requestType: nil, + issueModificationRequestWithdrawalDate: nil, + modificationRequestReason: nil, + newDecisionDate: nil, + newIssueDescription: nil, + newIssueType: nil, + previousDecisionDate: nil, + previousIssueDescription: nil, + previousIssueType: nil, + previousModificationRequestReason: nil, + previousWithdrawalDate: nil, + removeOriginalIssue: nil, + issueModificationRequestStatus: nil, + requestor: nil, + decider: nil, + decidedAtDate: nil, + decisionReason: nil + } + end + let(:serialized_hash_array) do [ { @@ -43,23 +66,24 @@ type: :change_history_event, attributes: { claimType: "Higher-Level Review", + readableEventType: "Claim created", claimantName: events[0].claimant_name, - details: - { - benefitType: "vha", - decisionDate: nil, - decisionDescription: nil, - disposition: nil, - dispositionDate: nil, - issueDescription: nil, - issueType: nil, - withdrawalRequestDate: nil - }, + eventUser: "L. Roth", eventDate: events[0].event_date, eventType: :claim_creation, - eventUser: "L. Roth", - readableEventType: "Claim created", - taskID: vha_task.id + taskID: vha_task.id, + details: + { + benefitType: "vha", + decisionDate: nil, + decisionDescription: nil, + disposition: nil, + dispositionDate: nil, + issueDescription: nil, + issueType: nil, + withdrawalRequestDate: nil + }, + modificationRequestDetails: modificationRequestDetailsObject } }, { @@ -67,6 +91,7 @@ type: :change_history_event, attributes: { claimType: "Higher-Level Review", + readableEventType: "Added issue", claimantName: events[1].claimant_name, details: { @@ -79,10 +104,34 @@ issueType: "Other", withdrawalRequestDate: nil }, + modificationRequestDetails: modificationRequestDetailsObject, eventDate: events[1].event_date, eventType: :added_issue, eventUser: "L. Roth", - readableEventType: "Added issue", + taskID: vha_task.id + } + }, + { + id: expected_uuid, + type: :change_history_event, + attributes: { + eventType: :in_progress, + eventUser: "System", + claimType: "Higher-Level Review", + readableEventType: "Claim status - In progress", + claimantName: events[2].claimant_name, + details: { + benefitType: "vha", + issueType: nil, + issueDescription: nil, + decisionDate: nil, + disposition: nil, + decisionDescription: nil, + dispositionDate: nil, + withdrawalRequestDate: nil + }, + modificationRequestDetails: modificationRequestDetailsObject, + eventDate: events[2].event_date, taskID: vha_task.id } } 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 a18a9782b70..581d37d1025 100644 --- a/spec/services/claim_change_history/change_history_reporter_spec.rb +++ b/spec/services/claim_change_history/change_history_reporter_spec.rb @@ -145,6 +145,95 @@ new_event.instance_variable_set(:@task_status, "cancelled") new_event end + let(:pending_issue_event) do + new_event = removed_issue_event.clone + new_event.instance_variable_set(:@event_type, :pending) + new_event.instance_variable_set(:@task_status, "assigned") + new_event + end + let(:issue_modification_request_event) do + new_event = pending_issue_event.clone + new_event.instance_variable_set(:@event_type, :modification) + new_event.instance_variable_set(:@task_status, "assigned") + 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(:remand_claim_creation_event) do new_event = added_issue_event.clone @@ -236,6 +325,8 @@ added_decision_date_event, removed_issue_event, withdrew_issue_event, + pending_issue_event, + issue_modification_request_event, remand_claim_creation_event, remand_in_progress_status_event, remand_cancelled_status_event, @@ -264,6 +355,11 @@ "CHAMPVA", "CHAMPVA issue description", added_issue_event.readable_decision_date, + nil, + nil, + nil, + nil, + nil, nil ] end @@ -282,6 +378,11 @@ "Claim created", nil, "Claim created.", + nil, + nil, + nil, + nil, + nil, nil ] end @@ -300,6 +401,11 @@ "Claim status - In progress", nil, "Claim can be processed.", + nil, + nil, + nil, + nil, + nil, nil ] end @@ -317,7 +423,12 @@ cancelled_status_event.readable_event_date, "Claim closed", nil, - "Claim closed.", + "Claim cancelled.", + nil, + nil, + nil, + nil, + nil, nil ] end @@ -335,6 +446,11 @@ "Claim closed", nil, "Claim closed.", + nil, + nil, + nil, + nil, + nil, nil ] end @@ -352,6 +468,11 @@ "Claim status - Incomplete", nil, "Claim cannot be processed until decision date is entered.", + nil, + nil, + nil, + nil, + nil, nil ] end @@ -370,6 +491,11 @@ "CHAMPVA", "CHAMPVA issue description", added_issue_without_decision_date_event.readable_decision_date, + nil, + nil, + nil, + nil, + nil, nil ] end @@ -388,6 +514,11 @@ "CHAMPVA", "CHAMPVA issue description", completed_disposition_event.readable_decision_date, + nil, + nil, + nil, + nil, + nil, "Granted", "Decision for CHAMPVA issue", completed_disposition_event.readable_disposition_date @@ -408,6 +539,11 @@ "CHAMPVA", "CHAMPVA issue description", added_decision_date_event.readable_decision_date, + nil, + nil, + nil, + nil, + nil, nil ] end @@ -426,6 +562,11 @@ "CHAMPVA", "CHAMPVA issue description", removed_issue_event.readable_decision_date, + nil, + nil, + nil, + nil, + nil, nil ] end @@ -444,6 +585,286 @@ "CHAMPVA", "CHAMPVA issue description", withdrew_issue_event.readable_decision_date, + nil, + nil, + nil, + nil, + nil, + nil + ] + end + let(:pending_issue_event_row) do + [ + "242080004", + "Mason Rodriguez", + "/decision_reviews/vha/tasks/900", + "in progress", + "20", + "Higher-Level Review", + "Austin AAC (200)", + "E. Thompson", + added_issue_event.readable_event_date, + "Claim status - Pending", + nil, + "Claim cannot be processed until VHA admin reviews pending requests.", + nil, + nil, + nil, + nil, + nil, + nil + ] + end + let(:modification_issue_event_row) do + [ + "242080004", + "Mason Rodriguez", + "/decision_reviews/vha/tasks/900", + "in progress", + "20", + "Higher-Level Review", + "Austin AAC (200)", + "E. Thompson", + added_issue_event.readable_event_date, + "Requested issue modification", + "CHAMPVA", + "CHAMPVA issue description", + added_issue_event.readable_decision_date, + nil, + nil, + nil, + nil, + nil, + nil + ] + 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, + nil, + nil, + nil, + nil, + 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, + nil, + nil, + nil, + nil, + 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 cancelled.", + nil, + nil, + nil, + nil, + nil, + 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, + nil, + nil, + nil, + nil, + 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, + nil, + nil, + nil, + nil, + 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, + nil, + nil, + nil, + nil, + 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, + nil, + nil, + nil, + nil, + nil, + "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, + nil, + nil, + nil, + nil, + 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, + nil, + nil, + nil, + nil, + 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, + nil, + nil, + nil, + nil, nil ] end @@ -630,6 +1051,7 @@ 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) expect(rows[0]).to eq([]) expect(rows[1]).to eq(column_headers) @@ -644,16 +1066,18 @@ 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) + expect(rows[13]).to eq(pending_issue_event_row) + expect(rows[14]).to eq(modification_issue_event_row) + expect(rows[15]).to eq(remand_claim_creation_event_row) + expect(rows[16]).to eq(remand_in_progress_status_event_row) + expect(rows[17]).to eq(remand_cancelled_status_event_row) + expect(rows[18]).to eq(remand_completed_status_event_row) + expect(rows[19]).to eq(remand_incomplete_status_event_row) + expect(rows[20]).to eq(remand_added_issue_without_decision_date_event_row) + expect(rows[21]).to eq(remand_completed_disposition_event_row) + expect(rows[22]).to eq(remand_added_decision_date_event_row) + expect(rows[23]).to eq(remand_removed_issue_event_row) + expect(rows[24]).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 8b4aeeabdb8..9a0a672018d 100644 --- a/spec/services/claim_change_history/claim_history_event_spec.rb +++ b/spec/services/claim_change_history/claim_history_event_spec.rb @@ -49,6 +49,35 @@ "task_versions" => version_changes, "days_waiting" => 25, "task_closed_at" => "2023-10-19 22:47:16.233187", + "issue_modification_request_id" => 61, + "requested_issue_type" => "Caregiver | Tier Level", + "requested_issue_description" => "test", + "remove_original_issue" => nil, + "modification_request_reason" => "Testing", + "requested_decision_date" => "2024-07-07", + "request_type" => request_type, + "issue_modification_request_status" => "cancelled", + "decision_reason" => nil, + "decider_id" => nil, + "requestor_id" => "2000006012", + "decided_at" => nil, + "issue_modification_request_created_at" => issue_modification_request_created_at, + "issue_modification_request_updated_at" => "2024-07-22 15:58:42.908194", + "issue_modification_request_edited_at" => nil, + "issue_modification_request_withdrawal_date" => nil, + "decision_review_id" => 65, + "decision_review_type" => "HigherLevelReview", + "requestor" => "Monte Mann", + "requestor_station_id" => "741", + "requestor_css_id" => "ACBAUERVVHAH", + "decider" => nil, + "decider_station_id" => nil, + "decider_css_id" => nil, + "imr_versions" => imr_versions, + "previous_imr_created_at" => nil, + "updater_user_name" => "Monte Mann", + "is_assigned_present" => is_assigned_present, + "previous_state_array" => previous_state_array, "type_classifier" => change_data_claim_type } end @@ -63,8 +92,20 @@ let(:change_data_decision_date) { "2023-05-31" } let(:change_data_decision_date_added_at) { Time.zone.parse("2023-10-19 22:48:25.281657") } let(:version_changes) { nil } + let(:imr_versions) { nil } + let(:request_type) { :addition } let(:request_issue_update_time) { Time.zone.parse("2023-10-19 22:47:16.233187") } let(:request_issue_created_at) { Time.zone.parse("2023-10-19 22:45:43.108934") } + let(:issue_modification_request_created_at) { Time.zone.parse("2023-10-20 22:47:16.233187") } + let(:previous_imr_created_at) { Time.zone.parse("2023-10-19 22:47:16.233187") } + let(:out_of_bounds_time) { Time.utc(9999, 12, 31, 23, 59, 59) } + + let(:decision_reason) { nil } + let(:decider_id) { nil } + let(:decided_at) { nil } + let(:is_assigned_present) { false } + let(:previous_state_array) {} + let(:event_attribute_data) do { assigned_at: Time.zone.parse("2023-10-19 22:47:16.222148"), @@ -117,6 +158,19 @@ } end + let(:issue_modification_request_attribute_data) do + { + request_type: request_type, + new_issue_type: "Caregiver | Tier Level", + new_issue_description: "test", + new_decision_date: "2024-07-07", + modification_request_reason: "Testing", + decision_reason: nil, + decided_at_date: nil, + issue_modification_request_withdrawal_date: nil + } + end + let(:intake_event_data) do { event_date: change_data["intake_completed_at"], @@ -135,6 +189,68 @@ } end + let(:pending_attribute_data) do + { + event_type: :pending, + claimant_name: "Bob Smithboehm", + event_user_name: "System" + } + end + let(:in_progress_attribute_data) do + { + event_type: :in_progress, + claimant_name: "Bob Smithboehm", + event_user_name: "System" + } + end + let(:modification_request_decision_date) { Time.zone.parse("2024-07-28").to_date } + let(:modification_attribute_data) do + { + event_type: :modification, + request_type: :modification, + benefit_type: "vha", + issue_type: "Clothing Allowance", + issue_description: "Clothing allowance no decision date", + decision_date: "2023-05-31", + new_issue_type: "Caregiver | Eligibility", + new_issue_description: "Rejection of withrwaslasas", + new_decision_date: modification_request_decision_date, + modification_request_reason: "Please withdwasdadsadadadad", + previous_issue_type: "Caregiver | Eligibility", + previous_issue_description: "Rejection of withrwaslasas", + previous_decision_date: modification_request_decision_date, + previous_modification_request_reason: "Please withdwasdadsadadadad" + } + end + let(:issue_modification_response_attribute) do + { + event_type: :request_approved, + request_type: request_type, + new_issue_type: "Caregiver | Tier Level", + new_issue_description: "test", + new_decision_date: "2024-07-07", + modification_request_reason: "Testing" + } + end + + let(:issue_modification_edited_attribute) do + { + event_type: :request_edited, + request_type: :withdrawal, + issue_type: "Clothing Allowance", + issue_description: "Clothing allowance no decision date", + new_issue_type: "Caregiver | Eligibility", + new_issue_description: "modifiedvalue", + new_decision_date: modification_request_decision_date, + modification_request_reason: "Addition is the only request issue-modifiedvalue Z", + event_user_name: "Monte Mann", + previous_issue_type: "Caregiver | Eligibility", + previous_issue_description: "Rejection of withrwaslasas", + previous_decision_date: modification_request_decision_date, + previous_modification_request_reason: "Please withdwasdadsadadadad" + } + end + describe "class methods" do describe ".from_change_data" do subject { described_class.from_change_data(event_type, change_data) } @@ -144,7 +260,6 @@ it "should create an instance and not raise an error" do claim_history_event = subject - expect_attributes(claim_history_event, status_event_attribute_data) end end @@ -165,6 +280,25 @@ expect { subject }.to raise_error(InvalidEventType) end end + + context "when the event type is request addition" do + let(:event_type) { :addition } + + it "should create an instance with issue request addition" do + claim_history_event = subject + + expect_attributes(claim_history_event, issue_modification_request_attribute_data) + end + end + + context "when the event type is request modification" do + let(:event_type) { :modification } + let(:request_type) { :modification } + it "should create an instance with issue request addition" do + claim_history_event = subject + expect_attributes(claim_history_event, issue_modification_request_attribute_data) + end + end end describe ".create_completed_disposition_event" do @@ -213,7 +347,7 @@ context "if the task status was assigned -> completed" do let(:version_changes) do - "{\"---\n" \ + "\"---\n" \ "closed_at:\n" \ "- \n" \ "- 2023-11-08 19:22:47.244142348 Z\n" \ @@ -223,7 +357,7 @@ "updated_at:\n" \ "- 2023-11-08 19:22:47.227634704 Z\n" \ "- 2023-11-09 19:22:47.244304624 Z\n" \ - "\"}" + "\"" end it "should create an in progress event and a completed status event" do @@ -240,7 +374,7 @@ context "if the task status was assigned -> cancelled" do let(:version_changes) do - "{\"---\n" \ + "\"---\n" \ "closed_at:\n" \ "- \n" \ "- 2023-11-09 23:16:28.446266110 Z\n" \ @@ -250,7 +384,7 @@ "updated_at:\n" \ "- 2023-11-09 23:16:15.724150103 Z\n" \ "- 2023-11-11 23:16:28.446399290 Z\n" \ - "\"}" + "\"" end it "should generate an in progress and a cancelled status event" do @@ -267,7 +401,7 @@ context "if the task status was assigned -> on_hold -> assigned -> completed" do let(:version_changes) do - "{\"---\n" \ + "\"---\n" \ "status:\n" \ "- assigned\n" \ "- on_hold\n" \ @@ -277,7 +411,7 @@ "updated_at:\n" \ "- 2023-10-19 22:39:14.207143000 Z\n" \ "- 2023-10-19 22:45:43.148742110 Z\n" \ - "\",---\n" \ + "\"|||---\n" \ "status:\n" \ "- on_hold\n" \ "- assigned\n" \ @@ -287,7 +421,7 @@ "updated_at:\n" \ "- 2023-10-19 22:45:43.148742000 Z\n" \ "- 2023-10-19 22:47:16.222311778 Z\n" \ - "\",---\n" \ + "\"|||---\n" \ "status:\n" \ "- assigned\n" \ "- completed\n" \ @@ -297,7 +431,7 @@ "updated_at:\n" \ "- 2023-10-19 22:47:16.222311000 Z\n" \ "- 2023-10-19 22:48:25.324023984 Z\n" \ - "\"}" + "\"" end it "should generate four status events" do @@ -324,7 +458,7 @@ context "if the task has no decision date and the task status was immediately set to on hold during intake" do let(:version_changes) do - "{\"---\n" \ + "\"---\n" \ "status:\n" \ "- assigned\n" \ "- on_hold\n" \ @@ -334,7 +468,7 @@ "updated_at:\n" \ "- 2023-10-19 22:39:14.207143000 Z\n" \ "- 2023-10-19 22:39:14.207143000 Z\n" \ - "\",---\n" \ + "\"|||---\n" \ "status:\n" \ "- on_hold\n" \ "- assigned\n" \ @@ -344,7 +478,7 @@ "updated_at:\n" \ "- 2023-10-19 22:45:43.148742000 Z\n" \ "- 2023-10-19 22:47:16.222311778 Z\n" \ - "\",---\n" \ + "\"|||---\n" \ "status:\n" \ "- assigned\n" \ "- completed\n" \ @@ -354,7 +488,7 @@ "updated_at:\n" \ "- 2023-10-19 22:47:16.222311000 Z\n" \ "- 2023-10-19 22:48:25.324023984 Z\n" \ - "\"}" + "\"" end it "should create an on_hold event, an in progress event, and a completed event" do @@ -389,7 +523,7 @@ context "if the task versions are from a hookless papertrail cancelled task" do let(:version_changes) do - "{\"--- {}\n\",\"--- {}\n\"}" + "\"--- {}\n\"|||\"--- {}\n\"" end it "should create an assigned and a cancelled task status event" do @@ -632,6 +766,190 @@ end end + describe ".create_pending_status_event" do + let(:event_date) { change_data["issue_modification_request_created_at"] } + let(:is_assigned_present) { true } + + subject { described_class.create_pending_status_event(change_data, event_date) } + + it "should return one pending event" do + expect_attributes(subject, pending_attribute_data) + end + + context "it should return pending event if issue modification requests are made in two different days" do + before do + change_data["previous_imr_created_at"] = previous_imr_created_at + end + + it "should return one pending event" do + expect_attributes(subject, pending_attribute_data) + end + end + + context "it should not pending event if issue modification requests are made in same transaction" do + let(:previous_imr_created_at) { issue_modification_request_created_at } + + before do + change_data["previous_imr_created_at"] = previous_imr_created_at + change_data["issue_modification_request_created_at"] = issue_modification_request_created_at + end + + it "should return one pending event" do + expect(subject).to be_nil + end + end + end + + describe ".create_issue_modification_request_event" do + context "when request type is modification" do + let(:request_type) { :modification } + let(:previous_state_array) do + "\"---\n" \ + "id: 150\n" \ + "status: assigned\n" \ + "requestor_id: 2000006012\n" \ + "nonrating_issue_category: Caregiver | Eligibility\n" \ + "decision_date: 2024-07-28\n" \ + "nonrating_issue_description: 'Rejection of withrwaslasas'\n" \ + "request_reason: Please withdwasdadsadadadad\n" \ + "withdrawal_date: 2024-08-04 04:00:00.000000000 Z\n" \ + "edited_at: \n" \ + "request_issue_id: 251\n" \ + "request_type: modification\n" \ + "benefit_type: vha\n" \ + "created_at: 2024-08-26 17:22:53.454663000 Z\n" \ + "decided_at: \n" \ + "decider_id: \n" \ + "decision_reason: \n" \ + "decision_review_id: 31\n" \ + "decision_review_type: SupplementalClaim\n" \ + "remove_original_issue: false\n" \ + "updated_at: 2024-08-26 17:22:53.454663000 Z\n" \ + "\"|||---\n" \ + "id: 150\n" \ + "status: assigned\n" \ + "requestor_id: 2000006012\n" \ + "nonrating_issue_category: Caregiver | Eligibility\n" \ + "decision_date: 2024-07-28\n" \ + "nonrating_issue_description: 'Rejection of withrwaslasas'\n" \ + "request_reason: Please Withdraw this one since its no longer valid.\n" \ + "withdrawal_date: 2024-08-04 04:00:00.000000000 Z\n" \ + "edited_at: 2024-08-26 17:23:28.055850000 Z\n" \ + "request_issue_id: 251\n" \ + "request_type: modification\n" \ + "benefit_type: vha\n" \ + "created_at: 2024-08-26 17:22:53.454663000 Z\n" \ + "decided_at: \n" \ + "decider_id: \n" \ + "decision_reason: \n" \ + "decision_review_id: 31\n" \ + "decision_review_type: SupplementalClaim\n" \ + "remove_original_issue: false\n" \ + "updated_at: 2024-08-26 17:23:28.072803000 Z\n" \ + "\"" + end + + subject { described_class.create_issue_modification_request_event(change_data) } + + it "should return single event with request_type passed as the request_type" do + verify_attributes_and_count(subject, 1, modification_attribute_data) + end + end + end + + describe ".create_edited_request_issue_events" do + let(:request_type) { :withdrawal } + let(:imr_versions) do + "\"---\n" \ + "nonrating_issue_description:\n" \ + "- First value\n" \ + "- modifiedvalue\n" \ + "request_reason:\n" \ + "- Addition is the only request issue\n" \ + "- Addition is the only request issue-modifiedvalue Z\n" \ + "edited_at:\n" \ + "- \n" \ + "- 2024-07-19 22:39:14.207143000 Z\n" \ + "updated_at:\n" \ + "- 2024-07-19 22:39:14.207143000 Z\n" \ + "- 2024-07-19 22:39:14.207143000 Z\n" \ + "\"|||---\n" \ + "status:\n" \ + "- assigned\n" \ + "- approved\n" \ + "nonrating_issue_description:\n" \ + "- modifiedvalue \n" \ + "- approved by me\n" \ + "updated_at:\n" \ + "- 2024-07-19 22:45:43.148742000 Z\n" \ + "- 2024-07-19 22:47:16.222311778 Z\n" \ + "\"" + end + let(:previous_state_array) do + "\"---\n" \ + "id: 150\n" \ + "status: assigned\n" \ + "requestor_id: 2000006012\n" \ + "nonrating_issue_category: Caregiver | Eligibility\n" \ + "decision_date: 2024-07-28\n" \ + "nonrating_issue_description: 'Rejection of withrwaslasas'\n" \ + "request_reason: Please withdwasdadsadadadad\n" \ + "withdrawal_date: 2024-08-04 04:00:00.000000000 Z\n" \ + "edited_at: \n" \ + "request_issue_id: 251\n" \ + "request_type: withdrawal\n" \ + "benefit_type: vha\n" \ + "created_at: 2024-08-26 17:22:53.454663000 Z\n" \ + "decided_at: \n" \ + "decider_id: \n" \ + "decision_reason: \n" \ + "decision_review_id: 31\n" \ + "decision_review_type: SupplementalClaim\n" \ + "remove_original_issue: false\n" \ + "updated_at: 2024-08-26 17:22:53.454663000 Z\n" \ + "\"|||---\n" \ + "id: 150\n" \ + "status: assigned\n" \ + "requestor_id: 2000006012\n" \ + "nonrating_issue_category: Caregiver | Eligibility\n" \ + "decision_date: 2024-07-28\n" \ + "nonrating_issue_description: 'Rejection of withrwaslasas'\n" \ + "request_reason: Please Withdraw this one since its no longer valid.\n" \ + "withdrawal_date: 2024-08-04 04:00:00.000000000 Z\n" \ + "edited_at: 2024-08-26 17:23:28.055850000 Z\n" \ + "request_issue_id: 251\n" \ + "request_type: withdrawal\n" \ + "benefit_type: vha\n" \ + "created_at: 2024-08-26 17:22:53.454663000 Z\n" \ + "decided_at: \n" \ + "decider_id: \n" \ + "decision_reason: \n" \ + "decision_review_id: 31\n" \ + "decision_review_type: SupplementalClaim\n" \ + "remove_original_issue: false\n" \ + "updated_at: 2024-08-26 17:23:28.072803000 Z\n" \ + "\"" + end + + before do + change_data["decided_at"] = Time.zone.parse("2023-10-21 22:47:16.233187") + change_data["next_decided_or_cancelled_at"] = out_of_bounds_time + change_data["next_created_at"] = out_of_bounds_time + # The event is generated via the versions so it needs to match the status instead of cancelled. + # It's not important for generation, but it's a more correct data state for a claim with a single approved IMR + change_data["issue_modification_request_status"] = "approved" + end + subject { described_class.create_edited_request_issue_events(change_data) } + + it "returns request_edited event type with multiple events" do + expect(subject.count).to be(4) + expect_attributes(subject[0], pending_attribute_data) + expect_attributes(subject[1], issue_modification_response_attribute) + expect_attributes(subject[3], issue_modification_edited_attribute) + expect_attributes(subject[2], in_progress_attribute_data) + end + end + describe "helper class methods" do describe ".retrieve_issue_data" do before do @@ -783,7 +1101,9 @@ event_instance.readable_task_status, event_instance.days_waiting, event_instance.readable_claim_type, event_instance.readable_facility_name, event_instance.readable_user_name, event_instance.readable_event_date, event_instance.readable_event_type, - event_instance.send(:issue_or_status_information), event_instance.send(:disposition_information) + event_instance.send(:issue_or_status_information), + event_instance.send(:issue_modification_request_information), + event_instance.send(:disposition_information) ] end @@ -921,7 +1241,16 @@ added_issue: "Added issue", withdrew_issue: "Withdrew issue", removed_issue: "Removed issue", - added_decision_date: "Added decision date" + added_decision_date: "Added decision date", + cancelled: "Claim closed", + addition: "Requested issue addition", + removal: "Requested issue removal", + modification: "Requested issue modification", + withdrawal: "Requested issue withdrawal", + request_approved: "Approval of request - issue addition", + request_denied: "Rejection of request - issue addition", + request_cancelled: "Cancellation of request", + request_edited: "Edit of request - issue addition" } event_types.each do |event_type, readable_name| @@ -1140,4 +1469,9 @@ def expect_attributes(object_instance, attribute_value_pairs) expect(object_instance.send(attribute)).to eq(expected_value) end end + + def verify_attributes_and_count(subject, num_of_records, attributes) + expect(subject.count).to eq(num_of_records) + expect_attributes(subject[0], attributes) + end end diff --git a/spec/services/claim_change_history/claim_history_service_spec.rb b/spec/services/claim_change_history/claim_history_service_spec.rb index fe244f251b5..c26c6f4d2e9 100644 --- a/spec/services/claim_change_history/claim_history_service_spec.rb +++ b/spec/services/claim_change_history/claim_history_service_spec.rb @@ -13,6 +13,14 @@ benefit_type: "vha", claimant_type: :dependent_claimant)) end + let!(:hlr_task_with_imr) do + create(:issue_modification_request, + :with_higher_level_review, + :edit_of_request, + :update_decider, + nonrating_issue_category: "Medical and Dental Care Reimbursement") + end + let!(:extra_hlr_request_issue) do create(:request_issue, nonrating_issue_category: "Camp Lejune Family Member", @@ -80,6 +88,8 @@ ] end + let(:total_event_count) { 22 } + let(:expected_sc_event_types) do [ :added_issue, @@ -88,6 +98,19 @@ ] end + let(:expected_imr_event_types) do + [ + :claim_creation, + :added_issue, + :in_progress, + :pending, + :addition, + :request_edited, + :request_approved, + :in_progress + ] + end + before do # Remove the versions to setup specific versions hlr_task.versions.each(&:delete) @@ -146,14 +169,14 @@ expect(events).to eq(service_instance.events) # Expect to get back all the combined event types - all_event_types = expected_hlr_event_types + expected_sc_event_types - expect(events.count).to eq(14) + all_event_types = expected_hlr_event_types + expected_sc_event_types + expected_imr_event_types + expect(events.count).to eq(total_event_count) expect(events.map(&:event_type)).to contain_exactly(*all_event_types) # Verify the issue data is correct for the completed_dispostion events disposition_events = events.select { |event| event.event_type == :completed_disposition } disposition_issue_types = ["Caregiver | Other", "Camp Lejune Family Member"] - disposition_issue_descriptions = ["VHA - Caregiver ", "Camp Lejune description"] + disposition_issue_descriptions = ["VHA - Caregiver", "Camp Lejune description"] disposition_user_names = ["Gaius Baelsar", "Gaius Baelsar"] disposition_values = %w[Granted denied] disposition_dates = [5.days.ago.to_date.to_s] * 2 @@ -165,9 +188,12 @@ expect(disposition_events.map(&:disposition_date)).to contain_exactly(*disposition_dates) # Verify the issue data is correct for all the add issue events - added_issue_types = [*disposition_issue_types, "CHAMPVA", "Beneficiary Travel"] - added_issue_descriptions = [*disposition_issue_descriptions, "Withdrew CHAMPVA", "VHA issue description "] - added_issue_user_names = ["Lauren Roth", "Lauren Roth", "Lauren Roth", "Eleanor Reynolds"] + added_issue_types = [*disposition_issue_types, "CHAMPVA", "Beneficiary Travel", "Caregiver | Other"] + added_issue_descriptions = [*disposition_issue_descriptions, + "Withdrew CHAMPVA", + "VHA issue description ", + "VHA - Caregiver"] + added_issue_user_names = ["Lauren Roth", "Lauren Roth", "Lauren Roth", "Eleanor Reynolds", "Lauren Roth"] add_issue_events = events.select do |event| event.event_type == :added_issue || event.event_type == :added_issue_without_decision_date end @@ -204,6 +230,353 @@ end end + context "issue modification edge cases" do + let!(:sc_task_with_imrs) do + create(:supplemental_claim_vha_task, + appeal: create(:supplemental_claim, + :with_vha_issue, + :with_intake, + benefit_type: "vha", + claimant_type: :veteran_claimant)) + end + + let(:request_issue) { sc_task_with_imrs.appeal.request_issues.first } + let(:supplemental_claim) { sc_task_with_imrs.appeal } + + let(:starting_imr_events) do + [:claim_creation, :added_issue, :in_progress, :removal, :pending, :addition] + end + + let!(:issue_modification_addition) do + create(:issue_modification_request, + request_type: "addition", + decision_review: supplemental_claim, + requestor: vha_user, + nonrating_issue_category: "CHAMPVA", + nonrating_issue_description: "Starting issue description", + decision_date: 5.days.ago) + end + + let(:issue_modification_modify) do + create(:issue_modification_request, + request_type: "modification", + decision_review: supplemental_claim, + requestor: vha_user, + request_issue: supplemental_claim.request_issues.first) + end + + # Only generate the events for this task to keep it focused on the issue modification request events + let!(:filters) { { task_id: [sc_task_with_imrs.id] } } + + let(:vha_admin) { create(:user, full_name: "VHA ADMIN", css_id: "VHAADMIN") } + let(:vha_user) { create(:user, full_name: "VHA USER", css_id: "VHAUSER") } + + before do + OrganizationsUser.make_user_admin(vha_admin, VhaBusinessLine.singleton) + VhaBusinessLine.singleton.add_user(vha_user) + Timecop.freeze(Time.zone.now) + end + + after do + Timecop.return + end + + def create_last_addition_and_verify_events(original_events, current_events) + new_events = current_events.dup + Timecop.travel(2.minutes.from_now) + addition = create(:issue_modification_request, request_type: "addition", decision_review: supplemental_claim) + + events = service_instance.build_events + new_events.push(:addition, :pending) + expect(events.map(&:event_type)).to contain_exactly(*original_events + new_events) + + # Approve the newest addition to make sure the in progress and approval events are correct + Timecop.travel(2.minutes.from_now) + addition.update!(decider: vha_admin, status: :approved, decision_reason: "Better reason2") + + events = service_instance.build_events + new_events.push(:in_progress, :request_approved) + expect(events.map(&:event_type)).to contain_exactly(*original_events + new_events) + end + + it "should correctly generate temporary in progress and pending events for a single imr event" do + events = subject + one_imr_events = *starting_imr_events - [:removal] + expect(events.map(&:event_type)).to contain_exactly(*one_imr_events) + + # Make an edit to the addition and make sure the events are correct + Timecop.travel(2.minutes.from_now) + issue_modification_addition.update!(edited_at: Time.zone.now, nonrating_issue_category: "CHAMPVA") + + # Rebuild events + service_instance.build_events + new_events = [:request_edited] + expect(events.map(&:event_type)).to contain_exactly(*one_imr_events + new_events) + + # Approve the addition and make sure the events are correct + # NOTE: This only does the issue modification events and does not create a request issue update + Timecop.travel(2.minutes.from_now) + issue_modification_addition.update!(decider: vha_admin, status: :approved, decision_reason: "Better reason") + + # Rebuild events + service_instance.build_events + new_events.push(:in_progress, :request_approved) + expect(events.map(&:event_type)).to contain_exactly(*one_imr_events + new_events) + + # Create another addition IMR to verify that the event sequence works through one more iteration + create_last_addition_and_verify_events(one_imr_events, new_events) + end + + it "should correctly generate events for an imr that is cancelled while another is added" do + events = subject + one_imr_events = *starting_imr_events - [:removal] + expect(events.map(&:event_type)).to contain_exactly(*one_imr_events) + + # Cancel the addition IMR at the same time as creating a new issue modification request to + # modify the existing request issue on the supplemental claim + Timecop.travel(2.minutes.from_now) + ActiveRecord::Base.transaction do + issue_modification_addition.update!(status: "cancelled") + issue_modification_modify + end + + # Rebuild events + service_instance.build_events + new_events = [:modification, :request_cancelled] + expect(events.map(&:event_type)).to contain_exactly(*one_imr_events + new_events) + + # Approve the modification to verify that it create a new in progress event and a denied event + Timecop.travel(2.minutes.from_now) + issue_modification_modify.update!(decider: vha_admin, status: :denied, decision_reason: "Better reason") + + # Rebuild events + service_instance.build_events + new_events.push(:in_progress, :request_denied) + expect(events.map(&:event_type)).to contain_exactly(*one_imr_events + new_events) + + # Create another addition IMR to verify that the event sequence works through one more iteration + create_last_addition_and_verify_events(one_imr_events, new_events) + end + + it "should correctly track the previous version data for multiple IMR edits" do + events = subject + one_imr_events = *starting_imr_events - [:removal] + expect(events.map(&:event_type)).to contain_exactly(*one_imr_events) + + # Edit several fields to create a new version of the IMR + Timecop.travel(2.minutes.from_now) + issue_modification_addition.update!(nonrating_issue_category: "Other", + nonrating_issue_description: "Edited description 1", + edited_at: Time.zone.now) + + # Rebuild events + service_instance.build_events + new_events = [:request_edited] + expect(events.map(&:event_type)).to contain_exactly(*one_imr_events + new_events) + + # Edit several fields to create a new version of the IMR + Timecop.travel(2.minutes.from_now) + issue_modification_addition.update!(nonrating_issue_description: "Edited description 2", + edited_at: Time.zone.now) + + # Rebuild events + service_instance.build_events + new_events.push(:request_edited) + expect(events.map(&:event_type)).to contain_exactly(*one_imr_events + new_events) + + # Verify that each of the edited events has the information from the previous version + edited_events = events.select { |event| event.event_type == :request_edited } + + first_edit = edited_events.first + second_edit = edited_events.last + + expect(first_edit).to have_attributes( + new_issue_description: "Edited description 1", + new_issue_type: "Other", + previous_issue_description: "Starting issue description", + previous_issue_type: "CHAMPVA" + ) + + expect(second_edit).to have_attributes( + new_issue_description: "Edited description 2", + new_issue_type: "Other", + previous_issue_description: "Edited description 1", + previous_issue_type: "Other" + ) + end + + context "starting with two imrs" do + let!(:issue_modification_removal) do + create(:issue_modification_request, + request_type: "removal", + request_issue: request_issue, + decision_review: supplemental_claim) + end + + it "should correctly generate temporary in progress events for two imrs created at the same time" do + events = subject + expect(events.map(&:event_type)).to contain_exactly(*starting_imr_events) + + # Deny the removal and make sure the events are correct + Timecop.travel(2.minutes.from_now) + issue_modification_removal.update!(decider: vha_admin, status: :denied, decision_reason: "Just cause") + + # Rebuild events + service_instance.build_events + new_events = [:request_denied] + expect(events.map(&:event_type)).to contain_exactly(*starting_imr_events + new_events) + + # Approve the addition and make sure the events are correct + # NOTE: This only does the issue modification events and does not create a request issue update + Timecop.travel(2.minutes.from_now) + issue_modification_addition.update!(decider: vha_admin, status: :approved, decision_reason: "Better reason") + + # Rebuild events + service_instance.build_events + new_events.push(:in_progress, :request_approved) + expect(events.map(&:event_type)).to contain_exactly(*starting_imr_events + new_events) + + # Create another addition IMR to verify that the event sequence works through one more iteration + create_last_addition_and_verify_events(starting_imr_events, new_events) + end + + it "should correctly generate temporary in progress events for two imrs decided at the same time" do + events = subject + expect(events.map(&:event_type)).to contain_exactly(*starting_imr_events) + + # Deny the removal and approve the addition and make sure the events are correct + Timecop.travel(2.minutes.from_now) + ActiveRecord::Base.transaction do + issue_modification_removal.update!(decider: vha_admin, status: :denied, decision_reason: "Just cause") + issue_modification_addition.update!(decider: vha_admin, status: :approved, decision_reason: "Better reason") + end + + # Rebuild events + service_instance.build_events + new_events = [:request_denied, :request_approved, :in_progress] + expect(events.map(&:event_type)).to contain_exactly(*starting_imr_events + new_events) + + # Create another addition IMR to verify that the event sequence works through one more iteration + Timecop.travel(2.minutes.from_now) + addition2 = create(:issue_modification_request, request_type: "addition", decision_review: supplemental_claim) + + service_instance.build_events + new_events.push(:addition, :pending) + expect(events.map(&:event_type)).to contain_exactly(*starting_imr_events + new_events) + + # Approve the newest addition to make sure the in progress and approval events are correct + Timecop.travel(2.minutes.from_now) + addition2.update!(decider: vha_admin, status: :approved, decision_reason: "Better reason2") + + service_instance.build_events + new_events.push(:in_progress, :request_approved) + expect(events.map(&:event_type)).to contain_exactly(*starting_imr_events + new_events) + + # Create another addition IMR to verify that the event sequence works through one more iteration + create_last_addition_and_verify_events(starting_imr_events, new_events) + end + + it "should correctly generate temporary in progress events for two imrs with one cancelled in reverse order" do + events = subject + expect(events.map(&:event_type)).to contain_exactly(*starting_imr_events) + + # Approve the addition and make sure the events are correct + # NOTE: This only does the issue modification events and does not create a request issue update + Timecop.travel(2.minutes.from_now) + issue_modification_addition.update!(decider: vha_admin, status: :approved, decision_reason: "Better reason") + + # Rebuild events + service_instance.build_events + new_events = [:request_approved] + expect(events.map(&:event_type)).to contain_exactly(*starting_imr_events + new_events) + + # Cancel the removal and make sure the events are correct + Timecop.travel(2.minutes.from_now) + issue_modification_removal.update!(decider: vha_admin, status: :cancelled, decision_reason: "Just cause") + + # Rebuild events + service_instance.build_events + new_events.push(:request_cancelled, :in_progress) + expect(events.map(&:event_type)).to contain_exactly(*starting_imr_events + new_events) + + # Create another addition IMR to verify that the event sequence works through one more iteration + create_last_addition_and_verify_events(starting_imr_events, new_events) + end + + it "when an imr is cancelled at the same time and another is created" do + events = subject + expect(events.map(&:event_type)).to contain_exactly(*starting_imr_events) + + # Approve the addition and make sure the events are correct + # NOTE: This only does the issue modification events and does not create a request issue update + Timecop.travel(2.minutes.from_now) + issue_modification_addition.update!(decider: vha_admin, status: :approved, decision_reason: "Better reason") + + # Rebuild events + service_instance.build_events + new_events = [:request_approved] + expect(events.map(&:event_type)).to contain_exactly(*starting_imr_events + new_events) + + # Cancel the removal and add a new approval at the same time + Timecop.travel(2.minutes.from_now) + addition2 = nil + ActiveRecord::Base.transaction do + issue_modification_removal.update!(decider: vha_admin, status: :cancelled, decision_reason: "Just cause") + addition2 = create(:issue_modification_request, + request_type: "addition", + decision_review: supplemental_claim) + end + + # Rebuild events + service_instance.build_events + new_events.push(:request_cancelled, :addition) + expect(events.map(&:event_type)).to contain_exactly(*starting_imr_events + new_events) + + # Approve the newest addition to make sure the in progress and approval events are correct + Timecop.travel(2.minutes.from_now) + addition2.update!(decider: vha_admin, status: :approved, decision_reason: "Better reason2") + + service_instance.build_events + new_events.push(:in_progress, :request_approved) + expect(events.map(&:event_type)).to contain_exactly(*starting_imr_events + new_events) + + # Create another addition IMR to verify that the event sequence works through one more iteration + create_last_addition_and_verify_events(starting_imr_events, new_events) + end + end + + context "with multiple text edit in for a withdrawal event" do + let!(:issue_modification_withdrawal) do + create(:issue_modification_request, + :withdrawal, + request_issue: request_issue, + decision_review: supplemental_claim, + request_reason: "first comment in the array", + nonrating_issue_description: "first nonrating description") + end + + let!(:issue_modification_edit_of_request_first) do + issue_modification_withdrawal.nonrating_issue_description = "this is first update" + issue_modification_withdrawal.updated_at = Time.zone.today + issue_modification_withdrawal.save! + end + + let!(:issue_modification_edit_of_request_second) do + issue_modification_withdrawal.nonrating_issue_description = "this is Second update" + issue_modification_withdrawal.withdrawal_date = Time.zone.today - 12.days + issue_modification_withdrawal.updated_at = Time.zone.today + issue_modification_withdrawal.save! + end + + it "should have two request of edit event and a withdrawal event" do + events = service_instance.build_events + starting_event_without_removal = *starting_imr_events - [:removal] + new_events = [:withdrawal, :request_edited, :request_edited] + expect(events.map(&:event_type)).to contain_exactly(*starting_event_without_removal + new_events) + end + end + end + context "with filters" do context "with task_id filter" do let(:filters) { { task_id: sc_task.id } } @@ -237,7 +610,10 @@ it "should only return events for tasks that match the claim type filter" do subject - expect(service_instance.events.map(&:event_type)).to contain_exactly(*expected_hlr_event_types) + expect(service_instance.events.map(&:event_type)).to contain_exactly( + *expected_hlr_event_types, + *expected_imr_event_types + ) end context "with no filter matches" do @@ -255,7 +631,10 @@ it "should only return events for the tasks that match the task status filter" do subject - expect(service_instance.events.map(&:event_type)).to contain_exactly(*expected_sc_event_types) + expect(service_instance.events.map(&:event_type)).to contain_exactly( + *expected_sc_event_types, + *expected_imr_event_types + ) end context "with no filter matches" do @@ -296,7 +675,7 @@ it "should return events without a disposition" do subject - expect(service_instance.events.count).to eq(14) + expect(service_instance.events.count).to eq(total_event_count) end end end @@ -308,7 +687,9 @@ subject expected_event_types = [ :added_issue, - :completed_disposition + :completed_disposition, + :added_issue, + :request_edited ] expect(service_instance.events.map(&:event_type)).to contain_exactly(*expected_event_types) end @@ -331,7 +712,9 @@ :added_issue, :completed_disposition, :added_issue, - :withdrew_issue + :withdrew_issue, + :added_issue, + :request_edited ] expect(service_instance.events.map(&:event_type)).to contain_exactly(*expected_event_types) end @@ -347,6 +730,7 @@ :added_issue, :added_issue, :added_issue, + :added_issue, :completed_disposition, :completed_disposition ] @@ -380,7 +764,8 @@ subject expect(service_instance.events.map(&:event_type)).to contain_exactly( *expected_hlr_event_types, - *expected_sc_event_types + *expected_sc_event_types, + *expected_imr_event_types ) end end @@ -408,7 +793,8 @@ ] expect(service_instance.events.map(&:event_type)).to contain_exactly( *filtered_hlr_event_types, - *expected_sc_event_types + *expected_sc_event_types, + *expected_imr_event_types ) end end @@ -432,7 +818,8 @@ subject expect(service_instance.events.map(&:event_type)).to contain_exactly( *(expected_hlr_event_types - [:claim_creation]), - *expected_sc_event_types + *expected_sc_event_types, + *expected_imr_event_types ) end @@ -542,7 +929,8 @@ it "should return all events" do subject expect(service_instance.events.map(&:event_type)).to contain_exactly(*expected_hlr_event_types, - *expected_sc_event_types) + *expected_sc_event_types, + *expected_imr_event_types) end end end @@ -553,7 +941,8 @@ it "should only return events for tasks that match the days waiting filter" do subject - expect(service_instance.events.map(&:event_type)).to contain_exactly(*expected_hlr_event_types) + expect(service_instance.events.map(&:event_type)).to contain_exactly(*expected_hlr_event_types, + *expected_imr_event_types) end end @@ -666,11 +1055,64 @@ subject expected_event_types = [ :completed, - :in_progress + :in_progress, + :request_approved ] expect(service_instance.events.map(&:event_type)).to contain_exactly(*expected_event_types) end end + + context "with issue modification request task id" do + let(:filters) { { task_id: hlr_task_with_imr.decision_review.tasks.ids[0] } } + it "should only return the filtered events for the specific task ids" do + subject + expect(service_instance.events.map(&:event_type)).to contain_exactly(*expected_imr_event_types) + end + end + + context "with multiple filters for task id and event" do + let(:filters) do + { task_id: hlr_task_with_imr.decision_review.tasks.ids[0], events: [:added_issue, :claim_creation] } + end + + it "should only return the filtered events for the specific task ids" do + subject + expect(service_instance.events.map(&:event_type)).to contain_exactly(:added_issue, :claim_creation) + end + end + + context "with multiple filters for task id and event" do + let(:filters) do + { task_id: hlr_task_with_imr.decision_review.tasks.ids[0], events: [:request_edited] } + end + + it "should only return the filtered events for the specific task ids" do + subject + expect(service_instance.events.map(&:event_type)).to contain_exactly(:request_edited) + end + end + + context "with multiple filters for task id and event" do + let(:filters) do + { task_id: hlr_task_with_imr.decision_review.tasks.ids[0], events: [:request_approved] } + end + + it "should only return the filtered events for the specific task ids" do + subject + expect(service_instance.events.map(&:event_type)).to contain_exactly(:request_approved) + end + end + + context "with multiple filters for task id and event" do + let(:filters) do + { task_id: hlr_task_with_imr.decision_review.tasks.ids[0], events: [:pending] } + end + + it "should only return the filtered events for the specific task ids" do + subject + expect(service_instance.events.map(&:event_type)).to contain_exactly(:pending) + end + end end end end