sunnyside 0.1.1 → 0.1.2

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8e0f90574f4539e1f9daa2488cbca27d81414ad3
4
+ data.tar.gz: 12ce71ebb22046f4d41c371ef3b5e984b04a25a4
5
+ SHA512:
6
+ metadata.gz: 4cace35d98ac36f145edd1f29c32564fc56195d8711ee6ab4a941e721d4d2ecfcee4070aa7dd2d88ee5b0f119979cbc56d6309508dcfd7d44e1b10aac396bc48
7
+ data.tar.gz: 0ac9c8a12d46770f952cfc36e3072bad461572c843b0c24ff69d17888ebdd51c9aced11f7e59c5cd78587b2be5f24beca8227816d4e48c4824161a6809d1e88b
data/bin/sunnyside CHANGED
@@ -1,6 +1,6 @@
1
- #!/usr/bin/env ruby
2
- require 'rubygems'
3
- require 'sequel'
4
- require 'sunnyside'
5
-
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'sequel'
4
+ require 'sunnyside'
5
+
6
6
  Sunnyside::Menu.new.start
data/lib/sunnyside.rb CHANGED
@@ -1,35 +1,35 @@
1
- require 'prawn'
2
- require 'sequel'
3
- require 'csv'
4
- require 'fileutils'
5
- require "sunnyside/version"
6
- require 'sunnyside/cash_receipts/cash_receipt'
7
- require 'sunnyside/ledger/ledger'
8
- require 'sunnyside/ledger/edi'
9
- require 'sunnyside/ledger/auth_report'
10
- require 'sunnyside/ledger/private'
11
- require 'sunnyside/reports/pdf_report'
12
- require 'sunnyside/reports/private'
13
- require 'sunnyside/reports/report'
14
- require 'sunnyside/query/query'
15
- require 'sunnyside/ftp'
16
- require 'sunnyside/menu'
17
- require 'sunnyside/expiring_auth'
18
- require 'sunnyside/models/db_setup'
19
- require 'sunnyside/advanced'
20
-
21
- module Sunnyside
22
- DRIVE = ENV["HOMEDRIVE"]
23
-
24
- Sunnyside.create_folders if !Dir.exist?("#{DRIVE}/sunnyside-files")
25
- Dir.chdir("R:/Departments/AR Department")
26
- DB = Sequel.connect("sqlite://sunnyside.db")
27
-
28
- if DB.tables.empty?
29
- Sunnyside.create_tables
30
- Sunnyside.add_providers
31
- Sunnyside.add_denial_data
32
- end
33
-
34
- require 'sunnyside/models/sequel_classes'
35
- end
1
+ require 'prawn'
2
+ require 'sequel'
3
+ require 'csv'
4
+ require 'fileutils'
5
+ require "sunnyside/version"
6
+ require 'sunnyside/cash_receipts/cash_receipt'
7
+ require 'sunnyside/ledger/ledger'
8
+ require 'sunnyside/ledger/edi'
9
+ require 'sunnyside/ledger/auth_report'
10
+ require 'sunnyside/ledger/private'
11
+ require 'sunnyside/reports/pdf_report'
12
+ require 'sunnyside/reports/private'
13
+ require 'sunnyside/reports/report'
14
+ require 'sunnyside/query/query'
15
+ require 'sunnyside/ftp'
16
+ require 'sunnyside/menu'
17
+ require 'sunnyside/expiring_auth'
18
+ require 'sunnyside/models/db_setup'
19
+ require 'sunnyside/advanced'
20
+
21
+ module Sunnyside
22
+ DRIVE = ENV["HOMEDRIVE"]
23
+
24
+ Sunnyside.create_folders if !Dir.exist?("#{DRIVE}/sunnyside-files")
25
+ Dir.chdir("R:/Departments/AR Department")
26
+ DB = Sequel.connect("sqlite://sunnyside.db")
27
+
28
+ if DB.tables.empty?
29
+ Sunnyside.create_tables
30
+ Sunnyside.add_providers
31
+ Sunnyside.add_denial_data
32
+ end
33
+
34
+ require 'sunnyside/models/sequel_classes'
35
+ end
@@ -1,85 +1,85 @@
1
- module Sunnyside
2
- def self.advanced_opts
3
- puts "1.) Add new provider"
4
- puts "2.) Export A/R denials"
5
-
6
- case gets.chomp
7
- when '1'
8
- print "Type in the provider name _EXACTLY_ how it appears on the SanData archive report files (e.g. Guildnet is always GUILDNET): "
9
- provider = gets.chomp
10
- print "Now type in the abbreviation (batch initials - e.g. MetroPlus Health is MPH): "
11
- abbrev = gets.chomp
12
- print "Now type in the CREDIT account that is used in FUND EZ: "
13
- credit = gets.chomp
14
- print "Now type in the DEBIT account that is used in FUND EZ: "
15
- debit = gets.chomp
16
- print "And finally, type in the FUND number that is used in FUND EZ: "
17
- fund = gets.chomp
18
- review = "--------Name: #{provider}, Credit Account: #{credit}, Debit Account: #{debit}, Fund: #{fund}, Abbreviation: #{abbrev}--------"
19
- puts "Please review the below information."
20
- puts '-' * review.length
21
- puts review
22
- puts '-' * review.length
23
- print "Is this correct? (Y for yes, N for No): "
24
- raise 'You have an empty field! Start over!' if [provider, credit, debit, fund, abbrev].any? { |elem| elem.empty? }
25
- if gets.chomp.upcase == 'Y'
26
- provider = Provider.insert(name: provider, credit_account: credit, debit_account: debit, fund: fund, abbreviation: abbrev)
27
- puts "#{Provider[provider].name} added."
28
- else
29
- Sunnyside.advanced_opts
30
- end
31
- else
32
- exit
33
- end
34
- end
35
-
36
- def self.rails_server
37
- puts "Please wait..."
38
-
39
- Dir.chdir("R:/Departments/AR Department/sunnyside-app")
40
-
41
- %x(rails s)
42
- end
43
-
44
- def self.determine_browser
45
- if Dir.exist?("#{DRIVE}/Program Files (x86)")
46
- Dir.chdir("#{DRIVE}/Program Files (x86)/Mozilla Firefox")
47
- else
48
- Dir.chdir("#{DRIVE}/Program Files/Mozilla Firefox")
49
- end
50
- end
51
-
52
- def self.add_provider_to_ftp
53
- Provider.all.each { |prov| puts "#{prov.id}: #{prov.name}"}
54
- print "Type in the corresponding ID Number for the provider you would like to add to FTP: "
55
-
56
- provider = Provider[gets.chomp].abbreviation
57
-
58
- puts "You've selected #{provider}"
59
-
60
- print "Type in the ftp address now: "
61
- site = gets.chomp
62
-
63
- print "Type in the username now: "
64
- username = gets.chomp
65
-
66
- print "Type in the password now: "
67
- password = gets.chomp
68
-
69
- review = "-------Provider: #{provider}, Site: #{site}, Username: #{username}, Password: #{password}--------------"
70
- puts "Please review the following information: "
71
- puts '-' * review.length
72
- puts review
73
- puts '-' * review.length
74
-
75
- puts "Is this correct? Type Y or N."
76
- if gets.chomp.downcase == 'y'
77
- Login.insert(site: site, username: username, password: password, provider: provider)
78
- Dir.mkdir("#{DRIVE}/sunnyside-files/ftp/835/#{provider}")
79
- Dir.mkdir("#{DRIVE}/sunnyside-files/ftp/837/#{provider}")
80
- else
81
- puts 'Please try again.'
82
- Sunnyside.add_provider_to_ftp
83
- end
84
- end
1
+ module Sunnyside
2
+ def self.advanced_opts
3
+ puts "1.) Add new provider"
4
+ puts "2.) Export A/R denials"
5
+
6
+ case gets.chomp
7
+ when '1'
8
+ print "Type in the provider name _EXACTLY_ how it appears on the SanData archive report files (e.g. Guildnet is always GUILDNET): "
9
+ provider = gets.chomp
10
+ print "Now type in the abbreviation (batch initials - e.g. MetroPlus Health is MPH): "
11
+ abbrev = gets.chomp
12
+ print "Now type in the CREDIT account that is used in FUND EZ: "
13
+ credit = gets.chomp
14
+ print "Now type in the DEBIT account that is used in FUND EZ: "
15
+ debit = gets.chomp
16
+ print "And finally, type in the FUND number that is used in FUND EZ: "
17
+ fund = gets.chomp
18
+ review = "--------Name: #{provider}, Credit Account: #{credit}, Debit Account: #{debit}, Fund: #{fund}, Abbreviation: #{abbrev}--------"
19
+ puts "Please review the below information."
20
+ puts '-' * review.length
21
+ puts review
22
+ puts '-' * review.length
23
+ print "Is this correct? (Y for yes, N for No): "
24
+ raise 'You have an empty field! Start over!' if [provider, credit, debit, fund, abbrev].any? { |elem| elem.empty? }
25
+ if gets.chomp.upcase == 'Y'
26
+ provider = Provider.insert(name: provider, credit_account: credit, debit_account: debit, fund: fund, abbreviation: abbrev)
27
+ puts "#{Provider[provider].name} added."
28
+ else
29
+ Sunnyside.advanced_opts
30
+ end
31
+ else
32
+ exit
33
+ end
34
+ end
35
+
36
+ def self.rails_server
37
+ puts "Please wait..."
38
+
39
+ Dir.chdir("R:/Departments/AR Department/sunnyside-app")
40
+
41
+ %x(rails s)
42
+ end
43
+
44
+ def self.determine_browser
45
+ if Dir.exist?("#{DRIVE}/Program Files (x86)")
46
+ Dir.chdir("#{DRIVE}/Program Files (x86)/Mozilla Firefox")
47
+ else
48
+ Dir.chdir("#{DRIVE}/Program Files/Mozilla Firefox")
49
+ end
50
+ end
51
+
52
+ def self.add_provider_to_ftp
53
+ Provider.all.each { |prov| puts "#{prov.id}: #{prov.name}"}
54
+ print "Type in the corresponding ID Number for the provider you would like to add to FTP: "
55
+
56
+ provider = Provider[gets.chomp].abbreviation
57
+
58
+ puts "You've selected #{provider}"
59
+
60
+ print "Type in the ftp address now: "
61
+ site = gets.chomp
62
+
63
+ print "Type in the username now: "
64
+ username = gets.chomp
65
+
66
+ print "Type in the password now: "
67
+ password = gets.chomp
68
+
69
+ review = "-------Provider: #{provider}, Site: #{site}, Username: #{username}, Password: #{password}--------------"
70
+ puts "Please review the following information: "
71
+ puts '-' * review.length
72
+ puts review
73
+ puts '-' * review.length
74
+
75
+ puts "Is this correct? Type Y or N."
76
+ if gets.chomp.downcase == 'y'
77
+ Login.insert(site: site, username: username, password: password, provider: provider)
78
+ Dir.mkdir("#{DRIVE}/sunnyside-files/ftp/835/#{provider}")
79
+ Dir.mkdir("#{DRIVE}/sunnyside-files/ftp/837/#{provider}")
80
+ else
81
+ puts 'Please try again.'
82
+ Sunnyside.add_provider_to_ftp
83
+ end
84
+ end
85
85
  end
