diff --git a/Gemfile.lock b/Gemfile.lock index 3dd7a71f6dd..508023b6094 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -416,7 +416,7 @@ GEM factory_bot_rails (6.4.3) factory_bot (~> 6.4) railties (>= 5.0.0) - faker (3.2.3) + faker (3.3.0) i18n (>= 1.8.11, < 2) faraday (2.9.0) faraday-net_http (>= 2.0, < 3.2) @@ -691,7 +691,7 @@ GEM os (1.1.4) ox (2.14.18) parallel (1.24.0) - parallel_tests (4.5.2) + parallel_tests (4.6.0) parallel parser (3.3.0.5) ast (~> 2.4.1) @@ -810,7 +810,7 @@ GEM rb-inotify (0.10.1) ffi (~> 1.0) rchardet (1.8.0) - rdoc (6.6.2) + rdoc (6.6.3.1) psych (>= 4.0.0) redis (5.1.0) redis-client (>= 0.17.0) diff --git a/app/controllers/v0/burial_claims_controller.rb b/app/controllers/v0/burial_claims_controller.rb index 56b202efb03..77d398fb6b5 100644 --- a/app/controllers/v0/burial_claims_controller.rb +++ b/app/controllers/v0/burial_claims_controller.rb @@ -8,18 +8,24 @@ class BurialClaimsController < ClaimsBaseController def create PensionBurial::TagSentry.tag_sentry - claim = claim_class.new(form: filtered_params[:form]) + + claim = if Flipper.enabled?(:va_burial_v2) + # cannot parse a nil form, to pass unit tests do a check for form presence + form = filtered_params[:form] + claim_class.new(form:, formV2: form.present? ? JSON.parse(form)['formV2'] : nil) + else + claim_class.new(form: filtered_params[:form]) + end unless claim.save StatsD.increment("#{stats_key}.failure") Sentry.set_tags(team: 'benefits-memorial-1') # tag sentry logs with team name raise Common::Exceptions::ValidationErrors, claim end - # this method also calls claim.process_attachments! claim.submit_to_structured_data_services! - Rails.logger.info "ClaimID=#{claim.confirmation_number} Form=#{claim.class::FORM}" + Rails.logger.info "ClaimID=#{claim.confirmation_number} Form=#{claim.form_id}" clear_saved_form(claim.form_id) render(json: claim) end diff --git a/app/controllers/v0/claim_documents_controller.rb b/app/controllers/v0/claim_documents_controller.rb index 1bcaba7a14c..95c471e4e66 100644 --- a/app/controllers/v0/claim_documents_controller.rb +++ b/app/controllers/v0/claim_documents_controller.rb @@ -31,7 +31,7 @@ def create def klass case form_id - when '21P-527EZ', '21P-530' + when '21P-527EZ', '21P-530', '21P-530V2' PensionBurial::TagSentry.tag_sentry PersistentAttachments::PensionBurial when '21-686C', '686C-674' diff --git a/app/models/form_profile.rb b/app/models/form_profile.rb index 6cc8209e578..bfd47678896 100644 --- a/app/models/form_profile.rb +++ b/app/models/form_profile.rb @@ -92,7 +92,7 @@ class FormProfile 22-5495 22-0993 22-0994 FEEDBACK-TOOL 22-10203 22-1990S 22-1990EZ], evss: ['21-526EZ'], hca: %w[1010ez 10-10EZR], - pension_burial: %w[21P-530 21P-527EZ], + pension_burial: %w[21P-530 21P-527EZ 21P-530V2], dependents: ['686C-674'], decision_review: %w[20-0995 20-0996 10182], mdot: ['MDOT'], @@ -121,6 +121,7 @@ class FormProfile '22-5490E' => ::FormProfiles::VA5490e, '22-5495' => ::FormProfiles::VA5495, '21P-530' => ::FormProfiles::VA21p530, + '21P-530V2' => ::FormProfiles::VA21p530v2, '21-686C' => ::FormProfiles::VA21686c, '686C-674' => ::FormProfiles::VA686c674, '40-10007' => ::FormProfiles::VA4010007, diff --git a/app/models/form_profiles/va_21p530v2.rb b/app/models/form_profiles/va_21p530v2.rb new file mode 100644 index 00000000000..4cdbdfe237e --- /dev/null +++ b/app/models/form_profiles/va_21p530v2.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'iso_country_codes' + +class FormProfiles::VA21p530v2 < FormProfile + def metadata + { + version: 0, + prefill: true, + returnUrl: '/claimant-information' + } + end + + def prefill + @identity_information = initialize_identity_information + @contact_information = initialize_contact_information + if @contact_information&.address&.country.present? + @contact_information.address.country = convert_to_iso2(@contact_information.address.country) + end + mappings = self.class.mappings_for_form(form_id) + + form_data = generate_prefill(mappings) if FormProfile.prefill_enabled_forms.include?(form_id) + + { form_data:, metadata: } + end + + private + + def convert_to_iso2(country_code) + code = IsoCountryCodes.find(country_code) + code.alpha2 + end +end diff --git a/app/models/saved_claim.rb b/app/models/saved_claim.rb index 83c812e08cf..1d73e4da0c2 100644 --- a/app/models/saved_claim.rb +++ b/app/models/saved_claim.rb @@ -31,7 +31,7 @@ class SavedClaim < ApplicationRecord # create a uuid for this second (used in the confirmation number) and store # the form type based on the constant found in the subclass. after_initialize do - self.form_id = self.class::FORM.upcase + self.form_id = self.class::FORM.upcase unless instance_of?(SavedClaim::Burial) end def self.add_form_and_validation(form_id) @@ -51,7 +51,7 @@ def process_attachments! def submit_to_structured_data_services! # Only 21P-530 burial forms are supported at this time - if form_id != '21P-530' + unless %w[21P-530 21P-530V2].include?(form_id) err_message = "Unsupported form id: #{form_id}" raise Common::Exceptions::UnprocessableEntity.new(detail: err_message), err_message end diff --git a/app/models/saved_claim/burial.rb b/app/models/saved_claim/burial.rb index 02bddbc08aa..465821ab084 100644 --- a/app/models/saved_claim/burial.rb +++ b/app/models/saved_claim/burial.rb @@ -5,6 +5,17 @@ class SavedClaim::Burial < CentralMailClaim FORM = '21P-530' + # attribute name is passed from the FE as a flag, maintaining camel case + attr_accessor :formV2 # rubocop:disable Naming/MethodName + + after_initialize do + self.form_id = if Flipper.enabled?(:va_burial_v2) + formV2 || form_id == '21P-530V2' ? '21P-530V2' : self.class::FORM.upcase + else + self.class::FORM.upcase + end + end + def process_attachments! refs = attachment_keys.map { |key| Array(open_struct_form.send(key)) }.flatten files = PersistentAttachment.where(guid: refs.map(&:confirmationCode)) @@ -25,6 +36,14 @@ def email parsed_form['claimantEmail'] end + def form_matches_schema + return unless form_is_string + + JSON::Validator.fully_validate(VetsJsonSchema::SCHEMAS[form_id], parsed_form).each do |v| + errors.add(:form, v.to_s) + end + end + def business_line 'NCA' end diff --git a/app/sidekiq/structured_data/process_data_job.rb b/app/sidekiq/structured_data/process_data_job.rb index b10191b15b9..c6f2400bf05 100644 --- a/app/sidekiq/structured_data/process_data_job.rb +++ b/app/sidekiq/structured_data/process_data_job.rb @@ -25,8 +25,7 @@ def perform(saved_claim_id) ensure @claim.process_attachments! # upload claim and attachments to Central Mail - send_confirmation_email if @claim.form_id == '21P-530' - + send_confirmation_email if %w[21P-530 21P-530V2].include?(@claim.form_id) # veteran lookup for hit/miss metrics in support of Automation work StatsD.increment("#{stats_key}.success", tags: %W[relationship:#{relationship_type} veteranInMVI:#{veteran&.participant_id}]) diff --git a/config/features.yml b/config/features.yml index ae260e16ecd..1d75967a58b 100644 --- a/config/features.yml +++ b/config/features.yml @@ -1020,10 +1020,6 @@ features: actor_type: user description: Allows veterans to cancel VA appointments enable_in_development: true - va_online_scheduling_clinic_filtering: - actor_type: user - description: Allows clinic selection filtering by stop codes - enable_in_development: true va_online_scheduling_community_care: actor_type: user description: Allows veterans to submit requests for Community Care appointments @@ -1139,6 +1135,9 @@ features: va_view_dependents_access: actor_type: user description: Allows us to gate the View/ Modify dependents content in a progressive rollout + va_burial_v2: + actor_type: user + description: Allows us to toggle between 21-P530 and 21-P530V2 show_edu_benefits_1990EZ_Wizard: actor_type: user description: Navigates user to 1990EZ or 1990 depending on form questions. diff --git a/lib/bip_claims/service.rb b/lib/bip_claims/service.rb index f1d8b5eec13..204809fb1e1 100644 --- a/lib/bip_claims/service.rb +++ b/lib/bip_claims/service.rb @@ -14,7 +14,7 @@ class Service < Common::Client::Base def veteran_attributes(claim) case claim.form_id - when '21P-530' + when '21P-530', '21P-530V2' ssn, full_name, bday = claim.parsed_form.values_at( 'veteranSocialSecurityNumber', 'veteranFullName', diff --git a/lib/decision_review/utilities/pdf_validation/configuration.rb b/lib/decision_review/utilities/pdf_validation/configuration.rb index 80f93c24878..75961e382b0 100644 --- a/lib/decision_review/utilities/pdf_validation/configuration.rb +++ b/lib/decision_review/utilities/pdf_validation/configuration.rb @@ -4,7 +4,7 @@ module DecisionReview module PdfValidation class Configuration < DecisionReview::Configuration ## - # @return [String] Base path for decision review URLs. + # @return [String] Base path for PDF validation URL. # def base_path Settings.decision_review.pdf_validation.url @@ -17,6 +17,19 @@ def service_name 'DecisionReview::PDFValidation' end + ## + # @return [Hash] The basic headers required for any decision review API call. + # + def self.base_request_headers + # Can use regular Decision Reviews API key in lower environments + return super unless Rails.env.production? + + # Since we're using the `uploads/validate_document` endpoint under Benefits Intake API, + # we need to use their API key. This is pulled from BenefitsIntakeService::Configuration + api_key = Settings.benefits_intake_service.api_key || Settings.form526_backup.api_key + super.merge('apiKey' => api_key) + end + ## # Creates the a connection with parsing json and adding breakers functionality. # diff --git a/lib/evss/disability_compensation_form/form4142_processor.rb b/lib/evss/disability_compensation_form/form4142_processor.rb index 07cef2a5465..446f5d55349 100644 --- a/lib/evss/disability_compensation_form/form4142_processor.rb +++ b/lib/evss/disability_compensation_form/form4142_processor.rb @@ -2,6 +2,7 @@ require 'pdf_fill/forms/va21p527ez' require 'pdf_fill/forms/va21p530' +require 'pdf_fill/forms/va21p530v2' require 'pdf_fill/forms/va214142' require 'pdf_fill/forms/va210781a' require 'pdf_fill/forms/va210781' diff --git a/lib/pdf_fill/filler.rb b/lib/pdf_fill/filler.rb index 69122f4c6cc..2ce6719beca 100644 --- a/lib/pdf_fill/filler.rb +++ b/lib/pdf_fill/filler.rb @@ -2,6 +2,7 @@ require 'pdf_fill/forms/va21p527ez' require 'pdf_fill/forms/va21p530' +require 'pdf_fill/forms/va21p530v2' require 'pdf_fill/forms/va214142' require 'pdf_fill/forms/va210781a' require 'pdf_fill/forms/va210781' @@ -25,6 +26,7 @@ module Filler FORM_CLASSES = { '21P-527EZ' => PdfFill::Forms::Va21p527ez, '21P-530' => PdfFill::Forms::Va21p530, + '21P-530V2' => PdfFill::Forms::Va21p530v2, '21-4142' => PdfFill::Forms::Va214142, '21-0781a' => PdfFill::Forms::Va210781a, '21-0781' => PdfFill::Forms::Va210781, diff --git a/lib/pdf_fill/forms/pdfs/21P-530V2.pdf b/lib/pdf_fill/forms/pdfs/21P-530V2.pdf new file mode 100644 index 00000000000..87650f0b3d5 Binary files /dev/null and b/lib/pdf_fill/forms/pdfs/21P-530V2.pdf differ diff --git a/lib/pdf_fill/forms/va21p530v2.rb b/lib/pdf_fill/forms/va21p530v2.rb new file mode 100644 index 00000000000..b850d80a404 --- /dev/null +++ b/lib/pdf_fill/forms/va21p530v2.rb @@ -0,0 +1,732 @@ +# frozen_string_literal: true + +require 'pdf_fill/hash_converter' +require 'pdf_fill/forms/form_base' +require 'pdf_fill/forms/form_helper' +require 'string_helpers' + +# rubocop:disable Metrics/ClassLength +module PdfFill + module Forms + class Va21p530v2 < FormBase + include FormHelper + + ITERATOR = PdfFill::HashConverter::ITERATOR + + PLACE_OF_DEATH_KEY = { + 'vaMedicalCenter' => 'VA MEDICAL CENTER', + 'stateVeteransHome' => 'STATE VETERANS HOME', + 'nursingHome' => 'NURSING HOME UNDER VA CONTRACT' + }.freeze + + # rubocop:disable Layout/LineLength + KEY = { + 'veteranFullName' => { # start veteran information + 'first' => { + key: 'form1[0].#subform[82].VeteransFirstName[0]', + limit: 12, + question_num: 1, + question_text: "DECEASED VETERAN'S FIRST NAME" + }, + 'middle' => { + key: 'form1[0].#subform[82].VeteransMiddleInitial1[0]', + question_num: 1, + limit: 1, + question_text: "DECEASED VETERAN'S MIDDLE INITIAL" + }, + 'last' => { + key: 'form1[0].#subform[82].VeteransLastName[0]', + limit: 18, + question_num: 1, + question_text: "DECEASED VETERAN'S LAST NAME" + }, + 'suffix' => { + key: 'form1[0].#subform[82].Suffix[0]', + question_num: 1, + limit: 0, + question_text: "DECEASED VETERAN'S SUFFIX" + } + }, + 'veteranSocialSecurityNumber' => { + 'first' => { + key: 'form1[0].#subform[82].VeteransSocialSecurityNumber_FirstThreeNumbers[0]' + }, + 'second' => { + key: 'form1[0].#subform[82].VeteransSocialSecurityNumber_SecondTwoNumbers[0]' + }, + 'third' => { + key: 'form1[0].#subform[82].VeteransSocialSecurityNumber_LastFourNumbers[0]' + } + }, + 'vaFileNumber' => { + key: 'form1[0].#subform[82].VAFileNumber[0]', + question_num: 3 + }, + 'veteranDateOfBirth' => { + 'month' => { + key: 'form1[0].#subform[82].Veterans_DOBmonth[0]', + limit: 2, + question_num: 4, + question_suffix: 'A', + question_text: 'VETERAN/CLAIMANT\'S IDENTIFICATION INFORMATION > VETERAN\'S DATE OF BIRTH (MM-DD-YYYY)' + }, + 'day' => { + key: 'form1[0].#subform[82].Veterans_DOBday[0]', + limit: 2, + question_num: 4, + question_suffix: 'B', + question_text: 'VETERAN/CLAIMANT\'S IDENTIFICATION INFORMATION > VETERAN\'S DATE OF BIRTH (MM-DD-YYYY)' + }, + 'year' => { + key: 'form1[0].#subform[82].Veterans_DOByear[0]', + limit: 4, + question_num: 4, + question_suffix: 'C', + question_text: 'VETERAN/CLAIMANT\'S IDENTIFICATION INFORMATION > VETERAN\'S DATE OF BIRTH (MM-DD-YYYY)' + } + }, + 'deathDate' => { + 'month' => { + key: 'form1[0].#subform[82].Veterans_DateOfDeathmonth[0]', + limit: 2, + question_num: 5, + question_suffix: 'A', + question_text: 'VETERAN/CLAIMANT\'S IDENTIFICATION INFORMATION > VETERAN\'S DATE OF DEATH (MM-DD-YYYY)' + }, + 'day' => { + key: 'form1[0].#subform[82].Veterans_DateofDeathday[0]', + limit: 2, + question_num: 5, + question_suffix: 'B', + question_text: 'VETERAN/CLAIMANT\'S IDENTIFICATION INFORMATION > VETERAN\'S DATE OF DEATH (MM-DD-YYYY)' + }, + 'year' => { + key: 'form1[0].#subform[82].Veterans_DateofDeathyear[0]', + limit: 4, + question_num: 5, + question_suffix: 'C', + question_text: 'VETERAN/CLAIMANT\'S IDENTIFICATION INFORMATION > VETERAN\'S DATE OF DEATH (MM-DD-YYYY)' + } + }, + 'burialDate' => { + 'month' => { + key: 'form1[0].#subform[82].Veterans_Date_of_Burial_Month[0]', + limit: 2, + question_num: 6, + question_suffix: 'A', + question_text: 'VETERAN/CLAIMANT\'S IDENTIFICATION INFORMATION > VETERAN\'S DATE OF BURIAL (MM-DD-YYYY)' + }, + 'day' => { + key: 'form1[0].#subform[82].Veterans_Date_of_Burial_Day[0]', + limit: 2, + question_num: 6, + question_suffix: 'B', + question_text: 'VETERAN/CLAIMANT\'S IDENTIFICATION INFORMATION > VETERAN\'S DATE OF BURIAL (MM-DD-YYYY)' + }, + 'year' => { + key: 'form1[0].#subform[82].Veterans_Date_of_Burial_Year[0]', + limit: 4, + question_num: 6, + question_suffix: 'C', + question_text: 'VETERAN/CLAIMANT\'S IDENTIFICATION INFORMATION > VETERAN\'S DATE OF BURIAL (MM-DD-YYYY)' + } + }, # end veteran information + 'claimantFullName' => { # start claimant information + 'first' => { + key: 'form1[0].#subform[82].ClaimantsFirstName[0]', + limit: 12, + question_num: 7, + question_text: "CLAIMANT'S FIRST NAME" + }, + 'middle' => { + key: 'form1[0].#subform[82].ClaimantsMiddleInitial1[0]' + }, + 'last' => { + key: 'form1[0].#subform[82].ClaimantsLastName[0]', + limit: 18, + question_num: 7, + question_text: "CLAIMANT'S LAST NAME" + }, + 'suffix' => { + key: 'form1[0].#subform[82].ClaimantSuffix[0]', + question_num: 7, + limit: 0, + question_text: "CLAIMANT'S SUFFIX" + } + }, + 'claimantSocialSecurityNumber' => { + 'first' => { + key: 'form1[0].#subform[82].Claimants_SocialSecurityNumber_FirstThreeNumbers[0]' + }, + 'second' => { + key: 'form1[0].#subform[82].Claimants_SocialSecurityNumber_SecondTwoNumbers[0]' + }, + 'third' => { + key: 'form1[0].#subform[82].Claimants_SocialSecurityNumber_LastFourNumbers[0]' + } + }, + 'claimantDateOfBirth' => { + 'month' => { + key: 'form1[0].#subform[82].Claimants_DOBmonth[0]', + limit: 2, + question_num: 9, + question_suffix: 'A', + question_text: 'VETERAN/CLAIMANT\'S IDENTIFICATION INFORMATION > CLAIMANT\'S DATE OF BIRTH (MM-DD-YYYY)' + }, + 'day' => { + key: 'form1[0].#subform[82].Claimants_DOBday[0]', + limit: 2, + question_num: 9, + question_suffix: 'B', + question_text: 'VETERAN/CLAIMANT\'S IDENTIFICATION INFORMATION > CLAIMANT\'S DATE OF BIRTH (MM-DD-YYYY)' + }, + 'year' => { + key: 'form1[0].#subform[82].Claimants_DOByear[0]', + limit: 4, + question_num: 9, + question_suffix: 'C', + question_text: 'VETERAN/CLAIMANT\'S IDENTIFICATION INFORMATION > CLAIMANT\'S DATE OF BIRTH (MM-DD-YYYY)' + } + }, + 'claimantAddress' => { + 'street' => { + key: 'form1[0].#subform[82].CurrentMailingAddress_NumberAndStreet[0]', + limit: 30, + question_num: 10, + question_text: "CLAIMANT'S ADDRESS - STREET" + }, + 'street2' => { + key: 'form1[0].#subform[82].CurrentMailingAddress_ApartmentOrUnitNumber[0]', + limit: 5, + question_num: 10, + question_text: "CLAIMANT'S ADDRESS - APT/UNIT NO." + }, + 'city' => { + key: 'form1[0].#subform[82].CurrentMailingAddress_City[0]', + limit: 18, + question_num: 10, + question_text: "CLAIMANT'S ADDRESS - CITY" + }, + 'state' => { + key: 'form1[0].#subform[82].CurrentMailingAddress_StateOrProvince[0]', + limit: 2, + question_num: 10, + question_text: "CLAIMANT'S ADDRESS - STATE" + }, + 'country' => { + key: 'form1[0].#subform[82].CurrentMailingAddress_Country[0]', + limit: 2, + question_num: 10, + question_text: "CLAIMANT'S ADDRESS - COUNTRY" + }, + 'postalCode' => { + 'firstFive' => { + key: 'form1[0].#subform[82].CurrentMailingAddress_ZIPOrPostalCode_FirstFiveNumbers[0]', + limit: 5, + question_num: 10, + question_text: "CLAIMANT'S ADDRESS - POSTAL CODE - FIRST FIVE" + }, + 'lastFour' => { + key: 'form1[0].#subform[82].CurrentMailingAddress_ZIPOrPostalCode_LastFourNumbers[0]', + limit: 4, + question: 10, + question_text: "CLAIMANT's ADDRESS - POSTAL CODE - LAST FOUR" + } + } + }, + 'claimantPhone' => { + 'first' => { + key: 'form1[0].#subform[82].TelephoneNumber_AreaCode[0]' + }, + 'second' => { + key: 'form1[0].#subform[82].TelephoneNumber_FirstThreeNumbers[0]' + }, + 'third' => { + key: 'form1[0].#subform[82].TelephoneNumber_LastFourNumbers[0]' + } + }, + 'claimantIntPhone' => { + key: 'form1[0].#subform[82].IntTelephoneNumber[0]', + question_num: 11, + question_text: "CLAIMANT'S INTERNATIONAL PHONE NUMBER", + limit: 0 # this will force this value that is not on the pdf to appear in the overflow + }, + 'claimantEmail' => { + key: 'form1[0].#subform[82].E-Mail_Address[0]', + limit: 31, + question_num: 12, + question_text: 'E-MAIL ADDRESS' + }, + 'relationshipToVeteran' => { + 'spouse' => { + key: 'form1[0].#subform[82].CheckboxSpouse[0]' + }, + 'child' => { + key: 'form1[0].#subform[82].CheckboxChild[0]' + }, + 'parent' => { + key: 'form1[0].#subform[82].CheckboxParent[0]' + }, + 'executor' => { + key: 'form1[0].#subform[82].CheckboxExecutor[0]' + }, + 'funeralHome' => { + key: 'form1[0].#subform[82].CheckboxFuneralHome[0]' + }, + 'other' => { + key: 'form1[0].#subform[82].CheckboxOther[0]' + } + }, + 'toursOfDuty' => { + limit: 3, + first_key: 'rank', + 'dateRangeStart' => { + key: "form1[0].#subform[82].DATE_ENTERED_SERVICE[#{ITERATOR}]", + question_num: 14, + question_suffix: 'A', + question_text: 'ENTERED SERVICE (date)', + format: 'date' + }, + 'placeOfEntry' => { + key: "form1[0].#subform[82].PLACE[#{ITERATOR}]", + limit: 14, + question_num: 14, + question_suffix: 'A', + question_text: 'ENTERED SERVICE (place)' + }, + 'militaryServiceNumber' => { + key: "form1[0].#subform[82].SERVICE_NUMBER[#{ITERATOR}]", + limit: 12, + question_num: 14, + question_suffix: 'B', + question_text: 'SERVICE NUMBER' + }, + 'dateRangeEnd' => { + key: "form1[0].#subform[82].DATE_SEPARATED_SERVICE[#{ITERATOR}]", + question_num: 14, + question_suffix: 'C', + question_text: 'SEPARATED FROM SERVICE (date)', + format: 'date' + }, + 'placeOfSeparation' => { + key: "form1[0].#subform[82].PLACE_SEPARATED[#{ITERATOR}]", + question_num: 14, + question_suffix: 'C', + question_text: 'SEPARATED FROM SERVICE (place)', + limit: 15 + }, + 'rank' => { + key: "form1[0].#subform[82].GRADE_RANK_OR_RATING[#{ITERATOR}]", + question_num: 11, + question_suffix: 'D', + question_text: 'GRADE, RANK OR RATING, ORGANIZATION AND BRANCH OF SERVICE', + limit: 31 + } + }, + 'previousNames' => { + key: 'form1[0].#subform[82].OTHER_NAME_VETERAN_SERVED_UNDER[0]', + question_num: 15, + question_text: 'IF VETERAN SERVED UNDER NAME OTHER THAN THAT SHOWN IN ITEM 1, GIVE FULL NAME AND SERVICE RENDERED UNDER THAT NAME', + limit: 180 + }, + 'veteranSocialSecurityNumber2' => { + 'first' => { + key: 'form1[0].#subform[83].#subform[84].VeteransSocialSecurityNumber_FirstThreeNumbers[1]' + }, + 'second' => { + key: 'form1[0].#subform[83].#subform[84].VeteransSocialSecurityNumber_SecondTwoNumbers[1]' + }, + 'third' => { + key: 'form1[0].#subform[83].#subform[84].VeteransSocialSecurityNumber_LastFourNumbers[1]' + } + }, + 'finalRestingPlace' => { # break into yes/nos + 'location' => { + 'cemetery' => { + key: 'form1[0].#subform[83].#subform[84].RestingPlaceCemetery[5]' + }, + 'privateResidence' => { + key: 'form1[0].#subform[83].#subform[84].RestingPlacePrivateResidence[5]' + }, + 'mausoleum' => { + key: 'form1[0].#subform[83].#subform[84].RestingPlaceMausoleum[5]' + }, + 'other' => { + key: 'form1[0].#subform[83].#subform[84].RestingPlaceOther[5]' + } + }, + 'other' => { + limit: 58, + question_num: 16, + question_text: "PLACE OF BURIAL PLOT, INTERMENT SITE, OR FINAL RESTING PLACE OF DECEASED VETERAN'S REMAINS", + key: 'form1[0].#subform[83].#subform[84].PLACE_OF_DEATH[0]' + } + }, + 'hasNationalOrFederal' => { + key: 'form1[0].#subform[37].FederalCemeteryYES[0]' + }, + 'noNationalOrFederal' => { + key: 'form1[0].#subform[37].FederalCemeteryNo[0]' + }, + 'name' => { + key: 'form1[0].#subform[37].FederalCemeteryName[0]', + limit: 50 + }, + 'cemetaryLocationQuestionCemetery' => { + key: 'form1[0].#subform[37].HasStateCemetery[2]' + }, + 'cemetaryLocationQuestionTribal' => { + key: 'form1[0].#subform[37].HasTribalTrust[2]' + }, + 'cemetaryLocationQuestionNone' => { + key: 'form1[0].#subform[37].NoStateCemetery[2]' + }, + 'stateCemeteryOrTribalTrustName' => { + key: 'form1[0].#subform[37].StateCemeteryOrTribalTrustName[2]', + limit: 33 + }, + 'stateCemeteryOrTribalTrustZip' => { + key: 'form1[0].#subform[37].StateCemeteryOrTribalTrustZip[2]' + }, + 'hasGovtContributions' => { + key: 'form1[0].#subform[37].GovContributionYES[0]' + }, + 'noGovtContributions' => { + key: 'form1[0].#subform[37].GovContributionNo[0]' + }, + 'amountGovtContribution' => { + key: 'form1[0].#subform[37].AmountGovtContribution[0]', + question_num: 19, + question_suffix: 'B', + dollar: true, + question_text: 'AMOUNT OF GOVERNMENT OR EMPLOYER CONTRIBUTION', + limit: 5 + }, + 'burialAllowanceRequested' => { + 'checkbox' => { + 'nonService' => { + key: 'form1[0].#subform[83].Non-Service-Connected[0]' + }, + 'service' => { + key: 'form1[0].#subform[83].Service-Connected[0]' + }, + 'unclaimed' => { + key: 'form1[0].#subform[83].UnclaimedRemains[0]' + } + } + }, + 'locationOfDeath' => { + 'checkbox' => { + 'nursingHomeUnpaid' => { + key: 'form1[0].#subform[83].NursingHomeOrResidenceNotPaid[1]' + }, + 'nursingHomePaid' => { + key: 'form1[0].#subform[83].NursingHomeOrResidencePaid[1]' + }, + 'vaMedicalCenter' => { + key: 'form1[0].#subform[83].VaMedicalCenter[1]' + }, + 'stateVeteransHome' => { + key: 'form1[0].#subform[83].StateVeteransHome[1]' + }, + 'other' => { + key: 'form1[0].#subform[83].DeathOccurredOther[1]' + } + }, + 'other' => { + key: 'form1[0].#subform[37].DeathOccurredOtherSpecify[1]', + question_num: 20, + question_suffix: 'B', + question_text: "WHERE DID THE VETERAN'S DEATH OCCUR?", + limit: 32 + } + }, + 'hasPreviouslyReceivedAllowance' => { + key: 'form1[0].#subform[83].PreviousAllowanceYes[0]' + }, + 'noPreviouslyReceivedAllowance' => { + key: 'form1[0].#subform[83].PreviousAllowanceNo[0]' + }, + 'hasBurialExpenseResponsibility' => { + key: 'form1[0].#subform[83].ResponsibleForBurialCostYes[0]' + }, + 'noBurialExpenseResponsibility' => { + key: 'form1[0].#subform[83].ResponsibleForBurialCostNo[0]' + }, + 'hasConfirmation' => { + key: 'form1[0].#subform[83].certifyUnclaimedYes[0]' + }, + 'noConfirmation' => { + key: 'form1[0].#subform[83].certifyUnclaimedNo[0]' + }, + 'hasPlotExpenseResponsibility' => { + key: 'form1[0].#subform[83].ResponsibleForPlotIntermentCostYes[0]' + }, + 'noPlotExpenseResponsibility' => { + key: 'form1[0].#subform[83].ResponsibleForPlotIntermentCostNo[0]' + }, + 'hasTransportation' => { + key: 'form1[0].#subform[83].ResponsibleForTransportationYes[0]' + }, + 'noTransportation' => { + key: 'form1[0].#subform[83].ResponsibleForTransportationNo[0]' + }, + 'hasProcessOption' => { + key: 'form1[0].#subform[83].WantClaimFDCProcessedYes[0]' + }, + 'noProcessOption' => { + key: 'form1[0].#subform[83].WantClaimFDCProcessedNo[0]' + }, + 'signature' => { + key: 'form1[0].#subform[83].CLAIMANT_SIGNATURE[0]', + limit: 45, + question_num: 25, + question_text: 'SIGNATURE OF CLAIMANT', + question_suffix: 'A' + }, + 'claimantPrintedName' => { + key: 'form1[0].#subform[83].ClaimantPrintedName[0]', + limit: 45, + question_num: 25, + question_text: 'Printed Name of Claimant', + question_suffix: 'B' + }, + 'firmNameAndAddr' => { + key: 'form1[0].#subform[83].FirmNameAndAddress[0]', + limit: 90, + question_num: 26, + question_suffix: 'B', + question_text: 'FULL NAME AND ADDRESS OF THE FIRM, CORPORATION, OR STATE AGENCY FILING AS CLAIMANT' + }, + 'officialPosition' => { + key: 'form1[0].#subform[83].OfficialPosition[0]', + limit: 90, + question_num: 26, + question_suffix: 'B', + question_text: 'OFFICIAL POSITION OF PERSON SIGNING ON BEHALF OF FIRM, CORPORATION OR STATE AGENCY' + }, + 'veteranSocialSecurityNumber3' => { + 'first' => { + key: 'form1[0].#subform[83].#subform[84].VeteransSocialSecurityNumber_FirstThreeNumbers[2]' + }, + 'second' => { + key: 'form1[0].#subform[83].#subform[84].VeteransSocialSecurityNumber_SecondTwoNumbers[2]' + }, + 'third' => { + key: 'form1[0].#subform[83].#subform[84].VeteransSocialSecurityNumber_LastFourNumbers[2]' + } + } + }.freeze + # rubocop:enable Layout/LineLength + + def split_phone(hash, key) + phone = hash[key] + return if phone.blank? + + hash[key] = { + 'first' => phone[0..2], + 'second' => phone[3..5], + 'third' => phone[6..9] + } + end + + def split_postal_code(hash) + postal_code = hash['claimantAddress']['postalCode'] + return if postal_code.blank? + + hash['claimantAddress']['postalCode'] = { + 'firstFive' => postal_code[0..4], + 'lastFour' => postal_code[6..10] + } + end + + # override for how this pdf works, it needs the strings of yes/no + def expand_checkbox(value, key) + { + "has#{key}" => value == true ? 'YES' : nil, + "no#{key}" => value == false ? 'NO' : nil + } + end + + def expand_checkbox_in_place(hash, key) + hash.merge!(expand_checkbox(hash[key], StringHelpers.capitalize_only(key))) + end + + def expand_relationship(hash, key) + expand_checkbox_as_hash(hash[key], 'type') + end + + def expand_tours_of_duty(tours_of_duty) + return if tours_of_duty.blank? + + tours_of_duty.each do |tour_of_duty| + expand_date_range(tour_of_duty, 'dateRange') + tour_of_duty['rank'] = combine_hash(tour_of_duty, %w[serviceBranch rank], ', ') + tour_of_duty['militaryServiceNumber'] = @form_data['militaryServiceNumber'] + end + end + + def convert_location_of_death + location_of_death = @form_data['locationOfDeath'] + return if location_of_death.blank? + + location_of_death['location'] = 'nursingHomeUnpaid' if location_of_death['location'] == 'atHome' + expand_checkbox_as_hash(@form_data['locationOfDeath'], 'location') + end + + def expand_burial_allowance + burial_allowance = @form_data['burialAllowanceRequested'] + return if burial_allowance.blank? + + burial_allowance.each do |key, value| + burial_allowance[key] = value.present? ? 'On' : nil + end + + @form_data['burialAllowanceRequested'] = { + 'checkbox' => burial_allowance + } + end + + def expand_cemetery_location + cemetery_location = @form_data['cemeteryLocation'] + return if cemetery_location.blank? + + @form_data['stateCemeteryOrTribalTrustName'] = cemetery_location['name'] if cemetery_location['name'].present? + @form_data['stateCemeteryOrTribalTrustZip'] = cemetery_location['zip'] if cemetery_location['zip'].present? + end + + # VA file number can be up to 10 digits long; An optional leading 'c' or 'C' followed by + # 7-9 digits. The file number field on the 4142 form has space for 9 characters so trim the + # potential leading 'c' to ensure the file number will fit into the form without overflow. + def extract_va_file_number(va_file_number) + return va_file_number if va_file_number.blank? || va_file_number.length < 10 + + va_file_number.sub(/^[Cc]/, '') + end + + # override for on/off vs 1/off + def select_checkbox(value) + value ? 'On' : 'Off' + end + + # override + def expand_checkbox_as_hash(hash, key) + value = hash.try(:[], key) + return if value.blank? + + hash['checkbox'] = { + value => 'On' + } + end + + def expand_confirmation_question + if @form_data['confirmation'].present? + confirmation = @form_data['confirmation'] + @form_data['confirmation'] = confirmation['checkBox'] + expand_checkbox_in_place(@form_data, 'confirmation') + end + end + + def expand_location_question + cemetery_location = @form_data['cemetaryLocationQuestion'] + @form_data['cemetaryLocationQuestionCemetery'] = select_checkbox(cemetery_location == 'cemetery') + @form_data['cemetaryLocationQuestionTribal'] = select_checkbox(cemetery_location == 'tribal') + @form_data['cemetaryLocationQuestionNone'] = select_checkbox(cemetery_location == 'none') + end + + def combine_previous_names_and_service(previous_names) + return if previous_names.blank? + + previous_names.map do |previous_name| + "#{combine_full_name(previous_name)} (#{previous_name['serviceBranch']})" + end.join('; ') + end + + # rubocop:disable Metrics/MethodLength + def merge_fields(_options = {}) + expand_signature(@form_data['claimantFullName']) + + %w[veteranFullName claimantFullName].each do |attr| + extract_middle_i(@form_data, attr) + end + + %w[veteranDateOfBirth deathDate burialDate claimantDateOfBirth].each do |attr| + @form_data[attr] = split_date(@form_data[attr]) + end + + ssn = @form_data['veteranSocialSecurityNumber'] + ['', '2', '3'].each do |suffix| + @form_data["veteranSocialSecurityNumber#{suffix}"] = split_ssn(ssn) + end + + @form_data['claimantSocialSecurityNumber'] = split_ssn(@form_data['claimantSocialSecurityNumber']) + + relationship_to_veteran = @form_data['relationshipToVeteran'] + @form_data['relationshipToVeteran'] = { + 'spouse' => select_checkbox(relationship_to_veteran == 'spouse'), + 'child' => select_checkbox(relationship_to_veteran == 'child'), + 'executor' => select_checkbox(relationship_to_veteran == 'executor'), + 'parent' => select_checkbox(relationship_to_veteran == 'parent'), + 'funeralHome' => select_checkbox(relationship_to_veteran == 'funeralHome'), + 'other' => select_checkbox(relationship_to_veteran == 'other') + } + + final_resting_place = @form_data.dig('finalRestingPlace', 'location') + @form_data['finalRestingPlace']['location'] = { + 'cemetery' => select_checkbox(final_resting_place == 'cemetery'), + 'privateResidence' => select_checkbox(final_resting_place == 'privateResidence'), + 'mausoleum' => select_checkbox(final_resting_place == 'mausoleum'), + 'other' => select_checkbox(final_resting_place == 'other') + } + + expand_cemetery_location + + # special case: these fields were built as checkboxes instead of radios, so usual radio logic can't be used. + burial_expense_responsibility = @form_data['burialExpenseResponsibility'] + @form_data['hasBurialExpenseResponsibility'] = burial_expense_responsibility ? 'On' : nil + @form_data['noBurialExpenseResponsibility'] = burial_expense_responsibility ? nil : 'On' + + # special case: these fields were built as checkboxes instead of radios, so usual radio logic can't be used. + plot_expense_responsibility = @form_data['plotExpenseResponsibility'] + @form_data['hasPlotExpenseResponsibility'] = plot_expense_responsibility ? 'On' : nil + @form_data['noPlotExpenseResponsibility'] = plot_expense_responsibility ? nil : 'On' + + # special case: these fields were built as checkboxes instead of radios, so usual radio logic can't be used. + process_option = @form_data['processOption'] + @form_data['hasProcessOption'] = process_option ? 'On' : nil + @form_data['noProcessOption'] = process_option ? nil : 'On' + + expand_confirmation_question + expand_location_question + + split_phone(@form_data, 'claimantPhone') + + split_postal_code(@form_data) + + expand_tours_of_duty(@form_data['toursOfDuty']) + + @form_data['previousNames'] = combine_previous_names_and_service(@form_data['previousNames']) + + @form_data['vaFileNumber'] = extract_va_file_number(@form_data['vaFileNumber']) + + expand_burial_allowance + + convert_location_of_death + + %w[ + nationalOrFederal + govtContributions + previouslyReceivedAllowance + allowanceStatementOfTruth + transportation + ].each do |attr| + expand_checkbox_in_place(@form_data, attr) + end + + @form_data + end + # rubocop:enable Metrics/MethodLength + end + end +end +# rubocop:enable Metrics/ClassLength diff --git a/lib/statsd_middleware.rb b/lib/statsd_middleware.rb index 9b1f97c3be0..7d54281a381 100644 --- a/lib/statsd_middleware.rb +++ b/lib/statsd_middleware.rb @@ -61,6 +61,7 @@ class StatsdMiddleware avs burial-poc-v6 burials + burials-v2 check-in claims-status coe diff --git a/modules/claims_api/lib/bgs_service/local_bgs.rb b/modules/claims_api/lib/bgs_service/local_bgs.rb index 9e99d25c9cb..a86683900b3 100644 --- a/modules/claims_api/lib/bgs_service/local_bgs.rb +++ b/modules/claims_api/lib/bgs_service/local_bgs.rb @@ -33,8 +33,11 @@ def self.breakers_service url = Settings.bgs.url path = URI.parse(url).path host = URI.parse(url).host + port = URI.parse(url).port matcher = proc do |request_env| - request_env.url.host == host && request_env.url.path =~ /^#{path}/ + request_env.url.host == host && + request_env.url.port == port && + request_env.url.path =~ /^#{path}/ end Breakers::Service.new( diff --git a/modules/claims_api/spec/fixtures/test_client.p12 b/modules/claims_api/spec/fixtures/test_client.p12 index a253a22f3ed..48fde3d8efa 100644 Binary files a/modules/claims_api/spec/fixtures/test_client.p12 and b/modules/claims_api/spec/fixtures/test_client.p12 differ diff --git a/modules/simple_forms_api/app/services/simple_forms_api/pdf_stamper.rb b/modules/simple_forms_api/app/services/simple_forms_api/pdf_stamper.rb index 26999cb00c3..a0f8d61bcb7 100644 --- a/modules/simple_forms_api/app/services/simple_forms_api/pdf_stamper.rb +++ b/modules/simple_forms_api/app/services/simple_forms_api/pdf_stamper.rb @@ -26,7 +26,7 @@ def self.stamp_pdf(stamped_template_path, form, current_loa) end stamp_text = SUBMISSION_TEXT + current_time desired_stamps = [[10, 10, stamp_text]] - stamp(desired_stamps, stamped_template_path, auth_text, text_only: false) + verify(stamped_template_path) { stamp(desired_stamps, stamped_template_path, auth_text, text_only: false) } stamp_submission_date(stamped_template_path, form.submission_date_config) end @@ -34,7 +34,7 @@ def self.stamp_pdf(stamped_template_path, form, current_loa) def self.stamp107959f1(stamped_template_path, form) desired_stamps = [[26, 82.5, form.data['statement_of_truth_signature']]] append_to_stamp = false - stamp(desired_stamps, stamped_template_path, append_to_stamp) + verify(stamped_template_path) { stamp(desired_stamps, stamped_template_path, append_to_stamp) } end def self.stamp264555(stamped_template_path, form) @@ -55,7 +55,7 @@ def self.stamp214142(stamped_template_path, form) { type: :new_page } ] - multistamp(stamped_template_path, signature_text, page_configuration) + verified_multistamp(stamped_template_path, signature_text, page_configuration) # This is a one-off case where we need to stamp a date on the first page of 21-4142 when resubmitting if form.data['in_progress_form_created_at'] @@ -74,7 +74,7 @@ def self.stamp214142_date_stamp_for_resubmission(stamped_template_path, date_tit { type: :new_page } ] - multistamp(stamped_template_path, date_title, page_configuration, 12) + verified_multistamp(stamped_template_path, date_title, page_configuration, 12) page_configuration = [ { type: :text, position: date_text_stamp_position }, @@ -82,7 +82,7 @@ def self.stamp214142_date_stamp_for_resubmission(stamped_template_path, date_tit { type: :new_page } ] - multistamp(stamped_template_path, date_text, page_configuration, 12) + verified_multistamp(stamped_template_path, date_text, page_configuration, 12) end def self.stamp2110210(stamped_template_path, form) @@ -94,7 +94,7 @@ def self.stamp2110210(stamped_template_path, form) { type: :text, position: desired_stamps[0] } ] - multistamp(stamped_template_path, signature_text, page_configuration) + verified_multistamp(stamped_template_path, signature_text, page_configuration) end def self.stamp210845(stamped_template_path, form) @@ -106,7 +106,7 @@ def self.stamp210845(stamped_template_path, form) { type: :text, position: desired_stamps[0] } ] - multistamp(stamped_template_path, signature_text, page_configuration) + verified_multistamp(stamped_template_path, signature_text, page_configuration) end def self.stamp21p0847(stamped_template_path, form) @@ -117,7 +117,7 @@ def self.stamp21p0847(stamped_template_path, form) { type: :text, position: desired_stamps[0] } ] - multistamp(stamped_template_path, signature_text, page_configuration) + verified_multistamp(stamped_template_path, signature_text, page_configuration) end def self.stamp210972(stamped_template_path, form) @@ -129,7 +129,7 @@ def self.stamp210972(stamped_template_path, form) { type: :text, position: desired_stamps[0] } ] - multistamp(stamped_template_path, signature_text, page_configuration) + verified_multistamp(stamped_template_path, signature_text, page_configuration) end def self.stamp210966(stamped_template_path, form) @@ -140,7 +140,7 @@ def self.stamp210966(stamped_template_path, form) { type: :text, position: desired_stamps[0] } ] - multistamp(stamped_template_path, signature_text, page_configuration) + verified_multistamp(stamped_template_path, signature_text, page_configuration) end def self.stamp2010207(stamped_template_path, form) @@ -162,7 +162,7 @@ def self.stamp2010207(stamped_template_path, form) { type: :text, position: desired_stamps[0] } ] - multistamp(stamped_template_path, signature_text, page_configuration) + verified_multistamp(stamped_template_path, signature_text, page_configuration) end def self.stamp4010007_uuid(uuid) @@ -173,7 +173,7 @@ def self.stamp4010007_uuid(uuid) { type: :text, position: desired_stamps[0] } ] - multistamp(stamped_template_path, uuid, page_configuration, 7) + verified_multistamp(stamped_template_path, uuid, page_configuration, 7) end def self.multistamp(stamped_template_path, signature_text, page_configuration, font_size = 16) @@ -200,9 +200,8 @@ def self.multistamp(stamped_template_path, signature_text, page_configuration, f def self.stamp(desired_stamps, stamped_template_path, append_to_stamp, text_only: true) current_file_path = stamped_template_path desired_stamps.each do |x, y, text| - out_path = CentralMail::DatestampPdf.new(current_file_path, append_to_stamp:).run(text:, x:, y:, text_only:, - size: 9) - current_file_path = out_path + datestamp_instance = CentralMail::DatestampPdf.new(current_file_path, append_to_stamp:) + current_file_path = datestamp_instance.run(text:, x:, y:, text_only:, size: 9) end File.rename(current_file_path, stamped_template_path) end @@ -225,16 +224,32 @@ def self.stamp_submission_date(stamped_template_path, config) page_configuration = default_page_configuration page_configuration[config[:page_number]] = { type: :text, position: date_title_stamp_position } - multistamp(stamped_template_path, SUBMISSION_DATE_TITLE, page_configuration, 12) + verified_multistamp(stamped_template_path, SUBMISSION_DATE_TITLE, page_configuration, 12) page_configuration = default_page_configuration page_configuration[config[:page_number]] = { type: :text, position: date_text_stamp_position } - multistamp(stamped_template_path, Time.current.in_time_zone('UTC').strftime('%H:%M %Z %D'), page_configuration, - 12) + current_time = Time.current.in_time_zone('UTC').strftime('%H:%M %Z %D') + verified_multistamp(stamped_template_path, current_time, page_configuration, 12) end end + def self.verify(template_path) + orig_size = File.size(template_path) + yield + stamped_size = File.size(template_path) + + raise StandardError, 'The PDF remained unchanged upon stamping.' unless stamped_size > orig_size + rescue => e + raise StandardError, "An error occurred while verifying stamp: #{e}" + end + + def self.verified_multistamp(stamped_template_path, stamp_text, page_configuration, *) + raise StandardError, 'The provided stamp content was empty.' if stamp_text.blank? + + verify(stamped_template_path) { multistamp(stamped_template_path, stamp_text, page_configuration, *) } + end + def self.default_page_configuration [ { type: :new_page }, diff --git a/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21P_0847.json b/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21P_0847.json index d274b0ddb0f..805624bc616 100644 --- a/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21P_0847.json +++ b/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21P_0847.json @@ -30,5 +30,6 @@ "relationship_to_veteran": "other", "otherRelationship_to_veteran": "friend of a friend" }, - "additional_information": "Lots of \"extra\" stuff here" + "additional_information": "Lots of \"extra\" stuff here", + "statement_of_truth_signature": "John Veteran" } diff --git a/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21_0845.json b/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21_0845.json index 1f44b104b94..2e6769e34c6 100644 --- a/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21_0845.json +++ b/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21_0845.json @@ -44,5 +44,6 @@ "release_duration": "untilDate", "release_end_date": "2033-06-16", "security_question": "motherBirthplace", - "security_answer": "Las Vegas, NV" -} \ No newline at end of file + "security_answer": "Las Vegas, NV", + "statement_of_truth_signature": "John Veteran" +} diff --git a/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21_10210.json b/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21_10210.json index 2a60ad76f32..c8f7f3e707b 100644 --- a/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21_10210.json +++ b/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21_10210.json @@ -53,5 +53,6 @@ }, "witness_other_relationship_to_claimant": "Other text", "claimant_type": "non-veteran", - "claim_ownership": "third-party" -} \ No newline at end of file + "claim_ownership": "third-party", + "statement_of_truth_signature": "John Veteran" +} diff --git a/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21_4142.json b/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21_4142.json index 9ecd0ea59eb..fee3abb9e15 100644 --- a/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21_4142.json +++ b/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_21_4142.json @@ -138,5 +138,6 @@ "preparer_organization": "Top Org", "court_appointment_info": "Representing \"court stuff\" like...Representing court stuff like...Representing court stuff like...Representing court stuff like...Representing court stuff like...Representing court stuff like...", "relationship_to_veteran": "Veteran Service Officer" - } + }, + "statement_of_truth_signature": "John Veteran" } diff --git a/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_unhandled.json b/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_unhandled.json index 264454c7afb..84a9daaf2a1 100644 --- a/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_unhandled.json +++ b/modules/simple_forms_api/spec/fixtures/form_json/form_with_dangerous_characters_unhandled.json @@ -59,5 +59,6 @@ "postal_code": "54890" } }, - "remarks": "Lengthy \"remarks\" here \nabout what is needed\tand such" + "remarks": "Lengthy \"remarks\" here \nabout what is needed\tand such", + "statement_of_truth_signature": "John Veteran" } diff --git a/modules/simple_forms_api/spec/fixtures/form_json/vba_21_0845.json b/modules/simple_forms_api/spec/fixtures/form_json/vba_21_0845.json index f70216a3970..61d5e28265a 100644 --- a/modules/simple_forms_api/spec/fixtures/form_json/vba_21_0845.json +++ b/modules/simple_forms_api/spec/fixtures/form_json/vba_21_0845.json @@ -36,5 +36,6 @@ "release_duration": "untilDate", "release_end_date": "2033-06-16", "security_question": "motherBirthplace", - "security_answer": "Las Vegas, NV" + "security_answer": "Las Vegas, NV", + "statement_of_truth_signature": "John M Veteran" } diff --git a/modules/simple_forms_api/spec/fixtures/form_json/vba_21_10210-min.json b/modules/simple_forms_api/spec/fixtures/form_json/vba_21_10210-min.json index cac1eca116f..4ed3b48f7fa 100644 --- a/modules/simple_forms_api/spec/fixtures/form_json/vba_21_10210-min.json +++ b/modules/simple_forms_api/spec/fixtures/form_json/vba_21_10210-min.json @@ -16,5 +16,6 @@ "state": "CA", "postal_code": "12345" }, - "veteran_phone": "555-555-5557" -} \ No newline at end of file + "veteran_phone": "555-555-5557", + "statement_of_truth_signature": "Arthur C Preparer" +} diff --git a/modules/simple_forms_api/spec/fixtures/form_json/vba_21_10210.json b/modules/simple_forms_api/spec/fixtures/form_json/vba_21_10210.json index 58faea2d302..a81b640091f 100644 --- a/modules/simple_forms_api/spec/fixtures/form_json/vba_21_10210.json +++ b/modules/simple_forms_api/spec/fixtures/form_json/vba_21_10210.json @@ -53,5 +53,6 @@ }, "witness_other_relationship_to_claimant": "Other text", "claimant_type": "non-veteran", - "claim_ownership": "third-party" -} \ No newline at end of file + "claim_ownership": "third-party", + "statement_of_truth_signature": "Joe Center Claimant" +} diff --git a/modules/simple_forms_api/spec/fixtures/form_json/vba_21p_0847-min.json b/modules/simple_forms_api/spec/fixtures/form_json/vba_21p_0847-min.json index 83febd1e180..8f5651e8163 100644 --- a/modules/simple_forms_api/spec/fixtures/form_json/vba_21p_0847-min.json +++ b/modules/simple_forms_api/spec/fixtures/form_json/vba_21p_0847-min.json @@ -22,5 +22,6 @@ "veteran_ssn": "999442222", "relationship_to_deceased_claimant": { "relationship_to_veteran": "spouse" - } + }, + "statement_of_truth_signature": "Arthur C Preparer" } diff --git a/modules/simple_forms_api/spec/fixtures/form_json/vba_21p_0847.json b/modules/simple_forms_api/spec/fixtures/form_json/vba_21p_0847.json index ac311237602..281ef545f63 100644 --- a/modules/simple_forms_api/spec/fixtures/form_json/vba_21p_0847.json +++ b/modules/simple_forms_api/spec/fixtures/form_json/vba_21p_0847.json @@ -30,5 +30,6 @@ "relationship_to_veteran": "other", "otherRelationship_to_veteran": "friend of a friend" }, - "additional_information": "Lots of extra stuff here" + "additional_information": "Lots of extra stuff here", + "statement_of_truth_signature": "Arthur C Preparer" } diff --git a/modules/simple_forms_api/spec/services/pdf_filler_spec.rb b/modules/simple_forms_api/spec/services/pdf_filler_spec.rb index 9e1c40752b1..1cfb14fb5fe 100644 --- a/modules/simple_forms_api/spec/services/pdf_filler_spec.rb +++ b/modules/simple_forms_api/spec/services/pdf_filler_spec.rb @@ -5,18 +5,21 @@ describe SimpleFormsApi::PdfFiller do def self.test_pdf_fill(form_number, test_payload = form_number) - it 'fills out a PDF from a templated JSON file' do - expected_pdf_path = "tmp/#{form_number}-tmp.pdf" + form_name = form_number.split(Regexp.union(%w[vba_ vha_]))[1].gsub('_', '-') + context "when filling the pdf for form #{form_name} given template #{test_payload}" do + it 'fills out a PDF from a templated JSON file' do + expected_pdf_path = "tmp/#{form_number}-tmp.pdf" - # remove the pdf if it already exists - FileUtils.rm_f(expected_pdf_path) + # remove the pdf if it already exists + FileUtils.rm_f(expected_pdf_path) - # fill the PDF - data = JSON.parse(File.read("modules/simple_forms_api/spec/fixtures/form_json/#{test_payload}.json")) - form = "SimpleFormsApi::#{form_number.titleize.gsub(' ', '')}".constantize.new(data) - filler = SimpleFormsApi::PdfFiller.new(form_number:, form:) - filler.generate - expect(File.exist?(expected_pdf_path)).to eq(true) + # fill the PDF + data = JSON.parse(File.read("modules/simple_forms_api/spec/fixtures/form_json/#{test_payload}.json")) + form = "SimpleFormsApi::#{form_number.titleize.gsub(' ', '')}".constantize.new(data) + filler = SimpleFormsApi::PdfFiller.new(form_number:, form:) + filler.generate + expect(File.exist?(expected_pdf_path)).to eq(true) + end end end diff --git a/modules/simple_forms_api/spec/services/pdf_stamper_spec.rb b/modules/simple_forms_api/spec/services/pdf_stamper_spec.rb index f5fdfc6bfaf..09927ed66fb 100644 --- a/modules/simple_forms_api/spec/services/pdf_stamper_spec.rb +++ b/modules/simple_forms_api/spec/services/pdf_stamper_spec.rb @@ -4,25 +4,150 @@ require SimpleFormsApi::Engine.root.join('spec', 'spec_helper.rb') describe SimpleFormsApi::PdfStamper do - def self.test_pdf_stamp_error(stamp_method, test_payload) - it 'raises an error when generating stamped file' do + let(:data) { JSON.parse(File.read("modules/simple_forms_api/spec/fixtures/form_json/#{test_payload}.json")) } + let(:form) { "SimpleFormsApi::#{test_payload.titleize.gsub(' ', '')}".constantize.new(data) } + let(:path) { 'tmp/stuff.json' } + + describe 'form-specific stamp methods' do + subject(:stamp) { described_class.send(stamp_method, generated_form_path, form) } + + before do allow(Common::FileHelpers).to receive(:random_file_path).and_return('fake/stamp_path') allow(Common::FileHelpers).to receive(:delete_file_if_exists) - allow(Prawn::Document).to receive(:generate).and_raise('Error generating stamped file') + end + + %w[21-4142 21-10210 21p-0847].each do |form_number| + context "when generating a stamped file for form #{form_number}" do + let(:stamp_method) { "stamp#{form_number.gsub('-', '')}" } + let(:test_payload) { "vba_#{form_number.gsub('-', '_')}" } + let(:generated_form_path) { 'fake/generated_form_path' } + + it 'raises an error' do + expect { stamp }.to raise_error(StandardError, /An error occurred while verifying stamp/) + end + end + end + end + + describe '.stamp107959f1' do + subject(:stamp107959f1) { described_class.stamp107959f1(path, form) } + + before do + allow(described_class).to receive(:stamp).and_return(true) + allow(File).to receive(:size).and_return(1, 2) + end + + context 'when statement_of_truth_signature is provided' do + before { stamp107959f1 } + + let(:test_payload) { 'vha_10_7959f_1' } + let(:signature) { form.data['statement_of_truth_signature'] } + let(:stamps) { [[26, 82.5, signature]] } + + it 'calls stamp with correct desired_stamp' do + expect(described_class).to have_received(:stamp).with(stamps, path, false) + end + end + end + + describe '.stamp264555' do + subject(:stamp264555) { described_class.stamp264555(path, form) } + + before do + allow(described_class).to receive(:stamp).and_return(true) + allow(File).to receive(:size).and_return(1, 2) + end + + context 'when it is called with legitimate parameters' do + before { stamp264555 } + + let(:test_payload) { 'vba_26_4555' } + let(:stamps) { [] } + + it 'calls stamp correctly' do + expect(described_class).to have_received(:stamp).with(stamps, path, false) + end + end + end + + describe '.stamp210845' do + subject(:stamp210845) { described_class.stamp210845(path, form) } - generated_form_path = 'fake/generated_form_path' - data = JSON.parse(File.read("modules/simple_forms_api/spec/fixtures/form_json/#{test_payload}.json")) - form = "SimpleFormsApi::#{test_payload.titleize.gsub(' ', '')}".constantize.new(data) + before do + allow(described_class).to receive(:multistamp).and_return(true) + allow(File).to receive(:size).and_return(1, 2) + end + + context 'when it is called with legitimate parameters' do + before { stamp210845 } - expect do - SimpleFormsApi::PdfStamper.send(stamp_method, generated_form_path, form) - end.to raise_error(RuntimeError, 'Error generating stamped file') + let(:test_payload) { 'vba_21_0845' } + let(:signature) { form.data['statement_of_truth_signature'] } + let(:page_config) do + [ + { type: :new_page }, + { type: :new_page }, + { type: :text, position: [50, 240] } + ] + end - expect(Common::FileHelpers).to have_received(:delete_file_if_exists).with('fake/stamp_path') + it 'calls multistamp correctly' do + expect(described_class).to have_received(:multistamp).with(path, signature, page_config) + end end end - test_pdf_stamp_error 'stamp214142', 'vba_21_4142' - test_pdf_stamp_error 'stamp2110210', 'vba_21_10210' - test_pdf_stamp_error 'stamp21p0847', 'vba_21p_0847' + describe '.verify' do + subject(:verify) { described_class.verify('template_path') { double } } + + before { allow(File).to receive(:size).and_return(orig_size, stamped_size) } + + describe 'when verifying a stamp' do + let(:orig_size) { 10_000 } + + context 'when the stamped file size is larger than the original' do + let(:stamped_size) { orig_size + 1 } + + it 'succeeds' do + expect { verify }.not_to raise_error + end + end + + context 'when the stamped file size is the same as the original' do + let(:stamped_size) { orig_size } + + it 'raises an error message' do + expect { verify }.to raise_error( + 'An error occurred while verifying stamp: The PDF remained unchanged upon stamping.' + ) + end + end + + context 'when the stamped file size is less than the original' do + let(:stamped_size) { orig_size - 1 } + + it 'raises an error message' do + expect { verify }.to raise_error( + 'An error occurred while verifying stamp: The PDF remained unchanged upon stamping.' + ) + end + end + end + end + + describe '.verified_multistamp' do + subject(:verified_multistamp) { described_class.verified_multistamp(path, signature_text, config) } + + before { allow(described_class).to receive(:verify).and_return(true) } + + context 'when signature_text is blank' do + let(:path) { nil } + let(:signature_text) { nil } + let(:config) { nil } + + it 'raises an error' do + expect { verified_multistamp }.to raise_error('The provided stamp content was empty.') + end + end + end end diff --git a/modules/vaos/app/services/vaos/v2/systems_service.rb b/modules/vaos/app/services/vaos/v2/systems_service.rb index 7b8da35423a..32503b2e1a1 100644 --- a/modules/vaos/app/services/vaos/v2/systems_service.rb +++ b/modules/vaos/app/services/vaos/v2/systems_service.rb @@ -3,8 +3,6 @@ module VAOS module V2 class SystemsService < VAOS::SessionService - STOP_CODE_FILTERS = :va_online_scheduling_clinic_filtering - def get_facility_clinics(location_id:, clinical_service: nil, clinic_ids: nil, @@ -21,11 +19,9 @@ def get_facility_clinics(location_id:, 'pageNumber' => page_number }.compact - # 'clinicalService' is used to retrieve clinics for appointment scheduling, + # 'clinicalService' is used when retrieving clinics for appointment scheduling, # triggering stop code filtering to avoid displaying unavailable clinics. - if url_params['clinicalService'].present? && Flipper.enabled?(STOP_CODE_FILTERS, user) - url_params.merge!('enableStopCodeFilter' => true) - end + url_params.merge!('enableStopCodeFilter' => true) if url_params['clinicalService'].present? response = perform(:get, url, url_params, headers) response.body[:data].map { |clinic| OpenStruct.new(clinic) } diff --git a/modules/vba_documents/app/sidekiq/vba_documents/upload_processor.rb b/modules/vba_documents/app/sidekiq/vba_documents/upload_processor.rb index 43a35c36ffb..6909fec42f0 100644 --- a/modules/vba_documents/app/sidekiq/vba_documents/upload_processor.rb +++ b/modules/vba_documents/app/sidekiq/vba_documents/upload_processor.rb @@ -21,8 +21,6 @@ class UploadProcessor sidekiq_options unique_for: 30.days def perform(guid, caller_data, retries = 0) - return if cancelled? - response = nil brt = Benchmark.realtime do # @retries variable used via the CentralMail::Utilities which is included via VBADocuments::UploadValidations @@ -46,20 +44,6 @@ def perform(guid, caller_data, retries = 0) response&.success? ? true : false end - def cancelled? - Sidekiq.redis do |c| - if c.respond_to? :exists? - c.exists?("cancelled-#{jid}") - else - c.exists("cancelled-#{jid}") - end - end - end - - def self.cancel!(jid) - Sidekiq.redis { |c| c.setex("cancelled-#{jid}", 86_400, 1) } - end - private # rubocop:disable Metrics/MethodLength diff --git a/modules/vba_documents/spec/request/v1/uploads_request_spec.rb b/modules/vba_documents/spec/request/v1/uploads_request_spec.rb index aec54da50f3..7553ff67406 100644 --- a/modules/vba_documents/spec/request/v1/uploads_request_spec.rb +++ b/modules/vba_documents/spec/request/v1/uploads_request_spec.rb @@ -135,7 +135,6 @@ @md = JSON.parse(valid_metadata) @upload_submission = VBADocuments::UploadSubmission.new @upload_submission.update(status: 'uploaded') - allow_any_instance_of(VBADocuments::UploadProcessor).to receive(:cancelled?).and_return(false) allow_any_instance_of(Tempfile).to receive(:size).and_return(1) # must be > 0 or submission will error w/DOC107 allow(VBADocuments::MultipartParser).to receive(:parse) { { 'metadata' => @md.to_json, 'content' => valid_doc } diff --git a/modules/vba_documents/spec/request/v2/uploads_request_spec.rb b/modules/vba_documents/spec/request/v2/uploads_request_spec.rb index 4a4e2646cb9..5d74d97bdbd 100644 --- a/modules/vba_documents/spec/request/v2/uploads_request_spec.rb +++ b/modules/vba_documents/spec/request/v2/uploads_request_spec.rb @@ -202,7 +202,6 @@ @md = JSON.parse(valid_metadata) @upload_submission = VBADocuments::UploadSubmission.new @upload_submission.update(status: 'uploaded') - allow_any_instance_of(VBADocuments::UploadProcessor).to receive(:cancelled?).and_return(false) allow_any_instance_of(Tempfile).to receive(:size).and_return(1) # must be > 0 or submission will error w/DOC107 allow(VBADocuments::MultipartParser).to receive(:parse) { { 'metadata' => @md.to_json, 'content' => valid_doc } diff --git a/modules/vba_documents/spec/sidekiq/upload_processor_spec.rb b/modules/vba_documents/spec/sidekiq/upload_processor_spec.rb index c032d7aa89c..5c6294272eb 100644 --- a/modules/vba_documents/spec/sidekiq/upload_processor_spec.rb +++ b/modules/vba_documents/spec/sidekiq/upload_processor_spec.rb @@ -72,7 +72,6 @@ end before do - allow_any_instance_of(described_class).to receive(:cancelled?).and_return(false) allow_any_instance_of(Tempfile).to receive(:size).and_return(1) # must be > 0 or submission will error w/DOC107 objstore = instance_double(VBADocuments::ObjectStore) version = instance_double(Aws::S3::ObjectVersion) diff --git a/spec/factories/burial_claim.rb b/spec/factories/burial_claim.rb index 8d8705b75aa..23c7189318d 100644 --- a/spec/factories/burial_claim.rb +++ b/spec/factories/burial_claim.rb @@ -33,6 +33,39 @@ end end + factory :burial_claim_v2, class: 'SavedClaim::Burial' do + form_id { '21P-530V2' } + form do + { + privacyAgreementAccepted: true, + veteranFullName: { + first: 'WESLEY', + last: 'FORD' + }, + claimantEmail: 'foo@foo.com', + deathDate: '1989-12-13', + veteranDateOfBirth: '1986-05-06', + veteranSocialSecurityNumber: '796043735', + claimantAddress: { + country: 'USA', + state: 'CA', + postalCode: '90210', + street: '123 Main St', + city: 'Anytown' + }, + claimantFullName: { + first: 'Derrick', + middle: 'A', + last: 'Stewart' + }, + burialAllowance: true, + plotAllowance: true, + transportation: true, + formV2: true + }.to_json + end + end + # Bad names that fail the EMMS API regex factory :burial_claim_bad_names, class: 'SavedClaim::Burial' do form_id { '21P-530' } diff --git a/spec/fixtures/pdf_fill/21P-530V2/kitchen_sink.json b/spec/fixtures/pdf_fill/21P-530V2/kitchen_sink.json new file mode 100644 index 00000000000..f4bc77bd629 --- /dev/null +++ b/spec/fixtures/pdf_fill/21P-530V2/kitchen_sink.json @@ -0,0 +1,56 @@ +{ + "transportationExpenses":true, + "plotExpenseResponsibility":true, + "govtContributions":true, + "amountGovtContribution":"50000", + "cemeteryLocation":{"name":"state cemetery", "zip":"04102"}, + "cemetaryLocationQuestion":"cemetery", + "nationalOrFederal":true, + "name":"name of cemetery", + "finalRestingPlace":{"location":"other", "other":"other"}, + "previouslyReceivedAllowance":true, + "burialExpenseResponsibility":true, + "confirmation":{"checkBox":true}, + "burialAllowanceRequested":{"service":true, "nonService":true, "unclaimed":true}, + "burialAllowance":true, + "plotAllowance":true, + "processOption":false, + "transportation":true, + "previousNames":[{"first":"previous", "middle":"other", "last":"name", "suffix":"Jr.", "serviceBranch":"navy"}], + "militaryServiceNumber":"123123", + "toursOfDuty": + [{"serviceBranch":"Air Force", + "dateRange":{"from":"1970-01-01", "to":"1974-01-01"}, + "placeOfEntry":"placeofentry12", + "placeOfSeparation":"placeofentry12", + "rank":"gradeofentry12", + "unit":"unit"}, + {"serviceBranch":"Army", + "dateRange":{"from":"1976-01-01", "to":"1978-01-02"}, + "placeOfEntry":"placeofentry34", + "placeOfSeparation":"placeofentry34", + "rank":"gradeofentry13", + "unit":"unit"}, + {"serviceBranch":"Navy", + "dateRange":{"from":"1980-01-01", "to":"1984-01-01"}, + "placeOfEntry":"placeofentry56", + "placeOfSeparation":"placeofentry56", + "rank":"gradeofentry56", + "unit":"unit"}], + "locationOfDeath":{"location":"other", "other":"other place"}, + "deathDate":"2024-01-01", + "burialDate":"2024-01-02", + "veteranFullName":{"first":"veteran", "middle":"middle", "last":"lastname", "suffix":"Jr."}, + "veteranSocialSecurityNumber":"987654322", + "vaFileNumber":"987654322", + "veteranDateOfBirth":"1950-01-01", + "claimantEmail":"test@test.com", + "claimantPhone":"5555555555", + "claimantIntPhone":"5555555556", + "claimantAddress":{"country":"US", "street":"123 fake street", "street2":"street address line 2", "city":"portland", "state":"ME", "postalCode":"04102"}, + "claimantFullName":{"first":"test", "middle":"middle", "last":"spouse", "suffix":"Jr."}, + "claimantSocialSecurityNumber":"987654321", + "claimantDateOfBirth":"1960-01-01", + "formV2":true, + "relationshipToVeteran":"spouse", + "privacyAgreementAccepted":true} \ No newline at end of file diff --git a/spec/fixtures/pdf_fill/21P-530V2/merge_fields.json b/spec/fixtures/pdf_fill/21P-530V2/merge_fields.json new file mode 100644 index 00000000000..108b49025f0 --- /dev/null +++ b/spec/fixtures/pdf_fill/21P-530V2/merge_fields.json @@ -0,0 +1,102 @@ +{ + "transportationExpenses":true, + "plotExpenseResponsibility":true, + "govtContributions":true, + "amountGovtContribution":"50000", + "cemeteryLocation":{"name":"state cemetery", "zip":"04102"}, + "cemetaryLocationQuestion":"cemetery", + "nationalOrFederal":true, + "name":"name of cemetery", + "finalRestingPlace":{"location":{"cemetery":"Off", "privateResidence":"Off", "mausoleum":"Off", "other":"On"}, "other":"other"}, + "previouslyReceivedAllowance":true, + "burialExpenseResponsibility":true, + "confirmation":true, + "burialAllowanceRequested":{"checkbox":{"service":"On", "nonService":"On", "unclaimed":"On"}}, + "burialAllowance":true, + "plotAllowance":true, + "processOption":false, + "transportation":true, + "previousNames":"previous other name Jr. (navy)", + "militaryServiceNumber":"123123", + "toursOfDuty":[ + { + "serviceBranch":"Air Force", + "placeOfEntry":"placeofentry12", + "placeOfSeparation":"placeofentry12", + "rank":"Air Force, gradeofentry12", + "unit":"unit", + "dateRangeStart":"1970-01-01", + "dateRangeEnd":"1974-01-01", + "militaryServiceNumber":"123123" + }, + { + "serviceBranch":"Army", + "placeOfEntry":"placeofentry34", + "placeOfSeparation":"placeofentry34", + "rank":"Army, gradeofentry13", + "unit":"unit", + "dateRangeStart":"1976-01-01", + "dateRangeEnd":"1978-01-02", + "militaryServiceNumber":"123123" + }, + { + "serviceBranch":"Navy", + "placeOfEntry":"placeofentry56", + "placeOfSeparation":"placeofentry56", + "rank":"Navy, gradeofentry56", + "unit":"unit", + "dateRangeStart":"1980-01-01", + "dateRangeEnd":"1984-01-01", + "militaryServiceNumber":"123123" + } + ], + "locationOfDeath": + { + "location":"other", + "other":"other place", + "checkbox":{"other":"On"} + }, + "deathDate":{"month":"01", "day":"01", "year":"2024"}, + "burialDate":{"month":"01", "day":"02", "year":"2024"}, + "veteranFullName":{"first":"veteran", "middle":"middle", "last":"lastname", "suffix":"Jr.", "middleInitial":"m"}, + "veteranSocialSecurityNumber":{"first":"987", "second":"65", "third":"4322"}, + "vaFileNumber":"987654322", + "veteranDateOfBirth":{"month":"01", "day":"01", "year":"1950"}, + "claimantEmail":"test@test.com", + "claimantPhone":{"first":"555", "second":"555", "third":"5555"}, + "claimantIntPhone":"5555555556", + "claimantAddress":{"country":"US", "street":"123 fake street", "street2":"street address line 2", "city":"portland", "state":"ME", "postalCode":{"firstFive":"04102", "lastFour":null}}, + "claimantFullName":{"first":"test", "middle":"middle", "last":"spouse", "suffix":"Jr.", "middleInitial":"m"}, + "claimantSocialSecurityNumber":{"first":"987", "second":"65", "third":"4321"}, + "claimantDateOfBirth":{"month":"01", "day":"01", "year":"1960"}, + "formV2":true, + "relationshipToVeteran":{"spouse":"On", "child":"Off", "executor":"Off", "parent":"Off", "funeralHome":"Off", "other":"Off"}, + "privacyAgreementAccepted":true, + "signature":"test spouse", + "signatureDate":"2024-03-21", + "veteranSocialSecurityNumber2":{"first":"987", "second":"65", "third":"4322"}, + "veteranSocialSecurityNumber3":{"first":"987", "second":"65", "third":"4322"}, + "stateCemeteryOrTribalTrustName":"state cemetery", + "stateCemeteryOrTribalTrustZip":"04102", + "hasBurialExpenseResponsibility":"On", + "noBurialExpenseResponsibility":null, + "hasPlotExpenseResponsibility":"On", + "noPlotExpenseResponsibility":null, + "hasProcessOption":null, + "noProcessOption":"On", + "hasConfirmation":"YES", + "noConfirmation":null, + "cemetaryLocationQuestionCemetery":"On", + "cemetaryLocationQuestionTribal":"Off", + "cemetaryLocationQuestionNone":"Off", + "hasNationalOrFederal":"YES", + "noNationalOrFederal":null, + "hasGovtContributions":"YES", + "noGovtContributions":null, + "hasPreviouslyReceivedAllowance":"YES", + "noPreviouslyReceivedAllowance":null, + "hasAllowanceStatementOfTruth":null, + "noAllowanceStatementOfTruth":null, + "hasTransportation":"YES", + "noTransportation":null +} \ No newline at end of file diff --git a/spec/lib/pdf_fill/forms/va21p530v2_spec.rb b/spec/lib/pdf_fill/forms/va21p530v2_spec.rb new file mode 100644 index 00000000000..36b6947982a --- /dev/null +++ b/spec/lib/pdf_fill/forms/va21p530v2_spec.rb @@ -0,0 +1,115 @@ +# frozen_string_literal: true + +require 'rails_helper' +require 'pdf_fill/forms/va21p530v2' + +def basic_class + PdfFill::Forms::Va21p530v2.new({}) +end + +describe PdfFill::Forms::Va21p530v2 do + let(:form_data) do + {} + end + + let(:new_form_class) do + described_class.new(form_data) + end + + def class_form_data + new_form_class.instance_variable_get(:@form_data) + end + + test_method( + basic_class, + 'expand_checkbox', + [ + [ + [ + true, 'GovtContribution' + ], + { 'hasGovtContribution' => 'YES', 'noGovtContribution' => nil } + ], + [ + [ + false, 'GovtContribution' + ], + { 'hasGovtContribution' => nil, 'noGovtContribution' => 'NO' } + ], + [ + [ + nil, 'GovtContribution' + ], + { 'hasGovtContribution' => nil, 'noGovtContribution' => nil } + ] + ] + ) + + test_method( + basic_class, + 'split_phone', + [ + [ + [{}, nil], + nil + ], + [ + [ + { phone: '1112223333' }, + :phone + ], + { 'first' => '111', 'second' => '222', 'third' => '3333' } + ] + ] + ) + + describe '#convert_location_of_death' do + subject do + new_form_class.convert_location_of_death + end + + context 'with no location of death' do + it 'returns nil' do + expect(subject).to eq(nil) + end + end + + context 'with a regular location of death' do + let(:form_data) do + { + 'locationOfDeath' => { + 'location' => 'nursingHomeUnpaid' + } + } + end + + it 'returns the directly mapped location' do + subject + expect(class_form_data['locationOfDeath']['checkbox']).to eq({ 'nursingHomeUnpaid' => 'On' }) + end + end + + context 'with a location needed for translation' do + let(:form_data) do + { + 'locationOfDeath' => { + 'location' => 'atHome' + } + } + end + + it 'returns the directly mapped location' do + subject + expect(class_form_data['locationOfDeath']['checkbox']).to eq({ 'nursingHomeUnpaid' => 'On' }) + end + end + end + + describe '#merge_fields' do + it 'merges the right fields', run_at: '2024-03-21 00:00:00 EDT' do + expect(described_class.new(get_fixture('pdf_fill/21P-530V2/kitchen_sink')).merge_fields.to_json).to eq( + get_fixture('pdf_fill/21P-530V2/merge_fields').to_json + ) + end + end +end diff --git a/spec/models/saved_claim/burial_spec.rb b/spec/models/saved_claim/burial_spec.rb index a606364789a..026298c434e 100644 --- a/spec/models/saved_claim/burial_spec.rb +++ b/spec/models/saved_claim/burial_spec.rb @@ -6,6 +6,7 @@ subject { described_class.new } let(:instance) { FactoryBot.build(:burial_claim) } + let(:instance_v2) { FactoryBot.build(:burial_claim_v2) } it 'responds to #confirmation_number' do expect(subject.confirmation_number).to eq(subject.guid) @@ -27,7 +28,11 @@ end end - context 'a record' do + context 'a record is processed through v1' do + before do + Flipper.disable(:va_burial_v2) + end + it 'inherits init callsbacks from saved_claim' do expect(subject.form_id).to eq(described_class::FORM) expect(subject.guid).not_to be_nil @@ -53,6 +58,38 @@ end end + context 'a record is processed through v2' do + before do + Flipper.enable(:va_burial_v2) + end + + let(:subject_v2) { described_class.new(formV2: true) } + + it 'inherits init callsbacks from saved_claim' do + expect(subject_v2.form_id).to eq('21P-530V2') + expect(subject_v2.guid).not_to be_nil + expect(subject_v2.type).to eq(described_class.to_s) + end + + context 'validates against the form schema' do + before do + expect(instance_v2.valid?).to be(true) + expect(JSON::Validator).to receive(:fully_validate).once.and_call_original + end + + # NOTE: We assume all forms have the privacyAgreementAccepted element. Obviously. + it 'rejects forms with missing elements' do + bad_form = instance_v2.parsed_form.deep_dup + bad_form.delete('privacyAgreementAccepted') + instance_v2.form = bad_form.to_json + instance_v2.remove_instance_variable(:@parsed_form) + expect(instance_v2.valid?).to be(false) + expect(instance_v2.errors.full_messages.size).to eq(1) + expect(instance_v2.errors.full_messages).to include(/privacyAgreementAccepted/) + end + end + end + describe '#email' do it 'returns the users email' do expect(instance.email).to eq('foo@foo.com')