Class: HealthCareApplication

Inherits:
ApplicationRecord show all
Includes:
SentryLogging, VA1010Forms::Utils
Defined in:
app/models/health_care_application.rb

Constant Summary collapse

FORM_ID =
'10-10EZ'
ACTIVEDUTY_ELIGIBILITY =
'TRICARE'
DISABILITY_THRESHOLD =
50
LOCKBOX =
Lockbox.new(key: Settings.lockbox.master_key, encode: true)
EE_DATA_SELECTED_KEYS =
%i[
  application_date
  enrollment_date
  preferred_facility
  effective_date
  primary_eligibility
  priority_group
  can_submit_financial_info
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from VA1010Forms::Utils

#es_submit, #log_payload_info, #override_parsed_form, #soap, #submission_body

Methods included from SentryLogging

#log_exception_to_sentry, #log_message_to_sentry, #non_nil_hash?, #normalize_level, #rails_logger, #set_sentry_metadata

Methods inherited from ApplicationRecord

descendants_using_encryption, lockbox_options, #timestamp_attributes_for_update_in_model, #valid?

Instance Attribute Details

#async_compatibleObject

Returns the value of attribute async_compatible.



19
20
21
# File 'app/models/health_care_application.rb', line 19

def async_compatible
  @async_compatible
end

#formObject

Returns the value of attribute form.



19
20
21
# File 'app/models/health_care_application.rb', line 19

def form
  @form
end

#google_analytics_client_idObject

Returns the value of attribute google_analytics_client_id.



19
20
21
# File 'app/models/health_care_application.rb', line 19

def google_analytics_client_id
  @google_analytics_client_id
end

#userObject

Returns the value of attribute user.



19
20
21
# File 'app/models/health_care_application.rb', line 19

def user
  @user
end

Class Method Details

.determine_active_duty(primary_eligibility, veteran) ⇒ Object



117
118
119
# File 'app/models/health_care_application.rb', line 117

def self.determine_active_duty(primary_eligibility, veteran)
  primary_eligibility == ACTIVEDUTY_ELIGIBILITY && veteran == 'false'
end

.determine_non_military(primary_eligibility, veteran, parsed_status) ⇒ Object



121
122
123
124
125
126
127
128
# File 'app/models/health_care_application.rb', line 121

def self.determine_non_military(primary_eligibility, veteran, parsed_status)
  if parsed_status == HCA::EnrollmentEligibility::Constants::ACTIVEDUTY &&
     !determine_active_duty(primary_eligibility, veteran)
    HCA::EnrollmentEligibility::Constants::NON_MILITARY
  else
    parsed_status
  end
end

.enrollment_status(icn, loa3) ⇒ Object



161
162
163
164
165
166
# File 'app/models/health_care_application.rb', line 161

def self.enrollment_status(icn, loa3)
  parsed_ee_data(
    HCA::EnrollmentEligibility::Service.new.lookup_user(icn),
    loa3
  )
end

.get_user_identifier(user) ⇒ Hash

Parameters:

Returns:

  • (Hash)


34
35
36
37
38
39
40
41
# File 'app/models/health_care_application.rb', line 34

def self.get_user_identifier(user)
  return if user.nil?

  {
    'icn' => user.icn,
    'edipi' => user.edipi
  }
end

.parsed_ee_data(ee_data, loa3) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'app/models/health_care_application.rb', line 140

def self.parsed_ee_data(ee_data, loa3)
  if loa3
    parsed_status = HCA::EnrollmentEligibility::StatusMatcher.parse(
      ee_data[:enrollment_status], ee_data[:ineligibility_reason]
    )

    parsed_status = determine_non_military(
      ee_data[:primary_eligibility], ee_data[:veteran],
      parsed_status
    )

    ee_data.slice(*EE_DATA_SELECTED_KEYS).merge(parsed_status:)
  else
    { parsed_status: if ee_data[:enrollment_status].present?
                       HCA::EnrollmentEligibility::Constants::LOGIN_REQUIRED
                     else
                       HCA::EnrollmentEligibility::Constants::NONE_OF_THE_ABOVE
                     end }
  end
end

.user_attributes(form) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'app/models/health_care_application.rb', line 176

def self.user_attributes(form)
  form ||= {}
  full_name = form['veteranFullName'] || {}

  return_val = HCA::UserAttributes.new(
    first_name: full_name['first'],
    middle_name: full_name['middle'],
    last_name: full_name['last'],
    birth_date: form['veteranDateOfBirth'],
    ssn: form['veteranSocialSecurityNumber'],
    gender: form['gender']
  )

  raise Common::Exceptions::ValidationErrors, return_val unless return_val.valid?

  return_val
end

.user_icn(user_attributes) ⇒ Object



168
169
170
171
172
173
174
# File 'app/models/health_care_application.rb', line 168

def self.user_icn(user_attributes)
  HCA::RateLimitedSearch.create_rate_limited_searches(user_attributes) unless Settings.mvi_hca.skip_rate_limit
  MPI::Service.new.find_profile_by_attributes(first_name: user_attributes.first_name,
                                              last_name: user_attributes.last_name,
                                              birth_date: user_attributes.birth_date,
                                              ssn: user_attributes.ssn)&.profile&.icn
end

Instance Method Details

#async_submission_failed?Boolean

Returns:

  • (Boolean)


55
56
57
# File 'app/models/health_care_application.rb', line 55

def async_submission_failed?
  saved_change_to_attribute?(:state) && failed?
end

#emailObject



59
60
61
62
63
# File 'app/models/health_care_application.rb', line 59

def email
  return nil if form.blank?

  parsed_form['email']
end

#failed?Boolean

Returns:

  • (Boolean)


47
48
49
# File 'app/models/health_care_application.rb', line 47

def failed?
  state == 'failed'
end

#form_matches_schemaObject (private)



307
308
309
310
311
312
313
# File 'app/models/health_care_application.rb', line 307

def form_matches_schema
  if form.present?
    JSON::Validator.fully_validate(VetsJsonSchema::SCHEMAS[self.class::FORM_ID], parsed_form).each do |v|
      errors.add(:form, v.to_s)
    end
  end
end

#form_submission_idObject



203
204
205
# File 'app/models/health_care_application.rb', line 203

def form_submission_id
  form_submission_id_string&.to_i
end

#log_async_submission_failureObject (private)



257
258
259
260
261
262
# File 'app/models/health_care_application.rb', line 257

def log_async_submission_failure
  log_zero_silent_failures
  StatsD.increment("#{HCA::Service::STATSD_KEY_PREFIX}.failed_wont_retry")
  StatsD.increment("#{HCA::Service::STATSD_KEY_PREFIX}.failed_wont_retry_short_form") if short_form?
  log_submission_failure_details
end

#log_submission_failure_detailsObject (private)



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
# File 'app/models/health_care_application.rb', line 269

def log_submission_failure_details
  return if parsed_form.blank?

  PersonalInformationLog.create!(
    data: parsed_form,
    error_class: 'HealthCareApplication FailedWontRetry'
  )

  log_message_to_sentry(
    'HCA total failure',
    :error,
    {
      first_initial: parsed_form.dig('veteranFullName', 'first')&.[](0) || 'no initial provided',
      middle_initial: parsed_form.dig('veteranFullName', 'middle')&.[](0) || 'no initial provided',
      last_initial: parsed_form.dig('veteranFullName', 'last')&.[](0) || 'no initial provided'
    },
    hca: :total_failure
  )
end

#log_sync_submission_failureObject (private)



251
252
253
254
255
# File 'app/models/health_care_application.rb', line 251

def log_sync_submission_failure
  StatsD.increment("#{HCA::Service::STATSD_KEY_PREFIX}.sync_submission_failed")
  StatsD.increment("#{HCA::Service::STATSD_KEY_PREFIX}.sync_submission_failed_short_form") if short_form?
  log_submission_failure_details
end

#log_zero_silent_failuresObject (private)



264
265
266
267
# File 'app/models/health_care_application.rb', line 264

def log_zero_silent_failures
  tags = ['service:healthcare-application', 'function: 10-10EZ async form submission']
  StatsD.increment('silent_failure_avoided_no_confirmation', tags:)
end

#long_form_required_fieldsObject (private)



213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'app/models/health_care_application.rb', line 213

def long_form_required_fields
  return if form.blank? || parsed_form['vaCompensationType'] == 'highDisability'

  %w[
    maritalStatus
    isEnrolledMedicarePartA
    lastServiceBranch
    lastEntryDate
    lastDischargeDate
  ].each do |attr|
    errors.add(:form, "#{attr} can't be null") if parsed_form[attr].nil?
  end
end

#parsed_formObject



207
208
209
# File 'app/models/health_care_application.rb', line 207

def parsed_form
  @parsed_form ||= form.present? ? JSON.parse(form) : nil
end

#prefill_fieldsObject (private)



227
228
229
230
231
232
233
234
235
# File 'app/models/health_care_application.rb', line 227

def prefill_fields
  return if user.blank? || !user.loa3?

  parsed_form.merge!({
    'veteranFullName' => user.full_name_normalized.compact.stringify_keys,
    'veteranDateOfBirth' => user.birth_date,
    'veteranSocialSecurityNumber' => user.ssn_normalized
  }.compact)
end

#process!Object



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'app/models/health_care_application.rb', line 91

def process!
  prefill_fields

  unless valid?
    StatsD.increment("#{HCA::Service::STATSD_KEY_PREFIX}.validation_error")

    StatsD.increment("#{HCA::Service::STATSD_KEY_PREFIX}.validation_error_short_form") if short_form?

    Sentry.set_extras(user_loa: user&.loa)

    PersonalInformationLog.create(
      data: parsed_form,
      error_class: 'HealthCareApplication ValidationError'
    )

    raise(Common::Exceptions::ValidationErrors, self)
  end

  if email.present? || async_compatible
    save!
    submit_async
  else
    submit_sync
  end
end

#send_failure_emailObject (private)



289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'app/models/health_care_application.rb', line 289

def send_failure_email
  first_name = parsed_form.dig('veteranFullName', 'first')
  template_id = Settings.vanotify.services.health_apps_1010.template_id.form1010_ez_failure_email
  api_key = Settings.vanotify.services.health_apps_1010.api_key

  salutation = first_name ? "Dear #{first_name}," : ''

  VANotify::EmailJob.perform_async(
    email,
    template_id,
    { 'salutation' => salutation },
    api_key
  )
  StatsD.increment("#{HCA::Service::STATSD_KEY_PREFIX}.submission_failure_email_sent")
rescue => e
  log_exception_to_sentry(e)
end

#send_failure_email?Boolean

Returns:

  • (Boolean)


65
66
67
# File 'app/models/health_care_application.rb', line 65

def send_failure_email?
  async_submission_failed? && email.present?
end

#set_result_on_success!(result) ⇒ Object



194
195
196
197
198
199
200
201
# File 'app/models/health_care_application.rb', line 194

def set_result_on_success!(result)
  update!(
    state: 'success',
    # this is a string because it overflowed the postgres integer limit in one of the tests
    form_submission_id_string: result[:formSubmissionId].to_s,
    timestamp: result[:timestamp]
  )
end

#short_form?Boolean

Returns:

  • (Boolean)


51
52
53
# File 'app/models/health_care_application.rb', line 51

def short_form?
  form.present? && parsed_form['lastServiceBranch'].blank?
end

#submit_asyncObject (private)



237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'app/models/health_care_application.rb', line 237

def submit_async
  submission_job = email.present? ? 'SubmissionJob' : 'AnonSubmissionJob'
  @parsed_form = override_parsed_form(parsed_form)

  "HCA::#{submission_job}".constantize.perform_async(
    self.class.get_user_identifier(user),
    HealthCareApplication::LOCKBOX.encrypt(parsed_form.to_json),
    id,
    google_analytics_client_id
  )

  self
end

#submit_syncObject



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'app/models/health_care_application.rb', line 69

def submit_sync
  @parsed_form = override_parsed_form(parsed_form)

  result = begin
    HCA::Service.new(user).submit_form(parsed_form)
  rescue Common::Client::Errors::ClientError => e
    log_exception_to_sentry(e)

    raise Common::Exceptions::BackendServiceException.new(
      nil, detail: e.message
    )
  end

  Rails.logger.info "SubmissionID=#{result[:formSubmissionId]}"

  result
rescue
  log_sync_submission_failure

  raise
end

#success?Boolean

Returns:

  • (Boolean)


43
44
45
# File 'app/models/health_care_application.rb', line 43

def success?
  state == 'success'
end