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
data/lib/reckon/csv_parser.rb
CHANGED
|
@@ -53,7 +53,12 @@ module Reckon
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def description_for(index)
|
|
56
|
-
description_column_indices.map { |i| columns[i][index]
|
|
56
|
+
description_column_indices.map { |i| columns[i][index].to_s.strip }
|
|
57
|
+
.reject(&:empty?)
|
|
58
|
+
.join("; ")
|
|
59
|
+
.squeeze(" ")
|
|
60
|
+
.gsub(/(;\s+){2,}/, '')
|
|
61
|
+
.strip
|
|
57
62
|
end
|
|
58
63
|
|
|
59
64
|
def row(index)
|
|
@@ -84,12 +89,7 @@ module Reckon
|
|
|
84
89
|
money_score += Money::likelihood( entry )
|
|
85
90
|
possible_neg_money_count += 1 if entry =~ /^\$?[\-\(]\$?\d+/
|
|
86
91
|
possible_pos_money_count += 1 if entry =~ /^\+?\$?\+?\d+/
|
|
87
|
-
date_score +=
|
|
88
|
-
date_score += 5 if entry =~ /^[\-\/\.\d:\[\]]+$/
|
|
89
|
-
date_score += entry.gsub(/[^\-\/\.\d:\[\]]/, '').length if entry.gsub(/[^\-\/\.\d:\[\]]/, '').length > 3
|
|
90
|
-
date_score -= entry.gsub(/[\-\/\.\d:\[\]]/, '').length
|
|
91
|
-
date_score += 30 if entry =~ /^\d+[:\/\.-]\d+[:\/\.-]\d+([ :]\d+[:\/\.]\d+)?$/
|
|
92
|
-
date_score += 10 if entry =~ /^\d+\[\d+:GMT\]$/i
|
|
92
|
+
date_score += DateColumn.likelihood(entry)
|
|
93
93
|
|
|
94
94
|
# Try to determine if this is a balance column
|
|
95
95
|
entry_as_num = entry.gsub(/[^\-\d\.]/, '').to_f
|
|
@@ -163,7 +163,7 @@ module Reckon
|
|
|
163
163
|
results = evaluate_columns(columns)
|
|
164
164
|
|
|
165
165
|
if options[:money_column]
|
|
166
|
-
self.money_column_indices = [
|
|
166
|
+
self.money_column_indices = [options[:money_column] - 1]
|
|
167
167
|
else
|
|
168
168
|
self.money_column_indices = results.select { |n| n[:is_money_column] }.map { |n| n[:index] }
|
|
169
169
|
if self.money_column_indices.length == 1
|
data/lib/reckon/date_column.rb
CHANGED
|
@@ -56,5 +56,15 @@ module Reckon
|
|
|
56
56
|
date.iso8601
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
def self.likelihood(entry)
|
|
60
|
+
date_score = 0
|
|
61
|
+
date_score += 10 if entry =~ /\b(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i
|
|
62
|
+
date_score += 5 if entry =~ /^[\-\/\.\d:\[\]]+$/
|
|
63
|
+
date_score += entry.gsub(/[^\-\/\.\d:\[\]]/, '').length if entry.gsub(/[^\-\/\.\d:\[\]]/, '').length > 3
|
|
64
|
+
date_score -= entry.gsub(/[\-\/\.\d:\[\]]/, '').length
|
|
65
|
+
date_score += 30 if entry =~ /^\d+[:\/\.-]\d+[:\/\.-]\d+([ :]\d+[:\/\.]\d+)?$/
|
|
66
|
+
date_score += 10 if entry =~ /^\d+\[\d+:GMT\]$/i
|
|
67
|
+
return date_score
|
|
68
|
+
end
|
|
59
69
|
end
|
|
60
70
|
end
|
data/lib/reckon/ledger_parser.rb
CHANGED
|
@@ -122,12 +122,13 @@ module Reckon
|
|
|
122
122
|
@entries = []
|
|
123
123
|
new_entry = {}
|
|
124
124
|
in_comment = false
|
|
125
|
+
comment_chars = ';#%*|'
|
|
125
126
|
ledger.strip.split("\n").each do |entry|
|
|
126
127
|
# strip comment lines
|
|
127
128
|
in_comment = true if entry == 'comment'
|
|
128
129
|
in_comment = false if entry == 'end comment'
|
|
129
130
|
next if in_comment
|
|
130
|
-
next if entry =~ /^\s
|
|
131
|
+
next if entry =~ /^\s*[#{comment_chars}]/
|
|
131
132
|
|
|
132
133
|
# (date, type, code, description), type and code are optional
|
|
133
134
|
if (m = entry.match(%r{^(\d+[\d/-]+)\s+([*!])?\s*(\([^)]+\))?\s*(.*)$}))
|
|
@@ -139,6 +140,9 @@ module Reckon
|
|
|
139
140
|
desc: m[4].strip,
|
|
140
141
|
accounts: []
|
|
141
142
|
}
|
|
143
|
+
elsif entry =~ /^\s*$/ && new_entry[:date]
|
|
144
|
+
add_entry(new_entry)
|
|
145
|
+
new_entry = {}
|
|
142
146
|
elsif new_entry[:date] && entry =~ /^\s+/
|
|
143
147
|
LOGGER.info("Adding new account #{entry}")
|
|
144
148
|
new_entry[:accounts] << parse_account_line(entry)
|
data/lib/reckon/money.rb
CHANGED
|
@@ -5,12 +5,13 @@ module Reckon
|
|
|
5
5
|
class Money
|
|
6
6
|
include Comparable
|
|
7
7
|
attr_accessor :amount, :currency, :suffixed
|
|
8
|
-
def initialize(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
def initialize(amount, options = {})
|
|
9
|
+
@options = options
|
|
10
|
+
@amount_raw = amount
|
|
11
|
+
@raw = options[:raw]
|
|
12
|
+
|
|
13
|
+
@amount = parse(amount, options)
|
|
14
|
+
@amount = -@amount if options[:inverse]
|
|
14
15
|
@currency = options[:currency] || "$"
|
|
15
16
|
@suffixed = options[:suffixed]
|
|
16
17
|
end
|
|
@@ -19,11 +20,19 @@ module Reckon
|
|
|
19
20
|
return @amount
|
|
20
21
|
end
|
|
21
22
|
|
|
23
|
+
def to_s
|
|
24
|
+
return @options[:raw] ? "#{@amount_raw} | #{@amount}" : @amount
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# unary minus
|
|
28
|
+
# ex
|
|
29
|
+
# m = Money.new
|
|
30
|
+
# -m
|
|
22
31
|
def -@
|
|
23
|
-
Money.new(
|
|
32
|
+
Money.new(-@amount, :currency => @currency, :suffixed => @suffixed)
|
|
24
33
|
end
|
|
25
34
|
|
|
26
|
-
def <=>(
|
|
35
|
+
def <=>(mon)
|
|
27
36
|
other_amount = mon.to_f
|
|
28
37
|
if @amount < other_amount
|
|
29
38
|
-1
|
|
@@ -34,7 +43,13 @@ module Reckon
|
|
|
34
43
|
end
|
|
35
44
|
end
|
|
36
45
|
|
|
37
|
-
def pretty(
|
|
46
|
+
def pretty(negate = false)
|
|
47
|
+
if @raw
|
|
48
|
+
return @amount_raw unless negate
|
|
49
|
+
|
|
50
|
+
return @amount_raw[0] == '-' ? @amount_raw[1..-1] : "-#{@amount_raw}"
|
|
51
|
+
end
|
|
52
|
+
|
|
38
53
|
if @suffixed
|
|
39
54
|
(@amount >= 0 ? " " : "") + sprintf("%0.2f #{@currency}", @amount * (negate ? -1 : 1))
|
|
40
55
|
else
|
|
@@ -42,34 +57,20 @@ module Reckon
|
|
|
42
57
|
end
|
|
43
58
|
end
|
|
44
59
|
|
|
45
|
-
def
|
|
60
|
+
def parse(value, options = {})
|
|
61
|
+
value = value.to_s
|
|
46
62
|
# Empty string is treated as money with value 0
|
|
47
|
-
return
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
value = value.
|
|
52
|
-
value = value.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
any_number_regex = /^(.*?)([\d\.]+)/
|
|
56
|
-
|
|
57
|
-
# Prefer matching the money_format, match any number otherwise
|
|
58
|
-
m = value.match( money_format_regex ) ||
|
|
59
|
-
value.match( any_number_regex )
|
|
60
|
-
if m
|
|
61
|
-
amount = m[2].to_f
|
|
62
|
-
# Check whether the money had a - or (, which indicates negative amounts
|
|
63
|
-
if (m[1].match( /^[\(-]/ ) || m[1].match( /-$/ ))
|
|
64
|
-
amount *= -1
|
|
65
|
-
end
|
|
66
|
-
return Money.new( amount, options )
|
|
67
|
-
else
|
|
68
|
-
return nil
|
|
69
|
-
end
|
|
63
|
+
return value.to_f if value.to_s.empty?
|
|
64
|
+
|
|
65
|
+
invert = value.match(/^\(.*\)$/)
|
|
66
|
+
value = value.gsub(/[^0-9,.-]/, '')
|
|
67
|
+
value = value.tr('.', '').tr(',', '.') if options[:comma_separates_cents]
|
|
68
|
+
value = value.tr(',', '')
|
|
69
|
+
value = value.to_f
|
|
70
|
+
return invert ? -value : value
|
|
70
71
|
end
|
|
71
72
|
|
|
72
|
-
def Money::likelihood(
|
|
73
|
+
def Money::likelihood(entry)
|
|
73
74
|
money_score = 0
|
|
74
75
|
# digits separated by , or . with no more than 2 trailing digits
|
|
75
76
|
money_score += 40 if entry.match(/\d+[,.]\d{2}[^\d]*$/)
|
|
@@ -83,31 +84,30 @@ module Reckon
|
|
|
83
84
|
end
|
|
84
85
|
|
|
85
86
|
class MoneyColumn < Array
|
|
86
|
-
def initialize(
|
|
87
|
-
arr.each { |str|
|
|
87
|
+
def initialize(arr = [], options = {})
|
|
88
|
+
arr.each { |str| push(Money.new(str, options)) }
|
|
88
89
|
end
|
|
89
90
|
|
|
90
91
|
def positive?
|
|
91
|
-
|
|
92
|
-
return false if money < 0
|
|
92
|
+
each do |money|
|
|
93
|
+
return false if money && money < 0
|
|
93
94
|
end
|
|
94
95
|
true
|
|
95
96
|
end
|
|
96
97
|
|
|
97
|
-
def merge!(
|
|
98
|
+
def merge!(other_column)
|
|
98
99
|
invert = false
|
|
99
|
-
invert = true if
|
|
100
|
-
|
|
100
|
+
invert = true if positive? && other_column.positive?
|
|
101
|
+
each_with_index do |mon, i|
|
|
101
102
|
other = other_column[i]
|
|
102
|
-
return nil if
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
elsif mon == 0.00 && other != 0.00
|
|
103
|
+
return nil if !mon || !other
|
|
104
|
+
|
|
105
|
+
if mon != 0.0 && other == 0.0
|
|
106
|
+
self[i] = -mon if invert
|
|
107
|
+
elsif mon == 0.0 && other != 0.0
|
|
108
108
|
self[i] = other
|
|
109
109
|
else
|
|
110
|
-
|
|
110
|
+
self[i] = Money.new(0)
|
|
111
111
|
end
|
|
112
112
|
end
|
|
113
113
|
self
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
module Reckon
|
|
2
|
+
class Options
|
|
3
|
+
def self.parse(args = ARGV, stdin = $stdin)
|
|
4
|
+
options = { output_file: $stdout }
|
|
5
|
+
OptionParser.new do |opts|
|
|
6
|
+
opts.banner = "Usage: Reckon.rb [options]"
|
|
7
|
+
opts.separator ""
|
|
8
|
+
|
|
9
|
+
opts.on("-f", "--file FILE", "The CSV file to parse") do |file|
|
|
10
|
+
options[:file] = file
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
opts.on("-a", "--account NAME", "The Ledger Account this file is for") do |a|
|
|
14
|
+
options[:bank_account] = a
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
|
18
|
+
options[:verbose] = v
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
opts.on("-i", "--inverse", "Use the negative of each amount") do |v|
|
|
22
|
+
options[:inverse] = v
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
opts.on("-p", "--print-table", "Print out the parsed CSV in table form") do |p|
|
|
26
|
+
options[:print_table] = p
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
opts.on("-o", "--output-file FILE", "The ledger file to append to") do |o|
|
|
30
|
+
options[:output_file] = File.open(o, 'a')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
opts.on("-l", "--learn-from FILE", "An existing ledger file to learn accounts from") do |l|
|
|
34
|
+
options[:existing_ledger_file] = l
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
opts.on("", "--ignore-columns 1,2,5", "Columns to ignore, starts from 1") do |ignore|
|
|
38
|
+
options[:ignore_columns] = ignore.split(",").map(&:to_i)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
opts.on("", "--money-column 2", Integer, "Column number of the money column, starts from 1") do |col|
|
|
42
|
+
options[:money_column] = col
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
opts.on("", "--raw-money", "Don't format money column (for stocks)") do |n|
|
|
46
|
+
options[:raw] = n
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
opts.on("", "--date-column 3", Integer, "Column number of the date column, starts from 1") do |col|
|
|
50
|
+
options[:date_column] = col
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
opts.on("", "--contains-header [N]", Integer, "Skip N header rows - default 1") do |hdr|
|
|
54
|
+
options[:contains_header] = 1
|
|
55
|
+
options[:contains_header] = hdr.to_i
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
opts.on("", "--csv-separator ','", "CSV separator (default ',')") do |sep|
|
|
59
|
+
options[:csv_separator] = sep
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
opts.on("", "--comma-separates-cents", "Use comma to separate cents ($100,50 vs. $100.50)") do |c|
|
|
63
|
+
options[:comma_separates_cents] = c
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
opts.on("", "--encoding 'UTF-8'", "Specify an encoding for the CSV file") do |e|
|
|
67
|
+
options[:encoding] = e
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
opts.on("-c", "--currency '$'", "Currency symbol to use - default $ (ex £, EUR)") do |e|
|
|
71
|
+
options[:currency] = e
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
opts.on("", "--date-format '%d/%m/%Y'", "Force the date format (see Ruby DateTime strftime)") do |d|
|
|
75
|
+
options[:date_format] = d
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
opts.on("-u", "--unattended", "Don't ask questions and guess all the accounts automatically. Use with --learn-from or --account-tokens options.") do |n|
|
|
79
|
+
options[:unattended] = n
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
opts.on("-t", "--account-tokens FILE", "YAML file with manually-assigned tokens for each account (see README)") do |a|
|
|
83
|
+
options[:account_tokens_file] = a
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
options[:default_into_account] = 'Expenses:Unknown'
|
|
87
|
+
opts.on("", "--default-into-account NAME", "Default into account") do |a|
|
|
88
|
+
options[:default_into_account] = a
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
options[:default_outof_account] = 'Income:Unknown'
|
|
92
|
+
opts.on("", "--default-outof-account NAME", "Default 'out of' account") do |a|
|
|
93
|
+
options[:default_outof_account] = a
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
opts.on("", "--fail-on-unknown-account", "Fail on unmatched transactions.") do |n|
|
|
97
|
+
options[:fail_on_unknown_account] = n
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
opts.on("", "--suffixed", "Append currency symbol as a suffix.") do |e|
|
|
101
|
+
options[:suffixed] = e
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
|
105
|
+
puts opts
|
|
106
|
+
exit
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
opts.on_tail("--version", "Show version") do
|
|
110
|
+
puts VERSION
|
|
111
|
+
exit
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
opts.parse!(args)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
if options[:file] == '-'
|
|
118
|
+
unless options[:unattended]
|
|
119
|
+
raise "--unattended is required to use STDIN as CSV source."
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
options[:string] = stdin.read
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
unless options[:file]
|
|
126
|
+
options[:file] = @@cli.ask("What CSV file should I parse? ")
|
|
127
|
+
unless options[:file].empty?
|
|
128
|
+
puts "\nYou must provide a CSV file to parse.\n"
|
|
129
|
+
puts parser
|
|
130
|
+
exit
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
unless options[:bank_account]
|
|
135
|
+
raise "Must specify --account in unattended mode" if options[:unattended]
|
|
136
|
+
|
|
137
|
+
options[:bank_account] = @@cli.ask("What is this account named in Ledger? ") do |q|
|
|
138
|
+
q.readline = true
|
|
139
|
+
q.validate = /^.{2,}$/
|
|
140
|
+
q.default = "Assets:Bank:Checking"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
return options
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
data/lib/reckon/version.rb
CHANGED
data/reckon.gemspec
CHANGED
|
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
|
|
|
13
13
|
|
|
14
14
|
s.files = `git ls-files`.split("\n")
|
|
15
15
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
16
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
|
17
17
|
s.require_paths = ["lib"]
|
|
18
18
|
|
|
19
19
|
s.add_development_dependency "rspec", ">= 1.2.9"
|
|
@@ -21,6 +21,5 @@ Gem::Specification.new do |s|
|
|
|
21
21
|
s.add_development_dependency "rantly", "= 1.2.0"
|
|
22
22
|
s.add_runtime_dependency "chronic", ">= 0.3.0"
|
|
23
23
|
s.add_runtime_dependency "highline", ">= 1.5.2"
|
|
24
|
-
s.add_runtime_dependency "terminal-table", ">= 1.4.2"
|
|
25
24
|
s.add_runtime_dependency "rchardet", ">= 1.8.0"
|
|
26
25
|
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
DEBIT,2011/12/24,"HOST 037196321563 MO 12/22SLICEHOST",($85.00)
|
|
2
|
+
CHECK,2010/12/24,"CHECK 2656",($20.00)
|
|
3
|
+
DEBIT,2009/12/24,"GITHUB 041287430274 CA 12/22GITHUB 04",($7.00)
|
|
4
|
+
CREDIT,2008/12/24,"Some Company vendorpymt PPD ID: 59728JSL20",$3520.00
|
|
5
|
+
CREDIT,2007/12/24,"Blarg BLARG REVENUE PPD ID: 00jah78563",$1558.52
|
|
6
|
+
DEBIT,2006/12/24,"WEBSITE-BALANCE-17DEC09 12 12/17WEBSITE-BAL",$.23
|
|
7
|
+
DEBIT,2005/12/24,"WEBSITE-BALANCE-10DEC09 12 12/10WEBSITE-BAL",($0.96)
|
|
8
|
+
CREDIT,2004/12/24,"PAYPAL TRANSFER PPD ID: PAYPALSDSL",($116.22)
|
|
9
|
+
CREDIT,2003/12/24,"Some Company vendorpymt PPD ID: 5KL3832735",$2105.00
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
2003-12-24 CREDIT; Some Company vendorpymt PPD ID: 5KL3832735
|
|
2
|
+
Assets:Bank:Checking $2105.00
|
|
3
|
+
Income:Unknown
|
|
4
|
+
|
|
5
|
+
2004-12-24 CREDIT; PAYPAL TRANSFER PPD ID: PAYPALSDSL
|
|
6
|
+
Income:Unknown
|
|
7
|
+
Assets:Bank:Checking -$116.22
|
|
8
|
+
|
|
9
|
+
2005-12-24 DEBIT; WEBSITE-BALANCE-10DEC09 12 12/10WEBSITE-BAL
|
|
10
|
+
Expenses:Unknown
|
|
11
|
+
Assets:Bank:Checking -$0.96
|
|
12
|
+
|
|
13
|
+
2006-12-24 DEBIT; WEBSITE-BALANCE-17DEC09 12 12/17WEBSITE-BAL
|
|
14
|
+
Assets:Bank:Checking $0.23
|
|
15
|
+
Expenses:Unknown
|
|
16
|
+
|
|
17
|
+
2007-12-24 CREDIT; Blarg BLARG REVENUE PPD ID: 00jah78563
|
|
18
|
+
Assets:Bank:Checking $1558.52
|
|
19
|
+
Income:Unknown
|
|
20
|
+
|
|
21
|
+
2008-12-24 CREDIT; Some Company vendorpymt PPD ID: 59728JSL20
|
|
22
|
+
Assets:Bank:Checking $3520.00
|
|
23
|
+
Income:Unknown
|
|
24
|
+
|
|
25
|
+
2009-12-24 DEBIT; GITHUB 041287430274 CA 12/22GITHUB 04
|
|
26
|
+
Expenses:Unknown
|
|
27
|
+
Assets:Bank:Checking -$7.00
|
|
28
|
+
|
|
29
|
+
2010-12-24 CHECK; CHECK 2656
|
|
30
|
+
Expenses:Unknown
|
|
31
|
+
Assets:Bank:Checking -$20.00
|
|
32
|
+
|
|
33
|
+
2011-12-24 DEBIT; HOST 037196321563 MO 12/22SLICEHOST
|
|
34
|
+
Expenses:Unknown
|
|
35
|
+
Assets:Bank:Checking -$85.00
|
|
36
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
-f input.csv --unattended --account Assets:Bank:Checking
|