Class: Bankjob::Transaction

Inherits:
Object
  • Object
show all
Defined in:
lib/bankjob/transaction.rb

Overview

A Transaction object represents a transaction in a bank account (a withdrawal, deposit, transfer, etc) and is generally the result of running a Bankjob scraper.

A Scraper will create Transactions while scraping web pages in an online banking site. These Transactions will be collected in a Statement object which will then be written to a file.

A Transaction object knows how to write itself as a record in a CSV (Comma Separated Values) file using to_csv or as an XML element in an OFX (Open Financial eXchange www.ofx.net) file using to_ofx

Constant Summary collapse

CREDIT =

OFX transaction type for Generic credit

"CREDIT"
DEBIT =

OFX transaction type for Generic debit

"DEBIT"
INT =

OFX transaction type for Interest earned or paid. (Depends on signage of amount)

"INT"
DIV =

OFX transaction type for Dividend

"DIV"
FEE =

OFX transaction type for FI fee

"FEE"
SRVCHG =

OFX transaction type for Service charge

"SRVCHG"
DEP =

OFX transaction type for Deposit

"DEP"
ATM =

OFX transaction type for ATM debit or credit. (Depends on signage of amount)

"ATM"
POS =

OFX transaction type for Point of sale debit or credit. (Depends on signage of amount)

"POS"
XFER =

OFX transaction type for Transfer

"XFER"
CHECK =

OFX transaction type for Check

"CHECK"
PAYMENT =

OFX transaction type for Electronic payment

"PAYMENT"
CASH =

OFX transaction type for Cash withdrawal

"CASH"
DIRECTDEP =

OFX transaction type for Direct deposit

"DIRECTDEP"
DIRECTDEBIT =

OFX transaction type for Merchant initiated debit

"DIRECTDEBIT"
REPEATPMT =

OFX transaction type for Repeating payment/standing order

"REPEATPMT"
OTHER =

OFX transaction type for Other

"OTHER"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(decimal = ".") ⇒ Transaction

Creates a new Transaction with the specified attributes.



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/bankjob/transaction.rb', line 166

def initialize(decimal = ".")
  @ofx_id = nil
  @date = nil
  @value_date = nil
  @raw_description = nil
  @description = nil
  @amount = 0
  @new_balance = 0
  @decimal = decimal

  # Always create a Payee even if it doesn't get used - this ensures an empty
  # <PAYEE> element in the OFX output which is more correct and, for one thing,
  # stops Wesabe from adding UNKNOWN PAYEE to every transaction (even deposits)
  @payee = Payee.new()
  @check_number = nil
  @type = OTHER
end

Instance Attribute Details

#amountObject

amount of the credit or debit (negative for debits) Translates to OFX element TRNAMT



118
119
120
# File 'lib/bankjob/transaction.rb', line 118

def amount
  @amount
end

#check_numberObject

the cheque number of a cheque transaction This is of type Payee and translates to OFX element CHECKNUM



139
140
141
# File 'lib/bankjob/transaction.rb', line 139

def check_number
  @check_number
end

#dateObject

date of the transaction Translates to OFX element DTPOSTED



97
98
99
# File 'lib/bankjob/transaction.rb', line 97

def date
  @date
end

#descriptionObject

Returns the description, defaulting to the raw_description if no specific description has been set by the user.



108
109
110
# File 'lib/bankjob/transaction.rb', line 108

def description
  @description
end

#new_balanceObject

account balance after the transaction Not used in OFX but important for working out statement balances



122
123
124
# File 'lib/bankjob/transaction.rb', line 122

def new_balance
  @new_balance
end

#ofx_idObject

Creates a unique ID for the transaction for use in OFX documents, unless one has already been set. All OFX transactions need a unique identifier.

Note that this is generated by creating an MD5 digest of the transaction date, raw description, type, amount and new_balance. Which means that two identical transactions will always produce the same ofx_id. (This is important so that repeated scrapes of the same transaction value

produce identical ofx_id values)


131
132
133
# File 'lib/bankjob/transaction.rb', line 131

def ofx_id
  @ofx_id
end

#payeeObject

the payee of an expenditure (ie a debit or transfer) This is of type Payee and translates to complex OFX element PAYEE



135
136
137
# File 'lib/bankjob/transaction.rb', line 135

def payee
  @payee
end

#raw_descriptionObject

