Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CAVC Remand model & Controller #15351

Merged
merged 32 commits into from
Oct 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3e5ce3a
migration
hschallhorn Sep 23, 2020
440f5d0
WIP
hschallhorn Sep 23, 2020
8b9f455
Merge branch 'master' into hschallhorn/15281-cavc-remand-model
hschallhorn Sep 28, 2020
7238807
Fixes
hschallhorn Sep 28, 2020
c2f7203
remove focus
hschallhorn Sep 28, 2020
f8b7271
cc
hschallhorn Sep 28, 2020
51b89b3
get tests working
hschallhorn Sep 28, 2020
dc0ff4d
Renames some fields!
lomky Sep 29, 2020
6fbdc7b
Merge branch 'master' into hschallhorn/15281-cavc-remand-model
hschallhorn Sep 29, 2020
82beb99
Schema & docs
lomky Sep 29, 2020
7ca4474
Merge branch 'hschallhorn/15281-cavc-remand-model' of https://github.…
lomky Sep 29, 2020
44347ce
Merge branch 'master' into hschallhorn/15281-cavc-remand-model
lomky Sep 29, 2020
7218f6c
Typo!
lomky Sep 29, 2020
d067ef5
Test field name fixes
lomky Sep 29, 2020
cc5f81d
Lints!
lomky Sep 29, 2020
0962954
Allow null dates for MDR
lomky Sep 29, 2020
394b3ee
Validate JMR has all issues
lomky Sep 29, 2020
abe4920
Corrects id check on decision issues
lomky Sep 30, 2020
62be7f1
Merge branch 'master' into hschallhorn/15281-cavc-remand-model
lomky Sep 30, 2020
6bb4c15
Apply suggestions from code review
lomky Oct 2, 2020
1b61ac2
Merge branch 'master' into kat/15281-cavc-remand-model
lomky Oct 2, 2020
51e7760
Adding remand subtype validation
lomky Oct 2, 2020
afdb5cd
Cleans the array comparison
lomky Oct 2, 2020
f3c10e3
Apply suggestions from code review
lomky Oct 2, 2020
e7bfea7
Dries up rspec tests
lomky Oct 2, 2020
7eabf8e
Merge branch 'kat/15281-cavc-remand-model' of https://github.com/depa…
lomky Oct 2, 2020
b074beb
Update db/migrate/20200923192959_create_cavc_remand.rb
lomky Oct 2, 2020
92bc1b1
Remigrates
lomky Oct 2, 2020
e0b0793
Deletes extra line
lomky Oct 2, 2020
2088b3e
Merge branch 'master' into kat/15281-cavc-remand-model
lomky Oct 2, 2020
8702dc8
CAVC Remand controller (#15372)
lomky Oct 2, 2020
14addc1
Merge branch 'master' into kat/15281-cavc-remand-model
Oct 2, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions app/controllers/cavc_remands_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

# Controller to create CAVC Remands and kick off a new Appeal stream on an appeal

class CavcRemandsController < ApplicationController
before_action :validate_cavc_remand_access

def create
cavc_remand = CavcRemand.create(create_params)
render json: { cavc_remand: cavc_remand }, status: :created
end

# def update
# only for mdr, not yet implemented
# end

private

def appeal
@appeal ||= Appeal.find_appeal_by_uuid_or_find_or_create_legacy_appeal_by_vacols_id(params[:appeal_id])
end

def validate_cavc_remand_access
unless LitigationSupport.singleton.user_has_access?(current_user)
msg = "Only Litigation Support users can create CAVC Remands"
fail Caseflow::Error::ActionForbiddenError, message: msg
end
end

def create_params
permitted = params.permit(:judgement_date, :mandate_date, :appeal_id, :cavc_docket_number, :cavc_judge_full_name,
:cavc_decision_type, :decision_date, :instructions, :remand_subtype,
:represented_by_attorney)
.merge(params.permit(decision_issue_ids: []))
.merge(created_by: current_user, updated_by: current_user)
permitted.require([:appeal_id, :cavc_docket_number, :cavc_judge_full_name, :cavc_decision_type, :decision_date,
:decision_issue_ids, :instructions, :remand_subtype, :represented_by_attorney])
permitted
end
end
36 changes: 36 additions & 0 deletions app/models/cavc_remand.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

# Model to store information captured when processing a form for an appeal remanded by CAVC

class CavcRemand < CaseflowRecord
belongs_to :created_by, class_name: "User"
belongs_to :updated_by, class_name: "User"
belongs_to :appeal

validates :created_by, :updated_by, :appeal, :cavc_docket_number, :represented_by_attorney, :cavc_judge_full_name,
:cavc_decision_type, :decision_date, :decision_issue_ids, :instructions, presence: true
validates :remand_subtype, presence: true, if: :remand?
validates :judgement_date, :mandate_date, presence: true, unless: :mdr?
validates :cavc_judge_full_name, inclusion: { in: Constants::CAVC_JUDGE_FULL_NAMES }
validate :decision_issue_ids_match_appeal_decision_issues, if: :jmr?

enum cavc_decision_type: {
Constants.CAVC_DECISION_TYPES.remand.to_sym => Constants.CAVC_DECISION_TYPES.remand,
Constants.CAVC_DECISION_TYPES.straight_reversal.to_sym => Constants.CAVC_DECISION_TYPES.straight_reversal,
Constants.CAVC_DECISION_TYPES.death_dismissal.to_sym => Constants.CAVC_DECISION_TYPES.death_dismissal
}

# Joint Motion Remand, Joint Motion Partial Remand, and Memorandum Decision on Remand
# The Board uses the initialisms more than the full words, so we are following that norm
enum remand_subtype: {
Constants.CAVC_REMAND_SUBTYPES.jmr.to_sym => Constants.CAVC_REMAND_SUBTYPES.jmr,
Constants.CAVC_REMAND_SUBTYPES.jmpr.to_sym => Constants.CAVC_REMAND_SUBTYPES.jmpr,
Constants.CAVC_REMAND_SUBTYPES.mdr.to_sym => Constants.CAVC_REMAND_SUBTYPES.mdr
}

def decision_issue_ids_match_appeal_decision_issues
lomky marked this conversation as resolved.
Show resolved Hide resolved
unless (appeal.decision_issues.map(&:id) - decision_issue_ids).empty?
fail Caseflow::Error::JmrAppealDecisionIssueMismatch, message: "JMR remands must address all decision issues"
end
end
end
5 changes: 5 additions & 0 deletions client/constants/CAVC_DECISION_TYPES.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"remand": "remand",
"straight_reversal": "straight_reversal",
"death_dismissal": "death_dismissal"
}
21 changes: 21 additions & 0 deletions client/constants/CAVC_JUDGE_FULL_NAMES.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[
"Margaret Bartley",
"Coral W. Pietsch",
"William S. Greenberg",
"Michael P. Allen",
"Amanda L. Meredith",
"Joseph L. Toth",
"Joseph L. Falvey, Jr.",
"Scott J. Laurer",
"Grant C. Jaquith",
"Frank Q. Nebeker",
"Kenneth B. Kramer",
"Ronald M. Holdaway",
"William P. Greene, Jr.",
"Bruce E. Kasold",
"Lawrence B. Hagel",
"William A. Moorman",
"Alan G. Lance, Sr.",
"Robert N. Davis",
"Mary J. Schoelen"
]
5 changes: 5 additions & 0 deletions client/constants/CAVC_REMAND_SUBTYPES.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"jmr": "jmr",
"jmpr": "jmpr",
"mdr": "mdr"
}
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
get 'tasks', to: "tasks#for_appeal"
patch 'update'
post 'work_mode', to: "work_modes#create"
post 'cavc_remand', to: "cavc_remands#create"
end
end
match '/appeals/:appeal_id/edit/:any' => 'appeals#edit', via: [:get]
Expand Down
22 changes: 22 additions & 0 deletions db/migrate/20200923192959_create_cavc_remand.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class CreateCavcRemand < Caseflow::Migration
def change
create_table :cavc_remands do |t|
t.bigint "appeal_id", null: false, comment: "Appeal that CAVC has remanded"
t.bigint "created_by_id", null: false, comment: "User that created this record"
t.bigint "updated_by_id", null: false, comment: "User that updated this record. For MDR remands, judgement and mandate dates will be added after the record is first created."

