reckon 0.5.1 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +50 -0
  3. data/.gitignore +2 -0
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +74 -0
  6. data/Gemfile.lock +1 -5
  7. data/README.md +72 -16
  8. data/Rakefile +17 -1
  9. data/lib/reckon.rb +2 -5
  10. data/lib/reckon/app.rb +145 -71
  11. data/lib/reckon/cosine_similarity.rb +92 -89
  12. data/lib/reckon/csv_parser.rb +67 -122
  13. data/lib/reckon/date_column.rb +10 -0
  14. data/lib/reckon/ledger_parser.rb +11 -1
  15. data/lib/reckon/logger.rb +4 -0
  16. data/lib/reckon/money.rb +52 -51
  17. data/lib/reckon/version.rb +1 -1
  18. data/reckon.gemspec +1 -2
  19. data/spec/data_fixtures/51-sample.csv +8 -0
  20. data/spec/data_fixtures/51-tokens.yml +9 -0
  21. data/spec/data_fixtures/85-date-example.csv +2 -0
  22. data/spec/integration/another_bank_example/input.csv +9 -0
  23. data/spec/integration/another_bank_example/output.ledger +36 -0
  24. data/spec/integration/another_bank_example/test_args +1 -0
  25. data/spec/integration/austrian_example/input.csv +13 -0
  26. data/spec/integration/austrian_example/output.ledger +52 -0
  27. data/spec/integration/austrian_example/test_args +2 -0
  28. data/spec/integration/bom_utf8_file/input.csv +3 -0
  29. data/spec/integration/bom_utf8_file/output.ledger +4 -0
  30. data/spec/integration/bom_utf8_file/test_args +3 -0
  31. data/spec/integration/broker_canada_example/input.csv +12 -0
  32. data/spec/integration/broker_canada_example/output.ledger +48 -0
  33. data/spec/integration/broker_canada_example/test_args +1 -0
  34. data/spec/integration/chase/account_tokens_and_regex/output.ledger +36 -0
  35. data/spec/integration/chase/account_tokens_and_regex/test_args +2 -0
  36. data/spec/integration/chase/account_tokens_and_regex/tokens.yml +16 -0
  37. data/spec/integration/chase/default_account_names/output.ledger +36 -0
  38. data/spec/integration/chase/default_account_names/test_args +3 -0
  39. data/spec/integration/chase/input.csv +9 -0
  40. data/spec/integration/chase/learn_from_existing/learn.ledger +7 -0
  41. data/spec/integration/chase/learn_from_existing/output.ledger +36 -0
  42. data/spec/integration/chase/learn_from_existing/test_args +1 -0
  43. data/spec/integration/chase/simple/output.ledger +36 -0
  44. data/spec/integration/chase/simple/test_args +1 -0
  45. data/spec/integration/danish_kroner_nordea_example/input.csv +6 -0
  46. data/spec/integration/danish_kroner_nordea_example/output.ledger +24 -0
  47. data/spec/integration/danish_kroner_nordea_example/test_args +1 -0
  48. data/spec/integration/english_date_example/input.csv +3 -0
  49. data/spec/integration/english_date_example/output.ledger +12 -0
  50. data/spec/integration/english_date_example/test_args +1 -0
  51. data/spec/integration/extratofake/input.csv +24 -0
  52. data/spec/integration/extratofake/output.ledger +92 -0
  53. data/spec/integration/extratofake/test_args +1 -0
  54. data/spec/integration/french_example/input.csv +9 -0
  55. data/spec/integration/french_example/output.ledger +36 -0
  56. data/spec/integration/french_example/test_args +2 -0
  57. data/spec/integration/german_date_example/input.csv +3 -0
  58. data/spec/integration/german_date_example/output.ledger +12 -0
  59. data/spec/integration/german_date_example/test_args +1 -0
  60. data/spec/integration/harder_date_example/input.csv +5 -0
  61. data/spec/integration/harder_date_example/output.ledger +20 -0
  62. data/spec/integration/harder_date_example/test_args +1 -0
  63. data/spec/integration/ing/input.csv +3 -0
  64. data/spec/integration/ing/output.ledger +12 -0
  65. data/spec/integration/ing/test_args +1 -0
  66. data/spec/integration/intuit_mint_example/input.csv +7 -0
  67. data/spec/integration/intuit_mint_example/output.ledger +28 -0
  68. data/spec/integration/intuit_mint_example/test_args +1 -0
  69. data/spec/integration/invalid_header_example/input.csv +6 -0
  70. data/spec/integration/invalid_header_example/output.ledger +8 -0
  71. data/spec/integration/invalid_header_example/test_args +1 -0
  72. data/spec/integration/inversed_credit_card/input.csv +16 -0
  73. data/spec/integration/inversed_credit_card/output.ledger +64 -0
  74. data/spec/integration/inversed_credit_card/test_args +1 -0
  75. data/spec/integration/nationwide/input.csv +4 -0
  76. data/spec/integration/nationwide/output.ledger +16 -0
  77. data/spec/integration/nationwide/test_args +1 -0
  78. data/spec/integration/regression/issue_51_account_tokens/input.csv +8 -0
  79. data/spec/integration/regression/issue_51_account_tokens/output.ledger +32 -0
  80. data/spec/integration/regression/issue_51_account_tokens/test_args +4 -0
  81. data/spec/integration/regression/issue_51_account_tokens/tokens.yml +9 -0
  82. data/spec/integration/regression/issue_64_date_column/input.csv +3 -0
  83. data/spec/integration/regression/issue_64_date_column/output.ledger +8 -0
  84. data/spec/integration/regression/issue_64_date_column/test_args +1 -0
  85. data/spec/integration/regression/issue_73_account_token_matching/input.csv +2 -0
  86. data/spec/integration/regression/issue_73_account_token_matching/output.ledger +4 -0
  87. data/spec/integration/regression/issue_73_account_token_matching/test_args +6 -0
  88. data/spec/integration/regression/issue_73_account_token_matching/tokens.yml +8 -0
  89. data/spec/integration/regression/issue_85_date_example/input.csv +2 -0
  90. data/spec/integration/regression/issue_85_date_example/output.ledger +8 -0
  91. data/spec/integration/regression/issue_85_date_example/test_args +1 -0
  92. data/spec/integration/spanish_date_example/input.csv +3 -0
  93. data/spec/integration/spanish_date_example/output.ledger +12 -0
  94. data/spec/integration/spanish_date_example/test_args +1 -0
  95. data/spec/integration/suntrust/input.csv +7 -0
  96. data/spec/integration/suntrust/output.ledger +28 -0
  97. data/spec/integration/suntrust/test_args +1 -0
  98. data/spec/integration/test.sh +82 -0
  99. data/spec/integration/test_money_column/input.csv +3 -0
  100. data/spec/integration/test_money_column/output.ledger +8 -0
  101. data/spec/integration/test_money_column/test_args +1 -0
  102. data/spec/integration/two_money_columns/input.csv +5 -0
  103. data/spec/integration/two_money_columns/output.ledger +20 -0
  104. data/spec/integration/two_money_columns/test_args +1 -0
  105. data/spec/integration/yyyymmdd_date_example/input.csv +1 -0
  106. data/spec/integration/yyyymmdd_date_example/output.ledger +4 -0
  107. data/spec/integration/yyyymmdd_date_example/test_args +1 -0
  108. data/spec/reckon/app_spec.rb +18 -2
  109. data/spec/reckon/csv_parser_spec.rb +129 -129
  110. data/spec/reckon/ledger_parser_spec.rb +42 -5
  111. data/spec/reckon/money_column_spec.rb +24 -24
  112. data/spec/reckon/money_spec.rb +36 -42
  113. data/spec/spec_helper.rb +19 -0
  114. metadata +97 -22
  115. data/.travis.yml +0 -13
