Skip to content

Commit

Permalink
Merge pull request #48 from BlackEdder/suntrust
Browse files Browse the repository at this point in the history
[RFC] Fix #42: Work with suntrust double column csv files
  • Loading branch information
BlackEdder committed Jul 7, 2015
2 parents 216de9b + 2330452 commit 3d2b1e5
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 24 deletions.
7 changes: 3 additions & 4 deletions lib/reckon/csv_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,9 @@ def merge_columns(a, b)
output_columns = []
columns.each_with_index do |column, index|
if index == a
new_column = []
column.each_with_index do |row, row_index|
new_column << row + " " + (columns[b][row_index] || '')
end
new_column = MoneyColumn.new( column )
.merge!( MoneyColumn.new( columns[b] ) )
.map { |m| m.amount.to_s }
output_columns << new_column
elsif index == b
# skip
Expand Down
32 changes: 26 additions & 6 deletions lib/reckon/money.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,30 @@ def pretty( negate = false )
end

def Money::from_s( value, options = {} )
return nil if value.empty?
# Empty string is treated as money with value 0
return Money.new( 0.00, options ) if value.empty?

# Remove 1000 separaters and replace , with . if comma_separates_cents
# 1.000,00 -> 1000.00
value = value.gsub(/\./, '').gsub(/,/, '.') if options[:comma_separates_cents]
amount = value.gsub(/[^\d\.]/, '').to_f
amount *= -1 if value =~ /[\(\-]/
Money.new( amount, options )
value = value.gsub(/,/, '')

money_format_regex = /^(.*?)(\d+\.\d\d)/ # Money has two decimal precision
any_number_regex = /^(.*?)([\d\.]+)/

# Prefer matching the money_format, match any number otherwise
m = value.match( money_format_regex ) ||
value.match( any_number_regex )
if m
amount = m[2].to_f
# Check whether the money had a - or (, which indicates negative amounts
if (m[1].match( /^[\(-]/ ) || m[1].match( /-$/ ))
amount *= -1
end
return Money.new( amount, options )
else
return nil
end
end

def Money::likelihood( entry )
Expand Down Expand Up @@ -79,11 +98,12 @@ def merge!( other_column )
invert = true if self.positive? && other_column.positive?
self.each_with_index do |mon, i|
other = other_column[i]
if mon && !other
return nil if (!mon || !other)
if mon != 0.00 && other == 0.0
if invert
self[i]= -mon
end
elsif !mon && other
elsif mon == 0.00 && other != 0.00
self[i] = other
else
return nil
Expand Down
22 changes: 12 additions & 10 deletions spec/reckon/csv_parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
@chase = Reckon::CSVParser.new(:string => CHASE_CSV)
@some_other_bank = Reckon::CSVParser.new(:string => SOME_OTHER_CSV)
@two_money_columns = Reckon::CSVParser.new(:string => TWO_MONEY_COLUMNS_BANK)
@suntrust_csv = Reckon::CSVParser.new(:string => SUNTRUST_CSV)
@simple_csv = Reckon::CSVParser.new(:string => SIMPLE_CSV)
@nationwide = Reckon::CSVParser.new( :string => NATIONWIDE_CSV, :csv_separator => ',', :suffixed => true, :currency => "POUND" )
@german_date = Reckon::CSVParser.new(:string => GERMAN_DATE_EXAMPLE)
Expand Down Expand Up @@ -65,6 +66,7 @@
@chase.money_column_indices.should == [3]
@some_other_bank.money_column_indices.should == [3]
@two_money_columns.money_column_indices.should == [3, 4]
@suntrust_csv.money_column_indices.should == [3, 4]
@nationwide.money_column_indices.should == [3, 4]
@harder_date_example_csv.money_column_indices.should == [1]
@danish_kroner_nordea.money_column_indices.should == [3]
Expand Down Expand Up @@ -221,16 +223,6 @@
end
end

describe "merge_columns" do
it "should work on adjacent columns" do
@simple_csv.merge_columns(0,1).should == [["entry1 entry2", "entry4 entry5"], ["entry3", "entry6"]]
end

it "should work on non-adjacent columns" do
@simple_csv.merge_columns(0,2).should == [["entry1 entry3", "entry4 entry6"], ["entry2", "entry5"]]
end
end

# Data

SIMPLE_CSV = "entry1,entry2,entry3\nentry4,entry5,entry6"
Expand Down Expand Up @@ -286,6 +278,16 @@
3/26/2008,Check - 0000000251,251,"","+$88.55","$1,298.57"
CSV

SUNTRUST_CSV = (<<-CSV).strip
11/01/2014,0, Deposit,0,500.00,500.00
11/02/2014,101,Check,100.00,0,400.00
11/03/2014,102,Check,100.00,0,300.00
11/04/2014,103,Check,100.00,0,200.00
11/05/2014,104,Check,100.00,0,100.00
11/06/2014,105,Check,100.00,0,0.00
11/17/2014,0, Deposit,0,700.00,700.00
CSV

NATIONWIDE_CSV = (<<-CSV).strip
07 Nov 2013,Bank credit,Bank credit,,£500.00,£500.00
09 Oct 2013,ATM Withdrawal,Withdrawal,£20.00,,£480.00
Expand Down
14 changes: 13 additions & 1 deletion spec/reckon/money_column_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,23 @@
Reckon::MoneyColumn.new( ["1.00", ""] ).merge!(
Reckon::MoneyColumn.new( ["", "-2.00"] ) ).should == [
Reckon::Money.new( 1.00 ), Reckon::Money.new( -2.00 ) ]
end
Reckon::MoneyColumn.new( ["1.00", "0"] ).merge!(
Reckon::MoneyColumn.new( ["0", "-2.00"] ) ).should == [
Reckon::Money.new( 1.00 ), Reckon::Money.new( -2.00 ) ]
Reckon::MoneyColumn.new( ["AB1.00C", ""] ).merge!(
Reckon::MoneyColumn.new( ["", "AB-2.00C"] ) ).should == [
Reckon::Money.new( 1.00 ), Reckon::Money.new( -2.00 ) ]
Reckon::MoneyColumn.new( ["AB1.00C", "AB0C"] ).merge!(
Reckon::MoneyColumn.new( ["AB0C", "AB-2.00C"] ) ).should == [
Reckon::Money.new( 1.00 ), Reckon::Money.new( -2.00 ) ]
end

it "should return nil if columns cannot be merged" do
Reckon::MoneyColumn.new( ["1.00", ""] ).merge!(
Reckon::MoneyColumn.new( ["1.00", "-2.00"] ) ).should == nil

Reckon::MoneyColumn.new( ["From1", "Names"] ).merge!(
Reckon::MoneyColumn.new( ["Acc", "NL28 INGB 1200 3244 16,21817"] ) ).should == nil
end

it "should invert first column if both positive" do
Expand Down
24 changes: 21 additions & 3 deletions spec/reckon/money_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,33 @@
Reckon::Money::from_s( "$-1025,67", :comma_separates_cents => true ).should == -1025.67
end

it "should return nil for an empty string" do
Reckon::Money::from_s( "" ).should == nil
Reckon::Money::from_s( "" ).should_not == 0
it "should return 0 for an empty string" do
Reckon::Money::from_s( "" ).should == 0
end

it "should handle 1000 indicators correctly" do
Reckon::Money::from_s( "$2.000,00", :comma_separates_cents => true ).should == 2000.00
Reckon::Money::from_s( "-$1,025.67" ).should == -1025.67
end

it "should keep numbers together" do
Reckon::Money::from_s( "1A1" ).should == 1
end

it "should prefer numbers with precision of two" do
Reckon::Money::from_s( "1A2.00" ).should == 2
Reckon::Money::from_s( "2.00A1" ).should == 2
end

it "should handle arbitrary prefixes and postfixes" do
Reckon::Money::from_s( "AB1.00C" ).should == 1
Reckon::Money::from_s( "AB0C" ).should == 0
Reckon::Money::from_s( "AB-2.00C" ).should == -2
end

it "should return nil if no numbers are found" do
Reckon::Money::from_s( "BAC" ).should == nil
end
end

describe "pretty" do
Expand Down

0 comments on commit 3d2b1e5

Please sign in to comment.