Class: LloydsTSB::Customer

Inherits:
Object show all
Defined in:
lib/lloydstsb/customer.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(settings = {}) ⇒ Customer

Returns a new instance of Customer.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/lloydstsb/customer.rb', line 12

def initialize(settings = {})
  # Creates a new Customer object - expects a hash with keys :username,
  # :password and :memorable_word
  @agent = Mechanize.new
  @settings = settings

  if @settings[:username].blank? ||
    @settings[:password].blank? ||
    @settings[:memorable_word].blank?
      raise "You must provide a username, password and memorable word."
  end

  @agent.get "https://online.lloydstsb.co.uk/personal/logon/login.jsp?WT.ac=hpIBlogon"
  
  # Fill in the first authentication form then submits
  @agent.page.forms[0]["frmLogin:strCustomerLogin_userID"] = @settings[:username]
  @agent.page.forms[0]["frmLogin:strCustomerLogin_pwd"] = @settings[:password]
  @agent.page.forms[0].submit
  
  # Checks for any errors on the page indicating a failure to login
  if @agent.page.search('.formSubmitError').any?
    raise "There was a problem when submitting your username and password.
      (#{@agent.page.search('.formSubmitError').text})"
  end
  
  # Works out from the text on the page what characters from the memorable
  # word are required
  mc1 = @agent.page
    .at('//*[@id="frmentermemorableinformation1"]/fieldset/div/div/div[1]/label').text.split(" ")[1].to_i
  mc2 = @agent.page
    .at('//*[@id="frmentermemorableinformation1"]/fieldset/div/div/div[2]/label').text.split(" ")[1].to_i
  mc3 = @agent.page.
    at('//*[@id="frmentermemorableinformation1"]/fieldset/div/div/div[3]/label')
      .text.split(" ")[1].to_i
    
  # Files in the memorable word fields and logs in
  @agent.page.forms[0]["frmentermemorableinformation1:strEnterMemorableInformation_memInfo1"] = " " + @settings[:memorable_word][mc1-1]
  @agent.page.forms[0]["frmentermemorableinformation1:strEnterMemorableInformation_memInfo2"] = " " + @settings[:memorable_word][mc2-1]
  @agent.page.forms[0]["frmentermemorableinformation1:strEnterMemorableInformation_memInfo3"] = " " + @settings[:memorable_word][mc3-1]
  @agent.page.forms[0].click_button

  # Checks for any errors indicating a failure to login - the final hurdle
  if @agent.page.search('.formSubmitError').any?
    raise "There was a problem when submitting your memorable word.
      (#{@agent.page.search('.formSubmitError').text})"
  end
  
  @name = @agent.page.at('span.name').text
  
end

Instance Attribute Details

#agentObject (readonly)

Returns the value of attribute agent.



10
11
12
# File 'lib/lloydstsb/customer.rb', line 10

def agent
  @agent
end

#nameObject (readonly)

Returns the value of attribute name.



10
11
12
# File 'lib/lloydstsb/customer.rb', line 10

def name
  @name
end

Instance Method Details

#accountsObject



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/lloydstsb/customer.rb', line 63

def accounts
  # Fills in the relevant forms to login, gets account details and then
  # provides a response of accounts and transactions
  
  return @accounts if @accounts

  # We're in, now to find the accounts...
  accounts = []
  doc = Nokogiri::HTML(@agent.page.body, 'UTF-8')
   doc.css('li.clearfix').each do ||
    # This is an account in the table - let's read out the details...
    acct = {
      name: .css('a')[0].text,
      balance: .css('p.balance').text.split(" ")[1]
        .gsub("£", "").gsub(",", "").to_f,
      limit: .css('p.accountMsg').text.split(" ")[2]
        .gsub("£", "").gsub(",", "").to_f,
      transactions: []
      }

    # Now we need to find the recent transactions for the account...We'll
    # go to the account's transactions page and read the table
     = @agent.dup
    .get(.css('a')[0]['href'])
    
    # If there's a mention of "minimum payment" on the transactions page,
    # this is a credit card rather than a bank account
    if .page.body.include?("Minimum payment")
      acct[:type] = :credit_card
      acct[:details] = {
        card_number: .css('.numbers').text.gsub(" Card Number ", "")
      }
      Nokogiri::HTML(.page.body, 'UTF-8').css('tbody tr').each do |transaction|
        
        # Credit card statements start with the previous statement's
        # balance. We don't want to record this as a transaction.
        next if transaction.css('td')[1].text == "Balance from Previous Statement"
        
        # Let's get the data for the transaction...
        data = {
          date: Date.parse(transaction.css('td')[0].text),
          narrative: transaction.css('td')[1].text,
        }
        data[:amount] = transaction.css('td')[4].text.split(" ")[0].to_f
        
        # And now we work out whether the transaction was a credit or
        # debit by checking, in a round-about way, whether the
        # transaction amount contained "CR" (credit)
        if transaction.css('td')[4].text.split(" ").length > 1
          data[:type] = :credit
          data[:direction] = :credit
        else
          data[:type] = :debit
          data[:direction] = :debit
        end
        
        # And finally, we add the transaction object to the array
        acct[:transactions] << LloydsTSB::Transaction.new(data)
      end
    else
      # This is a bank account of some description
      acct[:type] = :bank_account
      details = .css('.numbers').text.gsub(" Sort Code", "").gsub("Account Number ", "").split(", ")
      acct[:details] = {
        sort_code: details[0],
        account_number: details[1]
      }
      Nokogiri::HTML(.page.body, 'UTF-8').css('tbody tr').each do |transaction|
        # Let's read the details from the table...
        data = {
          date: Date.parse(transaction.css('th.first').text),
          narrative: transaction.css('td')[0].text,
          type: transaction.css('td')[1].text.to_sym,
        }
        
        # Regardless of what the transaction is, there's an incoming
        # and an outgoing column. Let's work out which this is...
        incoming = transaction.css('td')[2].text
        out = transaction.css('td')[3].text
        if incoming == ""
          data[:direction] = :debit
          data[:amount] = out.to_f
        else
          data[:direction] = :credit
          data[:amount] = incoming.to_f
        end
        
        # To finish, we add the newly built transaction to the array
        acct[:transactions] << LloydsTSB::Transaction.new(data)
      end
    end

    accounts << LloydsTSB::Account.new(acct)
  end
  @accounts = accounts
  accounts
end