@@ -0,0 +1,3 @@
1
+ "Date","Note","Amount"
2
+ "2012/3/22","DEPOSIT","50.00"
3
+ "2012/3/23","TRANSFER TO SAVINGS","-10.00"
@@ -0,0 +1,8 @@
1
+ 2012-03-22 DEPOSIT
2
+ Assets:Bank:Checking $50.00
3
+ Income:Unknown
4
+
5
+ 2012-03-23 TRANSFER TO SAVINGS
6
+ Expenses:Unknown
7
+ Assets:Bank:Checking -$10.00
8
+
@@ -0,0 +1 @@
1
+ -f input.csv --unattended --account Assets:Bank:Checking
@@ -0,0 +1,5 @@
1
+ 4/1/2008,Check - 0000000122,122,-$76.00,"","$1,750.06"
2
+ 3/28/2008,BLARG R SH 456930,"","",+$327.49,"$1,826.06"
3
+ 3/27/2008,Check - 0000000112,112,-$800.00,"","$1,498.57"
4
+ 3/26/2008,Check - 0000000251,251,-$88.55,"","$1,298.57"
5
+ 3/26/2008,Check - 0000000251,251,"","+$88.55","$1,298.57"
@@ -0,0 +1,20 @@
1
+ 2008-03-26 Check - 0000000251; 251; $1,298.57
2
+ Assets:Bank:Checking $88.55
3
+ Income:Unknown
4
+
5
+ 2008-03-26 Check - 0000000251; 251; $1,298.57
6
+ Income:Unknown
7
+ Assets:Bank:Checking -$88.55
8
+
9
+ 2008-03-27 Check - 0000000112; 112; $1,498.57
10
+ Income:Unknown
11
+ Assets:Bank:Checking -$800.00
12
+
13
+ 2008-03-28 BLARG R SH 456930; $1,826.06
14
+ Assets:Bank:Checking $327.49
15
+ Income:Unknown
16
+
17
+ 2008-04-01 Check - 0000000122; 122; $1,750.06
18
+ Income:Unknown
19
+ Assets:Bank:Checking -$76.00
20
+
@@ -0,0 +1 @@
1
+ -f input.csv --unattended --account Assets:Bank:Checking
@@ -0,0 +1 @@
1
+ DEBIT,20121231,"ODESK***BAL-27DEC12 650-12345 CA 12/28",-123.45
@@ -0,0 +1,4 @@
1
+ 2012-12-31 DEBIT; ODESK***BAL-27DEC12 650-12345 CA 12/28
2
+ Expenses:Unknown
3
+ Assets:Bank:Checking -$123.45
4
+
@@ -0,0 +1 @@
1
+ -f input.csv --unattended --account Assets:Bank:Checking
@@ -8,7 +8,7 @@ describe Reckon::App do
8
8
  context 'with chase csv input' do
