reckon 0.5.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +50 -0
- data/.gitignore +3 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +77 -8
- data/Gemfile.lock +1 -5
- data/README.md +74 -21
- data/Rakefile +17 -1
- data/bin/build-new-version.sh +26 -0
- data/bin/reckon +6 -1
- data/lib/reckon.rb +2 -2
- data/lib/reckon/app.rb +140 -194
- data/lib/reckon/csv_parser.rb +8 -8
- data/lib/reckon/date_column.rb +10 -0
- data/lib/reckon/ledger_parser.rb +5 -1
- data/lib/reckon/money.rb +48 -48
- data/lib/reckon/options.rb +147 -0
- data/lib/reckon/version.rb +1 -1
- data/reckon.gemspec +1 -2
- data/spec/integration/another_bank_example/input.csv +9 -0
- data/spec/integration/another_bank_example/output.ledger +36 -0
- data/spec/integration/another_bank_example/test_args +1 -0
- data/spec/integration/austrian_example/input.csv +13 -0
- data/spec/integration/austrian_example/output.ledger +52 -0
- data/spec/integration/austrian_example/test_args +2 -0
- data/spec/integration/bom_utf8_file/input.csv +3 -0
- data/spec/integration/bom_utf8_file/output.ledger +4 -0
- data/spec/integration/bom_utf8_file/test_args +3 -0
- data/spec/integration/broker_canada_example/input.csv +12 -0
- data/spec/integration/broker_canada_example/output.ledger +48 -0
- data/spec/integration/broker_canada_example/test_args +1 -0
- data/spec/integration/chase/account_tokens_and_regex/output.ledger +36 -0
- data/spec/integration/chase/account_tokens_and_regex/test_args +2 -0
- data/spec/integration/chase/account_tokens_and_regex/tokens.yml +16 -0
- data/spec/integration/chase/default_account_names/output.ledger +36 -0
- data/spec/integration/chase/default_account_names/test_args +3 -0
- data/spec/integration/chase/input.csv +9 -0
- data/spec/integration/chase/learn_from_existing/learn.ledger +7 -0
- data/spec/integration/chase/learn_from_existing/output.ledger +36 -0
- data/spec/integration/chase/learn_from_existing/test_args +1 -0
- data/spec/integration/chase/simple/output.ledger +36 -0
- data/spec/integration/chase/simple/test_args +1 -0
- data/spec/integration/danish_kroner_nordea_example/input.csv +6 -0
- data/spec/integration/danish_kroner_nordea_example/output.ledger +24 -0
- data/spec/integration/danish_kroner_nordea_example/test_args +1 -0
- data/spec/integration/english_date_example/input.csv +3 -0
- data/spec/integration/english_date_example/output.ledger +12 -0
- data/spec/integration/english_date_example/test_args +1 -0
- data/spec/integration/extratofake/input.csv +24 -0
- data/spec/integration/extratofake/output.ledger +92 -0
- data/spec/integration/extratofake/test_args +1 -0
- data/spec/integration/french_example/input.csv +9 -0
- data/spec/integration/french_example/output.ledger +36 -0
- data/spec/integration/french_example/test_args +2 -0
- data/spec/integration/german_date_example/input.csv +3 -0
- data/spec/integration/german_date_example/output.ledger +12 -0
- data/spec/integration/german_date_example/test_args +1 -0
- data/spec/integration/harder_date_example/input.csv +5 -0
- data/spec/integration/harder_date_example/output.ledger +20 -0
- data/spec/integration/harder_date_example/test_args +1 -0
- data/spec/integration/ing/input.csv +3 -0
- data/spec/integration/ing/output.ledger +12 -0
- data/spec/integration/ing/test_args +1 -0
- data/spec/integration/intuit_mint_example/input.csv +7 -0
- data/spec/integration/intuit_mint_example/output.ledger +28 -0
- data/spec/integration/intuit_mint_example/test_args +1 -0
- data/spec/integration/invalid_header_example/input.csv +6 -0
- data/spec/integration/invalid_header_example/output.ledger +8 -0
- data/spec/integration/invalid_header_example/test_args +1 -0
- data/spec/integration/inversed_credit_card/input.csv +16 -0
- data/spec/integration/inversed_credit_card/output.ledger +64 -0
- data/spec/integration/inversed_credit_card/test_args +1 -0
- data/spec/integration/nationwide/input.csv +4 -0
- data/spec/integration/nationwide/output.ledger +16 -0
- data/spec/integration/nationwide/test_args +1 -0
- data/spec/integration/regression/issue_51_account_tokens/input.csv +8 -0
- data/spec/integration/regression/issue_51_account_tokens/output.ledger +32 -0
- data/spec/integration/regression/issue_51_account_tokens/test_args +4 -0
- data/spec/integration/regression/issue_51_account_tokens/tokens.yml +9 -0
- data/spec/integration/regression/issue_64_date_column/input.csv +3 -0
- data/spec/integration/regression/issue_64_date_column/output.ledger +8 -0
- data/spec/integration/regression/issue_64_date_column/test_args +1 -0
- data/spec/integration/regression/issue_73_account_token_matching/input.csv +2 -0
- data/spec/integration/regression/issue_73_account_token_matching/output.ledger +4 -0
- data/spec/integration/regression/issue_73_account_token_matching/test_args +6 -0
- data/spec/integration/regression/issue_73_account_token_matching/tokens.yml +8 -0
- data/spec/integration/regression/issue_85_date_example/input.csv +2 -0
- data/spec/integration/regression/issue_85_date_example/output.ledger +8 -0
- data/spec/integration/regression/issue_85_date_example/test_args +1 -0
- data/spec/integration/spanish_date_example/input.csv +3 -0
- data/spec/integration/spanish_date_example/output.ledger +12 -0
- data/spec/integration/spanish_date_example/test_args +1 -0
- data/spec/integration/suntrust/input.csv +7 -0
- data/spec/integration/suntrust/output.ledger +28 -0
- data/spec/integration/suntrust/test_args +1 -0
- data/spec/integration/test.sh +83 -0
- data/spec/integration/test_money_column/input.csv +3 -0
- data/spec/integration/test_money_column/output.ledger +8 -0
- data/spec/integration/test_money_column/test_args +1 -0
- data/spec/integration/two_money_columns/input.csv +5 -0
- data/spec/integration/two_money_columns/output.ledger +20 -0
- data/spec/integration/two_money_columns/test_args +1 -0
- data/spec/integration/yyyymmdd_date_example/input.csv +1 -0
- data/spec/integration/yyyymmdd_date_example/output.ledger +4 -0
- data/spec/integration/yyyymmdd_date_example/test_args +1 -0
- data/spec/reckon/app_spec.rb +25 -7
- data/spec/reckon/csv_parser_spec.rb +5 -0
- data/spec/reckon/ledger_parser_spec.rb +19 -4
- data/spec/reckon/money_column_spec.rb +24 -24
- data/spec/reckon/money_spec.rb +13 -32
- data/spec/reckon/options_spec.rb +17 -0
- data/spec/spec_helper.rb +6 -1
- metadata +98 -59
- data/.travis.yml +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9e7bf4198ae8265e025b20821adea75c3acc7cc9493298ac6e7604b342e04bb1
|
|
4
|
+
data.tar.gz: 4e7e9be82a15f1aef0ab7667e4a751b7e52472673fe0f57359e5d7c5fdac80f6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eb4ce5f4bcb0b9663b5ab89c5071086bf6b13c157f4d45d1608dc1b530b076b82402b25003b4156882b8c56441f66c1cc97867cd781cb8a6b8386804eddb0673
|
|
7
|
+
data.tar.gz: ec5941351ad06d6bf029af5b47a9453a611c4a17aa14bddbe8b5b2cf4584bcdceb134a0c9016d32f046832ea507a611807a159a2f85a673374bbf591a063a42a
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
2
|
+
# They are provided by a third-party and are governed by
|
|
3
|
+
# separate terms of service, privacy policy, and support
|
|
4
|
+
# documentation.
|
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
|
7
|
+
|
|
8
|
+
name: Build Status
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
push:
|
|
12
|
+
branches: [ master ]
|
|
13
|
+
pull_request:
|
|
14
|
+
branches: [ master ]
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
strategy:
|
|
21
|
+
matrix:
|
|
22
|
+
ruby-version:
|
|
23
|
+
# Current ruby stable version
|
|
24
|
+
- 3.0
|
|
25
|
+
# Ubuntu 20.10
|
|
26
|
+
- 2.7
|
|
27
|
+
# Ubuntu 19.10
|
|
28
|
+
- 2.5
|
|
29
|
+
# Mac v11 Big Sur
|
|
30
|
+
# - 2.6?
|
|
31
|
+
# Mac v10.15 Catalina
|
|
32
|
+
- 2.6
|
|
33
|
+
# Mac v10.14 Mojave
|
|
34
|
+
- 2.3.7
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v2
|
|
37
|
+
- name: Install packages
|
|
38
|
+
run: sudo apt-get -y install ledger hledger
|
|
39
|
+
- name: Install bundler
|
|
40
|
+
run: sudo gem install -v 1.17.3 bundler
|
|
41
|
+
- name: Set up Ruby
|
|
42
|
+
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
|
43
|
+
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
|
44
|
+
uses: ruby/setup-ruby@v1
|
|
45
|
+
# uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
|
|
46
|
+
with:
|
|
47
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
48
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
|
49
|
+
- name: Run tests
|
|
50
|
+
run: bundle exec rake test_all
|
data/.gitignore
CHANGED
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
3.0.0
|
data/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,81 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [
|
|
3
|
+
## [0.7.0](https://github.com/cantino/reckon/tree/0.7.0) (2021-02-06)
|
|
4
4
|
|
|
5
|
-
[Full Changelog](https://github.com/cantino/reckon/compare/v0.
|
|
5
|
+
[Full Changelog](https://github.com/cantino/reckon/compare/v0.6.2...0.7.0)
|
|
6
6
|
|
|
7
7
|
**Closed issues:**
|
|
8
8
|
|
|
9
|
+
- fail on unknown accounts [\#96](https://github.com/cantino/reckon/issues/96)
|
|
10
|
+
|
|
11
|
+
**Merged pull requests:**
|
|
12
|
+
|
|
13
|
+
- Fail on unknown account [\#102](https://github.com/cantino/reckon/pull/102) ([benprew](https://github.com/benprew))
|
|
14
|
+
- Joined split sentence to one [\#101](https://github.com/cantino/reckon/pull/101) ([RidaAyed](https://github.com/RidaAyed))
|
|
15
|
+
|
|
16
|
+
## [v0.6.2](https://github.com/cantino/reckon/tree/v0.6.2) (2021-01-25)
|
|
17
|
+
|
|
18
|
+
[Full Changelog](https://github.com/cantino/reckon/compare/v0.6.1...v0.6.2)
|
|
19
|
+
|
|
20
|
+
**Closed issues:**
|
|
21
|
+
|
|
22
|
+
- spaces in tokens [\#97](https://github.com/cantino/reckon/issues/97)
|
|
23
|
+
- read from stdin [\#95](https://github.com/cantino/reckon/issues/95)
|
|
24
|
+
|
|
25
|
+
**Merged pull requests:**
|
|
26
|
+
|
|
27
|
+
- Allow using '-' as filename in -f to read csv from STDIN. Fixes \#95 [\#98](https://github.com/cantino/reckon/pull/98) ([benprew](https://github.com/benprew))
|
|
28
|
+
|
|
29
|
+
## [v0.6.1](https://github.com/cantino/reckon/tree/v0.6.1) (2021-01-23)
|
|
30
|
+
|
|
31
|
+
[Full Changelog](https://github.com/cantino/reckon/compare/v0.6.0...v0.6.1)
|
|
32
|
+
|
|
33
|
+
**Implemented enhancements:**
|
|
34
|
+
|
|
35
|
+
- \[Feature Request\] Note flag --add-notes in CLI to allow additional notes for each ledger entry [\#86](https://github.com/cantino/reckon/issues/86)
|
|
36
|
+
|
|
37
|
+
**Closed issues:**
|
|
38
|
+
|
|
39
|
+
- Migrate CI system from travis-ci.org [\#93](https://github.com/cantino/reckon/issues/93)
|
|
40
|
+
- \[Feature Request\] Pipe ledger file input to the bayesian predictor \(instead of csv\) [\#91](https://github.com/cantino/reckon/issues/91)
|
|
41
|
+
|
|
42
|
+
**Merged pull requests:**
|
|
43
|
+
|
|
44
|
+
- Add github actions [\#100](https://github.com/cantino/reckon/pull/100) ([benprew](https://github.com/benprew))
|
|
45
|
+
- Add documentation for doing a substring match. Fixes \#97 [\#99](https://github.com/cantino/reckon/pull/99) ([benprew](https://github.com/benprew))
|
|
46
|
+
- Test fixes [\#94](https://github.com/cantino/reckon/pull/94) ([benprew](https://github.com/benprew))
|
|
47
|
+
|
|
48
|
+
## [v0.6.0](https://github.com/cantino/reckon/tree/v0.6.0) (2020-09-04)
|
|
49
|
+
|
|
50
|
+
[Full Changelog](https://github.com/cantino/reckon/compare/v0.5.4...v0.6.0)
|
|
51
|
+
|
|
52
|
+
**Fixed bugs:**
|
|
53
|
+
|
|
54
|
+
- \[BUG\] Reckon appears not to be parsing ISO standard date yyyy-mm-dd? [\#85](https://github.com/cantino/reckon/issues/85)
|
|
55
|
+
|
|
56
|
+
**Closed issues:**
|
|
57
|
+
|
|
58
|
+
- duplicate detection [\#16](https://github.com/cantino/reckon/issues/16)
|
|
59
|
+
|
|
60
|
+
**Merged pull requests:**
|
|
61
|
+
|
|
62
|
+
- Add ability to add note to transaction when entering it [\#89](https://github.com/cantino/reckon/pull/89) ([benprew](https://github.com/benprew))
|
|
63
|
+
|
|
64
|
+
## [v0.5.4](https://github.com/cantino/reckon/tree/v0.5.4) (2020-06-05)
|
|
65
|
+
|
|
66
|
+
[Full Changelog](https://github.com/cantino/reckon/compare/v0.5.3...v0.5.4)
|
|
67
|
+
|
|
68
|
+
**Fixed bugs:**
|
|
69
|
+
|
|
70
|
+
- order of transactions [\#88](https://github.com/cantino/reckon/issues/88)
|
|
9
71
|
- Is reckon failing to handle comments when learning? [\#87](https://github.com/cantino/reckon/issues/87)
|
|
72
|
+
|
|
73
|
+
## [v0.5.3](https://github.com/cantino/reckon/tree/v0.5.3) (2020-05-02)
|
|
74
|
+
|
|
75
|
+
[Full Changelog](https://github.com/cantino/reckon/compare/v0.5.2...v0.5.3)
|
|
76
|
+
|
|
77
|
+
**Closed issues:**
|
|
78
|
+
|
|
10
79
|
- \[FEATURE REQUEST\] Ask for currency of Account and output in output file in standard format of xxxx TLA for currency [\#84](https://github.com/cantino/reckon/issues/84)
|
|
11
80
|
|
|
12
81
|
## [v0.5.2](https://github.com/cantino/reckon/tree/v0.5.2) (2020-03-07)
|
|
@@ -112,7 +181,6 @@
|
|
|
112
181
|
**Merged pull requests:**
|
|
113
182
|
|
|
114
183
|
- Better ISO 8601 dates support [\#49](https://github.com/cantino/reckon/pull/49) ([vzctl](https://github.com/vzctl))
|
|
115
|
-
- Unattended mode and custom tokens support [\#47](https://github.com/cantino/reckon/pull/47) ([vzctl](https://github.com/vzctl))
|
|
116
184
|
- \[RFC\] Implement issue \#40: Tab completion [\#46](https://github.com/cantino/reckon/pull/46) ([BlackEdder](https://github.com/BlackEdder))
|
|
117
185
|
- set readline to allow for backspace in ask dialog [\#44](https://github.com/cantino/reckon/pull/44) ([mrtazz](https://github.com/mrtazz))
|
|
118
186
|
|
|
@@ -136,6 +204,7 @@
|
|
|
136
204
|
|
|
137
205
|
**Merged pull requests:**
|
|
138
206
|
|
|
207
|
+
- Unattended mode and custom tokens support [\#47](https://github.com/cantino/reckon/pull/47) ([vzctl](https://github.com/vzctl))
|
|
139
208
|
- Added spec for csv files from Broker Canada [\#36](https://github.com/cantino/reckon/pull/36) ([BlackEdder](https://github.com/BlackEdder))
|
|
140
209
|
- Date format [\#35](https://github.com/cantino/reckon/pull/35) ([BlackEdder](https://github.com/BlackEdder))
|
|
141
210
|
- Added example from a french bank [\#34](https://github.com/cantino/reckon/pull/34) ([BlackEdder](https://github.com/BlackEdder))
|
|
@@ -212,15 +281,15 @@
|
|
|
212
281
|
|
|
213
282
|
## [v0.3.3](https://github.com/cantino/reckon/tree/v0.3.3) (2013-01-13)
|
|
214
283
|
|
|
215
|
-
[Full Changelog](https://github.com/cantino/reckon/compare/v0.3.
|
|
284
|
+
[Full Changelog](https://github.com/cantino/reckon/compare/v0.3.1...v0.3.3)
|
|
216
285
|
|
|
217
|
-
## [v0.3.
|
|
286
|
+
## [v0.3.1](https://github.com/cantino/reckon/tree/v0.3.1) (2012-07-30)
|
|
218
287
|
|
|
219
|
-
[Full Changelog](https://github.com/cantino/reckon/compare/v0.3.
|
|
288
|
+
[Full Changelog](https://github.com/cantino/reckon/compare/v0.3.2...v0.3.1)
|
|
220
289
|
|
|
221
|
-
## [v0.3.
|
|
290
|
+
## [v0.3.2](https://github.com/cantino/reckon/tree/v0.3.2) (2012-07-30)
|
|
222
291
|
|
|
223
|
-
[Full Changelog](https://github.com/cantino/reckon/compare/5c07bea3fe63f9b909b4b76bd49f22fd8faf7a29...v0.3.
|
|
292
|
+
[Full Changelog](https://github.com/cantino/reckon/compare/5c07bea3fe63f9b909b4b76bd49f22fd8faf7a29...v0.3.2)
|
|
224
293
|
|
|
225
294
|
|
|
226
295
|
|
data/Gemfile.lock
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
reckon (0.
|
|
4
|
+
reckon (0.7.0)
|
|
5
5
|
chronic (>= 0.3.0)
|
|
6
6
|
highline (>= 1.5.2)
|
|
7
7
|
rchardet (>= 1.8.0)
|
|
8
|
-
terminal-table (>= 1.4.2)
|
|
9
8
|
|
|
10
9
|
GEM
|
|
11
10
|
remote: http://rubygems.org/
|
|
@@ -34,9 +33,6 @@ GEM
|
|
|
34
33
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
35
34
|
rspec-support (~> 3.9.0)
|
|
36
35
|
rspec-support (3.9.2)
|
|
37
|
-
terminal-table (1.8.0)
|
|
38
|
-
unicode-display_width (~> 1.1, >= 1.1.1)
|
|
39
|
-
unicode-display_width (1.6.1)
|
|
40
36
|
|
|
41
37
|
PLATFORMS
|
|
42
38
|
ruby
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Reckon
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

|
|
4
4
|
|
|
5
5
|
Reckon automagically converts CSV files for use with the command-line accounting tool [Ledger](http://www.ledger-cli.org/). It also helps you to select the correct accounts associated with the CSV data using Bayesian machine learning.
|
|
6
6
|
|
|
@@ -34,9 +34,8 @@ Learn more:
|
|
|
34
34
|
|
|
35
35
|
Usage: Reckon.rb [options]
|
|
36
36
|
|
|
37
|
-
|
|
38
37
|
-f, --file FILE The CSV file to parse
|
|
39
|
-
-a, --account
|
|
38
|
+
-a, --account NAME The Ledger Account this file is for
|
|
40
39
|
-v, --[no-]verbose Run verbosely
|
|
41
40
|
-i, --inverse Use the negative of each amount
|
|
42
41
|
-p, --print-table Print out the parsed CSV in table form
|
|
@@ -44,6 +43,12 @@ Learn more:
|
|
|
44
43
|
-l, --learn-from FILE An existing ledger file to learn accounts from
|
|
45
44
|
--ignore-columns 1,2,5
|
|
46
45
|
Columns to ignore in the CSV file - the first column is column 1
|
|
46
|
+
--money-column 2
|
|
47
|
+
Specify the money column instead of letting Reckon guess - the first column is column 1
|
|
48
|
+
--raw-money
|
|
49
|
+
Don't format money column (for stocks)
|
|
50
|
+
--date-column 3
|
|
51
|
+
Specify the date column instead of letting Reckon guess - the first column is column 1
|
|
47
52
|
--contains-header [N]
|
|
48
53
|
The first row of the CSV is a header and should be skipped. Optionally add the number of rows to skip.
|
|
49
54
|
--csv-separator ','
|
|
@@ -57,9 +62,9 @@ Learn more:
|
|
|
57
62
|
Force the date format (see Ruby DateTime strftime)
|
|
58
63
|
-u, --unattended Don't ask questions and guess all the accounts automatically. Used with --learn-from or --account-tokens options.
|
|
59
64
|
-t, --account-tokens FILE YAML file with manually-assigned tokens for each account (see README)
|
|
60
|
-
--default-into-account
|
|
65
|
+
--default-into-account NAME
|
|
61
66
|
Default into account
|
|
62
|
-
--default-outof-account
|
|
67
|
+
--default-outof-account NAME
|
|
63
68
|
Default 'out of' account
|
|
64
69
|
--suffixed
|
|
65
70
|
If --currency should be used as a suffix. Defaults to false.
|
|
@@ -77,6 +82,20 @@ To guess the accounts reckon can use an existing ledger file or a token file wit
|
|
|
77
82
|
|
|
78
83
|
`reckon --unattended --account-tokens tokens.yaml -f bank.csv -o ledger.dat`
|
|
79
84
|
|
|
85
|
+
In unattended mode, you can use STDIN to read your csv data, by specifying `-` as the argument to `-f`.
|
|
86
|
+
|
|
87
|
+
`csv_file_generator | reckon --unattended -l 2010.dat -o ledger.dat -f -`
|
|
88
|
+
|
|
89
|
+
### Account Tokens
|
|
90
|
+
|
|
91
|
+
The account tokens file provides a way to teach reckon about what tokens are associated with an account. As an example, this `tokens.yaml` file:
|
|
92
|
+
|
|
93
|
+
Expenses:
|
|
94
|
+
Bank:
|
|
95
|
+
- 'ING Direct Deposit'
|
|
96
|
+
|
|
97
|
+
Would tokenize to 'ING', 'Direct' and 'Deposit'. The matcher would then suggest matches to transactions that included those tokens. (ex 'Chase Direct Deposit')
|
|
98
|
+
|
|
80
99
|
Here's an example of `tokens.yaml`:
|
|
81
100
|
|
|
82
101
|
```
|
|
@@ -96,27 +115,61 @@ Expenses:
|
|
|
96
115
|
- '4433221100' # Your own account number
|
|
97
116
|
```
|
|
98
117
|
|
|
99
|
-
|
|
100
|
-
|
|
118
|
+
Reckon will use `Income:Unknown` or `Expenses:Unknown` if it can't match a transaction to an account.
|
|
119
|
+
|
|
120
|
+
You can override these names with the `--default_outof_account` and `--default_into_account` options.
|
|
121
|
+
|
|
122
|
+
### Substring Match
|
|
123
|
+
|
|
124
|
+
If, in the above example, you'd prefer to match any transaction that contains the string 'ING Direct Deposit' you have to use a regex:
|
|
125
|
+
|
|
126
|
+
Expenses:
|
|
127
|
+
Bank:
|
|
128
|
+
- /ING Direct Deposit/
|
|
129
|
+
|
|
130
|
+
## Contributing
|
|
131
|
+
|
|
132
|
+
We encourage you to contribute to Reckon! Here is some information to help you.
|
|
101
133
|
|
|
102
|
-
|
|
134
|
+
### Patches/Pull Requests Process
|
|
103
135
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
136
|
+
1. Fork the project.
|
|
137
|
+
2. Make your feature addition or bug fix.
|
|
138
|
+
3. Add tests for it. This is important so I don't break it in a future version unintentionally.
|
|
139
|
+
4. Commit, do not mess with rakefile, version, or history.
|
|
140
|
+
- (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
|
141
|
+
5. Send me a pull request. Bonus points for topic branches.
|
|
142
|
+
|
|
143
|
+
### Integration Tests
|
|
144
|
+
|
|
145
|
+
Reckon has integration test located in `spec/integration`. These are integration and regression tests for reckon.
|
|
146
|
+
|
|
147
|
+
Run all the tests:
|
|
148
|
+
|
|
149
|
+
./spec/integration/test.sh
|
|
150
|
+
|
|
151
|
+
Run a single test
|
|
152
|
+
|
|
153
|
+
./spec/integration/test.sh chase/account_tokens_and_regex
|
|
154
|
+
|
|
155
|
+
#### Add a new integration test
|
|
156
|
+
|
|
157
|
+
Each test has it's own directory, which you can add any files you want, but the following files are required:
|
|
158
|
+
|
|
159
|
+
- `test_args` - arguments to add to the reckon command to test against, can specify `--unattended`, `-f input.csv`, etc
|
|
160
|
+
- `output.ledger` - the expected ledger file output
|
|
161
|
+
|
|
162
|
+
If the result of running reckon with `test_args` does not match `output.ledger`, then the test fails.
|
|
163
|
+
|
|
164
|
+
Most tests will specify `--unattended`, otherwise reckon prompts for keyboard input.
|
|
165
|
+
|
|
166
|
+
The convention is to use `input.csv` as the input file, and `tokens.yml` as the tokens file, but it is not required.
|
|
111
167
|
|
|
112
|
-
## Making a release
|
|
113
|
-
* Update lib/reckon/version.rb
|
|
114
|
-
* Run `github_changelog_generator --future-release v$(egrep '"[^"]+"' -o lib/reckon/version.rb |sed -e 's/"//g') --user cantino --project reckon -t $(cat ~/.github_token)`
|
|
115
|
-
* Commit
|
|
116
|
-
* Tag the commit same as in version.rb vX.XX.XX (ex v0.5.2)
|
|
117
168
|
|
|
118
169
|
## Copyright
|
|
119
170
|
|
|
120
|
-
Copyright (c) 2013 Andrew Cantino. See LICENSE for details.
|
|
171
|
+
Copyright (c) 2013 Andrew Cantino (@cantino). See LICENSE for details.
|
|
121
172
|
|
|
122
173
|
Thanks to @BlackEdder for many contributions!
|
|
174
|
+
|
|
175
|
+
Currently maintained by @benprew. Thank you!
|
data/Rakefile
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "bundler/gem_tasks"
|
|
2
4
|
require 'rspec/core/rake_task'
|
|
5
|
+
require 'English'
|
|
3
6
|
|
|
4
7
|
RSpec::Core::RakeTask.new(:spec)
|
|
5
8
|
|
|
6
|
-
task :
|
|
9
|
+
task default: :spec
|
|
10
|
+
|
|
11
|
+
task :test_all do
|
|
12
|
+
puts "#{`ledger --version |head -n1`}"
|
|
13
|
+
puts "Running unit tests"
|
|
14
|
+
Rake::Task["spec"].invoke
|
|
15
|
+
puts "Running integration tests"
|
|
16
|
+
Rake::Task["integration_tests"].invoke
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
task :integration_tests do
|
|
20
|
+
puts `./spec/integration/test.sh`
|
|
21
|
+
raise 'Integration tests failed' if $CHILD_STATUS.exitstatus != 0
|
|
22
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
VERSION=$1
|
|
6
|
+
|
|
7
|
+
echo "Install github_changelog_generator"
|
|
8
|
+
gem install --user github_changelog_generator
|
|
9
|
+
|
|
10
|
+
echo "Update 'lib/reckon/version.rb'"
|
|
11
|
+
echo -e "module Reckon\n VERSION=\"$VERSION\"\nend" > lib/reckon/version.rb
|
|
12
|
+
echo "Run `bundle install` to build updated Gemfile.lock"
|
|
13
|
+
bundle install
|
|
14
|
+
echo "3. Run changelog generator (requires $TOKEN to be your github token)"
|
|
15
|
+
github_changelog_generator -u cantino -p reckon -t $TOKEN --future-release $VERSION
|
|
16
|
+
echo "4. Commit changes"
|
|
17
|
+
git add CHANGELOG.md lib/reckon/version.rb Gemfile.lock
|
|
18
|
+
git commit -m "Release $VERSION"
|
|
19
|
+
echo "7. Build new gem"
|
|
20
|
+
gem build reckon.gemspec
|
|
21
|
+
echo "5. Tag release"
|
|
22
|
+
git tag v$VERSION
|
|
23
|
+
echo "Push changes and tags"
|
|
24
|
+
echo git push && git push --tags
|
|
25
|
+
echo "Push new gem"
|
|
26
|
+
echo gem push reckon-$VERSION.gem
|
data/bin/reckon
CHANGED
data/lib/reckon.rb
CHANGED
|
@@ -4,9 +4,8 @@ require 'rubygems'
|
|
|
4
4
|
require 'rchardet'
|
|
5
5
|
require 'chronic'
|
|
6
6
|
require 'csv'
|
|
7
|
-
require 'highline
|
|
7
|
+
require 'highline'
|
|
8
8
|
require 'optparse'
|
|
9
|
-
require 'terminal-table'
|
|
10
9
|
require 'time'
|
|
11
10
|
require 'logger'
|
|
12
11
|
|
|
@@ -17,4 +16,5 @@ require_relative 'reckon/date_column'
|
|
|
17
16
|
require_relative 'reckon/money'
|
|
18
17
|
require_relative 'reckon/ledger_parser'
|
|
19
18
|
require_relative 'reckon/csv_parser'
|
|
19
|
+
require_relative 'reckon/options'
|
|
20
20
|
require_relative 'reckon/app'
|
data/lib/reckon/app.rb
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
|
+
|
|
2
3
|
require 'pp'
|
|
3
4
|
require 'yaml'
|
|
4
5
|
|
|
5
6
|
module Reckon
|
|
6
7
|
class App
|
|
7
8
|
attr_accessor :options, :seen, :csv_parser, :regexps, :matcher
|
|
9
|
+
@@cli = HighLine.new
|
|
8
10
|
|
|
9
|
-
def initialize(
|
|
11
|
+
def initialize(opts = {})
|
|
12
|
+
self.options = opts
|
|
10
13
|
LOGGER.level = Logger::INFO if options[:verbose]
|
|
11
|
-
|
|
14
|
+
|
|
12
15
|
self.regexps = {}
|
|
13
|
-
self.seen =
|
|
16
|
+
self.seen = Set.new
|
|
14
17
|
self.options[:currency] ||= '$'
|
|
15
|
-
options[:string] = File.read(options[:file]) unless options[:string]
|
|
16
18
|
@csv_parser = CSVParser.new( options )
|
|
17
19
|
@matcher = CosineSimilarity.new(options)
|
|
18
20
|
learn!
|
|
@@ -20,22 +22,19 @@ module Reckon
|
|
|
20
22
|
|
|
21
23
|
def interactive_output(str)
|
|
22
24
|
return if options[:unattended]
|
|
25
|
+
|
|
23
26
|
puts str
|
|
24
27
|
end
|
|
25
28
|
|
|
26
29
|
def learn!
|
|
27
30
|
learn_from_account_tokens(options[:account_tokens_file])
|
|
28
|
-
|
|
29
|
-
ledger_file = options[:existing_ledger_file]
|
|
30
|
-
return unless ledger_file
|
|
31
|
-
fail "#{ledger_file} doesn't exist!" unless File.exists?(ledger_file)
|
|
32
|
-
learn_from(File.read(ledger_file))
|
|
31
|
+
learn_from_ledger_file(options[:existing_ledger_file])
|
|
33
32
|
end
|
|
34
33
|
|
|
35
34
|
def learn_from_account_tokens(filename)
|
|
36
35
|
return unless filename
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
raise "#{filename} doesn't exist!" unless File.exist?(filename)
|
|
39
38
|
|
|
40
39
|
extract_account_tokens(YAML.load_file(filename)).each do |account, tokens|
|
|
41
40
|
tokens.each do |t|
|
|
@@ -48,14 +47,27 @@ module Reckon
|
|
|
48
47
|
end
|
|
49
48
|
end
|
|
50
49
|
|
|
51
|
-
def
|
|
50
|
+
def learn_from_ledger_file(ledger_file)
|
|
51
|
+
return unless ledger_file
|
|
52
|
+
|
|
53
|
+
raise "#{ledger_file} doesn't exist!" unless File.exist?(ledger_file)
|
|
54
|
+
|
|
55
|
+
learn_from_ledger(File.read(ledger_file))
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def learn_from_ledger(ledger)
|
|
59
|
+
LOGGER.info "learning from #{ledger}"
|
|
52
60
|
LedgerParser.new(ledger).entries.each do |entry|
|
|
53
61
|
entry[:accounts].each do |account|
|
|
54
62
|
str = [entry[:desc], account[:amount]].join(" ")
|
|
55
|
-
|
|
63
|
+
if account[:name] != options[:bank_account]
|
|
64
|
+
LOGGER.info "adding document #{account[:name]} #{str}"
|
|
65
|
+
@matcher.add_document(account[:name], str)
|
|
66
|
+
end
|
|
56
67
|
pretty_date = entry[:date].iso8601
|
|
57
|
-
|
|
58
|
-
|
|
68
|
+
if account[:name] == options[:bank_account]
|
|
69
|
+
seen << seen_key(pretty_date, @csv_parser.pretty_money(account[:amount]))
|
|
70
|
+
end
|
|
59
71
|
end
|
|
60
72
|
end
|
|
61
73
|
end
|
|
@@ -91,9 +103,10 @@ module Reckon
|
|
|
91
103
|
end
|
|
92
104
|
|
|
93
105
|
def walk_backwards
|
|
106
|
+
cmd_options = "[account]/[q]uit/[s]kip/[n]ote/[d]escription"
|
|
94
107
|
seen_anything_new = false
|
|
95
108
|
each_row_backwards do |row|
|
|
96
|
-
|
|
109
|
+
print_transaction([row])
|
|
97
110
|
|
|
98
111
|
if already_seen?(row)
|
|
99
112
|
interactive_output "NOTE: This row is very similar to a previous one!"
|
|
@@ -105,50 +118,28 @@ module Reckon
|
|
|
105
118
|
seen_anything_new = true
|
|
106
119
|
end
|
|
107
120
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
else
|
|
114
|
-
out_of_account = ask("Which account provided this income? ([account]/[q]uit/[s]kip) ") { |q|
|
|
115
|
-
q.completion = possible_answers
|
|
116
|
-
q.readline = true
|
|
117
|
-
q.default = possible_answers.first
|
|
118
|
-
}
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
finish if out_of_account == "quit" || out_of_account == "q"
|
|
122
|
-
if out_of_account == "skip" || out_of_account == "s"
|
|
123
|
-
interactive_output "Skipping"
|
|
124
|
-
next
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
ledger_format( row,
|
|
128
|
-
[options[:bank_account], row[:pretty_money]],
|
|
129
|
-
[out_of_account, row[:pretty_money_negated]] )
|
|
121
|
+
if row[:money] > 0
|
|
122
|
+
# out_of_account
|
|
123
|
+
answer = ask_account_question("Which account provided this income? (#{cmd_options})", row)
|
|
124
|
+
line1 = [options[:bank_account], row[:pretty_money]]
|
|
125
|
+
line2 = [answer, ""]
|
|
130
126
|
else
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
q.default = possible_answers.first
|
|
138
|
-
}
|
|
139
|
-
end
|
|
140
|
-
finish if into_account == "quit" || into_account == 'q'
|
|
141
|
-
if into_account == "skip" || into_account == 's'
|
|
142
|
-
interactive_output "Skipping"
|
|
143
|
-
next
|
|
144
|
-
end
|
|
127
|
+
# into_account
|
|
128
|
+
answer = ask_account_question("To which account did this money go? (#{cmd_options})", row)
|
|
129
|
+
# line1 = [answer, row[:pretty_money_negated]]
|
|
130
|
+
line1 = [answer, ""]
|
|
131
|
+
line2 = [options[:bank_account], row[:pretty_money]]
|
|
132
|
+
end
|
|
145
133
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
134
|
+
finish if %w[quit q].include?(answer)
|
|
135
|
+
if %w[skip s].include?(answer)
|
|
136
|
+
interactive_output "Skipping"
|
|
137
|
+
next
|
|
149
138
|
end
|
|
150
139
|
|
|
151
|
-
|
|
140
|
+
ledger = ledger_format(row, line1, line2)
|
|
141
|
+
LOGGER.info "ledger line: #{ledger}"
|
|
142
|
+
learn_from_ledger(ledger) unless options[:account_tokens_file]
|
|
152
143
|
output(ledger)
|
|
153
144
|
end
|
|
154
145
|
end
|
|
@@ -167,16 +158,95 @@ module Reckon
|
|
|
167
158
|
:money => @csv_parser.money_for(index),
|
|
168
159
|
:description => @csv_parser.description_for(index) }
|
|
169
160
|
end
|
|
170
|
-
rows.sort_by { |n| n[:date] }.each {|row| yield row }
|
|
161
|
+
rows.sort_by { |n| [n[:date], -n[:money], n[:description]] }.each { |row| yield row }
|
|
171
162
|
end
|
|
172
163
|
|
|
173
|
-
def
|
|
164
|
+
def print_transaction(rows)
|
|
165
|
+
str = "\n"
|
|
166
|
+
header = %w[Date Amount Description Note]
|
|
167
|
+
maxes = header.map(&:length)
|
|
168
|
+
|
|
169
|
+
rows = rows.map { |r| [r[:pretty_date], r[:pretty_money], r[:description], r[:note]] }
|
|
170
|
+
|
|
171
|
+
rows.each do |r|
|
|
172
|
+
r.length.times { |i| l = r[i] ? r[i].length : 0; maxes[i] = l if maxes[i] < l }
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
header.each_with_index do |n, i|
|
|
176
|
+
str += " #{n.center(maxes[i])} |"
|
|
177
|
+
end
|
|
178
|
+
str += "\n"
|
|
179
|
+
|
|
180
|
+
rows.each do |row|
|
|
181
|
+
row.each_with_index do |_, i|
|
|
182
|
+
just = maxes[i]
|
|
183
|
+
str += sprintf(" %#{just}s |", row[i])
|
|
184
|
+
end
|
|
185
|
+
str += "\n"
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
interactive_output str
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def ask_account_question(msg, row)
|
|
192
|
+
possible_answers = suggest(row)
|
|
193
|
+
LOGGER.info "possible_answers===> #{possible_answers.inspect}"
|
|
194
|
+
|
|
195
|
+
if options[:unattended]
|
|
196
|
+
if options[:fail_on_unknown_account] && possible_answers.empty?
|
|
197
|
+
raise %(Couldn't find any matches for '#{row[:description]}'
|
|
198
|
+
Try adding an account token with --account-tokens)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
default = options[:default_outof_account]
|
|
202
|
+
default = options[:default_into_account] if row[:pretty_money][0] == '-'
|
|
203
|
+
return possible_answers[0] || default
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
answer = @@cli.ask(msg) do |q|
|
|
207
|
+
q.completion = possible_answers
|
|
208
|
+
q.readline = true
|
|
209
|
+
q.default = possible_answers.first
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# if answer isn't n/note/d/description, must be an account name, or skip, or quit
|
|
213
|
+
return answer unless %w[n note d description].include?(answer)
|
|
214
|
+
|
|
215
|
+
add_description(row) if %w[d description].include?(answer)
|
|
216
|
+
add_note(row) if %w[n note].include?(answer)
|
|
217
|
+
|
|
218
|
+
print_transaction([row])
|
|
219
|
+
# give user a chance to set account name or retry description
|
|
220
|
+
return ask_account_question(msg, row)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def add_description(row)
|
|
224
|
+
desc_answer = @@cli.ask("Enter a new description for this transaction (empty line aborts)\n") do |q|
|
|
225
|
+
q.overwrite = true
|
|
226
|
+
q.readline = true
|
|
227
|
+
q.default = row[:description]
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
row[:description] = desc_answer unless desc_answer.empty?
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def add_note(row)
|
|
234
|
+
desc_answer = @@cli.ask("Enter a new note for this transaction (empty line aborts)\n") do |q|
|
|
235
|
+
q.overwrite = true
|
|
236
|
+
q.readline = true
|
|
237
|
+
q.default = row[:note]
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
row[:note] = desc_answer unless desc_answer.empty?
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def most_specific_regexp_match(row)
|
|
174
244
|
matches = regexps.map { |regexp, account|
|
|
175
245
|
if match = regexp.match(row[:description])
|
|
176
246
|
[account, match[0]]
|
|
177
247
|
end
|
|
178
248
|
}.compact
|
|
179
|
-
matches.sort_by! { |
|
|
249
|
+
matches.sort_by! { |_account, matched_text| matched_text.length }.map(&:first)
|
|
180
250
|
end
|
|
181
251
|
|
|
182
252
|
def suggest(row)
|
|
@@ -185,9 +255,9 @@ module Reckon
|
|
|
185
255
|
end
|
|
186
256
|
|
|
187
257
|
def ledger_format(row, line1, line2)
|
|
188
|
-
out = "#{row[:pretty_date]}\t#{row[:description]}\n"
|
|
189
|
-
out += "\t#{line1.first}\t\t\t
|
|
190
|
-
out += "\t#{line2.first}\t\t\t
|
|
258
|
+
out = "#{row[:pretty_date]}\t#{row[:description]}#{row[:note] ? "\t; " + row[:note]: ""}\n"
|
|
259
|
+
out += "\t#{line1.first}\t\t\t#{line1.last}\n"
|
|
260
|
+
out += "\t#{line2.first}\t\t\t#{line2.last}\n\n"
|
|
191
261
|
out
|
|
192
262
|
end
|
|
193
263
|
|
|
@@ -196,8 +266,12 @@ module Reckon
|
|
|
196
266
|
options[:output_file].flush
|
|
197
267
|
end
|
|
198
268
|
|
|
269
|
+
def seen_key(date, amount)
|
|
270
|
+
return [date, amount].join("|")
|
|
271
|
+
end
|
|
272
|
+
|
|
199
273
|
def already_seen?(row)
|
|
200
|
-
seen
|
|
274
|
+
seen.include?(seen_key(row[:pretty_date], row[:pretty_money]))
|
|
201
275
|
end
|
|
202
276
|
|
|
203
277
|
def finish
|
|
@@ -207,139 +281,11 @@ module Reckon
|
|
|
207
281
|
end
|
|
208
282
|
|
|
209
283
|
def output_table
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
t << [ row[:pretty_date], row[:pretty_money], row[:description] ]
|
|
214
|
-
end
|
|
215
|
-
end
|
|
216
|
-
interactive_output output
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
def self.parse_opts(args = ARGV)
|
|
220
|
-
options = { :output_file => STDOUT }
|
|
221
|
-
parser = OptionParser.new do |opts|
|
|
222
|
-
opts.banner = "Usage: Reckon.rb [options]"
|
|
223
|
-
opts.separator ""
|
|
224
|
-
|
|
225
|
-
opts.on("-f", "--file FILE", "The CSV file to parse") do |file|
|
|
226
|
-
options[:file] = file
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
opts.on("-a", "--account NAME", "The Ledger Account this file is for") do |a|
|
|
230
|
-
options[:bank_account] = a
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
|
234
|
-
options[:verbose] = v
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
opts.on("-i", "--inverse", "Use the negative of each amount") do |v|
|
|
238
|
-
options[:inverse] = v
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
opts.on("-p", "--print-table", "Print out the parsed CSV in table form") do |p|
|
|
242
|
-
options[:print_table] = p
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
opts.on("-o", "--output-file FILE", "The ledger file to append to") do |o|
|
|
246
|
-
options[:output_file] = File.open(o, 'a')
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
opts.on("-l", "--learn-from FILE", "An existing ledger file to learn accounts from") do |l|
|
|
250
|
-
options[:existing_ledger_file] = l
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
opts.on("", "--ignore-columns 1,2,5", "Columns to ignore in the CSV file - the first column is column 1") do |ignore|
|
|
254
|
-
options[:ignore_columns] = ignore.split(",").map { |i| i.to_i }
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
opts.on("", "--money-column 2", Integer, "Specify the money column instead of letting Reckon guess - the first column is column 1") do |column_number|
|
|
258
|
-
options[:money_column] = column_number
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
opts.on("", "--date-column 3", Integer, "Specify the date column instead of letting Reckon guess - the first column is column 1") do |column_number|
|
|
262
|
-
options[:date_column] = column_number
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
opts.on("", "--contains-header [N]", "The first row of the CSV is a header and should be skipped. Optionally add the number of rows to skip.") do |contains_header|
|
|
266
|
-
options[:contains_header] = 1
|
|
267
|
-
options[:contains_header] = contains_header.to_i if contains_header
|
|
268
|
-
end
|
|
269
|
-
|
|
270
|
-
opts.on("", "--csv-separator ','", "Separator for parsing the CSV - default is comma.") do |csv_separator|
|
|
271
|
-
options[:csv_separator] = csv_separator
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
opts.on("", "--comma-separates-cents", "Use comma instead of period to deliminate dollars from cents when parsing ($100,50 instead of $100.50)") do |c|
|
|
275
|
-
options[:comma_separates_cents] = c
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
opts.on("", "--encoding 'UTF-8'", "Specify an encoding for the CSV file; not usually needed") do |e|
|
|
279
|
-
options[:encoding] = e
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
opts.on("-c", "--currency '$'", "Currency symbol to use, defaults to $ (£, EUR)") do |e|
|
|
283
|
-
options[:currency] = e
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
opts.on("", "--date-format '%d/%m/%Y'", "Force the date format (see Ruby DateTime strftime)") do |d|
|
|
287
|
-
options[:date_format] = d
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
opts.on("-u", "--unattended", "Don't ask questions and guess all the accounts automatically. Used with --learn-from or --account-tokens options.") do |n|
|
|
291
|
-
options[:unattended] = n
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
opts.on("-t", "--account-tokens FILE", "YAML file with manually-assigned tokens for each account (see README)") do |a|
|
|
295
|
-
options[:account_tokens_file] = a
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
opts.on("", "--default-into-account NAME", "Default into account") do |a|
|
|
299
|
-
options[:default_into_account] = a
|
|
300
|
-
end
|
|
301
|
-
|
|
302
|
-
opts.on("", "--default-outof-account NAME", "Default 'out of' account") do |a|
|
|
303
|
-
options[:default_outof_account] = a
|
|
304
|
-
end
|
|
305
|
-
|
|
306
|
-
opts.on("", "--suffixed", "If --currency should be used as a suffix. Defaults to false.") do |e|
|
|
307
|
-
options[:suffixed] = e
|
|
308
|
-
end
|
|
309
|
-
|
|
310
|
-
opts.on_tail("-h", "--help", "Show this message") do
|
|
311
|
-
puts opts
|
|
312
|
-
exit
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
opts.on_tail("--version", "Show version") do
|
|
316
|
-
puts VERSION
|
|
317
|
-
exit
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
opts.parse!(args)
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
unless options[:file]
|
|
324
|
-
options[:file] = ask("What CSV file should I parse? ")
|
|
325
|
-
unless options[:file].length > 0
|
|
326
|
-
puts "\nYou must provide a CSV file to parse.\n"
|
|
327
|
-
puts parser
|
|
328
|
-
exit
|
|
329
|
-
end
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
unless options[:bank_account]
|
|
333
|
-
fail "Please specify --account for the unattended mode" if options[:unattended]
|
|
334
|
-
|
|
335
|
-
options[:bank_account] = ask("What is the account name of this bank account in Ledger? ") do |q|
|
|
336
|
-
q.readline = true
|
|
337
|
-
q.validate = /^.{2,}$/
|
|
338
|
-
q.default = "Assets:Bank:Checking"
|
|
339
|
-
end
|
|
284
|
+
rows = []
|
|
285
|
+
each_row_backwards do |row|
|
|
286
|
+
rows << row
|
|
340
287
|
end
|
|
341
|
-
|
|
342
|
-
options
|
|
288
|
+
print_transaction(rows)
|
|
343
289
|
end
|
|
344
290
|
end
|
|
345
291
|
end
|