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.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +50 -0
  3. data/.gitignore +3 -0
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +77 -8
  6. data/Gemfile.lock +1 -5
  7. data/README.md +74 -21
  8. data/Rakefile +17 -1
  9. data/bin/build-new-version.sh +26 -0
  10. data/bin/reckon +6 -1
  11. data/lib/reckon.rb +2 -2
  12. data/lib/reckon/app.rb +140 -194
  13. data/lib/reckon/csv_parser.rb +8 -8
  14. data/lib/reckon/date_column.rb +10 -0
  15. data/lib/reckon/ledger_parser.rb +5 -1
  16. data/lib/reckon/money.rb +48 -48
  17. data/lib/reckon/options.rb +147 -0
  18. data/lib/reckon/version.rb +1 -1
  19. data/reckon.gemspec +1 -2
  20. data/spec/integration/another_bank_example/input.csv +9 -0
  21. data/spec/integration/another_bank_example/output.ledger +36 -0
  22. data/spec/integration/another_bank_example/test_args +1 -0
  23. data/spec/integration/austrian_example/input.csv +13 -0
  24. data/spec/integration/austrian_example/output.ledger +52 -0
  25. data/spec/integration/austrian_example/test_args +2 -0
  26. data/spec/integration/bom_utf8_file/input.csv +3 -0
  27. data/spec/integration/bom_utf8_file/output.ledger +4 -0
  28. data/spec/integration/bom_utf8_file/test_args +3 -0
  29. data/spec/integration/broker_canada_example/input.csv +12 -0
  30. data/spec/integration/broker_canada_example/output.ledger +48 -0
  31. data/spec/integration/broker_canada_example/test_args +1 -0
  32. data/spec/integration/chase/account_tokens_and_regex/output.ledger +36 -0
  33. data/spec/integration/chase/account_tokens_and_regex/test_args +2 -0
  34. data/spec/integration/chase/account_tokens_and_regex/tokens.yml +16 -0
  35. data/spec/integration/chase/default_account_names/output.ledger +36 -0
  36. data/spec/integration/chase/default_account_names/test_args +3 -0
  37. data/spec/integration/chase/input.csv +9 -0
  38. data/spec/integration/chase/learn_from_existing/learn.ledger +7 -0
  39. data/spec/integration/chase/learn_from_existing/output.ledger +36 -0
  40. data/spec/integration/chase/learn_from_existing/test_args +1 -0
  41. data/spec/integration/chase/simple/output.ledger +36 -0
  42. data/spec/integration/chase/simple/test_args +1 -0
  43. data/spec/integration/danish_kroner_nordea_example/input.csv +6 -0
  44. data/spec/integration/danish_kroner_nordea_example/output.ledger +24 -0
  45. data/spec/integration/danish_kroner_nordea_example/test_args +1 -0
  46. data/spec/integration/english_date_example/input.csv +3 -0
  47. data/spec/integration/english_date_example/output.ledger +12 -0
  48. data/spec/integration/english_date_example/test_args +1 -0
  49. data/spec/integration/extratofake/input.csv +24 -0
  50. data/spec/integration/extratofake/output.ledger +92 -0
  51. data/spec/integration/extratofake/test_args +1 -0
  52. data/spec/integration/french_example/input.csv +9 -0
  53. data/spec/integration/french_example/output.ledger +36 -0
  54. data/spec/integration/french_example/test_args +2 -0
  55. data/spec/integration/german_date_example/input.csv +3 -0
  56. data/spec/integration/german_date_example/output.ledger +12 -0
  57. data/spec/integration/german_date_example/test_args +1 -0
  58. data/spec/integration/harder_date_example/input.csv +5 -0
  59. data/spec/integration/harder_date_example/output.ledger +20 -0
  60. data/spec/integration/harder_date_example/test_args +1 -0
  61. data/spec/integration/ing/input.csv +3 -0
  62. data/spec/integration/ing/output.ledger +12 -0
  63. data/spec/integration/ing/test_args +1 -0
  64. data/spec/integration/intuit_mint_example/input.csv +7 -0
  65. data/spec/integration/intuit_mint_example/output.ledger +28 -0
  66. data/spec/integration/intuit_mint_example/test_args +1 -0
  67. data/spec/integration/invalid_header_example/input.csv +6 -0
  68. data/spec/integration/invalid_header_example/output.ledger +8 -0
  69. data/spec/integration/invalid_header_example/test_args +1 -0
  70. data/spec/integration/inversed_credit_card/input.csv +16 -0
  71. data/spec/integration/inversed_credit_card/output.ledger +64 -0
  72. data/spec/integration/inversed_credit_card/test_args +1 -0
  73. data/spec/integration/nationwide/input.csv +4 -0
  74. data/spec/integration/nationwide/output.ledger +16 -0
  75. data/spec/integration/nationwide/test_args +1 -0
  76. data/spec/integration/regression/issue_51_account_tokens/input.csv +8 -0
  77. data/spec/integration/regression/issue_51_account_tokens/output.ledger +32 -0
  78. data/spec/integration/regression/issue_51_account_tokens/test_args +4 -0
  79. data/spec/integration/regression/issue_51_account_tokens/tokens.yml +9 -0
  80. data/spec/integration/regression/issue_64_date_column/input.csv +3 -0
  81. data/spec/integration/regression/issue_64_date_column/output.ledger +8 -0
  82. data/spec/integration/regression/issue_64_date_column/test_args +1 -0
  83. data/spec/integration/regression/issue_73_account_token_matching/input.csv +2 -0
  84. data/spec/integration/regression/issue_73_account_token_matching/output.ledger +4 -0
  85. data/spec/integration/regression/issue_73_account_token_matching/test_args +6 -0
  86. data/spec/integration/regression/issue_73_account_token_matching/tokens.yml +8 -0
  87. data/spec/integration/regression/issue_85_date_example/input.csv +2 -0
  88. data/spec/integration/regression/issue_85_date_example/output.ledger +8 -0
  89. data/spec/integration/regression/issue_85_date_example/test_args +1 -0
  90. data/spec/integration/spanish_date_example/input.csv +3 -0
  91. data/spec/integration/spanish_date_example/output.ledger +12 -0
  92. data/spec/integration/spanish_date_example/test_args +1 -0
  93. data/spec/integration/suntrust/input.csv +7 -0
  94. data/spec/integration/suntrust/output.ledger +28 -0
  95. data/spec/integration/suntrust/test_args +1 -0
  96. data/spec/integration/test.sh +83 -0
  97. data/spec/integration/test_money_column/input.csv +3 -0
  98. data/spec/integration/test_money_column/output.ledger +8 -0
  99. data/spec/integration/test_money_column/test_args +1 -0
  100. data/spec/integration/two_money_columns/input.csv +5 -0
  101. data/spec/integration/two_money_columns/output.ledger +20 -0
  102. data/spec/integration/two_money_columns/test_args +1 -0
  103. data/spec/integration/yyyymmdd_date_example/input.csv +1 -0
  104. data/spec/integration/yyyymmdd_date_example/output.ledger +4 -0
  105. data/spec/integration/yyyymmdd_date_example/test_args +1 -0
  106. data/spec/reckon/app_spec.rb +25 -7
  107. data/spec/reckon/csv_parser_spec.rb +5 -0
  108. data/spec/reckon/ledger_parser_spec.rb +19 -4
  109. data/spec/reckon/money_column_spec.rb +24 -24
  110. data/spec/reckon/money_spec.rb +13 -32
  111. data/spec/reckon/options_spec.rb +17 -0
  112. data/spec/spec_helper.rb +6 -1
  113. metadata +98 -59
  114. data/.travis.yml +0 -13