9
9
  before do
10
10
  @chase = Reckon::App.new(string: BANK_CSV)
11
- @chase.learn_from(BANK_LEDGER)
11
+ @chase.learn_from_ledger(BANK_LEDGER)
12
12
  @rows = []
13
13
  @chase.each_row_backwards { |row| @rows.push(row) }
14
14
  end
@@ -68,7 +68,7 @@ describe Reckon::App do
68
68
  end
69
69
 
70
70
  it 'should learn from a ledger file' do
71
- chase.learn_from( BANK_LEDGER )
71
+ chase.learn_from_ledger(BANK_LEDGER)
72
72
  chase.walk_backwards
73
73
  output_file.string.scan('Expenses:Books').count.should == 1
74
74
  end
@@ -121,6 +121,22 @@ describe Reckon::App do
121
121
  end
122
122
  end
123
123
 
124
+ context 'Issue #51 - regression test' do
125
+ it 'should assign correct accounts with tokens' do
126
+ output = StringIO.new
127
+ Reckon::App.new(
128
+ file: fixture_path('51-sample.csv'),
129
+ unattended: true,
130
+ account_tokens_file: fixture_path('51-tokens.yml'),
131
+ ignore_columns: [5],
132
+ bank_account: 'Assets:Chequing',
133
+ output_file: output
134
+ ).walk_backwards
135
+ expect(output.string).not_to include('Income:Unknown')
136
+ expect(output.string.scan('Expenses:Dining:Resturant').size).to eq(8)
137
+ end
138
+ end
139
+
124
140
  #DATA
125
141
  BANK_CSV = (<<-CSV).strip
126
142
  DEBIT,20091224120000[0:GMT],"HOST 037196321563 MO 12/22SLICEHOST",-85.00
@@ -5,38 +5,29 @@ require_relative "../spec_helper"
5
5
  require 'rubygems'
6
6
  require_relative '../../lib/reckon'
7
7
 
8
- Reckon::CSVParser.settings[:testing] = true
9
-
10
8
  describe Reckon::CSVParser do