@@ -3,14 +3,18 @@ module Sunnyside
3
3
  def self.cash_receipt
4
4
  puts "1.) EDI PAYMENT"
5
5
  puts "2.) MANUAL PAYMENT"
6
- cash_receipt =
7
- case gets.chomp
8
- when '1'
9
- CashReceipt.new(:electronic)
10
- when '2'
11
- CashReceipt.new(:manual)
12
- end
13
- cash_receipt.collate
6
+ puts "3.) RESET A/R SPREADSHEET"
7
+ case gets.chomp
8
+ when '1'
9
+ cash_receipt = CashReceipt.new(:electronic)
10
+ when '2'
11
+ cash_receipt = CashReceipt.new(:manual)
12
+ when '3'
13
+ CSV.open("#{DRIVE}/sunnyside-files/cash_receipts/EDI-citywide-import.csv", "w") { |row|
14
+ row << ['Seq','Receipt','post_date','other id','invoice','header memo','batch','doc date','detail memo','fund','account','cc1','cc2','cc3','debit','credit']
15
+ }
16
+ end
17
+ cash_receipt.collate if cash_receipt
14
18
  end
15
19
 
16
20
  class CashReceipt
@@ -39,9 +43,9 @@ module Sunnyside
39
43
  print "You have typed out #{invoices.length} number of invoices. Do you wish to add more to the same check? (Y or N): "