t.string "cavc_docket_number", null: false, comment: "Docket number of the CAVC judgement"
t.boolean "represented_by_attorney", null: false, comment: "Whether or not the appellant was represented by an attorney"
t.string "cavc_judge_full_name", null: false, comment: "CAVC judge that passed the judgement on the remand"
t.string "cavc_decision_type", null: false, comment: "CAVC decision type. Expecting 'remand', 'straight_reversal', or 'death_dismissal'"
t.string "remand_subtype", comment: "Type of remand. If the cavc_decision_type is 'remand', expecting one of 'jmp', 'jmpr', or 'mdr'. Otherwise, this can be null."
t.date "decision_date", null: false, comment: "Date CAVC issued a decision, according to the CAVC"
t.date "judgement_date", comment: "Date CAVC issued a judgement, according to the CAVC"
t.date "mandate_date", comment: "Date that CAVC reported the mandate was given"
t.bigint "decision_issue_ids", default: [], array: true, comment: "Decision issues being remanded; IDs refer to decision_issues table. For a JMR, all decision issues on the previous appeal will be remanded. For a JMPR, only some"
t.string "instructions", null: false, comment: "Instructions and context provided upon creation of the remand record"

t.timestamps null: false, comment: "Default timestamps"
end
end
end
5 changes: 5 additions & 0 deletions db/migrate/20200923200721_add_index_to_cavc_remand.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddIndexToCavcRemand < Caseflow::Migration
def change
add_safe_index :cavc_remands, [:appeal_id]
lomky marked this conversation as resolved.
Show resolved Hide resolved
end
end
19 changes: 19 additions & 0 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,25 @@
t.index ["updated_at"], name: "index_cached_user_attributes_on_updated_at"
end

