bank-account-statement 0.1.1

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.
@@ -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����^