@@ -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] }.reject(&:empty?).join("; ").squeeze(" ").gsub(/(;\s+){2,}/, '').strip
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 += 10 if entry =~ /\b(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i
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 = [ options[:money_column] - 1 ]
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
@@ -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
@@ -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*$/ || entry =~ /^[;#%|*]/
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( amount, options = {} )
9
- if options[:inverse]
10
- @amount = -1*amount.to_f
11
- else
12
- @amount = amount.to_f
13
- end
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( -@amount, :currency => @currency, :suffixed => @suffixed )
32
+ Money.new(-@amount, :currency => @currency, :suffixed => @suffixed)
24
33
  end
25
34
 
26
- def <=>( mon )
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( negate = false )
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 Money::from_s( value, options = {} )
60
+ def parse(value, options = {})
61
+ value = value.to_s
46
62
  # Empty string is treated as money with value 0
47
- return Money.new( 0.00, options ) if value.empty?
48
-
49
- # Remove 1000 separaters and replace , with . if comma_separates_cents
50
- # 1.000,00 -> 1000.00
51
- value = value.gsub(/\./, '').gsub(/,/, '.') if options[:comma_separates_cents]
52
- value = value.gsub(/,/, '')
53
-
54
- money_format_regex = /^(.*?)(\d+\.\d\d)/ # Money has two decimal precision
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( entry )
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( arr = [], options = {} )
87
- arr.each { |str| self.push( Money.from_s( str, options ) ) }
87
+ def initialize(arr = [], options = {})
88
+ arr.each { |str| push(Money.new(str, options)) }
88
89
  end
89
90
 
90
91
  def positive?
91
- self.each do |money|
92
- return false if money < 0 if money
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!( other_column )
98
+ def merge!(other_column)
98
99
  invert = false
99
- invert = true if self.positive? && other_column.positive?
100
- self.each_with_index do |mon, i|
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 (!mon || !other)
103
- if mon != 0.00 && other == 0.0
104
- if invert
105
- self[i]= -mon
106
- end
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
- return nil
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
@@ -1,3 +1,3 @@
1
1
  module Reckon
2
- VERSION = "0.5.3"
2
+ VERSION="0.7.0"
3
3
  end
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