40
44
  if gets.chomp.upcase == 'Y'
41
45
  more_invoices = gets.chomp.split
42
- return (more_invoices + invoices)
46
+ return (more_invoices + invoices).uniq
43
47
  else
44
- return invoices
48
+ return invoices.uniq
45
49
  end
46
50
  end
47
51
 
@@ -61,7 +65,6 @@ module Sunnyside
61
65
  manual_invoices
62
66
  end
63
67
  end
64
-
65
68
  end
66
69
 
67
70
  def invoices_exist?(invoices)
File without changes
data/lib/sunnyside/ftp.rb CHANGED
File without changes
File without changes
@@ -1,175 +1,175 @@
1
- module Sunnyside
2
- def self.edi_parser
3
- print "checking for new files...\n"
4
- Dir["#{DRIVE}/sunnyside-files/835/*.txt"].each do |file|
5
-
6
- if Filelib.where(filename: file).count > 0
7
- puts "This file has been processed already. File removed."
8
- File.delete(file)
9
- else
10
- print "processing #{file}...\n"
11
- file_data = File.open(file)
12
- data = file_data.read
13
- # Detect to see if the EDI file already has new lines inserted. If so, the newlines are removed before the file gets processed.
14
-
15
- data.gsub!(/\n/, '')
16
-
17
- data = data.split(/~CLP\*/)
18
-
19
- edi_file = EdiReader.new(data)
20
- edi_file.parse_claims
21
- Filelib.insert(filename: file, purpose: '835')
22
- file_data.close
23
- FileUtils.mv(file, "#{DRIVE}/sunnyside-files/835/archive/#{File.basename(file)}")
24
- end
25
- end
26
- end
27
-
28
- class EdiReader
29
- attr_reader :data
30
-
31
- def initialize(data)
32
- @data = data
33
- end
34
-
35
- def claims
36
- data.select { |clm| clm =~ /^\d+/ }.map { |clm| clm.split(/~(?=SVC)/) }
37
- end
38
-
39
- def check_number
40
- check = data[0][/(?<=~TRN\*\d\*)\w+/]
41
- return check.include?('E') ? check[/\d+$/] : check
42
- end
43
-
44
- def check_total
45
- data[0][/(?<=~BPR\*\w\*)[0-9\.\-]+/, 0]
46
- end
47
-
48
- def parse_claims
49
- payment_id = Payment.insert(check_number: check_number, check_total: check_total)
50
- claims.each { |claim| ClaimParser.new(claim, payment_id).parse }
51
- end
52
- end
53
-
54
- class ClaimParser < EdiReader
55
- attr_reader :claim_header, :service_data, :payment_id
56
-
57
- def initialize(claim, payment_id)
58
- @claim_header = claim[0].split(/\*/)
59
- @service_data = claim.select { |clm| clm =~ /^SVC/ }
60
- @payment_id = Payment[payment_id]
61
- end
62
-
63
- def header
64
- {
65
- :invoice => claim_header[0],
66
- :response_code => claim_header[1],
67
- :billed => claim_header[2],
68
- :paid => claim_header[3],
69
- :units => claim_header[5], # 4 is not used - that is the patient responsibility amount
70
- :claim_number => claim_header[6][/^\d+/]
71
- }
72
- end
73
-
74
- def parse
75
- claim = ClaimEntry.new(header)
76
- claim_id = claim.to_db(payment_id)
77
- service_data.each { |service| ServiceParser.new(service, claim_id).parse }
78
- end
79
- end
80
-
81
- class ClaimEntry < EdiReader
82
- attr_reader :invoice, :response_code, :billed, :paid, :units, :claim_number
83
-
84
- def initialize(header = {})
85
- @invoice = header[:invoice].gsub(/[OLD]/, 'O' => '0', 'D' => '8', 'L' => '1').gsub(/^0/, '')[0..5].to_i # for the corrupt SHP EDI files
86
- @response_code = header[:response_code]
87
- @billed = header[:billed]
88
- @paid = header[:paid]
89
- @units = header[:units]
90
- @claim_number = header[:claim_number]
91
- end
92
-
93
- def to_db(payment)
94
- payment.update(provider_id: inv.provider_id) if payment.provider_id.nil?
95
- Claim.insert(
96
- :invoice_id => invoice,
97
- :payment_id => payment.id,
98
- :client_id => inv.client_id,
99
- :control_number => claim_number,
100
- :paid => paid,
101
- :billed => billed,
102
- :status => response_code,
103
- :provider_id => inv.provider_id,
104
- :recipient_id => inv.recipient_id
105
- )
106
- end
107
-
108
- def inv
109
- Invoice[invoice]
110
- end
111
- end
112
-
113
- class ServiceParser < EdiReader
114
- attr_reader :service_line, :claim
115
-
116
- def initialize(service_line, claim_id)
117
- @service_line = service_line.split(/~/)
118
- @claim = Claim[claim_id]
119
- end
120
-
121
- def dos
122
- service_line.detect { |svc| svc =~ /^DTM/ }[/\w+$/]
123
- end
124
-
125
- def service_header
126
- line = service_line[0].split(/\*/).drop(1)
127
- if line.length == 7 || line[1] != line[2]
128
- return line.uniq
129
- else
130
- return line
131
- end
132
- end
133
-
134
- def error_codes
135
- service_line.find_all { |section| section =~ /^CAS|^SE/ }.map { |code| code[/\w+\*\w+\*(\d+)/, 1] }
136
- end
137
-
138
- def parse
139
- service = ServiceEntry.new(service_header, dos, error_codes)
140
- service.to_db(claim)
141
- end
142
- end
143
-
144
- class ServiceEntry < EdiReader
145
- attr_reader :paid, :billed, :service_code, :units, :res_code, :error_codes, :dos
146
-
147
- def initialize(service_header, dos, error_codes)
148
- @service_code, @billed, @paid, @res_code, @units = service_header
149
- @dos = Date.parse(dos)
150
- @error_codes = error_codes.map { |id| Denial[id].denial_explanation }
151
- end
152
-
153
- def to_db(claim)
154
- Service.insert(
155
- :claim_id => claim.id,
156
- :invoice_id => claim.invoice_id,
157
- :payment_id => claim.payment_id,
158
- :denial_reason => denial_reason,
159
- :service_code => service_code.gsub(/HC:/, ''),
160
- :paid => paid,
161
- :billed => billed,
162
- :units => units,
163
- :dos => dos
164
- )
165
- end
166
-
167
- def denial_reason
168
- error_codes.join("\n") if denied?
169
- end
170
-
171
- def denied?
172
- paid != billed
173
- end
174
- end
1
+ module Sunnyside
2
+ def self.edi_parser
3
+ print "checking for new files...\n"
4
+ Dir["#{DRIVE}/sunnyside-files/835/*.txt"].each do |file|
5
+
6
+ if Filelib.where(filename: file).count > 0
7
+ puts "This file has been processed already. File removed."
8
+ File.delete(file)
9
+ else
10
+ print "processing #{file}...\n"
11
+ file_data = File.open(file)
12
+ data = file_data.read
13
+ # Detect to see if the EDI file already has new lines inserted. If so, the newlines are removed before the file gets processed.
14
+
15
+ data.gsub!(/\n/, '')
16
+
17
+ data = data.split(/~CLP\*/)
18
+
19
+ edi_file = EdiReader.new(data)
20
+ edi_file.parse_claims
21
+ Filelib.insert(filename: File.basename(file), purpose: '835')
22
+ file_data.close
23
+ FileUtils.mv(file, "#{DRIVE}/sunnyside-files/835/archive/#{File.basename(file)}")
24
+ end
25
+ end
26
+ end
27
+
28
+ class EdiReader
29
+ attr_reader :data
30
+
31
+ def initialize(data)
32
+ @data = data
33
+ end
34
+
35
+ def claims
36
+ data.select { |clm| clm =~ /^\d+/ }.map { |clm| clm.split(/~(?=SVC)/) }
37
+ end
38
+
39
+ def check_number
40
+ check = data[0][/(?<=~TRN\*\d\*)\w+/]
41
+ return check.include?('E') ? check[/\d+$/] : check
42
+ end
43
+
44
+ def check_total
45
+ data[0][/(?<=~BPR\*\w\*)[0-9\.\-]+/, 0]
46
+ end
47
+
48
+ def parse_claims
49
+ payment_id = Payment.insert(check_number: check_number, check_total: check_total)
50
+ claims.each { |claim| ClaimParser.new(claim, payment_id).parse }
51
+ end
52
+ end
53
+
54
+ class ClaimParser < EdiReader
55
+ attr_reader :claim_header, :service_data, :payment_id
56
+
57
+ def initialize(claim, payment_id)
58
+ @claim_header = claim[0].split(/\*/)
59
+ @service_data = claim.select { |clm| clm =~ /^SVC/ }
60
+ @payment_id = Payment[payment_id]
61
+ end
62
+
63
+ def header
64
+ {
65
+ :invoice => claim_header[0],
66
+ :response_code => claim_header[1],
67
+ :billed => claim_header[2],
68
+ :paid => claim_header[3],
69
+ :units => claim_header[5], # 4 is not used - that is the patient responsibility amount
70
+ :claim_number => claim_header[6][/^\d+/]
71
+ }
72
+ end
73
+
74
+ def parse
75
+ claim = ClaimEntry.new(header)
76
+ claim_id = claim.to_db(payment_id)
77
+ service_data.each { |service| ServiceParser.new(service, claim_id).parse }
78
+ end
79
+ end
80
+
81
+ class ClaimEntry < EdiReader
82
+ attr_reader :invoice, :response_code, :billed, :paid, :units, :claim_number
83
+
84
+ def initialize(header = {})
85
+ @invoice = header[:invoice].gsub(/[OLD]/, 'O' => '0', 'D' => '8', 'L' => '1').gsub(/^0/, '')[0..5].to_i # for the corrupt SHP EDI files
86
+ @response_code = header[:response_code]
87
+ @billed = header[:billed]
88
+ @paid = header[:paid]
89
+ @units = header[:units]
90
+ @claim_number = header[:claim_number]
91
+ end
92
+
93
+ def to_db(payment)
94
+ payment.update(provider_id: inv.provider_id) if payment.provider_id.nil?
95
+ Claim.insert(
96
+ :invoice_id => invoice,
97
+ :payment_id => payment.id,
98
+ :client_id => inv.client_id,
99
+ :control_number => claim_number,
100
+ :paid => paid,
101
+ :billed => billed,
102
+ :status => response_code,
103
+ :provider_id => inv.provider_id,
104
+ :recipient_id => inv.recipient_id
105
+ )
106
+ end
107
+
108
+ def inv
109
+ Invoice[invoice]
110
+ end
111
+ end
112
+
113
+ class ServiceParser < EdiReader
114
+ attr_reader :service_line, :claim
115
+
116
+ def initialize(service_line, claim_id)
117
+ @service_line = service_line.split(/~/)
118
+ @claim = Claim[claim_id]
119
+ end
120
+
121
+ def dos
122
+ service_line.detect { |svc| svc =~ /^DTM/ }[/\w+$/]
123
+ end
124
+
125
+ def service_header
126
+ line = service_line[0].split(/\*/).drop(1)
127
+ if line.length == 7 || line[1] != line[2]
128
+ return line.uniq
129
+ else
130
+ return line
131
+ end
132
+ end
133
+
134
+ def error_codes
135
+ service_line.find_all { |section| section =~ /^CAS|^SE/ }.map { |code| code[/\w+\*\w+\*(\d+)/, 1] }
136
+ end
137
+
138
+ def parse
139
+ service = ServiceEntry.new(service_header, dos, error_codes)
140
+ service.to_db(claim)
141
+ end
142
+ end
143
+
144
+ class ServiceEntry < EdiReader
145
+ attr_reader :paid, :billed, :service_code, :units, :res_code, :error_codes, :dos
146
+
147
+ def initialize(service_header, dos, error_codes)
148
+ @service_code, @billed, @paid, @res_code, @units = service_header
149
+ @dos = Date.parse(dos)
150
+ @error_codes = error_codes.map { |id| Denial[id].denial_explanation }
151
+ end
152
+
153
+ def to_db(claim)
154
+ Service.insert(
155
+ :claim_id => claim.id,
156
+ :invoice_id => claim.invoice_id,
157
+ :payment_id => claim.payment_id,
158
+ :denial_reason => denial_reason,
159
+ :service_code => service_code.gsub(/HC:/, ''),
160
+ :paid => paid,
161
+ :billed => billed,
162
+ :units => units,
163
+ :dos => dos
164
+ )
165
+ end
166
+
167
+ def denial_reason
168
+ error_codes.join("\n") if denied?
169
+ end
170
+
171
+ def denied?
172
+ paid != billed
173
+ end
174
+ end
175
175
  end