From 6fde0c30f7fb06910396148e09162f49fd6ec1a0 Mon Sep 17 00:00:00 2001 From: mchristiansonVA <95487885+mchristiansonVA@users.noreply.github.com> Date: Wed, 31 May 2023 09:25:34 -0400 Subject: [PATCH] Api 24834 section 7 service pay validation (#12771) * Incremental commit * Rubocop fixes * Test fixes & cleanup * Disable module length check * Update context descriptions for updated field names * Linting fix for quotes * Remove duplicate module declaration * Changes for review feedback * Add generated swagger --- .../v2/disability_compensation_validation.rb | 46 +++ .../swagger/claims_api/v2/dev/swagger.json | 8 +- .../disability_compensation/example.json | 2 +- .../form_526_json_api.json | 2 +- .../disability_compensation_request_spec.rb | 332 +++++++++++++++++- 5 files changed, 382 insertions(+), 8 deletions(-) diff --git a/modules/claims_api/app/controllers/concerns/claims_api/v2/disability_compensation_validation.rb b/modules/claims_api/app/controllers/concerns/claims_api/v2/disability_compensation_validation.rb index 2ec7e241aab..3df5197097d 100644 --- a/modules/claims_api/app/controllers/concerns/claims_api/v2/disability_compensation_validation.rb +++ b/modules/claims_api/app/controllers/concerns/claims_api/v2/disability_compensation_validation.rb @@ -14,6 +14,8 @@ def validate_form_526_submission_values! validate_form_526_current_mailing_address_country! # ensure homeless information is valid validate_form_526_veteran_homelessness! + # ensure military service pay information is valid + validate_form_526_service_pay! # ensure treament centers information is valid validate_form_526_treatments! # ensure service information is valid @@ -119,6 +121,50 @@ def missing_point_of_contact? homelessness_poc_attr.blank? && (currently_homeless_attr.present? || homelessness_risk_attr.present?) end + def validate_form_526_service_pay! + validate_form_526_military_retired_pay! + validate_form_526_future_military_retired_pay! + validate_form_526_separation_pay_received_date! + end + + def validate_form_526_military_retired_pay! + receiving_attr = form_attributes.dig('servicePay', 'receivingMilitaryRetiredPay') + future_attr = form_attributes.dig('servicePay', 'futureMilitaryRetiredPay') + + return if receiving_attr.nil? || future_attr.nil? + return unless receiving_attr == future_attr + + # EVSS does not allow both attributes to be the same value (unless that value is nil) + raise ::Common::Exceptions::InvalidFieldValue.new( + 'servicePay.militaryRetiredPay', + form_attributes['servicePay']['militaryRetiredPay'] + ) + end + + def validate_form_526_future_military_retired_pay! + future_attr = form_attributes.dig('servicePay', 'futureMilitaryRetiredPay') + future_explanation_attr = form_attributes.dig('servicePay', 'futureMilitaryRetiredPayExplanation') + return if future_attr.nil? + + if future_attr == true && future_explanation_attr.blank? + raise ::Common::Exceptions::UnprocessableEntity.new( + detail: "If 'servicePay.futureMilitaryRetiredPay' is true, then " \ + "'servicePay.futureMilitaryRetiredPayExplanation' is required" + ) + end + end + + def validate_form_526_separation_pay_received_date! + separation_pay_received_date = form_attributes.dig('servicePay', 'separationSeverancePay', + 'datePaymentReceived') + return if separation_pay_received_date.blank? + + return if Date.parse(separation_pay_received_date) < Time.zone.today + + raise ::Common::Exceptions::InvalidFieldValue.new('separationSeverancePay.datePaymentReceived', + separation_pay_received_date) + end + def validate_form_526_treatments! treatments = form_attributes['treatments'] return if treatments.blank? diff --git a/modules/claims_api/app/swagger/claims_api/v2/dev/swagger.json b/modules/claims_api/app/swagger/claims_api/v2/dev/swagger.json index c915d1ee045..1822d7f11b4 100644 --- a/modules/claims_api/app/swagger/claims_api/v2/dev/swagger.json +++ b/modules/claims_api/app/swagger/claims_api/v2/dev/swagger.json @@ -3938,9 +3938,7 @@ "branchOfService": "Army", "monthlyAmount": 840.75 }, - "retiredStatus": [ - - ], + "retiredStatus": "RETIRED", "receivedSeparationOrSeverancePay": false, "separationSeverancePay": { "datePaymentReceived": "2018-07-31", @@ -4594,8 +4592,8 @@ "id": "1", "type": "intent_to_file", "attributes": { - "creationDate": "2023-05-24", - "expirationDate": "2024-05-24", + "creationDate": "2023-05-26", + "expirationDate": "2024-05-26", "type": "compensation", "status": "active" } diff --git a/modules/claims_api/config/schemas/v2/request_bodies/disability_compensation/example.json b/modules/claims_api/config/schemas/v2/request_bodies/disability_compensation/example.json index cbe3952ab41..c1afd0c6f54 100644 --- a/modules/claims_api/config/schemas/v2/request_bodies/disability_compensation/example.json +++ b/modules/claims_api/config/schemas/v2/request_bodies/disability_compensation/example.json @@ -184,7 +184,7 @@ "branchOfService": "Army", "monthlyAmount": 840.75 }, - "retiredStatus": [], + "retiredStatus": "RETIRED", "receivedSeparationOrSeverancePay": false, "separationSeverancePay": { "datePaymentReceived": "2018-07-31", diff --git a/modules/claims_api/spec/fixtures/v2/veterans/disability_compensation/form_526_json_api.json b/modules/claims_api/spec/fixtures/v2/veterans/disability_compensation/form_526_json_api.json index 29c9158e468..1b86a829854 100644 --- a/modules/claims_api/spec/fixtures/v2/veterans/disability_compensation/form_526_json_api.json +++ b/modules/claims_api/spec/fixtures/v2/veterans/disability_compensation/form_526_json_api.json @@ -204,7 +204,7 @@ "retiredStatus": "RETIRED", "receivedSeparationOrSeverancePay": false, "separationSeverancePay": { - "datePaymentReceived": "7169-07-31", + "datePaymentReceived": "2022-03-01", "branchOfService": "Naval Academy", "preTaxAmountReceived": 379.25 } diff --git a/modules/claims_api/spec/requests/v2/veterans/disability_compensation_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/disability_compensation_request_spec.rb index 0a6bf1b1570..4171733052e 100644 --- a/modules/claims_api/spec/requests/v2/veterans/disability_compensation_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/disability_compensation_request_spec.rb @@ -16,7 +16,6 @@ before do stub_poa_verification - stub_mpi Timecop.freeze(Time.zone.now) end @@ -834,6 +833,337 @@ end end + describe "'servicePay validations'" do + describe 'retired pay validations' do + describe "'receivingMilitaryRetiredPay' and 'futureMilitaryRetiredPay' validations" do + let(:service_pay_attribute) do + { + receivingMilitaryRetiredPay: receiving, + futureMilitaryRetiredPay: future, + futureMilitaryRetiredPayExplanation: 'Some explanation', + militaryRetiredPay: { + branchOfService: 'Air Force' + } + } + end + + context "when 'receivingMilitaryRetiredPay' and 'futureMilitaryRetiredPay' are equal but not 'nil'" do + context "when both are 'true'" do + let(:receiving) { true } + let(:future) { true } + + it 'responds with a bad request' do + with_okta_user(scopes) do |auth_header| + VCR.use_cassette('evss/claims/claims') do + VCR.use_cassette('brd/countries') do + json_data = JSON.parse data + params = json_data + params['data']['attributes']['servicePay'] = service_pay_attribute + post path, params: params.to_json, headers: headers.merge(auth_header) + expect(response).to have_http_status(:bad_request) + end + end + end + end + end + + context "when both are 'false'" do + let(:receiving) { false } + let(:future) { false } + + it 'responds with a bad request' do + with_okta_user(scopes) do |auth_header| + VCR.use_cassette('brd/countries') do + json_data = JSON.parse data + params = json_data + params['data']['attributes']['servicePay'] = service_pay_attribute + post path, params: params.to_json, headers: headers.merge(auth_header) + expect(response).to have_http_status(:bad_request) + end + end + end + end + end + + context "when 'receivingMilitaryRetiredPay' and 'futureMilitaryRetiredPay' are not equal" do + context "when 'receivingMilitaryRetiredPay' is 'false' and 'futureMilitaryRetiredPay' is 'true'" do + let(:receiving) { false } + let(:future) { true } + + it 'responds with a 200' do + with_okta_user(scopes) do |auth_header| + VCR.use_cassette('evss/claims/claims') do + VCR.use_cassette('brd/countries') do + json_data = JSON.parse data + params = json_data + params['data']['attributes']['servicePay'] = service_pay_attribute + post path, params: params.to_json, headers: headers.merge(auth_header) + expect(response).to have_http_status(:ok) + end + end + end + end + end + + context "when 'receivingMilitaryRetiredPay' is 'true' and 'futureMilitaryRetiredPay' is 'false'" do + let(:receiving) { true } + let(:future) { false } + + it 'responds with a 200' do + with_okta_user(scopes) do |auth_header| + VCR.use_cassette('evss/claims/claims') do + VCR.use_cassette('brd/countries') do + json_data = JSON.parse data + params = json_data + params['data']['attributes']['servicePay'] = service_pay_attribute + post path, params: params.to_json, headers: headers.merge(auth_header) + expect(response).to have_http_status(:ok) + end + end + end + end + end + end + end + + describe "'payment'" do + let(:service_pay_attribute) do + { + receivingMilitaryRetiredPay: true, + futureMilitaryRetiredPay: false, + militaryRetiredPay: { + branchOfService: 'Air Force', + monthlyAmount: military_retired_payment_amount + } + } + end + + context "when 'monthlyAmount' is below the minimum" do + let(:military_retired_payment_amount) { 0 } + + it 'responds with an unprocessible entity' do + with_okta_user(scopes) do |auth_header| + VCR.use_cassette('brd/countries') do + json_data = JSON.parse data + params = json_data + params['data']['attributes']['servicePay'] = service_pay_attribute + post path, params: params.to_json, headers: headers.merge(auth_header) + expect(response).to have_http_status(:unprocessable_entity) + end + end + end + end + + context "when 'monthlyAmount' is above the maximum" do + let(:military_retired_payment_amount) { 1_000_000 } + + it 'responds with an unprocessible entity' do + with_okta_user(scopes) do |auth_header| + VCR.use_cassette('evss/claims/claims') do + VCR.use_cassette('brd/countries') do + json_data = JSON.parse data + params = json_data + params['data']['attributes']['servicePay'] = service_pay_attribute + post path, params: params.to_json, headers: headers.merge(auth_header) + expect(response).to have_http_status(:unprocessable_entity) + end + end + end + end + end + + context "when 'monthlyAmount' is within limits" do + let(:military_retired_payment_amount) { 100 } + + it 'responds with a 200' do + with_okta_user(scopes) do |auth_header| + VCR.use_cassette('evss/claims/claims') do + VCR.use_cassette('brd/countries') do + json_data = JSON.parse data + params = json_data + params['data']['attributes']['servicePay'] = service_pay_attribute + post path, params: params.to_json, headers: headers.merge(auth_header) + expect(response).to have_http_status(:ok) + end + end + end + end + end + end + + describe "'futurePayExplanation'" do + context "when 'futureMilitaryRetiredPay' is 'true'" do + let(:future_military_retired_pay) { true } + + context "when 'futureMilitaryRetiredPayExplanation' is not provided" do + let(:service_pay_attribute) do + { + receivingMilitaryRetiredPay: false, + futureMilitaryRetiredPay: future_military_retired_pay, + militaryRetiredPay: { + branchOfService: 'Air Force' + } + } + end + + it 'responds with an unprocessible entity' do + with_okta_user(scopes) do |auth_header| + VCR.use_cassette('brd/countries') do + json_data = JSON.parse data + params = json_data + params['data']['attributes']['servicePay'] = service_pay_attribute + post path, params: params.to_json, headers: headers.merge(auth_header) + expect(response).to have_http_status(:unprocessable_entity) + end + end + end + end + + context "when 'futureMilitaryRetiredPayExplanation' is provided" do + let(:service_pay_attribute) do + { + receivingMilitaryRetiredPay: false, + futureMilitaryRetiredPay: future_military_retired_pay, + futureMilitaryRetiredPayExplanation: 'Retiring soon.', + militaryRetiredPay: { + branchOfService: 'Air Force' + } + } + end + + it 'responds with a 200' do + with_okta_user(scopes) do |auth_header| + VCR.use_cassette('evss/claims/claims') do + VCR.use_cassette('brd/countries') do + json_data = JSON.parse data + params = json_data + params['data']['attributes']['servicePay'] = service_pay_attribute + post path, params: params.to_json, headers: headers.merge(auth_header) + expect(response).to have_http_status(:ok) + end + end + end + end + end + end + end + end + + describe "'servicePay.separationSeverancePay' validations" do + describe "'payment'" do + let(:service_pay_attribute) do + { + receivedSeparationOrSeverancePay: true, + separationSeverancePay: { + datePaymentReceived: (Time.zone.today - 1.year).to_s, + branchOfService: 'Air Force', + preTaxAmountReceived: separation_payment_amount + } + } + end + + context "when 'preTaxAmountReceived' is below the minimum" do + let(:separation_payment_amount) { 0 } + + it 'responds with an unprocessible entity' do + with_okta_user(scopes) do |auth_header| + VCR.use_cassette('brd/countries') do + json_data = JSON.parse data + params = json_data + params['data']['attributes']['servicePay'] = service_pay_attribute + post path, params: params.to_json, headers: headers.merge(auth_header) + expect(response).to have_http_status(:unprocessable_entity) + end + end + end + end + + context "when 'preTaxAmountReceived' is above the maximum" do + let(:separation_payment_amount) { 1_000_000 } + + it 'responds with an unprocessible entity' do + with_okta_user(scopes) do |auth_header| + VCR.use_cassette('evss/claims/claims') do + VCR.use_cassette('brd/countries') do + json_data = JSON.parse data + params = json_data + params['data']['attributes']['servicePay'] = service_pay_attribute + post path, params: params.to_json, headers: headers.merge(auth_header) + expect(response).to have_http_status(:unprocessable_entity) + end + end + end + end + end + + context "when 'preTaxAmountReceived' is within limits" do + let(:separation_payment_amount) { 100 } + + it 'responds with a 200' do + with_okta_user(scopes) do |auth_header| + VCR.use_cassette('evss/claims/claims') do + VCR.use_cassette('brd/countries') do + json_data = JSON.parse data + params = json_data + params['data']['attributes']['servicePay'] = service_pay_attribute + post path, params: params.to_json, headers: headers.merge(auth_header) + expect(response).to have_http_status(:ok) + end + end + end + end + end + end + + describe "'datePaymentReceived'" do + let(:service_pay_attribute) do + { + receivedSeparationOrSeverancePay: true, + separationSeverancePay: { + datePaymentReceived: received_date, + branchOfService: 'Air Force', + preTaxAmountReceived: 100 + } + } + end + + context "when 'datePaymentReceived' is not in the past" do + let(:received_date) { (Time.zone.today + 1.day).to_s } + + it 'responds with a bad request' do + with_okta_user(scopes) do |auth_header| + VCR.use_cassette('brd/countries') do + json_data = JSON.parse data + params = json_data + params['data']['attributes']['servicePay'] = service_pay_attribute + post path, params: params.to_json, headers: headers.merge(auth_header) + expect(response).to have_http_status(:bad_request) + end + end + end + end + + context "when 'datePaymentReceived' is in the past" do + let(:received_date) { (Time.zone.today - 1.year).to_s } + + it 'responds with a 200' do + with_okta_user(scopes) do |auth_header| + VCR.use_cassette('evss/claims/claims') do + VCR.use_cassette('brd/countries') do + json_data = JSON.parse data + params = json_data + params['data']['attributes']['servicePay'] = service_pay_attribute + post path, params: params.to_json, headers: headers.merge(auth_header) + expect(response).to have_http_status(:ok) + end + end + end + end + end + end + end + end + describe 'Validation of treament elements' do context 'when treatment startDate is included and in the correct pattern' do it 'returns a 200' do