create_table "cavc_remands", force: :cascade do |t|
t.bigint "appeal_id", null: false, comment: "Appeal that CAVC has remanded"
t.string "cavc_decision_type", null: false, comment: "CAVC decision type. Expecting 'remand', 'straight_reversal', or 'death_dismissal'"
t.string "cavc_docket_number", null: false, comment: "Docket number of the CAVC judgement"
t.string "cavc_judge_full_name", null: false, comment: "CAVC judge that passed the judgement on the remand"
t.datetime "created_at", null: false, comment: "Default timestamps"
t.bigint "created_by_id", null: false, comment: "User that created this record"
t.date "decision_date", null: false, comment: "Date CAVC issued a decision, according to the CAVC"
t.bigint "decision_issue_ids", default: [], comment: "Decision issues being remanded; IDs refer to decision_issues table. For a JMR, all decision issues on the previous appeal will be remanded. For a JMPR, only some", array: true
t.string "instructions", null: false, comment: "Instructions and context provided upon creation of the remand record"
t.date "judgement_date", comment: "Date CAVC issued a judgement, according to the CAVC"
t.date "mandate_date", comment: "Date that CAVC reported the mandate was given"
t.string "remand_subtype", comment: "Type of remand. If the cavc_decision_type is 'remand', expecting one of 'jmp', 'jmpr', or 'mdr'. Otherwise, this can be null."
t.boolean "represented_by_attorney", null: false, comment: "Whether or not the appellant was represented by an attorney"
t.datetime "updated_at", null: false, comment: "Default timestamps"
t.bigint "updated_by_id", null: false, comment: "User that updated this record. For MDR remands, judgement and mandate dates will be added after the record is first created."
t.index ["appeal_id"], name: "index_cavc_remands_on_appeal_id"
end