the original format of the description as scraped from the bank site This allows the raw information to be preserved when modifying the description with transaction rules (see Scraper#transaction_rule) This does not appear in the OFX output, only description does.



114
115
116
# File 'lib/bankjob/transaction.rb', line 114

def raw_description
  @raw_description
end

#real_amountObject (readonly)

Returns the Transaction amount attribute as a ruby Float after replacing the decimal separator with a . and stripping any other separators.



161
162
163
# File 'lib/bankjob/transaction.rb', line 161

def real_amount
  @real_amount
end

#real_new_balanceObject (readonly)

Returns the new balance after the transaction as a ruby Float after replacing the decimal separator with a . and stripping any other separators.



127
128
129
# File 'lib/bankjob/transaction.rb', line 127

def real_new_balance
  @real_new_balance
end

#typeObject

OFX type of the transaction (credit, debit, atm withdrawal, etc) Translates to the OFX element TRNTYPE and according to the OFX 2.0.3 schema this can be one of

  • CREDIT

  • DEBIT

  • INT

  • DIV

  • FEE

  • SRVCHG

  • DEP

  • ATM

  • POS

  • XFER

  • CHECK

  • PAYMENT

  • CASH

  • DIRECTDEP

  • DIRECTDEBIT

  • REPEATPMT

  • OTHER



93
94
95
# File 'lib/bankjob/transaction.rb', line 93

def type
  @type
end

#value_dateObject

the date the value affects the account (e.g. funds become available) Translates to OFX element DTUSER



101
102
103
# File 'lib/bankjob/transaction.rb', line 101

def value_date
  @value_date
end

Class Method Details

.csv_headerObject

Generates a string for use as a header in a CSV file for transactions. This will produce the following string:

date, value_date, description, real_amount, real_new_balance, amount, new_balance, raw_description, ofx_id



259
260
261
# File 'lib/bankjob/transaction.rb', line 259

def self.csv_header
  %w{ Date Value-Date Description Amount New-Balance Raw-Amount Raw-New-Balance Raw-Description OFX-ID }.to_csv
end

.from_csv(csv_row, decimal) ⇒ Object

Creates a new Transaction from a string that defines a row in a CSV file.

csv_row must hold an array of values in precisely this order:

date, value_date, description, real_amount, real_new_balance, amount, new_balance, raw_description, ofx_id

(The format should be the same as that produced by to_csv)



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/bankjob/transaction.rb', line 272

def self.from_csv(csv_row, decimal)
  if (csv_row.length != 9)  # must have 9 cols
    csv_lines = csv_row.join("\n\t")
    msg = "Failed to create Transaction from csv row: \n\t#{csv_lines}\n"
    msg << " - 9 columns are required in the form: date, value_date, "
    msg << "description, real_amount, real_new_balance, amount, new_balance, "
    msg << "raw_description, ofx_id"
    raise msg
  end
  tx = Transaction.new(decimal)
  tx.date, tx.value_date, tx.description = csv_row[0..2]
  # skip real_amount and real_new_balance, they're read only and calculated
  tx.amount, tx.new_balance, tx.raw_description, tx.ofx_id = csv_row[5..8]
  return tx
end

Instance Method Details

#==(other) ⇒ Object

Overrides == to allow comparison of Transaction objects so that they can be merged in Statements. See Statement#merge



362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/bankjob/transaction.rb', line 362

def ==(other) #:nodoc:
  if other.kind_of?(Transaction)
    # sometimes the same date, when written and read back will not appear equal so convert to 
    # a canonical string first
    return (Bankjob.date_time_to_ofx(@date) == Bankjob.date_time_to_ofx(other.date) and
        # ignore value date - it may be updated between statements
        # (consider using ofx_id here later)
        @raw_description == other.raw_description and
        @amount == other.amount and
        @type == other.type and
        @new_balance == other.new_balance)
  end
end

#eql?(other) ⇒ Boolean

Overrides eql? so that array union will work when merging statements

Returns:

  • (Boolean)


379
380
381
# File 'lib/bankjob/transaction.rb', line 379

def eql?(other) #:nodoc:
  return self == other
end

#hashObject

Overrides hash so that array union will work when merging statements



386
387
388
389
390
391
392
393
394
395
396
# File 'lib/bankjob/transaction.rb', line 386

def hash() #:nodoc:
  prime = 31;
  result = 1;
  result = prime * result + @amount.to_i
  result = prime * result + @new_balance.to_i
  result = prime * result + (@date.nil? ? 0 : Bankjob.date_time_to_ofx(@date).hash);
  result = prime * result + (@raw_description.nil? ? 0 : @raw_description.hash);
  result = prime * result + (@type.nil? ? 0 : @type.hash);
  # don't use value date
  return result;
end

#to_csvObject

Generates a string representing this Transaction as comma separated values in the form:

date, value_date, description, real_amount, real_new_balance, amount, new_balance, raw_description, ofx_id



243
244
245
246
247
248
249
250
251
# File 'lib/bankjob/transaction.rb', line 243

def to_csv
  # if there's a payee, prepend their name to the description - otherwise skip it
  if (not payee.nil? and (not payee.name.nil?))
    desc = payee.name + " - " + description
  else
    desc = description
  end
  [Bankjob.date_time_to_csv(date), Bankjob.date_time_to_csv(value_date), desc, real_amount, real_new_balance, amount, new_balance, raw_description, ofx_id].to_csv
end

#to_ofxObject

Generates an XML string adhering to the OFX standard (see Open Financial Exchange www.ofx.net) representing a single Transaction XML element.

The OFX 2 schema defines a STMTTRN (SatementTransaction) as follows:

<xsd:complexType name="StatementTransaction">
  <xsd:annotation>
    <xsd:documentation>
      The OFX element "STMTTRN" is of type "StatementTransaction"
    </xsd:documentation>
  </xsd:annotation>
  <xsd:sequence>
    <xsd:element name="TRNTYPE" type="ofx:TransactionEnum"/>
    <xsd:element name="DTPOSTED" type="ofx:DateTimeType"/>
    <xsd:element name="DTUSER" type="ofx:DateTimeType" minOccurs="0"/>
    <xsd:element name="DTAVAIL" type="ofx:DateTimeType" minOccurs="0"/>
    <xsd:element name="TRNAMT" type="ofx:AmountType"/>
    <xsd:element name="FITID" type="ofx:FinancialInstitutionTransactionIdType"/>
    <xsd:sequence minOccurs="0">
      <xsd:element name="CORRECTFITID" type="ofx:FinancialInstitutionTransactionIdType"/>
      <xsd:element name="CORRECTACTION" type="ofx:CorrectiveActionEnum"/>
    </xsd:sequence>
    <xsd:element name="SRVRTID" type="ofx:ServerIdType" minOccurs="0"/>
    <xsd:element name="CHECKNUM" type="ofx:CheckNumberType" minOccurs="0"/>
    <xsd:element name="REFNUM" type="ofx:ReferenceNumberType" minOccurs="0"/>
    <xsd:element name="SIC" type="ofx:StandardIndustryCodeType" minOccurs="0"/>
    <xsd:element name="PAYEEID" type="ofx:PayeeIdType" minOccurs="0"/>
    <xsd:choice minOccurs="0">
      <xsd:element name="NAME" type="ofx:GenericNameType"/>
      <xsd:element name="PAYEE" type="ofx:Payee"/>
    </xsd:choice>
    <xsd:choice minOccurs="0">
      <xsd:element name="BANKACCTTO" type="ofx:BankAccount"/>
      <xsd:element name="CCACCTTO" type="ofx:CreditCardAccount"/>
    </xsd:choice>
    <xsd:element name="MEMO" type="ofx:MessageType" minOccurs="0"/>
    <xsd:choice minOccurs="0">
      <xsd:element name="CURRENCY" type="ofx:Currency"/>
      <xsd:element name="ORIGCURRENCY" type="ofx:Currency"/>
    </xsd:choice>
    <xsd:element name="INV401KSOURCE" type="ofx:Investment401kSourceEnum" minOccurs="0"/>
  </xsd:sequence>
</xsd:complexType>


334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/bankjob/transaction.rb', line 334

def to_ofx
  buf = ""
  # Set margin=5 to indent it nicely within the output from Statement.to_ofx
  x = Builder::XmlMarkup.new(:target => buf, :indent => 2, :margin=>5)
  x.STMTTRN {	# transaction statement
    x.TRNTYPE type
    x.DTPOSTED Bankjob.date_time_to_ofx(date)	#Date transaction was posted to account, [datetime] yyyymmdd or yyyymmddhhmmss
    x.TRNAMT amount	#Ammount of transaction [amount] can be , or . separated
    x.FITID ofx_id
    x.CHECKNUM check_number unless check_number.nil?
    buf << payee.to_ofx unless payee.nil?
    #x.NAME description
    x.MEMO description
  }
  return buf
end

#to_sObject

Produces a string representation of the transaction



354
355
356
# File 'lib/bankjob/transaction.rb', line 354

def to_s
  "#{self.class} - ofx_id: #{@ofx_id}, date:#{@date}, raw description: #{@raw_description}, type: #{@type} amount: #{@amount}, new balance: #{@new_balance}"
end