11
- before do
12
- @chase = Reckon::CSVParser.new(file: fixture_path('chase.csv'))
13
- @some_other_bank = Reckon::CSVParser.new(file: fixture_path('some_other.csv'))
14
- @two_money_columns = Reckon::CSVParser.new(file: fixture_path('two_money_columns.csv'))
15
- @suntrust_csv = Reckon::CSVParser.new(file: fixture_path('suntrust.csv'))
16
- @simple_csv = Reckon::CSVParser.new(file: fixture_path('simple.csv'))
17
- @nationwide = Reckon::CSVParser.new(file: fixture_path('nationwide.csv'), csv_separator: ',', suffixed: true, currency: "POUND")
18
- @german_date = Reckon::CSVParser.new(file: fixture_path('german_date_example.csv'))
19
- @danish_kroner_nordea = Reckon::CSVParser.new(file: fixture_path('danish_kroner_nordea_example.csv'), csv_separator: ';', comma_separates_cents: true)
20
- @yyyymmdd_date = Reckon::CSVParser.new(file: fixture_path('yyyymmdd_date_example.csv'))
21
- @spanish_date = Reckon::CSVParser.new(file: fixture_path('spanish_date_example.csv'), date_format: '%d/%m/%Y')
22
- @english_date = Reckon::CSVParser.new(file: fixture_path('english_date_example.csv'))
23
- @ing_csv = Reckon::CSVParser.new(file: fixture_path('ing.csv'), comma_separates_cents: true )
24
- @austrian_csv = Reckon::CSVParser.new(file: fixture_path('austrian_example.csv'), comma_separates_cents: true, csv_separator: ';' )
25
- @french_csv = Reckon::CSVParser.new(file: fixture_path('french_example.csv'), csv_separator: ';', comma_separates_cents: true)
26
- @broker_canada = Reckon::CSVParser.new(file: fixture_path('broker_canada_example.csv'))
27
- @intuit_mint = Reckon::CSVParser.new(file: fixture_path('intuit_mint_example.csv'))
28
- end
29
-
30
- it "should be in testing mode" do
31
- @chase.settings[:testing].should be true
32
- Reckon::CSVParser.settings[:testing].should be true
33
- end
9
+ let(:chase) { Reckon::CSVParser.new(file: fixture_path('chase.csv')) }
10
+ let(:some_other_bank) { Reckon::CSVParser.new(file: fixture_path('some_other.csv')) }
11
+ let(:two_money_columns) { Reckon::CSVParser.new(file: fixture_path('two_money_columns.csv')) }
12
+ let(:suntrust_csv) { Reckon::CSVParser.new(file: fixture_path('suntrust.csv')) }
13
+ let(:simple_csv) { Reckon::CSVParser.new(file: fixture_path('simple.csv')) }
14
+ let(:nationwide) { Reckon::CSVParser.new(file: fixture_path('nationwide.csv'), csv_separator: ',', suffixed: true, currency: "POUND") }
15
+ let(:german_date) { Reckon::CSVParser.new(file: fixture_path('german_date_example.csv')) }
16
+ let(:danish_kroner_nordea) { Reckon::CSVParser.new(file: fixture_path('danish_kroner_nordea_example.csv'), csv_separator: ';', comma_separates_cents: true) }
17
+ let(:yyyymmdd_date) { Reckon::CSVParser.new(file: fixture_path('yyyymmdd_date_example.csv')) }
18
+ let(:spanish_date) { Reckon::CSVParser.new(file: fixture_path('spanish_date_example.csv'), date_format: '%d/%m/%Y') }
19
+ let(:english_date) { Reckon::CSVParser.new(file: fixture_path('english_date_example.csv')) }
20
+ let(:ing_csv) { Reckon::CSVParser.new(file: fixture_path('ing.csv'), comma_separates_cents: true ) }
21
+ let(:austrian_csv) { Reckon::CSVParser.new(file: fixture_path('austrian_example.csv'), comma_separates_cents: true, csv_separator: ';' ) }
22
+ let(:french_csv) { Reckon::CSVParser.new(file: fixture_path('french_example.csv'), csv_separator: ';', comma_separates_cents: true) }
23
+ let(:broker_canada) { Reckon::CSVParser.new(file: fixture_path('broker_canada_example.csv')) }
24
+ let(:intuit_mint) { Reckon::CSVParser.new(file: fixture_path('intuit_mint_example.csv')) }
34
25
 
35
26
  describe "parse" do
36
27
  it "should use binary encoding if none specified and chardet fails" do
37
28
  allow(CharDet).to receive(:detect).and_return({'encoding' => nil})
38
29
  app = Reckon::CSVParser.new(file: fixture_path("extratofake.csv"))
39
- expect(app.try_encoding("foobarbaz")).to eq("BINARY")
30
+ expect(app.send(:try_encoding, "foobarbaz")).to eq("BINARY")
40
31
  end
41
32
 
42
33
  it "should work with foreign character encodings" do
@@ -76,8 +67,8 @@ describe Reckon::CSVParser do
76
67
 
77
68
  describe "columns" do
78
69
  it "should return the csv transposed" do
79
- @simple_csv.columns.should == [["entry1", "entry4"], ["entry2", "entry5"], ["entry3", "entry6"]]
80
- @chase.columns.length.should == 4
70
+ simple_csv.columns.should == [["entry1", "entry4"], ["entry2", "entry5"], ["entry3", "entry6"]]
71
+ chase.columns.length.should == 4
81
72
  end
82
73
 
83
74
  it "should be ok with empty lines" do
@@ -88,46 +79,44 @@ describe Reckon::CSVParser do
88
79
  end
89
80
 
90
81
  describe "detect_columns" do
91
- before do
92
- @harder_date_example_csv = Reckon::CSVParser.new(file: fixture_path('harder_date_example.csv'))
93
- end
82
+ let(:harder_date_example_csv) { Reckon::CSVParser.new(file: fixture_path('harder_date_example.csv')) }
94
83
 
95
84
  it "should detect the money column" do