create_table "certification_cancellations", id: :serial, force: :cascade do |t|
t.string "cancellation_reason"
t.integer "certification_id"
Expand Down
19 changes: 18 additions & 1 deletion docs/schema/caseflow.csv
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,23 @@ cached_user_attributes,stafkey,string ∗,x,,,,,
cached_user_attributes,stitle,string (16),,,,,,
cached_user_attributes,svlj,string,,,,,,
cached_user_attributes,updated_at,datetime ∗,x,,,,x,
cavc_remands,,,,,,,,
cavc_remands,appeal_id,integer (8) ∗ FK,x,,x,,x,Appeal that CAVC has remanded
cavc_remands,cavc_decision_type,string ∗,x,,,,,"CAVC decision type. Expecting 'remand', 'straight_reversal', or 'death_dismissal'"
cavc_remands,cavc_docket_number,string ∗,x,,,,,Docket number of the CAVC judgement
cavc_remands,cavc_judge_full_name,string ∗,x,,,,,CAVC judge that passed the judgement on the remand
cavc_remands,created_at,datetime ∗,x,,,,,Default timestamps
cavc_remands,created_by_id,integer (8) ∗ FK,x,,x,,,User that created this record
cavc_remands,decision_date,date ∗,x,,,,,"Date CAVC issued a decision, according to the CAVC"
cavc_remands,decision_issue_ids,integer (8) ∗,x,,,,,"Decision issues being remanded; IDs refer to decision_issues table. For a JMR, all decision issues on the previous appeal will be remanded. For a JMPR, only some"
cavc_remands,id,integer (8) PK,x,x,,,,
cavc_remands,instructions,string ∗,x,,,,,Instructions and context provided upon creation of the remand record
cavc_remands,judgement_date,date ∗,x,,,,,"Date CAVC issued a judgement, according to the CAVC"
cavc_remands,mandate_date,date ∗,x,,,,,Date that CAVC reported the mandate was given
cavc_remands,remand_subtype,string ∗,x,,,,,"Type of remand. If the cavc_decision_type is 'remand', expecting one of 'jmp', 'jmpr', or 'mdr'. Otherwise, this can be null."
cavc_remands,represented_by_attorney,boolean ∗,x,,,,,Whether or not the appellant was represented by an attorney
cavc_remands,updated_at,datetime ∗,x,,,,,Default timestamps
cavc_remands,updated_by_id,integer (8) ∗ FK,x,,x,,,"User that updated this record. For MDR remands, judgement and mandate dates will be added after the record is first created."
certifications,,,,,,,,
certifications,already_certified,boolean,,,,,,
certifications,bgs_rep_address_line_1,string,,,,,,
Expand Down Expand Up @@ -886,6 +903,7 @@ request_issues,nonrating_issue_description,string,,,,,,The user entered descript
request_issues,notes,text,,,,,,"Notes added by the Claims Assistant when adding request issues. This may be used to capture handwritten notes on the form, or other comments the CA wants to capture."
request_issues,ramp_claim_id,string,,,,,,"If a rating issue was created as a result of an issue intaken for a RAMP Review, it will be connected to the former RAMP issue by its End Product's claim ID."
request_issues,rating_issue_associated_at,datetime,,,,,,Timestamp when a contention and its contested rating issue are associated in VBMS.
request_issues,type,string,,,,,,Determines whether the issue is a rating issue or a nonrating issue
request_issues,unidentified_issue_text,string,,,,,,User entered description if the request issue is neither a rating or a nonrating issue
request_issues,untimely_exemption,boolean,,,,,,"If the contested issue's decision date was more than a year before the receipt date, it is considered untimely (unless it is a Supplemental Claim). However, an exemption to the timeliness can be requested. If so, it is indicated here."
request_issues,untimely_exemption_notes,text,,,,,,Notes related to the untimeliness exemption requested.
Expand All @@ -894,7 +912,6 @@ request_issues,vacols_id,string,,,,,,The vacols_id of the legacy appeal that had
request_issues,vacols_sequence_id,integer,,,,,,"The vacols_sequence_id, for the specific issue on the legacy appeal which the Claims Assistant determined to match the request issue on the Decision Review. A combination of the vacols_id (for the legacy appeal), and vacols_sequence_id (for which issue on the legacy appeal), is required to identify the issue being opted-in."
request_issues,verified_unidentified_issue,boolean,,,,,,"A verified unidentified issue allows an issue whose rating data is missing to be intaken as a regular rating issue. In order to be marked as verified, a VSR needs to confirm that they were able to find the record of the decision for the issue."
request_issues,veteran_participant_id,string,,,,,,The veteran participant ID. This should be unique in upstream systems and used in the future to reconcile duplicates.
request_issues,type,string,,,,,,This determines whether the issue is a rating issue or a nonrating issue.
request_issues_updates,,,,,,,,"Keeps track of edits to request issues on a decision review that happen after the initial intake, such as removing and adding issues. When the decision review is processed in VBMS, this also tracks whether adding or removing contentions in VBMS for the update has succeeded."
request_issues_updates,after_request_issue_ids,integer ∗,x,,,,,An array of the active request issue IDs after a user has finished editing a decision review. Used with before_request_issue_ids to determine appropriate actions (such as which contentions need to be added).
request_issues_updates,attempted_at,datetime,,,,,,Timestamp for when the request issue update processing was last attempted.
Expand Down
9 changes: 9 additions & 0 deletions lib/caseflow/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,15 @@ def initialize(args)
end
end

