diff --git a/config/settings.yml b/config/settings.yml index f90840cc4c4..916e6b302bc 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -1288,6 +1288,8 @@ vanotify: form21_674_action_needed_email: form21_674_action_needed_email_template_id form21_0966_confirmation_email: form21_0966_confirmation_email_template_id form21_0966_error_email: form21_0966_error_email_template_id + form21_0966_received_email: form21_0966_received_email_template_id + form21_0966_itf_api_received_email: form21_0966_itf_api_received_email_template_id form21_0972_confirmation_email: form21_0972_confirmation_email_template_id form21_0972_error_email: form21_0972_error_email_template_id form21_0972_received_email: form21_0972_received_email_template_id diff --git a/config/settings/production.yml b/config/settings/production.yml index 94c004502e0..6546f69e0a5 100644 --- a/config/settings/production.yml +++ b/config/settings/production.yml @@ -1,2 +1,44 @@ +betamocks: + recording: false +central_mail: + upload: + enabled: true +check_in: + authentication: + retry_attempt_expiry: 604800 + chip_api_v2: + mock: false + timeout: 30 + lorota_v2: + mock: false + travel_reimbursement_api: + redis_token_expiry: 3540 +clamav: + mock: false +coverband: + github_organization: department-of-veterans-affairs +decision_review: + mock: false + pdf_validation: + enabled: true +dogstatsd: + enabled: true +evss: + s3: + uploads_enabled: true +expiry_scanner: + slack: + channel_id: C24RH0W11 +flipper: + github_organization: department-of-veterans-affairs + mute_logs: false +form_10_10cg: + poa: + s3: + enabled: true +form526_backup: + submission_method: single +google_analytics_cvu: + type: service_account hca: ca: [] diff --git a/modules/simple_forms_api/app/controllers/simple_forms_api/v1/uploads_controller.rb b/modules/simple_forms_api/app/controllers/simple_forms_api/v1/uploads_controller.rb index 3f2b1194f31..953021afff3 100644 --- a/modules/simple_forms_api/app/controllers/simple_forms_api/v1/uploads_controller.rb +++ b/modules/simple_forms_api/app/controllers/simple_forms_api/v1/uploads_controller.rb @@ -91,13 +91,13 @@ def handle_210966_authenticated form.track_user_identity(confirmation_number) if confirmation_number && Flipper.enabled?(:simple_forms_email_confirmations) - send_confirmation_email(parsed_form_data, get_form_id, confirmation_number) + send_intent_received_email(parsed_form_data, confirmation_number, expiration_date) end json_for210966(confirmation_number, expiration_date, existing_intents) - rescue Common::Exceptions::UnprocessableEntity, Net::ReadTimeout => e + rescue Common::Exceptions::UnprocessableEntity, Exceptions::BenefitsClaimsApiDownError => e # Common::Exceptions::UnprocessableEntity: There is an authentication issue with the Intent to File API - # Faraday::TimeoutError: The Intent to File API is down or timed out + # Exceptions::BenefitsClaimsApiDownError: The Intent to File API is down or timed out # In either case, we revert to sending a PDF to Central Mail through the Benefits Intake API prepare_params_for_benefits_intake_and_log_error(e) submit_form_to_benefits_intake @@ -292,6 +292,22 @@ def send_confirmation_email(parsed_form_data, form_id, confirmation_number) ) notification_email.send end + + def send_intent_received_email(parsed_form_data, confirmation_number, expiration_date) + config = { + form_data: parsed_form_data, + form_number: 'vba_21_0966_intent_api', + confirmation_number:, + date_submitted: Time.zone.today.strftime('%B %d, %Y'), + expiration_date: + } + notification_email = SimpleFormsApi::NotificationEmail.new( + config, + notification_type: :received, + user: @current_user + ) + notification_email.send + end end end end diff --git a/modules/simple_forms_api/app/services/simple_forms_api/intent_to_file.rb b/modules/simple_forms_api/app/services/simple_forms_api/intent_to_file.rb index 53cceef8dbe..a835f7446b5 100644 --- a/modules/simple_forms_api/app/services/simple_forms_api/intent_to_file.rb +++ b/modules/simple_forms_api/app/services/simple_forms_api/intent_to_file.rb @@ -26,9 +26,7 @@ def submit type = benefit_type.downcase next if existing_intents[type] - response = benefits_claims_lighthouse_service.create_intent_to_file(type, ssn) - confirmation_number = response.dig('data', 'id') - expiration_date = response.dig('data', 'attributes', 'expirationDate') + confirmation_number, expiration_date = create_intent_to_file(type, ssn) end user_account_uuid = user.user_account_uuid @@ -61,6 +59,21 @@ def benefits_claims_lighthouse_service @benefits_claims_lighthouse_service ||= BenefitsClaims::Service.new(icn) end + def create_intent_to_file(type, ssn) + response = benefits_claims_lighthouse_service.create_intent_to_file(type, ssn) + [response.dig('data', 'id'), response.dig('data', 'attributes', 'expirationDate')] + rescue Common::Exceptions::ResourceNotFound => e + Rails.logger.error( + 'Simple forms api - Benefits Claims API, intent to file endpoint is down', + { + intent_type: type, + form_number: params[:form_number], + error: e + } + ) + raise Exceptions::BenefitsClaimsApiDownError + end + def existing_compensation_intent @existing_compensation_intent ||= benefits_claims_lighthouse_service.get_intent_to_file('compensation')&.dig('data', 'attributes') diff --git a/modules/simple_forms_api/app/services/simple_forms_api/notification_email.rb b/modules/simple_forms_api/app/services/simple_forms_api/notification_email.rb index d62a29b0e5e..cb2a600b152 100644 --- a/modules/simple_forms_api/app/services/simple_forms_api/notification_email.rb +++ b/modules/simple_forms_api/app/services/simple_forms_api/notification_email.rb @@ -2,8 +2,8 @@ module SimpleFormsApi class NotificationEmail - attr_reader :form_number, :confirmation_number, :date_submitted, :lighthouse_updated_at, :notification_type, :user, - :user_account, :form_data + attr_reader :form_number, :confirmation_number, :date_submitted, :expiration_date, :lighthouse_updated_at, + :notification_type, :user, :user_account, :form_data TEMPLATE_IDS = { 'vba_21_0845' => { @@ -19,7 +19,10 @@ class NotificationEmail 'vba_21_0966' => { confirmation: Settings.vanotify.services.va_gov.template_id.form21_0966_confirmation_email, error: Settings.vanotify.services.va_gov.template_id.form21_0966_error_email, - received: nil + received: Settings.vanotify.services.va_gov.template_id.form21_0966_received_email + }, + 'vba_21_0966_intent_api' => { + received: Settings.vanotify.services.va_gov.template_id.form21_0966_itf_api_received_email }, 'vba_21_0972' => { confirmation: Settings.vanotify.services.va_gov.template_id.form21_0972_confirmation_email, @@ -66,6 +69,7 @@ def initialize(config, notification_type: :confirmation, user: nil, user_account @form_number = config[:form_number] @confirmation_number = config[:confirmation_number] @date_submitted = config[:date_submitted] + @expiration_date = config[:expiration_date] @lighthouse_updated_at = config[:lighthouse_updated_at] @notification_type = notification_type @user = user @@ -90,11 +94,16 @@ def send(at: nil) def check_missing_keys(config) missing_keys = %i[form_data form_number confirmation_number date_submitted].select { |key| config[key].nil? } + if config[:form_number] == 'vba_21_0966_intent_api' && config[:expiration_date].nil? + missing_keys << :expiration_date + end raise ArgumentError, "Missing keys: #{missing_keys.join(', ')}" if missing_keys.any? end def flipper? - Flipper.enabled?(:"form#{form_number.gsub('vba_', '')}_confirmation_email") + number = form_number + number = 'vba_21_0966' if form_number.start_with? 'vba_21_0966' + Flipper.enabled?(:"form#{number.gsub('vba_', '')}_confirmation_email") end def enqueue_email(at, template_id) @@ -183,7 +192,7 @@ def get_email_address_from_form_data form21_0845_contact_info[0] when 'vba_21p_0847', 'vba_21_0972' form_data['preparer_email'] - when 'vba_21_0966' + when 'vba_21_0966', 'vba_21_0966_intent_api' form21_0966_email_address when 'vba_21_4142' form_data.dig('veteran', 'email') @@ -207,7 +216,7 @@ def get_first_name_from_form_data form21_0845_contact_info[1] when 'vba_21p_0847' form_data.dig('preparer_name', 'first') - when 'vba_21_0966' + when 'vba_21_0966', 'vba_21_0966_intent_api' form21_0966_first_name when 'vba_21_0972' form_data.dig('preparer_full_name', 'first') @@ -248,7 +257,7 @@ def get_first_name_from_user end def get_personalization(first_name) - if @form_number == 'vba_21_0966' + if @form_number.start_with? 'vba_21_0966' default_personalization(first_name).merge(form21_0966_personalization) else default_personalization(first_name) @@ -359,18 +368,31 @@ def form21_0966_email_address end def form21_0966_personalization + intent_to_file_benefits, intent_to_file_benefits_links = get_intent_to_file_benefits_variables + { + 'intent_to_file_benefits' => intent_to_file_benefits, + 'intent_to_file_benefits_links' => intent_to_file_benefits_links, + 'itf_api_expiration_date' => expiration_date + } + end + + def get_intent_to_file_benefits_variables benefits = @form_data['benefit_selection'] - intent_to_file_benefits = if benefits['compensation'] && benefits['pension'] - 'Disability Compensation (VA Form 21-526EZ) and Pension (VA Form 21P-527EZ)' - elsif benefits['compensation'] - 'Disability Compensation (VA Form 21-526EZ)' - elsif benefits['pension'] - 'Pension (VA Form 21P-527EZ)' - elsif benefits['survivor'] - 'Survivors Pension and/or Dependency and Indemnity Compensation (DIC)' \ - ' (VA Form 21P-534 or VA Form 21P-534EZ)' - end - { 'intent_to_file_benefits' => intent_to_file_benefits } + if benefits['compensation'] && benefits['pension'] + ['disability compensation and Veterans pension benefits', + '[File for disability compensation (VA Form 21-526EZ)]' \ + '(https://www.va.gov/disability/file-disability-claim-form-21-526ez/introduction) and [Apply for Veterans ' \ + 'Pension benefits (VA Form 21P-527EZ)](https://www.va.gov/find-forms/about-form-21p-527ez/)'] + elsif benefits['compensation'] + ['disability compensation', + '[File for disability compensation (VA Form 21-526EZ)](https://www.va.gov/disability/file-disability-claim-form-21-526ez/introduction)'] + elsif benefits['pension'] + ['Veterans pension benefits', + '[Apply for Veterans Pension benefits (VA Form 21P-527EZ)](https://www.va.gov/find-forms/about-form-21p-527ez/)'] + elsif benefits['survivor'] + ['survivors pension benefits', + '[Apply for DIC, Survivors Pension, and/or Accrued Benefits (VA Form 21P-534EZ)](https://www.va.gov/find-forms/about-form-21p-534ez/)'] + end end def form40_10007_first_name diff --git a/modules/simple_forms_api/lib/simple_forms_api.rb b/modules/simple_forms_api/lib/simple_forms_api.rb index 84c928a7f22..18d6fc212e3 100644 --- a/modules/simple_forms_api/lib/simple_forms_api.rb +++ b/modules/simple_forms_api/lib/simple_forms_api.rb @@ -75,5 +75,7 @@ def aggregate_words(parsed_params) words.uniq.sort_by(&:length).reverse end end + + class BenefitsClaimsApiDownError < RuntimeError; end end end diff --git a/modules/simple_forms_api/spec/requests/simple_forms_api/v1/simple_forms_spec.rb b/modules/simple_forms_api/spec/requests/simple_forms_api/v1/simple_forms_spec.rb index e507b06a50d..076bf2e128e 100644 --- a/modules/simple_forms_api/spec/requests/simple_forms_api/v1/simple_forms_spec.rb +++ b/modules/simple_forms_api/spec/requests/simple_forms_api/v1/simple_forms_spec.rb @@ -852,9 +852,11 @@ end context 'veteran preparer' do - it 'successful submission' do + let(:expiration_date) { Time.zone.now } + + it 'sends the received email' do allow_any_instance_of(SimpleFormsApi::IntentToFile) - .to receive(:submit).and_return([confirmation_number, Time.zone.now]) + .to receive(:submit).and_return([confirmation_number, expiration_date]) allow_any_instance_of(SimpleFormsApi::IntentToFile) .to receive(:existing_intents) .and_return({ 'compensation' => 'false', 'pension' => 'false', 'survivor' => 'false' }) @@ -867,14 +869,16 @@ expect(VANotify::EmailJob).to have_received(:perform_async).with( 'abraham.lincoln@vets.gov', - 'form21_0966_confirmation_email_template_id', + 'form21_0966_itf_api_received_email_template_id', { 'first_name' => 'Veteran', 'date_submitted' => Time.zone.today.strftime('%B %d, %Y'), 'confirmation_number' => confirmation_number, 'lighthouse_updated_at' => nil, - 'intent_to_file_benefits' => 'Survivors Pension and/or Dependency and Indemnity Compensation (DIC)' \ - ' (VA Form 21P-534 or VA Form 21P-534EZ)' + 'intent_to_file_benefits' => 'survivors pension benefits', + 'intent_to_file_benefits_links' => '[Apply for DIC, Survivors Pension, and/or Accrued Benefits ' \ + '(VA Form 21P-534EZ)](https://www.va.gov/find-forms/about-form-21p-534ez/)', + 'itf_api_expiration_date' => expiration_date } ) end @@ -897,8 +901,10 @@ 'date_submitted' => Time.zone.today.strftime('%B %d, %Y'), 'confirmation_number' => confirmation_number, 'lighthouse_updated_at' => nil, - 'intent_to_file_benefits' => 'Survivors Pension and/or Dependency and Indemnity Compensation (DIC)' \ - ' (VA Form 21P-534 or VA Form 21P-534EZ)' + 'intent_to_file_benefits' => 'survivors pension benefits', + 'intent_to_file_benefits_links' => '[Apply for DIC, Survivors Pension, and/or Accrued Benefits ' \ + '(VA Form 21P-534EZ)](https://www.va.gov/find-forms/about-form-21p-534ez/)', + 'itf_api_expiration_date' => nil } ) end diff --git a/modules/simple_forms_api/spec/services/intent_to_file_spec.rb b/modules/simple_forms_api/spec/services/intent_to_file_spec.rb index 64a3d36cc34..afe1ab22b8b 100644 --- a/modules/simple_forms_api/spec/services/intent_to_file_spec.rb +++ b/modules/simple_forms_api/spec/services/intent_to_file_spec.rb @@ -4,22 +4,55 @@ require SimpleFormsApi::Engine.root.join('spec', 'spec_helper.rb') describe SimpleFormsApi::IntentToFile do - describe 'an Intent To File has previously been submitted' do - let(:params) do + let(:ssn) { 'fake-ssn' } + let(:icn) { '123498767V234859' } + let(:params) do + { + 'benefit_selection' => { + 'COMPENSATION' => true + }, + 'preparer_identification' => 'VETERAN', + 'veteran_id' => { + 'ssn' => ssn + } + } + end + + describe '#submit' do + let(:expiration_date) { 'fake-expiration-date' } + let(:id) { 'fake-id' } + let(:compensation_intent) do { - 'benefit_selection' => { - 'COMPENSATION' => true - }, - 'preparer_identification' => 'VETERAN', - 'preparer_id' => { - 'ssn' => 'fake-ssn' + 'data' => { + 'id' => id, + 'attributes' => { + 'expirationDate' => expiration_date + } } } end + context 'lighthouse service is down' do + it 'raises Exceptions::BenefitsClaimsApiDownError' do + intent_to_file_service = SimpleFormsApi::IntentToFile.new(build(:user), params) + allow_any_instance_of(BenefitsClaims::Service).to receive(:get_intent_to_file).with('compensation') + .and_return(nil) + allow_any_instance_of(BenefitsClaims::Service).to receive(:get_intent_to_file).with('pension').and_return(nil) + allow_any_instance_of(BenefitsClaims::Service).to receive(:get_intent_to_file).with('survivor').and_return(nil) + allow_any_instance_of(BenefitsClaims::Service).to receive(:create_intent_to_file).with( + 'compensation', + ssn + ).and_raise(Common::Exceptions::ResourceNotFound) + + expect { intent_to_file_service.submit }.to raise_error(SimpleFormsApi::Exceptions::BenefitsClaimsApiDownError) + end + end + end + + describe 'an Intent To File has previously been submitted' do it 'returns no confirmation number and no expiration date if no new ITF is filed' do user = build(:user) - allow(user).to receive_messages(icn: '123498767V234859', participant_id: 'fake-participant-id') + allow(user).to receive_messages(icn:, participant_id: 'fake-participant-id') intent_to_file_service = SimpleFormsApi::IntentToFile.new(user, params) expiration_date = 'fake-expiration-date' compensation_intent = { @@ -41,22 +74,8 @@ end describe 'no Intent to File has previously been submitted' do - let(:ssn) { 'fake-ssn' } - let(:params) do - { - 'benefit_selection' => { - 'COMPENSATION' => true - }, - 'preparer_identification' => 'VETERAN', - 'veteran_id' => { - 'ssn' => ssn - } - } - end - it 'return the expiration date of a newly-created Intent To File' do - user = build(:user) - intent_to_file_service = SimpleFormsApi::IntentToFile.new(user, params) + intent_to_file_service = SimpleFormsApi::IntentToFile.new(build(:user), params) expiration_date = 'fake-expiration-date' id = 'fake-id' compensation_intent = { diff --git a/modules/simple_forms_api/spec/services/notification_email_spec.rb b/modules/simple_forms_api/spec/services/notification_email_spec.rb index db39339c802..8c7ec6c455c 100644 --- a/modules/simple_forms_api/spec/services/notification_email_spec.rb +++ b/modules/simple_forms_api/spec/services/notification_email_spec.rb @@ -730,7 +730,35 @@ end let(:user) { create(:user, :loa3) } - context 'template_id is provided', unless: notification_type == :received do + it 'sends the email' do + allow(VANotify::EmailJob).to receive(:perform_async) + + subject = described_class.new(config, notification_type:, user:) + + subject.send + + expect(VANotify::EmailJob).to have_received(:perform_async).with( + user.va_profile_email, + "form21_0966_#{notification_type}_email_template_id", + { + 'first_name' => 'Veteran', + 'date_submitted' => date_submitted, + 'confirmation_number' => 'confirmation_number', + 'lighthouse_updated_at' => nil, + 'intent_to_file_benefits' => 'survivors pension benefits', + 'intent_to_file_benefits_links' => '[Apply for DIC, Survivors Pension, and/or Accrued Benefits ' \ + '(VA Form 21P-534EZ)](https://www.va.gov/find-forms/about-form-21p-534ez/)', + 'itf_api_expiration_date' => nil + } + ) + end + + context 'preparer is surviving dependent' do + before do + data['preparer_identification'] = 'SURVIVING_DEPENDENT' + config[:form_data] = data + end + it 'sends the email' do allow(VANotify::EmailJob).to receive(:perform_async) @@ -739,23 +767,39 @@ subject.send expect(VANotify::EmailJob).to have_received(:perform_async).with( - user.va_profile_email, + 'survivor@dependent.com', "form21_0966_#{notification_type}_email_template_id", { - 'first_name' => 'Veteran', + 'first_name' => 'I', 'date_submitted' => date_submitted, 'confirmation_number' => 'confirmation_number', 'lighthouse_updated_at' => nil, - 'intent_to_file_benefits' => 'Survivors Pension and/or Dependency and Indemnity Compensation (DIC)' \ - ' (VA Form 21P-534 or VA Form 21P-534EZ)' + 'intent_to_file_benefits' => 'survivors pension benefits', + 'intent_to_file_benefits_links' => '[Apply for DIC, Survivors Pension, and/or Accrued Benefits ' \ + '(VA Form 21P-534EZ)](https://www.va.gov/find-forms/about-form-21p-534ez/)', + 'itf_api_expiration_date' => nil } ) end + end + end + + describe '21_0966 through Intent to File API', if: notification_type == :received do + let(:date_submitted) { Time.zone.today.strftime('%B %d, %Y') } + let(:expiration_date) { 1.year.from_now.strftime('%B %d, %Y') } + let(:data) do + fixture_path = Rails.root.join( + 'modules', 'simple_forms_api', 'spec', 'fixtures', 'form_json', 'vba_21_0966.json' + ) + JSON.parse(fixture_path.read) + end + let(:user) { create(:user, :loa3) } - context 'preparer is surviving dependent' do - before do - data['preparer_identification'] = 'SURVIVING_DEPENDENT' - config[:form_data] = data + context 'template_id is provided' do + context 'expiration_date is provided' do + let(:config) do + { form_data: data, form_number: 'vba_21_0966_intent_api', + confirmation_number: 'confirmation_number', date_submitted:, expiration_date: } end it 'sends the email' do @@ -766,22 +810,70 @@ subject.send expect(VANotify::EmailJob).to have_received(:perform_async).with( - 'survivor@dependent.com', - "form21_0966_#{notification_type}_email_template_id", + user.va_profile_email, + 'form21_0966_itf_api_received_email_template_id', { - 'first_name' => 'I', + 'first_name' => 'Veteran', 'date_submitted' => date_submitted, 'confirmation_number' => 'confirmation_number', 'lighthouse_updated_at' => nil, - 'intent_to_file_benefits' => 'Survivors Pension and/or Dependency and Indemnity Compensation (DIC)' \ - ' (VA Form 21P-534 or VA Form 21P-534EZ)' + 'intent_to_file_benefits' => 'survivors pension benefits', + 'intent_to_file_benefits_links' => '[Apply for DIC, Survivors Pension, and/or Accrued Benefits ' \ + '(VA Form 21P-534EZ)](https://www.va.gov/find-forms/about-form-21p-534ez/)', + 'itf_api_expiration_date' => expiration_date } ) end + + context 'preparer is surviving dependent' do + before do + data['preparer_identification'] = 'SURVIVING_DEPENDENT' + config[:form_data] = data + end + + it 'sends the email' do + allow(VANotify::EmailJob).to receive(:perform_async) + + subject = described_class.new(config, notification_type:, user:) + + subject.send + + expect(VANotify::EmailJob).to have_received(:perform_async).with( + 'survivor@dependent.com', + 'form21_0966_itf_api_received_email_template_id', + { + 'first_name' => 'I', + 'date_submitted' => date_submitted, + 'confirmation_number' => 'confirmation_number', + 'lighthouse_updated_at' => nil, + 'intent_to_file_benefits' => 'survivors pension benefits', + 'intent_to_file_benefits_links' => '[Apply for DIC, Survivors Pension, and/or Accrued Benefits ' \ + '(VA Form 21P-534EZ)](https://www.va.gov/find-forms/about-form-21p-534ez/)', + 'itf_api_expiration_date' => expiration_date + } + ) + end + end + end + + context 'expiration_date is missing' do + let(:config) do + { form_data: data, form_number: 'vba_21_0966_intent_api', + confirmation_number: 'confirmation_number', date_submitted: } + end + + it 'raises ArgumentError' do + expect { described_class.new(config, notification_type:, user:) }.to raise_error(ArgumentError) + end end end - context 'template_id is missing', if: notification_type == :received do + context 'template_id is missing', unless: notification_type == :received do + let(:config) do + { form_data: data, form_number: 'vba_21_0966_intent_api', + confirmation_number: 'confirmation_number', date_submitted:, expiration_date: } + end + let(:data) do fixture_path = Rails.root.join( 'modules', 'simple_forms_api', 'spec', 'fixtures', 'form_json', 'vba_21_0966.json'