diff --git a/app/models/decision_issue.rb b/app/models/decision_issue.rb index 7dff27d5f0f..7f3f733090c 100644 --- a/app/models/decision_issue.rb +++ b/app/models/decision_issue.rb @@ -2,6 +2,7 @@ class DecisionIssue < CaseflowRecord include HasDecisionReviewUpdatedSince + include EventConcern validates :benefit_type, inclusion: { in: Constants::BENEFIT_TYPES.keys.map(&:to_s) } validates :disposition, presence: true @@ -23,6 +24,8 @@ class DecisionIssue < CaseflowRecord has_one :effectuation, class_name: "BoardGrantEffectuation", foreign_key: :granted_decision_issue_id has_many :contesting_request_issues, class_name: "RequestIssue", foreign_key: "contested_decision_issue_id" + has_one :event_record, as: :evented_record + # NOTE: These are the string identifiers for remand dispositions returned from VBMS. # The characters and encoding are precise so don't change these unless you # know they match VBMS values. diff --git a/app/models/events/event_record.rb b/app/models/events/event_record.rb index 901d42f6399..896d40b0694 100644 --- a/app/models/events/event_record.rb +++ b/app/models/events/event_record.rb @@ -18,6 +18,7 @@ def valid_evented_record Veteran Person RequestIssue + DecisionIssue LegacyIssue LegacyIssueOptin User diff --git a/app/services/events/decision_review_completed.rb b/app/services/events/decision_review_completed.rb index 6d2caf107bc..9376d63758f 100644 --- a/app/services/events/decision_review_completed.rb +++ b/app/services/events/decision_review_completed.rb @@ -37,10 +37,10 @@ def complete!(params, headers, payload) review = epe&.source - Events::DecisionReviewCompleted::CompleteInformalConference.process!(event: event, parser: parser) - Events::DecisionReviewCompleted::CompleteClaimReview.process!(event: event, parser: parser) + Events::DecisionReviewCompleted::CompleteClaimReview.process!(event: event, parser: parser, review: review) Events::DecisionReviewCompleted::CompleteEndProductEstablishment.process!(event: event, parser: parser) - DecisionIssuesCompleteEvent.new(user: user, review: review, parser: parser, event: event, epe: epe).perform! + Events::DecisionReviewCompleted::CompleteRequestIssues.process!(event: event, parser: parser, review: review) + # DecisionIssuesCompleteEvent.new(user: user, review: review, parser: parser, event: event, epe: epe).perform! # Update the Event after all operations have completed event.update!(completed_at: Time.now.in_time_zone, error: nil, info: { "event_payload" => payload }) end diff --git a/app/services/events/decision_review_completed/complete_claim_review.rb b/app/services/events/decision_review_completed/complete_claim_review.rb index aa85176f028..96d88b68a5d 100644 --- a/app/services/events/decision_review_completed/complete_claim_review.rb +++ b/app/services/events/decision_review_completed/complete_claim_review.rb @@ -1,9 +1,29 @@ # frozen_string_literal: true # Service class to handle updates to the ClaimReview (I.E. HigherLevelReview or SupplementalClaim) -# This is temporary empty class +# This Class will handle the completion of the ClaimReview class Events::DecisionReviewCompleted::CompleteClaimReview class << self - def process!(params); end + def process!(params) + event = params[:event] + parser = params[:parser] + claim_review = params[:review] + + # may not need this? + # claim_review.update!(legacy_opt_in_approved: parser.claim_review_legacy_opt_in_approved) + + # Will these fields change during completion? + if parser.detail_type == "HigherLevelReview" + claim_review.update!( + informal_conference: parser.claim_review_informal_conference, + same_office: parser.claim_review_same_office + ) + end + + EventRecord.create!(event: event, evented_record: claim_review) + claim_review + rescue StandardError => error + raise Caseflow::Error::DecisionReviewCompletedClaimReviewError, error.message + end end end diff --git a/app/services/events/decision_review_completed/complete_end_product_establishment.rb b/app/services/events/decision_review_completed/complete_end_product_establishment.rb index 5421d6351fa..0d64cda0af6 100644 --- a/app/services/events/decision_review_completed/complete_end_product_establishment.rb +++ b/app/services/events/decision_review_completed/complete_end_product_establishment.rb @@ -1,9 +1,24 @@ # frozen_string_literal: true -# Service class to handle updates to the EPE -# This is temporary empty class +# Service class to handle completion of the EPE in Caseflow after receiving a DecisionReviewCompletedEvent from VBMS class Events::DecisionReviewCompleted::CompleteEndProductEstablishment class << self - def process!(params); end + def process!(params) + event = params[:event] + parser = params[:parser] + epe = EndProductEstablishment.find_by( + reference_id: parser.end_product_establishment_reference_id + ) + epe.update!( + code: parser.end_product_establishment_code, + development_item_reference_id: parser.epe_development_item_reference_id, + synced_status: parser.end_product_establishment_synced_status, + last_synced_at: parser.end_product_establishment_last_synced_at + ) + EventRecord.create!(event: event, evented_record: epe) + epe + rescue StandardError => error + raise Caseflow::Error::DecisionReviewCompletedEndProductEstablishmentError, error.message + end end end diff --git a/app/services/events/decision_review_completed/complete_informal_conference.rb b/app/services/events/decision_review_completed/complete_informal_conference.rb deleted file mode 100644 index e75729d6a2c..00000000000 --- a/app/services/events/decision_review_completed/complete_informal_conference.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -# This is temporary empty class -class Events::DecisionReviewCompleted::CompleteInformalConference - class << self - def process!(params); end - end -end diff --git a/app/services/events/decision_review_completed/complete_request_issues.rb b/app/services/events/decision_review_completed/complete_request_issues.rb new file mode 100644 index 00000000000..bfd38b68e8d --- /dev/null +++ b/app/services/events/decision_review_completed/complete_request_issues.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +# Service class to handle completion of RequestIssues and creating DecisionIssues +class Events::DecisionReviewCompleted::CompleteRequestIssues + class << self + def process!(params) + process_request_issues(params) + rescue StandardError => error + raise Caseflow::Error::DecisionReviewCompletedRequestIssueError, error.message + end + + private + + # iterate through the array of completed_issues and create DecisionIssues from each one + def process_request_issues(params) + event = params[:event] + parser = params[:parser] + claim_review = params[:review] + completed_request_issues = [] + request_issues_to_complete = parser.completed_issues + + request_issues_to_complete&.each do |issue| + # create backfill RI object using extracted values + parser_issue = Events::DecisionReviewCompleted::DecisionReviewCompletedIssueParser.new(issue) + + if parser_issue.ri_reference_id.nil? + fail Caseflow::Error::DecisionReviewCompletedRequestIssuesError, "reference_id cannot be null" + end + + # fetch existing RequestIssue in CF + ri = RequestIssue.find(reference_id: parser_issue.ri_reference_id) + + # TODO: update all RI fields; RequestIssueUpdate model needed? + # TODO: create new EventRecord(s) for each RI to show an update was performed + + # create DI backfill and associated Records + create_decision_issue_backfill(issue, ri) + + # TODO: determine what the return object should be + end + end + + def create_decision_issue_backfill(issue_param, request_issue) + di = DecisionIssue.create!( + benefit_type: issue_param.decision_issue_benefit_type, + contention_reference_id: issue_param.decision_issue_contention_reference_id, + decision_text: issue_param.decision_issue_decision_text, + description: issue_param.decision_issue_description, + diagnostic_code: issue_param.decision_issue_diagnostic_code, + disposition: issue_param.decision_issue_disposition, + end_product_last_action_date: issue_param.decision_issue_end_product_last_action_date, + participant_id: issue_param.decision_issue_participant_id, + percent_number: issue_param.decision_issue_percent_number, + rating_issue_reference_id: issue_param.decision_issue_rating_issue_reference_id, + rating_profile_date: issue_param.decision_issue_rating_profile_date, + rating_promulgation_date: issue_param.decision_issue_rating_promulgation_date, + subject_text: issue_param.decision_issue_subject_text + ) + + # create RequestDecisionIssue to link the DI with the RI + RequestDecisionIssue.create!(decision_issue: di, request_issue: request_issue) + + # create EventRecords + EventRecord.create!( + event: event, + evented_record: di + ) + end + end +end diff --git a/lib/caseflow/error.rb b/lib/caseflow/error.rb index a5c2b9faff2..fb4a06b9185 100644 --- a/lib/caseflow/error.rb +++ b/lib/caseflow/error.rb @@ -529,6 +529,10 @@ def initialize(msg = "Decision review update mismatched removed issues") end class DecisionReviewUpdatedClaimReviewError < StandardError; end class DecisionReviewUpdatedEndProductEstablishmentError < StandardError; end + class DecisionReviewCompletedEndProductEstablishmentError < StandardError; end + class DecisionReviewCompletedClaimReviewError < StandardError; end + class DecisionReviewCompletedRequestIssueError < StandardError; end + class MaximumBatchSizeViolationError < StandardError def initialize(msg = "The batch size of jobs must not exceed 10") super(msg) diff --git a/spec/feature/events/decision_review_completed/scenario_complete_epe_spec.rb b/spec/feature/events/decision_review_completed/scenario_complete_epe_spec.rb new file mode 100644 index 00000000000..22ba1fbdcdf --- /dev/null +++ b/spec/feature/events/decision_review_completed/scenario_complete_epe_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +# rubocop:disable Style/NumericLiterals +RSpec.describe Api::Events::V1::DecisionReviewCompletedController, type: :controller do + describe "POST #decision_review_completed" do + let!(:current_user) { User.authenticate! } + let(:api_key) { ApiKey.create!(consumer_name: "API TEST TOKEN") } + let!(:epe) { create(:end_product_establishment, :active_hlr, reference_id: 1234567) } + + # TODO: change test payload as needed as Phase 3 implementation continues + def json_test_payload + { + "event_id": 214706, + "claim_id": 1234567, + "css_id": "BVADWISE101", + "detail_type": "HigherLevelReview", + "station": "101", + "claim_review": { + "auto_remand": false, + "remand_source_id": nil, + "informal_conference": false, + "same_office": false, + "legacy_opt_in_approved": false + }, + "end_product_establishment": { + "code": "030HLRR", + "development_item_reference_id": "1", + "reference_id": "1234567", + "synced_status": "CLR", + "last_synced_at": 1726688419000 + }, + "completed_issues": nil + } + end + + let!(:valid_params) do + json_test_payload + end + + context "a payload that ONLY affects the EPE" do + before do + FeatureToggle.disable!(:disable_ama_eventing) + request.headers["Authorization"] = "Token token=#{api_key.key_string}" + end + it "updates the EPE accordingly with a Cleared or Canceled status, indicating completion" do + request.headers["Authorization"] = "Token #{api_key.key_string}" + post :decision_review_completed, params: valid_params + expect(response).to have_http_status(:created) + epe.reload + expect(epe.code).to eq("030HLRR") + expect(epe.synced_status).to eq("CLR") + expect(epe.last_synced_at).to_not be_nil + end + end + end +end + +# rubocop:enable Style/NumericLiterals