ofx_for_ruby 0.1.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.
Files changed (74) hide show
  1. data/.externalToolBuilders/Rebuild parsers.launch +10 -0
  2. data/.gitignore +9 -0
  3. data/.loadpath +5 -0
  4. data/.project +27 -0
  5. data/COPYING +674 -0
  6. data/Gemfile +2 -0
  7. data/README +47 -0
  8. data/RELEASE_NOTES +45 -0
  9. data/Rakefile +63 -0
  10. data/lib/ofx.rb +60 -0
  11. data/lib/ofx/1.0.2/banking_message_set.rb +261 -0
  12. data/lib/ofx/1.0.2/credit_card_statement_message_set.rb +269 -0
  13. data/lib/ofx/1.0.2/document.rb +34 -0
  14. data/lib/ofx/1.0.2/email_message_set.rb +34 -0
  15. data/lib/ofx/1.0.2/financial_institution_profile_message_set.rb +155 -0
  16. data/lib/ofx/1.0.2/header.rb +48 -0
  17. data/lib/ofx/1.0.2/interbank_funds_transfer_message_set.rb +30 -0
  18. data/lib/ofx/1.0.2/investment_security_list_message_set.rb +30 -0
  19. data/lib/ofx/1.0.2/investment_statement_message_set.rb +30 -0
  20. data/lib/ofx/1.0.2/lexer.rb +135 -0
  21. data/lib/ofx/1.0.2/message_set.rb +165 -0
  22. data/lib/ofx/1.0.2/ofx_102.racc +90 -0
  23. data/lib/ofx/1.0.2/ofx_102.rex +65 -0
  24. data/lib/ofx/1.0.2/parser.rb +215 -0
  25. data/lib/ofx/1.0.2/payment_message_set.rb +30 -0
  26. data/lib/ofx/1.0.2/serializer.rb +114 -0
  27. data/lib/ofx/1.0.2/signon_message_set.rb +121 -0
  28. data/lib/ofx/1.0.2/signup_message_set.rb +186 -0
  29. data/lib/ofx/1.0.2/statements.rb +54 -0
  30. data/lib/ofx/1.0.2/status.rb +30 -0
  31. data/lib/ofx/1.0.2/wire_funds_transfer_message_set.rb +30 -0
  32. data/lib/ofx/banking_message_set.rb +125 -0
  33. data/lib/ofx/credit_card_statement_message_set.rb +104 -0
  34. data/lib/ofx/document.rb +30 -0
  35. data/lib/ofx/email_message_set.rb +42 -0
  36. data/lib/ofx/file_unique_identifier.rb +28 -0
  37. data/lib/ofx/financial_client.rb +76 -0
  38. data/lib/ofx/financial_institution.rb +83 -0
  39. data/lib/ofx/financial_institution_profile_message_set.rb +67 -0
  40. data/lib/ofx/header.rb +115 -0
  41. data/lib/ofx/http/cacert.pem +3509 -0
  42. data/lib/ofx/http/ofx_http_client.rb +84 -0
  43. data/lib/ofx/interbank_funds_transfer_message_set.rb +33 -0
  44. data/lib/ofx/investment_security_list_message_set.rb +33 -0
  45. data/lib/ofx/investment_statement_message_set.rb +33 -0
  46. data/lib/ofx/message_set.rb +78 -0
  47. data/lib/ofx/payment_message_set.rb +33 -0
  48. data/lib/ofx/serializer.rb +39 -0
  49. data/lib/ofx/signon_message_set.rb +141 -0
  50. data/lib/ofx/signup_message_set.rb +84 -0
  51. data/lib/ofx/statements.rb +49 -0
  52. data/lib/ofx/status.rb +100 -0
  53. data/lib/ofx/transaction_unique_identifier.rb +28 -0
  54. data/lib/ofx/version.rb +82 -0
  55. data/lib/ofx/wire_funds_transfer_message_set.rb +33 -0
  56. data/ofx_for_ruby.gemspec +25 -0
  57. data/planning/OFX Specification completion.ods +0 -0
  58. data/test/capital_one/capital_one_helper.rb +77 -0
  59. data/test/capital_one/fixtures/README +10 -0
  60. data/test/capital_one/fixtures/fipid-5599.xml +98 -0
  61. data/test/capital_one/test_banking_statement.rb +108 -0
  62. data/test/capital_one/test_financial_institution_profile.rb +295 -0
  63. data/test/capital_one/test_ofx_http_client.rb +72 -0
  64. data/test/capital_one/test_signup_account_information.rb +100 -0
  65. data/test/citi/citi_helper.rb +77 -0
  66. data/test/citi/fixtures/README +9 -0
  67. data/test/citi/fixtures/fipid-6642.xml +98 -0
  68. data/test/citi/test_credit_card_statement.rb +159 -0
  69. data/test/citi/test_financial_institution_profile.rb +231 -0
  70. data/test/citi/test_ofx_http_client.rb +71 -0
  71. data/test/citi/test_signup_account_information.rb +85 -0
  72. data/test/test_helper.rb +4 -0
  73. data/test/test_ofx_version.rb +141 -0
  74. metadata +210 -0
