trizetto-api 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,54 @@
1
+ module Trizetto
2
+ module Api
3
+ module Eligibility
4
+ module WebService
5
+
6
+ # When a benefit is related to an entity, such as a primary care provider,
7
+ # the entity details are captured here
8
+ #
9
+ # <b>Example XML</b>
10
+ # <benefitentity>
11
+ # <entitycode>Primary Care Provider</entitycode>
12
+ # <name>JULIUS</name>
13
+ # <first>GROLLMAN</first>
14
+ # <identification_code_qualifier>Health Care Financing Administration National Provider Identifier</identification_code_qualifier>
15
+ # <benefit_related_entity_id>1609858695</benefit_related_entity_id>
16
+ # <benefit_related_entity_address_1>4101 TORRANCE BLVD</benefit_related_entity_address_1>
17
+ # <benefit_related_entity_city>TORRANCE</benefit_related_entity_city>
18
+ # <benefit_related_entity_state>CA</benefit_related_entity_state>
19
+ # <benefit_related_entity_zip>90503</benefit_related_entity_zip>
20
+ # <communicationnumberqualifier>Telephone</communicationnumberqualifier>
21
+ # <communicationnumber>3103035750</communicationnumber>
22
+ # </benefitentity>
23
+ #
24
+ # <b>Example</b>
25
+ # benefit.entity.entity_code # => "Primary Care Provider"
26
+ # benefit.entity.name # => "JULIUS"
27
+ # benefit.entity.first # => "GROLLMAN"
28
+ # benefit.entity.id # => "1609858695"
29
+ # benefit.entity.identification_code_qualifier # => "Health Care Financing Administration National Provider Identifier"
30
+ # benefit.entity.city # => "TORRANCE"
31
+ # benefit.entity.communication_number # => "3103035750"
32
+ #
33
+ class BenefitEntity < Node
34
+ KEY_CLEANUP =
35
+ {
36
+ entitycode: :entity_code,
37
+ communicationnumberqualifier: :communication_number_qualifier,
38
+ communicationnumber: :communication_number,
39
+ }
40
+
41
+ PREFIX_TRANSLATIONS =
42
+ [
43
+ :benefit_related_entity
44
+ ]
45
+
46
+ def initialize(raw_hash = {})
47
+ super(raw_hash)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
@@ -0,0 +1,72 @@
1
+ module Trizetto
2
+ module Api
3
+ module Eligibility
4
+ module WebService
5
+ # A dependent in the eligibility XML.
6
+ #
7
+ # <b>NOTE: Not all fields have been transcribed to objects</b>
8
+ #
9
+ # <b>Example XML</b>
10
+ #
11
+ # <dependent>
12
+ # <trace_number>999999999</trace_number>
13
+ # <trace_id>99TRIZETTO</trace_id>
14
+ # <subscriberaddinfo>
15
+ # <subsupplementalid>99</subsupplementalid>
16
+ # <grouppolicynum>999</grouppolicynum>
17
+ # </subscriberaddinfo>
18
+ # <subscriberaddinfo>
19
+ # <subsupplementalid>6P</subsupplementalid>
20
+ # <grouppolicynum>999999999A6AG999</grouppolicynum>
21
+ # <plansponsorname>BERGE-GREENHOLT</plansponsorname>
22
+ # </subscriberaddinfo>
23
+ # <date>
24
+ # <datequalifier>Plan</datequalifier>
25
+ # <date-of-service>20160101-99991231</date-of-service>
26
+ # </date>
27
+ # <date>
28
+ # <datequalifier>Service</datequalifier>
29
+ # <date-of-service>20180116</date-of-service>
30
+ # </date>
31
+ # <patientname>
32
+ # <first>JUANA</first>
33
+ # <middle>M</middle>
34
+ # <last>ORN</last>
35
+ # </patientname>
36
+ # <sex>F</sex>
37
+ # <date-of-birth>19630717</date-of-birth>
38
+ # <relationship>
39
+ # <insuredindicator>No</insuredindicator>
40
+ # <relationshipcode>Spouse</relationshipcode>
41
+ # <relationshiptypecode>Change</relationshiptypecode>
42
+ # <relationshipreasoncode>Change in Identifying Data Elements</relationshipreasoncode>
43
+ # </relationship>
44
+ # <benefit>
45
+ # <info>Active Coverage</info>
46
+ # <coveragelevel>Employee and Spouse</coveragelevel>
47
+ # <servicetype>Health Benefit Plan Coverage</servicetype>
48
+ # <servicetypecode>30</servicetypecode>
49
+ # <insurancetype>Preferred Provider Organization (PPO)</insurancetype>
50
+ # <insurancetypecode>PR</insurancetypecode>
51
+ # <plancoveragedescription>CDHP</plancoveragedescription>
52
+ # </benefit>
53
+ # </dependent>
54
+ #
55
+ # <b>Example</b>
56
+ #
57
+ # dependent.trace_number # => "999999999"
58
+ # dependent.name.first # => "JUNNA"
59
+ # dependent.name.middle # => "M"
60
+ # dependent.name.last # => "ORN"
61
+ # dependent.name.sex # => "F"
62
+ # dependent.name.benefits # => [ Array of Benefits ]
63
+ class Dependent < Patient
64
+ def initialize(raw_hash = {})
65
+ super(raw_hash)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
@@ -0,0 +1,168 @@
1
+ module Trizetto
2
+ module Api
3
+ module Eligibility
4
+ module WebService
5
+
6
+ # The parsed response from an eligibility check
7
+ class DoInquiryResponse
8
+
9
+ # The SuccessCode in the XML response from the eligibility request.
10
+ #
11
+ # Takes on one of these values
12
+ # * <tt>Success</tt> - The request was well formed and not missing any fields
13
+ # * <tt>ValidationFailure</tt> - The request was not valid. Maybe a field was formatted incorrectly or omitted.
14
+ # * <tt>PayerTimeout</tt> - ?
15
+ # * <tt>PayerNotSupported</tt> - ?
16
+ # * <tt>SystemError</tt> - ?
17
+ # * <tt>PayerEnrollmentRequired</tt> - ?
18
+ # * <tt>ProviderEnrollmentRequired</tt> - ?
19
+ # * <tt>ProductRequired</tt> - ?
20
+ attr_accessor :success_code
21
+
22
+ # For a successful request that returned an eligibility response, the
23
+ # transaction identifier from Trizetto. Seems to be 30 random alpha
24
+ # numeric characters
25
+ attr_accessor :transaction_id
26
+
27
+ # For a successful request, the name of the payer (insurance company)
28
+ attr_accessor :payer_name
29
+
30
+ # For a successful request, the identifier of the payer (insurance company)
31
+ attr_accessor :payer_id
32
+
33
+ # Any validation error or messages provided in the response
34
+ #
35
+ # *Example*
36
+ # client.extra_processing_info.messages # => ["Invalid InsuredFirstName Length."]
37
+ # client.extra_processing_info.validation_failures.first.affected_fields # => ["InsuredFirstName"]
38
+ # client.extra_processing_info.validation_failures.first.message # => "Invalid InsuredFirstName Length."
39
+ #
40
+ # @see ExtraProcessingInfo
41
+ #
42
+ attr_accessor :extra_processing_info
43
+
44
+ # The eligibility response xml parsed into a hash.
45
+ attr_accessor :eligibility_response_as_hash
46
+
47
+ # The Subscriber in the eligibility XML.
48
+ #
49
+ # @see Subscriber
50
+ attr_accessor :subscriber
51
+
52
+ # The Dependent in the eligibility XML.
53
+ #
54
+ # @see Dependent
55
+ attr_accessor :dependent
56
+
57
+ # The infosource in the eligibilty XML
58
+ #
59
+ # @see InfoSource
60
+ attr_accessor :info_source
61
+
62
+ # The inforeceiver in the eligibilty XML
63
+ #
64
+ # @see InfoReceiver
65
+ attr_accessor :info_receiver
66
+
67
+
68
+ def initialize(response)
69
+ response = response.to_hash.dig(:do_inquiry_response, :do_inquiry_result) || {}
70
+
71
+ self.success_code = response[:success_code]
72
+ self.response_as_xml = response[:response_as_xml]
73
+ self.extra_processing_info = ExtraProcessingInfo.new(response[:extra_processing_info])
74
+ end
75
+
76
+ def response_as_xml=(eligibility_response_xml_as_string)
77
+ parser = Nori.new(convert_tags_to: lambda { |tag| tag.snakecase.to_sym })
78
+ self.eligibility_response_as_hash = parser.parse(eligibility_response_xml_as_string || '')[:eligibilityresponse] || {}
79
+
80
+ self.transaction_id = eligibility_response_as_hash.dig(:infosource, :transactionid)
81
+ self.payer_name = eligibility_response_as_hash.dig(:infosource, :payername)
82
+ self.payer_id = eligibility_response_as_hash.dig(:infosource, :payerid)
83
+
84
+ # TODO: I have not yet been able to find an example of multiple subscribers
85
+ # in a response from trizetto. But, I think if there were multiple
86
+ # subscribers matching a queruy, the would come back as <subscriber>...</subscriber><subscriber>...</subscriber>
87
+ # which Nori will turn into an array (subscriber: [...]).
88
+
89
+ if subscriber_xml = eligibility_response_as_hash[:subscriber]
90
+ raise MultipleSubscribersError if subscriber_xml.is_a?(Array)
91
+ self.subscriber = Subscriber.new(subscriber_xml)
92
+ end
93
+
94
+ if dependent_xml = eligibility_response_as_hash[:dependent]
95
+ raise MultipleDependentsError if dependent_xml.is_a?(Array)
96
+ self.dependent = Dependent.new(dependent_xml)
97
+ end
98
+
99
+ if info_source_xml = eligibility_response_as_hash[:infosource]
100
+ self.info_source = InfoSource.new(info_source_xml)
101
+ end
102
+
103
+ if info_receiver_xml = eligibility_response_as_hash[:inforeceiver]
104
+ self.info_receiver = InfoReceiver.new(info_receiver_xml)
105
+ end
106
+ end
107
+ protected :response_as_xml=
108
+
109
+ # Did we successfully get back an eligibility response from the payer.
110
+ #
111
+ # This does not mean the patient has active coverage.
112
+ #
113
+ # @return Boolean
114
+ def success?
115
+ success_code == 'Success' && [transaction_id, payer_name, payer_id].none?(&:blank?)
116
+ end
117
+
118
+ # The dependent or the subscriber - the best guess at who is the patient
119
+ def patient
120
+ dependent || subscriber
121
+ end
122
+
123
+ # Was the eligibility check rejected. Eligibility checks may be rejected
124
+ # because they have expired, they don't exist, the payer service is unable
125
+ # to response, or there were errors with the request handled at the
126
+ # payer level instead of through Trizetto. There can be _multiple_ rejections
127
+ # on a single request
128
+ #
129
+ # @see #rejections
130
+ def rejected?
131
+ !rejections.empty?
132
+ end
133
+
134
+ # Rejections can appear in the subscriber, info source, info receiver,
135
+ # and possibly in the dependent. Additionaly, there can be _multiple_
136
+ # rejections in any one of those. This collects them all
137
+ def rejections
138
+ [subscriber, dependent, info_receiver, info_source].compact.map(&:rejections).flatten.compact
139
+ end
140
+
141
+ # Validation errors handled at Trizetto's level
142
+ #
143
+ # @return ExtraProcessingInfo
144
+ def errors
145
+ self.extra_processing_info
146
+ end
147
+
148
+ # Does the patient we asked about have active insurance coverage for
149
+ # this service type code? Service type codes are strings and the
150
+ # most common is 30, general health benefits coverage.
151
+ #
152
+ # <b>Example</b>
153
+ #
154
+ # response.active_coverage_for?("30") #=> true
155
+ #
156
+ # <b>References</b>
157
+ #
158
+ # * {http://www.x12.org/codes/health-care-service-type-codes/ Service Type Code Reference<}
159
+ def active_coverage_for?(service_type_code)
160
+ !!(patient && patient.benefits.any? do |benefit|
161
+ benefit.active_coverage? && benefit.service_type_codes.include?(service_type_code.to_s)
162
+ end)
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,41 @@
1
+ module Trizetto
2
+ module Api
3
+ module Eligibility
4
+ module WebService
5
+
6
+ # Validation failures from the DoInquiryRequest.
7
+ #
8
+ # <b>WSDL Reference</b>
9
+ #
10
+ # <s:element minOccurs="0" maxOccurs="1" name="ExtraProcessingInfo" type="tns:ValidationFailureCollection" />
11
+ #
12
+ class ExtraProcessingInfo
13
+
14
+ # An array of strings, each a single validation failure
15
+ attr_accessor :messages
16
+
17
+ # An array of ValidationFailure, each indicating field that had errors
18
+ attr_accessor :validation_failures
19
+
20
+ def initialize(extra_processing_info)
21
+ self.messages = Array(extra_processing_info.dig(:all_messages, :string))
22
+
23
+ failures = extra_processing_info.dig(:failures,:validation_failure) || []
24
+ failures = [failures] if failures.is_a?(Hash)
25
+
26
+ self.validation_failures = failures.map do |failure|
27
+ ValidationFailure.new(failure)
28
+ end
29
+ end
30
+
31
+ def to_h
32
+ {
33
+ messages: messages,
34
+ validation_failures: validation_failures.map(&:to_h)
35
+ }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,14 @@
1
+ module Trizetto
2
+ module Api
3
+ module Eligibility
4
+ module WebService
5
+ #
6
+ # Information about the Receiver. May include rejections
7
+ #
8
+ class InfoReceiver < Node
9
+ prepend Rejectable
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Trizetto
2
+ module Api
3
+ module Eligibility
4
+ module WebService
5
+ #
6
+ # Information about the Source. May include rejections
7
+ #
8
+ class InfoSource < Node
9
+ prepend Rejectable
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,43 @@
1
+ require 'ostruct'
2
+
3
+ module Trizetto
4
+ module Api
5
+ module Eligibility
6
+ module WebService
7
+
8
+ # Base class for parsed reponses in the eligibility response.
9
+ class Node < OpenStruct
10
+ def initialize(raw_hash = {})
11
+ required_keys = self.class.constants.include?(:REQUIRED_KEYS) ? self.class::REQUIRED_KEYS : {}
12
+ clean_hash = required_keys.merge(raw_hash)
13
+
14
+ cleanup_keys = self.class.constants.include?(:KEY_CLEANUP) ? self.class::KEY_CLEANUP : {}
15
+ cleanup_keys.each do |uglykey, friendly_key|
16
+ clean_hash[friendly_key] = clean_hash.delete(uglykey) if clean_hash.has_key?(uglykey)
17
+ end
18
+
19
+ # Convert prefixed keys "benefit_related_entity_id" to simple keys "id"
20
+ prefix_translations = self.class.constants.include?(:PREFIX_TRANSLATIONS) ? self.class::PREFIX_TRANSLATIONS : {}
21
+ prefix_translations.each do |key_prefix|
22
+ clean_hash.keys.each do |key|
23
+ if key.to_s =~ /^#{key_prefix}_(.*)$/
24
+ clean_hash["#{$1}".to_sym] = clean_hash.delete(key)
25
+ end
26
+ end
27
+ end
28
+
29
+ super(clean_hash)
30
+
31
+ after_inititlize(clean_hash)
32
+ end
33
+
34
+ # Callback after the prased eligibility response has been cleaned up
35
+ def after_inititlize(hash)
36
+ end
37
+ protected :after_inititlize
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,36 @@
1
+ module Trizetto
2
+ module Api
3
+ module Eligibility
4
+ module WebService
5
+
6
+ # A Patient is either a Subscriber or Depedent. This is the common
7
+ # attributes between the two
8
+ class Patient < Node
9
+
10
+ # @see PatientName
11
+ attr_accessor :name
12
+
13
+ # The benefits this patient has.
14
+ #
15
+ attr_accessor :benefits
16
+
17
+ KEY_CLEANUP = {
18
+ :patientid => :id
19
+ }
20
+
21
+ def initialize(raw_hash = {})
22
+ super(raw_hash)
23
+ self.name = PatientName.new(raw_hash[:patientname]) if raw_hash.has_key?(:patientname)
24
+
25
+ benefits_xml = raw_hash[:benefit] || []
26
+ benefits_xml = [benefits_xml] if benefits_xml.is_a?(Hash)
27
+
28
+ self.benefits = benefits_xml.map do |benefit_xml|
29
+ Benefit.new(benefit_xml)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end