96
- @chase.money_column_indices.should == [3]
97
- @some_other_bank.money_column_indices.should == [3]
98
- @two_money_columns.money_column_indices.should == [3, 4]
99
- @suntrust_csv.money_column_indices.should == [3, 4]
100
- @nationwide.money_column_indices.should == [3, 4]
101
- @harder_date_example_csv.money_column_indices.should == [1]
102
- @danish_kroner_nordea.money_column_indices.should == [3]
103
- @yyyymmdd_date.money_column_indices.should == [3]
104
- @ing_csv.money_column_indices.should == [6]
105
- @austrian_csv.money_column_indices.should == [4]
106
- @french_csv.money_column_indices.should == [4]
107
- @broker_canada.money_column_indices.should == [8]
108
- @intuit_mint.money_column_indices.should == [3]
85
+ chase.money_column_indices.should == [3]
86
+ some_other_bank.money_column_indices.should == [3]
87
+ two_money_columns.money_column_indices.should == [3, 4]
88
+ suntrust_csv.money_column_indices.should == [3, 4]
89
+ nationwide.money_column_indices.should == [3, 4]
90
+ harder_date_example_csv.money_column_indices.should == [1]
91
+ danish_kroner_nordea.money_column_indices.should == [3]
92
+ yyyymmdd_date.money_column_indices.should == [3]
93
+ ing_csv.money_column_indices.should == [6]
94
+ austrian_csv.money_column_indices.should == [4]
95
+ french_csv.money_column_indices.should == [4]
96
+ broker_canada.money_column_indices.should == [8]
97
+ intuit_mint.money_column_indices.should == [3]
109
98
  end
110
99
 
111
100
  it "should detect the date column" do
112
- @chase.date_column_index.should == 1
113
- @some_other_bank.date_column_index.should == 1
114
- @two_money_columns.date_column_index.should == 0
115
- @harder_date_example_csv.date_column_index.should == 0
116
- @danish_kroner_nordea.date_column_index.should == 0
117
- @yyyymmdd_date.date_column_index.should == 1
118
- @french_csv.date_column_index.should == 1
119
- @broker_canada.date_column_index.should == 0
120
- @intuit_mint.date_column_index.should == 0
101
+ chase.date_column_index.should == 1
102
+ some_other_bank.date_column_index.should == 1
103
+ two_money_columns.date_column_index.should == 0
104
+ harder_date_example_csv.date_column_index.should == 0
105
+ danish_kroner_nordea.date_column_index.should == 0
106
+ yyyymmdd_date.date_column_index.should == 1
107
+ french_csv.date_column_index.should == 1
108
+ broker_canada.date_column_index.should == 0
109
+ intuit_mint.date_column_index.should == 0
121
110
  Reckon::CSVParser.new(:string => '2014-01-13,"22211100000",-10').date_column_index.should == 0
122
111
  end
123
112
 
124
113
  it "should consider all other columns to be description columns" do
125
- @chase.description_column_indices.should == [0, 2]
126
- @some_other_bank.description_column_indices.should == [0, 2]
127
- @two_money_columns.description_column_indices.should == [1, 2, 5]
128
- @harder_date_example_csv.description_column_indices.should == [2, 3, 4, 5, 6, 7]
129
- @danish_kroner_nordea.description_column_indices.should == [1, 2, 4]
130
- @yyyymmdd_date.description_column_indices.should == [0, 2]
114
+ chase.description_column_indices.should == [0, 2]
115
+ some_other_bank.description_column_indices.should == [0, 2]
116
+ two_money_columns.description_column_indices.should == [1, 2, 5]
117
+ harder_date_example_csv.description_column_indices.should == [2, 3, 4, 5, 6, 7]
118
+ danish_kroner_nordea.description_column_indices.should == [1, 2, 4]
119
+ yyyymmdd_date.description_column_indices.should == [0, 2]
131
120
  end
132
121
  end
133
122
 
@@ -143,36 +132,36 @@ describe Reckon::CSVParser do
143
132
 
144
133
  describe "money_for" do
145
134
  it "should return the appropriate fields" do
