diff --git a/Gemfile b/Gemfile index 056e8a031aa..ca26ce94c50 100644 --- a/Gemfile +++ b/Gemfile @@ -174,7 +174,7 @@ end group :test do gem 'apivore', git: 'https://github.com/department-of-veterans-affairs/apivore', tag: 'v2.0.0.vsp' - gem 'mock_redis' + gem 'fakeredis' gem 'pdf-inspector' gem 'rspec_junit_formatter' gem 'rspec-retry' @@ -215,7 +215,7 @@ group :development, :test do gem 'rubocop-rails' gem 'rubocop-rspec' gem 'rubocop-thread_safety' - gem 'sidekiq', '~> 7.2.0' + gem 'sidekiq', '>= 6.4.0' gem 'timecop' gem 'webmock' gem 'yard' diff --git a/Gemfile.lock b/Gemfile.lock index 2f93bb0bca1..66b5c15fe00 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -133,13 +133,12 @@ PATH GEM remote: https://enterprise.contribsys.com/ specs: - sidekiq-ent (7.2.2) - einhorn (~> 1.0) - gserver - sidekiq (>= 7.2.0, < 8) - sidekiq-pro (>= 7.2.0, < 8) - sidekiq-pro (7.2.0) - sidekiq (>= 7.2.0, < 8) + sidekiq-ent (2.5.3) + einhorn (>= 0.7.4) + sidekiq (>= 6.5.0, < 7) + sidekiq-pro (>= 5.5.0, < 6) + sidekiq-pro (5.5.8) + sidekiq (~> 6.0, >= 6.5.6) GEM remote: https://rubygems.org/ @@ -283,7 +282,7 @@ GEM bundler (>= 1.2.0, < 3) thor (~> 1.0) byebug (11.1.3) - carrierwave (3.0.6) + carrierwave (3.0.7) activemodel (>= 6.0.0) activesupport (>= 6.0.0) addressable (~> 2.6) @@ -418,6 +417,8 @@ GEM railties (>= 5.0.0) faker (3.2.3) i18n (>= 1.8.11, < 2) + fakeredis (0.9.2) + redis (~> 4.8) faraday (2.9.0) faraday-net_http (>= 2.0, < 3.2) faraday-follow_redirects (0.3.0) @@ -602,7 +603,7 @@ GEM ffi (~> 1.0) libddwaf (1.14.0.0.0-x86_64-linux) ffi (~> 1.0) - liquid (5.4.0) + liquid (5.5.0) listen (3.8.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) @@ -633,7 +634,6 @@ GEM mini_mime (1.1.5) mini_portile2 (2.8.5) minitest (5.22.3) - mock_redis (0.44.0) msgpack (1.7.2) msgpack (1.7.2-java) multi_json (1.15.0) @@ -689,7 +689,7 @@ GEM operating_hours (0.1.0) optimist (3.1.0) os (1.1.4) - ox (2.14.17) + ox (2.14.18) parallel (1.24.0) parallel_tests (4.5.2) parallel @@ -812,10 +812,7 @@ GEM rchardet (1.8.0) rdoc (6.6.2) psych (>= 4.0.0) - redis (5.1.0) - redis-client (>= 0.17.0) - redis-client (0.20.0) - connection_pool + redis (4.8.1) redis-namespace (1.11.0) redis (>= 4) regexp_parser (2.9.0) @@ -957,11 +954,10 @@ GEM shrine (3.5.0) content_disposition (~> 1.0) down (~> 5.1) - sidekiq (7.2.2) - concurrent-ruby (< 2) - connection_pool (>= 2.3.0) - rack (>= 2.2.4) - redis-client (>= 0.19.0) + sidekiq (6.5.12) + connection_pool (>= 2.2.5, < 3) + rack (~> 2.0) + redis (>= 4.5.0, < 5) sidekiq_alive (2.4.0) gserver (~> 0.0.1) sidekiq (>= 5, < 8) @@ -1126,6 +1122,7 @@ DEPENDENCIES facilities_api! factory_bot_rails faker + fakeredis faraday (~> 2.9) faraday-follow_redirects faraday-httpclient @@ -1173,7 +1170,6 @@ DEPENDENCIES mimemagic mini_magick mobile! - mock_redis mocked_authentication! my_health! net-sftp @@ -1238,7 +1234,7 @@ DEPENDENCIES sentry-ruby shoulda-matchers shrine - sidekiq (~> 7.2.0) + sidekiq (>= 6.4.0) sidekiq-ent! sidekiq-pro! sidekiq_alive diff --git a/app/models/saved_claim.rb b/app/models/saved_claim.rb index 79498d6f1e4..83c812e08cf 100644 --- a/app/models/saved_claim.rb +++ b/app/models/saved_claim.rb @@ -102,6 +102,10 @@ def update_form(key, value) self.form = JSON.generate(application) end + def business_line + '' + end + private def attachment_keys diff --git a/app/models/saved_claim/burial.rb b/app/models/saved_claim/burial.rb index 141c77704b8..02bddbc08aa 100644 --- a/app/models/saved_claim/burial.rb +++ b/app/models/saved_claim/burial.rb @@ -24,4 +24,8 @@ def attachment_keys def email parsed_form['claimantEmail'] end + + def business_line + 'NCA' + end end diff --git a/app/models/saved_claim/education_career_counseling_claim.rb b/app/models/saved_claim/education_career_counseling_claim.rb index a2cc5a262e2..4ca69e12edf 100644 --- a/app/models/saved_claim/education_career_counseling_claim.rb +++ b/app/models/saved_claim/education_career_counseling_claim.rb @@ -29,4 +29,8 @@ def process_attachments! CentralMail::SubmitSavedClaimJob.new.perform(id) end + + def business_line + 'EDU' + end end diff --git a/app/models/saved_claim/veteran_readiness_employment_claim.rb b/app/models/saved_claim/veteran_readiness_employment_claim.rb index 590db719eae..59abdfb0c86 100644 --- a/app/models/saved_claim/veteran_readiness_employment_claim.rb +++ b/app/models/saved_claim/veteran_readiness_employment_claim.rb @@ -111,10 +111,7 @@ def add_office_location(updated_form) end def send_to_vre(user) - if user&.participant_id.blank? - log_message_to_sentry('Participant id is blank when submitting VRE claim', :warn) - send_to_central_mail!(user) - else + if user&.participant_id begin upload_to_vbms send_vbms_confirmation_email(user) @@ -129,6 +126,9 @@ def send_to_vre(user) log_exception_to_sentry(e, { uuid: user.uuid }) end end + else + log_message_to_sentry('Participant id is blank when submitting VRE claim', :warn) + send_to_central_mail!(user) end send_vre_email_form(user) @@ -156,11 +156,10 @@ def send_to_central_mail!(user) form_copy['vaFileNumber'] = parsed_form.dig('veteranInformation', 'VAFileNumber') update!(form: form_copy.to_json) - log_message_to_sentry(guid, :warn, { attachment_id: guid }, { team: 'vfs-ebenefits' }) @sent_to_cmp = true log_to_statsd('cmp') do - process_attachments! + process_attachments!(user) end send_central_mail_confirmation_email(user) @@ -218,12 +217,20 @@ def send_central_mail_confirmation_email(user) ) end - def process_attachments! + def process_attachments!(user) refs = attachment_keys.map { |key| Array(open_struct_form.send(key)) }.flatten files = PersistentAttachment.where(guid: refs.map(&:confirmationCode)) files.find_each { |f| f.update(saved_claim_id: id) } - CentralMail::SubmitSavedClaimJob.new.perform(id) + if Flipper.enabled?(:central_mail_benefits_intake_submission, user) + Lighthouse::SubmitBenefitsIntakeClaim.new.perform(id) + else + CentralMail::SubmitSavedClaimJob.new.perform(id) + end + end + + def business_line + 'VRE' end private diff --git a/app/sidekiq/lighthouse/submit_benefits_intake_claim.rb b/app/sidekiq/lighthouse/submit_benefits_intake_claim.rb new file mode 100644 index 00000000000..ea7a722eec0 --- /dev/null +++ b/app/sidekiq/lighthouse/submit_benefits_intake_claim.rb @@ -0,0 +1,128 @@ +# frozen_string_literal: true + +require 'central_mail/service' +require 'central_mail/datestamp_pdf' +require 'pension_burial/tag_sentry' +require 'benefits_intake_service/service' +require 'simple_forms_api_submission/metadata_validator' +require 'pdf_info' + +module Lighthouse + class SubmitBenefitsIntakeClaim + include Sidekiq::Job + include SentryLogging + class BenefitsIntakeClaimError < StandardError; end + + FOREIGN_POSTALCODE = '00000' + STATSD_KEY_PREFIX = 'worker.central_mail.submit_benefits_intake_claim' + + # Sidekiq has built in exponential back-off functionality for retries + # A max retry attempt of 14 will result in a run time of ~25 hours + RETRY = 14 + + sidekiq_options retry: RETRY + + sidekiq_retries_exhausted do |msg, _ex| + Rails.logger.send( + :error, + "Failed all retries on CentralMail::SubmitBenefitsIntakeClaim, last error: #{msg['error_message']}" + ) + StatsD.increment("#{STATSD_KEY_PREFIX}.exhausted") + end + + # rubocop:disable Metrics/MethodLength + def perform(saved_claim_id) + @claim = SavedClaim.find(saved_claim_id) + @pdf_path = process_record(@claim) + @attachment_paths = @claim.persistent_attachments.map do |record| + process_record(record) + end + + @lighthouse_service = BenefitsIntakeService::Service.new(with_upload_location: true) + create_form_submission_attempt(@lighthouse_service.uuid) + + payload = { + upload_url: @lighthouse_service.location, + file: split_file_and_path(@pdf_path), + metadata: generate_metadata.to_json, + attachments: @attachment_paths.map(&method(:split_file_and_path)) + } + + response = @lighthouse_service.upload_doc(**payload) + + if response.success? + log_message_to_sentry('CentralMail::SubmitSavedClaimJob succeeded', :info, generate_sentry_details) + @claim.send_confirmation_email if @claim.respond_to?(:send_confirmation_email) + else + raise BenefitsIntakeClaimError, response.body + end + rescue => e + log_message_to_sentry('CentralMail::SubmitBenefitsIntakeClaim failed, retrying...', :warn, + generate_sentry_details(e)) + raise + ensure + cleanup_file_paths + end + + # rubocop:enable Metrics/MethodLength + def generate_metadata + form = @claim.parsed_form + veteran_full_name = form['veteranFullName'] + address = form['claimantAddress'] || form['veteranAddress'] + + metadata = { + 'veteranFirstName' => veteran_full_name['first'], + 'veteranLastName' => veteran_full_name['last'], + 'fileNumber' => form['vaFileNumber'] || form['veteranSocialSecurityNumber'], + 'zipCode' => address['country'] == 'USA' ? address['postalCode'] : FOREIGN_POSTALCODE, + 'source' => "#{@claim.class} va.gov", + 'docType' => @claim.form_id, + 'businessLine' => @claim.business_line + } + + SimpleFormsApiSubmission::MetadataValidator.validate(metadata) + end + + def process_record(record) + pdf_path = record.to_pdf + stamped_path = CentralMail::DatestampPdf.new(pdf_path).run(text: 'VA.GOV', x: 5, y: 5) + CentralMail::DatestampPdf.new(stamped_path).run( + text: 'FDC Reviewed - va.gov Submission', + x: 429, + y: 770, + text_only: true + ) + end + + def split_file_and_path(path) + { file: path, file_name: path.split('/').last } + end + + private + + def generate_sentry_details(e = nil) + details = { + 'guid' => @claim&.guid, + 'docType' => @claim&.form_id, + 'savedClaimId' => @saved_claim_id + } + details['error'] = e.message if e + details + end + + def create_form_submission_attempt(intake_uuid) + form_submission = FormSubmission.create( + form_type: @claim.form_id, + form_data: @claim.to_json, + benefits_intake_uuid: intake_uuid, + saved_claim: @claim + ) + @form_submission_attempt = FormSubmissionAttempt.create(form_submission:) + end + + def cleanup_file_paths + Common::FileHelpers.delete_file_if_exists(@pdf_path) if @pdf_path + @attachment_paths&.each { |p| Common::FileHelpers.delete_file_if_exists(p) } + end + end +end diff --git a/config/features.yml b/config/features.yml index cf3b21130a6..336efbf6796 100644 --- a/config/features.yml +++ b/config/features.yml @@ -166,10 +166,6 @@ features: actor_type: user description: Enables users to access the claim letters page enable_in_development: true - cst_use_lighthouse: - actor_type: user - description: When enabled, claims status tool uses the Lighthouse API instead of EVSS - enable_in_development: true cst_use_lighthouse_5103: actor_type: user description: When enabled, claims status tool uses the Lighthouse API for the 5103 endpoint @@ -190,10 +186,6 @@ features: actor_type: user description: When enabled, the Download Decision Letters feature includes 5103 letters enable_in_development: true - cst_use_new_claim_cards: - actor_type: user - description: When enabled, claims status tool uses the new claim card designs - enable_in_development: true cst_use_claim_details_v2: actor_type: user description: When enabled, claims status tool uses the new claim details design @@ -1128,6 +1120,10 @@ features: actor_type: user enable_in_development: true description: Allows appointment cancellations to be routed to Oracle Health sites. + va_online_scheduling_enable_OH_slots_search: + actor_type: user + enable_in_development: true + description: Toggle for routing slots search requests to the VetsAPI Gateway Service(VPG) instead of vaos-service va_online_scheduling_datadog_RUM: actor_type: user description: Enables datadog Real User Monitoring. @@ -1318,6 +1314,9 @@ features: pension_claim_submission_to_lighthouse: actor_type: user description: Pension claim submission uses Lighthouse API + central_mail_benefits_intake_submission: + actor_type: user + description: Enable central mail claims submission uses Benefits Intake API virtual_agent_enable_param_error_detection: actor_type: user description: If enabled, Allows for the detection of errors in the chatbot params diff --git a/config/initializers/01_redis.rb b/config/initializers/01_redis.rb index fcf0ffd2bd5..bd43c3a719d 100644 --- a/config/initializers/01_redis.rb +++ b/config/initializers/01_redis.rb @@ -4,10 +4,6 @@ REDIS_CONFIG = Rails.application.config_for(:redis).freeze # set the current global instance of Redis based on environment specific config -$redis = - if Rails.env.test? - require 'mock_redis' - MockRedis.new(url: REDIS_CONFIG[:redis][:url]) - else - Redis.new(REDIS_CONFIG[:redis].to_h) - end +$redis = Redis.new(REDIS_CONFIG[:redis].to_hash) + +Redis.exists_returns_integer = true diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 41fb273cc5c..b2852d54867 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -59,8 +59,6 @@ end # Remove the default error handler - config.error_handlers.delete(Sidekiq::Config::ERROR_HANDLER) + config.error_handlers.delete_if { |handler| handler.is_a?(Sidekiq::ExceptionHandler::Logger) } end - - Sidekiq.strict_args!(false) end diff --git a/config/locales/en.yml b/config/locales/en.yml index 99be4cfaf7a..690ace033e8 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -23,6 +23,7 @@ en: time: formats: pdf_stamp: "%Y-%m-%d" + pdf_stamp4010007: "%m/%d/%Y" dependency_claim_failure_mailer: subject: We can’t process your dependents application body_html: > diff --git a/config/redis.yml b/config/redis.yml index 3cde49a7e0b..c3444f1575c 100644 --- a/config/redis.yml +++ b/config/redis.yml @@ -197,9 +197,6 @@ test: <<: *defaults redis: inherit_socket: true - url: <%= Settings.redis.app_data.url %> - sidekiq: - url: <%= Settings.redis.sidekiq.url %> production: <<: *defaults diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 907cf5c2d27..e9f5a783f9f 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -1,7 +1,5 @@ version: '3.4' services: - redis: - image: redis:6.2-alpine postgres: image: mdillon/postgis:11-alpine command: postgres -c shared_preload_libraries=pg_stat_statements -c pg_stat_statements.track=all -c max_connections=200 @@ -23,8 +21,6 @@ services: "Settings.database_url": "postgis://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DATABASE:-vets_api_development}?pool=4" "Settings.test_database_url": "postgis://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DATABASE:-vets_api_test}" "Settings.binaries.clamdscan": "clamscan" # Not running a separate process within the container for clamdscan, so we use clamscan which requires no daemon - "Settings.redis.app_data.url": "redis://redis:6379" - "Settings.redis.sidekiq.url": "redis://redis:6379" POSTGRES_HOST: "${POSTGRES_HOST:-postgres}" POSTGRES_PORT: "${POSTGRES_PORT:-5432}" POSTGRES_USER: "${POSTGRES_USER:-postgres}" @@ -38,9 +34,7 @@ services: DANGER_GITHUB_API_TOKEN: "${DANGER_GITHUB_API_TOKEN}" depends_on: - postgres - - redis links: - postgres - - redis volumes: test_bundle: diff --git a/lib/central_mail/datestamp_pdf.rb b/lib/central_mail/datestamp_pdf.rb index fc5c83ee886..25681203567 100644 --- a/lib/central_mail/datestamp_pdf.rb +++ b/lib/central_mail/datestamp_pdf.rb @@ -13,7 +13,7 @@ def initialize(file_path, append_to_stamp: nil) def run(settings) stamp_path = Common::FileHelpers.random_file_path generate_stamp(stamp_path, settings[:text], settings[:x], settings[:y], settings[:text_only], settings[:size], - settings[:timestamp], settings[:page_number], settings[:template]) + settings[:timestamp], settings[:page_number], settings[:template], @file_path) stamp(@file_path, stamp_path, multistamp: settings[:multistamp]) ensure Common::FileHelpers.delete_file_if_exists(stamp_path) if defined?(stamp_path) @@ -21,10 +21,15 @@ def run(settings) # rubocop:disable Metrics/ParameterLists # rubocop:disable Metrics/MethodLength - def generate_stamp(stamp_path, text, x, y, text_only, size = 10, timestamp = nil, page_number = nil, template = nil) + def generate_stamp(stamp_path, text, x, y, text_only, size = 10, timestamp = nil, page_number = nil, + template = nil, file_path = nil) timestamp ||= Time.zone.now unless text_only - text += " #{I18n.l(timestamp, format: :pdf_stamp)}" + text += if file_path == 'tmp/vba_40_10007-stamped.pdf' + " #{I18n.l(timestamp, format: :pdf_stamp4010007)}" + else + " #{I18n.l(timestamp, format: :pdf_stamp)}" + end text += ". #{@append_to_stamp}" if @append_to_stamp end diff --git a/lib/lighthouse/service_exception.rb b/lib/lighthouse/service_exception.rb index 45d7ae831e8..a46bef3b50c 100644 --- a/lib/lighthouse/service_exception.rb +++ b/lib/lighthouse/service_exception.rb @@ -90,7 +90,6 @@ def self.transform_error_keys(error_body, status, title, detail, code) def self.send_error_logs(error, service_name, lighthouse_client_id, url) base_key_string = "#{lighthouse_client_id} #{url} Lighthouse Error" Rails.logger.error( - error.response, base_key_string ) diff --git a/lib/sidekiq/semantic_logging.rb b/lib/sidekiq/semantic_logging.rb index b6043e958ca..822699bd393 100644 --- a/lib/sidekiq/semantic_logging.rb +++ b/lib/sidekiq/semantic_logging.rb @@ -4,11 +4,6 @@ require 'sidekiq/job_logger' class Sidekiq::SemanticLogging < Sidekiq::JobLogger - def initialize - logger = Rails.logger - super(logger) - end - def call(_worker, item, queue) logger_tags = { class: item['class'], diff --git a/modules/ask_va_api/app/services/ask_va_api/redis_client.rb b/modules/ask_va_api/app/services/ask_va_api/redis_client.rb deleted file mode 100644 index 5c25c9f5c40..00000000000 --- a/modules/ask_va_api/app/services/ask_va_api/redis_client.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -module AskVAApi - class RedisClient - def fetch(key) - Rails.cache.read( - key, - namespace: 'crm-api-cache' - ) - end - - def store_data(key:, data:, ttl:) - Rails.cache.write( - key, - data, - namespace: 'crm-api-cache', - expires_in: ttl - ) - end - end -end diff --git a/modules/ask_va_api/app/services/crm/cache_data.rb b/modules/ask_va_api/app/services/crm/cache_data.rb index 9b53c16c82e..a3d286b9f33 100644 --- a/modules/ask_va_api/app/services/crm/cache_data.rb +++ b/modules/ask_va_api/app/services/crm/cache_data.rb @@ -4,7 +4,7 @@ module Crm class CacheData attr_reader :cache_client, :service - def initialize(service: Service.new(icn: nil), cache_client: AskVAApi::RedisClient.new) + def initialize(service: Service.new(icn: nil), cache_client: RedisClient.new) @cache_client = cache_client @service = service end diff --git a/modules/ask_va_api/app/services/crm/crm_token.rb b/modules/ask_va_api/app/services/crm/crm_token.rb index 3b023fa2ced..b945e181ed7 100644 --- a/modules/ask_va_api/app/services/crm/crm_token.rb +++ b/modules/ask_va_api/app/services/crm/crm_token.rb @@ -16,7 +16,7 @@ class CrmToken def initialize @settings = Settings.ask_va_api.crm_api - @cache_client = AskVAApi::RedisClient.new + @cache_client = RedisClient.new @logger = LogService.new end diff --git a/modules/ask_va_api/app/services/redis_client.rb b/modules/ask_va_api/app/services/redis_client.rb new file mode 100644 index 00000000000..ae809c2eed2 --- /dev/null +++ b/modules/ask_va_api/app/services/redis_client.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class RedisClient + def fetch(key) + Rails.cache.read( + key, + namespace: 'crm-api-cache' + ) + end + + def store_data(key:, data:, ttl:) + Rails.cache.write( + key, + data, + namespace: 'crm-api-cache', + expires_in: ttl + ) + end +end diff --git a/modules/ask_va_api/spec/services/crm/cache_data_spec.rb b/modules/ask_va_api/spec/services/crm/cache_data_spec.rb index 1e341f6469d..b1b247a2b5c 100644 --- a/modules/ask_va_api/spec/services/crm/cache_data_spec.rb +++ b/modules/ask_va_api/spec/services/crm/cache_data_spec.rb @@ -4,7 +4,7 @@ RSpec.describe Crm::CacheData do let(:service) { double('Crm::Service') } - let(:cache_client) { double('AskVAApi::RedisClient') } + let(:cache_client) { double('RedisClient') } let(:cache_data_instance) { Crm::CacheData.new(service:, cache_client:) } let(:cache_data) { { topics: [{ id: 1, name: 'Topic 1' }] } } diff --git a/modules/ask_va_api/spec/services/redis_client_spec.rb b/modules/ask_va_api/spec/services/redis_client_spec.rb index b8d2b7e74c0..fd58fdc3795 100644 --- a/modules/ask_va_api/spec/services/redis_client_spec.rb +++ b/modules/ask_va_api/spec/services/redis_client_spec.rb @@ -2,8 +2,8 @@ require 'rails_helper' -RSpec.describe AskVAApi::RedisClient do - let(:redis_client) { AskVAApi::RedisClient.new } +RSpec.describe RedisClient do + let(:redis_client) { RedisClient.new } let(:token) { 'some-access-token' } describe '#fetch' do diff --git a/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb b/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb index 784253396d7..519ea0a5552 100644 --- a/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb +++ b/modules/claims_api/app/controllers/claims_api/v2/veterans/power_of_attorney/base_controller.rb @@ -46,9 +46,10 @@ def request_representative def shared_form_validation(form_number) target_veteran # Custom validations for POA submission, we must check this first - @claims_api_forms_validation_errors = validate_form_2122_and_2122a_submission_values + @claims_api_forms_validation_errors = validate_form_2122_and_2122a_submission_values(user_profile) # JSON validations for POA submission, will combine with previously captured errors and raise validate_json_schema(form_number.upcase) + add_claimant_data_to_form if user_profile # if we get here there were only validations file errors if @claims_api_forms_validation_errors raise ::ClaimsApi::Common::Exceptions::Lighthouse::JsonDisabilityCompensationValidationError, @@ -167,6 +168,30 @@ def nullable_icn nil end + + def user_profile + return @user_profile if defined? @user_profile + + @user_profile ||= fetch_claimant + end + + def fetch_claimant + claimant_icn = form_attributes.dig('claimant', 'claimantId') + if claimant_icn.present? + mpi_profile = mpi_service.find_profile_by_identifier(identifier: claimant_icn, + identifier_type: MPI::Constants::ICN) + end + rescue ArgumentError + mpi_profile + end + + def add_claimant_data_to_form + if user_profile&.status == :ok + first_name = user_profile.profile.given_names.first + last_name = user_profile.profile.family_name + form_attributes['claimant'].merge!(firstName: first_name, lastName: last_name) + end + end end end end diff --git a/modules/claims_api/app/controllers/concerns/claims_api/v2/power_of_attorney_validation.rb b/modules/claims_api/app/controllers/concerns/claims_api/v2/power_of_attorney_validation.rb index b535ff11709..c1001eb50b0 100644 --- a/modules/claims_api/app/controllers/concerns/claims_api/v2/power_of_attorney_validation.rb +++ b/modules/claims_api/app/controllers/concerns/claims_api/v2/power_of_attorney_validation.rb @@ -1,19 +1,22 @@ # frozen_string_literal: false +# rubocop:disable Metrics/ModuleLength + module ClaimsApi module V2 module PowerOfAttorneyValidation - def validate_form_2122_and_2122a_submission_values - validate_claimant + def validate_form_2122_and_2122a_submission_values(user_profile) + validate_claimant(user_profile) # collect errors and pass back to the controller raise_error_collection if @errors end private - def validate_claimant + def validate_claimant(user_profile) return if form_attributes['claimant'].blank? + validate_claimant_id_included(user_profile) validate_address validate_relationship end @@ -92,6 +95,26 @@ def validate_relationship end end + def validate_claimant_id_included(user_profile) + claimant_icn = form_attributes.dig('claimant', 'claimantId') + if (user_profile.blank? || user_profile&.status == :not_found) && claimant_icn + collect_error_messages( + source: 'claimant/claimantId', + detail: "The 'claimantId' must be valid" + ) + else + address = form_attributes.dig('claimant', 'address') + phone = form_attributes.dig('claimant', 'phone') + relationship = form_attributes.dig('claimant', 'relationship') + return if claimant_icn.present? && (address.present? || phone.present? || relationship.present?) + + collect_error_messages( + source: '/claimant/claimantId/', + detail: "If claimant is present 'claimantId' must be filled in" + ) + end + end + def errors_array @errors ||= [] end @@ -109,3 +132,4 @@ def raise_error_collection end end end +# rubocop:enable Metrics/ModuleLength diff --git a/modules/claims_api/app/sidekiq/claims_api/poa_updater.rb b/modules/claims_api/app/sidekiq/claims_api/poa_updater.rb index 700b084fdb1..b7f24070b3f 100644 --- a/modules/claims_api/app/sidekiq/claims_api/poa_updater.rb +++ b/modules/claims_api/app/sidekiq/claims_api/poa_updater.rb @@ -40,14 +40,6 @@ def perform(power_of_attorney_id) # rubocop:disable Metrics/MethodLength private - def extract_poa_code(poa_form_data) - if poa_form_data.key?('serviceOrganization') - poa_form_data['serviceOrganization']['poaCode'] - elsif poa_form_data.key?('representative') # V2 2122a - poa_form_data['representative']['poaCode'] - end - end - def enable_vbms_access?(poa_form:) poa_form.form_data['recordConsent'] && poa_form.form_data['consentLimits'].blank? end diff --git a/modules/claims_api/app/sidekiq/claims_api/poa_vbms_updater.rb b/modules/claims_api/app/sidekiq/claims_api/poa_vbms_updater.rb index 0b090232ecb..1b64ef487c0 100644 --- a/modules/claims_api/app/sidekiq/claims_api/poa_vbms_updater.rb +++ b/modules/claims_api/app/sidekiq/claims_api/poa_vbms_updater.rb @@ -10,18 +10,18 @@ def perform(power_of_attorney_id) # rubocop:disable Metrics/MethodLength external_uid: poa_form.external_uid, external_key: poa_form.external_key ) + poa_code = extract_poa_code(poa_form.form_data) ClaimsApi::Logger.log( 'poa_vbms_updater', poa_id: power_of_attorney_id, detail: 'Updating Access', - poa_code: poa_form.form_data.dig('serviceOrganization', 'poaCode') + poa_code: ) - # allow_poa_c_add reports 'No Data' if sent lowercase response = service.corporate_update.update_poa_access( participant_id: poa_form.auth_headers['va_eauth_pid'], - poa_code: poa_form.form_data.dig('serviceOrganization', 'poaCode'), + poa_code:, allow_poa_access: 'y', allow_poa_c_add: allow_address_change?(poa_form, power_of_attorney_id) ? 'Y' : 'N' ) diff --git a/modules/claims_api/app/sidekiq/claims_api/service_base.rb b/modules/claims_api/app/sidekiq/claims_api/service_base.rb index 9a7259d5a90..18effe692f6 100644 --- a/modules/claims_api/app/sidekiq/claims_api/service_base.rb +++ b/modules/claims_api/app/sidekiq/claims_api/service_base.rb @@ -123,5 +123,13 @@ def log_job_progress(claim_id, detail) claim_id:, detail:) end + + def extract_poa_code(poa_form_data) + if poa_form_data.key?('serviceOrganization') + poa_form_data['serviceOrganization']['poaCode'] + elsif poa_form_data.key?('representative') # V2 2122a + poa_form_data['representative']['poaCode'] + end + end end end diff --git a/modules/claims_api/app/sidekiq/claims_api/v2/poa_form_builder_job.rb b/modules/claims_api/app/sidekiq/claims_api/v2/poa_form_builder_job.rb index 0517d611694..45067e86e59 100644 --- a/modules/claims_api/app/sidekiq/claims_api/v2/poa_form_builder_job.rb +++ b/modules/claims_api/app/sidekiq/claims_api/v2/poa_form_builder_job.rb @@ -65,18 +65,19 @@ def data(power_of_attorney, form_number) end def organization_signatures(power_of_attorney) - first_name = power_of_attorney.form_data['serviceOrganization']['firstName'] - last_name = power_of_attorney.form_data['serviceOrganization']['lastName'] + rep_first_name = power_of_attorney.form_data['serviceOrganization']['firstName'] + rep_last_name = power_of_attorney.form_data['serviceOrganization']['lastName'] + first_name, last_name = veteran_or_claimant_signature(power_of_attorney) { 'page2' => [ { - 'signature' => "#{power_of_attorney.auth_headers['va_eauth_firstName']} " \ - "#{power_of_attorney.auth_headers['va_eauth_lastName']} - signed via api.va.gov", + 'signature' => "#{first_name} " \ + "#{last_name} - signed via api.va.gov", 'x' => 35, 'y' => 240 }, { - 'signature' => "#{first_name} #{last_name} - signed via api.va.gov", + 'signature' => "#{rep_first_name} #{rep_last_name} - signed via api.va.gov", 'x' => 35, 'y' => 200 } @@ -109,21 +110,34 @@ def individual_page1_signatures(power_of_attorney, first_name, last_name) ] end - def individual_page2_signatures(power_of_attorney, first_name, last_name) + def individual_page2_signatures(power_of_attorney, rep_first_name, rep_last_name) + first_name, last_name = veteran_or_claimant_signature(power_of_attorney) [ { - 'signature' => "#{power_of_attorney.auth_headers['va_eauth_firstName']} " \ - "#{power_of_attorney.auth_headers['va_eauth_lastName']} - signed via api.va.gov", + 'signature' => "#{first_name} " \ + "#{last_name} - signed via api.va.gov", 'x' => 35, 'y' => 306 }, { - 'signature' => "#{first_name} #{last_name} - signed via api.va.gov", + 'signature' => "#{rep_first_name} #{rep_last_name} - signed via api.va.gov", 'x' => 35, 'y' => 200 } ] end + + def veteran_or_claimant_signature(power_of_attorney) + claimant = power_of_attorney.form_data['claimant'].present? + if claimant + first_name = power_of_attorney.form_data['claimant']['firstName'] + last_name = power_of_attorney.form_data['claimant']['lastName'] + else + first_name = power_of_attorney.auth_headers['va_eauth_firstName'] + last_name = power_of_attorney.auth_headers['va_eauth_lastName'] + end + [first_name, last_name] + end end end end 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 b8b42e0fc36..45d2acf2493 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 @@ -9022,23 +9022,10 @@ "type": "object", "additionalProperties": false, "properties": { - "firstName": { - "description": "First name of Claimant.", - "type": "string", - "example": "John", - "maxLength": 12 - }, - "middleInitial": { - "description": "Middle initial of Claimant.", + "claimantId": { "type": "string", - "example": "M", - "maxLength": 1 - }, - "lastName": { - "description": "Last name of Claimant.", - "type": "string", - "example": "Dow", - "maxLength": 18 + "example": "123456789", + "description": "Id of the claimant." }, "address": { "type": "object", @@ -9716,20 +9703,10 @@ "type": "object", "additionalProperties": false, "properties": { - "firstName": { - "description": "First name of Claimant.", - "type": "string", - "example": "John" - }, - "middleInitial": { - "description": "Middle initial of Claimant.", - "type": "string", - "example": "M" - }, - "lastName": { - "description": "Last name of Claimant.", + "claimantId": { "type": "string", - "example": "Dow" + "example": "123456789", + "description": "Id of the claimant." }, "address": { "type": "object", @@ -10359,23 +10336,10 @@ "type": "object", "additionalProperties": false, "properties": { - "firstName": { - "description": "First name of Claimant.", - "type": "string", - "example": "John", - "maxLength": 12 - }, - "middleInitial": { - "description": "Middle initial of Claimant.", + "claimantId": { "type": "string", - "example": "M", - "maxLength": 1 - }, - "lastName": { - "description": "Last name of Claimant.", - "type": "string", - "example": "Dow", - "maxLength": 18 + "example": "123456789", + "description": "Id of the claimant." }, "address": { "type": "object", @@ -11044,20 +11008,10 @@ "type": "object", "additionalProperties": false, "properties": { - "firstName": { - "description": "First name of Claimant.", - "type": "string", - "example": "John" - }, - "middleInitial": { - "description": "Middle initial of Claimant.", - "type": "string", - "example": "M" - }, - "lastName": { - "description": "Last name of Claimant.", + "claimantId": { "type": "string", - "example": "Dow" + "example": "123456789", + "description": "Id of the claimant." }, "address": { "type": "object", diff --git a/modules/claims_api/config/schemas/v2/2122.json b/modules/claims_api/config/schemas/v2/2122.json index 79f8511c99b..543686f5c4c 100644 --- a/modules/claims_api/config/schemas/v2/2122.json +++ b/modules/claims_api/config/schemas/v2/2122.json @@ -112,20 +112,10 @@ "type": "object", "additionalProperties": false, "properties": { - "firstName": { - "description": "First name of Claimant.", - "type": "string", - "example": "John" - }, - "middleInitial": { - "description": "Middle initial of Claimant.", - "type": "string", - "example": "M" - }, - "lastName": { - "description": "Last name of Claimant.", + "claimantId": { "type": "string", - "example": "Dow" + "example": "123456789", + "description": "Id of the claimant." }, "address": { "type": "object", diff --git a/modules/claims_api/config/schemas/v2/2122a.json b/modules/claims_api/config/schemas/v2/2122a.json index 6a90f806fd4..48d66dfa061 100644 --- a/modules/claims_api/config/schemas/v2/2122a.json +++ b/modules/claims_api/config/schemas/v2/2122a.json @@ -128,23 +128,10 @@ "type": "object", "additionalProperties": false, "properties": { - "firstName": { - "description": "First name of Claimant.", - "type": "string", - "example": "John", - "maxLength": 12 - }, - "middleInitial": { - "description": "Middle initial of Claimant.", - "type": "string", - "example": "M", - "maxLength": 1 - }, - "lastName": { - "description": "Last name of Claimant.", + "claimantId": { "type": "string", - "example": "Dow", - "maxLength": 18 + "example": "123456789", + "description": "Id of the claimant." }, "address": { "type": "object", diff --git a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_ind_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_ind_request_spec.rb index 95c25dcb5fa..591c9a7f414 100644 --- a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_ind_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_ind_request_spec.rb @@ -81,8 +81,7 @@ } }, claimant: { - firstName: 'first', - lastName: 'last', + claimantId: '1013062086V794840', address: { addressLine1: '123', city: 'city', @@ -112,15 +111,16 @@ context 'when provided' do context 'when valid' do it 'returns a 202' do - mock_ccg(scopes) do |auth_header| - expect_any_instance_of(local_bgs).to receive(:find_poa_by_participant_id) - .and_return(bgs_poa) - allow_any_instance_of(local_bgs).to receive(:find_poa_history_by_ptcpnt_id) - .and_return({ person_poa_history: nil }) - - post appoint_individual_path, params: data.to_json, headers: auth_header + VCR.use_cassette('mpi/find_candidate/valid_icn_full') do + mock_ccg(scopes) do |auth_header| + expect_any_instance_of(local_bgs).to receive(:find_poa_by_participant_id) + .and_return(bgs_poa) + allow_any_instance_of(local_bgs).to receive(:find_poa_history_by_ptcpnt_id) + .and_return({ person_poa_history: nil }) - expect(response).to have_http_status(:accepted) + post appoint_individual_path, params: data.to_json, headers: auth_header + expect(response).to have_http_status(:accepted) + end end end end @@ -147,8 +147,9 @@ it 'returns a 202 when all conditionally required data is present' do mock_ccg(scopes) do |auth_header| - post appoint_individual_path, params: claimant_data.to_json, headers: auth_header - + VCR.use_cassette('mpi/find_candidate/valid_icn_full') do + post appoint_individual_path, params: claimant_data.to_json, headers: auth_header + end expect(response).to have_http_status(:accepted) end end @@ -156,9 +157,9 @@ it 'returns a 422 if claimant.address.addressLine1 is not provided' do mock_ccg(scopes) do |auth_header| claimant_data[:data][:attributes][:claimant][:address][:addressLine1] = nil - - post appoint_individual_path, params: claimant_data.to_json, headers: auth_header - + VCR.use_cassette('mpi/find_candidate/valid_icn_full') do + post appoint_individual_path, params: claimant_data.to_json, headers: auth_header + end expect(response).to have_http_status(:unprocessable_entity) response_body = JSON.parse(response.body) expect(response_body['errors'][0]['detail']).to eq( @@ -170,9 +171,9 @@ it 'returns a 422 if claimant.address.city is not provided' do mock_ccg(scopes) do |auth_header| claimant_data[:data][:attributes][:claimant][:address][:city] = nil - - post appoint_individual_path, params: claimant_data.to_json, headers: auth_header - + VCR.use_cassette('mpi/find_candidate/valid_icn_full') do + post appoint_individual_path, params: claimant_data.to_json, headers: auth_header + end expect(response).to have_http_status(:unprocessable_entity) response_body = JSON.parse(response.body) expect(response_body['errors'][0]['detail']).to eq( @@ -184,9 +185,9 @@ it 'returns a 422 if claimant.address.stateCode is not provided' do mock_ccg(scopes) do |auth_header| claimant_data[:data][:attributes][:claimant][:address][:stateCode] = nil - - post appoint_individual_path, params: claimant_data.to_json, headers: auth_header - + VCR.use_cassette('mpi/find_candidate/valid_icn_full') do + post appoint_individual_path, params: claimant_data.to_json, headers: auth_header + end expect(response).to have_http_status(:unprocessable_entity) response_body = JSON.parse(response.body) expect(response_body['errors'][0]['detail']).to eq( @@ -198,9 +199,9 @@ it 'returns a 422 if claimant.address.country is not provided' do mock_ccg(scopes) do |auth_header| claimant_data[:data][:attributes][:claimant][:address][:country] = nil - - post appoint_individual_path, params: claimant_data.to_json, headers: auth_header - + VCR.use_cassette('mpi/find_candidate/valid_icn_full') do + post appoint_individual_path, params: claimant_data.to_json, headers: auth_header + end expect(response).to have_http_status(:unprocessable_entity) response_body = JSON.parse(response.body) expect(response_body['errors'][0]['detail']).to eq( @@ -212,9 +213,9 @@ it 'returns a 422 if claimant.address.zipCode is not provided' do mock_ccg(scopes) do |auth_header| claimant_data[:data][:attributes][:claimant][:address][:zipCode] = nil - - post appoint_individual_path, params: claimant_data.to_json, headers: auth_header - + VCR.use_cassette('mpi/find_candidate/valid_icn_full') do + post appoint_individual_path, params: claimant_data.to_json, headers: auth_header + end expect(response).to have_http_status(:unprocessable_entity) response_body = JSON.parse(response.body) expect(response_body['errors'][0]['detail']).to eq( @@ -224,16 +225,17 @@ end it 'returns a 422 if claimant.relationship is not provided' do - mock_ccg(scopes) do |auth_header| - claimant_data[:data][:attributes][:claimant][:relationship] = nil - - post appoint_individual_path, params: claimant_data.to_json, headers: auth_header - - expect(response).to have_http_status(:unprocessable_entity) - response_body = JSON.parse(response.body) - expect(response_body['errors'][0]['detail']).to eq( - "If claimant is present 'relationship' must be filled in" - ) + VCR.use_cassette('mpi/find_candidate/valid_icn_full') do + mock_ccg(scopes) do |auth_header| + claimant_data[:data][:attributes][:claimant][:relationship] = nil + + post appoint_individual_path, params: claimant_data.to_json, headers: auth_header + expect(response).to have_http_status(:unprocessable_entity) + response_body = JSON.parse(response.body) + expect(response_body['errors'][0]['detail']).to eq( + "If claimant is present 'relationship' must be filled in" + ) + end end end end @@ -380,6 +382,45 @@ end end end + + context 'when no claimantId is provided and other claimant data is present' do + let(:request_body) do + Rails.root.join('modules', 'claims_api', 'spec', 'fixtures', 'v2', 'veterans', + 'power_of_attorney', '2122a', 'valid.json').read + end + + let(:claimant) do + { + email: 'lillian@disney.com', + relationship: 'Spouse', + address: { + addressLine1: '2688 S Camino Real', + city: 'Palm Springs', + stateCode: 'CA', + country: 'US', + zipCode: '92264' + }, + phone: { + areaCode: '555', + phoneNumber: '5551337' + } + } + end + let(:error_msg) { "If claimant is present 'claimantId' must be filled in" } + + it 'returns a meaningful 422' do + mock_ccg(%w[claim.write claim.read]) do |auth_header| + json = JSON.parse(request_body) + json['data']['attributes']['claimant'] = claimant + request_body = json.to_json + post validate2122a_path, params: request_body, headers: auth_header + + response_body = JSON.parse(response.body)['errors'][0] + expect(response).to have_http_status(:unprocessable_entity) + expect(response_body['detail']).to eq(error_msg) + end + end + end end end end diff --git a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_org_request_spec.rb b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_org_request_spec.rb index 4d90fd57f7d..9e94f75e87b 100644 --- a/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_org_request_spec.rb +++ b/modules/claims_api/spec/requests/v2/veterans/power_of_attorney_org_request_spec.rb @@ -189,6 +189,47 @@ end end end + + context 'when no claimantId is provided and other claimant data is present' do + let(:request_body) do + Rails.root.join('modules', 'claims_api', 'spec', 'fixtures', 'v2', 'veterans', + 'power_of_attorney', '2122', 'valid.json').read + end + + let(:claimant) do + { + email: 'lillian@disney.com', + relationship: 'Spouse', + address: { + addressLine1: '2688 S Camino Real', + city: 'Palm Springs', + stateCode: 'CA', + country: 'US', + zipCode: '92264' + }, + phone: { + areaCode: '555', + phoneNumber: '5551337' + } + } + end + let(:error_msg) { "If claimant is present 'claimantId' must be filled in" } + + it 'returns a meaningful 422' do + VCR.use_cassette('mpi/find_candidate/valid_icn_full') do + mock_ccg(%w[claim.write claim.read]) do |auth_header| + json = JSON.parse(request_body) + json['data']['attributes']['claimant'] = claimant + request_body = json.to_json + post validate2122_path, params: request_body, headers: auth_header + + response_body = JSON.parse(response.body)['errors'][0] + expect(response).to have_http_status(:unprocessable_entity) + expect(response_body['detail']).to eq(error_msg) + end + end + end + end end end end diff --git a/modules/claims_api/spec/sidekiq/v2/poa_form_builder_job_spec.rb b/modules/claims_api/spec/sidekiq/v2/poa_form_builder_job_spec.rb index af45a521059..69532e7d682 100644 --- a/modules/claims_api/spec/sidekiq/v2/poa_form_builder_job_spec.rb +++ b/modules/claims_api/spec/sidekiq/v2/poa_form_builder_job_spec.rb @@ -14,7 +14,7 @@ end describe 'generating and uploading the signed pdf' do - context '2122a' do + context '2122a veteran claimant' do before do power_of_attorney.form_data = { recordConsent: true, @@ -34,9 +34,7 @@ } }, claimant: { - firstName: 'Lillian', - middleInitial: 'A', - lastName: 'Disney', + claimantId: '1012830872V584140', email: 'lillian@disney.com', relationship: 'Spouse', address: { @@ -49,7 +47,9 @@ phone: { areaCode: '555', phoneNumber: '5551337' - } + }, + firstName: 'JESSE', + lastName: 'GRAY' }, representative: { poaCode: poa_code.to_s, @@ -69,6 +69,245 @@ power_of_attorney.save end + it 'generates e-signatures correctly for a veteran claimant' do + VCR.use_cassette('mpi/find_candidate/valid_icn_full') do + data = power_of_attorney + .form_data + .deep_merge( + { + 'veteran' => { + 'firstName' => power_of_attorney.auth_headers['va_eauth_firstName'], + 'lastName' => power_of_attorney.auth_headers['va_eauth_lastName'], + 'ssn' => power_of_attorney.auth_headers['va_eauth_pnid'], + 'birthdate' => power_of_attorney.auth_headers['va_eauth_birthdate'] + } + } + ) + final_data = data.merge( + { + 'text_signatures' => { + 'page1' => [ + { + 'signature' => 'JESSE GRAY - signed via api.va.gov', + 'x' => 35, + 'y' => 73 + }, + { + 'signature' => 'Bob Representative - signed via api.va.gov', + 'x' => 35, + 'y' => 100 + } + ], + 'page2' => [ + { + 'signature' => 'JESSE GRAY - signed via api.va.gov', + 'x' => 35, + 'y' => 306 + }, + { + 'signature' => 'Bob Representative - signed via api.va.gov', + 'x' => 35, + 'y' => 200 + } + ] + } + } + ) + + allow_any_instance_of(BGS::PersonWebService).to receive(:find_by_ssn).and_return({ file_nbr: '123456789' }) + expect_any_instance_of(ClaimsApi::V2::PoaPdfConstructor::Individual) + .to receive(:construct) + .with(final_data, id: power_of_attorney.id) + .and_call_original + + subject.new.perform(power_of_attorney.id, '2122A') + end + end + + it 'Calls the POA updater job upon successful upload to VBMS' do + VCR.use_cassette('mpi/find_candidate/valid_icn_full') do + token_response = OpenStruct.new(upload_token: '<{573F054F-E9F7-4BF2-8C66-D43ADA5C62E7}') + document_response = OpenStruct.new(upload_document_response: { + '@new_document_version_ref_id' => '{52300B69-1D6E-43B2-8BEB-67A7C55346A2}', + '@document_series_ref_id' => '{A57EF6CC-2236-467A-BA4F-1FA1EFD4B374}' + }.with_indifferent_access) + + allow_any_instance_of(ClaimsApi::VBMSUploader).to receive(:fetch_upload_token).and_return(token_response) + allow_any_instance_of(ClaimsApi::VBMSUploader).to receive(:upload_document).and_return(document_response) + allow_any_instance_of(BGS::PersonWebService).to receive(:find_by_ssn).and_return({ file_nbr: '123456789' }) + + expect(ClaimsApi::PoaUpdater).to receive(:perform_async) + + subject.new.perform(power_of_attorney.id, '2122A') + end + end + end + + context '2122a non-veteran claimant' do + before do + power_of_attorney.form_data = { + recordConsent: true, + consentAddressChange: true, + consentLimits: ['DRUG ABUSE', 'SICKLE CELL'], + veteran: { + serviceBranch: 'ARMY', + address: { + numberAndStreet: '2719 Hyperion Ave', + city: 'Los Angeles', + state: 'CA', + country: 'US', + zipFirstFive: '92264' + }, + phone: { + areaCode: '555', + phoneNumber: '5551337' + } + }, + claimant: { + claimantId: '1012830872V584140', + email: 'lillian@disney.com', + relationship: 'Spouse', + address: { + numberAndStreet: '2688 S Camino Real', + city: 'Palm Springs', + state: 'CA', + country: 'US', + zipFirstFive: '92264' + }, + phone: { + areaCode: '555', + phoneNumber: '5551337' + }, + firstName: 'Mitchell', + lastName: 'Jenkins' + }, + representative: { + poaCode: poa_code.to_s, + type: 'SERVICE ORGANIZATION REPRESENTATIVE', + firstName: 'Bob', + lastName: 'Representative', + organizationName: 'I Help Vets LLC', + address: { + numberAndStreet: '2719 Hyperion Ave', + city: 'Los Angeles', + state: 'CA', + country: 'US', + zipFirstFive: '92264' + } + } + } + power_of_attorney.save + end + + it 'generates e-signatures correctly for a non-veteran claimant' do + VCR.use_cassette('mpi/find_candidate/valid_icn_full') do + data = power_of_attorney + .form_data + .deep_merge( + { + 'veteran' => { + 'firstName' => power_of_attorney.auth_headers['va_eauth_firstName'], + 'lastName' => power_of_attorney.auth_headers['va_eauth_lastName'], + 'ssn' => power_of_attorney.auth_headers['va_eauth_pnid'], + 'birthdate' => power_of_attorney.auth_headers['va_eauth_birthdate'] + } + } + ) + final_data = data.merge( + { + 'text_signatures' => { + 'page1' => [ + { + 'signature' => 'JESSE GRAY - signed via api.va.gov', + 'x' => 35, + 'y' => 73 + }, + { + 'signature' => 'Bob Representative - signed via api.va.gov', + 'x' => 35, + 'y' => 100 + } + ], + 'page2' => [ + { + 'signature' => 'Mitchell Jenkins - signed via api.va.gov', + 'x' => 35, + 'y' => 306 + }, + { + 'signature' => 'Bob Representative - signed via api.va.gov', + 'x' => 35, + 'y' => 200 + } + ] + } + } + ) + + allow_any_instance_of(BGS::PersonWebService).to receive(:find_by_ssn).and_return({ file_nbr: '123456789' }) + expect_any_instance_of(ClaimsApi::V2::PoaPdfConstructor::Individual) + .to receive(:construct) + .with(final_data, id: power_of_attorney.id) + .and_call_original + + subject.new.perform(power_of_attorney.id, '2122A') + end + end + end + + context '2122 veteran claimant' do + before do + power_of_attorney.form_data = { + recordConsent: true, + consentAddressChange: true, + consentLimits: %w[DRUG_ABUSE SICKLE_CELL], + veteran: { + address: { + numberAndStreet: '2719 Hyperion Ave', + city: 'Los Angeles', + state: 'CA', + country: 'US', + zipFirstFive: '92264' + }, + phone: { + areaCode: '555', + phoneNumber: '5551337' + } + }, + claimant: { + email: 'lillian@disney.com', + relationship: 'Spouse', + address: { + numberAndStreet: '2688 S Camino Real', + city: 'Palm Springs', + state: 'CA', + country: 'US', + zipFirstFive: '92264' + }, + phone: { + areaCode: '555', + phoneNumber: '5551337' + }, + firstName: 'JESSE', + lastName: 'GRAY' + }, + serviceOrganization: { + poaCode: poa_code.to_s, + firstName: 'Bob', + lastName: 'Representative', + organizationName: 'I Help Vets LLC', + address: { + numberAndStreet: '2719 Hyperion Ave', + city: 'Los Angeles', + state: 'CA', + country: 'US', + zipFirstFive: '92264' + } + } + } + power_of_attorney.save + end + it 'generates e-signatures correctly' do data = power_of_attorney .form_data @@ -85,23 +324,11 @@ final_data = data.merge( { 'text_signatures' => { - 'page1' => [ - { - 'signature' => 'JESSE GRAY - signed via api.va.gov', - 'x' => 35, - 'y' => 73 - }, - { - 'signature' => 'Bob Representative - signed via api.va.gov', - 'x' => 35, - 'y' => 100 - } - ], 'page2' => [ { 'signature' => 'JESSE GRAY - signed via api.va.gov', 'x' => 35, - 'y' => 306 + 'y' => 240 }, { 'signature' => 'Bob Representative - signed via api.va.gov', @@ -114,12 +341,14 @@ ) allow_any_instance_of(BGS::PersonWebService).to receive(:find_by_ssn).and_return({ file_nbr: '123456789' }) - expect_any_instance_of(ClaimsApi::V2::PoaPdfConstructor::Individual) - .to receive(:construct) - .with(final_data, id: power_of_attorney.id) - .and_call_original + VCR.use_cassette('mpi/find_candidate/valid_icn_full') do + expect_any_instance_of(ClaimsApi::V2::PoaPdfConstructor::Organization) + .to receive(:construct) + .with(final_data, id: power_of_attorney.id) + .and_call_original - subject.new.perform(power_of_attorney.id, '2122A') + subject.new.perform(power_of_attorney.id, '2122') + end end it 'Calls the POA updater job upon successful upload to VBMS' do @@ -132,19 +361,20 @@ allow_any_instance_of(ClaimsApi::VBMSUploader).to receive(:fetch_upload_token).and_return(token_response) allow_any_instance_of(ClaimsApi::VBMSUploader).to receive(:upload_document).and_return(document_response) allow_any_instance_of(BGS::PersonWebService).to receive(:find_by_ssn).and_return({ file_nbr: '123456789' }) + VCR.use_cassette('mpi/find_candidate/valid_icn_full') do + expect(ClaimsApi::PoaUpdater).to receive(:perform_async) - expect(ClaimsApi::PoaUpdater).to receive(:perform_async) - - subject.new.perform(power_of_attorney.id, '2122A') + subject.new.perform(power_of_attorney.id, '2122') + end end end - context '2122' do + context '2122 non-veteran claimant' do before do power_of_attorney.form_data = { recordConsent: true, consentAddressChange: true, - consentLimits: %w[DRUG_ABUSE SICKLE_CELL], + consentLimits: ['DRUG ABUSE', 'SICKLE CELL'], veteran: { address: { numberAndStreet: '2719 Hyperion Ave', @@ -159,9 +389,7 @@ } }, claimant: { - firstName: 'Lillian', - middleInitial: 'A', - lastName: 'Disney', + claimantId: '1012830872V584140', email: 'lillian@disney.com', relationship: 'Spouse', address: { @@ -174,7 +402,9 @@ phone: { areaCode: '555', phoneNumber: '5551337' - } + }, + firstName: 'Mitchell', + lastName: 'Jenkins' }, serviceOrganization: { poaCode: poa_code.to_s, @@ -211,7 +441,7 @@ 'text_signatures' => { 'page2' => [ { - 'signature' => 'JESSE GRAY - signed via api.va.gov', + 'signature' => 'Mitchell Jenkins - signed via api.va.gov', 'x' => 35, 'y' => 240 }, @@ -226,28 +456,14 @@ ) allow_any_instance_of(BGS::PersonWebService).to receive(:find_by_ssn).and_return({ file_nbr: '123456789' }) - expect_any_instance_of(ClaimsApi::V2::PoaPdfConstructor::Organization) - .to receive(:construct) - .with(final_data, id: power_of_attorney.id) - .and_call_original - - subject.new.perform(power_of_attorney.id, '2122') - end - - it 'Calls the POA updater job upon successful upload to VBMS' do - token_response = OpenStruct.new(upload_token: '<{573F054F-E9F7-4BF2-8C66-D43ADA5C62E7}') - document_response = OpenStruct.new(upload_document_response: { - '@new_document_version_ref_id' => '{52300B69-1D6E-43B2-8BEB-67A7C55346A2}', - '@document_series_ref_id' => '{A57EF6CC-2236-467A-BA4F-1FA1EFD4B374}' - }.with_indifferent_access) - - allow_any_instance_of(ClaimsApi::VBMSUploader).to receive(:fetch_upload_token).and_return(token_response) - allow_any_instance_of(ClaimsApi::VBMSUploader).to receive(:upload_document).and_return(document_response) - allow_any_instance_of(BGS::PersonWebService).to receive(:find_by_ssn).and_return({ file_nbr: '123456789' }) - - expect(ClaimsApi::PoaUpdater).to receive(:perform_async) + VCR.use_cassette('mpi/find_candidate/valid_icn_full') do + expect_any_instance_of(ClaimsApi::V2::PoaPdfConstructor::Organization) + .to receive(:construct) + .with(final_data, id: power_of_attorney.id) + .and_call_original - subject.new.perform(power_of_attorney.id, '2122') + subject.new.perform(power_of_attorney.id, '2122') + end end end end diff --git a/modules/mobile/app/controllers/mobile/v0/maintenance_windows_controller.rb b/modules/mobile/app/controllers/mobile/v0/maintenance_windows_controller.rb index dfde1cdb9f0..b2b0c3f6324 100644 --- a/modules/mobile/app/controllers/mobile/v0/maintenance_windows_controller.rb +++ b/modules/mobile/app/controllers/mobile/v0/maintenance_windows_controller.rb @@ -11,7 +11,6 @@ class MaintenanceWindowsController < ApplicationController SERVICE_GRAPH = Mobile::V0::ServiceGraph.new( %i[bgs lighthouse], %i[mpi lighthouse], - %i[evss lighthouse], %i[bgs caseflow], %i[bgs payment_history], %i[arcgis facility_locator], @@ -20,9 +19,9 @@ class MaintenanceWindowsController < ApplicationController %i[vbms evss], %i[vbms lighthouse], %i[lighthouse claims], - %i[lighthouse direct_deposit_benefits], - %i[lighthouse disability_rating], - %i[lighthouse letters_and_documents], + %i[lighthouse_direct_deposit direct_deposit_benefits], + %i[evss disability_rating], + %i[evss letters_and_documents], %i[lighthouse immunizations], %i[mhv_platform mhv_sm], %i[mhv_platform mhv_meds], diff --git a/modules/mobile/spec/request/maintenance_windows_request_spec.rb b/modules/mobile/spec/request/maintenance_windows_request_spec.rb index 8e049b3a535..271f83c6e47 100644 --- a/modules/mobile/spec/request/maintenance_windows_request_spec.rb +++ b/modules/mobile/spec/request/maintenance_windows_request_spec.rb @@ -62,48 +62,39 @@ def mw_uuid(service_name) expect(response.parsed_body['data']).to eq( [ { - 'id' => mw_uuid('claims'), - 'type' => 'maintenance_window', - 'attributes' => { - 'service' => 'claims', - 'startTime' => '2021-05-25T21:33:39.000Z', - 'endTime' => '2021-05-25T22:33:39.000Z' - } - }, - { - 'id' => mw_uuid('direct_deposit_benefits'), + 'id' => mw_uuid('disability_rating'), 'type' => 'maintenance_window', 'attributes' => { - 'service' => 'direct_deposit_benefits', + 'service' => 'disability_rating', 'startTime' => '2021-05-25T21:33:39.000Z', 'endTime' => '2021-05-25T22:33:39.000Z' } }, { - 'id' => mw_uuid('disability_rating'), + 'id' => mw_uuid('letters_and_documents'), 'type' => 'maintenance_window', 'attributes' => { - 'service' => 'disability_rating', + 'service' => 'letters_and_documents', 'startTime' => '2021-05-25T21:33:39.000Z', 'endTime' => '2021-05-25T22:33:39.000Z' } }, { - 'id' => mw_uuid('letters_and_documents'), + 'id' => 'ebd9fb30-4df2-52e9-b951-297a9a4cc65c', 'type' => 'maintenance_window', 'attributes' => { - 'service' => 'letters_and_documents', - 'startTime' => '2021-05-25T21:33:39.000Z', - 'endTime' => '2021-05-25T22:33:39.000Z' + 'service' => 'claims', + 'startTime' => '2021-05-25T23:33:39.000Z', + 'endTime' => '2021-05-26T01:45:00.000Z' } }, { - 'id' => mw_uuid('immunizations'), + 'id' => '0ce167f8-9522-52c3-94e0-4f1e367d7064', 'type' => 'maintenance_window', 'attributes' => { 'service' => 'immunizations', - 'startTime' => '2021-05-25T21:33:39.000Z', - 'endTime' => '2021-05-25T22:33:39.000Z' + 'startTime' => '2021-05-25T23:33:39.000Z', + 'endTime' => '2021-05-26T01:45:00.000Z' } } ] @@ -159,8 +150,7 @@ def mw_uuid(service_name) evss_latest_end_time = latest_evss_starting.end_time.iso8601 expect(response.body).to match_json_schema('maintenance_windows') - expect(attributes.pluck('service').uniq).to eq(%w[claims direct_deposit_benefits disability_rating - letters_and_documents immunizations]) + expect(attributes.pluck('service').uniq).to eq(%w[disability_rating letters_and_documents]) expect(attributes.map { |w| Time.parse(w['startTime']).iso8601 }.uniq).to eq([evss_eariest_start_time]) expect(attributes.map { |w| Time.parse(w['endTime']).iso8601 }.uniq).to eq([evss_eariest_end_time]) @@ -169,8 +159,7 @@ def mw_uuid(service_name) expect(response.body).to match_json_schema('maintenance_windows') attributes = response.parsed_body['data'].pluck('attributes') - expect(attributes.pluck('service').uniq).to eq(%w[claims direct_deposit_benefits disability_rating - letters_and_documents immunizations]) + expect(attributes.pluck('service').uniq).to eq(%w[disability_rating letters_and_documents]) expect(attributes.map { |w| Time.parse(w['startTime']).iso8601 }.uniq).to eq([evss_middle_start_time]) expect(attributes.map { |w| Time.parse(w['endTime']).iso8601 }.uniq).to eq([evss_middle_end_time]) @@ -179,8 +168,8 @@ def mw_uuid(service_name) expect(response.body).to match_json_schema('maintenance_windows') attributes = response.parsed_body['data'].pluck('attributes') - expect(attributes.pluck('service').uniq).to eq(%w[claims direct_deposit_benefits disability_rating - letters_and_documents immunizations]) + expect(attributes.pluck('service').uniq).to eq(%w[disability_rating letters_and_documents]) + expect(attributes.map { |w| Time.parse(w['startTime']).iso8601 }.uniq).to eq([evss_latest_start_time]) expect(attributes.map { |w| Time.parse(w['endTime']).iso8601 }.uniq).to eq([evss_latest_end_time]) end @@ -191,7 +180,7 @@ def mw_uuid(service_name) let!(:latest_evss_starting) { FactoryBot.create(:mobile_maintenance_evss_second) } let!(:earliest_bgs_starting) { FactoryBot.create(:mobile_maintenance_bgs_first) } let!(:latest_bgs_starting) { FactoryBot.create(:mobile_maintenance_bgs_second) } - let(:evss_services) { %w[claims direct_deposit_benefits disability_rating letters_and_documents].freeze } + let(:evss_services) { %w[disability_rating letters_and_documents].freeze } let(:bgs_services) { %w[payment_history appeals].freeze } before { Timecop.freeze('2021-05-25T03:33:39Z') } diff --git a/modules/my_health/app/serializers/my_health/v1/prescription_details_serializer.rb b/modules/my_health/app/serializers/my_health/v1/prescription_details_serializer.rb index 10f58cdd9f6..d407ca1cc9b 100644 --- a/modules/my_health/app/serializers/my_health/v1/prescription_details_serializer.rb +++ b/modules/my_health/app/serializers/my_health/v1/prescription_details_serializer.rb @@ -30,6 +30,11 @@ class PrescriptionDetailsSerializer < PrescriptionSerializer attribute :tracking attribute :orderable_item attribute :sorted_dispensed_date + + def rx_rf_records + records = object&.rx_rf_records + records&.dig(0, 1) || [] + end end end end diff --git a/modules/my_health/spec/serializer/v1/prescription_details_serializer_spec.rb b/modules/my_health/spec/serializer/v1/prescription_details_serializer_spec.rb new file mode 100644 index 00000000000..9b71249de6c --- /dev/null +++ b/modules/my_health/spec/serializer/v1/prescription_details_serializer_spec.rb @@ -0,0 +1,172 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe MyHealth::V1::PrescriptionDetailsSerializer, type: :serializer do + subject { serialize(prescription, serializer_class: described_class) } + + let(:prescription) { build(:prescription_details) } + let(:data) { JSON.parse(subject)['data'] } + let(:attributes) { data['attributes'] } + + it 'includes the prescription_id' do + expect(attributes['prescription_id']).to eq(prescription.prescription_id) + end + + it 'includes the refill_status' do + expect(attributes['refill_status']).to eq(prescription.refill_status) + end + + it 'includes the refill_date' do + expect(attributes['refill_date']).to eq(prescription.refill_date.strftime('%Y-%m-%dT%H:%M:%S.%LZ')) + end + + it 'includes the refill_submit_date' do + expect(attributes['refill_submit_date']).to eq(prescription.refill_submit_date.strftime('%Y-%m-%dT%H:%M:%S.%LZ')) + end + + it 'includes the refill_remaining' do + expect(attributes['refill_remaining']).to eq(prescription.refill_remaining) + end + + it 'includes the facility_name' do + expect(attributes['facility_name']).to eq(prescription.facility_name) + end + + it 'includes the ordered_date' do + expect(attributes['ordered_date']).to eq(prescription.ordered_date.strftime('%Y-%m-%dT%H:%M:%S.%LZ')) + end + + it 'includes the quantity' do + expect(attributes['quantity']).to eq(prescription.quantity) + end + + it 'includes the expiration_date' do + expect(attributes['expiration_date']).to eq(prescription.expiration_date.strftime('%Y-%m-%dT%H:%M:%S.%LZ')) + end + + it 'includes the prescription_number' do + expect(attributes['prescription_number']).to eq(prescription.prescription_number) + end + + it 'includes the prescription_name' do + expect(attributes['prescription_name']).to eq(prescription.prescription_name) + end + + it 'includes the dispensed_date' do + expect(attributes['dispensed_date']).to eq(prescription.dispensed_date.strftime('%Y-%m-%dT%H:%M:%S.%LZ')) + end + + it 'includes the station_number' do + expect(attributes['station_number']).to eq(prescription.station_number) + end + + it 'includes the is_refillable' do + expect(attributes['is_refillable']).to eq(prescription.is_refillable) + end + + it 'includes the is_trackable' do + expect(attributes['is_trackable']).to eq(prescription.is_trackable) + end + + it 'includes the in_cerner_transition' do + expect(attributes['in_cerner_transition']).to eq(prescription.in_cerner_transition) + end + + it 'includes the not_refillable_display_message' do + expect(attributes['not_refillable_display_message']).to eq(prescription.not_refillable_display_message) + end + + it 'includes the cmop_ndc_number' do + expect(attributes['cmop_ndc_number']).to eq(prescription.cmop_ndc_number) + end + + it 'includes the sig' do + expect(attributes['sig']).to eq(prescription.sig) + end + + it 'includes the user_id' do + expect(attributes['user_id']).to eq(prescription.user_id) + end + + it 'includes the provider_first_name' do + expect(attributes['provider_first_name']).to eq(prescription.provider_first_name) + end + + it 'includes the provider_last_name' do + expect(attributes['provider_last_name']).to eq(prescription.provider_last_name) + end + + it 'includes the remarks' do + expect(attributes['remarks']).to eq(prescription.remarks) + end + + it 'includes the division_name' do + expect(attributes['division_name']).to eq(prescription.division_name) + end + + it 'includes the modified_date' do + expect(attributes['modified_date']).to eq(prescription.modified_date.strftime('%Y-%m-%dT%H:%M:%S.%LZ')) + end + + it 'includes the institution_id' do + expect(attributes['institution_id']).to eq(prescription.institution_id) + end + + it 'includes the dial_cmop_division_phone' do + expect(attributes['dial_cmop_division_phone']).to eq(prescription.dial_cmop_division_phone) + end + + it 'includes the disp_status' do + expect(attributes['disp_status']).to eq(prescription.disp_status) + end + + it 'includes the ndc' do + expect(attributes['ndc']).to eq(prescription.ndc) + end + + it 'includes the reason' do + expect(attributes['reason']).to eq(prescription.reason) + end + + it 'includes the prescription_number_index' do + expect(attributes['prescription_number_index']).to eq(prescription.prescription_number_index) + end + + it 'includes the prescription_source' do + expect(attributes['prescription_source']).to eq(prescription.prescription_source) + end + + it 'includes the disclaimer' do + expect(attributes['disclaimer']).to eq(prescription.disclaimer) + end + + it 'includes the indication_for_use' do + expect(attributes['indication_for_use']).to eq(prescription.indication_for_use) + end + + it 'includes the indication_for_use_flag' do + expect(attributes['indication_for_use_flag']).to eq(prescription.indication_for_use_flag) + end + + it 'includes the category' do + expect(attributes['category']).to eq(prescription.category) + end + + it 'includes the tracking' do + expect(attributes['tracking']).to eq(prescription.tracking) + end + + it 'includes the orderable_item' do + expect(attributes['orderable_item']).to eq(prescription.orderable_item) + end + + it 'includes the sorted_dispensed_date' do + expect(attributes['sorted_dispensed_date']).to eq(prescription.sorted_dispensed_date.strftime('%Y-%m-%d')) + end + + it 'includes the rx_rf_records' do + expect(attributes['rx_rf_records']).to be_an(Array) + expect(attributes['rx_rf_records']).to all(be_a(Hash)) + end +end diff --git a/modules/simple_forms_api/app/form_mappings/vba_40_10007.json.erb b/modules/simple_forms_api/app/form_mappings/vba_40_10007.json.erb index 3457742f35e..612ccea645e 100644 --- a/modules/simple_forms_api/app/form_mappings/vba_40_10007.json.erb +++ b/modules/simple_forms_api/app/form_mappings/vba_40_10007.json.erb @@ -1,6 +1,6 @@ { - "F[0].Page_1[0].Field16[0]": "<%= (form.data.dig('application', 'claimant', 'relationship_to_vet')) != '1' ? (form.data.dig('application', 'veteran', 'current_name', 'last') || '')+" "+(form.data.dig('application', 'veteran', 'current_name', 'first') || '')+ " "+(form.data.dig('application', 'veteran', 'current_name', 'middle') || '')+" "+(form.data.dig('application', 'veteran', 'current_name', 'suffix') || '') : (form.data.dig('application', 'claimant', 'name', 'last') || '')+" "+(form.data.dig('application', 'claimant', 'name', 'first') || '')+ " "+(form.data.dig('application', 'claimant', 'name', 'middle') || '')+" "+(form.data.dig('application', 'claimant', 'name', 'suffix') || '') %>", - "F[0].Page_1[0].Field16[1]": "<%= (form.data.dig('application', 'veteran', 'service_name', 'last') || '')+" "+(form.data.dig('application', 'veteran', 'service_name', 'first') || '')+" "+ (form.data.dig('application', 'veteran', 'service_name', 'middle') || '')+" "+(form.data.dig('application', 'veteran', 'service_name', 'suffix') || '') %>", + "F[0].Page_1[0].Field16[0]": "<%= (form.data.dig('application', 'claimant', 'relationship_to_vet') != '1' ? [form.data.dig('application', 'veteran', 'current_name', 'last'), form.data.dig('application', 'veteran', 'current_name', 'first'), form.data.dig('application', 'veteran', 'current_name', 'middle'), form.data.dig('application', 'veteran', 'current_name', 'suffix')] : [form.data.dig('application', 'claimant', 'name', 'last'), form.data.dig('application', 'claimant', 'name', 'first'), form.data.dig('application', 'claimant', 'name', 'middle'), form.data.dig('application', 'claimant', 'name', 'suffix')]).compact.reject(&:empty?).join(', ') %>", + "F[0].Page_1[0].Field16[1]": "<%= [form.data.dig('application', 'veteran', 'service_name', 'last'), form.data.dig('application', 'veteran', 'service_name', 'first'), form.data.dig('application', 'veteran', 'service_name', 'middle'), form.data.dig('application', 'veteran', 'service_name', 'suffix') ].compact.reject(&:empty?).join(', ')%>", "F[0].Page_1[0].Field16[2]": "<%= (form.data.dig('application', 'claimant', 'relationship_to_vet')) != '1' ? (form.data.dig('application', 'veteran', 'address', 'street') || '') + '\n' + (form.data.dig('application', 'veteran', 'address', 'street2') || '') + '\n'+ (form.data.dig('application', 'veteran', 'address', 'city') || '') + ' ' + (form.data.dig('application', 'veteran', 'address', 'state') || '') +' '+ (form.data.dig('application', 'veteran', 'address', 'postal_code') || '') +' '+ (form.data.dig('application', 'veteran', 'address', 'country') || '') : (form.data.dig('application', 'claimant', 'address', 'street') || '') + ' ' + (form.data.dig('application', 'claimant', 'address', 'street2') || '')+ ' '+ (form.data.dig('application', 'claimant', 'address', 'city') || '') + ' ' + (form.data.dig('application', 'claimant', 'address', 'state') || '') +' '+ (form.data.dig('application', 'claimant', 'address', 'postal_code') || '') +' '+ (form.data.dig('application', 'claimant', 'address', 'country') || '')%>", "F[0].Page_1[0].Field18[0]": "<%= (form.data.dig('application', 'claimant', 'relationship_to_vet')) != '1' ? (form.data.dig('application', 'veteran', 'ssn') || '') : (form.data.dig('application', 'claimant', 'ssn') || '') %>", "F[0].Page_1[0].Field19[0]": "<%= (form.data.dig('application', 'veteran', 'military_service_number') || '') %>", @@ -29,10 +29,8 @@ "F[0].Page_1[0].Field13[18]": "<%= (form.data.dig('application', 'veteran', 'military_status')) == 'V' ? 1 : 0%>", "F[0].Page_1[0].Field13[19]": "<%= (form.data.dig('application', 'veteran', 'military_status')) == 'E' ? 1 : 0%>", "F[0].Page_1[0].Field13[20]": "<%= (form.data.dig('application', 'veteran', 'military_status')) == 'D' ? 1 : 0%>", - "F[0].Page_1[0].Field13[21]": "<%= (form.data.dig('application', 'veteran', 'military_status')) == 'O' ? 1 : 0%>", - "F[0].Page_1[0].Field13[22]": "<%= (form.data.dig('application', 'veteran', 'military_status')) == 'O' ? 1 : 0%>", "F[0].Page_1[0].Field13[23]": "<%= (form.data.dig('application', 'veteran', 'military_status')) == 'I' ? 1 : 0%>", - "F[0].Page_1[0].Field13[24]": "<%= ((form.data.dig('application', 'veteran', 'military_status')) == 'X')|| (form.data.dig('application', 'veteran', 'military_status')) == 'A' ||(form.data.dig('application', 'veteran', 'military_status')) == 'R' ||(form.data.dig('application', 'veteran', 'military_status')) == 'S' ? 1 : 0%>", + "F[0].Page_1[0].Field13[24]": "<%= ((form.data.dig('application', 'veteran', 'military_status')) == 'X')|| (form.data.dig('application', 'veteran', 'military_status')) == 'A' ||(form.data.dig('application', 'veteran', 'military_status')) == 'R' || (form.data.dig('application', 'veteran', 'military_status')) == 'O' || (form.data.dig('application', 'veteran', 'military_status')) == 'S' ? 1 : 0%>", "F[0].Page_1[0].Field16[3]": "<%= form.get_service_label(form.service(0, "service_branch", nil)) %>", "F[0].Page_1[0].Field30[0]": "<%= form.format_date(form.service(0, "date_range", "from")) %>", "F[0].Page_1[0].Field31[0]": "<%= form.format_date(form.service(0, "date_range", "to")) %>", @@ -57,7 +55,7 @@ "F[0].Page_1[0].Field8[0]": "<%= form.data.dig('application', 'currently_buried_persons').to_a.map { |person| [person.dig('name', 'first'), person.dig('name', 'last')].reject(&:blank?).join(' ').presence || "" }.join(', ') %>", "F[0].Page_1[0].Field13[27]": "<%= (form.data.dig('application', 'preneed_attachments') ? 1 : 0 )%>", "F[0].Page_1[0].Field13[28]": "<%= (form.data.dig('application', 'preneed_attachments') ? 0 : 1 )%>", - "F[0].Page_1[0].Field16[6]": "<%= (form.data.dig('application', 'claimant', 'name', 'last') || '') +" "+(form.data.dig('application', 'claimant', 'name', 'first') || '')+" "+(form.data.dig('application', 'claimant', 'name', 'middle') || '')+" "+(form.data.dig('application', 'claimant', 'name', 'suffix') || '') %>", + "F[0].Page_1[0].Field16[6]": "<%= [form.data.dig('application', 'claimant', 'name', 'last'), form.data.dig('application', 'claimant', 'name', 'first'),form.data.dig('application', 'claimant', 'name', 'middle'), form.data.dig('application', 'claimant', 'name', 'suffix')].compact.reject(&:empty?).join(', ') %>", "F[0].Page_1[0].Field12[1]": "<%= (form.data.dig('application', 'claimant', 'relationship_to_vet')) == '1' ? 1 : 0%>", "F[0].Page_1[0].Field12[2]": "<%= (form.data.dig('application', 'claimant', 'relationship_to_vet')) == '2' ? 1 : 0%>", "F[0].Page_1[0].Field12[3]": "<%= (form.data.dig('application', 'claimant', 'relationship_to_vet')) == '3' ? 1 : 0%>", @@ -74,9 +72,9 @@ "F[0].#subform[1].Field30[0]": "<%= Time.current.in_time_zone('America/Chicago').strftime('%m/%d/%Y') %>", "F[0].#subform[1].Field12[0]": "<%= (form.data.dig('application', 'applicant', 'applicant_relationship_to_claimant')) == 'Self' ? 1 : 0%>", "F[0].#subform[1].Field12[1]": "<%= (form.data.dig('application', 'applicant', 'applicant_relationship_to_claimant')) == 'Authorized Agent/Rep' ? 1 : 0%>", - "F[0].#subform[1].Field11[1]": "<%= (form.data.dig('application', 'applicant', 'name', 'last') || '')+" "+(form.data.dig('application', 'applicant', 'name', 'first') || '')%>", + "F[0].#subform[1].Field11[1]": "<%= [form.data.dig('application', 'applicant', 'name', 'last'), form.data.dig('application', 'applicant', 'name', 'first')].compact.reject(&:empty?).join(', ')%>", "F[0].#subform[1].Field16[0]": "<%= (form.data.dig('application', 'applicant', 'mailing_address', 'street') || '') + ' ' + (form.data.dig('application', 'applicant', 'mailing_address', 'street2') || '')+ ' '+ (form.data.dig('application', 'applicant', 'mailing_address', 'city') || '') + ' ' + (form.data.dig('application', 'applicant', 'mailing_address', 'state') || '') +' '+ (form.data.dig('application', 'applicant', 'mailing_address', 'postal_code') || '') +' '+ (form.data.dig('application', 'applicant', 'mailing_address', 'country') || '') %>", "F[0].#subform[1].Field11[2]": "<%= (form.data.dig('application', 'applicant', 'applicant_phone_number')|| '')%>", "F[0].#subform[1].Field8[0]": "<%= form.data.dig('Field8') %>", "F[0]": "<%= form.data.dig('F') %>" -} \ No newline at end of file +} diff --git a/modules/simple_forms_api/app/models/simple_forms_api/vba_40_10007.rb b/modules/simple_forms_api/app/models/simple_forms_api/vba_40_10007.rb index 55bd5887a7b..0f9453a951c 100644 --- a/modules/simple_forms_api/app/models/simple_forms_api/vba_40_10007.rb +++ b/modules/simple_forms_api/app/models/simple_forms_api/vba_40_10007.rb @@ -163,35 +163,35 @@ def create_attachment_page(file_path) pdf.move_down 20 pdf.text 'The following pages contain data related to the application.', align: :center pdf.move_down 20 - pdf.text 'Question 10' + pdf.text 'Question 10 Place of Birth' pdf.text "Place of Birth: #{place_of_birth}", size: 10 pdf.move_down 10 - pdf.text 'Question 15 Service Branch Line 1' - pdf.text "Service Branch: #{service_branch_value_a}", size: 10 + pdf.text 'Question 15 Branch of Service Line 1' + pdf.text "Branch of Service: #{service_branch_value_a}", size: 10 pdf.move_down 10 - pdf.text 'Question 18 Discharge type Line 1' - pdf.text "Discharge Type: #{discharge_type_a}", size: 10 + pdf.text 'Question 18 Discharge - Character of Service Line 1' + pdf.text "Character of Service: #{discharge_type_a}", size: 10 pdf.move_down 10 - pdf.text 'Question 19 Highest rank Line 1' - pdf.text "Highest Rank: #{highest_rank_a}", size: 10 + pdf.text 'Question 19 Highest Rank Attained Line 1' + pdf.text "Highest Rank Attained: #{highest_rank_a}", size: 10 pdf.move_down 10 - pdf.text 'Question 15 Service Branch Line 2' - pdf.text "Service Branch: #{service_branch_value_b}", size: 10 + pdf.text 'Question 15 Branch of Service Line 2' + pdf.text "Branch of Service: #{service_branch_value_b}", size: 10 pdf.move_down 10 - pdf.text 'Question 18 Discharge type Line 2' - pdf.text "Discharge Type: #{discharge_type_b}", size: 10 + pdf.text 'Question 18 Discharge - Character of Service Line 2' + pdf.text "Character of Service: #{discharge_type_b}", size: 10 pdf.move_down 10 - pdf.text 'Question 19 Highest rank Line 2' - pdf.text "Highest Rank: #{highest_rank_b}", size: 10 + pdf.text 'Question 19 Highest Rank Attained Line 2' + pdf.text "Highest Rank Attained: #{highest_rank_b}", size: 10 pdf.move_down 10 - pdf.text 'Question 15 Service Branch Line 3' - pdf.text "Service Branch: #{service_branch_value_c}", size: 10 + pdf.text 'Question 15 Branch of Service Line 3' + pdf.text "Branch of Service: #{service_branch_value_c}", size: 10 pdf.move_down 10 - pdf.text 'Question 18 Discharge type Line 3' - pdf.text "Discharge Type: #{discharge_type_c}", size: 10 + pdf.text 'Question 18 Discharge - Character of Service Line 3' + pdf.text "Character of Service: #{discharge_type_c}", size: 10 pdf.move_down 10 - pdf.text 'Question 19 Highest rank Line 3' - pdf.text "Highest Rank: #{highest_rank_c}", size: 10 + pdf.text 'Question 19 Highest Rank Attained Line 3' + pdf.text "Highest Rank Attained: #{highest_rank_c}", size: 10 end end diff --git a/modules/simple_forms_api/app/models/simple_forms_api/vha_10_10d.rb b/modules/simple_forms_api/app/models/simple_forms_api/vha_10_10d.rb index 3933b273bb8..3dc0096195b 100644 --- a/modules/simple_forms_api/app/models/simple_forms_api/vha_10_10d.rb +++ b/modules/simple_forms_api/app/models/simple_forms_api/vha_10_10d.rb @@ -5,6 +5,7 @@ module SimpleFormsApi class VHA1010d include Virtus.model(nullify_blank: true) + STATS_KEY = 'api.simple_forms_api.1010d' attribute :data @@ -50,7 +51,11 @@ def submission_date_config { should_stamp_date?: false } end - def track_user_identity(confirmation_number); end + def track_user_identity(confirmation_number) + identity = "#{data['claimant_type']} #{data['claim_ownership']}" + StatsD.increment("#{STATS_KEY}.#{identity}") + Rails.logger.info('Simple forms api - 1010d submission user identity', identity:, confirmation_number:) + end private diff --git a/modules/simple_forms_api/spec/fixtures/form_json/vba_40_10007_with_supporting_document.json b/modules/simple_forms_api/spec/fixtures/form_json/vba_40_10007_with_supporting_document.json index d346a56f824..97705ea9e68 100644 --- a/modules/simple_forms_api/spec/fixtures/form_json/vba_40_10007_with_supporting_document.json +++ b/modules/simple_forms_api/spec/fixtures/form_json/vba_40_10007_with_supporting_document.json @@ -17,6 +17,10 @@ "state": "OH", "postal_code": "23432" }, + "name": { + "first": "john", + "last": "stockton" + }, "applicant_phone_number": "3125555678" }, "currently_buried_persons": [ @@ -72,9 +76,31 @@ "veteran": { "service_records": [ { - "service_branch": "FF", + "date_range": { + "from": "1990-01-01", + "to": "2000-02-03" + }, + "service_branch": "CV", + "discharge_type": "1", + "highest_rank": "Lord Commander of a" + }, + { + "date_range": { + "from": "1990-01-01", + "to": "2000-02-03" + }, + "service_branch": "AR", "discharge_type": "3", - "highest_rank": "Commander of the Starship Trooper legion of Klendathu" + "highest_rank": "Lord Commander of b" + }, + { + "date_range": { + "from": "1990-01-01", + "to": "2000-02-03" + }, + "service_branch": "CI", + "discharge_type": "4", + "highest_rank": "Lord Commander of c" } ], "address": { @@ -100,6 +126,12 @@ "race": { "is_asian": true }, + "service_name": { + "first": "Quinlan", + "middle": "Tucker Strong", + "last": "Short", + "suffix": "III" + }, "marital_status": "Divorced", "place_of_birth": "Adipisci et numquam anim sit odio dolore velit molestiae soluta sit voluptatem doloremque" } diff --git a/modules/vaos/app/services/vaos/v2/appointments_service.rb b/modules/vaos/app/services/vaos/v2/appointments_service.rb index 56850312837..3fcadb28f69 100644 --- a/modules/vaos/app/services/vaos/v2/appointments_service.rb +++ b/modules/vaos/app/services/vaos/v2/appointments_service.rb @@ -17,7 +17,6 @@ class AppointmentsService < VAOS::SessionService AVS_APPT_TEST_ID = '192308' AVS_FLIPPER = :va_online_scheduling_after_visit_summary - CANCEL_EXCLUSION = :va_online_scheduling_cancellation_exclusion ORACLE_HEALTH_CANCELLATIONS = :va_online_scheduling_enable_OH_cancellations APPOINTMENTS_USE_VPG = :va_online_scheduling_use_vpg @@ -34,9 +33,6 @@ def get_appointments(start_date, end_date, statuses = nil, pagination_params = { response = perform(:get, appointments_base_path, params, headers) SchemaContract::ValidationInitiator.call(user:, response:, contract_name: 'appointments_index') response.body[:data].each do |appt| - # for Lovell appointments set cancellable to false per GH#75512 - set_cancellable_false(appt) if lovell_appointment?(appt) && Flipper.enabled?(CANCEL_EXCLUSION, user) - # for CnP and covid appointments set cancellable to false per GH#57824, GH#58690 set_cancellable_false(appt) if cnp?(appt) || covid?(appt) @@ -68,11 +64,6 @@ def get_appointment(appointment_id) response = perform(:get, get_appointment_base_path(appointment_id), params, headers) convert_appointment_time(response.body[:data]) - # for Lovell appointments set cancellable to false per GH#75512 - if lovell_appointment?(response.body[:data]) && Flipper.enabled?(CANCEL_EXCLUSION, user) - set_cancellable_false(response.body[:data]) - end - # for CnP and covid appointments set cancellable to false per GH#57824, GH#58690 set_cancellable_false(response.body[:data]) if cnp?(response.body[:data]) || covid?(response.body[:data]) @@ -304,12 +295,6 @@ def codes(input) input.flat_map { |codeable_concept| codeable_concept[:coding]&.pluck(:code) }.compact end - def lovell_appointment?(appt) - return false if appt.nil? || appt[:location_id].nil? - - appt[:location_id].start_with?('556') - end - # Checks if the appointment is associated with cerner. It looks through each identifier and checks if the system # contains cerner. If it does, it returns true. Otherwise, it returns false. # diff --git a/modules/vaos/app/services/vaos/v2/mobile_facility_service.rb b/modules/vaos/app/services/vaos/v2/mobile_facility_service.rb index d4683452203..268d5d4384d 100644 --- a/modules/vaos/app/services/vaos/v2/mobile_facility_service.rb +++ b/modules/vaos/app/services/vaos/v2/mobile_facility_service.rb @@ -6,8 +6,6 @@ module VAOS module V2 class MobileFacilityService < VAOS::SessionService - REMOVE_LOVELL = :va_online_scheduling_booking_exclusion - # Retrieves information about a VA clinic from the VAOS Service. # # @param station_id [String] the ID of the VA facility where the clinic is located @@ -88,10 +86,8 @@ def get_facilities(ids:, schedulable:, children: nil, type: nil, pagination_para with_monitoring do options = { params_encoder: Faraday::FlatParamsEncoder } response = perform(:get, facilities_url, params, headers, options) - # prevent Lovell sites from being scheduled for appointments GH#75460 - filtered_facilities = remove_lovell_sites(response.body[:data]) if Flipper.enabled?(REMOVE_LOVELL, user) { - data: deserialized_facilities(filtered_facilities || response.body[:data]), + data: deserialized_facilities(response.body[:data]), meta: pagination(pagination_params) } end @@ -181,12 +177,6 @@ def get_scheduling_configurations(facility_ids, cc_enabled = nil, pagination_par private - def remove_lovell_sites(facilities) - return [] if facilities.blank? - - facilities.reject { |facility| facility[:id].start_with?('556') } - end - # Reads cached facilities from Rails cache. It reads the cache for each id # provided in the array, maps them into a new array and returns the new array # excluding nil values. diff --git a/modules/vaos/spec/services/v2/appointment_service_spec.rb b/modules/vaos/spec/services/v2/appointment_service_spec.rb index c7b5b73adc2..34d1dbd93f7 100644 --- a/modules/vaos/spec/services/v2/appointment_service_spec.rb +++ b/modules/vaos/spec/services/v2/appointment_service_spec.rb @@ -940,25 +940,4 @@ end end end - - describe 'lovell_appointment?' do - it 'returns false when the appointment is nil' do - expect(subject.send(:lovell_appointment?, nil)).to eq(false) - end - - it 'returns false when the appointment location id is missing' do - appointment = { id: '123456' } - expect(subject.send(:lovell_appointment?, appointment)).to eq(false) - end - - it 'returns true if the appointment is a Lovell appointment' do - appointment = { location_id: '556', id: '123456' } - expect(subject.send(:lovell_appointment?, appointment)).to eq(true) - end - - it 'returns false if the appointment is not a Lovell appointment' do - appointment = { location_id: '983', id: '123456' } - expect(subject.send(:lovell_appointment?, appointment)).to eq(false) - end - end end diff --git a/modules/vaos/spec/services/v2/mobile_facility_service_spec.rb b/modules/vaos/spec/services/v2/mobile_facility_service_spec.rb index 8cf78c56035..c48c6eed370 100644 --- a/modules/vaos/spec/services/v2/mobile_facility_service_spec.rb +++ b/modules/vaos/spec/services/v2/mobile_facility_service_spec.rb @@ -452,32 +452,6 @@ end end - describe '#remove_lovell_sites' do - context 'when facilities are blank' do - let(:facilities) { [] } - - it 'returns an empty array' do - expect(subject.send(:remove_lovell_sites, facilities)).to eq([]) - end - end - - context 'when facilities are not blank' do - let(:facilities) { [{ id: '556' }, { id: '983' }, { id: '556GA' }] } - - it 'returns an array without facilities starting with 556' do - expect(subject.send(:remove_lovell_sites, facilities)).to eq([{ id: '983' }]) - end - end - - context 'when facilities are all Lovell' do - let(:facilities) { [{ id: '556' }, { id: '556GA' }] } - - it 'returns an empty array' do - expect(subject.send(:remove_lovell_sites, facilities)).to eq([]) - end - end - end - describe '#page_params' do context 'when per_page is positive' do context 'when per_page is positive' do diff --git a/spec/lib/central_mail/datestamp_pdf_spec.rb b/spec/lib/central_mail/datestamp_pdf_spec.rb index 50c1dabba5f..554a2ead6cb 100644 --- a/spec/lib/central_mail/datestamp_pdf_spec.rb +++ b/spec/lib/central_mail/datestamp_pdf_spec.rb @@ -45,6 +45,22 @@ def assert_pdf_stamp(file, stamp) File.delete(out_path) end + it 'adds text with a datestamp for all forms except 40-10007 with expected formatting' do + out_path = instance.run(text: 'Received via vets.gov', x: 10, y: 10, timestamp: Time.zone.local(2024, 1, 30)) + pdf_reader = PDF::Reader.new(out_path) + expect(pdf_reader.pages[0].text).to eq('Received via vets.gov 2024-01-30. Confirmation=VETS-XX-1234') + File.delete(out_path) + end + + it 'adds text with a datestamp for form 40-10007 with expected formatting' do + @file_path = 'tmp/vba_40_10007-stamped.pdf' + Prawn::Document.new.render_file @file_path + out_path = instance.run(text: 'Received via vets.gov', x: 10, y: 10, timestamp: Time.zone.local(2024, 1, 30)) + pdf_reader = PDF::Reader.new(out_path) + expect(pdf_reader.pages[0].text).to eq('Received via vets.gov 01/30/2024. Confirmation=VETS-XX-1234') + File.delete(out_path) + end + context 'with no additional text' do let(:opt) do {} diff --git a/spec/lib/chip/redis_client_spec.rb b/spec/lib/chip/redis_client_spec.rb index 6d5e4c7d667..cb8f8c9e618 100644 --- a/spec/lib/chip/redis_client_spec.rb +++ b/spec/lib/chip/redis_client_spec.rb @@ -29,17 +29,11 @@ let(:token) { 'test_token' } it 'saves entry' do - expect_any_instance_of(Redis::Namespace).to receive(:set).once.with( - tenant_id, 'test_token', { ex: REDIS_CONFIG[:chip][:each_ttl] } + expect_any_instance_of(Redis).to receive(:set).once.with( + "chip:#{tenant_id}", 'test_token', { ex: REDIS_CONFIG[:chip][:each_ttl] } ) redis_client.save(token:) end - - it 'saves entry with chip namespace' do - redis_client.save(token:) - - expect(redis_client.redis_namespace.redis.keys).to include("chip:#{tenant_id}") - end end describe 'ttl' do diff --git a/spec/lib/common/models/redis_store_spec.rb b/spec/lib/common/models/redis_store_spec.rb index 0e0b3a1cfd0..d9147240c6b 100644 --- a/spec/lib/common/models/redis_store_spec.rb +++ b/spec/lib/common/models/redis_store_spec.rb @@ -62,18 +62,12 @@ describe '#save' do it 'saves serialized class to redis with the correct namespace' do - expect_any_instance_of(Redis::Namespace).to receive(:set).once.with( - 'e66fd7b7-94e0-4748-8063-283f55efb0ea', + expect_any_instance_of(Redis).to receive(:set).once.with( + 'my_namespace:e66fd7b7-94e0-4748-8063-283f55efb0ea', '{":uuid":"e66fd7b7-94e0-4748-8063-283f55efb0ea",":email":"foo@bar.com"}' ) subject.save end - - it 'saves entry with namespace' do - subject.save - - expect(subject.redis_namespace.redis.keys).to include('my_namespace:e66fd7b7-94e0-4748-8063-283f55efb0ea') - end end describe '#update' do @@ -98,18 +92,12 @@ describe '#destroy' do it 'removes itself from redis with the correct namespace' do - expect_any_instance_of(Redis::Namespace).to receive(:del).once.with( - 'e66fd7b7-94e0-4748-8063-283f55efb0ea' + expect_any_instance_of(Redis).to receive(:del).once.with( + 'my_namespace:e66fd7b7-94e0-4748-8063-283f55efb0ea' ) subject.destroy end - it "entry doesn't exists" do - subject.destroy - - expect(subject.redis_namespace.redis.keys).not_to include('my_namespace:e66fd7b7-94e0-4748-8063-283f55efb0ea') - end - it 'freezes the instance after destroy is called' do subject.destroy expect(subject.destroyed?).to eq(true) diff --git a/spec/models/saved_claim/veteran_readiness_employment_claim_spec.rb b/spec/models/saved_claim/veteran_readiness_employment_claim_spec.rb index 3c032fd3896..cd34d56d998 100644 --- a/spec/models/saved_claim/veteran_readiness_employment_claim_spec.rb +++ b/spec/models/saved_claim/veteran_readiness_employment_claim_spec.rb @@ -62,6 +62,7 @@ context 'when VBMS response is VBMSDownForMaintenance' do before do + allow(OpenSSL::PKCS12).to receive(:new).and_return(double.as_null_object) @vbms_client = FakeVBMS.new allow(VBMS::Client).to receive(:from_env_vars).and_return(@vbms_client) end @@ -170,6 +171,10 @@ describe '#send_to_central_mail!' do subject { claim.send_to_central_mail!(user_object) } + before do + allow_any_instance_of(Flipper::DSL).to receive(:enabled?).and_return(false) + end + it 'adds `veteranFullName` key to db so that SavedClaimJob can use it' do Sidekiq::Testing.inline! do VCR.use_cassette('central_mail/upload_one_attachment') do diff --git a/spec/requests/breakers_integration_spec.rb b/spec/requests/breakers_integration_spec.rb index 512c36ce557..48b56c4b60c 100644 --- a/spec/requests/breakers_integration_spec.rb +++ b/spec/requests/breakers_integration_spec.rb @@ -30,6 +30,7 @@ # Not clearing the breakers would cause subsequent RX calls to fail after the breaker is # triggered in this group. + # fakeredis/rspec has a `before` callback, but it's for the suite, not each example. Oops. Breakers.client.redis_connection.redis.flushdb end diff --git a/spec/sidekiq/lighthouse/submit_benefits_intake_claim_spec.rb b/spec/sidekiq/lighthouse/submit_benefits_intake_claim_spec.rb new file mode 100644 index 00000000000..665410df4bb --- /dev/null +++ b/spec/sidekiq/lighthouse/submit_benefits_intake_claim_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Lighthouse::SubmitBenefitsIntakeClaim, uploader_helpers: true do + stub_virus_scan + let(:job) { described_class.new } + let(:claim) { create(:veteran_readiness_employment_claim) } + + describe '#perform' do + let(:service) { double('service') } + let(:response) { double('response') } + let(:pdf_path) { 'random/path/to/pdf' } + let(:location) { 'test_location' } + + before do + allow(BenefitsIntakeService::Service).to receive(:new).and_return(service) + allow(service).to receive(:uuid) + allow(service).to receive(:location).and_return(location) + allow(service).to receive(:upload_doc).and_return(response) + end + + it 'submits the saved claim successfully' do + allow(response).to receive(:success?).and_return(true) + expect(job).to receive(:create_form_submission_attempt) + expect(job).to receive(:generate_metadata).once + expect(service).to receive(:upload_doc) + job.perform(claim.id) + expect(response.success?).to eq(true) + expect(claim.form_submissions).not_to eq(nil) + expect(claim.business_line).not_to eq(nil) + end + + it 'submits and gets a response error' do + allow(response).to receive(:success?).and_return(false) + allow(response).to receive(:body).and_return('There was an error submitting the claim') + expect(job).to receive(:create_form_submission_attempt) + expect(job).to receive(:generate_metadata).once + expect(service).to receive(:upload_doc) + expect { job.perform(claim.id) }.to raise_error(Lighthouse::SubmitBenefitsIntakeClaim::BenefitsIntakeClaimError) + expect(response.success?).to eq(false) + end + # perform + end + + describe '#process_record' do + it 'processes a record and add stamps' do + record = double + datestamp_double1 = double + datestamp_double2 = double + + expect(record).to receive(:to_pdf).and_return('path1') + expect(CentralMail::DatestampPdf).to receive(:new).with('path1').and_return(datestamp_double1) + expect(datestamp_double1).to receive(:run).with(text: 'VA.GOV', x: 5, y: 5).and_return('path2') + expect(CentralMail::DatestampPdf).to receive(:new).with('path2').and_return(datestamp_double2) + expect(datestamp_double2).to receive(:run).with( + text: 'FDC Reviewed - va.gov Submission', + x: 429, + y: 770, + text_only: true + ).and_return('path3') + + expect(described_class.new.process_record(record)).to eq('path3') + end + end + + describe 'sidekiq_retries_exhausted block' do + it 'logs a distinct error when retries are exhausted' do + Lighthouse::SubmitBenefitsIntakeClaim.within_sidekiq_retries_exhausted_block do + expect(Rails.logger).to receive(:error).exactly(:once) + expect(StatsD).to receive(:increment).with('worker.central_mail.submit_benefits_intake_claim.exhausted') + end + end + end + # Rspec.describe +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 8b036e3f589..b858a23e81a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'fakeredis/rspec' require 'i18n' require 'support/spec_builders' require 'support/matchers' @@ -14,7 +15,6 @@ require 'rspec/its' require 'rspec/retry' require 'aasm/rspec' -require 'mock_redis' # By default run SimpleCov, but allow an environment variable to disable. unless ENV['NOCOVERAGE'] @@ -192,8 +192,4 @@ config.after do Timecop.return end - - config.before do - $redis.flushdb - end end