bank-account-statement 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9c68dfeb0826852bcd185429b142c4580471111d
4
+ data.tar.gz: 96b675ec2b0be0b42ff8ab45112d9f3dcf7f765d
5
+ SHA512:
6
+ metadata.gz: 8dde34aacec4bfec90d722bd33d01daaf36e36bc521b5f459c3900ee00d68bb000bde008f573ae5bf24141c300b33420d0589ba8de21feb726c923c503d59f73
7
+ data.tar.gz: 8f3288fa74bd01e37f51839562ff4025303f179338c17e9626acb206c8fddf62c316a33d26f6ef10722792203f79a11277ed9ceb89dd467a2ba27a9076a228fa
Binary file
Binary file
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1 @@
1
+ 2.2.3
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.10.6
@@ -0,0 +1,33 @@
1
+ # Bank Account Statement Changelog
2
+
3
+
4
+ ## 0.1.1
5
+
6
+ - update packaging; new version so tags match built packages
7
+
8
+ ## 0.1.0
9
+
10
+ - first release! :D MIT Licence
11
+
12
+ - support Ruby 2.2.3 only
13
+
14
+ - `bank-account-statement` executable: `--in`, `--in-format`, `--out`,
15
+ `--out-format`, `--in-formats`, `--out-formats`, `--help`, `--version`
16
+ [tiredpixel]
17
+
18
+ - add input format `HTML/CPBKGB22/Personal/Current/V_2011_05_07`, parsing
19
+ HTML The Co-operative Bank (GB) Personal Current account pre-2015-03-03
20
+ statements [tiredpixel]
21
+
22
+ - [#1] add input format `HTML/CPBKGB22/Personal/Current/V_2015_03_03`, parsing
23
+ HTML The Co-operative Bank (GB) Personal Current account 2015-03-03 onwards
24
+ statements [tiredpixel]
25
+
26
+ - [#3] add input format `HTML/CPBKGB22/Personal/Savings/V_2011_05_07`, similar
27
+ to `HTML/CPBKGB22/Personal/Current/V_2011_05_07` [tiredpixel]
28
+
29
+ - [#3] add input format `HTML/CPBKGB22/Personal/Savings/V_2015_03_03`, similar
30
+ to `HTML/CPBKGB22/Personal/Current/V_2015_03_03` [tiredpixel]
31
+
32
+ - add output format `OFX/V_2_1_1`, generating OFX (Open Financial Exchange)
33
+ 2.1.1 [tiredpixel]
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bank-account-statement.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 tiredpixel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,128 @@
1
+ # Bank Account Statement (Ruby)
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/bank-account-statement.png)](http://badge.fury.io/rb/bank-account-statement)
4
+ [![Build Status](https://travis-ci.org/tiredpixel/bank-account-statement-rb.png?branch=master,release)](https://travis-ci.org/tiredpixel/bank-account-statement-rb)
5
+
6
+ Bank account statement format transformation (HTML to OFX).
7
+
8
+ Bank Account Statement is a program for transforming the format of bank account
9
+ statements. For some reason, many banks don't offer online bank statements in a
10
+ readily-consumable format (this seems to be especially true in the UK). For
11
+ example, despite it being possible to view bank statements online for UK bank
12
+ *The Co-operative Bank*, statements for personal current (checking) accounts
13
+ cannot be downloaded except in HTML. Business accounts often don't fare much
14
+ better.
15
+
16
+ Bank Account Statement mitigates this problem by providing input parsers and
17
+ output generators, with a simple executable. Unlike various other similar
18
+ programs, I **am** prepared to accept tested pull-requests for other banks and
19
+ output formats. (Please remember to sanitise test fixtures!)
20
+
21
+ More sleep lost by [tiredpixel](https://www.tiredpixel.com/).
22
+
23
+
24
+ ## Installation
25
+
26
+ - [Ruby](https://www.ruby-lang.org/en/)
27
+
28
+ The default version supported is defined in `.ruby-version`.
29
+ Any other versions supported are defined in `.travis.yml`.
30
+
31
+ Install using [gem](https://rubygems.org/):
32
+
33
+ ```shell
34
+ gem cert --add <(curl -Ls https://raw.github.com/tiredpixel/bank-account-statement-rb/master/certs/gem-public_cert-tiredpixel@posteo.de-2015-12-08.pem)
35
+ gem install bank-account-statement -P MediumSecurity
36
+ ```
37
+
38
+
39
+ ## Usage
40
+
41
+ Get help:
42
+
43
+ ```shell
44
+ bank-account-statement --help
45
+ ```
46
+
47
+ To convert all UK bank *The Co-operative Bank* HTML personal current account
48
+ pre-2015-03-03 statements to *OFX 2.1.1* (see other formats for later):
49
+
50
+ ```shell
51
+ bank-account-statement \
52
+ --in "IN_DIR/*.html" \
53
+ --in-format HTML/CPBKGB22/Personal/Current/V_2011_05_07 \
54
+ --out "OUT_DIR/" \
55
+ --out-format OFX/V_2_1_1
56
+ ```
57
+
58
+
59
+ ## Input Formats
60
+
61
+ Input formats supported are:
62
+
63
+ ```
64
+ HTML/CPBKGB22/Personal/Current/V_2011_05_07
65
+ HTML/CPBKGB22/Personal/Current/V_2015_03_03
66
+ HTML/CPBKGB22/Personal/Savings/V_2011_05_07
67
+ HTML/CPBKGB22/Personal/Savings/V_2015_03_03
68
+ ```
69
+
70
+ (Generated with `bank-account-statement --in-formats`.)
71
+
72
+ BIC | Country | Name
73
+ ---------|---------|-----
74
+ CPBKGB22 | GB | The Co-operative Bank
75
+
76
+
77
+ ## Output Formats
78
+
79
+ Output formats supported are:
80
+
81
+ ```
82
+ OFX/V_2_1_1
83
+ ```
84
+
85
+ (Generated with `bank-account-statement --out-formats`.)
86
+
87
+
88
+ ## Development
89
+
90
+ Run the tests, which use [MiniTest](https://github.com/seattlerb/minitest):
91
+
92
+ ```shell
93
+ rake test
94
+ ```
95
+
96
+
97
+ ## Stay Tuned
98
+
99
+ You can become a
100
+ [watcher](https://github.com/tiredpixel/bank-account-statement-rb/watchers)
101
+ on GitHub. And you can become a
102
+ [stargazer](https://github.com/tiredpixel/bank-account-statement-rb/stargazers)
103
+ if you are so minded. :D
104
+
105
+
106
+ ## Contributions
107
+
108
+ Contributions are embraced with much love and affection!
109
+ Please fork the repository and wizard your magic, ensuring that any tests are
110
+ not broken by the changes. :) Then send me a pull request.
111
+ If you'd like to discuss what you're doing or planning to do, or if you get
112
+ stuck on something, then just wave. :)
113
+
114
+ Do whatever makes you happy. We'll probably still like you. :)
115
+
116
+ Please remember to sanitise test fixtures!
117
+
118
+
119
+ ## Blessing
120
+
121
+ May you find peace, and help others to do likewise.
122
+
123
+
124
+ ## Licence
125
+
126
+ © [tiredpixel](https://www.tiredpixel.com/) 2015.
127
+ It is free software, released under the MIT License, and may be redistributed
128
+ under the terms specified in `LICENSE.txt`.
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bank-account-statement/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "bank-account-statement"
8
+ spec.version = BankAccountStatement::VERSION
9
+ spec.authors = ["tiredpixel"]
10
+ spec.email = ["tiredpixel@posteo.de"]
11
+
12
+ spec.cert_chain = [
13
+ 'certs/gem-public_cert-tiredpixel@posteo.de-2015-12-08.pem',
14
+ ]
15
+ spec.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/
16
+
17
+ spec.summary = %q{Bank account statement format transformation (HTML to OFX).}
18
+ spec.description = %q{}
19
+ spec.homepage = "https://github.com/tiredpixel/bank-account-statement-rb"
20
+ spec.license = "MIT"
21
+
22
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ spec.bindir = "exe"
24
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
+ spec.require_paths = ["lib"]
26
+
27
+ spec.add_dependency "nokogiri", "~> 1.6"
28
+
29
+ spec.add_development_dependency "bundler", "~> 1.10"
30
+ spec.add_development_dependency "rake", "~> 10.0"
31
+ spec.add_development_dependency "minitest", "~> 5.8"
32
+ spec.add_development_dependency "minitest-reporters", "~> 1.1"
33
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "bank-account-statement"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,21 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDeDCCAmCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBBMRMwEQYDVQQDDAp0aXJl
3
+ ZHBpeGVsMRYwFAYKCZImiZPyLGQBGRYGcG9zdGVvMRIwEAYKCZImiZPyLGQBGRYC
4
+ ZGUwHhcNMTUxMjA4MTgxNzE3WhcNMTYxMjA3MTgxNzE3WjBBMRMwEQYDVQQDDAp0
5
+ aXJlZHBpeGVsMRYwFAYKCZImiZPyLGQBGRYGcG9zdGVvMRIwEAYKCZImiZPyLGQB
6
+ GRYCZGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPBOgaVUF/VmkQ
7
+ s46X17lcsQgQae79Jo+f4ZUpHkCX/fXS13ZsD7aw3O+UnyxEl2qXbEIqEUaGlsHV
8
+ 9JI1QFHJhckJtmafqH08RF/CxquAmVOc2fGMFmof8GSfCFA6mJJv+byOSTHdb1xi
9
+ VXz3t3EV65zG11PIvF/P/Dgc5qkuU8zNud2Oix88gMcqH+BnVHAxT5/abWoHFel7
10
+ cBY5tAePpri6wOld2YDlESrLucfyzVAZKFX2si8MqHCpxGSzOULsjux+RmLTGhtn
11
+ 8eiqeu5/2rmniWbX/Qi7XacBeSgbq9lAk58du/jFSQ+P6dIO51U9NEujh/hMKaJb
12
+ F0x2Ov8jAgMBAAGjezB5MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
13
+ BBSwXMv+54+aTy5gxO40oF8wUlhUvTAfBgNVHREEGDAWgRR0aXJlZHBpeGVsQHBv
14
+ c3Rlby5kZTAfBgNVHRIEGDAWgRR0aXJlZHBpeGVsQHBvc3Rlby5kZTANBgkqhkiG
15
+ 9w0BAQUFAAOCAQEAHypsO0vAldZDf+r1ATAgqgg4ZEiyvOYMoeO+GL3a+mRAg2iA
16
+ Dky/xfkB1s7K4ZegqozRLQncw+fJyK8TNwz9vtv4WL/S59m9PdUNmtNMoHT+REDf
17
+ 6KuaSzorbBys8NumjO+wqDB/rn3O0wgCPrr8d+5gMNxIh1L0i4j3XfxmG8RM5zHN
18
+ DaTOtKpO1Lfu4y4CUQqG6D7UYksvEG3FManaJ/FUwcSP4gndso5cm/OGd1AQ4J3k
19
+ ppcrIPUDXq0GfUcVaX1wKZDweDEt5M4u27/HsVj1UI9KmjN0aRdNuag/gSlLzyCG
20
+ Kz+K4HAQyy8zLmjScK7mREMnzwrVZyboiP6a2w==
21
+ -----END CERTIFICATE-----
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ $stdout.sync = true
5
+
6
+
7
+ require 'logger'
8
+ require 'optparse'
9
+ require 'ostruct'
10
+
11
+ require_relative '../lib/bank-account-statement'
12
+ require_relative '../lib/bank-account-statement/app'
13
+ require_relative '../lib/bank-account-statement/inputs'
14
+ require_relative '../lib/bank-account-statement/outputs'
15
+
16
+
17
+ options = OpenStruct.new
18
+
19
+ OptionParser.new do |opts|
20
+ opts.version = BankAccountStatement::VERSION
21
+
22
+ opts.banner = opts.ver
23
+
24
+ opts.separator ""
25
+
26
+ opts.separator "Usage: bank-account-statement [OPTIONS]"
27
+
28
+ opts.separator ""
29
+
30
+ opts.on("-i", "--in INPUT",
31
+ "input"
32
+ ) do |o|
33
+ options[:in] = o
34
+ end
35
+
36
+ opts.on("-I", "--in-format INPUT_FORMAT",
37
+ "input format for INPUT"
38
+ ) do |o|
39
+ options[:in_format] = o
40
+ end
41
+
42
+ opts.on("-o", "--out OUTPUT",
43
+ "output"
44
+ ) do |o|
45
+ options[:out] = o
46
+ end
47
+
48
+ opts.on("-O", "--out-format OUTPUT_FORMAT",
49
+ "output format for OUTPUT"
50
+ ) do |o|
51
+ options[:out_format] = o
52
+ end
53
+
54
+ opts.separator ""
55
+
56
+ opts.on("--in-formats",
57
+ "list INPUT_FORMAT support"
58
+ ) do
59
+ puts BankAccountStatement::Inputs::Base.formats.keys.sort
60
+
61
+ exit
62
+ end
63
+
64
+ opts.on("--out-formats",
65
+ "list OUTPUT_FORMAT support"
66
+ ) do
67
+ puts BankAccountStatement::Outputs::Base.formats.keys.sort
68
+
69
+ exit
70
+ end
71
+
72
+ opts.separator ""
73
+
74
+ opts.on_tail("--help",
75
+ "Output (this) help and exit."
76
+ ) do
77
+ puts opts
78
+ exit
79
+ end
80
+
81
+ opts.on_tail("--version",
82
+ "Output version and exit."
83
+ ) do
84
+ puts opts.ver
85
+ exit
86
+ end
87
+ end.parse!
88
+
89
+
90
+ logger = Logger.new($stdout)
91
+
92
+ logger.formatter = proc do |severity, datetime, progname, msg|
93
+ "[#{severity[0]}] #{msg}\n"
94
+ end
95
+
96
+ app = BankAccountStatement::App.new(
97
+ :in => options[:in],
98
+ :in_format => options[:in_format],
99
+ :out => options[:out],
100
+ :out_format => options[:out_format],
101
+ :logger => logger
102
+ )
103
+ app.run
@@ -0,0 +1 @@
1
+ require "bank-account-statement/version"
@@ -0,0 +1,55 @@
1
+ require 'logger'
2
+
3
+
4
+ module BankAccountStatement
5
+
6
+ class App
7
+
8
+ def initialize(opts = {})
9
+ @in = opts[:in].to_s
10
+
11
+ @in_format = BankAccountStatement::Inputs::Base.formats[opts[:in_format]]
12
+ raise "IN_FORMAT unknown" unless @in_format
13
+
14
+ @out = opts[:out].to_s
15
+ raise "OUT not directory" unless Dir.exist?(@out)
16
+
17
+ @out_format = BankAccountStatement::Outputs::Base.formats[opts[:out_format]]
18
+ raise "OUT_FORMAT unknown" unless @out_format
19
+
20
+ @logger = opts[:logger] || Logger.new(nil)
21
+ end
22
+
23
+ def run
24
+ Dir.glob(@in) do |f_in|
25
+ f_o_n = File.basename(f_in, @in_format::FILE_EXT) + @out_format::FILE_EXT
26
+ f_out = File.join(@out, f_o_n)
27
+
28
+ if File.exist?(f_out)
29
+ @logger.warn { "SKIPPED\t#{f_in}" }
30
+ else
31
+ begin
32
+ _convert(f_in, f_out)
33
+ @logger.info { "CONVERTED\t#{f_in}\t#{f_out}" }
34
+ rescue
35
+ @logger.error { "ERRORED\t#{f_in}" }
36
+ raise
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def _convert(file_in, file_out)
45
+ file_in_c = File.read(file_in)
46
+ in_f = @in_format.new(file_in_c)
47
+ data = in_f.parse
48
+ out_f = @out_format.new(data)
49
+ out_f_c = out_f.generate
50
+ File.write(file_out, out_f_c)
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,4 @@
1
+ require_relative 'inputs/HTML/CPBKGB22/Personal/Current/V_2011_05_07'
2
+ require_relative 'inputs/HTML/CPBKGB22/Personal/Current/V_2015_03_03'
3
+ require_relative 'inputs/HTML/CPBKGB22/Personal/Savings/V_2011_05_07'
4
+ require_relative 'inputs/HTML/CPBKGB22/Personal/Savings/V_2015_03_03'
@@ -0,0 +1,53 @@
1
+ require_relative 'base'
2
+
3
+
4
+ module BankAccountStatement
5
+ module Inputs
6
+ module HTML
7
+ module CPBKGB22
8
+ module Personal
9
+ module Current
10
+
11
+ # HTML statement parsing for The Co-operative Bank current accounts.
12
+ #
13
+ # This version is named 2011-05-07 because for quite a while after that time,
14
+ # statements were made available in the same format (also on that date, and
15
+ # perhaps before?). Note, however, that the statement format changed some time
16
+ # around 2015-03-03, for which a different parser should be used. If you
17
+ # experience an error trying to process a recent statement (or rather, a
18
+ # statement downloaded recently, as it could be an old statement), then this is
19
+ # probably why.
20
+ class V_2011_05_07 < Current::Base
21
+
22
+ TH = Hash[{
23
+ :date => 'Date',
24
+ :desc => 'Transaction',
25
+ :deposit => 'Deposits',
26
+ :withdrawal => 'Withdrawals',
27
+ :balance => 'Balance',
28
+ }.map { |k, v| [k, v.freeze] }].freeze
29
+
30
+ private
31
+
32
+ def _bank_account_ids
33
+ t = @doc.xpath('//table//table//table//td[@class="field"]/h4').first.text
34
+ t.match(/\D(?<bank_id>\d{2}-\d{2}-\d{2})\D+(?<account_id>\d{8})\D/)
35
+ end
36
+
37
+ def _transaction_rows
38
+ header = @doc.xpath('//table//table//table//table//table/thead/tr/th'
39
+ ).map(&:text)
40
+
41
+ @doc.xpath('//table//table//table//table//table/tbody/tr').map { |r|
42
+ Hash[header.zip(r.xpath('td').map(&:text))]
43
+ }
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,52 @@
1
+ require_relative 'base'
2
+
3
+
4
+ module BankAccountStatement
5
+ module Inputs
6
+ module HTML
7
+ module CPBKGB22
8
+ module Personal
9
+ module Current
10
+
11
+ # HTML statement parsing for The Co-operative Bank current accounts.
12
+ #
13
+ # This version is named 2015-03-03 because around that time the statement format
14
+ # changed. If you experience an error trying to process old statements (i.e.
15
+ # statements downloaded before this date), please try using a different parser.
16
+ class V_2015_03_03 < Current::Base
17
+
18
+ TH = Hash[{
19
+ :date => 'Date',
20
+ :desc => 'Transaction',
21
+ :deposit => 'Money in',
22
+ :withdrawal => 'Money out',
23
+ :balance => 'Balance',
24
+ }.map { |k, v| [k, v.freeze] }].freeze
25
+
26
+ private
27
+
28
+ def _bank_account_ids
29
+ t = @doc.xpath('//table//table//table//table//tr').text
30
+
31
+ {
32
+ :account_id => t[/\b(\d{8})\b/],
33
+ :bank_id => t[/\b(\d{2}-\d{2}-\d{2})\b/],
34
+ }
35
+ end
36
+
37
+ def _transaction_rows
38
+ header = @doc.xpath('//table//table//table/thead/tr/th').map(&:text)
39
+
40
+ @doc.xpath('//table//table//table/tbody/tr').map { |r|
41
+ Hash[header.zip(r.xpath('td').map(&:text))]
42
+ }
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,101 @@
1
+ require 'bigdecimal'
2
+ require 'date'
3
+
4
+ require_relative '../../../base'
5
+
6
+
7
+ module BankAccountStatement
8
+ module Inputs
9
+ module HTML
10
+ module CPBKGB22
11
+ module Personal
12
+ module Current
13
+
14
+ class Base < HTML::Base
15
+
16
+ ACCOUNT_TYPE = :CHECKING
17
+
18
+ def bank
19
+ {
20
+ :id => _bank_account_ids[:bank_id].tr('-', ''),
21
+ }
22
+ end
23
+
24
+ def account
25
+ {
26
+ :id => _bank_account_ids[:account_id],
27
+ :type => self.class::ACCOUNT_TYPE,
28
+ }
29
+ end
30
+
31
+ def currency
32
+ :GBP
33
+ end
34
+
35
+ def transactions
36
+ _transaction_rows.map { |r|
37
+ a = _transaction_amount(
38
+ r[self.class::TH[:deposit]],
39
+ r[self.class::TH[:withdrawal]]
40
+ )
41
+
42
+ {
43
+ :posted_at => Date.parse(r[self.class::TH[:date]]),
44
+ :type => _transaction_type(r[self.class::TH[:desc]], a),
45
+ :name => r[self.class::TH[:desc]].strip,
46
+ :amount => a,
47
+ }
48
+ }
49
+ end
50
+
51
+ def balance
52
+ r = _transaction_rows.last
53
+
54
+ {
55
+ :ledger => {
56
+ :balanced_at => Date.parse(r[self.class::TH[:date]]),
57
+ :amount => _clean_amount(r[self.class::TH[:balance]]),
58
+ },
59
+ }
60
+ end
61
+
62
+ private
63
+
64
+ def _clean_str(str)
65
+ str.encode('UTF-8', invalid: :replace, replace: '').strip
66
+ end
67
+
68
+ def _clean_amount(str)
69
+ BigDecimal(_clean_str(str))
70
+ end
71
+
72
+ def _transaction_amount(deposit, withdrawal)
73
+ d = _clean_amount(deposit)
74
+ w = _clean_amount(withdrawal)
75
+
76
+ w != 0 ? (w * -1) : d
77
+ end
78
+
79
+ def _transaction_type(name, amount)
80
+ case name
81
+ when /^BROUGHT FORWARD$/
82
+ :OTHER
83
+ when /^COOP ATM/
84
+ :ATM
85
+ when /^LINK /
86
+ :ATM
87
+ when /^TFR /
88
+ :XFER
89
+ else
90
+ amount >= 0 ? :CREDIT : :DEBIT
91
+ end
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,27 @@
1
+ require_relative '../Current/V_2011_05_07'
2
+
3
+
4
+ module BankAccountStatement
5
+ module Inputs
6
+ module HTML
7
+ module CPBKGB22
8
+ module Personal
9
+ module Savings
10
+
11
+ # HTML statement parsing for The Co-operative Bank savings accounts.
12
+ #
13
+ # This is almost identical to the parsing of current accounts, with versions
14
+ # having changed at the same time. To save time, even tests are simply copied.
15
+ # If incompatibilities emerge, +Personal::Savings+ can be made more independent.
16
+ class V_2011_05_07 < Personal::Current::V_2011_05_07
17
+
18
+ ACCOUNT_TYPE = :SAVINGS
19
+
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require_relative '../Current/V_2015_03_03'
2
+
3
+
4
+ module BankAccountStatement
5
+ module Inputs
6
+ module HTML
7
+ module CPBKGB22
8
+ module Personal
9
+ module Savings
10
+
11
+ # HTML statement parsing for The Co-operative Bank savings accounts.
12
+ #
13
+ # This is almost identical to the parsing of current accounts, with versions
14
+ # having changed at the same time. To save time, even tests are simply copied.
15
+ # If incompatibilities emerge, +Personal::Savings+ can be made more independent.
16
+ class V_2015_03_03 < Personal::Current::V_2015_03_03
17
+
18
+ ACCOUNT_TYPE = :SAVINGS
19
+
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ require 'nokogiri'
2
+
3
+ require_relative '../base'
4
+
5
+
6
+ module BankAccountStatement
7
+ module Inputs
8
+ module HTML
9
+
10
+ class Base < Inputs::Base
11
+
12
+ FILE_EXT = '.html'.freeze
13
+
14
+ def initialize(html)
15
+ @doc = Nokogiri::HTML(html)
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ module BankAccountStatement
2
+ module Inputs
3
+
4
+ class Base
5
+
6
+ def self.inherited(subklass)
7
+ @@format_klasses ||= []
8
+ @@format_klasses << subklass unless subklass.name =~ /Base$/
9
+ super
10
+ end
11
+
12
+ def self.formats
13
+ Hash[@@format_klasses.map { |k|
14
+ [k.name.split('::').drop(2).join('/'), k]
15
+ }]
16
+ end
17
+
18
+ def parse
19
+ {
20
+ :bank => bank,
21
+ :account => account,
22
+ :currency => currency,
23
+ :transactions => transactions,
24
+ :balance => balance,
25
+ }
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1 @@
1
+ require_relative 'outputs/OFX/V_2_1_1'
@@ -0,0 +1,72 @@
1
+ require 'nokogiri'
2
+
3
+ require_relative 'base'
4
+
5
+
6
+ module BankAccountStatement
7
+ module Outputs
8
+ module OFX
9
+
10
+ # OFX 2.1.1 statement generation.
11
+ class V_2_1_1 < OFX::Base
12
+
13
+ OFX_STRFTIME = '%Y%m%d%H%M%S.%L[%:::z]'.freeze
14
+
15
+ OFX_HEADER = [
16
+ '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'.freeze,
17
+ '<?OFX OFXHEADER="200" VERSION="211" SECURITY="NONE" OLDFILEUID="NONE" NEWFILEUID="NONE"?>'.freeze,
18
+ ].freeze
19
+
20
+ def generate
21
+ xml_b = _xml.to_xml.split("\n").drop(1) # :|
22
+ (OFX_HEADER + xml_b).join("\n")
23
+ end
24
+
25
+ private
26
+
27
+ def _time(datetime)
28
+ datetime.strftime(OFX_STRFTIME)
29
+ end
30
+
31
+ def _amount(amount)
32
+ amount.to_s('F')
33
+ end
34
+
35
+ def _xml
36
+ Nokogiri::XML::Builder.new { |x|
37
+ x.OFX {
38
+ x.BANKMSGSRSV1 {
39
+ x.STMTTRNRS {
40
+ x.STMTRS {
41
+ x.CURDEF @data[:currency]
42
+ x.BANKACCTFROM {
43
+ x.BANKID @data[:bank][:id]
44
+ x.ACCTID @data[:account][:id]
45
+ x.ACCTTYPE @data[:account][:type]
46
+ }
47
+ x.BANKTRANLIST {
48
+ @data[:transactions].each { |transaction|
49
+ x.STMTTRN {
50
+ x.TRNTYPE transaction[:type]
51
+ x.DTPOSTED _time(transaction[:posted_at])
52
+ x.TRNAMT _amount(transaction[:amount])
53
+ x.NAME transaction[:name]
54
+ }
55
+ }
56
+ }
57
+ x.LEDGERBAL {
58
+ x.BALAMT _amount(@data[:balance][:ledger][:amount])
59
+ x.DTASOF _time(@data[:balance][:ledger][:balanced_at])
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,16 @@
1
+ require_relative '../base'
2
+
3
+
4
+ module BankAccountStatement
5
+ module Outputs
6
+ module OFX
7
+
8
+ class Base < Outputs::Base
9
+
10
+ FILE_EXT = '.ofx'.freeze
11
+
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ module BankAccountStatement
2
+ module Outputs
3
+
4
+ class Base
5
+
6
+ def self.inherited(subklass)
7
+ @@format_klasses ||= []
8
+ @@format_klasses << subklass unless subklass.name =~ /Base$/
9
+ super
10
+ end
11
+
12
+ def self.formats
13
+ Hash[@@format_klasses.map { |k|
14
+ [k.name.split('::').drop(2).join('/'), k]
15
+ }]
16
+ end
17
+
18
+ def initialize(data)
19
+ @data = data
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ module BankAccountStatement
2
+
3
+ VERSION = "0.1.1"
4
+
5
+ end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bank-account-statement
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - tiredpixel
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDeDCCAmCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBBMRMwEQYDVQQDDAp0aXJl
14
+ ZHBpeGVsMRYwFAYKCZImiZPyLGQBGRYGcG9zdGVvMRIwEAYKCZImiZPyLGQBGRYC
15
+ ZGUwHhcNMTUxMjA4MTgxNzE3WhcNMTYxMjA3MTgxNzE3WjBBMRMwEQYDVQQDDAp0
16
+ aXJlZHBpeGVsMRYwFAYKCZImiZPyLGQBGRYGcG9zdGVvMRIwEAYKCZImiZPyLGQB
17
+ GRYCZGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPBOgaVUF/VmkQ
18
+ s46X17lcsQgQae79Jo+f4ZUpHkCX/fXS13ZsD7aw3O+UnyxEl2qXbEIqEUaGlsHV
19
+ 9JI1QFHJhckJtmafqH08RF/CxquAmVOc2fGMFmof8GSfCFA6mJJv+byOSTHdb1xi
20
+ VXz3t3EV65zG11PIvF/P/Dgc5qkuU8zNud2Oix88gMcqH+BnVHAxT5/abWoHFel7
21
+ cBY5tAePpri6wOld2YDlESrLucfyzVAZKFX2si8MqHCpxGSzOULsjux+RmLTGhtn
22
+ 8eiqeu5/2rmniWbX/Qi7XacBeSgbq9lAk58du/jFSQ+P6dIO51U9NEujh/hMKaJb
23
+ F0x2Ov8jAgMBAAGjezB5MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
24
+ BBSwXMv+54+aTy5gxO40oF8wUlhUvTAfBgNVHREEGDAWgRR0aXJlZHBpeGVsQHBv
25
+ c3Rlby5kZTAfBgNVHRIEGDAWgRR0aXJlZHBpeGVsQHBvc3Rlby5kZTANBgkqhkiG
26
+ 9w0BAQUFAAOCAQEAHypsO0vAldZDf+r1ATAgqgg4ZEiyvOYMoeO+GL3a+mRAg2iA
27
+ Dky/xfkB1s7K4ZegqozRLQncw+fJyK8TNwz9vtv4WL/S59m9PdUNmtNMoHT+REDf
28
+ 6KuaSzorbBys8NumjO+wqDB/rn3O0wgCPrr8d+5gMNxIh1L0i4j3XfxmG8RM5zHN
29
+ DaTOtKpO1Lfu4y4CUQqG6D7UYksvEG3FManaJ/FUwcSP4gndso5cm/OGd1AQ4J3k
30
+ ppcrIPUDXq0GfUcVaX1wKZDweDEt5M4u27/HsVj1UI9KmjN0aRdNuag/gSlLzyCG
31
+ Kz+K4HAQyy8zLmjScK7mREMnzwrVZyboiP6a2w==
32
+ -----END CERTIFICATE-----
33
+ date: 2015-12-08 00:00:00.000000000 Z
34
+ dependencies:
35
+ - !ruby/object:Gem::Dependency
36
+ name: nokogiri
37
+ requirement: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.6'
42
+ type: :runtime
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '1.6'
49
+ - !ruby/object:Gem::Dependency
50
+ name: bundler
51
+ requirement: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '1.10'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '1.10'
63
+ - !ruby/object:Gem::Dependency
64
+ name: rake
65
+ requirement: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '10.0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '10.0'
77
+ - !ruby/object:Gem::Dependency
78
+ name: minitest
79
+ requirement: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '5.8'
84
+ type: :development
85
+ prerelease: false
86
+ version_requirements: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '5.8'
91
+ - !ruby/object:Gem::Dependency
92
+ name: minitest-reporters
93
+ requirement: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '1.1'
98
+ type: :development
99
+ prerelease: false
100
+ version_requirements: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '1.1'
105
+ description: ''
106
+ email:
107
+ - tiredpixel@posteo.de
108
+ executables:
109
+ - bank-account-statement
110
+ extensions: []
111
+ extra_rdoc_files: []
112
+ files:
113
+ - ".gitignore"
114
+ - ".ruby-version"
115
+ - ".travis.yml"
116
+ - CHANGELOG.md
117
+ - Gemfile
118
+ - LICENSE.txt
119
+ - README.md
120
+ - Rakefile
121
+ - bank-account-statement.gemspec
122
+ - bin/console
123
+ - bin/setup
124
+ - certs/gem-public_cert-tiredpixel@posteo.de-2015-12-08.pem
125
+ - exe/bank-account-statement
126
+ - lib/bank-account-statement.rb
127
+ - lib/bank-account-statement/app.rb
128
+ - lib/bank-account-statement/inputs.rb
129
+ - lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Current/V_2011_05_07.rb
130
+ - lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Current/V_2015_03_03.rb
131
+ - lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Current/base.rb
132
+ - lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Savings/V_2011_05_07.rb
133
+ - lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Savings/V_2015_03_03.rb
134
+ - lib/bank-account-statement/inputs/HTML/base.rb
135
+ - lib/bank-account-statement/inputs/base.rb
136
+ - lib/bank-account-statement/outputs.rb
137
+ - lib/bank-account-statement/outputs/OFX/V_2_1_1.rb
138
+ - lib/bank-account-statement/outputs/OFX/base.rb
139
+ - lib/bank-account-statement/outputs/base.rb
140
+ - lib/bank-account-statement/version.rb
141
+ homepage: https://github.com/tiredpixel/bank-account-statement-rb
142
+ licenses:
143
+ - MIT
144
+ metadata: {}
145
+ post_install_message:
146
+ rdoc_options: []
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ required_rubygems_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ requirements: []
160
+ rubyforge_project:
161
+ rubygems_version: 2.4.5.1
162
+ signing_key:
163
+ specification_version: 4
164
+ summary: Bank account statement format transformation (HTML to OFX).
165
+ test_files: []
@@ -0,0 +1,3 @@
1
+ R�j)°�w+����a��3^6̈�$c�Cz�~r�M,�@�A[s�jz7� �6���|=3V�?ؽq����ń�胆�cQ:�eNPe�i��v~���?&hb�������-�UyHY�*�eGE;�??
2
+ ~�t�?���n�fƸ����~�UP�ք����Aӹ��
3
+ /�0Dž��9U��L���C�P#\4e t#�5��յ�����L����^