146
- @chase.money_for(1).should == -20
147
- @chase.money_for(4).should == 1558.52
148
- @chase.money_for(7).should == -116.22
149
- @some_other_bank.money_for(1).should == -20
150
- @some_other_bank.money_for(4).should == 1558.52
151
- @some_other_bank.money_for(7).should == -116.22
152
- @two_money_columns.money_for(0).should == -76
153
- @two_money_columns.money_for(1).should == 327.49
154
- @two_money_columns.money_for(2).should == -800
155
- @two_money_columns.money_for(3).should == -88.55
156
- @two_money_columns.money_for(4).should == 88.55
157
- @nationwide.money_for(0).should == 500.00
158
- @nationwide.money_for(1).should == -20.00
159
- @danish_kroner_nordea.money_for(0).should == -48.00
160
- @danish_kroner_nordea.money_for(1).should == -79.00
161
- @danish_kroner_nordea.money_for(2).should == 497.90
162
- @danish_kroner_nordea.money_for(3).should == -995.00
163
- @danish_kroner_nordea.money_for(4).should == -3452.90
164
- @danish_kroner_nordea.money_for(5).should == -655.00
165
- @yyyymmdd_date.money_for(0).should == -123.45
166
- @ing_csv.money_for(0).should == -136.13
167
- @ing_csv.money_for(1).should == 375.00
168
- @austrian_csv.money_for(0).should == -18.00
169
- @austrian_csv.money_for(2).should == 120.00
170
- @french_csv.money_for(0).should == -10.00
171
- @french_csv.money_for(1).should == -5.76
172
- @broker_canada.money_for(0).should == 12.55
173
- @broker_canada.money_for(1).should == -81.57
174
- @intuit_mint.money_for(0).should == 0.01
175
- @intuit_mint.money_for(1).should == -331.63
135
+ chase.money_for(1).should == -20
136
+ chase.money_for(4).should == 1558.52
137
+ chase.money_for(7).should == -116.22
138
+ some_other_bank.money_for(1).should == -20
139
+ some_other_bank.money_for(4).should == 1558.52
140
+ some_other_bank.money_for(7).should == -116.22
141
+ two_money_columns.money_for(0).should == -76
142
+ two_money_columns.money_for(1).should == 327.49
143
+ two_money_columns.money_for(2).should == -800
144
+ two_money_columns.money_for(3).should == -88.55
145
+ two_money_columns.money_for(4).should == 88.55
146
+ nationwide.money_for(0).should == 500.00
147
+ nationwide.money_for(1).should == -20.00
148
+ danish_kroner_nordea.money_for(0).should == -48.00
149
+ danish_kroner_nordea.money_for(1).should == -79.00
150
+ danish_kroner_nordea.money_for(2).should == 497.90
151
+ danish_kroner_nordea.money_for(3).should == -995.00
152
+ danish_kroner_nordea.money_for(4).should == -3452.90
153
+ danish_kroner_nordea.money_for(5).should == -655.00
154
+ yyyymmdd_date.money_for(0).should == -123.45
155
+ ing_csv.money_for(0).should == -136.13
156
+ ing_csv.money_for(1).should == 375.00
157
+ austrian_csv.money_for(0).should == -18.00
158
+ austrian_csv.money_for(2).should == 120.00
159
+ french_csv.money_for(0).should == -10.00
160
+ french_csv.money_for(1).should == -5.76
161
+ broker_canada.money_for(0).should == 12.55
162
+ broker_canada.money_for(1).should == -81.57
163
+ intuit_mint.money_for(0).should == 0.01
164
+ intuit_mint.money_for(1).should == -331.63
176
165
  end
177
166
 
178
167
  it "should handle the comma_separates_cents option correctly" do
@@ -200,59 +189,63 @@ describe Reckon::CSVParser do
200
189
 
201
190
  describe "date_for" do
202
191
  it "should return a parsed date object" do
203
- @chase.date_for(1).year.should == Time.parse("2009/12/24").year
204
- @chase.date_for(1).month.should == Time.parse("2009/12/24").month
205
- @chase.date_for(1).day.should == Time.parse("2009/12/24").day
206
- @some_other_bank.date_for(1).year.should == Time.parse("2010/12/24").year
207
- @some_other_bank.date_for(1).month.should == Time.parse("2010/12/24").month
208
- @some_other_bank.date_for(1).day.should == Time.parse("2010/12/24").day
209
- @german_date.date_for(1).year.should == Time.parse("2009/12/24").year
210
- @german_date.date_for(1).month.should == Time.parse("2009/12/24").month
211
- @german_date.date_for(1).day.should == Time.parse("2009/12/24").day
212
- @danish_kroner_nordea.date_for(0).year.should == Time.parse("2012/11/16").year
213
- @danish_kroner_nordea.date_for(0).month.should == Time.parse("2012/11/16").month
214
- @danish_kroner_nordea.date_for(0).day.should == Time.parse("2012/11/16").day
215
- @yyyymmdd_date.date_for(0).year.should == Time.parse("2012/12/31").year
216
- @yyyymmdd_date.date_for(0).month.should == Time.parse("2012/12/31").month
217
- @yyyymmdd_date.date_for(0).day.should == Time.parse("2012/12/31").day
218
- @spanish_date.date_for(1).year.should == Time.parse("2009/12/02").year
219
- @spanish_date.date_for(1).month.should == Time.parse("2009/12/02").month
220
- @spanish_date.date_for(1).day.should == Time.parse("2009/12/02").day
221
- @english_date.date_for(1).year.should == Time.parse("2009/12/24").year
222
- @english_date.date_for(1).month.should == Time.parse("2009/12/24").month
223
- @english_date.date_for(1).day.should == Time.parse("2009/12/24").day
224
- @nationwide.date_for(1).month.should == 10
225
- @ing_csv.date_for(1).month.should == Time.parse("2012/11/12").month
226
- @ing_csv.date_for(1).day.should == Time.parse("2012/11/12").day
227
- @broker_canada.date_for(5).year.should == 2014
228
- @broker_canada.date_for(5).month.should == 1
229
- @broker_canada.date_for(5).day.should == 7
230
- @intuit_mint.date_for(1).year.should == 2014
231
- @intuit_mint.date_for(1).month.should == 2
232
- @intuit_mint.date_for(1).day.should == 3
192
+ chase.date_for(1).year.should == Time.parse("2009/12/24").year
193
+ chase.date_for(1).month.should == Time.parse("2009/12/24").month
194
+ chase.date_for(1).day.should == Time.parse("2009/12/24").day
195
+ some_other_bank.date_for(1).year.should == Time.parse("2010/12/24").year
196
+ some_other_bank.date_for(1).month.should == Time.parse("2010/12/24").month
197
+ some_other_bank.date_for(1).day.should == Time.parse("2010/12/24").day
198
+ german_date.date_for(1).year.should == Time.parse("2009/12/24").year
199
+ german_date.date_for(1).month.should == Time.parse("2009/12/24").month
200
+ german_date.date_for(1).day.should == Time.parse("2009/12/24").day
201
+ danish_kroner_nordea.date_for(0).year.should == Time.parse("2012/11/16").year
202
+ danish_kroner_nordea.date_for(0).month.should == Time.parse("2012/11/16").month
203
+ danish_kroner_nordea.date_for(0).day.should == Time.parse("2012/11/16").day
204
+ yyyymmdd_date.date_for(0).year.should == Time.parse("2012/12/31").year
205
+ yyyymmdd_date.date_for(0).month.should == Time.parse("2012/12/31").month
206
+ yyyymmdd_date.date_for(0).day.should == Time.parse("2012/12/31").day
207
+ spanish_date.date_for(1).year.should == Time.parse("2009/12/02").year
208
+ spanish_date.date_for(1).month.should == Time.parse("2009/12/02").month
209
+ spanish_date.date_for(1).day.should == Time.parse("2009/12/02").day
210
+ english_date.date_for(1).year.should == Time.parse("2009/12/24").year
211
+ english_date.date_for(1).month.should == Time.parse("2009/12/24").month
212
+ english_date.date_for(1).day.should == Time.parse("2009/12/24").day
213
+ nationwide.date_for(1).month.should == 10
214
+ ing_csv.date_for(1).month.should == Time.parse("2012/11/12").month
215
+ ing_csv.date_for(1).day.should == Time.parse("2012/11/12").day
216
+ broker_canada.date_for(5).year.should == 2014
217
+ broker_canada.date_for(5).month.should == 1
218
+ broker_canada.date_for(5).day.should == 7
219
+ intuit_mint.date_for(1).year.should == 2014
220
+ intuit_mint.date_for(1).month.should == 2
221
+ intuit_mint.date_for(1).day.should == 3
233
222
  end