class JmrAppealDecisionIssueMismatch < SerializableError
def initialize(args)
@code = args[:code] || 422
@decision_issue_ids = args[:decision_issue_ids]
@appeal_id = args[:appeal_id]
@message = args[:message] || "JMR remands must include all appeal decision issues."
end
end

class BvaDispatchTaskCountMismatch < SerializableError
# Add attr_accessors for testing
attr_accessor :user_id, :appeal_id, :tasks
Expand Down
88 changes: 88 additions & 0 deletions spec/controllers/cavc_remands_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# frozen_string_literal: true

RSpec.describe CavcRemandsController, type: :controller do
before do
Fakes::Initializer.load!
User.authenticate!(user: lit_support_user)
end

let!(:lit_support_user) do
LitigationSupport.singleton.add_user(create(:user))
LitigationSupport.singleton.users.first
end

describe "POST /appeals/:appeal_id/cavc_remands" do
let(:appeal) { create(:appeal) }
let(:appeal_id) { appeal.id }
let(:cavc_docket_number) { "123-1234567" }
let(:represented_by_attorney) { true }
let(:cavc_judge_full_name) { Constants::CAVC_JUDGE_FULL_NAMES.first }
let(:cavc_decision_type) { Constants::CAVC_DECISION_TYPES.keys.first }
let(:remand_subtype) { Constants::CAVC_REMAND_SUBTYPES.keys.first }
let(:decision_date) { 5.days.ago.to_date }
let(:judgement_date) { 4.days.ago.to_date }
let(:mandate_date) { 3.days.ago.to_date }
let(:decision_issues) do
create_list(
:decision_issue,
3,
:rating,
decision_review: appeal,
disposition: "denied",
description: "Decision issue description",
decision_text: "decision issue"
)
end
let(:decision_issue_ids) { decision_issues.map(&:id) }
let(:instructions) { "Intructions!" }

let(:params) do
{
appeal_id: appeal_id,
cavc_docket_number: cavc_docket_number,
represented_by_attorney: represented_by_attorney,
cavc_judge_full_name: cavc_judge_full_name,
cavc_decision_type: cavc_decision_type,
remand_subtype: remand_subtype,
decision_date: decision_date,
judgement_date: judgement_date,
mandate_date: mandate_date,
decision_issue_ids: decision_issue_ids,
instructions: instructions
}
end

subject { post :create, params: params }

context "with a Lit Support User" do
context "with insufficient parameters" do
it "does not create the CAVC remand" do
params.delete(:cavc_docket_number)
expect { subject }.to raise_error do |error|
expect(error).to be_a(ActionController::ParameterMissing)
end
end
end

context "with correct parameters" do
it "creates the CAVC remand" do
subject

expect(JSON.parse(response.body)["cavc_remand"]["appeal_id"]).to eq(appeal_id)
expect(response.status).to eq(201)
end
end
end

context "without a Lit Support User" do
it "does not create the CAVC remand" do
User.authenticate!(user: create(:user))
subject

expect(response.status).to eq(403)
expect(JSON.parse(response.body)["errors"][0]["title"])
.to eq("Only Litigation Support users can create CAVC Remands")
end
end
end
end
Loading