@@ -0,0 +1,269 @@
1
+ # Copyright © 2007 Chris Guidry <chrisguidry@gmail.com>
2
+ #
3
+ # This file is part of OFX for Ruby.
4
+ #
5
+ # OFX for Ruby is free software; you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation; either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # OFX for Ruby is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module OFX
19
+ class CreditCardStatementMessageSet < MessageSet
20
+ def ofx_102_message_set_name
21
+ 'CREDITCARD'
22
+ end
23
+
24
+ def request_or_response_from_ofx_102_tag_name(response_or_request_name)
25
+ case response_or_request_name
26
+ when "CCSTMTTRNRQ" then CreditCardStatementRequest.new
27
+ when "CCSTMTTRNRS" then CreditCardStatementResponse.new
28
+ when "CCSTMTENDTRNRQ" then CreditCardClosingStatementRequest.new
29
+ when "CCSTMTENDTRNRS" then CreditCardClosingStatementResponse.new
30
+ else raise NotImplementedError, response_or_request_name
31
+ end
32
+ end
33
+
34
+ def self.from_ofx_102_hash(message_set_hash)
35
+ message_set = CreditCardStatementMessageSet.new
36
+
37
+ message_set_hash.each_pair() do |transaction_name, transaction_hash|
38
+ case transaction_name
39
+ when "CCSTMTTRNRQ" then message_set.requests << CreditCardStatementRequest.from_ofx_102_hash(transaction_hash)
40
+ when "CCSTMTTRNRS" then message_set.responses << CreditCardStatementResponse.from_ofx_102_hash(transaction_hash)
41
+ when "CCSTMTENDTRNRQ" then message_set.requests << CreditCardClosingStatementRequest.from_ofx_102_hash(transaction_hash)
42
+ when "CCSTMTENDTRNRS" then message_set.responses << CreditCardClosingStatementResponse.from_ofx_102_hash(transaction_hash)
43
+ else raise NotImplementedError, transaction_name
44
+ end
45
+ end
46
+
47
+ return message_set
48
+ end
49
+ end
50
+
51
+ class CreditCardStatementMessageSetProfile < MessageSetProfile
52
+ def self.from_ofx_102_hash(message_set_description_hash)
53
+ profile = OFX::CreditCardStatementMessageSetProfile.new
54
+ profile.message_set_class = OFX::CreditCardStatementMessageSet
55
+ profile.from_ofx_102_hash(message_set_description_hash)
56
+
57
+ profile.closing_statement_available = message_set_description_hash['CLOSINGAVAIL'] == 'Y'
58
+
59
+ profile
60
+ end
61
+ end
62
+
63
+ class CreditCardAccount
64
+ def to_ofx_102_request_body
65
+ " <CCACCTFROM>\n" +
66
+ " <ACCTID>#{account_identifier}\n" +
67
+
68
+ (account_key ? " <ACCTKEY>#{account_key}\n" : "") +
69
+
70
+ " </CCACCTFROM>\n"
71
+ end
72
+
73
+ def self.from_ofx_102_hash(account_hash)
74
+ account = OFX::BankingAccount.new
75
+
76
+ account.account_identifier = account_hash['ACCTID']
77
+ account.account_key = account_hash['ACCTKEY']
78
+
79
+ account
80
+ end
81
+ end
82
+
83
+ class CreditCardStatementRequest < TransactionalRequest
84
+ def ofx_102_name
85
+ 'CCSTMT'
86
+ end
87
+ def ofx_102_request_body
88
+ body = ""
89
+
90
+ body += account.to_ofx_102_request_body
91
+
92
+ body +=
93
+ " <INCTRAN>\n" +
94
+ " <INCLUDE>#{include_transactions.to_ofx_102_s}\n" if include_transactions
95
+
96
+ body +=
97
+ " <DTSTART>#{included_range.begin.to_ofx_102_s}\n" +
98
+ " <DTEND>#{included_range.end.to_ofx_102_s}\n" if included_range
99
+
100
+ body +=
101
+ " </INCTRAN>" if include_transactions
102
+
103
+ body
104
+ end
105
+
106
+ def self.from_ofx_102_hash(transaction_hash)
107
+ raise NotImplementedError
108
+ end
109
+ end
110
+
111
+ class CreditCardStatementResponse < TransactionalResponse
112
+ def ofx_102_name
113
+ 'CCSTMT'
114
+ end
115
+
116
+ def ofx_102_response_body
117
+ raise NotImplementedError
118
+ end
119
+
120
+ def self.from_ofx_102_hash(transaction_hash)
121
+ response = CreditCardStatementResponse.new
122
+
123
+ response.transaction_identifier = transaction_hash['TRNUID']
124
+ response.status = OFX::Status.from_ofx_102_hash(transaction_hash['STATUS'])
125
+
126
+ response_hash = transaction_hash['CCSTMTRS']
127
+ return unless response_hash
128
+
129
+ response.default_currency = response_hash['CURDEF']
130
+ response.account = CreditCardAccount.from_ofx_102_hash(response_hash['CCACCTFROM'])
131
+
132
+ response.marketing_information = response_hash['MKTGINFO']
133
+
134
+ response.ledger_balance = Balance.from_ofx_102_hash(response_hash['LEDGERBAL']) if response_hash['LEDGERBAL']
135
+ response.available_balance = Balance.from_ofx_102_hash(response_hash['AVAILBAL']) if response_hash['AVAILBAL']
136
+
137
+ transaction_list_hash = response_hash['BANKTRANLIST']
138
+ if (transaction_list_hash)
139
+ response.transaction_range = transaction_list_hash['DTSTART'].to_datetime..transaction_list_hash['DTEND'].to_datetime
140
+
141
+ response.transactions = []
142
+ transactions = transaction_list_hash['STMTTRN'] if transaction_list_hash['STMTTRN'].kind_of?(Array)
143
+ transactions = [transaction_list_hash['STMTTRN']] if transaction_list_hash['STMTTRN'].kind_of?(Hash)
144
+ transactions = [] unless transactions
145
+
146
+ transactions.each do |transaction_hash|
147
+ transaction = Transaction.new
148
+
149
+ transaction.transaction_type = Transaction.ofx_102_transaction_type_name_to_transaction_type(transaction_hash['TRNTYPE'])
150
+ transaction.date_posted = transaction_hash['DTPOSTED'].to_datetime if transaction_hash['DTPOSTED']
151
+ transaction.date_initiated = transaction_hash['DTUSER'].to_datetime if transaction_hash['DTUSER']
152
+ transaction.date_available = transaction_hash['DTAVAIL'].to_datetime if transaction_hash['DTAVAIL']
153
+
154
+ transaction.amount = transaction_hash['TRNAMT'].to_d
155
+ transaction.currency = transaction_hash['CURRENCY'] || transaction_hash['ORIGCURRENCY'] || response.default_currency
156
+
157
+ transaction.financial_institution_transaction_identifier = transaction_hash['FITID']
158
+ transaction.corrected_financial_institution_transaction_identifier = transaction_hash['CORRECTFITID']
159
+ transaction.correction_action = case transaction_hash['CORRECT_ACTION']
160
+ when 'REPLACE' then :replace
161
+ when 'DELETE' then :delete
162
+ end if transaction_hash['CORRECT_ACTION']
163
+ transaction.server_transaction_identifier = transaction_hash['SRVRTID']
164
+ transaction.check_number = transaction_hash['CHECKNUM']
165
+ transaction.reference_number = transaction_hash['REFNUM']
166
+
167
+ transaction.standard_industrial_code = transaction_hash['SIC']
168
+
169
+ transaction.payee_identifier = transaction_hash['PAYEEID']
170
+ if transaction_hash['PAYEE']
171
+ raise NotImplementedError
172
+ else
173
+ transaction.payee = transaction_hash['NAME']
174
+ end
175
+
176
+ if (transaction_hash['BANKACCTTO'])
177
+ transaction.transfer_destination_account = BankingAccount.from_ofx_102_hash(transaction_hash['BANKACCTTO'])
178
+ elsif (transaction_hash['CCACCTTO'])
179
+ transaction.transfer_destination_account = CreditCardAccount.from_ofx_102_hash(transaction_hash['CCACCTTO'])
180
+ end
181
+
182
+ transaction.memo = transaction_hash['MEMO']
183
+
184
+ response.transactions << transaction
185
+ end
186
+ end
187
+
188
+ response
189
+ end
190
+ end
191
+
192
+ class CreditCardClosingStatementRequest < TransactionalRequest
193
+ def ofx_102_name
194
+ 'CCSTMTEND'
195
+ end
196
+ def ofx_102_request_body
197
+ body = ""
198
+
199
+ body += account.to_ofx_102_request_body
200
+
201
+ body +=
202
+ " <DTSTART>#{statement_range.begin.to_ofx_102_s}\n" +
203
+ " <DTEND>#{statement_range.end.to_ofx_102_s}\n" if statement_range
204
+
205
+ body
206
+ end
207
+
208
+ def self.from_ofx_102_hash(transaction_hash)
209
+ raise NotImplementedError
210
+ end
211
+ end
212
+
213
+ class CreditCardClosingStatementResponse < TransactionalResponse
214
+ def ofx_102_name
215
+ 'CCSTMTEND'
216
+ end
217
+
218
+ def ofx_102_response_body
219
+ raise NotImplementedError
220
+ end
221
+
222
+ def self.from_ofx_102_hash(transaction_hash)
223
+ response = CreditCardClosingStatementResponse.new
224
+
225
+ response.transaction_identifier = transaction_hash['TRNUID']
226
+ response.status = OFX::Status.from_ofx_102_hash(transaction_hash['STATUS'])
227
+
228
+ response_hash = transaction_hash['CCSTMTENDRS']
229
+ return unless response_hash
230
+
231
+ response.default_currency = response_hash['CURDEF']
232
+ response.account = CreditCardAccount.from_ofx_102_hash(response_hash['CCACCTFROM'])
233
+
234
+ response.statements = []
235
+ closings = response_hash['CCCLOSING'] if response_hash['CCCLOSING'].kind_of?(Array)
236
+ closings = [response_hash['CCCLOSING']] if response_hash['CCCLOSING'].kind_of?(Hash)
237
+ closings = [] unless closings
238
+ closings.each do |closing_hash|
239
+ statement = CreditCardClosingStatement.new
240
+
241
+ statement.currency = closing_hash['CURRENCY'] || closing_hash['ORIGCURRENCY'] || response.default_currency
242
+
243
+ statement.finanical_institution_transaction_identifier = closing_hash['FITID']
244
+ statement.statement_range = closing_hash['DTOPEN'].to_date..closing_hash['DTCLOSE'].to_date
245
+ statement.next_statement_close = closing_hash['DTNEXT'].to_date if closing_hash['DTNEXT']
246
+
247
+ statement.opening_balance = closing_hash['BALOPEN'].to_d if closing_hash['BALOPEN']
248
+ statement.closing_balance = closing_hash['BALCLOSE'].to_d if closing_hash['BALCLOSE']
249
+
250
+ statement.payment_due_date = closing_hash['DTPMTDUE'].to_date if closing_hash['DTPMTDUE']
251
+ statement.minimum_payment_due = closing_hash['MINPMTDUE'].to_d if closing_hash['MINPMTDUE']
252
+
253
+ statement.finance_charge = closing_hash['FINCHG'].to_d if closing_hash['FINCHG']
254
+ statement.total_of_payments_and_charges = closing_hash['PAYANDCREDIT'].to_d if closing_hash['PAYANDCREDIT']
255
+ statement.total_of_purchases_and_advances = closing_hash['PURANDADV'].to_d if closing_hash['PURANDADV']
256
+ statement.debit_adjustements = closing_hash['DEBADJ'].to_d if closing_hash['DEBADJ']
257
+ statement.credit_limit = closing_hash['CREDITLIMIT'].to_d if closing_hash['CREDITLIMIT']
258
+
259
+ statement.transaction_range = closing_hash['DTPOSTSTART'].to_date..closing_hash['DTPOSTEND'].to_date
260
+
261
+ statement.marketing_information = closing_hash['MKTGINFO']
262
+
263
+ response.statements << statement
264
+ end
265
+
266
+ response
267
+ end
268
+ end
269
+ end
@@ -0,0 +1,34 @@
1
+ # Copyright © 2007 Chris Guidry <chrisguidry@gmail.com>
2
+ #
3
+ # This file is part of OFX for Ruby.
4
+ #
5
+ # OFX for Ruby is free software; you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation; either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # OFX for Ruby is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module OFX
19
+ class Document
20
+ def self.from_ofx_102_hash(document_hash)
21
+ contents = document_hash['OFX']
22
+ raise 'The top of the document was not an <OFX> tag.' unless contents
23
+
24
+ document = OFX::Document.new
25
+
26
+ contents.each_pair() do |message_set, message_set_hash|
27
+ document.message_sets << OFX::MessageSet.from_ofx_102_message_set_hash(message_set, message_set_hash)
28
+ end
29
+ document.message_sets.sort! { |left, right| left.precedence <=> right.precedence }
30
+
31
+ return document
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ # Copyright © 2007 Chris Guidry <chrisguidry@gmail.com>
2
+ #
3
+ # This file is part of OFX for Ruby.
4
+ #
5
+ # OFX for Ruby is free software; you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation; either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # OFX for Ruby is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ module OFX
19
+ class EmailMessageSet < MessageSet
20
+ end
21
+
22
+ class EmailMessageSetProfile < MessageSetProfile
23
+ def self.from_ofx_102_hash(message_set_description_hash)
24
+ profile = OFX::EmailMessageSetProfile.new
25
+ profile.message_set_class = OFX::EmailMessageSet
26
+ profile.from_ofx_102_hash(message_set_description_hash)
27
+
28
+ profile.supports_email = message_set_description_hash['MAILSUP'] == 'Y'
29
+ profile.supports_mime_messages = message_set_description_hash['GETMIMESUP'] == 'Y'
30
+
31
+ profile
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,155 @@
1
+ # Copyright © 2007 Chris Guidry <chrisguidry@gmail.com>
2
+ #
3
+ # This file is part of OFX for Ruby.
4
+ #
5
+ # OFX for Ruby is free software; you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation; either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # OFX for Ruby is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+
18
+ require 'uri'
19
+
20
+ module OFX
21
+ class FinancialInstitutionProfileMessageSet < MessageSet
22
+ def ofx_102_message_set_name
23
+ 'PROF'
24
+ end
25
+
26
+ def self.from_ofx_102_hash(message_set_hash)
27
+ message_set = FinancialInstitutionProfileMessageSet.new
28
+
29
+ message_set_hash.each_pair() do |transaction_name, transaction_hash|
30
+ case transaction_name
31
+ when "PROFTRNRQ" then message_set.requests << FinancialInstitutionProfileRequest.from_ofx_102_hash(transaction_hash)
32
+ when "PROFTRNRS" then message_set.responses << FinancialInstitutionProfileResponse.from_ofx_102_hash(transaction_hash)
33
+ else raise NotImplementedError, transaction_name
34
+ end
35
+ end
36
+
37
+ return message_set
38
+ end
39
+ end
40
+
41
+ class FinancialInstitutionProfileMessageSetProfile < MessageSetProfile
42
+ def self.from_ofx_102_hash(message_set_description_hash)
43
+ profile = OFX::FinancialInstitutionProfileMessageSetProfile.new
44
+ profile.message_set_class = OFX::FinancialInstitutionProfileMessageSet
45
+ profile.from_ofx_102_hash(message_set_description_hash)
46
+
47
+ profile
48
+ end
49
+ end
50
+
51
+ class FinancialInstitutionProfileRequest < TransactionalRequest
52
+ def ofx_102_name
53
+ 'PROF'
54
+ end
55
+ def ofx_102_request_body
56
+ " <CLIENTROUTING>#{client_routing}\n" +
57
+ " <DTPROFUP>#{date_of_last_profile_update.to_ofx_102_s}"
58
+ end
59
+
60
+ def self.from_ofx_102_hash(transaction_hash)
61
+ raise NotImplementedError
62
+ end
63
+ end
64
+
65
+ class FinancialInstitutionProfileResponse < TransactionalResponse
66
+ def ofx_102_name
67
+ 'PROF'
68
+ end
69
+
70
+ def ofx_102_response_body
71
+ raise NotImplementedError
72
+ end
73
+
74
+ def self.from_ofx_102_hash(transaction_hash)
75
+ response = FinancialInstitutionProfileResponse.new
76
+
77
+ response.transaction_identifier = transaction_hash['TRNUID']
78
+ response.status = OFX::Status.from_ofx_102_hash(transaction_hash['STATUS'])
79
+
80
+ response.message_sets = {}
81
+ response.signon_realms = []
82
+
83
+ response_hash = transaction_hash['PROFRS']
84
+ if response_hash
85
+ response.date_of_last_profile_update = response_hash['DTPROFUP'].to_datetime
86
+ response.financial_institution_name = response_hash['FINAME']
87
+ response.address = response_hash['ADDR1'] || ""
88
+ response.address += "\n" + response_hash['ADDR2'] if response_hash['ADDR2']
89
+ response.address += "\n" + response_hash['ADDR3'] if response_hash['ADDR3']
90
+ response.city = response_hash['CITY']
91
+ response.state = response_hash['STATE']
92
+ response.postal_code = response_hash['POSTALCODE']
93
+ response.country = response_hash['COUNTRY']
94
+ response.customer_service_telephone = response_hash['CSPHONE']
95
+ response.technical_support_telephone = response_hash['TSPHONE']
96
+ response.facsimile_telephone = response_hash['FAXPHONE']
97
+ response.url = URI.parse(response_hash['URL']) if response_hash['URL']
98
+ response.email_address = response_hash['EMAIL']
99
+
100
+ message_sets_hash = response_hash['MSGSETLIST']
101
+ #require 'pp'
102
+ #pp message_sets_hash
103
+ message_sets_hash.each_pair do |message_set_name, message_set_descriptions|
104
+ profile_class = MESSAGE_SET_NAMES_TO_PROFILE_CLASSES[message_set_name]
105
+ supported_messages = response.message_sets[profile_class.message_set_class] = {}
106
+ message_set_descriptions.each do |message_set_version_name, message_set_description|
107
+ supported_messages[OFX::Version.new(message_set_version_name[-1..-1].to_i)] =
108
+ profile_class.from_ofx_102_hash(message_set_description)
109
+ end
110
+ end
111
+
112
+ signons_hash = response_hash['SIGNONINFOLIST']
113
+ #require 'pp'
114
+ #pp signons_hash
115
+ signons_hash.each_pair do |aggregate_name, signon_realm_info|
116
+ signon_realm = SignonRealm.new
117
+
118
+ signon_realm.name = signon_realm_info['SIGNONREALM']
119
+ signon_realm.password_length_constraint = signon_realm_info['MIN'].to_i..signon_realm_info['MAX'].to_i
120
+ signon_realm.password_characters_constraint = case signon_realm_info['CHARTYPE']
121
+ when 'ALPHAONLY' then :alphabetic_only
122
+ when 'NUMERICONLY' then :numeric_only
123
+ when 'ALPHAORNUMERIC' then :alphabetic_or_numeric
124
+ when 'ALPHAANDNUMERIC' then :alphabetic_and_numeric
125
+ end
126
+ signon_realm.case_sensitive = signon_realm_info['CASESEN'] == 'Y'
127
+ signon_realm.allows_special_characters = signon_realm_info['SPECIAL'] == 'Y'
128
+ signon_realm.allows_spaces = signon_realm_info['SPACES'] == 'Y'
129
+ signon_realm.supports_pin_changes = signon_realm_info['PINCH'] == 'Y'
130
+ signon_realm.requires_initial_pin_change = signon_realm_info['CHGPINFIRST'] == 'Y'
131
+
132
+ response.signon_realms << signon_realm
133
+ end
134
+ end
135
+
136
+ return response
137
+ end
138
+
139
+ private
140
+ MESSAGE_SET_NAMES_TO_PROFILE_CLASSES =
141
+ {
142
+ 'SIGNONMSGSET' => OFX::SignonMessageSetProfile,
143
+ 'SIGNUPMSGSET' => OFX::SignupMessageSetProfile,
144
+ 'BANKMSGSET' => OFX::BankingMessageSetProfile,
145
+ 'CREDITCARDMSGSET' => OFX::CreditCardStatementMessageSetProfile,
146
+ 'INVSTMTMSGSET' => OFX::InvestmentStatementMessageSetProfile,
147
+ 'INTERXFERMSGSET' => OFX::InterbankFundsTransferMessageSetProfile,
148
+ 'WIREXFERMSGSET' => OFX::WireFundsTransferMessageSetProfile,
149
+ 'BILLPAYMSGSET' => OFX::PaymentMessageSetProfile,
150
+ 'EMAILMSGSET' => OFX::EmailMessageSetProfile,
151
+ 'SECLISTMSGSET' => OFX::InvestmentSecurityListMessageSetProfile,
152
+ 'PROFMSGSET' => OFX::FinancialInstitutionProfileMessageSetProfile
153
+ }
154
+ end
155
+ end