234
223
  end
235
224
 
236
225
  describe "description_for" do
237
226
  it "should return the combined fields that are not money for date fields" do
238
- @chase.description_for(1).should == "CHECK; CHECK 2656"
239
- @chase.description_for(7).should == "CREDIT; PAYPAL TRANSFER PPD ID: PAYPALSDSL"
227
+ chase.description_for(1).should == "CHECK; CHECK 2656"
228
+ chase.description_for(7).should == "CREDIT; PAYPAL TRANSFER PPD ID: PAYPALSDSL"
240
229
  end
241
230
 
242
231
  it "should not append empty description column" do
243
232
  parser = Reckon::CSVParser.new(:string => '01/09/2015,05354 SUBWAY,8.19,,',:date_format => '%d/%m/%Y')
244
- parser.description_column_indices.should == [1, 4]
245
233
  parser.description_for(0).should == '05354 SUBWAY'
246
234
  end
235
+
236
+ it "should handle nil description" do
237
+ parser = Reckon::CSVParser.new(string: '2015-09-01,test,3.99')
238
+ expect(parser.description_for(1)).to eq("")
239
+ end
247
240
  end
248
241
 
249
242
  describe "pretty_money_for" do
250
243
  it "work with negative and positive numbers" do
251
- @some_other_bank.pretty_money_for(1).should == "-$20.00"
252
- @some_other_bank.pretty_money_for(4).should == " $1558.52"
253
- @some_other_bank.pretty_money_for(7).should == "-$116.22"
254
- @some_other_bank.pretty_money_for(5).should == " $0.23"
255
- @some_other_bank.pretty_money_for(6).should == "-$0.96"
244
+ some_other_bank.pretty_money_for(1).should == "-$20.00"
245
+ some_other_bank.pretty_money_for(4).should == " $1558.52"
246
+ some_other_bank.pretty_money_for(7).should == "-$116.22"
247
+ some_other_bank.pretty_money_for(5).should == " $0.23"
248
+ some_other_bank.pretty_money_for(6).should == "-$0.96"
256
249
  end
257
250
 
258
251
  it "work with other currencies such as €" do
@@ -274,8 +267,15 @@ describe Reckon::CSVParser do
274
267
  end
275
268
 
276
269
  it "should work with merge columns" do
277
- @nationwide.pretty_money_for(0).should == " 500.00 POUND"
278
- @nationwide.pretty_money_for(1).should == "-20.00 POUND"
270
+ nationwide.pretty_money_for(0).should == " 500.00 POUND"
271
+ nationwide.pretty_money_for(1).should == "-20.00 POUND"
272
+ end
273
+ end
274
+
275
+ describe '85 regression test' do
276
+ it 'should detect correct date column' do
277
+ p = Reckon::CSVParser.new(file:fixture_path('85-date-example.csv'))
278
+ expect(p.date_column_index).to eq(2)
279
279
  end
