From d24ff4adf26d47881a37d70cf02c64ea6a143216 Mon Sep 17 00:00:00 2001 From: KonstantinShevtsov Date: Fri, 20 Sep 2024 15:47:11 -0400 Subject: [PATCH 1/9] issue parser applied --- app/models/request_issues_update_event.rb | 91 ++++++++++++----------- 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/app/models/request_issues_update_event.rb b/app/models/request_issues_update_event.rb index 242044e36e5..d16d4146cfc 100644 --- a/app/models/request_issues_update_event.rb +++ b/app/models/request_issues_update_event.rb @@ -153,46 +153,46 @@ def from_intake_data(data, decision_review: nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Layout/LineLength def attributes_from_intake_data(data) parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(data) - contested_issue_present = attributes_look_like_contested_issue?(data) - issue_text = data[parser_issue.ri_is_unidentified] ? data[parser_issue.ri_contested_issue_description] : nil + contested_issue_present = attributes_look_like_contested_issue?(parser_issue) + issue_text = parser_issue.ri_is_unidentified ? parser_issue.ri_contested_issue_description : nil { - benefit_type: data[parser_issue.ri_benefit_type], - closed_at: data[parser_issue.ri_closed_at], - closed_status: data[parser_issue.ri_closed_status], - contention_reference_id: data[parser_issue.ri_contention_reference_id], - contested_decision_issue_id: data[parser_issue.ri_contested_decision_issue_id], - contested_rating_issue_reference_id: data[parser_issue.ri_contested_rating_issue_reference_id], - contested_rating_issue_diagnostic_code: data[parser_issue.ri_contested_rating_issue_diagnostic_code], - contested_rating_decision_reference_id: data[parser_issue.ri_contested_rating_decision_reference_id], - contested_issue_description: contested_issue_present ? data[parser_issue.ri_contested_issue_description] : nil, - contested_rating_issue_profile_date: data[parser_issue.ri_contested_rating_issue_profile_date], - nonrating_issue_description: data[parser_issue.ri_nonrating_issue_category] ? data[parser_issue.ri_contested_issue_description] : nil, + benefit_type: parser_issue.ri_benefit_type, + withdrawal_date: parser_issue.ri_closed_at, + closed_status: parser_issue.ri_closed_status, + contention_reference_id: parser_issue.ri_contention_reference_id, + contested_decision_issue_id: parser_issue.ri_contested_decision_issue_id, + contested_rating_issue_reference_id: parser_issue.ri_contested_rating_issue_reference_id, + contested_rating_issue_diagnostic_code: parser_issue.ri_contested_rating_issue_diagnostic_code, + contested_rating_decision_reference_id: parser_issue.ri_contested_rating_decision_reference_id, + contested_issue_description: contested_issue_present ? parser_issue.ri_contested_issue_description : nil, + contested_rating_issue_profile_date: parser_issue.ri_contested_rating_issue_profile_date, + nonrating_issue_description: parser_issue.ri_nonrating_issue_category ? parser_issue.ri_contested_issue_description : nil, unidentified_issue_text: issue_text, - decision_date: data[parser_issue.ri_decision_date], - nonrating_issue_category: data[parser_issue.ri_nonrating_issue_category], - is_unidentified: data[parser_issue.ri_is_unidentified], - untimely_exemption: data[parser_issue.ri_untimely_exemption], - untimely_exemption_notes: data[parser_issue.ri_untimely_exemption_notes], - ramp_claim_id: data[parser_issue.ri_ramp_claim_id], - vacols_id: data[parser_issue.ri_vacols_id], - vacols_sequence_id: data[parser_issue.ri_vacols_sequence_id], - ineligible_reason: data[parser_issue.ri_ineligible_reason], - ineligible_due_to_id: data[parser_issue.ri_ineligible_due_to_id], - reference_id: data[parser_issue.ri_reference_id], - type: data[parser_issue.ri_type], - veteran_participant_id: data[parser_issue.ri_veteran_participant_id], - rating_issue_associated_at: data[parser_issue.ri_rating_issue_associated_at], - nonrating_issue_bgs_source: data[parser_issue.ri_nonrating_issue_bgs_source], - nonrating_issue_bgs_id: data[parser_issue.ri_nonrating_issue_bgs_id] + decision_date: parser_issue.ri_decision_date, + nonrating_issue_category: parser_issue.ri_nonrating_issue_category, + is_unidentified: parser_issue.ri_is_unidentified, + untimely_exemption: parser_issue.ri_untimely_exemption, + untimely_exemption_notes: parser_issue.ri_untimely_exemption_notes, + ramp_claim_id: parser_issue.ri_ramp_claim_id, + vacols_id: parser_issue.ri_vacols_id, + vacols_sequence_id: parser_issue.ri_vacols_sequence_id, + ineligible_reason: parser_issue.ri_ineligible_reason, + ineligible_due_to_id: parser_issue.ri_ineligible_due_to_id, + reference_id: parser_issue.ri_reference_id, + type: parser_issue.ri_type, + veteran_participant_id: parser_issue.ri_veteran_participant_id, + rating_issue_associated_at: parser_issue.ri_rating_issue_associated_at, + nonrating_issue_bgs_source: parser_issue.ri_nonrating_issue_bgs_source, + nonrating_issue_bgs_id: parser_issue.ri_nonrating_issue_bgs_id } end - def attributes_look_like_contested_issue?(data) - data[:ri_contested_rating_issue_reference_id] || - data[:ri_contested_decision_issue_id] || - data[:ri_contested_rating_decision_reference_id] || - data[:ri_contested_rating_issue_diagnostic_code] + def attributes_look_like_contested_issue?(parser_issue) + parser_issue.ri_contested_rating_issue_reference_id || + parser_issue.ri_contested_decision_issue_id || + parser_issue.ri_contested_rating_decision_reference_id || + parser_issue.ri_contested_rating_issue_diagnostic_code end # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Layout/LineLength @@ -217,14 +217,15 @@ def process_eligible_to_ineligible_issues! return if @eligible_to_ineligible_issue_data.empty? @eligible_to_ineligible_issue_data.each do |issue_data| - request_issue = review.request_issues.find_by(reference_id: issue_data[:reference_id]) + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) + request_issue = review.request_issues.find_by(reference_id: parser_issue.reference_id) unless request_issue - fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, issue_data[:reference_id] + fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.reference_id end request_issue.update( - ineligible_reason: issue_data[:ineligible_reason], - closed_at: issue_data[:closed_at], + ineligible_reason: parser_issue.ineligible_reason, + closed_at: parser_issue.closed_at, contention_reference_id: nil ) end @@ -234,9 +235,10 @@ def process_ineligible_to_eligible_issues! return if @ineligible_to_eligible_issue_data.empty? @ineligible_to_eligible_issue_data.each do |issue_data| - issue = review.request_issues.find_by(reference_id: issue_data[:reference_id]) + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) + issue = review.request_issues.find_by(reference_id: parser_issue.reference_id) unless issue - fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, issue_data[:reference_id] + fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.reference_id end issue.update(ineligible_reason: nil, closed_status: nil, closed_at: nil) @@ -247,14 +249,15 @@ def process_ineligible_to_ineligible_issues! return if @ineligible_to_ineligible_issue_data.empty? @ineligible_to_ineligible_issue_data.each do |issue_data| - issue = review.request_issues.find_by(reference_id: issue_data[:reference_id]) + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) + issue = review.request_issues.find_by(reference_id: parser_issue.reference_id) unless issue - fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, issue_data[:reference_id] + fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.reference_id end issue.update( - ineligible_reason: issue_data[:ineligible_reason], - closed_at: issue_data[:closed_at], + ineligible_reason: parser_issue.ineligible_reason, + closed_at: parser_issue.closed_at, contention_reference_id: nil ) end From 37417606f154b9f44a72879a0ca51af2446cefb7 Mon Sep 17 00:00:00 2001 From: Nader Kutub Date: Sun, 22 Sep 2024 19:56:41 -0700 Subject: [PATCH 2/9] refactor request_issues_update_event --- app/models/request_issues_update_event.rb | 306 +++--- app/models/request_issues_update_evnt.rb | 187 ++++ lib/caseflow/error.rb | 5 + .../request_issues_update_event_spec.rb | 935 +++++------------- .../models/request_issues_update_evnt_spec.rb | 291 ++++++ 5 files changed, 831 insertions(+), 893 deletions(-) create mode 100644 app/models/request_issues_update_evnt.rb create mode 100644 spec/models/request_issues_update_evnt_spec.rb diff --git a/app/models/request_issues_update_event.rb b/app/models/request_issues_update_event.rb index d16d4146cfc..62b5fc533e1 100644 --- a/app/models/request_issues_update_event.rb +++ b/app/models/request_issues_update_event.rb @@ -1,174 +1,162 @@ # frozen_string_literal: true class RequestIssuesUpdateEvent < RequestIssuesUpdate - # example of calling RequestIssuesUpdateEvent - # RequestIssuesUpdateEvent.new( - # user: user, - # review: review, - # parser: parser - # ) - - def initialize(user:, review:, parser:) + def initialize(review:, user:, parser:) @parser = parser - super(user: user, review: review) - - @eligible_to_ineligible_issue_data = parser.eligible_to_ineligible_issues - @ineligible_to_ineligible_issue_data = parser.ineligible_to_ineligible_issues - @ineligible_to_eligible_issue_data = parser.ineligible_to_eligible_issues - @withdrawn_issue_data = parser.withdrawn_issues - @edited_issue_data = parser.updated_issues - @removed_issue_data = parser.removed_issues - @added_issue_data = parser.added_issues + @request_issues_data = build_request_issues_data + super( + user: user, + review: review, + request_issues_data: @request_issues_data + ) end - def perform! - return false unless validate_before_perform - return false if processed? + # closures havppen by calling liunes similar to that in EP establishment line 460 + # should not add closure issues becused the base class calcuates the delta from before and after + # Question: should there be handling for corrected issues? + # Contentions are never removed, contention_removed_date is set instead + # do we need to update attributes for eligibility change issues or just status? - transaction do - process_issues! - # updates rating_issue_associated_at of review's issues to nil - review.mark_rating_request_issues_to_reassociate! - update!( - before_request_issue_ids: before_issues.map(&:reference_id), - after_request_issue_ids: after_issues.map(&:reference_id), - withdrawn_request_issue_ids: withdrawn_issues.map(&:reference_id), - edited_request_issue_ids: edited_issues.map(&:reference_id) - ) + def perform! + # Call the base class's perform! method + result = super + if result + remove_request_issues_with_no_decision! + process_eligible_to_ineligible_issues! + process_ineligible_to_eligible_issues! + process_ineligible_to_ineligible_issues! + true + else + false end - - process_job - - true end - def process_issues! - process_added_issues! - process_removed_issues! - process_legacy_issues! - process_withdrawn_issues! - process_edited_issues! - process_eligible_to_ineligible_issues! - process_ineligible_to_eligible_issues! - process_ineligible_to_ineligible_issues! - end - - def process_added_issues! - review.create_issues!(added_issues, self) - end - - def process_removed_issues! - return if removed_issues.nil? - - removed_issues.each(&:remove!) - end - - def added_issues - calculate_added_issues - end - - def removed_issues - return if @removed_issue_data.empty? + def process_eligible_to_ineligible_issues! + return if @parser.eligible_to_ineligible_issues.empty? - @removed_issue_data.map do |issue_data| - issue = review.request_issues.find_by(reference_id: issue_data[:reference_id]) - unless issue - fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, issue_data[:reference_id] + @parser.eligible_to_ineligible_issues.each do |issue_data| + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) + request_issue = review.request_issues.find_by(reference_id: parser_issue.ri_reference_id) + unless request_issue + fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.ri_reference_id end - issue + request_issue.update( + ineligible_reason: parser_issue.ri_ineligible_reason, + closed_at: parser_issue.ri_closed_at, + ) + RequestIssueContention.new(request_issue).remove! end end - def withdrawn_issues - @withdrawn_issues ||= withdrawn_request_issue_ids ? fetch_withdrawn_issues : calculate_withdrawn_issues - end + def process_ineligible_to_eligible_issues! + return if @parser.ineligible_to_eligible_issues.empty? - def all_updated_issues - (added_issues || []) + (removed_issues || []) + (withdrawn_issues || []) + (edited_issues || []) + @parser.ineligible_to_eligible_issues.each do |issue_data| + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) + request_issue = review.request_issues.find_by(reference_id: parser_issue.ri_reference_id) + unless request_issue + fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.ri_reference_id + end + request_issue.update( + ineligible_reason: nil, + closed_status: nil, + closed_at: nil + ) + end end - private - - def process_edited_issues! - return if @edited_issue_data.empty? + def process_ineligible_to_ineligible_issues! + return if @parser.ineligible_to_ineligible_issues.empty? - @edited_issue_data.each do |issue_data| - request_issue = review.request_issues.find_by(reference_id: issue_data[:reference_id]) + @parser.ineligible_to_ineligible_issues.each do |issue_data| + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) + request_issue = review.request_issues.find_by(reference_id: parser_issue.ri_reference_id) unless request_issue - fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, issue_data[:reference_id] + fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.ri_reference_id end - request_issue.save_edited_contention_text!(issue_data[:edited_description]) - request_issue.save_decision_date!(issue_data[:decision_date]) if issue_data[:decision_date] + request_issue.update( + ineligible_reason: parser_issue.ri_ineligible_reason, + closed_at: parser_issue.ri_closed_at, + ) end end - def changes? - all_updated_issues.any? - end + # check to see if the there are closed issues that are deferent from before - after + # if so, then raise an error - def calculate_after_issues - (before_issues || []) + (added_issues || []) - (removed_issues || []) - end + def remove_request_issues_with_no_decision! + return if @parser.removed_issues.empty? - def calculate_edited_issues - calculate_issues(@edited_issue_data) + check_for_mismatched_closed_issues! + @parser.removed_issues.each do |issue| + RequestIssueClosure.new(issue).with_no_decision! + end end - def calculate_added_issues - calculate_issues(@added_issue_data) - end + def check_for_mismatched_closed_issues! + parser_removed_issues = @parser.removed_issues.map do |issue| + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue) + parser_issue.ri_reference_id + end + base_removed_issues = removed_issues.map(&:reference_id) - def calculate_withdrawn_issues - calculate_issues(@withdrawn_issue_data) - end + # Check for issues in parser.removed_issues but not in base removed_issues + parser_only = parser_removed_issues - base_removed_issues + # Check for issues in base removed_issues but not in parser.removed_issues + base_only = base_removed_issues - parser_removed_issues - def calculate_issues(issues_data) - issues_data.map do |issue_data| - find_or_build_request_issue_from_intake_data(issue_data) + if parser_only.any? || base_only.any? + raise Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError, + "Mismatched removed issues: CaseFlow only = #{base_only.join(', ')} - Event only = #{parser_only.join(', ')}" end + true end - def find_or_build_request_issue_from_intake_data(issue_data) - # find exising issue or build a new one - # this method is based on the find_or_build_request_issue_from_intake_data - # method in the DecisionReview model which uses :requested_issue_id key - # when in our case parser returns issue_data with :reference_id key - begin - return review.request_issues.find_by(reference_id: issue_data[:reference_id]) if issue_data[:reference_id] - rescue ActiveRecord::RecordNotFound - raise Caseflow::Error::DecisionReviewUpdateMissingIssueError, issue_data[:reference_id] + # Add any other methods you need + def build_request_issues_data + @request_issues_data = [] + + # Handle updated issues + @parser.updated_issues.each do |issue| + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue) + @request_issues_data << build_issue_data(parser_issue: parser_issue) end - from_intake_data(issue_data, decision_review: review) - end - def from_intake_data(data, decision_review: nil) - attrs = attributes_from_intake_data(data) - attrs = attrs.merge(decision_review: decision_review) if decision_review + # Handle withdrawn issues + @parser.withdrawn_issues.each do |issue| + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue) + @request_issues_data << build_issue_data(parser_issue: parser_issue, is_withdrawn: true) + end + + # Handle added issues + @parser.added_issues.each do |issue| + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue) + @request_issues_data << build_issue_data(parser_issue: parser_issue, is_new: true) + end - RequestIssue.new(attrs).tap(&:validate_eligibility!) + @request_issues_data end - # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Layout/LineLength - def attributes_from_intake_data(data) - parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(data) - contested_issue_present = attributes_look_like_contested_issue?(parser_issue) - issue_text = parser_issue.ri_is_unidentified ? parser_issue.ri_contested_issue_description : nil + def build_issue_data(parser_issue:, is_withdrawn: false, is_new: false) + return {} if parser_issue.nil? { + request_issue_id: is_new ? nil : find_request_issue_id(parser_issue), benefit_type: parser_issue.ri_benefit_type, - withdrawal_date: parser_issue.ri_closed_at, + closed_date: parser_issue.ri_closed_at, + withdrawal_date: is_withdrawn ? parser_issue.ri_closed_at : nil, closed_status: parser_issue.ri_closed_status, contention_reference_id: parser_issue.ri_contention_reference_id, contested_decision_issue_id: parser_issue.ri_contested_decision_issue_id, contested_rating_issue_reference_id: parser_issue.ri_contested_rating_issue_reference_id, contested_rating_issue_diagnostic_code: parser_issue.ri_contested_rating_issue_diagnostic_code, contested_rating_decision_reference_id: parser_issue.ri_contested_rating_decision_reference_id, - contested_issue_description: contested_issue_present ? parser_issue.ri_contested_issue_description : nil, + contested_issue_description: parser_issue.ri_contested_issue_description, contested_rating_issue_profile_date: parser_issue.ri_contested_rating_issue_profile_date, nonrating_issue_description: parser_issue.ri_nonrating_issue_category ? parser_issue.ri_contested_issue_description : nil, - unidentified_issue_text: issue_text, + unidentified_issue_text: parser_issue.ri_unidentified_issue_text, decision_date: parser_issue.ri_decision_date, nonrating_issue_category: parser_issue.ri_nonrating_issue_category, is_unidentified: parser_issue.ri_is_unidentified, @@ -188,78 +176,12 @@ def attributes_from_intake_data(data) } end - def attributes_look_like_contested_issue?(parser_issue) - parser_issue.ri_contested_rating_issue_reference_id || - parser_issue.ri_contested_decision_issue_id || - parser_issue.ri_contested_rating_decision_reference_id || - parser_issue.ri_contested_rating_issue_diagnostic_code - end - # rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Layout/LineLength - - def fetch_withdrawn_issues - RequestIssue.where(reference_id: withdrawn_request_issue_ids) - end - - def process_withdrawn_issues! - return if @withdrawn_issue_data.empty? - - @withdrawn_issue_data.each do |withdrawn_issue| - issue = request_issue = RequestIssue.find_by(reference_id: withdrawn_issue[:reference_id].to_s) - unless issue - fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, withdrawn_issue[:reference_id] - end - - request_issue.withdraw!(withdrawn_issue[:closed_at]) - end - end - - def process_eligible_to_ineligible_issues! - return if @eligible_to_ineligible_issue_data.empty? - - @eligible_to_ineligible_issue_data.each do |issue_data| - parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) - request_issue = review.request_issues.find_by(reference_id: parser_issue.reference_id) - unless request_issue - fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.reference_id - end - - request_issue.update( - ineligible_reason: parser_issue.ineligible_reason, - closed_at: parser_issue.closed_at, - contention_reference_id: nil - ) - end - end - - def process_ineligible_to_eligible_issues! - return if @ineligible_to_eligible_issue_data.empty? - - @ineligible_to_eligible_issue_data.each do |issue_data| - parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) - issue = review.request_issues.find_by(reference_id: parser_issue.reference_id) - unless issue - fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.reference_id - end - - issue.update(ineligible_reason: nil, closed_status: nil, closed_at: nil) - end - end - - def process_ineligible_to_ineligible_issues! - return if @ineligible_to_ineligible_issue_data.empty? - - @ineligible_to_ineligible_issue_data.each do |issue_data| - parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) - issue = review.request_issues.find_by(reference_id: parser_issue.reference_id) - unless issue - fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.reference_id - end - - issue.update( - ineligible_reason: parser_issue.ineligible_reason, - closed_at: parser_issue.closed_at, - contention_reference_id: nil - ) + def find_request_issue_id(parser_issue) + request_issue = RequestIssue.find_by(reference_id: parser_issue.ri_reference_id) + if request_issue + request_issue.id + else + raise(Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.ri_reference_id) end end end diff --git a/app/models/request_issues_update_evnt.rb b/app/models/request_issues_update_evnt.rb new file mode 100644 index 00000000000..d2bb4fe09b1 --- /dev/null +++ b/app/models/request_issues_update_evnt.rb @@ -0,0 +1,187 @@ +# frozen_string_literal: true + +class RequestIssuesUpdateEvnt < RequestIssuesUpdate + def initialize(review:, user:, parser:) + @parser = parser + @request_issues_data = build_request_issues_data + super( + user: user, + review: review, + request_issues_data: @request_issues_data + ) + end + + # closures havppen by calling liunes similar to that in EP establishment line 460 + # should not add closure issues becused the base class calcuates the delta from before and after + # Question: should there be handling for corrected issues? + # Contentions are never removed, contention_removed_date is set instead + # do we need to update attributes for eligibility change issues or just status? + + def perform! + # Call the base class's perform! method + result = super + if result + remove_request_issues_with_no_decision! + process_eligible_to_ineligible_issues! + process_ineligible_to_eligible_issues! + process_ineligible_to_ineligible_issues! + true + else + false + end + end + + def process_eligible_to_ineligible_issues! + return if @parser.eligible_to_ineligible_issues.empty? + + @parser.eligible_to_ineligible_issues.each do |issue_data| + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) + request_issue = review.request_issues.find_by(reference_id: parser_issue.ri_reference_id) + unless request_issue + fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.ri_reference_id + end + + request_issue.update( + ineligible_reason: parser_issue.ri_ineligible_reason, + closed_at: parser_issue.ri_closed_at, + ) + RequestIssueContention.new(request_issue).remove! + end + end + + def process_ineligible_to_eligible_issues! + return if @parser.ineligible_to_eligible_issues.empty? + + @parser.ineligible_to_eligible_issues.each do |issue_data| + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) + request_issue = review.request_issues.find_by(reference_id: parser_issue.ri_reference_id) + unless request_issue + fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.ri_reference_id + end + request_issue.update( + ineligible_reason: nil, + closed_status: nil, + closed_at: nil + ) + end + end + + def process_ineligible_to_ineligible_issues! + return if @parser.ineligible_to_ineligible_issues.empty? + + @parser.ineligible_to_ineligible_issues.each do |issue_data| + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) + request_issue = review.request_issues.find_by(reference_id: parser_issue.ri_reference_id) + unless request_issue + fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.ri_reference_id + end + + request_issue.update( + ineligible_reason: parser_issue.ri_ineligible_reason, + closed_at: parser_issue.ri_closed_at, + ) + end + end + + # check to see if the there are closed issues that are deferent from before - after + # if so, then raise an error + + def remove_request_issues_with_no_decision! + return if @parser.removed_issues.empty? + + check_for_mismatched_closed_issues! + @parser.removed_issues.each do |issue| + RequestIssueClosure.new(issue).with_no_decision! + end + end + + def check_for_mismatched_closed_issues! + parser_removed_issues = @parser.removed_issues.map do |issue| + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue) + parser_issue.ri_reference_id + end + base_removed_issues = removed_issues.map(&:reference_id) + + # Check for issues in parser.removed_issues but not in base removed_issues + parser_only = parser_removed_issues - base_removed_issues + # Check for issues in base removed_issues but not in parser.removed_issues + base_only = base_removed_issues - parser_removed_issues + + if parser_only.any? || base_only.any? + raise Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError, + "Mismatched removed issues: CaseFlow only = #{base_only.join(', ')} - Event only = #{parser_only.join(', ')}" + end + true + end + + # Add any other methods you need + def build_request_issues_data + @request_issues_data = [] + + # Handle updated issues + @parser.updated_issues.each do |issue| + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue) + @request_issues_data << build_issue_data(parser_issue: parser_issue) + end + + # Handle withdrawn issues + @parser.withdrawn_issues.each do |issue| + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue) + @request_issues_data << build_issue_data(parser_issue: parser_issue, is_withdrawn: true) + end + + # Handle added issues + @parser.added_issues.each do |issue| + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue) + @request_issues_data << build_issue_data(parser_issue: parser_issue, is_new: true) + end + + @request_issues_data + end + + def build_issue_data(parser_issue:, is_withdrawn: false, is_new: false) + return {} if parser_issue.nil? + + { + request_issue_id: is_new ? nil : find_request_issue_id(parser_issue), + benefit_type: parser_issue.ri_benefit_type, + closed_date: parser_issue.ri_closed_at, + withdrawal_date: is_withdrawn ? parser_issue.ri_closed_at : nil, + closed_status: parser_issue.ri_closed_status, + contention_reference_id: parser_issue.ri_contention_reference_id, + contested_decision_issue_id: parser_issue.ri_contested_decision_issue_id, + contested_rating_issue_reference_id: parser_issue.ri_contested_rating_issue_reference_id, + contested_rating_issue_diagnostic_code: parser_issue.ri_contested_rating_issue_diagnostic_code, + contested_rating_decision_reference_id: parser_issue.ri_contested_rating_decision_reference_id, + contested_issue_description: parser_issue.ri_contested_issue_description, + contested_rating_issue_profile_date: parser_issue.ri_contested_rating_issue_profile_date, + nonrating_issue_description: parser_issue.ri_nonrating_issue_category ? parser_issue.ri_contested_issue_description : nil, + unidentified_issue_text: parser_issue.ri_unidentified_issue_text, + decision_date: parser_issue.ri_decision_date, + nonrating_issue_category: parser_issue.ri_nonrating_issue_category, + is_unidentified: parser_issue.ri_is_unidentified, + untimely_exemption: parser_issue.ri_untimely_exemption, + untimely_exemption_notes: parser_issue.ri_untimely_exemption_notes, + ramp_claim_id: parser_issue.ri_ramp_claim_id, + vacols_id: parser_issue.ri_vacols_id, + vacols_sequence_id: parser_issue.ri_vacols_sequence_id, + ineligible_reason: parser_issue.ri_ineligible_reason, + ineligible_due_to_id: parser_issue.ri_ineligible_due_to_id, + reference_id: parser_issue.ri_reference_id, + type: parser_issue.ri_type, + veteran_participant_id: parser_issue.ri_veteran_participant_id, + rating_issue_associated_at: parser_issue.ri_rating_issue_associated_at, + nonrating_issue_bgs_source: parser_issue.ri_nonrating_issue_bgs_source, + nonrating_issue_bgs_id: parser_issue.ri_nonrating_issue_bgs_id + } + end + + def find_request_issue_id(parser_issue) + request_issue = RequestIssue.find_by(reference_id: parser_issue.ri_reference_id) + if request_issue + request_issue.id + else + raise(Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.ri_reference_id) + end + end +end diff --git a/lib/caseflow/error.rb b/lib/caseflow/error.rb index 8e8482ac69b..66958c14e7f 100644 --- a/lib/caseflow/error.rb +++ b/lib/caseflow/error.rb @@ -511,6 +511,11 @@ def initialize(reference_id) super("Request issue not found for REFERENCE_ID: #{reference_id}") end end + class DecisionReviewUpdateMismatchedRemovedIssuesError < StandardError + def initialize(msg = "Decision review update mismatched removed issues") + super(msg) + end + end class DecisionReviewUpdatedClaimReviewError < StandardError; end class DecisionReviewUpdatedEndProductEstablishmentError < StandardError; end class MaximumBatchSizeViolationError < StandardError diff --git a/spec/models/request_issues_update_event_spec.rb b/spec/models/request_issues_update_event_spec.rb index 48941f90c70..5dddeaf1c96 100644 --- a/spec/models/request_issues_update_event_spec.rb +++ b/spec/models/request_issues_update_event_spec.rb @@ -2,757 +2,290 @@ RSpec.describe RequestIssuesUpdateEvent, type: :model do let(:user) { create(:user) } - let(:review) { create(:supplemental_claim) } - + let(:review) { create(:higher_level_review) } + let!(:existing_request_issue) { create(:request_issue, decision_review: review, reference_id: "some_reference_id") } let(:parser) do - json_path = Rails.root.join("app", - "services", "events", "decision_review_updated", - "decision_review_updated_example.json") - json_content = File.read(json_path) - Events::DecisionReviewUpdated::DecisionReviewUpdatedParser.new(nil, JSON.parse(json_content)) - end - - subject do - described_class.new( - user: user, - review: review, - parser: parser - ) - end - - describe "#perform!" do - context "when valid and not processed" do - before do - allow(subject).to receive(:validate_before_perform).and_return(true) - allow(subject).to receive(:processed?).and_return(false) - allow(subject).to receive(:transaction).and_yield - allow(subject).to receive(:process_job).and_return(true) - end - - it "processes issues and returns true" do - expect(subject).to receive(:process_issues!) - expect(subject.perform!).to be true - end - - it "updates the review and issues" do - allow(subject).to receive(:process_issues!) - expect(subject).to receive(:update!).with( - before_request_issue_ids: anything, - after_request_issue_ids: anything, - withdrawn_request_issue_ids: anything, - edited_request_issue_ids: anything - ) - subject.perform! - end - end - - context "when validation fails" do - it "returns false" do - allow(subject).to receive(:validate_before_perform).and_return(false) - expect(subject.perform!).to be false - end - end - end - - describe "#after_issues" do - it "calculates or fetches the after issues" do - allow(subject).to receive(:after_request_issue_ids).and_return(nil) - expect(subject).to receive(:calculate_after_issues).and_return([]) - subject.after_issues - end - end - - describe "#edited_issues" do - it "calculates or fetches the edited issues" do - allow(subject).to receive(:edited_request_issue_ids).and_return(nil) - expect(subject).to receive(:calculate_edited_issues).and_return([]) - subject.edited_issues - end - end - - describe "#added_issues" do - it "calculates the added issues" do - expect(subject).to receive(:calculate_added_issues).and_return(parser.added_issues) - subject.added_issues + instance_double(Events::DecisionReviewUpdated::DecisionReviewUpdatedParser).tap do |parser| + allow(parser).to receive(:updated_issues).and_return([]) + allow(parser).to receive(:withdrawn_issues).and_return([]) + allow(parser).to receive(:added_issues).and_return([]) + allow(parser).to receive(:removed_issues).and_return([]) end end - describe "#removed_issues" do - it "calculates the removed issues" do - expect(subject).to receive(:removed_issues).and_return(parser.removed_issues) - subject.removed_issues + let(:parser_issue) do + instance_double(Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser).tap do |issue| + allow(issue).to receive(:ri_reference_id).and_return("some_reference_id") + allow(issue).to receive(:ri_benefit_type).and_return("some_benefit_type") + allow(issue).to receive(:ri_closed_at).and_return(Time.zone.now) + allow(issue).to receive(:ri_closed_status).and_return("some_status") + allow(issue).to receive(:ri_contested_issue_description).and_return("some_description") + allow(issue).to receive(:ri_contention_reference_id).and_return("some_contention_id") + allow(issue).to receive(:ri_contested_rating_issue_diagnostic_code).and_return("some_diagnostic_code") + allow(issue).to receive(:ri_contested_rating_decision_reference_id).and_return("some_decision_id") + allow(issue).to receive(:ri_contested_rating_issue_profile_date).and_return(Time.zone.today) + allow(issue).to receive(:ri_contested_rating_issue_reference_id).and_return("some_issue_id") + allow(issue).to receive(:ri_contested_decision_issue_id).and_return("some_decision_issue_id") + allow(issue).to receive(:ri_decision_date).and_return(Time.zone.today) + allow(issue).to receive(:ri_ineligible_due_to_id).and_return("some_ineligible_id") + allow(issue).to receive(:ri_ineligible_reason).and_return("some_reason") + allow(issue).to receive(:ri_is_unidentified).and_return(false) + allow(issue).to receive(:ri_unidentified_issue_text).and_return("some_text") + allow(issue).to receive(:ri_nonrating_issue_category).and_return("some_category") + allow(issue).to receive(:ri_nonrating_issue_description).and_return("some_description") + allow(issue).to receive(:ri_nonrating_issue_bgs_id).and_return("some_bgs_id") + allow(issue).to receive(:ri_nonrating_issue_bgs_source).and_return("some_source") + allow(issue).to receive(:ri_ramp_claim_id).and_return("some_claim_id") + allow(issue).to receive(:ri_rating_issue_associated_at).and_return(Time.zone.now) + allow(issue).to receive(:ri_untimely_exemption).and_return(false) + allow(issue).to receive(:ri_untimely_exemption_notes).and_return("some_notes") + allow(issue).to receive(:ri_vacols_id).and_return("some_vacols_id") + allow(issue).to receive(:ri_vacols_sequence_id).and_return("some_sequence_id") + allow(issue).to receive(:ri_veteran_participant_id).and_return("some_participant_id") + allow(issue).to receive(:ri_type).and_return("some_type") + allow(issue).to receive(:ri_decision).and_return("some_decision") end end - describe "#withdrawn_issues" do - it "calculates or fetches the withdrawn issues" do - allow(subject).to receive(:withdrawn_request_issue_ids).and_return(nil) - expect(subject).to receive(:calculate_withdrawn_issues).and_return(parser.withdrawn_issues) - subject.withdrawn_issues - end + let(:issue_payload) do + { + decision_review_issue_id: "some_reference_id", + benefit_type: "compensation", + closed_at: 1_625_151_600, + closed_status: "withdrawn", + contention_reference_id: 7_905_752, + contested_decision_issue_id: 201, + contested_issue_description: "Service connection for PTSD", + contested_rating_decision_reference_id: nil, + contested_rating_issue_diagnostic_code: "9411", + contested_rating_issue_profile_date: 1_625_076_000, + contested_rating_issue_reference_id: "REF9411", + type: "RequestIssue", + decision: [ + { + award_event_id: 679, + category: "decision", + contention_id: 35, + decision_finalized_time: nil, + decision_recorded_time: nil, + decision_source: "the source", + decision_text: "", + description: nil, + disposition: nil, + dta_error_explanation: nil, + id: 1738, + rating_profile_date: nil + } + ], + decision_date: 19_568, + ineligible_due_to_id: 301, + ineligible_reason: nil, + is_unidentified: false, + nonrating_issue_bgs_id: "13", + nonrating_issue_bgs_source: "CORP_AWARD_ATTORNEY_FEE", + nonrating_issue_category: "Accrued Benefits", + nonrating_issue_description: "Chapter 35 benefits", + ramp_claim_id: "RAMP123", + rating_issue_associated_at: 1_625_076_000, + unidentified_issue_text: nil, + untimely_exemption: nil, + untimely_exemption_notes: nil, + vacols_id: "VAC123", + vacols_sequence_id: nil + } end - describe "#validate_before_perform" do - context "when there are no changes" do - it "sets error_code to :no_changes and returns false" do - allow(subject).to receive(:changes?).and_return(false) - expect(subject.send(:validate_before_perform)).to be false - expect(subject.error_code).to eq(:no_changes) - end - end - end - - describe "#perform!" do - context "when only added issues are present" do - before do - subject.instance_variable_set(:@added_issue_data, parser.added_issues) - subject.instance_variable_set(:@removed_issue_data, []) - subject.instance_variable_set(:@edited_issue_data, []) - subject.instance_variable_set(:@withdrawn_issue_data, []) - - allow(subject).to receive(:validate_before_perform).and_return(true) - allow(subject).to receive(:processed?).and_return(false) - allow(subject).to receive(:transaction).and_yield - allow(subject).to receive(:process_job).and_return(true) - end - - it "processes only the added issues and returns true" do - expect(subject).to receive(:process_issues!) - expect(subject.perform!).to be true - end - - it "updates the review and issues with added issues only" do - allow(subject).to receive(:process_issues!) - expect(subject).to receive(:update!).with( - before_request_issue_ids: anything, - after_request_issue_ids: anything, - withdrawn_request_issue_ids: anything, - edited_request_issue_ids: anything - ) - subject.perform! - end + describe "#initialize" do + it "calls build_request_issues_data" do + expect_any_instance_of(described_class).to receive(:build_request_issues_data) + described_class.new(review: review, user: user, parser: parser) end end - describe "#calculate_added_issues" do - let(:multiple_issue_data) { parser.added_issues + parser.added_issues } # Simulating multiple issues - - before do - subject.instance_variable_set(:@added_issue_data, multiple_issue_data) + describe "#find_request_issue_id" do + it "returns the request issue id" do + result = described_class.new(review: review, user: user, parser: parser).find_request_issue_id(parser_issue) + expect(result).to eq(existing_request_issue.id) end - it "processes all added issues and returns the correct count" do - added_issues = subject.send(:calculate_added_issues) - expect(added_issues.size).to eq(2) + it "raises an error if the request issue is not found" do + allow(RequestIssue).to receive(:find_by).and_return(nil) + expect do + described_class.new(review: review, user: user, parser: parser).find_request_issue_id(parser_issue) + end.to raise_error(Caseflow::Error::DecisionReviewUpdateMissingIssueError) end end - context "when multiple types of issues are present" do - before do - subject.instance_variable_set(:@added_issue_data, parser.added_issues) - subject.instance_variable_set(:@removed_issue_data, parser.removed_issues) - subject.instance_variable_set(:@edited_issue_data, parser.updated_issues) - subject.instance_variable_set(:@withdrawn_issue_data, parser.withdrawn_issues) - - allow(subject).to receive(:validate_before_perform).and_return(true) - allow(subject).to receive(:processed?).and_return(false) - allow(subject).to receive(:transaction).and_yield + describe "#build_issue_data" do + it "returns an empty hash if the parser issue is nil" do + result = described_class.new(review: review, user: user, parser: parser).build_issue_data(parser_issue: nil) + expect(result).to eq({}) end - it "processes all types of issues and returns true" do - expect(subject).to receive(:process_issues!) - expect(subject.perform!).to be true - end - end - - context "when an exception occurs during transaction" do - before do - allow(subject).to receive(:validate_before_perform).and_return(true) - allow(subject).to receive(:processed?).and_return(false) - allow(subject).to receive(:transaction).and_raise(StandardError) - end - - it "raises an error and does not commit changes" do - expect { subject.perform! }.to raise_error(StandardError) - expect(subject).not_to receive(:process_issues!) - end - end - - context "when validation fails" do - before do - allow(subject).to receive(:validate_before_perform).and_return(false) + it "returns a hash of request issue data" do + result = described_class.new(review: review, user: user, parser: parser).build_issue_data( + parser_issue: parser_issue + ) + expect(result).to eq( + { + request_issue_id: existing_request_issue.id, + benefit_type: parser_issue.ri_benefit_type, + closed_date: parser_issue.ri_closed_at, + withdrawal_date: nil, + closed_status: parser_issue.ri_closed_status, + contention_reference_id: parser_issue.ri_contention_reference_id, + contested_decision_issue_id: parser_issue.ri_contested_decision_issue_id, + contested_rating_issue_reference_id: parser_issue.ri_contested_rating_issue_reference_id, + contested_rating_issue_diagnostic_code: parser_issue.ri_contested_rating_issue_diagnostic_code, + contested_rating_decision_reference_id: parser_issue.ri_contested_rating_decision_reference_id, + contested_rating_issue_profile_date: parser_issue.ri_contested_rating_issue_profile_date, + contested_issue_description: parser_issue.ri_contested_issue_description, + unidentified_issue_text: parser_issue.ri_unidentified_issue_text, + decision_date: parser_issue.ri_decision_date, + nonrating_issue_category: parser_issue.ri_nonrating_issue_category, + nonrating_issue_description: parser_issue.ri_nonrating_issue_description, + is_unidentified: parser_issue.ri_is_unidentified, + untimely_exemption: parser_issue.ri_untimely_exemption, + untimely_exemption_notes: parser_issue.ri_untimely_exemption_notes, + ramp_claim_id: parser_issue.ri_ramp_claim_id, + vacols_id: parser_issue.ri_vacols_id, + vacols_sequence_id: parser_issue.ri_vacols_sequence_id, + ineligible_reason: parser_issue.ri_ineligible_reason, + ineligible_due_to_id: parser_issue.ri_ineligible_due_to_id, + reference_id: parser_issue.ri_reference_id, + type: parser_issue.ri_type, + veteran_participant_id: parser_issue.ri_veteran_participant_id, + rating_issue_associated_at: parser_issue.ri_rating_issue_associated_at, + nonrating_issue_bgs_source: parser_issue.ri_nonrating_issue_bgs_source, + nonrating_issue_bgs_id: parser_issue.ri_nonrating_issue_bgs_id + } + ) end - it "returns false and does not process issues" do - expect(subject).not_to receive(:process_issues!) - expect(subject.perform!).to be false + it "returns a hash of request issue data with a withdrawal date equal to the closed date" do + result = described_class.new( + review: review, user: user, parser: parser + ).build_issue_data(parser_issue: parser_issue, is_withdrawn: true) + expect(result[:withdrawal_date]).to eq(result[:closed_date]) end end - context "when handling errors in #process_withdrawn_issues!" do - let(:invalid_reference_id) { "12345" } - - before do - subject.instance_variable_set(:@withdrawn_issue_data, [{ reference_id: invalid_reference_id }]) - allow(review.request_issues).to receive(:find_by) - .with(reference_id: invalid_reference_id) - .and_return(nil) - end - - it "raises a DecisionReviewUpdateMissingIssueError with the correct message" do - expect { subject.perform! }.to raise_error( - Caseflow::Error::DecisionReviewUpdateMissingIssueError, - "Request issue not found for REFERENCE_ID: #{invalid_reference_id}" + describe "#build_request_issues_data" do + it "returns an array of request issue data for updated issues" do + allow(parser).to receive(:updated_issues).and_return([issue_payload]) + allow(Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser).to receive(:new).and_return(parser_issue) + issue_data = described_class.new(review: review, user: user, parser: parser).build_issue_data( + parser_issue: parser_issue ) + result = described_class.new(review: review, user: user, parser: parser).build_request_issues_data + expect(result).to eq([issue_data]) end end - context "when handling errors in #process_removed_issues!" do - let(:invalid_reference_id) { "12345" } - - before do - subject.instance_variable_set(:@removed_issue_data, [{ reference_id: invalid_reference_id }]) - allow(review.request_issues).to receive(:find_by) - .with(reference_id: invalid_reference_id) - .and_return(nil) - end - - it "raises a DecisionReviewUpdateMissingIssueError with the correct message" do - expect { subject.perform! }.to raise_error( - Caseflow::Error::DecisionReviewUpdateMissingIssueError, - "Request issue not found for REFERENCE_ID: #{invalid_reference_id}" - ) + describe "#perform!" do + it "returns true if the base perform! is successful" do + allow_any_instance_of(RequestIssuesUpdate).to receive(:perform!).and_return(true) + allow_any_instance_of(described_class).to receive(:remove_request_issues_with_no_decision!).and_return(true) + allow_any_instance_of(described_class).to receive(:process_eligible_to_ineligible_issues!).and_return(true) + allow_any_instance_of(described_class).to receive(:process_ineligible_to_eligible_issues!).and_return(true) + allow_any_instance_of(described_class).to receive(:process_ineligible_to_ineligible_issues!).and_return(true) + subject = described_class.new(review: review, user: user, parser: parser) + expect(subject).to receive(:remove_request_issues_with_no_decision!) + expect(subject).to receive(:process_eligible_to_ineligible_issues!) + expect(subject).to receive(:process_ineligible_to_eligible_issues!) + expect(subject).to receive(:process_ineligible_to_ineligible_issues!) + expect(subject.perform!).to be_truthy end end - context "when handling errors in #process_withdrawn_issues!" do - let(:invalid_reference_id) { "12345" } - - before do - subject.instance_variable_set(:@withdrawn_issue_data, [{ reference_id: invalid_reference_id }]) - end - - context "when a request issue is not found" do - before do - allow(RequestIssue).to receive(:find).and_raise(ActiveRecord::RecordNotFound) - end - - it "raises a DecisionReviewUpdateMissingIssueError with the correct message" do - expect { subject.perform! }.to raise_error(Caseflow::Error::DecisionReviewUpdateMissingIssueError, - "Request issue not found for REFERENCE_ID: #{invalid_reference_id}") - end + describe "#remove_request_issues_with_no_decision!" do + it "removes request issues with no decision" do + allow_any_instance_of(RequestIssueClosure).to receive(:with_no_decision!).and_return(true) + allow(parser).to receive(:removed_issues).and_return([issue_payload]) + allow_any_instance_of(described_class).to receive(:check_for_mismatched_closed_issues!).and_return(true) + expect(described_class.new(review: review, user: user, parser: parser).remove_request_issues_with_no_decision!).to be_truthy end end - context "when handling errors in #process_eligible_to_ineligible_issues!" do - let(:invalid_reference_id) { "12345" } - let(:eligible_to_ineligible_issue) do - [{ reference_id: invalid_reference_id, ineligible_reason: "higher_level_review_to_higher_level_review" }] + describe "#check_for_mismatched_closed_issues!" do + it "raises an error if the issues are mismatched" do + issue_payload[:decision_review_issue_id] = "some_diff_reference_id" + allow(parser).to receive(:removed_issues).and_return([issue_payload]) + allow_any_instance_of(RequestIssuesUpdate).to receive(:removed_issues).and_return([existing_request_issue]) + expect do + described_class.new(review: review, user: user, parser: parser).check_for_mismatched_closed_issues! + end.to raise_error(Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError) end - before do - subject.instance_variable_set(:@eligible_to_ineligible_issue_data, [{ reference_id: invalid_reference_id }]) + it "does not raise an error if the removed issues are matched" do + allow(parser).to receive(:removed_issues).and_return([issue_payload]) + allow_any_instance_of(RequestIssuesUpdate).to receive(:removed_issues).and_return([existing_request_issue]) + expect do + described_class.new(review: review, user: user, parser: parser).check_for_mismatched_closed_issues! + end.to_not raise_error end - context "when a request issue is not found" do - before do - allow(RequestIssue).to receive(:find).and_raise(ActiveRecord::RecordNotFound) - - allow_any_instance_of(RequestIssue).to receive(:closed_at).and_return(nil) - allow_any_instance_of(RequestIssue).to receive(:save!).and_return(true) - end - - it "raises a DecisionReviewUpdateMissingIssueError with the correct message" do - expect { subject.perform! }.to raise_error(Caseflow::Error::DecisionReviewUpdateMissingIssueError, - "Request issue not found for REFERENCE_ID: #{invalid_reference_id}") - end - end - end - - context "when handling errors in #process_ineligible_to_eligible_issues!" do - let(:invalid_reference_id) { "12345" } - - before do - subject.instance_variable_set(:@ineligible_to_eligible_issue_data, [{ reference_id: invalid_reference_id }]) - allow(review.request_issues).to receive(:find_by) - .with(reference_id: invalid_reference_id) - .and_return(nil) + it "raises an error if the removed issues are missing in Caseflow" do + allow(parser).to receive(:removed_issues).and_return([issue_payload]) + allow_any_instance_of(RequestIssuesUpdate).to receive(:removed_issues).and_return([]) + expect do + described_class.new(review: review, user: user, parser: parser).check_for_mismatched_closed_issues! + end.to raise_error(Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError) end - it "raises a DecisionReviewUpdateMissingIssueError with the correct message" do - expect { subject.perform! }.to raise_error( - Caseflow::Error::DecisionReviewUpdateMissingIssueError, - "Request issue not found for REFERENCE_ID: #{invalid_reference_id}" - ) + it "raises an error if the removed issues are missing in parser" do + allow(parser).to receive(:removed_issues).and_return([]) + allow_any_instance_of(RequestIssuesUpdate).to receive(:removed_issues).and_return([existing_request_issue]) + expect do + described_class.new(review: review, user: user, parser: parser).check_for_mismatched_closed_issues! + end.to raise_error(Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError) end end - context "when handling errors in #process_ineligible_to_ineligible_issues!" do - let(:invalid_reference_id) { "12345" } - - before do - subject.instance_variable_set(:@ineligible_to_ineligible_issue_data, [{ reference_id: invalid_reference_id }]) - allow(review.request_issues).to receive(:find_by) - .with(reference_id: invalid_reference_id) - .and_return(nil) - end - - it "raises a DecisionReviewUpdateMissingIssueError with the correct message" do - expect { subject.perform! }.to raise_error( - Caseflow::Error::DecisionReviewUpdateMissingIssueError, - "Request issue not found for REFERENCE_ID: #{invalid_reference_id}" - ) + describe "#process_eligible_to_ineligible_issues!" do + it "updates the request issues to ineligible" do + issue_payload[:ineligible_reason] = "untimely" + issue_payload[:contention_reference_id] = nil + issue_payload[:closed_at] = 1_625_151_600 + allow(parser).to receive(:eligible_to_ineligible_issues).and_return([issue_payload]) + expect(described_class.new(review: review, user: user, parser: parser).process_eligible_to_ineligible_issues!).to be_truthy + request_issue = RequestIssue.find(existing_request_issue.id) + expect(request_issue.ineligible_reason).to eq(issue_payload[:ineligible_reason]) + expect(request_issue.closed_at).to eq("1970-01-19 14:25:51.000000000 -0500") + expect(request_issue.contention_removed_at).to be end end -end - -describe "additional positive tests" do - let(:user) { create(:user) } - let(:review) { create(:supplemental_claim) } - let(:parser) do - json_path = Rails.root.join("app", "services", "events", - "decision_review_updated", "decision_review_updated_example.json") - json_content = File.read(json_path) - Events::DecisionReviewUpdated::DecisionReviewUpdatedParser.new(nil, JSON.parse(json_content)) - end - let(:request_issues_update_event) do - RequestIssuesUpdateEvent.new( - user: user, - review: review, - parser: parser - ) - end - - describe "#perform!" do - context "when validation passes and issues are processed" do - before do - allow(request_issues_update_event).to receive(:validate_before_perform).and_return(true) - allow(request_issues_update_event).to receive(:processed?).and_return(false) - allow(review).to receive(:mark_rating_request_issues_to_reassociate!) - allow(request_issues_update_event).to receive(:process_job) - end - - it "processes issues and updates review" do - expect(request_issues_update_event).to receive(:process_issues!) - expect(request_issues_update_event).to receive(:update!) - - expect(request_issues_update_event.perform!).to be_truthy - end - end - - context "when validation fails" do - before do - allow(request_issues_update_event).to receive(:validate_before_perform).and_return(false) - end - - it "does not process issues" do - expect(request_issues_update_event.perform!).to be_falsey - expect(request_issues_update_event).not_to receive(:process_issues!) - end - end - - context "when already processed" do - before do - allow(request_issues_update_event).to receive(:processed?).and_return(true) - end - - it "does not process issues" do - expect(request_issues_update_event.perform!).to be_falsey - expect(request_issues_update_event).not_to receive(:process_issues!) - end - end - end - - describe "#process_issues!" do - it "calls individual issue processing methods" do - expect(request_issues_update_event).to receive(:process_added_issues!) - expect(request_issues_update_event).to receive(:process_removed_issues!) - expect(request_issues_update_event).to receive(:process_withdrawn_issues!) - expect(request_issues_update_event).to receive(:process_edited_issues!) - expect(request_issues_update_event).to receive(:process_eligible_to_ineligible_issues!) - expect(request_issues_update_event).to receive(:process_ineligible_to_eligible_issues!) - expect(request_issues_update_event).to receive(:process_ineligible_to_ineligible_issues!) - - request_issues_update_event.process_issues! + describe "#process_ineligible_to_eligible_issues!" do + it "updates the request issues to eligible" do + existing_request_issue.update( + ineligible_reason: "untimely", + closed_status: "ineligible", + closed_at: Time.zone.now + ) + allow(parser).to receive(:ineligible_to_eligible_issues).and_return([issue_payload]) + expect(described_class.new(review: review, user: user, parser: parser).process_ineligible_to_eligible_issues!).to be_truthy + existing_request_issue.reload + expect(existing_request_issue.ineligible_reason).to eq(nil) + expect(existing_request_issue.closed_status).to eq(nil) + expect(existing_request_issue.closed_at).to eq(nil) end end - context "new tests" do - let(:user) { create(:user) } - let(:review) { create(:supplemental_claim) } - - let(:parser) do - json_path = Rails.root.join("app", "services", "events", "decision_review_updated", - "decision_review_updated_example.json") - json_content = File.read(json_path) - Events::DecisionReviewUpdated::DecisionReviewUpdatedParser.new(nil, JSON.parse(json_content)) - end - - let(:request_issues_update_event) do - RequestIssuesUpdateEvent.new( - user: user, - review: review, - parser: parser + describe "#process_ineligible_to_ineligible_issues!" do + it "updates the request issues to ineligible" do + existing_request_issue.update( + ineligible_reason: "untimely", + closed_status: "ineligible", + closed_at: Time.zone.now ) + issue_payload[:ineligible_reason] = "before_ama" + issue_payload[:closed_at] = 1_625_151_600 + allow(parser).to receive(:ineligible_to_ineligible_issues).and_return([issue_payload]) + expect(described_class.new(review: review, user: user, parser: parser).process_ineligible_to_ineligible_issues!).to be_truthy + existing_request_issue.reload + expect(existing_request_issue.ineligible_reason).to eq(issue_payload[:ineligible_reason]) + expect(existing_request_issue.closed_at).to eq("1970-01-19 14:25:51.000000000 -0500") end - - describe "#perform!" do - context "when processing all types of issues successfully" do - let!(:existing_issue1) { create(:request_issue, decision_review: review, reference_id: "1") } - let!(:existing_issue2) { create(:request_issue, decision_review: review, reference_id: "2") } - - let(:added_issue_data) do - [{ - reference_id: "3", - ri_contested_issue_description: "Added Issue", - ri_benefit_type: "compensation" - }] - end - - let(:removed_issue_data) { [{ reference_id: "1" }] } - let(:edited_issue_data) do - [{ reference_id: "2", - edited_description: "Edited Issue", decision_date: Time.zone.today }] - end - let(:withdrawn_issue_data) { [{ reference_id: "2", closed_at: Time.zone.now }] } - - before do - # Create the added issue in the database before running the test - create(:request_issue, decision_review: review, reference_id: "3") - - request_issues_update_event.instance_variable_set(:@added_issue_data, added_issue_data) - request_issues_update_event.instance_variable_set(:@removed_issue_data, removed_issue_data) - request_issues_update_event.instance_variable_set(:@edited_issue_data, edited_issue_data) - request_issues_update_event.instance_variable_set(:@withdrawn_issue_data, withdrawn_issue_data) - - allow(request_issues_update_event).to receive(:validate_before_perform).and_return(true) - allow(request_issues_update_event).to receive(:processed?).and_return(false) - allow(review).to receive(:mark_rating_request_issues_to_reassociate!) - allow(request_issues_update_event).to receive(:process_job) - end - - it "marks the issue as removed" do - request_issues_update_event.perform! - existing_issue1.reload - expect(existing_issue1.closed_status).to eq("removed") - expect(existing_issue1.closed_at).not_to be_nil - end - end - end - - # 1. Testing #process_added_issues! - describe "#process_added_issues!" do - context "when there are added issues" do - let!(:existing_issue1) { create(:request_issue, decision_review: review, reference_id: "1") } - let!(:existing_issue2) { create(:request_issue, decision_review: review, reference_id: "2") } - - let(:added_issue_data) do - [ - { - reference_id: "3", - ri_contested_issue_description: "Issue 3", - ri_benefit_type: "compensation" - }, - { - reference_id: "4", - ri_contested_issue_description: "Issue 4", - ri_benefit_type: "compensation" - } - ] - end - - before do - # Create the added issues in the database - create(:request_issue, decision_review: review, reference_id: "3") - create(:request_issue, decision_review: review, reference_id: "4") - - request_issues_update_event.instance_variable_set(:@added_issue_data, added_issue_data) - end - # rubocop: disable Lint/AmbiguousBlockAssociation - - it "processes existing added issues without creating new ones" do - expect { request_issues_update_event.process_added_issues! } - .not_to change { review.request_issues.count } - expect(review.request_issues.exists?(reference_id: "3")).to be_truthy - expect(review.request_issues.exists?(reference_id: "4")).to be_truthy - end - end - - context "when there are no added issues" do - before do - request_issues_update_event.instance_variable_set(:@added_issue_data, []) - end - - it "does not create any new request issues" do - expect { request_issues_update_event.process_added_issues! }.not_to change { review.request_issues.count } - end - end - end - - # 2. Testing #process_removed_issues! - describe "#process_removed_issues!" do - context "when there are removed issues" do - let!(:existing_issue) { create(:request_issue, decision_review: review, reference_id: "1") } - let(:removed_issue_data) { [{ reference_id: "1" }] } - - before do - request_issues_update_event.instance_variable_set(:@removed_issue_data, removed_issue_data) - end - - it "marks the issue as removed" do - request_issues_update_event.process_removed_issues! - existing_issue.reload - expect(existing_issue.closed_status).to eq("removed") - expect(existing_issue.closed_at).not_to be_nil - end - end - - context "when there are no removed issues" do - before do - request_issues_update_event.instance_variable_set(:@removed_issue_data, []) - end - - it "does not alter any request issues" do - expect { request_issues_update_event.process_removed_issues! }.not_to change { review.request_issues.count } - end - end - end - - # 3. Testing #process_withdrawn_issues! - describe "#process_withdrawn_issues!" do - context "when there are withdrawn issues" do - let!(:existing_issue) { create(:request_issue, decision_review: review, reference_id: "2") } - let(:withdrawn_issue_data) { [{ reference_id: "2", closed_at: Time.zone.now }] } - - before do - request_issues_update_event.instance_variable_set(:@withdrawn_issue_data, withdrawn_issue_data) - end - - it "marks the issue as withdrawn with the correct closed_at date" do - request_issues_update_event.send(:process_withdrawn_issues!) - existing_issue.reload - expect(existing_issue.closed_status).to eq("withdrawn") - expect(existing_issue.closed_at).to be_within(1.second).of(withdrawn_issue_data.first[:closed_at]) - end - end - end - - # 4. Testing #process_edited_issues! - describe "#process_edited_issues!" do - context "when there are edited issues" do - let!(:existing_issue) do - create(:request_issue, decision_review: review, reference_id: "2", decision_date: 1.day.ago) - end - - let(:edited_issue_data) do - [{ reference_id: "2", edited_description: "Updated Issue", decision_date: Time.zone.today }] - end - - before do - request_issues_update_event.instance_variable_set(:@edited_issue_data, edited_issue_data) - end - - it "updates the edited_description and decision date of the issue" do - request_issues_update_event.send(:process_edited_issues!) - existing_issue.reload - expect(existing_issue.edited_description).to eq("Updated Issue") - expect(existing_issue.decision_date).to eq(Time.zone.today) - end - end - end - - # 5. Testing #process_eligible_to_ineligible_issues! - describe "#process_eligible_to_ineligible_issues!" do - context "when there are eligible to ineligible issues" do - let!(:existing_issue) do - create(:request_issue, decision_review: review, reference_id: "2", ineligible_reason: nil) - end - let(:issue_data) do - [{ reference_id: "2", ineligible_reason: "duplicate_of_rating_issue_in_active_review", - closed_at: Time.zone.now }] - end - - before do - request_issues_update_event.instance_variable_set(:@eligible_to_ineligible_issue_data, issue_data) - end - - it "updates the issue to be ineligible with the correct reason and closed_at date" do - request_issues_update_event.send(:process_eligible_to_ineligible_issues!) - existing_issue.reload - expect(existing_issue.ineligible_reason).to eq("duplicate_of_rating_issue_in_active_review") - expect(existing_issue.closed_at).to be_within(1.second).of(issue_data.first[:closed_at]) - expect(existing_issue.contention_reference_id).to be_nil - end - end - - context "when there are 2 issues to process" do - let!(:existing_issue1) do - create(:request_issue, decision_review: review, reference_id: "1", ineligible_reason: nil) - end - - let!(:existing_issue2) do - create(:request_issue, decision_review: review, reference_id: "2", ineligible_reason: nil) - end - - let(:issue_data) do - [ - { reference_id: "1", ineligible_reason: "untimely", closed_at: Time.zone.now }, - { reference_id: "2", ineligible_reason: "appeal_to_appeal", closed_at: Time.zone.now } - ] - end - - before do - request_issues_update_event.instance_variable_set(:@eligible_to_ineligible_issue_data, issue_data) - end - - it "updates all eligible issues" do - request_issues_update_event.send(:process_eligible_to_ineligible_issues!) - - existing_issue1.reload - existing_issue2.reload - - expect(existing_issue1.ineligible_reason).to eq("untimely") - expect(existing_issue1.closed_at).to be_within(1.second).of(issue_data[0][:closed_at]) - expect(existing_issue1.contention_reference_id).to be_nil - - expect(existing_issue2.ineligible_reason).to eq("appeal_to_appeal") - expect(existing_issue2.closed_at).to be_within(1.second).of(issue_data[1][:closed_at]) - expect(existing_issue2.contention_reference_id).to be_nil - end - end - - context "when processing 2 issues and one is missing" do - let!(:existing_issue) do - create(:request_issue, decision_review: review, reference_id: "1", ineligible_reason: nil) - end - - let(:issue_data) do - [ - { reference_id: "1", ineligible_reason: "untimely", closed_at: Time.zone.now }, - { reference_id: "non_existent_id", ineligible_reason: "appeal_to_appeal", closed_at: Time.zone.now } - ] - end - - before do - request_issues_update_event.instance_variable_set(:@eligible_to_ineligible_issue_data, issue_data) - end - - it "updates existing issues and raises an error for the missing one" do - expect do - request_issues_update_event.send(:process_eligible_to_ineligible_issues!) - end.to raise_error(Caseflow::Error::DecisionReviewUpdateMissingIssueError, - "Request issue not found for REFERENCE_ID: non_existent_id") - - # Ensure that the existing issue was updated before the error was raised - existing_issue.reload - expect(existing_issue.ineligible_reason).to eq("untimely") - expect(existing_issue.closed_at).to be_within(1.second).of(issue_data[0][:closed_at]) - expect(existing_issue.contention_reference_id).to be_nil - end - end - end - - # 6. Testing #process_ineligible_to_eligible_issues! - describe "#process_ineligible_to_eligible_issues!" do - context "when there are ineligible to eligible issues" do - let!(:existing_issue) do - create(:request_issue, decision_review: review, reference_id: "2", - ineligible_reason: "untimely", closed_at: Time.zone.now) - end - let(:issue_data) { [{ reference_id: "2" }] } - - before do - request_issues_update_event.instance_variable_set(:@ineligible_to_eligible_issue_data, issue_data) - end - - it "updates the issue to be eligible by clearing ineligible_reason and closed_at" do - request_issues_update_event.send(:process_ineligible_to_eligible_issues!) - existing_issue.reload - expect(existing_issue.ineligible_reason).to be_nil - expect(existing_issue.closed_at).to be_nil - end - end - - context "when there are no ineligible to eligible issues" do - before do - request_issues_update_event.instance_variable_set(:@ineligible_to_eligible_issue_data, []) - end - - it "does not alter any request issues" do - expect { request_issues_update_event.send(:process_ineligible_to_eligible_issues!) } - .not_to change { review.request_issues.pluck(:ineligible_reason) } - end - end - end - - # 7. Testing #process_ineligible_to_ineligible_issues! - describe "#process_ineligible_to_ineligible_issues!" do - context "when there are ineligible to ineligible issues with new reasons" do - let!(:existing_issue) do - create(:request_issue, decision_review: review, reference_id: "2", ineligible_reason: "untimely", - closed_at: Time.zone.now) - end - let(:issue_data) do - [{ reference_id: "2", ineligible_reason: "higher_level_review_to_higher_level_review", - closed_at: Time.zone.now }] - end - - before do - request_issues_update_event.instance_variable_set(:@ineligible_to_ineligible_issue_data, issue_data) - end - - it "updates the issue with the new ineligible reason and closed_at date" do - request_issues_update_event.send(:process_ineligible_to_ineligible_issues!) - existing_issue.reload - expect(existing_issue.ineligible_reason).to eq("higher_level_review_to_higher_level_review") - expect(existing_issue.closed_at).to be_within(1.second).of(issue_data.first[:closed_at]) - expect(existing_issue.contention_reference_id).to be_nil - end - end - - context "when there are no ineligible to ineligible issues" do - before do - request_issues_update_event.instance_variable_set(:@ineligible_to_ineligible_issue_data, []) - end - - it "does not alter any request issues" do - expect { request_issues_update_event.send(:process_ineligible_to_ineligible_issues!) } - .not_to change { review.request_issues.pluck(:ineligible_reason) } - end - end - end - - # 8. Testing #calculate_added_issues - describe "#calculate_added_issues" do - let!(:existing_issue1) { create(:request_issue, decision_review: review, reference_id: "3") } - let!(:existing_issue2) { create(:request_issue, decision_review: review, reference_id: "4") } - - let(:added_issue_data) do - [ - { - reference_id: "3", - ri_contested_issue_description: "New Issue 3", - ri_benefit_type: "compensation" - }, - { - reference_id: "4", - ri_contested_issue_description: "New Issue 4", - ri_benefit_type: "pension" - } - ] - end - - before do - request_issues_update_event.instance_variable_set(:@added_issue_data, added_issue_data) - end - - it "fetches existing request issues from the added issue data" do - added_issues = request_issues_update_event.send(:calculate_added_issues) - expect(added_issues.size).to eq(2) - expect(added_issues.map(&:reference_id)).to include("3", "4") - end - end - # rubocop: enable Lint/AmbiguousBlockAssociation end end diff --git a/spec/models/request_issues_update_evnt_spec.rb b/spec/models/request_issues_update_evnt_spec.rb new file mode 100644 index 00000000000..8a133d8c631 --- /dev/null +++ b/spec/models/request_issues_update_evnt_spec.rb @@ -0,0 +1,291 @@ +# frozen_string_literal: true + +RSpec.describe RequestIssuesUpdateEvnt, type: :model do + let(:user) { create(:user) } + let(:review) { create(:higher_level_review) } + let!(:existing_request_issue) { create(:request_issue, decision_review: review, reference_id: "some_reference_id") } + let(:parser) do + instance_double(Events::DecisionReviewUpdated::DecisionReviewUpdatedParser).tap do |parser| + allow(parser).to receive(:updated_issues).and_return([]) + allow(parser).to receive(:withdrawn_issues).and_return([]) + allow(parser).to receive(:added_issues).and_return([]) + allow(parser).to receive(:removed_issues).and_return([]) + end + end + + let(:parser_issue) do + instance_double(Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser).tap do |issue| + allow(issue).to receive(:ri_reference_id).and_return("some_reference_id") + allow(issue).to receive(:ri_benefit_type).and_return("some_benefit_type") + allow(issue).to receive(:ri_closed_at).and_return(Time.zone.now) + allow(issue).to receive(:ri_closed_status).and_return("some_status") + allow(issue).to receive(:ri_contested_issue_description).and_return("some_description") + allow(issue).to receive(:ri_contention_reference_id).and_return("some_contention_id") + allow(issue).to receive(:ri_contested_rating_issue_diagnostic_code).and_return("some_diagnostic_code") + allow(issue).to receive(:ri_contested_rating_decision_reference_id).and_return("some_decision_id") + allow(issue).to receive(:ri_contested_rating_issue_profile_date).and_return(Time.zone.today) + allow(issue).to receive(:ri_contested_rating_issue_reference_id).and_return("some_issue_id") + allow(issue).to receive(:ri_contested_decision_issue_id).and_return("some_decision_issue_id") + allow(issue).to receive(:ri_decision_date).and_return(Time.zone.today) + allow(issue).to receive(:ri_ineligible_due_to_id).and_return("some_ineligible_id") + allow(issue).to receive(:ri_ineligible_reason).and_return("some_reason") + allow(issue).to receive(:ri_is_unidentified).and_return(false) + allow(issue).to receive(:ri_unidentified_issue_text).and_return("some_text") + allow(issue).to receive(:ri_nonrating_issue_category).and_return("some_category") + allow(issue).to receive(:ri_nonrating_issue_description).and_return("some_description") + allow(issue).to receive(:ri_nonrating_issue_bgs_id).and_return("some_bgs_id") + allow(issue).to receive(:ri_nonrating_issue_bgs_source).and_return("some_source") + allow(issue).to receive(:ri_ramp_claim_id).and_return("some_claim_id") + allow(issue).to receive(:ri_rating_issue_associated_at).and_return(Time.zone.now) + allow(issue).to receive(:ri_untimely_exemption).and_return(false) + allow(issue).to receive(:ri_untimely_exemption_notes).and_return("some_notes") + allow(issue).to receive(:ri_vacols_id).and_return("some_vacols_id") + allow(issue).to receive(:ri_vacols_sequence_id).and_return("some_sequence_id") + allow(issue).to receive(:ri_veteran_participant_id).and_return("some_participant_id") + allow(issue).to receive(:ri_type).and_return("some_type") + allow(issue).to receive(:ri_decision).and_return("some_decision") + end + end + + let(:issue_payload) do + { + decision_review_issue_id: "some_reference_id", + benefit_type: "compensation", + closed_at: 1_625_151_600, + closed_status: "withdrawn", + contention_reference_id: 7_905_752, + contested_decision_issue_id: 201, + contested_issue_description: "Service connection for PTSD", + contested_rating_decision_reference_id: nil, + contested_rating_issue_diagnostic_code: "9411", + contested_rating_issue_profile_date: 1_625_076_000, + contested_rating_issue_reference_id: "REF9411", + type: "RequestIssue", + decision: [ + { + award_event_id: 679, + category: "decision", + contention_id: 35, + decision_finalized_time: nil, + decision_recorded_time: nil, + decision_source: "the source", + decision_text: "", + description: nil, + disposition: nil, + dta_error_explanation: nil, + id: 1738, + rating_profile_date: nil + } + ], + decision_date: 19_568, + ineligible_due_to_id: 301, + ineligible_reason: nil, + is_unidentified: false, + nonrating_issue_bgs_id: "13", + nonrating_issue_bgs_source: "CORP_AWARD_ATTORNEY_FEE", + nonrating_issue_category: "Accrued Benefits", + nonrating_issue_description: "Chapter 35 benefits", + ramp_claim_id: "RAMP123", + rating_issue_associated_at: 1_625_076_000, + unidentified_issue_text: nil, + untimely_exemption: nil, + untimely_exemption_notes: nil, + vacols_id: "VAC123", + vacols_sequence_id: nil + } + end + + describe "#initialize" do + it "calls build_request_issues_data" do + expect_any_instance_of(RequestIssuesUpdateEvnt).to receive(:build_request_issues_data) + described_class.new(review: review, user: user, parser: parser) + end + end + + describe "#find_request_issue_id" do + it "returns the request issue id" do + result = described_class.new(review: review, user: user, parser: parser).find_request_issue_id(parser_issue) + expect(result).to eq(existing_request_issue.id) + end + + it "raises an error if the request issue is not found" do + allow(RequestIssue).to receive(:find_by).and_return(nil) + expect do + described_class.new(review: review, user: user, parser: parser).find_request_issue_id(parser_issue) + end.to raise_error(Caseflow::Error::DecisionReviewUpdateMissingIssueError) + end + end + + describe "#build_issue_data" do + it "returns an empty hash if the parser issue is nil" do + result = described_class.new(review: review, user: user, parser: parser).build_issue_data(parser_issue: nil) + expect(result).to eq({}) + end + + it "returns a hash of request issue data" do + result = described_class.new(review: review, user: user, parser: parser).build_issue_data( + parser_issue: parser_issue + ) + expect(result).to eq( + { + request_issue_id: existing_request_issue.id, + benefit_type: parser_issue.ri_benefit_type, + closed_date: parser_issue.ri_closed_at, + withdrawal_date: nil, + closed_status: parser_issue.ri_closed_status, + contention_reference_id: parser_issue.ri_contention_reference_id, + contested_decision_issue_id: parser_issue.ri_contested_decision_issue_id, + contested_rating_issue_reference_id: parser_issue.ri_contested_rating_issue_reference_id, + contested_rating_issue_diagnostic_code: parser_issue.ri_contested_rating_issue_diagnostic_code, + contested_rating_decision_reference_id: parser_issue.ri_contested_rating_decision_reference_id, + contested_rating_issue_profile_date: parser_issue.ri_contested_rating_issue_profile_date, + contested_issue_description: parser_issue.ri_contested_issue_description, + unidentified_issue_text: parser_issue.ri_unidentified_issue_text, + decision_date: parser_issue.ri_decision_date, + nonrating_issue_category: parser_issue.ri_nonrating_issue_category, + nonrating_issue_description: parser_issue.ri_nonrating_issue_description, + is_unidentified: parser_issue.ri_is_unidentified, + untimely_exemption: parser_issue.ri_untimely_exemption, + untimely_exemption_notes: parser_issue.ri_untimely_exemption_notes, + ramp_claim_id: parser_issue.ri_ramp_claim_id, + vacols_id: parser_issue.ri_vacols_id, + vacols_sequence_id: parser_issue.ri_vacols_sequence_id, + ineligible_reason: parser_issue.ri_ineligible_reason, + ineligible_due_to_id: parser_issue.ri_ineligible_due_to_id, + reference_id: parser_issue.ri_reference_id, + type: parser_issue.ri_type, + veteran_participant_id: parser_issue.ri_veteran_participant_id, + rating_issue_associated_at: parser_issue.ri_rating_issue_associated_at, + nonrating_issue_bgs_source: parser_issue.ri_nonrating_issue_bgs_source, + nonrating_issue_bgs_id: parser_issue.ri_nonrating_issue_bgs_id + } + ) + end + + it "returns a hash of request issue data with a withdrawal date equal to the closed date" do + result = described_class.new( + review: review, user: user, parser: parser + ).build_issue_data(parser_issue: parser_issue, is_withdrawn: true) + expect(result[:withdrawal_date]).to eq(result[:closed_date]) + end + end + + describe "#build_request_issues_data" do + it "returns an array of request issue data for updated issues" do + allow(parser).to receive(:updated_issues).and_return([issue_payload]) + allow(Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser).to receive(:new).and_return(parser_issue) + issue_data = described_class.new(review: review, user: user, parser: parser).build_issue_data( + parser_issue: parser_issue + ) + result = described_class.new(review: review, user: user, parser: parser).build_request_issues_data + expect(result).to eq([issue_data]) + end + end + + describe "#perform!" do + it "returns true if the base perform! is successful" do + allow_any_instance_of(RequestIssuesUpdate).to receive(:perform!).and_return(true) + allow_any_instance_of(described_class).to receive(:remove_request_issues_with_no_decision!).and_return(true) + allow_any_instance_of(described_class).to receive(:process_eligible_to_ineligible_issues!).and_return(true) + allow_any_instance_of(described_class).to receive(:process_ineligible_to_eligible_issues!).and_return(true) + allow_any_instance_of(described_class).to receive(:process_ineligible_to_ineligible_issues!).and_return(true) + subject = described_class.new(review: review, user: user, parser: parser) + expect(subject).to receive(:remove_request_issues_with_no_decision!) + expect(subject).to receive(:process_eligible_to_ineligible_issues!) + expect(subject).to receive(:process_ineligible_to_eligible_issues!) + expect(subject).to receive(:process_ineligible_to_ineligible_issues!) + expect(subject.perform!).to be_truthy + end + end + + describe "#remove_request_issues_with_no_decision!" do + it "removes request issues with no decision" do + allow_any_instance_of(RequestIssueClosure).to receive(:with_no_decision!).and_return(true) + allow(parser).to receive(:removed_issues).and_return([issue_payload]) + allow_any_instance_of(described_class).to receive(:check_for_mismatched_closed_issues!).and_return(true) + expect(described_class.new(review: review, user: user, parser: parser).remove_request_issues_with_no_decision!).to be_truthy + end + end + + describe "#check_for_mismatched_closed_issues!" do + it "raises an error if the issues are mismatched" do + issue_payload[:decision_review_issue_id] = "some_diff_reference_id" + allow(parser).to receive(:removed_issues).and_return([issue_payload]) + allow_any_instance_of(RequestIssuesUpdate).to receive(:removed_issues).and_return([existing_request_issue]) + expect do + described_class.new(review: review, user: user, parser: parser).check_for_mismatched_closed_issues! + end.to raise_error(Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError) + end + + it "does not raise an error if the removed issues are matched" do + allow(parser).to receive(:removed_issues).and_return([issue_payload]) + allow_any_instance_of(RequestIssuesUpdate).to receive(:removed_issues).and_return([existing_request_issue]) + expect do + described_class.new(review: review, user: user, parser: parser).check_for_mismatched_closed_issues! + end.to_not raise_error + end + + it "raises an error if the removed issues are missing in Caseflow" do + allow(parser).to receive(:removed_issues).and_return([issue_payload]) + allow_any_instance_of(RequestIssuesUpdate).to receive(:removed_issues).and_return([]) + expect do + described_class.new(review: review, user: user, parser: parser).check_for_mismatched_closed_issues! + end.to raise_error(Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError) + end + + it "raises an error if the removed issues are missing in parser" do + allow(parser).to receive(:removed_issues).and_return([]) + allow_any_instance_of(RequestIssuesUpdate).to receive(:removed_issues).and_return([existing_request_issue]) + expect do + described_class.new(review: review, user: user, parser: parser).check_for_mismatched_closed_issues! + end.to raise_error(Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError) + end + end + + describe "#process_eligible_to_ineligible_issues!" do + it "updates the request issues to ineligible" do + issue_payload[:ineligible_reason] = "untimely" + issue_payload[:contention_reference_id] = nil + issue_payload[:closed_at] = 1_625_151_600 + allow(parser).to receive(:eligible_to_ineligible_issues).and_return([issue_payload]) + expect(described_class.new(review: review, user: user, parser: parser).process_eligible_to_ineligible_issues!).to be_truthy + request_issue = RequestIssue.find(existing_request_issue.id) + expect(request_issue.ineligible_reason).to eq(issue_payload[:ineligible_reason]) + expect(request_issue.closed_at).to eq("1970-01-19 14:25:51.000000000 -0500") + expect(request_issue.contention_removed_at).to be + end + end + + describe "#process_ineligible_to_eligible_issues!" do + it "updates the request issues to eligible" do + existing_request_issue.update( + ineligible_reason: "untimely", + closed_status: "ineligible", + closed_at: Time.zone.now + ) + allow(parser).to receive(:ineligible_to_eligible_issues).and_return([issue_payload]) + expect(described_class.new(review: review, user: user, parser: parser).process_ineligible_to_eligible_issues!).to be_truthy + existing_request_issue.reload + expect(existing_request_issue.ineligible_reason).to eq(nil) + expect(existing_request_issue.closed_status).to eq(nil) + expect(existing_request_issue.closed_at).to eq(nil) + end + end + + describe "#process_ineligible_to_ineligible_issues!" do + it "updates the request issues to ineligible" do + existing_request_issue.update( + ineligible_reason: "untimely", + closed_status: "ineligible", + closed_at: Time.zone.now + ) + issue_payload[:ineligible_reason] = "before_ama" + issue_payload[:closed_at] = 1_625_151_600 + allow(parser).to receive(:ineligible_to_ineligible_issues).and_return([issue_payload]) + expect(described_class.new(review: review, user: user, parser: parser).process_ineligible_to_ineligible_issues!).to be_truthy + existing_request_issue.reload + expect(existing_request_issue.ineligible_reason).to eq(issue_payload[:ineligible_reason]) + expect(existing_request_issue.closed_at).to eq("1970-01-19 14:25:51.000000000 -0500") + end + end +end From 6840f9bbb36c5f0af8708dc691dde7ccd8f86f6e Mon Sep 17 00:00:00 2001 From: Nader Kutub Date: Sun, 22 Sep 2024 19:59:23 -0700 Subject: [PATCH 3/9] refactor request_issues_update_event- remove temp files --- app/models/request_issues_update_evnt.rb | 187 ----------- .../models/request_issues_update_evnt_spec.rb | 291 ------------------ 2 files changed, 478 deletions(-) delete mode 100644 app/models/request_issues_update_evnt.rb delete mode 100644 spec/models/request_issues_update_evnt_spec.rb diff --git a/app/models/request_issues_update_evnt.rb b/app/models/request_issues_update_evnt.rb deleted file mode 100644 index d2bb4fe09b1..00000000000 --- a/app/models/request_issues_update_evnt.rb +++ /dev/null @@ -1,187 +0,0 @@ -# frozen_string_literal: true - -class RequestIssuesUpdateEvnt < RequestIssuesUpdate - def initialize(review:, user:, parser:) - @parser = parser - @request_issues_data = build_request_issues_data - super( - user: user, - review: review, - request_issues_data: @request_issues_data - ) - end - - # closures havppen by calling liunes similar to that in EP establishment line 460 - # should not add closure issues becused the base class calcuates the delta from before and after - # Question: should there be handling for corrected issues? - # Contentions are never removed, contention_removed_date is set instead - # do we need to update attributes for eligibility change issues or just status? - - def perform! - # Call the base class's perform! method - result = super - if result - remove_request_issues_with_no_decision! - process_eligible_to_ineligible_issues! - process_ineligible_to_eligible_issues! - process_ineligible_to_ineligible_issues! - true - else - false - end - end - - def process_eligible_to_ineligible_issues! - return if @parser.eligible_to_ineligible_issues.empty? - - @parser.eligible_to_ineligible_issues.each do |issue_data| - parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) - request_issue = review.request_issues.find_by(reference_id: parser_issue.ri_reference_id) - unless request_issue - fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.ri_reference_id - end - - request_issue.update( - ineligible_reason: parser_issue.ri_ineligible_reason, - closed_at: parser_issue.ri_closed_at, - ) - RequestIssueContention.new(request_issue).remove! - end - end - - def process_ineligible_to_eligible_issues! - return if @parser.ineligible_to_eligible_issues.empty? - - @parser.ineligible_to_eligible_issues.each do |issue_data| - parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) - request_issue = review.request_issues.find_by(reference_id: parser_issue.ri_reference_id) - unless request_issue - fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.ri_reference_id - end - request_issue.update( - ineligible_reason: nil, - closed_status: nil, - closed_at: nil - ) - end - end - - def process_ineligible_to_ineligible_issues! - return if @parser.ineligible_to_ineligible_issues.empty? - - @parser.ineligible_to_ineligible_issues.each do |issue_data| - parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) - request_issue = review.request_issues.find_by(reference_id: parser_issue.ri_reference_id) - unless request_issue - fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.ri_reference_id - end - - request_issue.update( - ineligible_reason: parser_issue.ri_ineligible_reason, - closed_at: parser_issue.ri_closed_at, - ) - end - end - - # check to see if the there are closed issues that are deferent from before - after - # if so, then raise an error - - def remove_request_issues_with_no_decision! - return if @parser.removed_issues.empty? - - check_for_mismatched_closed_issues! - @parser.removed_issues.each do |issue| - RequestIssueClosure.new(issue).with_no_decision! - end - end - - def check_for_mismatched_closed_issues! - parser_removed_issues = @parser.removed_issues.map do |issue| - parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue) - parser_issue.ri_reference_id - end - base_removed_issues = removed_issues.map(&:reference_id) - - # Check for issues in parser.removed_issues but not in base removed_issues - parser_only = parser_removed_issues - base_removed_issues - # Check for issues in base removed_issues but not in parser.removed_issues - base_only = base_removed_issues - parser_removed_issues - - if parser_only.any? || base_only.any? - raise Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError, - "Mismatched removed issues: CaseFlow only = #{base_only.join(', ')} - Event only = #{parser_only.join(', ')}" - end - true - end - - # Add any other methods you need - def build_request_issues_data - @request_issues_data = [] - - # Handle updated issues - @parser.updated_issues.each do |issue| - parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue) - @request_issues_data << build_issue_data(parser_issue: parser_issue) - end - - # Handle withdrawn issues - @parser.withdrawn_issues.each do |issue| - parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue) - @request_issues_data << build_issue_data(parser_issue: parser_issue, is_withdrawn: true) - end - - # Handle added issues - @parser.added_issues.each do |issue| - parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue) - @request_issues_data << build_issue_data(parser_issue: parser_issue, is_new: true) - end - - @request_issues_data - end - - def build_issue_data(parser_issue:, is_withdrawn: false, is_new: false) - return {} if parser_issue.nil? - - { - request_issue_id: is_new ? nil : find_request_issue_id(parser_issue), - benefit_type: parser_issue.ri_benefit_type, - closed_date: parser_issue.ri_closed_at, - withdrawal_date: is_withdrawn ? parser_issue.ri_closed_at : nil, - closed_status: parser_issue.ri_closed_status, - contention_reference_id: parser_issue.ri_contention_reference_id, - contested_decision_issue_id: parser_issue.ri_contested_decision_issue_id, - contested_rating_issue_reference_id: parser_issue.ri_contested_rating_issue_reference_id, - contested_rating_issue_diagnostic_code: parser_issue.ri_contested_rating_issue_diagnostic_code, - contested_rating_decision_reference_id: parser_issue.ri_contested_rating_decision_reference_id, - contested_issue_description: parser_issue.ri_contested_issue_description, - contested_rating_issue_profile_date: parser_issue.ri_contested_rating_issue_profile_date, - nonrating_issue_description: parser_issue.ri_nonrating_issue_category ? parser_issue.ri_contested_issue_description : nil, - unidentified_issue_text: parser_issue.ri_unidentified_issue_text, - decision_date: parser_issue.ri_decision_date, - nonrating_issue_category: parser_issue.ri_nonrating_issue_category, - is_unidentified: parser_issue.ri_is_unidentified, - untimely_exemption: parser_issue.ri_untimely_exemption, - untimely_exemption_notes: parser_issue.ri_untimely_exemption_notes, - ramp_claim_id: parser_issue.ri_ramp_claim_id, - vacols_id: parser_issue.ri_vacols_id, - vacols_sequence_id: parser_issue.ri_vacols_sequence_id, - ineligible_reason: parser_issue.ri_ineligible_reason, - ineligible_due_to_id: parser_issue.ri_ineligible_due_to_id, - reference_id: parser_issue.ri_reference_id, - type: parser_issue.ri_type, - veteran_participant_id: parser_issue.ri_veteran_participant_id, - rating_issue_associated_at: parser_issue.ri_rating_issue_associated_at, - nonrating_issue_bgs_source: parser_issue.ri_nonrating_issue_bgs_source, - nonrating_issue_bgs_id: parser_issue.ri_nonrating_issue_bgs_id - } - end - - def find_request_issue_id(parser_issue) - request_issue = RequestIssue.find_by(reference_id: parser_issue.ri_reference_id) - if request_issue - request_issue.id - else - raise(Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.ri_reference_id) - end - end -end diff --git a/spec/models/request_issues_update_evnt_spec.rb b/spec/models/request_issues_update_evnt_spec.rb deleted file mode 100644 index 8a133d8c631..00000000000 --- a/spec/models/request_issues_update_evnt_spec.rb +++ /dev/null @@ -1,291 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe RequestIssuesUpdateEvnt, type: :model do - let(:user) { create(:user) } - let(:review) { create(:higher_level_review) } - let!(:existing_request_issue) { create(:request_issue, decision_review: review, reference_id: "some_reference_id") } - let(:parser) do - instance_double(Events::DecisionReviewUpdated::DecisionReviewUpdatedParser).tap do |parser| - allow(parser).to receive(:updated_issues).and_return([]) - allow(parser).to receive(:withdrawn_issues).and_return([]) - allow(parser).to receive(:added_issues).and_return([]) - allow(parser).to receive(:removed_issues).and_return([]) - end - end - - let(:parser_issue) do - instance_double(Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser).tap do |issue| - allow(issue).to receive(:ri_reference_id).and_return("some_reference_id") - allow(issue).to receive(:ri_benefit_type).and_return("some_benefit_type") - allow(issue).to receive(:ri_closed_at).and_return(Time.zone.now) - allow(issue).to receive(:ri_closed_status).and_return("some_status") - allow(issue).to receive(:ri_contested_issue_description).and_return("some_description") - allow(issue).to receive(:ri_contention_reference_id).and_return("some_contention_id") - allow(issue).to receive(:ri_contested_rating_issue_diagnostic_code).and_return("some_diagnostic_code") - allow(issue).to receive(:ri_contested_rating_decision_reference_id).and_return("some_decision_id") - allow(issue).to receive(:ri_contested_rating_issue_profile_date).and_return(Time.zone.today) - allow(issue).to receive(:ri_contested_rating_issue_reference_id).and_return("some_issue_id") - allow(issue).to receive(:ri_contested_decision_issue_id).and_return("some_decision_issue_id") - allow(issue).to receive(:ri_decision_date).and_return(Time.zone.today) - allow(issue).to receive(:ri_ineligible_due_to_id).and_return("some_ineligible_id") - allow(issue).to receive(:ri_ineligible_reason).and_return("some_reason") - allow(issue).to receive(:ri_is_unidentified).and_return(false) - allow(issue).to receive(:ri_unidentified_issue_text).and_return("some_text") - allow(issue).to receive(:ri_nonrating_issue_category).and_return("some_category") - allow(issue).to receive(:ri_nonrating_issue_description).and_return("some_description") - allow(issue).to receive(:ri_nonrating_issue_bgs_id).and_return("some_bgs_id") - allow(issue).to receive(:ri_nonrating_issue_bgs_source).and_return("some_source") - allow(issue).to receive(:ri_ramp_claim_id).and_return("some_claim_id") - allow(issue).to receive(:ri_rating_issue_associated_at).and_return(Time.zone.now) - allow(issue).to receive(:ri_untimely_exemption).and_return(false) - allow(issue).to receive(:ri_untimely_exemption_notes).and_return("some_notes") - allow(issue).to receive(:ri_vacols_id).and_return("some_vacols_id") - allow(issue).to receive(:ri_vacols_sequence_id).and_return("some_sequence_id") - allow(issue).to receive(:ri_veteran_participant_id).and_return("some_participant_id") - allow(issue).to receive(:ri_type).and_return("some_type") - allow(issue).to receive(:ri_decision).and_return("some_decision") - end - end - - let(:issue_payload) do - { - decision_review_issue_id: "some_reference_id", - benefit_type: "compensation", - closed_at: 1_625_151_600, - closed_status: "withdrawn", - contention_reference_id: 7_905_752, - contested_decision_issue_id: 201, - contested_issue_description: "Service connection for PTSD", - contested_rating_decision_reference_id: nil, - contested_rating_issue_diagnostic_code: "9411", - contested_rating_issue_profile_date: 1_625_076_000, - contested_rating_issue_reference_id: "REF9411", - type: "RequestIssue", - decision: [ - { - award_event_id: 679, - category: "decision", - contention_id: 35, - decision_finalized_time: nil, - decision_recorded_time: nil, - decision_source: "the source", - decision_text: "", - description: nil, - disposition: nil, - dta_error_explanation: nil, - id: 1738, - rating_profile_date: nil - } - ], - decision_date: 19_568, - ineligible_due_to_id: 301, - ineligible_reason: nil, - is_unidentified: false, - nonrating_issue_bgs_id: "13", - nonrating_issue_bgs_source: "CORP_AWARD_ATTORNEY_FEE", - nonrating_issue_category: "Accrued Benefits", - nonrating_issue_description: "Chapter 35 benefits", - ramp_claim_id: "RAMP123", - rating_issue_associated_at: 1_625_076_000, - unidentified_issue_text: nil, - untimely_exemption: nil, - untimely_exemption_notes: nil, - vacols_id: "VAC123", - vacols_sequence_id: nil - } - end - - describe "#initialize" do - it "calls build_request_issues_data" do - expect_any_instance_of(RequestIssuesUpdateEvnt).to receive(:build_request_issues_data) - described_class.new(review: review, user: user, parser: parser) - end - end - - describe "#find_request_issue_id" do - it "returns the request issue id" do - result = described_class.new(review: review, user: user, parser: parser).find_request_issue_id(parser_issue) - expect(result).to eq(existing_request_issue.id) - end - - it "raises an error if the request issue is not found" do - allow(RequestIssue).to receive(:find_by).and_return(nil) - expect do - described_class.new(review: review, user: user, parser: parser).find_request_issue_id(parser_issue) - end.to raise_error(Caseflow::Error::DecisionReviewUpdateMissingIssueError) - end - end - - describe "#build_issue_data" do - it "returns an empty hash if the parser issue is nil" do - result = described_class.new(review: review, user: user, parser: parser).build_issue_data(parser_issue: nil) - expect(result).to eq({}) - end - - it "returns a hash of request issue data" do - result = described_class.new(review: review, user: user, parser: parser).build_issue_data( - parser_issue: parser_issue - ) - expect(result).to eq( - { - request_issue_id: existing_request_issue.id, - benefit_type: parser_issue.ri_benefit_type, - closed_date: parser_issue.ri_closed_at, - withdrawal_date: nil, - closed_status: parser_issue.ri_closed_status, - contention_reference_id: parser_issue.ri_contention_reference_id, - contested_decision_issue_id: parser_issue.ri_contested_decision_issue_id, - contested_rating_issue_reference_id: parser_issue.ri_contested_rating_issue_reference_id, - contested_rating_issue_diagnostic_code: parser_issue.ri_contested_rating_issue_diagnostic_code, - contested_rating_decision_reference_id: parser_issue.ri_contested_rating_decision_reference_id, - contested_rating_issue_profile_date: parser_issue.ri_contested_rating_issue_profile_date, - contested_issue_description: parser_issue.ri_contested_issue_description, - unidentified_issue_text: parser_issue.ri_unidentified_issue_text, - decision_date: parser_issue.ri_decision_date, - nonrating_issue_category: parser_issue.ri_nonrating_issue_category, - nonrating_issue_description: parser_issue.ri_nonrating_issue_description, - is_unidentified: parser_issue.ri_is_unidentified, - untimely_exemption: parser_issue.ri_untimely_exemption, - untimely_exemption_notes: parser_issue.ri_untimely_exemption_notes, - ramp_claim_id: parser_issue.ri_ramp_claim_id, - vacols_id: parser_issue.ri_vacols_id, - vacols_sequence_id: parser_issue.ri_vacols_sequence_id, - ineligible_reason: parser_issue.ri_ineligible_reason, - ineligible_due_to_id: parser_issue.ri_ineligible_due_to_id, - reference_id: parser_issue.ri_reference_id, - type: parser_issue.ri_type, - veteran_participant_id: parser_issue.ri_veteran_participant_id, - rating_issue_associated_at: parser_issue.ri_rating_issue_associated_at, - nonrating_issue_bgs_source: parser_issue.ri_nonrating_issue_bgs_source, - nonrating_issue_bgs_id: parser_issue.ri_nonrating_issue_bgs_id - } - ) - end - - it "returns a hash of request issue data with a withdrawal date equal to the closed date" do - result = described_class.new( - review: review, user: user, parser: parser - ).build_issue_data(parser_issue: parser_issue, is_withdrawn: true) - expect(result[:withdrawal_date]).to eq(result[:closed_date]) - end - end - - describe "#build_request_issues_data" do - it "returns an array of request issue data for updated issues" do - allow(parser).to receive(:updated_issues).and_return([issue_payload]) - allow(Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser).to receive(:new).and_return(parser_issue) - issue_data = described_class.new(review: review, user: user, parser: parser).build_issue_data( - parser_issue: parser_issue - ) - result = described_class.new(review: review, user: user, parser: parser).build_request_issues_data - expect(result).to eq([issue_data]) - end - end - - describe "#perform!" do - it "returns true if the base perform! is successful" do - allow_any_instance_of(RequestIssuesUpdate).to receive(:perform!).and_return(true) - allow_any_instance_of(described_class).to receive(:remove_request_issues_with_no_decision!).and_return(true) - allow_any_instance_of(described_class).to receive(:process_eligible_to_ineligible_issues!).and_return(true) - allow_any_instance_of(described_class).to receive(:process_ineligible_to_eligible_issues!).and_return(true) - allow_any_instance_of(described_class).to receive(:process_ineligible_to_ineligible_issues!).and_return(true) - subject = described_class.new(review: review, user: user, parser: parser) - expect(subject).to receive(:remove_request_issues_with_no_decision!) - expect(subject).to receive(:process_eligible_to_ineligible_issues!) - expect(subject).to receive(:process_ineligible_to_eligible_issues!) - expect(subject).to receive(:process_ineligible_to_ineligible_issues!) - expect(subject.perform!).to be_truthy - end - end - - describe "#remove_request_issues_with_no_decision!" do - it "removes request issues with no decision" do - allow_any_instance_of(RequestIssueClosure).to receive(:with_no_decision!).and_return(true) - allow(parser).to receive(:removed_issues).and_return([issue_payload]) - allow_any_instance_of(described_class).to receive(:check_for_mismatched_closed_issues!).and_return(true) - expect(described_class.new(review: review, user: user, parser: parser).remove_request_issues_with_no_decision!).to be_truthy - end - end - - describe "#check_for_mismatched_closed_issues!" do - it "raises an error if the issues are mismatched" do - issue_payload[:decision_review_issue_id] = "some_diff_reference_id" - allow(parser).to receive(:removed_issues).and_return([issue_payload]) - allow_any_instance_of(RequestIssuesUpdate).to receive(:removed_issues).and_return([existing_request_issue]) - expect do - described_class.new(review: review, user: user, parser: parser).check_for_mismatched_closed_issues! - end.to raise_error(Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError) - end - - it "does not raise an error if the removed issues are matched" do - allow(parser).to receive(:removed_issues).and_return([issue_payload]) - allow_any_instance_of(RequestIssuesUpdate).to receive(:removed_issues).and_return([existing_request_issue]) - expect do - described_class.new(review: review, user: user, parser: parser).check_for_mismatched_closed_issues! - end.to_not raise_error - end - - it "raises an error if the removed issues are missing in Caseflow" do - allow(parser).to receive(:removed_issues).and_return([issue_payload]) - allow_any_instance_of(RequestIssuesUpdate).to receive(:removed_issues).and_return([]) - expect do - described_class.new(review: review, user: user, parser: parser).check_for_mismatched_closed_issues! - end.to raise_error(Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError) - end - - it "raises an error if the removed issues are missing in parser" do - allow(parser).to receive(:removed_issues).and_return([]) - allow_any_instance_of(RequestIssuesUpdate).to receive(:removed_issues).and_return([existing_request_issue]) - expect do - described_class.new(review: review, user: user, parser: parser).check_for_mismatched_closed_issues! - end.to raise_error(Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError) - end - end - - describe "#process_eligible_to_ineligible_issues!" do - it "updates the request issues to ineligible" do - issue_payload[:ineligible_reason] = "untimely" - issue_payload[:contention_reference_id] = nil - issue_payload[:closed_at] = 1_625_151_600 - allow(parser).to receive(:eligible_to_ineligible_issues).and_return([issue_payload]) - expect(described_class.new(review: review, user: user, parser: parser).process_eligible_to_ineligible_issues!).to be_truthy - request_issue = RequestIssue.find(existing_request_issue.id) - expect(request_issue.ineligible_reason).to eq(issue_payload[:ineligible_reason]) - expect(request_issue.closed_at).to eq("1970-01-19 14:25:51.000000000 -0500") - expect(request_issue.contention_removed_at).to be - end - end - - describe "#process_ineligible_to_eligible_issues!" do - it "updates the request issues to eligible" do - existing_request_issue.update( - ineligible_reason: "untimely", - closed_status: "ineligible", - closed_at: Time.zone.now - ) - allow(parser).to receive(:ineligible_to_eligible_issues).and_return([issue_payload]) - expect(described_class.new(review: review, user: user, parser: parser).process_ineligible_to_eligible_issues!).to be_truthy - existing_request_issue.reload - expect(existing_request_issue.ineligible_reason).to eq(nil) - expect(existing_request_issue.closed_status).to eq(nil) - expect(existing_request_issue.closed_at).to eq(nil) - end - end - - describe "#process_ineligible_to_ineligible_issues!" do - it "updates the request issues to ineligible" do - existing_request_issue.update( - ineligible_reason: "untimely", - closed_status: "ineligible", - closed_at: Time.zone.now - ) - issue_payload[:ineligible_reason] = "before_ama" - issue_payload[:closed_at] = 1_625_151_600 - allow(parser).to receive(:ineligible_to_ineligible_issues).and_return([issue_payload]) - expect(described_class.new(review: review, user: user, parser: parser).process_ineligible_to_ineligible_issues!).to be_truthy - existing_request_issue.reload - expect(existing_request_issue.ineligible_reason).to eq(issue_payload[:ineligible_reason]) - expect(existing_request_issue.closed_at).to eq("1970-01-19 14:25:51.000000000 -0500") - end - end -end From 2b69fa806b645446b40215cb87a8270644140f64 Mon Sep 17 00:00:00 2001 From: Nader Kutub Date: Mon, 23 Sep 2024 02:31:07 -0700 Subject: [PATCH 4/9] fix lint issues --- app/models/request_issues_update_event.rb | 47 +++++++++++++------ .../request_issues_update_event_spec.rb | 15 +++--- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/app/models/request_issues_update_event.rb b/app/models/request_issues_update_event.rb index 62b5fc533e1..d34cf29e10e 100644 --- a/app/models/request_issues_update_event.rb +++ b/app/models/request_issues_update_event.rb @@ -43,7 +43,7 @@ def process_eligible_to_ineligible_issues! request_issue.update( ineligible_reason: parser_issue.ri_ineligible_reason, - closed_at: parser_issue.ri_closed_at, + closed_at: parser_issue.ri_closed_at ) RequestIssueContention.new(request_issue).remove! end @@ -58,6 +58,7 @@ def process_ineligible_to_eligible_issues! unless request_issue fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.ri_reference_id end + request_issue.update( ineligible_reason: nil, closed_status: nil, @@ -78,7 +79,7 @@ def process_ineligible_to_ineligible_issues! request_issue.update( ineligible_reason: parser_issue.ri_ineligible_reason, - closed_at: parser_issue.ri_closed_at, + closed_at: parser_issue.ri_closed_at ) end end @@ -108,7 +109,7 @@ def check_for_mismatched_closed_issues! base_only = base_removed_issues - parser_removed_issues if parser_only.any? || base_only.any? - raise Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError, + fail Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError, "Mismatched removed issues: CaseFlow only = #{base_only.join(', ')} - Event only = #{parser_only.join(', ')}" end true @@ -142,23 +143,21 @@ def build_request_issues_data def build_issue_data(parser_issue:, is_withdrawn: false, is_new: false) return {} if parser_issue.nil? + issue_data = base_issue_data(parser_issue, is_withdrawn, is_new) + issue_data.merge!(contested_issue_data(parser_issue)) + issue_data.merge!(nonrating_issue_data(parser_issue)) + issue_data + end + + def base_issue_data(parser_issue, is_withdrawn, is_new) { request_issue_id: is_new ? nil : find_request_issue_id(parser_issue), benefit_type: parser_issue.ri_benefit_type, closed_date: parser_issue.ri_closed_at, withdrawal_date: is_withdrawn ? parser_issue.ri_closed_at : nil, closed_status: parser_issue.ri_closed_status, - contention_reference_id: parser_issue.ri_contention_reference_id, - contested_decision_issue_id: parser_issue.ri_contested_decision_issue_id, - contested_rating_issue_reference_id: parser_issue.ri_contested_rating_issue_reference_id, - contested_rating_issue_diagnostic_code: parser_issue.ri_contested_rating_issue_diagnostic_code, - contested_rating_decision_reference_id: parser_issue.ri_contested_rating_decision_reference_id, - contested_issue_description: parser_issue.ri_contested_issue_description, - contested_rating_issue_profile_date: parser_issue.ri_contested_rating_issue_profile_date, - nonrating_issue_description: parser_issue.ri_nonrating_issue_category ? parser_issue.ri_contested_issue_description : nil, unidentified_issue_text: parser_issue.ri_unidentified_issue_text, decision_date: parser_issue.ri_decision_date, - nonrating_issue_category: parser_issue.ri_nonrating_issue_category, is_unidentified: parser_issue.ri_is_unidentified, untimely_exemption: parser_issue.ri_untimely_exemption, untimely_exemption_notes: parser_issue.ri_untimely_exemption_notes, @@ -169,19 +168,37 @@ def build_issue_data(parser_issue:, is_withdrawn: false, is_new: false) ineligible_due_to_id: parser_issue.ri_ineligible_due_to_id, reference_id: parser_issue.ri_reference_id, type: parser_issue.ri_type, - veteran_participant_id: parser_issue.ri_veteran_participant_id, - rating_issue_associated_at: parser_issue.ri_rating_issue_associated_at, + rating_issue_associated_at: parser_issue.ri_rating_issue_associated_at + } + end + + def nonrating_issue_data(parser_issue) + { + nonrating_issue_category: parser_issue.ri_nonrating_issue_category, + nonrating_issue_description: parser_issue.ri_contested_issue_description, nonrating_issue_bgs_source: parser_issue.ri_nonrating_issue_bgs_source, nonrating_issue_bgs_id: parser_issue.ri_nonrating_issue_bgs_id } end + def contested_issue_data(parser_issue) + { + contention_reference_id: parser_issue.ri_contention_reference_id, + contested_decision_issue_id: parser_issue.ri_contested_decision_issue_id, + contested_rating_issue_reference_id: parser_issue.ri_contested_rating_issue_reference_id, + contested_rating_issue_diagnostic_code: parser_issue.ri_contested_rating_issue_diagnostic_code, + contested_rating_decision_reference_id: parser_issue.ri_contested_rating_decision_reference_id, + contested_issue_description: parser_issue.ri_contested_issue_description, + contested_rating_issue_profile_date: parser_issue.ri_contested_rating_issue_profile_date + } + end + def find_request_issue_id(parser_issue) request_issue = RequestIssue.find_by(reference_id: parser_issue.ri_reference_id) if request_issue request_issue.id else - raise(Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.ri_reference_id) + fail(Caseflow::Error::DecisionReviewUpdateMissingIssueError, parser_issue.ri_reference_id) end end end diff --git a/spec/models/request_issues_update_event_spec.rb b/spec/models/request_issues_update_event_spec.rb index 5dddeaf1c96..60eccd76a1f 100644 --- a/spec/models/request_issues_update_event_spec.rb +++ b/spec/models/request_issues_update_event_spec.rb @@ -41,9 +41,7 @@ allow(issue).to receive(:ri_untimely_exemption_notes).and_return("some_notes") allow(issue).to receive(:ri_vacols_id).and_return("some_vacols_id") allow(issue).to receive(:ri_vacols_sequence_id).and_return("some_sequence_id") - allow(issue).to receive(:ri_veteran_participant_id).and_return("some_participant_id") allow(issue).to receive(:ri_type).and_return("some_type") - allow(issue).to receive(:ri_decision).and_return("some_decision") end end @@ -154,7 +152,6 @@ ineligible_due_to_id: parser_issue.ri_ineligible_due_to_id, reference_id: parser_issue.ri_reference_id, type: parser_issue.ri_type, - veteran_participant_id: parser_issue.ri_veteran_participant_id, rating_issue_associated_at: parser_issue.ri_rating_issue_associated_at, nonrating_issue_bgs_source: parser_issue.ri_nonrating_issue_bgs_source, nonrating_issue_bgs_id: parser_issue.ri_nonrating_issue_bgs_id @@ -203,7 +200,9 @@ allow_any_instance_of(RequestIssueClosure).to receive(:with_no_decision!).and_return(true) allow(parser).to receive(:removed_issues).and_return([issue_payload]) allow_any_instance_of(described_class).to receive(:check_for_mismatched_closed_issues!).and_return(true) - expect(described_class.new(review: review, user: user, parser: parser).remove_request_issues_with_no_decision!).to be_truthy + expect( + described_class.new(review: review, user: user, parser: parser).remove_request_issues_with_no_decision! + ).to be_truthy end end @@ -248,7 +247,9 @@ issue_payload[:contention_reference_id] = nil issue_payload[:closed_at] = 1_625_151_600 allow(parser).to receive(:eligible_to_ineligible_issues).and_return([issue_payload]) - expect(described_class.new(review: review, user: user, parser: parser).process_eligible_to_ineligible_issues!).to be_truthy + expect( + described_class.new(review: review, user: user, parser: parser).process_eligible_to_ineligible_issues! + ).to be_truthy request_issue = RequestIssue.find(existing_request_issue.id) expect(request_issue.ineligible_reason).to eq(issue_payload[:ineligible_reason]) expect(request_issue.closed_at).to eq("1970-01-19 14:25:51.000000000 -0500") @@ -264,7 +265,9 @@ closed_at: Time.zone.now ) allow(parser).to receive(:ineligible_to_eligible_issues).and_return([issue_payload]) - expect(described_class.new(review: review, user: user, parser: parser).process_ineligible_to_eligible_issues!).to be_truthy + expect( + described_class.new(review: review, user: user, parser: parser).process_ineligible_to_eligible_issues! + ).to be_truthy existing_request_issue.reload expect(existing_request_issue.ineligible_reason).to eq(nil) expect(existing_request_issue.closed_status).to eq(nil) From 0e3f151bb77e5fbd235194b10eb86e047f1371f3 Mon Sep 17 00:00:00 2001 From: Nader Kutub Date: Mon, 23 Sep 2024 06:37:58 -0700 Subject: [PATCH 5/9] fix linting issues --- app/models/request_issues_update_event.rb | 27 ++++++++++--------- .../request_issues_update_event_spec.rb | 11 +++++--- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/models/request_issues_update_event.rb b/app/models/request_issues_update_event.rb index d34cf29e10e..d75e923283a 100644 --- a/app/models/request_issues_update_event.rb +++ b/app/models/request_issues_update_event.rb @@ -11,12 +11,6 @@ def initialize(review:, user:, parser:) ) end - # closures havppen by calling liunes similar to that in EP establishment line 460 - # should not add closure issues becused the base class calcuates the delta from before and after - # Question: should there be handling for corrected issues? - # Contentions are never removed, contention_removed_date is set instead - # do we need to update attributes for eligibility change issues or just status? - def perform! # Call the base class's perform! method result = super @@ -109,8 +103,8 @@ def check_for_mismatched_closed_issues! base_only = base_removed_issues - parser_removed_issues if parser_only.any? || base_only.any? - fail Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError, - "Mismatched removed issues: CaseFlow only = #{base_only.join(', ')} - Event only = #{parser_only.join(', ')}" + fail Caseflow::Error::DecisionReviewUpdateMismatchedRemovedIssuesError, + "CaseFlow only = #{base_only.join(', ')} - Event only = #{parser_only.join(', ')}" end true end @@ -143,18 +137,17 @@ def build_request_issues_data def build_issue_data(parser_issue:, is_withdrawn: false, is_new: false) return {} if parser_issue.nil? - issue_data = base_issue_data(parser_issue, is_withdrawn, is_new) + issue_data = base_issue_data(parser_issue) issue_data.merge!(contested_issue_data(parser_issue)) issue_data.merge!(nonrating_issue_data(parser_issue)) + issue_data.merge!(conditional_issue_data(parser_issue, is_withdrawn, is_new)) issue_data end - def base_issue_data(parser_issue, is_withdrawn, is_new) + def base_issue_data(parser_issue) { - request_issue_id: is_new ? nil : find_request_issue_id(parser_issue), benefit_type: parser_issue.ri_benefit_type, closed_date: parser_issue.ri_closed_at, - withdrawal_date: is_withdrawn ? parser_issue.ri_closed_at : nil, closed_status: parser_issue.ri_closed_status, unidentified_issue_text: parser_issue.ri_unidentified_issue_text, decision_date: parser_issue.ri_decision_date, @@ -168,7 +161,15 @@ def base_issue_data(parser_issue, is_withdrawn, is_new) ineligible_due_to_id: parser_issue.ri_ineligible_due_to_id, reference_id: parser_issue.ri_reference_id, type: parser_issue.ri_type, - rating_issue_associated_at: parser_issue.ri_rating_issue_associated_at + rating_issue_associated_at: parser_issue.ri_rating_issue_associated_at, + edited_description: parser_issue.ri_edited_description + } + end + + def conditional_issue_data(parser_issue, is_withdrawn, is_new) + { + request_issue_id: is_new ? nil : find_request_issue_id(parser_issue), + withdrawal_date: is_withdrawn ? parser_issue.ri_closed_at : nil, } end diff --git a/spec/models/request_issues_update_event_spec.rb b/spec/models/request_issues_update_event_spec.rb index 60eccd76a1f..a2c2d003a7e 100644 --- a/spec/models/request_issues_update_event_spec.rb +++ b/spec/models/request_issues_update_event_spec.rb @@ -42,6 +42,7 @@ allow(issue).to receive(:ri_vacols_id).and_return("some_vacols_id") allow(issue).to receive(:ri_vacols_sequence_id).and_return("some_sequence_id") allow(issue).to receive(:ri_type).and_return("some_type") + allow(issue).to receive(:ri_edited_description).and_return("Edited description") end end @@ -89,7 +90,8 @@ untimely_exemption: nil, untimely_exemption_notes: nil, vacols_id: "VAC123", - vacols_sequence_id: nil + vacols_sequence_id: nil, + edited_description: "Edited description" } end @@ -154,7 +156,8 @@ type: parser_issue.ri_type, rating_issue_associated_at: parser_issue.ri_rating_issue_associated_at, nonrating_issue_bgs_source: parser_issue.ri_nonrating_issue_bgs_source, - nonrating_issue_bgs_id: parser_issue.ri_nonrating_issue_bgs_id + nonrating_issue_bgs_id: parser_issue.ri_nonrating_issue_bgs_id, + edited_description: parser_issue.ri_edited_description } ) end @@ -285,7 +288,9 @@ issue_payload[:ineligible_reason] = "before_ama" issue_payload[:closed_at] = 1_625_151_600 allow(parser).to receive(:ineligible_to_ineligible_issues).and_return([issue_payload]) - expect(described_class.new(review: review, user: user, parser: parser).process_ineligible_to_ineligible_issues!).to be_truthy + expect( + described_class.new(review: review, user: user, parser: parser).process_ineligible_to_ineligible_issues! + ).to be_truthy existing_request_issue.reload expect(existing_request_issue.ineligible_reason).to eq(issue_payload[:ineligible_reason]) expect(existing_request_issue.closed_at).to eq("1970-01-19 14:25:51.000000000 -0500") From 6124432032c43247ecf7061d71d759f9cef71838 Mon Sep 17 00:00:00 2001 From: Nader Kutub Date: Mon, 23 Sep 2024 06:48:02 -0700 Subject: [PATCH 6/9] fix eligibility fields --- app/models/request_issues_update_event.rb | 6 ++++-- spec/models/request_issues_update_event_spec.rb | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/models/request_issues_update_event.rb b/app/models/request_issues_update_event.rb index d75e923283a..760781af45b 100644 --- a/app/models/request_issues_update_event.rb +++ b/app/models/request_issues_update_event.rb @@ -56,7 +56,9 @@ def process_ineligible_to_eligible_issues! request_issue.update( ineligible_reason: nil, closed_status: nil, - closed_at: nil + closed_at: nil, + contention_reference_id: parser_issue.ri_contention_reference_id, + contention_removed_at: nil ) end end @@ -169,7 +171,7 @@ def base_issue_data(parser_issue) def conditional_issue_data(parser_issue, is_withdrawn, is_new) { request_issue_id: is_new ? nil : find_request_issue_id(parser_issue), - withdrawal_date: is_withdrawn ? parser_issue.ri_closed_at : nil, + withdrawal_date: is_withdrawn ? parser_issue.ri_closed_at : nil } end diff --git a/spec/models/request_issues_update_event_spec.rb b/spec/models/request_issues_update_event_spec.rb index a2c2d003a7e..7fa034c753a 100644 --- a/spec/models/request_issues_update_event_spec.rb +++ b/spec/models/request_issues_update_event_spec.rb @@ -265,7 +265,9 @@ existing_request_issue.update( ineligible_reason: "untimely", closed_status: "ineligible", - closed_at: Time.zone.now + closed_at: Time.zone.now, + contention_reference_id: nil, + contention_removed_at: nil ) allow(parser).to receive(:ineligible_to_eligible_issues).and_return([issue_payload]) expect( @@ -275,6 +277,8 @@ expect(existing_request_issue.ineligible_reason).to eq(nil) expect(existing_request_issue.closed_status).to eq(nil) expect(existing_request_issue.closed_at).to eq(nil) + expect(existing_request_issue.contention_reference_id).to eq(issue_payload[:contention_reference_id]) + expect(existing_request_issue.contention_removed_at).to eq(nil) end end From 0a061d5d07eb82a1f5111daf456c4adccc0e5350 Mon Sep 17 00:00:00 2001 From: Nader Kutub Date: Mon, 23 Sep 2024 07:07:26 -0700 Subject: [PATCH 7/9] add gaurd for missing participant in request issues serializer --- app/serializers/api/v3/issues/ama/request_issue_serializer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/serializers/api/v3/issues/ama/request_issue_serializer.rb b/app/serializers/api/v3/issues/ama/request_issue_serializer.rb index 6a931938b72..3008bff36e2 100644 --- a/app/serializers/api/v3/issues/ama/request_issue_serializer.rb +++ b/app/serializers/api/v3/issues/ama/request_issue_serializer.rb @@ -33,7 +33,7 @@ class Api::V3::Issues::Ama::RequestIssueSerializer attribute :caseflow_considers_eligible, &:eligible? attribute :claimant_participant_id do |object| - object.decision_review.claimant.participant_id + object&.decision_review&.claimant&.participant_id end attribute :claim_id do |object| From 4f1128433284b646f695e2b8290890e1db15d8ad Mon Sep 17 00:00:00 2001 From: Nader Kutub Date: Mon, 23 Sep 2024 07:22:33 -0700 Subject: [PATCH 8/9] add description fields to eligibility updates --- app/models/request_issues_update_event.rb | 15 ++++++++++++--- spec/models/request_issues_update_event_spec.rb | 10 ++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app/models/request_issues_update_event.rb b/app/models/request_issues_update_event.rb index 760781af45b..eee7b18b674 100644 --- a/app/models/request_issues_update_event.rb +++ b/app/models/request_issues_update_event.rb @@ -37,7 +37,10 @@ def process_eligible_to_ineligible_issues! request_issue.update( ineligible_reason: parser_issue.ri_ineligible_reason, - closed_at: parser_issue.ri_closed_at + closed_at: parser_issue.ri_closed_at, + contested_issue_description: parser_issue.ri_contested_issue_description, + nonrating_issue_category: parser_issue.ri_nonrating_issue_category, + nonrating_issue_description: parser_issue.ri_nonrating_issue_description ) RequestIssueContention.new(request_issue).remove! end @@ -58,7 +61,10 @@ def process_ineligible_to_eligible_issues! closed_status: nil, closed_at: nil, contention_reference_id: parser_issue.ri_contention_reference_id, - contention_removed_at: nil + contention_removed_at: nil, + contested_issue_description: parser_issue.ri_contested_issue_description, + nonrating_issue_category: parser_issue.ri_nonrating_issue_category, + nonrating_issue_description: parser_issue.ri_nonrating_issue_description ) end end @@ -75,7 +81,10 @@ def process_ineligible_to_ineligible_issues! request_issue.update( ineligible_reason: parser_issue.ri_ineligible_reason, - closed_at: parser_issue.ri_closed_at + closed_at: parser_issue.ri_closed_at, + contested_issue_description: parser_issue.ri_contested_issue_description, + nonrating_issue_category: parser_issue.ri_nonrating_issue_category, + nonrating_issue_description: parser_issue.ri_nonrating_issue_description ) end end diff --git a/spec/models/request_issues_update_event_spec.rb b/spec/models/request_issues_update_event_spec.rb index 7fa034c753a..9a20a0a7222 100644 --- a/spec/models/request_issues_update_event_spec.rb +++ b/spec/models/request_issues_update_event_spec.rb @@ -249,6 +249,7 @@ issue_payload[:ineligible_reason] = "untimely" issue_payload[:contention_reference_id] = nil issue_payload[:closed_at] = 1_625_151_600 + issue_payload[:nonrating_issue_description] = "some_nonrating_issue_description" allow(parser).to receive(:eligible_to_ineligible_issues).and_return([issue_payload]) expect( described_class.new(review: review, user: user, parser: parser).process_eligible_to_ineligible_issues! @@ -257,6 +258,9 @@ expect(request_issue.ineligible_reason).to eq(issue_payload[:ineligible_reason]) expect(request_issue.closed_at).to eq("1970-01-19 14:25:51.000000000 -0500") expect(request_issue.contention_removed_at).to be + expect(request_issue.contested_issue_description).to eq(issue_payload[:contested_issue_description]) + expect(request_issue.nonrating_issue_category).to eq(issue_payload[:nonrating_issue_category]) + expect(request_issue.nonrating_issue_description).to eq(issue_payload[:nonrating_issue_description]) end end @@ -279,6 +283,9 @@ expect(existing_request_issue.closed_at).to eq(nil) expect(existing_request_issue.contention_reference_id).to eq(issue_payload[:contention_reference_id]) expect(existing_request_issue.contention_removed_at).to eq(nil) + expect(existing_request_issue.contested_issue_description).to eq(issue_payload[:contested_issue_description]) + expect(existing_request_issue.nonrating_issue_category).to eq(issue_payload[:nonrating_issue_category]) + expect(existing_request_issue.nonrating_issue_description).to eq(issue_payload[:nonrating_issue_description]) end end @@ -298,6 +305,9 @@ existing_request_issue.reload expect(existing_request_issue.ineligible_reason).to eq(issue_payload[:ineligible_reason]) expect(existing_request_issue.closed_at).to eq("1970-01-19 14:25:51.000000000 -0500") + expect(existing_request_issue.contested_issue_description).to eq(issue_payload[:contested_issue_description]) + expect(existing_request_issue.nonrating_issue_category).to eq(issue_payload[:nonrating_issue_category]) + expect(existing_request_issue.nonrating_issue_description).to eq(issue_payload[:nonrating_issue_description]) end end end From c5663cda9443eda64be310b641c054d93635135e Mon Sep 17 00:00:00 2001 From: Nader Kutub Date: Mon, 23 Sep 2024 08:49:51 -0700 Subject: [PATCH 9/9] bug fixes with controller --- .../v1/decision_review_updated_controller.rb | 11 +++++++++- app/models/request_issues_update_event.rb | 22 ++++++++++++++++++- .../request_issues_update_event_spec.rb | 15 +++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/events/v1/decision_review_updated_controller.rb b/app/controllers/api/events/v1/decision_review_updated_controller.rb index c61b509b9cb..08cf02130c7 100644 --- a/app/controllers/api/events/v1/decision_review_updated_controller.rb +++ b/app/controllers/api/events/v1/decision_review_updated_controller.rb @@ -97,7 +97,16 @@ def dru_params :untimely_exemption_notes, :vacols_id, :vacols_sequence_id, - :veteran_participant_id + :veteran_participant_id, + :claim_id, + :end_product_establishment, + :added_issues, + :updated_issues, + :removed_issues, + :withdrawn_issues, + :ineligible_to_eligible_issues, + :eligible_to_ineligible_issues, + :ineligible_to_ineligible_issues ] ) end diff --git a/app/models/request_issues_update_event.rb b/app/models/request_issues_update_event.rb index eee7b18b674..c7c8e4c5b84 100644 --- a/app/models/request_issues_update_event.rb +++ b/app/models/request_issues_update_event.rb @@ -19,12 +19,32 @@ def perform! process_eligible_to_ineligible_issues! process_ineligible_to_eligible_issues! process_ineligible_to_ineligible_issues! + process_request_issues_data! true else false end end + # Process aditional updates for all data that was passed to base class + def process_request_issues_data! + return if @request_issues_data.empty? + + @request_issues_data.each do |issue_data| + parser_issue = Events::DecisionReviewUpdated::DecisionReviewUpdatedIssueParser.new(issue_data) + request_issue = review.request_issues.find_by(reference_id: issue_data[:reference_id]) + unless request_issue + fail Caseflow::Error::DecisionReviewUpdateMissingIssueError, issue_data[:reference_id] + end + + request_issue.update( + contested_issue_description: parser_issue.ri_contested_issue_description, + nonrating_issue_category: parser_issue.ri_nonrating_issue_category, + nonrating_issue_description: parser_issue.ri_nonrating_issue_description + ) + end + end + def process_eligible_to_ineligible_issues! return if @parser.eligible_to_ineligible_issues.empty? @@ -187,7 +207,7 @@ def conditional_issue_data(parser_issue, is_withdrawn, is_new) def nonrating_issue_data(parser_issue) { nonrating_issue_category: parser_issue.ri_nonrating_issue_category, - nonrating_issue_description: parser_issue.ri_contested_issue_description, + nonrating_issue_description: parser_issue.ri_nonrating_issue_description, nonrating_issue_bgs_source: parser_issue.ri_nonrating_issue_bgs_source, nonrating_issue_bgs_id: parser_issue.ri_nonrating_issue_bgs_id } diff --git a/spec/models/request_issues_update_event_spec.rb b/spec/models/request_issues_update_event_spec.rb index 9a20a0a7222..1cb3f777116 100644 --- a/spec/models/request_issues_update_event_spec.rb +++ b/spec/models/request_issues_update_event_spec.rb @@ -189,11 +189,13 @@ allow_any_instance_of(described_class).to receive(:process_eligible_to_ineligible_issues!).and_return(true) allow_any_instance_of(described_class).to receive(:process_ineligible_to_eligible_issues!).and_return(true) allow_any_instance_of(described_class).to receive(:process_ineligible_to_ineligible_issues!).and_return(true) + allow_any_instance_of(described_class).to receive(:process_request_issues_data!).and_return(true) subject = described_class.new(review: review, user: user, parser: parser) expect(subject).to receive(:remove_request_issues_with_no_decision!) expect(subject).to receive(:process_eligible_to_ineligible_issues!) expect(subject).to receive(:process_ineligible_to_eligible_issues!) expect(subject).to receive(:process_ineligible_to_ineligible_issues!) + expect(subject).to receive(:process_request_issues_data!) expect(subject.perform!).to be_truthy end end @@ -310,4 +312,17 @@ expect(existing_request_issue.nonrating_issue_description).to eq(issue_payload[:nonrating_issue_description]) end end + + describe "#process_request_issues_data!" do + it "updates addtional fields" do + allow(parser).to receive(:updated_issues).and_return([issue_payload]) + expect( + described_class.new(review: review, user: user, parser: parser).process_request_issues_data! + ).to be_truthy + existing_request_issue.reload + expect(existing_request_issue.contested_issue_description).to eq(issue_payload[:contested_issue_description]) + expect(existing_request_issue.nonrating_issue_category).to eq(issue_payload[:nonrating_issue_category]) + expect(existing_request_issue.nonrating_issue_description).to eq(issue_payload[:nonrating_issue_description]) + end + end end