Skip to content

Commit

Permalink
Add alternate transaction downloading, and move csv transactions to t…
Browse files Browse the repository at this point in the history
…he simple_transactions method.
  • Loading branch information
aasmith committed Feb 8, 2009
1 parent 410e7e8 commit 2f6884a
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 3 deletions.
15 changes: 15 additions & 0 deletions lib/yodlee.rb
Expand Up @@ -2,10 +2,25 @@
require 'mechanize'
require 'nokogiri'
require 'enumerator'
require 'time'

begin
require 'johnson'
rescue LoadError
end

require 'yodlee/account'
require 'yodlee/connection'
require 'yodlee/credentials'
require 'yodlee/exceptions'
require 'yodlee/monkeypatches'
require 'yodlee/version'

module Yodlee
class Transaction < Struct.new(
:account_name, :account_id,
:currency, :amount, :date,
:fit_id, :status, :description
)
end
end
9 changes: 7 additions & 2 deletions lib/yodlee/account.rb
Expand Up @@ -4,16 +4,21 @@ class Account

def initialize(connection)
@connection = connection
@account_info = nil
@account_info, @transactions = nil
end

[:transactions, :current_balance, :account_info, :last_updated, :next_update].each do |m|
[:simple_transactions, :current_balance, :account_info, :last_updated, :next_update].each do |m|
define_method m do
@account_info = @connection.account_info(self) unless @account_info
m == :account_info ? @account_info : @account_info[m]
end
end

def transactions
return @transactions if @transactions
@transactions = @connection.transactions(self)
end

def to_s
"#{@institute_name} - #{@name}"
end
Expand Down
141 changes: 140 additions & 1 deletion lib/yodlee/connection.rb
Expand Up @@ -56,7 +56,7 @@ def account_info(acct)
account_info[:last_updated] = last_upd

csv_page = page.form_with(:name => 'rep').submit
account_info[:transactions] = csv_page.response['content-type'] =~ /csv/ ? csv_page.body : []
account_info[:simple_transactions] = csv_page.response['content-type'] =~ /csv/ ? csv_page.body : []

account_info
end
Expand All @@ -78,6 +78,145 @@ def investment_account_info(doc)
doc.search("h2[contains('Account Overview')] + table tr[last()] td[last()]").text
account_info
end

# This method returns each transaction as an object, based on the underyling javascript
# structures used to build the transactions as displayed in the Yodlee UI. These objects
# are able to access more information than the CSV Yodlee provides, such as the finanical
# institute's transaction id, useful for tracking duplicates.
#
# Calling this method requires Johnson to be installed, otherwise an exception is raised.
def transactions(acct)
unless Object.const_defined? "Johnson"
raise "Johnson not found. Install the johnson gem, or use simple_transactions instead."
end

post_headers = {
"c0-scriptName"=>"TxnService",
"c0-methodName"=>"searchTransactions",
"c0-id"=>"#{rand(5000)}_#{Time.now.to_i}#{Time.now.usec / 1000}}",
"c0-e1"=>"number:10000004",
"c0-e2"=>"string:17CBE222A42161A3FF450E47CF4C1A00",
"c0-e3"=>"null:null",
"c0-e4"=>"number:1",
"c0-e5"=>"boolean:false",
"c0-e6"=>"string:#{acct.id}",
"c0-e7"=>"string:-1",
"c0-e8"=>"null:null",
"c0-e9"=>"string:-1",
"c0-e10"=>"null:null",
"c0-e11"=>"null:null",
"c0-e12"=>"null:null",
"c0-e13"=>"string:-1",
"c0-e14"=>"null:null",
"c0-e15"=>"number:-1",
"c0-e16"=>"number:-1",
"c0-e17"=>"boolean:false",
"c0-e18"=>"Boolean:false",
"c0-e19"=>"boolean:false",
"c0-e20"=>"Boolean:false",
"c0-e21"=>"string:",
"c0-e22"=>"string:",
"c0-e23"=>"Boolean:false",
"c0-e24"=>"Boolean:false",
"c0-e25"=>"boolean:false",
"c0-e26"=>"Number:0",
"c0-e27"=>"string:0",
"c0-e28"=>"null:null",
"c0-e29"=>"null:null",
"c0-e30"=>"string:allTransactions",
"c0-e31"=>"string:InProgressAndCleared",
"c0-e32"=>"number:999",
"c0-e33"=>"string:",
"c0-e34"=>"null:null",
"c0-e35"=>"null:null",
"c0-e36"=>"string:",
"c0-e37"=>"null:null",
"c0-e38"=>"string:ALL",
"c0-e39"=>"string:false",
"c0-e40"=>"string:0.0",
"c0-e41"=>"string:0.0",

"c0-param0"=>"Object:{
cobrandId:reference:c0-e1,
applicationId:reference:c0-e2,
csit:reference:c0-e3,
loggingLevel:reference:c0-e4,
loggingEnabled:reference:c0-e5}",

"c0-param1"=>"Object:{
itemAccountId:reference:c0-e6,
categoryId:reference:c0-e7,
categoryLevelId:reference:c0-e8,
dateRangeId:reference:c0-e9,
fromDate:reference:c0-e10,
toDate:reference:c0-e11,
groupBy:reference:c0-e12,
groupAccountId:reference:c0-e13,
filterTranasctions:reference:c0-e14,
transactionTypeId:reference:c0-e15,
transactionStatusId:reference:c0-e16,
ignorePendingTransactions:reference:c0-e17,
includeBusinessExpense:reference:c0-e18,
includeTransfer:reference:c0-e19,
includeReimbursableExpense:refrence:c0-e20,
fromDate1:reference:c0-e21,
toDate1:reference:c0-e22,
includeMedicalExpense:reference:c0-e23,
includeTaxDeductible:reference:c0-e24,
includePersonalExpense:reference:c0-e25,
transactionAmount:reference:c0-e26,
transactionAmountRange:reference:c0-e27,
billStatementRange:reference:c0-e28,
criteria:reference:c0-e29,
module:reference:c0-e30,
transactionType:reference:c0-e31,
pageSize:reference:c0-e32,
sharedMemId:reference:c0-e33,
overRideDateRangeId:reference:c0-e34,
overRideContainer:referencec0-e35,
searchString:reference:c0-e36,
pageId:reference:c0-e37,
splitTypeTransaction:reference:c0-e38,
isAvailableBalance:reference:c0-e39,
currentBalance:reference:c0-e40,
availableBalance:reference:c0-e41}",

"c0-param2"=>"boolean:false",

"callCount"=>"1",
"xml"=>"true",
}
page = @agent.post(
'https://moneycenter.yodlee.com/moneycenter/dwr/exec/TxnService.searchTransactions.dwr',
post_headers
)

j = Johnson::Runtime.new

# Remove the last line (a call to DWREngine), and execute
j.evaluate page.body.strip.sub(/\n[^\n]+\Z/m, '')

if x = j['s0']
transactions = x.transactions.map do |e|
transaction = Yodlee::Transaction.new
transaction.account_name = e.accountName
transaction.currency = e.amount.cobCurrencyCode
transaction.amount = e.amount.cobPreciseAmount
transaction.description = e.description
transaction.account_id = e.itemAccountId
transaction.fit_id = e.transactionId
transaction.status = e['type']['type']

# Re-parse in order to get a real Time, not a Johnson::SpiderMonkey::RubyLandProxy.
transaction.date = Time.parse(e.date.to_s)
transaction
end

return transactions
end

return []
end

def handle_connection!
login unless connected?
Expand Down

0 comments on commit 2f6884a

Please sign in to comment.