280
280
  end
281
281
  end
@@ -20,14 +20,16 @@ describe Reckon::LedgerParser do
20
20
  formats = ["%Y/%m/%d", "%Y-%m-%d"]
21
21
  types = [' ! ', ' * ', ' ']
22
22
  delimiters = [" ", "\t", "\t\t"]
23
+ comment_chars = ';#%*|'
23
24
  currency_delimiters = delimiters + ['']
24
25
  currencies = ['', '$', '£']
25
26
  property_of do
26
27
  Rantly do
27
28
  description = Proc.new do
28
- sized(15){string}.tr(%q{'`:*\\},'').gsub(/\s+/, ' ').gsub(/^[!;<\[( ]+/, '')
29
+ sized(15){string}.tr(%q{'`:*\\},'').gsub(/\s+/, ' ').gsub(/^[!;<\[( #{comment_chars}]+/, '')
29
30
  end
30
31
  currency = choose(*currencies) # to be consistent within the transaction
32
+ single_line_comments = ";#|%*".split('').map { |n| "#{n} #{call(description)}" }
31
33
  comments = ['', '; ', "\t;#{call(description)}", " ; #{call(description)}"]
32
34
  date = Time.at(range(0, 1_581_389_644)).strftime(choose(*formats))
33
35
  codes = [' ', " (#{string(:alnum).tr('()', '')}) "]
@@ -48,23 +50,58 @@ describe Reckon::LedgerParser do
48
50
  ledger += "#{call(account_line)}\n"
49
51
  end
50
52
  ledger += "#{call(account)}\n"
53
+ ledger += choose(*single_line_comments) + "\n"
51
54
  ledger
52
55
  end
53
56
  end.check(1000) do |s|
54
57
  filter_format = lambda { |n| [n['date'], n['desc'], n['name'], sprintf("%.02f", n['amount'])] }
55
58
  headers = %w[date code desc name currency amount type commend]
56
59
  safe_s = Shellwords.escape(s)
57
- ledger_csv = `echo #{safe_s} | ledger csv --date-format '%Y-%m-%d' -f - `
58
- ledger_parser_csv = Reckon::LedgerParser.new(s, date_format: '%Y/%m/%d').to_csv.join("\n")
59
60
 
60
- expected = CSV.parse(ledger_csv.gsub('\"', '""'), headers: headers).map &filter_format
61
- actual = CSV.parse(ledger_parser_csv, headers: headers).map &filter_format
61
+ lp_csv = Reckon::LedgerParser.new(s, date_format: '%Y/%m/%d').to_csv.join("\n")
62
+ actual = CSV.parse(lp_csv, headers: headers).map(&filter_format)
63
+
64
+ ledger_csv = `echo #{safe_s} | ledger csv --date-format '%Y/%m/%d' -f - `
65
+ expected = CSV.parse(ledger_csv.gsub('\"', '""'), headers: headers).map(&filter_format)
62
66
  expected.length.times do |i|
63
67
  expect(actual[i]).to eq(expected[i])
64
68
  end
65
69
  end
66
70
  end
67
71
 
72
+ it 'should filter block comments' do
73
+ ledger = <<HERE
74
+ 1970/11/01 Dinner should show up
75
+ Assets:Checking -123.00
76
+ Expenses:Restaurants
77
+
78
+ comment
79
+
80
+ 1970/11/01 Lunch should NOT show up
81
+ Assets:Checking -12.00
82
+ Expenses:Restaurants
83
+
84
+ end comment
85
+ HERE
86
+ l = Reckon::LedgerParser.new(ledger)
87
+ expect(l.entries.length).to eq(1)
88
+ expect(l.entries.first[:desc]).to eq('Dinner should show up')
89
+
90
+ end
91
+
92
+ it 'should transaction comments' do
93
+ ledger = <<HERE
94
+ 2020-03-27 AMZN Mktp USX999H3203; Shopping; Sale
95
+ Expenses:Household $82.77
96
+ Liabilities:ChaseSapphire -$81.77
97
+ # END FINANCE SCRIPT OUTPUT Thu 02 Apr 2020 12:05:54 PM EDT
98
+ HERE
99
+ l = Reckon::LedgerParser.new(ledger)
100
+ expect(l.entries.first[:accounts].map { |n| n[:name] }).to eq(['Expenses:Household', 'Liabilities:ChaseSapphire'])
101
+ expect(l.entries.first[:accounts].size).to eq(2)
102
+ expect(l.entries.length).to eq(1)
103
+ end
104
+
68
105
  it "should ignore non-standard entries" do
69
106
  @ledger.entries.length.should == 7
70
107
  end