trizetto-api 0.1.2 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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