ebutil 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 687efb0a972fdd133edf1b89fcefa1517fa81752
4
+ data.tar.gz: c6a6a6163da72f1a34687fd053194eb332547f89
5
+ SHA512:
6
+ metadata.gz: 4748cd886ad3a1ffb6b4fb252b84b3c429e0c12bf1a06f9788eeec998a26d6f59f8141845bd0e52f0a534a9c5b85231a3c60046cdc70d8e30805e1bd6be8bec0
7
+ data.tar.gz: 0985e7b1d65543cc454f9e38df697c26fcf1941b01336e01db120d1f7b1821a7a12c22f836c84088089e8db296ae21f89c0104e5a1bff7d74504ace36c36c1f9
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+ gem 'thor', '> 0.14.0'
6
+
7
+ # Add dependencies to develop your gem here.
8
+ # Include everything needed to run rake, tests, features, etc.
9
+ group :development do
10
+ gem "rspec"
11
+ gem "bundler"
12
+ gem "jeweler"
13
+ gem "simplecov"
14
+ gem "guard-rspec"
15
+ gem "rb-readline"
16
+ end
17
+
data/Gemfile.lock ADDED
@@ -0,0 +1,100 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ addressable (2.3.5)
5
+ builder (3.2.2)
6
+ coderay (1.0.9)
7
+ diff-lcs (1.2.4)
8
+ faraday (0.8.7)
9
+ multipart-post (~> 1.1)
10
+ ffi (1.9.0)
11
+ formatador (0.2.4)
12
+ git (1.2.5)
13
+ github_api (0.10.1)
14
+ addressable
15
+ faraday (~> 0.8.1)
16
+ hashie (>= 1.2)
17
+ multi_json (~> 1.4)
18
+ nokogiri (~> 1.5.2)
19
+ oauth2
20
+ guard (1.8.1)
21
+ formatador (>= 0.2.4)
22
+ listen (>= 1.0.0)
23
+ lumberjack (>= 1.0.2)
24
+ pry (>= 0.9.10)
25
+ thor (>= 0.14.6)
26
+ guard-rspec (3.0.2)
27
+ guard (>= 1.8)
28
+ rspec (~> 2.13)
29
+ hashie (2.0.5)
30
+ highline (1.6.19)
31
+ httpauth (0.2.0)
32
+ jeweler (1.8.6)
33
+ builder
34
+ bundler (~> 1.0)
35
+ git (>= 1.2.5)
36
+ github_api (= 0.10.1)
37
+ highline (>= 1.6.15)
38
+ nokogiri (= 1.5.10)
39
+ rake
40
+ rdoc
41
+ json (1.8.0)
42
+ jwt (0.1.8)
43
+ multi_json (>= 1.5)
44
+ listen (1.2.2)
45
+ rb-fsevent (>= 0.9.3)
46
+ rb-inotify (>= 0.9)
47
+ rb-kqueue (>= 0.2)
48
+ lumberjack (1.0.4)
49
+ method_source (0.8.1)
50
+ multi_json (1.7.7)
51
+ multi_xml (0.5.4)
52
+ multipart-post (1.2.0)
53
+ nokogiri (1.5.10)
54
+ oauth2 (0.9.2)
55
+ faraday (~> 0.8)
56
+ httpauth (~> 0.2)
57
+ jwt (~> 0.1.4)
58
+ multi_json (~> 1.0)
59
+ multi_xml (~> 0.5)
60
+ rack (~> 1.2)
61
+ pry (0.9.12.2)
62
+ coderay (~> 1.0.5)
63
+ method_source (~> 0.8)
64
+ slop (~> 3.4)
65
+ rack (1.5.2)
66
+ rake (10.1.0)
67
+ rb-fsevent (0.9.3)
68
+ rb-inotify (0.9.0)
69
+ ffi (>= 0.5.0)
70
+ rb-kqueue (0.2.0)
71
+ ffi (>= 0.5.0)
72
+ rb-readline (0.5.1)
73
+ rdoc (4.0.1)
74
+ json (~> 1.4)
75
+ rspec (2.14.0)
76
+ rspec-core (~> 2.14.0)
77
+ rspec-expectations (~> 2.14.0)
78
+ rspec-mocks (~> 2.14.0)
79
+ rspec-core (2.14.0)
80
+ rspec-expectations (2.14.0)
81
+ diff-lcs (>= 1.1.3, < 2.0)
82
+ rspec-mocks (2.14.1)
83
+ simplecov (0.7.1)
84
+ multi_json (~> 1.0)
85
+ simplecov-html (~> 0.7.1)
86
+ simplecov-html (0.7.1)
87
+ slop (3.4.5)
88
+ thor (0.18.1)
89
+
90
+ PLATFORMS
91
+ ruby
92
+
93
+ DEPENDENCIES
94
+ bundler
95
+ guard-rspec
96
+ jeweler
97
+ rb-readline
98
+ rspec
99
+ simplecov
100
+ thor (> 0.14.0)
data/Guardfile ADDED
@@ -0,0 +1,10 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ end
10
+
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Rob Westgeest
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ = ebutil
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to ebutil
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
+ * Fork the project
10
+ * Start a feature/bugfix branch
11
+ * Commit and push until you are happy with your contribution
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2011 Rob Westgeest. See LICENSE.txt for
18
+ further details.
19
+
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "ebutil"
16
+ gem.executables = "ebutil"
17
+ gem.license = "MIT"
18
+ gem.summary = %Q{utility script for eekboek shell}
19
+ gem.description = %Q{utility script for eekboek - converts bank transaction export to eb script}
20
+ gem.email = "rob@qwan.it"
21
+ gem.authors = ["Rob Westgeest"]
22
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
23
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
+ gem.add_runtime_dependency 'thor', '> 0.14.0'
25
+ gem.add_development_dependency 'rspec', '> 2.5.0'
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rspec/core'
30
+ require 'rspec/core/rake_task'
31
+ RSpec::Core::RakeTask.new(:spec) do |spec|
32
+ spec.pattern = FileList['spec/**/*_spec.rb']
33
+ end
34
+
35
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
36
+ spec.pattern = 'spec/**/*_spec.rb'
37
+ spec.rcov = true
38
+ end
39
+
40
+ task :default => :spec
41
+
42
+ require 'rdoc/task'
43
+ Rake::RDocTask.new do |rdoc|
44
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
45
+
46
+ rdoc.rdoc_dir = 'rdoc'
47
+ rdoc.title = "ebutil #{version}"
48
+ rdoc.rdoc_files.include('README*')
49
+ rdoc.rdoc_files.include('lib/**/*.rb')
50
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.2
data/bin/ebutil ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ require "bundler/setup"
4
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
5
+ require 'ebutil'
6
+ Ebutil.start
data/lib/ebutil.rb ADDED
@@ -0,0 +1,231 @@
1
+ require 'thor'
2
+ require 'ostruct'
3
+ require 'date'
4
+ class Ebutil < Thor
5
+ class_option :begin_saldo, :default => '0'
6
+ desc "import_csv <filename>", "imports csv bank transactions and produces an eb file for that"
7
+ def import_csv(filename)
8
+ BankSheet.new(Decimal.from_string(options[:begin_saldo])).convert_to_eb(filename,
9
+ CsvParser.new("Datum" => :date,
10
+ "Naam / Omschrijving" => :description,
11
+ "Tegenrekening" => :other_account,
12
+ "Af Bij" => :type,
13
+ "Bedrag (EUR)" => :amount),
14
+ Relations.new)
15
+ end
16
+ end
17
+
18
+ class Relations
19
+ def find_by_account(accoun)
20
+ return '?'
21
+ end
22
+ end
23
+
24
+ class BankSheet
25
+ attr_reader :transactions
26
+ attr_reader :balance
27
+ attr_reader :date
28
+
29
+ def initialize(initial_balance = Decimal.from_whole_and_part(0), date = Date.today)
30
+ @balance = initial_balance
31
+ @transactions = []
32
+ @date = date
33
+ end
34
+
35
+ def convert_to_eb filename, parser, relations
36
+ extract_date_from_filename filename
37
+ import(File.read(filename), parser)
38
+ File.open(eb_filename(filename), "w+") do |f|
39
+ f.write(export_to_eb(relations))
40
+ end
41
+ end
42
+
43
+ def import file_content, parser
44
+ parser.parse(file_content) do |attributes|
45
+ self << Transaction.send(type_for(attributes.delete(:type)), attributes)
46
+ end
47
+ end
48
+
49
+ def export_to_eb(relations)
50
+ result = ["ingbank:#{identification} #{date.strftime("%d-%m-%Y")} \"Afschrift per #{identification}\" --saldo=#{balance.to_s}"]
51
+ result += transactions.collect {|t| t.export_to_eb(relations)}
52
+ result.join(" \\\n ")
53
+ end
54
+
55
+ def <<(transaction)
56
+ transactions << transaction
57
+ @balance = transaction.add_to(@balance)
58
+ end
59
+
60
+ private
61
+ def identification
62
+ @date.strftime("%m%d")
63
+ end
64
+ def extract_date_from_filename(filename)
65
+ @date = Date.parse(File.basename(filename).split('_')[2])
66
+ end
67
+ def type_for(value)
68
+ value == 'Af' && :credit || :debit
69
+ end
70
+ def eb_filename(csv_filename)
71
+ csv_filename.sub(/csv$/, 'eb')
72
+ end
73
+ end
74
+
75
+ class Decimal
76
+ def initialize(value)
77
+ @value = value
78
+ end
79
+
80
+ def self.from_string(string, thousand_sep = '.', decimal_sep = ',')
81
+ string = string.to_s
82
+ from_whole_and_part(*string.split(decimal_sep))
83
+ end
84
+
85
+ def self.from_whole_and_part(whole, part = 0)
86
+ new( whole.to_i * 100 + part.to_i * (whole.to_i >= 0 ? 1 : -1))
87
+ end
88
+
89
+ def sign
90
+ value >= 0 ? 1 : -1
91
+ end
92
+
93
+ def whole
94
+ value.abs / 100
95
+ end
96
+
97
+ def part
98
+ value.abs % 100
99
+ end
100
+
101
+ def +(other)
102
+ return Decimal.from_value(value+other.value)
103
+ end
104
+
105
+ def -(other)
106
+ return Decimal.from_value(value - other.value)
107
+ end
108
+
109
+ def inverse
110
+ return Decimal.new(-value)
111
+ end
112
+
113
+ def to_s(decimal_sep = ',')
114
+ sprintf("#{string_sign}%d#{decimal_sep}%02d", whole, part)
115
+ end
116
+ alias_method :inspect, :to_s
117
+
118
+ def ==(other)
119
+ value == other.value
120
+ end
121
+
122
+ protected
123
+ attr_reader :value
124
+
125
+ def string_sign
126
+ sign < 0 && '-' || ''
127
+ end
128
+
129
+ def self.from_value(value)
130
+ new(value)
131
+ end
132
+ end
133
+
134
+ class Transaction < OpenStruct
135
+ def initialize(attributes)
136
+ @attributes = OpenStruct.new(attributes)
137
+ end
138
+ def self.credit(attributes)
139
+ Credit.new(attributes)
140
+ end
141
+ def self.debit(attributes)
142
+ Debit.new(attributes)
143
+ end
144
+ def export_to_eb(relations)
145
+ "#{kind} #{date} #{relations.find_by_account(other_account)} #{amount}"
146
+ end
147
+ def add_to(balance)
148
+ balance + amount
149
+ end
150
+ def method_missing(method, *args)
151
+ @attributes.send(method, *args)
152
+ end
153
+ end
154
+
155
+ class Credit < Transaction
156
+ def kind
157
+ 'crd'
158
+ end
159
+ def amount
160
+ Decimal.from_string(@attributes.amount).inverse
161
+ end
162
+ end
163
+
164
+ class Debit < Transaction
165
+ def kind
166
+ 'deb'
167
+ end
168
+ def amount
169
+ Decimal.from_string(@attributes.amount)
170
+ end
171
+ end
172
+
173
+ class String
174
+ def unquote()
175
+ self.sub(/^\"/, '').sub(/\"$/,'')
176
+ end
177
+ end
178
+ class CsvParser
179
+ class HeaderException < Exception
180
+ def self.translations_missing(translations)
181
+ new("header should but does not contain all mandatory keys; missing #{translations.keys.join(',')}")
182
+ end
183
+ end
184
+
185
+ def initialize translations
186
+ @translations = translations
187
+ end
188
+
189
+ def parse(csv_content, &block)
190
+ lines = csv_content.encode('utf-8', 'binary', :invalid => :replace, :undef => :replace).split($/)
191
+ LineParser.new(parse_keys(lines.shift), &block).parse(lines)
192
+ end
193
+
194
+ def parse_keys(head)
195
+ head.strip!
196
+ keys = {}
197
+ translations = @translations.dup
198
+ head.split('","').each_with_index do |header_cell, index|
199
+ header_cell = header_cell.unquote
200
+ keys[index] = translations.delete header_cell if @translations.has_key?(header_cell)
201
+ end
202
+ raise HeaderException.translations_missing(translations) unless translations.empty?
203
+ return keys
204
+ end
205
+
206
+ end
207
+
208
+ class LineParser
209
+
210
+ def initialize(keys, &line_response_block)
211
+ @keys = keys
212
+ @line_response_block = line_response_block
213
+ end
214
+
215
+ def parse(lines)
216
+ lines.each { |line| parse_line(line) }
217
+ end
218
+
219
+ def parse_line(line)
220
+ line.strip!
221
+ attributes = {}
222
+ line.split('","').each_with_index do |cell, index|
223
+ cell = cell.unquote
224
+ attributes[keys[index]] = cell if keys.has_key?(index)
225
+ end
226
+ line_response_block.call attributes
227
+ end
228
+
229
+ private
230
+ attr_reader :keys, :line_response_block
231
+ end
@@ -0,0 +1,202 @@
1
+ require File.expand_path File.join((File.dirname __FILE__), 'spec_helper')
2
+
3
+ describe BankSheet do
4
+ describe "import" do
5
+ let(:bank_sheet) { BankSheet.new }
6
+ let(:mock_csv_parser) { double 'CsvParser' }
7
+ it "delegates parsing to the parser" do
8
+ mock_csv_parser.should_receive(:parse).with "csv_content"
9
+ bank_sheet.import "csv_content", mock_csv_parser
10
+ end
11
+
12
+ it "generates a transaction for each yielded entry" do
13
+ mock_csv_parser.stub(:parse).
14
+ and_yield(:date => '30-12-2011', :other_account => '32131', :description => 'tanken', :amount => '12,45', :type => 'Af').
15
+ and_yield(:date => '30-12-2011', :other_account => '12323', :description => 'training', :amount => '300', :type => 'Bij')
16
+
17
+ bank_sheet.import "csv_content", mock_csv_parser
18
+ bank_sheet.should have(2).transactions
19
+ bank_sheet.transactions.first.should == Transaction.credit(:date => '30-12-2011', :other_account => '32131', :description => 'tanken', :amount => '12,45')
20
+ bank_sheet.transactions.last.should == Transaction.debit(:date => '30-12-2011', :other_account => '12323', :description => 'training', :amount => '300')
21
+ end
22
+
23
+ end
24
+
25
+ describe "adding a transaction" do
26
+ let(:bank_sheet) { BankSheet.new Decimal.from_whole_and_part(50)}
27
+ it "adds the amount of a debit to the total" do
28
+ bank_sheet << Transaction.debit(:amount => 50)
29
+ bank_sheet.balance.should == Decimal.from_whole_and_part(100)
30
+ end
31
+ it "subtracts the amount of a credit from the total" do
32
+ bank_sheet << Transaction.credit(:amount => 25)
33
+ bank_sheet.balance.should == Decimal.from_whole_and_part(25)
34
+ end
35
+
36
+ end
37
+
38
+ describe "export to eb" do
39
+ let(:bank_sheet) { BankSheet.new Decimal.from_whole_and_part(0), Date.parse("12-07-2011") }
40
+ let(:month_day) { "0712" }
41
+ let(:relations) { double "relation repository" }
42
+
43
+ before { relations.stub(:find_by_account) {"GASSTATION"} }
44
+ it "contains an ingbank statement with totals" do
45
+ bank_sheet.export_to_eb(relations).should == "ingbank:#{month_day} 12-07-2011 \"Afschrift per #{month_day}\" --saldo=0,00"
46
+ end
47
+
48
+ it "contains a crd statement for a credit" do
49
+ bank_sheet << Transaction.credit(:amount => "12,45", :other_account => '123123', :date => "31-05-2011", :description => "some costs" )
50
+ bank_sheet.export_to_eb(relations).should == %Q{ingbank:#{month_day} 12-07-2011 "Afschrift per #{month_day}" --saldo=-12,45 \\
51
+ crd 31-05-2011 GASSTATION -12,45}
52
+ end
53
+ it "contains a dev statement for a debit" do
54
+ bank_sheet << Transaction.debit(:amount => "12,45", :other_account => '123123', :date => "31-05-2011", :description => "some costs" )
55
+ bank_sheet.export_to_eb(relations).should == %Q{ingbank:#{month_day} 12-07-2011 "Afschrift per #{month_day}" --saldo=12,45 \\
56
+ deb 31-05-2011 GASSTATION 12,45}
57
+ end
58
+ end
59
+ end
60
+
61
+ describe Decimal do
62
+ describe "constructing" do
63
+ it "positive whole and part creates a positive Decimal" do
64
+ decimal = Decimal.from_whole_and_part(10, 20)
65
+ decimal.whole.should == 10
66
+ decimal.part.should == 20
67
+ decimal.sign.should == 1
68
+ end
69
+
70
+ it "zero whole and positive part creates a positive Decimal" do
71
+ decimal = Decimal.from_whole_and_part(0, 20)
72
+ decimal.whole.should == 0
73
+ decimal.part.should == 20
74
+ decimal.sign.should == 1
75
+ end
76
+ it "positive whole and negative part subtracts the part from the whole" do
77
+ Decimal.from_whole_and_part(1000, -20).should == Decimal.from_whole_and_part(999,80)
78
+ end
79
+ it "zero whole and negative pars creates a negative Decimal" do
80
+ decimal = Decimal.from_whole_and_part(0, -20)
81
+ decimal.whole.should == 0
82
+ decimal.part.should == 20
83
+ decimal.sign.should == -1
84
+ end
85
+ it "negative whole subtracts the part from the whole" do
86
+ decimal = Decimal.from_whole_and_part(-10, 20)
87
+ decimal.whole.should == 10
88
+ decimal.part.should == 20
89
+ decimal.sign.should == -1
90
+ end
91
+ it "negative whole and negative part adds the absolute part to the whole" do
92
+ Decimal.from_whole_and_part(-10, -10).should == Decimal.from_whole_and_part(-9,90)
93
+ end
94
+ end
95
+ describe "from_string" do
96
+ it "creates a decimal from the part before and after the comma" do
97
+ Decimal.from_string("10,10").should == Decimal.from_whole_and_part(10,10)
98
+ end
99
+ end
100
+ describe "to_s" do
101
+ it "prints sign whole and part" do
102
+ Decimal.from_whole_and_part(9,8).to_s.should == "9,08"
103
+ Decimal.from_whole_and_part(-9,8).to_s.should == "-9,08"
104
+ end
105
+ end
106
+ describe "adding" do
107
+ it "adds partials and wholes" do
108
+ (Decimal.from_whole_and_part(10, 10) + Decimal.from_whole_and_part(10,10)).should == Decimal.from_whole_and_part(20,20)
109
+ end
110
+
111
+ it "adds partial overflows to whole " do
112
+ (Decimal.from_whole_and_part(10, 10) + Decimal.from_whole_and_part(10,92)).should == Decimal.from_whole_and_part(21,02)
113
+ end
114
+ it "interprets string before adding"
115
+ it "adds integer to whole"
116
+ end
117
+
118
+ describe "subtracting" do
119
+ it "subtract partials and wholes" do
120
+ (Decimal.from_whole_and_part(10, 10) - Decimal.from_whole_and_part(5,5)).should == Decimal.from_whole_and_part(5,5)
121
+ end
122
+
123
+ it "subtracts partial underflows from whole if whole is positive" do
124
+ (Decimal.from_whole_and_part(10, 10) - Decimal.from_whole_and_part(5,11)).should == Decimal.from_whole_and_part(4,99)
125
+ end
126
+
127
+ it "adds partials if whole is negative" do
128
+ (Decimal.from_whole_and_part(-10, 10) - Decimal.from_whole_and_part(5,11)).should == Decimal.from_whole_and_part(-15,21)
129
+ end
130
+
131
+ it "becomes negative if only partial is negative and whole is nul" do
132
+ (Decimal.from_whole_and_part(10, 10) - Decimal.from_whole_and_part(10,11)).should == Decimal.from_whole_and_part(0,-1)
133
+ end
134
+
135
+ it "interprets string before subtracting"
136
+ it "subtracts integer from whole"
137
+ end
138
+ end
139
+ describe Transaction do
140
+ describe "amount" do
141
+ it "is a Decimal" do
142
+ Transaction.credit(:amount => '12,50').amount.should be_a(Decimal)
143
+ end
144
+ it "to_s makes it an amount with a comma" do
145
+ Transaction.credit(:amount => '12,50').amount.to_s.should == '-12,50'
146
+ Transaction.debit(:amount => '12,50').amount.to_s.should == '12,50'
147
+ end
148
+ end
149
+ end
150
+ describe CsvParser do
151
+ describe 'parse' do
152
+ let(:bank_sheet) { CsvParser.new "Datum" => :date,
153
+ "Naam / Omschrijving" => :description,
154
+ "Tegenrekening" => :other_account,
155
+ "Af Bij" => :type,
156
+ "Bedrag (EUR)" => :amount }
157
+ def a_parser_with_header_translations(translations)
158
+ CsvParser.new translations
159
+ end
160
+
161
+ def parse(content, translations)
162
+ result = []
163
+ a_parser_with_header_translations(translations).parse(content) do |attributes|
164
+ result << attributes
165
+ end
166
+ return result
167
+ end
168
+
169
+ it "creates an entry with one translation" do
170
+ parse(%Q{"Datum"\n"30-11-2011"}, "Datum" => :date).
171
+ should include :date => '30-11-2011'
172
+ end
173
+
174
+
175
+ it "creates an entry with multiple translations " do
176
+ parse(%{"Datum","Tijd"\n"30-11-2011","123"}, "Datum" => :date, "Tijd" => :time).
177
+ should include :date => '30-11-2011', :time => "123"
178
+ end
179
+
180
+ it "ignores cells that have no translations" do
181
+ parse(%Q{"Datum","Naam","Tijd"\n"30-11-2011","ignored","123"}, "Datum" => :date, "Tijd" => :time).
182
+ should include :date => '30-11-2011', :time => "123"
183
+ end
184
+
185
+ it "allows comma's in quotes" do
186
+ parse(%Q{"Am,ount"\n"55,12"}, "Am,ount" => :amount).
187
+ should include :amount => '55,12'
188
+ end
189
+
190
+ it "creates multiple entries" do
191
+ parse(%Q{"Datum"\n"30-11-2011"\n"01-07-1967"}, "Datum" => :date).
192
+ should == [ {:date => '30-11-2011'}, {:date => '01-07-1967'}]
193
+ end
194
+
195
+ it "raises exception when header does not contain all trainslations" do
196
+ lambda {
197
+ parse(%Q{"30-11-2011"\n"01-07-1967"}, "Datum" => :date)
198
+ }.should raise_exception(CsvParser::HeaderException)
199
+ end
200
+
201
+ end
202
+ end
@@ -0,0 +1,3 @@
1
+ "Datum","Naam / Omschrijving","Rekening","Tegenrekening","Code","Af Bij","Bedrag (EUR)","MutatieSoort","Mededelingen"
2
+ "30-05-2011","3010000 SHELL HILVARENBEEK>\HILV","653165315","509021689","BA","Af","54,82","Betaalautomaat"," PASVOLGNR 004 30-05-2011 08:06 TRANSACTIENR 4852668"
3
+ "30-05-2011","KN: 134858669000035","653165315","29225","IC","Af","95,18","Incasso"," FACTUUR : 134858669 000035 ZIE REKENING OP KPN.COM / HI.NL KPN - MOBIEL"
@@ -0,0 +1,18 @@
1
+ "Datum","Naam / Omschrijving","Rekening","Tegenrekening","Code","Af Bij","Bedrag (EUR)","MutatieSoort","Mededelingen"
2
+ "14-05-2014","12-05-14 18:57 BETAALAUTOMAAT ","653165315","","BA","Af","62,52","Betaalautomaat"," CCV*BP EXPRESS EINDH / EINDHOVEN 004 552056 CT344345 ING BANK NV PASTRANSACTIES"
3
+ "09-05-2014","07-05-14 21:40 BETAALAUTOMAAT ","653165315","","BA","Af","68,97","Betaalautomaat"," BP KEMPENBAAN 1 / TILBURG 004 159267 71097016 ING BANK NV PASTRANSACTIES"
4
+ "09-05-2014","TELFORT","653165315","NL70INGB0009275549","IC","Af","46,75","Incasso","Europese Incasso, doorlopend IBAN: NL70INGB0009275549 BIC: INGBNL2A Naam: TELFORT ID begunstigde: NL82TFT271247010003 SEPA ID machtiging: M10003064049 Kenmerk: 000000154644866 Omschrijving: 4838972 Betreft factuur d.d. 08-04- 2014 incl. 8,11 BTW Telfort"
5
+ "08-05-2014","KN: KM23602165","653165315","9478917","IC","Af","21,99","Incasso"," SERVICECONTRACT: WEB1031965 PERIODE: 12-05-14 - 11-06-14 NOTEBOOK ICARE PLUS 3000 HSC INZAKE ROSEMONT�ROTTERDAM"
6
+ "07-05-2014","Bosch Security Systems B.V.","653165315","NL98DEUT0265179165","OV","Bij","6534,00","Overschrijving","IBAN: NL98DEUT0265179165 BIC: DEUTNL2AXXX Naam: Bosch Security Systems B.V. Kenmerk: 0002003096 Omschrijving: /INV/2014-06 6.4.2014"
7
+ "07-05-2014","NUON CCC","653165315","NL54INGB0000000503","IC","Af","207,53","Incasso","Europese Incasso, doorlopend IBAN: NL54INGB0000000503 BIC: INGBNL2A Naam: NUON CCC ID begunstigde: NL43B2C091055420000 SEPA ID machtiging: M011000002586100 Kenmerk: R-511416811038 Omschrijving: 511416811038 BTW 36,02 KL ANTNR 22496934 CRN 3010044463 termi jn apr 2014 Elsakkersstraat 25 508"
8
+ "06-05-2014","04-05-14 16:50 BETAALAUTOMAAT ","653165315","","BA","Af","69,27","Betaalautomaat"," SHELL HILVARENBEE / HILVARENBEEK 004 104494 HKW224 ING BANK NV PASTRANSACTIES"
9
+ "06-05-2014","Reguliere aflossing ","653165315","","DV","Af","333,33","Diversen"," Rentevastlening 66.49.80.414 Rest hoofdsom EUR 46.533,58"
10
+ "06-05-2014","Afrekening Debetrente ","653165315","","DV","Af","226,52","Diversen"," Rentevastlening 66.49.80.414 Per. 01-04-2014 t/m 30-04-2014"
11
+ "05-05-2014","30-04-14 16:22 BETAALAUTOMAAT ","653165315","","BA","Af","70,30","Betaalautomaat"," SHELL HAARRIJN / BREUKELEN UT 004 123457 412K7K ING BANK NV PASTRANSACTIES"
12
+ "05-05-2014","ASR BETCENTR","653165315","NL45ABNA0869533843","IC","Af","85,00","Incasso","Europese Incasso, doorlopend IBAN: NL45ABNA0869533843 BIC: ABNANL2A Naam: ASR BETCENTR ID begunstigde: NL33ZZZ300756470001 SEPA ID machtiging: G- 70218499-0653165315 Kenmerk: E2E2014042908550455258787616500 Omschrijving: G 70218499 ASR Verz. 01-05-14 GA RANTIEVERZ. R.J.C. Westgeest"
13
+ "05-05-2014","NUON CCC","653165315","NL54INGB0000000503","IC","Af","94,79","Incasso","Europese Incasso, doorlopend IBAN: NL54INGB0000000503 BIC: INGBNL2A Naam: NUON CCC ID begunstigde: NL43B2C091055420000 SEPA ID machtiging: M011000002586099 Kenmerk: R-515401402391 Omschrijving: 515401402391 BTW 16,45 KL ANTNR 22496933 CRN 3010044462 termi jn apr 2014 Gondelstraat 2 5017 CK"
14
+ "02-05-2014","Hetzner online","653165315","DE97765515400000168500","GT","Af","41,18","Internetbankieren","IBAN: DE97765515400000168500 BIC: BYLADEM1GUN Naam: Hetzner online"
15
+ "02-05-2014","29-04-14 08:01 BETAALAUTOMAAT ","653165315","","BA","Af","193,20","Betaalautomaat"," HOTEL ZUR POST / BONN 004 812812 60940255 ING BANK NV PASTRANSACTIES"
16
+ "02-05-2014","29-04-14 21:00 BETAALAUTOMAAT ","653165315","","BA","Af","74,19","Betaalautomaat"," ESSO DE WOUWSE T / BERGEN OP ZOO 003 257281 QB8T23 ING BANK NV PASTRANSACTIES"
17
+ "02-05-2014","KN: KM23546539","653165315","9478917","IC","Af","13,99","Incasso"," SERVICECONTRACT: WEB1047618 PERIODE: 01-05-14 - 31-05-14 NOTEBOOK ICARE BASIS 2000 HSC INZAKE ROSEMONT�ROTTERDAM"
18
+ "02-05-2014","BRABANT WATER NV","653165315","NL25INGB0002606348","IC","Af","11,00","Incasso","Europese Incasso, doorlopend IBAN: NL25INGB0002606348 BIC: INGBNL2A Naam: BRABANT WATER NV ID begunstigde: NL32ZZZ160050770000 SEPA ID machtiging: MID07088197.1 Kenmerk: 9951394 Omschrijving: ONS KENMERK: 4177931, VOORSCHOT MEI 14 BTW 0.40 Gondelstraat 2 5017CK TILBURG"
@@ -0,0 +1,18 @@
1
+ ingbank:0630 30-06-2014 "Afschrift per 0630" --saldo=35913,47 \
2
+ crd 14-05-2014 ? -62,52 \
3
+ crd 09-05-2014 ? -68,97 \
4
+ crd 09-05-2014 ? -46,75 \
5
+ crd 08-05-2014 ? -21,99 \
6
+ deb 07-05-2014 ? 6534,00 \
7
+ crd 07-05-2014 ? -207,53 \
8
+ crd 06-05-2014 ? -69,27 \
9
+ crd 06-05-2014 ? -333,33 \
10
+ crd 06-05-2014 ? -226,52 \
11
+ crd 05-05-2014 ? -70,30 \
12
+ crd 05-05-2014 ? -85,00 \
13
+ crd 05-05-2014 ? -94,79 \
14
+ crd 02-05-2014 ? -41,18 \
15
+ crd 02-05-2014 ? -193,20 \
16
+ crd 02-05-2014 ? -74,19 \
17
+ crd 02-05-2014 ? -13,99 \
18
+ crd 02-05-2014 ? -11,00
@@ -0,0 +1,44 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe "ebutil integration" do
4
+ let(:file_base) { 'ing_16-05-2011_30-05-2011' }
5
+
6
+ def csv_file
7
+ data_file "#{file_base}.csv"
8
+ end
9
+
10
+ def eb_file
11
+ data_file "#{file_base}.eb"
12
+ end
13
+
14
+ def data_file(filename)
15
+ File.expand_path(File.join(File.dirname(__FILE__),'data', filename))
16
+ end
17
+
18
+ after(:all) do
19
+ File.delete(eb_file) if File.exists?(eb_file)
20
+ end
21
+ describe "import_csv" do
22
+ before do
23
+ Ebutil.new.invoke :import_csv, [csv_file], :begin_saldo => '31000'
24
+ end
25
+ it "generates eb file" do
26
+ File.should exist(eb_file)
27
+ end
28
+ describe "eb files content" do
29
+ attr_reader :content
30
+ before do
31
+ @content = File.read(eb_file)
32
+ end
33
+ it "contains an ingbank transaction" do
34
+ content.should include 'ingbank:0530 30-05-2011 "Afschrift per 0530" --saldo=30850,00'
35
+ end
36
+ end
37
+ context "contains strange chars" do
38
+ let(:file_base) { 'utfmess_15-06-2014_30-06-2014' }
39
+ it "should still succeed" do
40
+ File.should exist(eb_file)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,14 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'ebutil'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+ config.filter_run_excluding :broken => true
12
+ config.filter_run :focus => true
13
+ config.run_all_when_everything_filtered = true
14
+ end
metadata ADDED
@@ -0,0 +1,190 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ebutil
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Rob Westgeest
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>'
18
+ - !ruby/object:Gem::Version
19
+ version: 0.14.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>'
25
+ - !ruby/object:Gem::Version
26
+ version: 0.14.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: jeweler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rb-readline
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: thor
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>'
116
+ - !ruby/object:Gem::Version
117
+ version: 0.14.0
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>'
123
+ - !ruby/object:Gem::Version
124
+ version: 0.14.0
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>'
130
+ - !ruby/object:Gem::Version
131
+ version: 2.5.0
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>'
137
+ - !ruby/object:Gem::Version
138
+ version: 2.5.0
139
+ description: utility script for eekboek - converts bank transaction export to eb script
140
+ email: rob@qwan.it
141
+ executables:
142
+ - ebutil
143
+ extensions: []
144
+ extra_rdoc_files:
145
+ - LICENSE.txt
146
+ - README.rdoc
147
+ files:
148
+ - .document
149
+ - .rspec
150
+ - .ruby-version
151
+ - Gemfile
152
+ - Gemfile.lock
153
+ - Guardfile
154
+ - LICENSE.txt
155
+ - README.rdoc
156
+ - Rakefile
157
+ - VERSION
158
+ - bin/ebutil
159
+ - lib/ebutil.rb
160
+ - spec/ebutil_spec.rb
161
+ - spec/integration/data/ing_16-05-2011_30-05-2011.csv
162
+ - spec/integration/data/utfmess_15-06-2014_30-06-2014.csv
163
+ - spec/integration/data/utfmess_15-06-2014_30-06-2014.eb
164
+ - spec/integration/ebutil_spec.rb
165
+ - spec/spec_helper.rb
166
+ homepage:
167
+ licenses:
168
+ - MIT
169
+ metadata: {}
170
+ post_install_message:
171
+ rdoc_options: []
172
+ require_paths:
173
+ - lib
174
+ required_ruby_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - '>='
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ required_rubygems_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - '>='
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ requirements: []
185
+ rubyforge_project:
186
+ rubygems_version: 2.0.14
187
+ signing_key:
188
+ specification_version: 4
189
+ summary: utility script for eekboek shell
190
+ test_files: []