bank-account-statement 0.1.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.gitignore +8 -0
- data/CHANGELOG.md +29 -0
- data/MD5SUMS.md5 +1 -0
- data/README.md +11 -2
- data/SHA256SUMS.sha256 +1 -0
- data/lib/bank-account-statement/inputs.rb +3 -0
- data/lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/CreditCard/V_2011_04_09.rb +75 -0
- data/lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/CreditCard/V_2015_05_27.rb +86 -0
- data/lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/CreditCard/base.rb +29 -0
- data/lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Current/V_2011_05_07.rb +6 -0
- data/lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Current/V_2015_03_03.rb +6 -0
- data/lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Current/base.rb +3 -64
- data/lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/base.rb +83 -0
- data/lib/bank-account-statement/inputs/TXT/CPBKGB22/Business/Current/V_2015_12_06.rb +70 -0
- data/lib/bank-account-statement/inputs/TXT/CPBKGB22/Business/Current/base.rb +39 -0
- data/lib/bank-account-statement/inputs/TXT/CPBKGB22/Business/base.rb +72 -0
- data/lib/bank-account-statement/inputs/TXT/base.rb +21 -0
- data/lib/bank-account-statement/outputs.rb +1 -0
- data/lib/bank-account-statement/outputs/CSV/base.rb +18 -0
- data/lib/bank-account-statement/outputs/CSV/column_2.rb +52 -0
- data/lib/bank-account-statement/outputs/OFX/V_2_1_1.rb +39 -1
- data/lib/bank-account-statement/version.rb +1 -1
- metadata +14 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b980c9aeede10899ecc33c119839ed30de9af7a
|
4
|
+
data.tar.gz: c5727c92c47de0465f37dfa5f09c5584e455cddf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0df555ed685db1f3d350e33d5e001ce0b8ddd81d99577cb5da7938730f5970949f541d509b9ba1f33a1df7f3dc553ea711417f616166907a88cb43bcbb9e88ed
|
7
|
+
data.tar.gz: 26b0b19adda249587093611fed52eaba2999caaf02ce76908d982eb29b1078199c886a70e649761d2aa7685331a1a0979652c5f5059475a78a4cbc77f3e10f75
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/.gitignore
CHANGED
@@ -7,3 +7,11 @@
|
|
7
7
|
/pkg/
|
8
8
|
/spec/reports/
|
9
9
|
/tmp/
|
10
|
+
|
11
|
+
# tests excluded because I'm concerned about copyright
|
12
|
+
/test/bank-account-statement/inputs/HTML/CPBKGB22/Personal/CreditCard/2011-04-09.html
|
13
|
+
/test/bank-account-statement/inputs/HTML/CPBKGB22/Personal/CreditCard/2015-05-27.html
|
14
|
+
/test/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Current/2011-05-07.html
|
15
|
+
/test/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Current/2015-03-03.html
|
16
|
+
/test/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Savings/2011-05-07.html
|
17
|
+
/test/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Savings/2015-03-03.html
|
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,39 @@
|
|
1
1
|
# Bank Account Statement Changelog
|
2
2
|
|
3
3
|
|
4
|
+
## 1.0.0
|
5
|
+
|
6
|
+
- [#2] add input format `HTML/CPBKGB22/Personal/CreditCard/V_2011_04_09`,
|
7
|
+
parsing HTML The Co-operative Bank (GB) Personal Credit Card bank account
|
8
|
+
statements downloaded from 2011-04-09 onwards [tiredpixel]
|
9
|
+
|
10
|
+
- [#2] add input format `HTML/CPBKGB22/Personal/CreditCard/V_2015_05_27`,
|
11
|
+
parsing HTML The Co-operative Bank (GB) Personal Credit Card bank account
|
12
|
+
statements downloaded from 2015-05-27 onwards [tiredpixel]
|
13
|
+
|
14
|
+
- [#2] extend output format `OFX/V_2_1_1` to support credit card statements
|
15
|
+
[tiredpixel]
|
16
|
+
|
17
|
+
- [#7] fix `HTML/CPBKGB22/Personal` negative balances throughout [tiredpixel]
|
18
|
+
|
19
|
+
- [#5] add input format `TXT/CPBKGB22/Business/Current/V_2015_12_06`,
|
20
|
+
parsing TXT The Co-operative Bank (GB) Business Current account statements
|
21
|
+
downloaded from 2015-12-06 onwards [tiredpixel]
|
22
|
+
|
23
|
+
- [#4] add output format `CSV/Column_2`, generating '2-column' (separate
|
24
|
+
withdrawals and deposits columns) CSV files [tiredpixel]
|
25
|
+
|
26
|
+
- first major release! :D supporting: The Co-operative Bank (GB) Personal
|
27
|
+
account HTML input (2 Credit Card formats, 2 Current formats, 2 Savings
|
28
|
+
formats); The Co-operative Bank (GB) Business account TXT input (1 Current
|
29
|
+
format); OFX (Open Financial Exchange) 2.1.1 output; CSV output
|
30
|
+
|
31
|
+
|
4
32
|
## 0.1.1
|
5
33
|
|
6
34
|
- update packaging; new version so tags match built packages
|
7
35
|
|
36
|
+
|
8
37
|
## 0.1.0
|
9
38
|
|
10
39
|
- first release! :D MIT Licence
|
data/MD5SUMS.md5
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
28416921aa047bea26f849c5421a7ade bank-account-statement-0.1.1.gem
|
data/README.md
CHANGED
@@ -15,8 +15,10 @@ better.
|
|
15
15
|
|
16
16
|
Bank Account Statement mitigates this problem by providing input parsers and
|
17
17
|
output generators, with a simple executable. Unlike various other similar
|
18
|
-
programs, I **am** prepared to accept
|
19
|
-
output formats. (Please remember to sanitise test fixtures!
|
18
|
+
programs, I **am** prepared to accept pull-requests for other banks and
|
19
|
+
output formats. (Please remember to sanitise any test fixtures! Tests for new
|
20
|
+
input formats are not necessarily expected, however, because I'm concerned
|
21
|
+
about copyright.)
|
20
22
|
|
21
23
|
More sleep lost by [tiredpixel](https://www.tiredpixel.com/).
|
22
24
|
|
@@ -61,10 +63,13 @@ bank-account-statement \
|
|
61
63
|
Input formats supported are:
|
62
64
|
|
63
65
|
```
|
66
|
+
HTML/CPBKGB22/Personal/CreditCard/V_2011_04_09
|
67
|
+
HTML/CPBKGB22/Personal/CreditCard/V_2015_05_27
|
64
68
|
HTML/CPBKGB22/Personal/Current/V_2011_05_07
|
65
69
|
HTML/CPBKGB22/Personal/Current/V_2015_03_03
|
66
70
|
HTML/CPBKGB22/Personal/Savings/V_2011_05_07
|
67
71
|
HTML/CPBKGB22/Personal/Savings/V_2015_03_03
|
72
|
+
TXT/CPBKGB22/Business/Current/V_2015_12_06
|
68
73
|
```
|
69
74
|
|
70
75
|
(Generated with `bank-account-statement --in-formats`.)
|
@@ -79,6 +84,7 @@ CPBKGB22 | GB | The Co-operative Bank
|
|
79
84
|
Output formats supported are:
|
80
85
|
|
81
86
|
```
|
87
|
+
CSV/Column_2
|
82
88
|
OFX/V_2_1_1
|
83
89
|
```
|
84
90
|
|
@@ -93,6 +99,9 @@ Run the tests, which use [MiniTest](https://github.com/seattlerb/minitest):
|
|
93
99
|
rake test
|
94
100
|
```
|
95
101
|
|
102
|
+
Unfortunately, many input formats have no tests committed, because I'm concerned
|
103
|
+
about copyright.
|
104
|
+
|
96
105
|
|
97
106
|
## Stay Tuned
|
98
107
|
|
data/SHA256SUMS.sha256
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
67dd600f569b26e49968fe1a629340ee469ed7c7224115a87e1b15ff29efbc1f bank-account-statement-0.1.1.gem
|
@@ -1,4 +1,7 @@
|
|
1
|
+
require_relative 'inputs/HTML/CPBKGB22/Personal/CreditCard/V_2011_04_09'
|
2
|
+
require_relative 'inputs/HTML/CPBKGB22/Personal/CreditCard/V_2015_05_27'
|
1
3
|
require_relative 'inputs/HTML/CPBKGB22/Personal/Current/V_2011_05_07'
|
2
4
|
require_relative 'inputs/HTML/CPBKGB22/Personal/Current/V_2015_03_03'
|
3
5
|
require_relative 'inputs/HTML/CPBKGB22/Personal/Savings/V_2011_05_07'
|
4
6
|
require_relative 'inputs/HTML/CPBKGB22/Personal/Savings/V_2015_03_03'
|
7
|
+
require_relative 'inputs/TXT/CPBKGB22/Business/Current/V_2015_12_06'
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
|
4
|
+
module BankAccountStatement
|
5
|
+
module Inputs
|
6
|
+
module HTML
|
7
|
+
module CPBKGB22
|
8
|
+
module Personal
|
9
|
+
module CreditCard
|
10
|
+
|
11
|
+
# HTML statement parsing for The Co-operative Bank credit card accounts.
|
12
|
+
#
|
13
|
+
# This version is named 2011-04-09 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
|
+
# later, for which a different parser should be used. If you experience an error
|
17
|
+
# trying to process a statement downloaded recently, then this is probably why.
|
18
|
+
#
|
19
|
+
# This is similar in format to +Personal::Current::V_2011_05_07+, but contains
|
20
|
+
# some differences such as not having a balance column, the balance instead
|
21
|
+
# being provided above the transactions (although note the balance might include
|
22
|
+
# transactions from a previous statement), and not having a bank id (as account
|
23
|
+
# id is the full credit card number).
|
24
|
+
class V_2011_04_09 < CreditCard::Base
|
25
|
+
|
26
|
+
TH = Hash[{
|
27
|
+
:date => 'Date',
|
28
|
+
:desc => 'Transaction',
|
29
|
+
:deposit => 'Deposits',
|
30
|
+
:withdrawal => 'Withdrawals',
|
31
|
+
}.map { |k, v| [k, v.freeze] }].freeze
|
32
|
+
|
33
|
+
def balance
|
34
|
+
balanced_at = @doc.xpath('(//table//table//table//table//tr)[1]/td[2]').text
|
35
|
+
amount = @doc.xpath('(//table//table//table//table//tr)[3]/td[2]').text
|
36
|
+
|
37
|
+
{
|
38
|
+
:ledger => {
|
39
|
+
:balanced_at => Date.parse(balanced_at),
|
40
|
+
:amount => _clean_amount(amount),
|
41
|
+
},
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def _clean_amount(str)
|
48
|
+
s = _clean_str(str)
|
49
|
+
m = s[-2..-1] == 'DR' ? -1 : 1
|
50
|
+
BigDecimal(s) * m
|
51
|
+
end
|
52
|
+
|
53
|
+
def _bank_account_ids
|
54
|
+
t = @doc.xpath('//table//table//table//td[@class="field"]/h4').first.text
|
55
|
+
|
56
|
+
{ :account_id => t[/\b(\d{16})\b/] }
|
57
|
+
end
|
58
|
+
|
59
|
+
def _transaction_rows
|
60
|
+
header = @doc.xpath('//table//table//table//table//table/thead/tr/th'
|
61
|
+
).map(&:text)
|
62
|
+
|
63
|
+
@doc.xpath('//table//table//table//table//table/tbody/tr').map { |r|
|
64
|
+
Hash[header.zip(r.xpath('td').map(&:text))]
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
|
4
|
+
module BankAccountStatement
|
5
|
+
module Inputs
|
6
|
+
module HTML
|
7
|
+
module CPBKGB22
|
8
|
+
module Personal
|
9
|
+
module CreditCard
|
10
|
+
|
11
|
+
# HTML statement parsing for The Co-operative Bank credit card accounts.
|
12
|
+
#
|
13
|
+
# This version is named 2015-05-27 because around that time the statement format
|
14
|
+
# changed (possibly as early as 2015-03-03 or earlier, when current account
|
15
|
+
# statements changed, but I don't know). If you experience an error trying to
|
16
|
+
# process old statements (i.e. statements downloaded before this date), please
|
17
|
+
# try using a different parser.
|
18
|
+
#
|
19
|
+
# This is similar in format to +Personal::Current::V_2015_03_03+, but contains
|
20
|
+
# some differences such as not having a balance column, the balance instead
|
21
|
+
# being provided above the transactions (although note the balance might include
|
22
|
+
# transactions from a previous statement), and not having a bank id (as account
|
23
|
+
# id is the full credit card number).
|
24
|
+
#
|
25
|
+
# Note the use of `+` and `-` signs in these statements, which follows liability
|
26
|
+
# ledger convention, rather than customer point-of-view convention. Thus, `+`
|
27
|
+
# indicates an increase in liability, with more money owed, and `-` indicates a
|
28
|
+
# decrease in liability, with some debt paid. This is different to OFX 2.1.1.
|
29
|
+
class V_2015_05_27 < CreditCard::Base
|
30
|
+
|
31
|
+
TH = Hash[{
|
32
|
+
:date => 'Date',
|
33
|
+
:desc => 'Transaction',
|
34
|
+
:amount => 'Amount',
|
35
|
+
}.map { |k, v| [k, v.freeze] }].freeze
|
36
|
+
|
37
|
+
def balance
|
38
|
+
# original format has 5 columns
|
39
|
+
balanced_at = @doc.xpath(
|
40
|
+
'(//table//table//td[@class="dataRowL"]/table//tr)[1]/td[5]').text
|
41
|
+
# slight change in format has 4 columns
|
42
|
+
balanced_at2 = @doc.xpath(
|
43
|
+
'(//table//table//td[@class="dataRowL"]/table//tr)[1]/td[4]').text
|
44
|
+
amount = @doc.xpath(
|
45
|
+
'(//table//table//td[@class="dataRowL"]/table//tr)[2]/td[2]').text
|
46
|
+
|
47
|
+
{
|
48
|
+
:ledger => {
|
49
|
+
:balanced_at => Date.parse([balanced_at, balanced_at2].join(' ')),
|
50
|
+
:amount => _clean_amount(amount),
|
51
|
+
},
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def _clean_amount(str)
|
58
|
+
s = _clean_str(str)
|
59
|
+
m = s[-1] == '+' ? -1 : 1 # liability point-of-view; `+` is more debt
|
60
|
+
BigDecimal(s) * m
|
61
|
+
end
|
62
|
+
|
63
|
+
def _bank_account_ids
|
64
|
+
t = @doc.xpath(
|
65
|
+
'(//table//table//td[@class="dataRowL"]/table//tr)[1]/td[2]').text
|
66
|
+
|
67
|
+
{ :account_id => t[/\b(\d{16})\b/] }
|
68
|
+
end
|
69
|
+
|
70
|
+
def _transaction_rows
|
71
|
+
header = @doc.xpath('//table[@class="summaryTable"]/thead/tr/th'
|
72
|
+
).map(&:text)
|
73
|
+
|
74
|
+
@doc.xpath('//table[@class="summaryTable"]/tbody/tr').map { |r|
|
75
|
+
Hash[header.zip(r.xpath('td').map(&:text))]
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,29 @@
|
|
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 CreditCard
|
13
|
+
|
14
|
+
class Base < Personal::Base
|
15
|
+
|
16
|
+
ACCOUNT_TYPE = :CREDITLINE
|
17
|
+
|
18
|
+
def bank
|
19
|
+
nil # credit card statement, so account id only
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -29,6 +29,12 @@ class V_2011_05_07 < Current::Base
|
|
29
29
|
|
30
30
|
private
|
31
31
|
|
32
|
+
def _clean_amount(str)
|
33
|
+
s = _clean_str(str)
|
34
|
+
m = s[-2..-1] == 'DR' ? -1 : 1
|
35
|
+
BigDecimal(s) * m
|
36
|
+
end
|
37
|
+
|
32
38
|
def _bank_account_ids
|
33
39
|
t = @doc.xpath('//table//table//table//td[@class="field"]/h4').first.text
|
34
40
|
t.match(/\D(?<bank_id>\d{2}-\d{2}-\d{2})\D+(?<account_id>\d{8})\D/)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'bigdecimal'
|
2
2
|
require 'date'
|
3
3
|
|
4
|
-
require_relative '
|
4
|
+
require_relative '../base'
|
5
5
|
|
6
6
|
|
7
7
|
module BankAccountStatement
|
@@ -11,41 +11,12 @@ module CPBKGB22
|
|
11
11
|
module Personal
|
12
12
|
module Current
|
13
13
|
|
14
|
-
class Base <
|
14
|
+
class Base < Personal::Base
|
15
15
|
|
16
16
|
ACCOUNT_TYPE = :CHECKING
|
17
17
|
|
18
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
|
-
}
|
19
|
+
{ :id => _bank_account_ids[:bank_id].tr('-', '') }
|
49
20
|
end
|
50
21
|
|
51
22
|
def balance
|
@@ -59,38 +30,6 @@ class Base < HTML::Base
|
|
59
30
|
}
|
60
31
|
end
|
61
32
|
|
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
33
|
end
|
95
34
|
|
96
35
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require_relative '../../base'
|
2
|
+
|
3
|
+
|
4
|
+
module BankAccountStatement
|
5
|
+
module Inputs
|
6
|
+
module HTML
|
7
|
+
module CPBKGB22
|
8
|
+
module Personal
|
9
|
+
|
10
|
+
class Base < HTML::Base
|
11
|
+
|
12
|
+
def account
|
13
|
+
{
|
14
|
+
:id => _bank_account_ids[:account_id],
|
15
|
+
:type => self.class::ACCOUNT_TYPE,
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def currency
|
20
|
+
:GBP
|
21
|
+
end
|
22
|
+
|
23
|
+
def transactions
|
24
|
+
_transaction_rows.map { |r|
|
25
|
+
begin
|
26
|
+
posted_at = Date.parse(r[self.class::TH[:date]])
|
27
|
+
rescue ArgumentError
|
28
|
+
next # annotation line
|
29
|
+
end
|
30
|
+
|
31
|
+
a = if self.class::TH.has_key?(:amount)
|
32
|
+
_clean_amount(r[self.class::TH[:amount]])
|
33
|
+
else
|
34
|
+
_transaction_amount(
|
35
|
+
r[self.class::TH[:deposit]],
|
36
|
+
r[self.class::TH[:withdrawal]]
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
{
|
41
|
+
:posted_at => posted_at,
|
42
|
+
:type => _transaction_type(r[self.class::TH[:desc]], a),
|
43
|
+
:name => r[self.class::TH[:desc]].strip,
|
44
|
+
:amount => a,
|
45
|
+
}
|
46
|
+
}.compact
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def _clean_str(str)
|
52
|
+
str.encode('UTF-8', invalid: :replace, replace: '').strip
|
53
|
+
end
|
54
|
+
|
55
|
+
def _transaction_amount(deposit, withdrawal)
|
56
|
+
d = _clean_amount(deposit)
|
57
|
+
w = _clean_amount(withdrawal)
|
58
|
+
|
59
|
+
w != 0 ? (w * -1) : d
|
60
|
+
end
|
61
|
+
|
62
|
+
def _transaction_type(name, amount)
|
63
|
+
case name
|
64
|
+
when /^BROUGHT FORWARD$/
|
65
|
+
:OTHER
|
66
|
+
when /^COOP ATM/
|
67
|
+
:ATM
|
68
|
+
when /^LINK /
|
69
|
+
:ATM
|
70
|
+
when /^TFR /
|
71
|
+
:XFER
|
72
|
+
else
|
73
|
+
amount >= 0 ? :CREDIT : :DEBIT
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
|
6
|
+
module BankAccountStatement
|
7
|
+
module Inputs
|
8
|
+
module TXT
|
9
|
+
module CPBKGB22
|
10
|
+
module Business
|
11
|
+
module Current
|
12
|
+
|
13
|
+
# TXT statement parsing for The Co-operative Bank business current accounts.
|
14
|
+
class V_2015_12_06 < Current::Base
|
15
|
+
|
16
|
+
TH = Hash[{
|
17
|
+
:date => 'DATE',
|
18
|
+
:desc => 'DESCRIPTION',
|
19
|
+
:withdrawal => 'WITHDRAWALS',
|
20
|
+
:deposit => 'DEPOSITS',
|
21
|
+
:balance => 'BALANCE',
|
22
|
+
}.map { |k, v| [k, v.freeze] }].freeze
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def _clean_amount(str)
|
27
|
+
a = str.tr(',', '')
|
28
|
+
BigDecimal(a)
|
29
|
+
end
|
30
|
+
|
31
|
+
def _bank_account_ids
|
32
|
+
@doc_e[0].match(/\b(?<bank_id>\d{6})(?<account_id>\d{8})\d{2}\b/)
|
33
|
+
end
|
34
|
+
|
35
|
+
def _transaction_rows
|
36
|
+
hr = @doc_e[3]
|
37
|
+
|
38
|
+
cs = _columns(hr)
|
39
|
+
|
40
|
+
body_rs = @doc_e[4..-1]
|
41
|
+
|
42
|
+
body_rs.map { |r|
|
43
|
+
Hash[TH.keys.map { |e| [e, r[cs[e]]] }]
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def _columns(header_row)
|
48
|
+
hi = Hash[TH.map { |k, v|
|
49
|
+
i = header_row.index(v)
|
50
|
+
[k, i...(i + v.length)]
|
51
|
+
}]
|
52
|
+
|
53
|
+
cs = {}
|
54
|
+
cs[:date] = 0...10
|
55
|
+
cs[:desc] = (cs[:date].last)...(hi[:withdrawal].first)
|
56
|
+
cs[:withdrawal] = (cs[:desc].last)...(hi[:withdrawal].last)
|
57
|
+
cs[:deposit] = (cs[:withdrawal].last)...(hi[:deposit].last)
|
58
|
+
cs[:balance] = (cs[:deposit].last)...(hi[:balance].last)
|
59
|
+
|
60
|
+
cs
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
require_relative '../base'
|
4
|
+
|
5
|
+
|
6
|
+
module BankAccountStatement
|
7
|
+
module Inputs
|
8
|
+
module TXT
|
9
|
+
module CPBKGB22
|
10
|
+
module Business
|
11
|
+
module Current
|
12
|
+
|
13
|
+
class Base < Business::Base
|
14
|
+
|
15
|
+
ACCOUNT_TYPE = :CHECKING
|
16
|
+
|
17
|
+
def bank
|
18
|
+
{ :id => _bank_account_ids[:bank_id] }
|
19
|
+
end
|
20
|
+
|
21
|
+
def balance
|
22
|
+
r = _transaction_rows.last
|
23
|
+
|
24
|
+
{
|
25
|
+
:ledger => {
|
26
|
+
:balanced_at => Date.parse(r[:date]),
|
27
|
+
:amount => _clean_amount(r[:balance]),
|
28
|
+
},
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require_relative '../../base'
|
2
|
+
|
3
|
+
|
4
|
+
module BankAccountStatement
|
5
|
+
module Inputs
|
6
|
+
module TXT
|
7
|
+
module CPBKGB22
|
8
|
+
module Business
|
9
|
+
|
10
|
+
class Base < TXT::Base
|
11
|
+
|
12
|
+
def account
|
13
|
+
{
|
14
|
+
:id => _bank_account_ids[:account_id],
|
15
|
+
:type => self.class::ACCOUNT_TYPE,
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def currency
|
20
|
+
:GBP
|
21
|
+
end
|
22
|
+
|
23
|
+
def transactions
|
24
|
+
_transaction_rows.map { |r|
|
25
|
+
begin
|
26
|
+
posted_at = Date.parse(r[:date])
|
27
|
+
rescue ArgumentError
|
28
|
+
next # annotation line
|
29
|
+
end
|
30
|
+
|
31
|
+
a= _transaction_amount(r[:deposit], r[:withdrawal])
|
32
|
+
|
33
|
+
{
|
34
|
+
:posted_at => posted_at,
|
35
|
+
:type => _transaction_type(r[:desc], a),
|
36
|
+
:name => r[:desc].strip,
|
37
|
+
:amount => a,
|
38
|
+
}
|
39
|
+
}.compact
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def _clean_str(str)
|
45
|
+
str.strip
|
46
|
+
end
|
47
|
+
|
48
|
+
def _transaction_amount(deposit, withdrawal)
|
49
|
+
d = _clean_amount(deposit)
|
50
|
+
w = _clean_amount(withdrawal)
|
51
|
+
|
52
|
+
w != 0 ? (w * -1) : d
|
53
|
+
end
|
54
|
+
|
55
|
+
def _transaction_type(name, amount)
|
56
|
+
case name
|
57
|
+
when /^DD /
|
58
|
+
:DIRECTDEBIT
|
59
|
+
when /^SO /
|
60
|
+
:REPEATPMT
|
61
|
+
else
|
62
|
+
amount >= 0 ? :CREDIT : :DEBIT
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative '../base'
|
2
|
+
|
3
|
+
|
4
|
+
module BankAccountStatement
|
5
|
+
module Inputs
|
6
|
+
module TXT
|
7
|
+
|
8
|
+
class Base < Inputs::Base
|
9
|
+
|
10
|
+
FILE_EXT = '.txt'.freeze
|
11
|
+
|
12
|
+
def initialize(txt)
|
13
|
+
@doc = txt.split("\n").map(&:rstrip)
|
14
|
+
@doc_e = @doc.reject(&:empty?)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
|
3
|
+
|
4
|
+
module BankAccountStatement
|
5
|
+
module Outputs
|
6
|
+
module CSV
|
7
|
+
|
8
|
+
# CSV 2-column (separate withdrawal and deposit columns) statement generation.
|
9
|
+
class Column_2 < CSV::Base
|
10
|
+
|
11
|
+
HEADER = Hash[{
|
12
|
+
:posted_at => 'Date',
|
13
|
+
:name => 'Description',
|
14
|
+
:withdrawal => 'Withdrawals',
|
15
|
+
:deposit => 'Deposits',
|
16
|
+
}.map { |k, v| [k, v.freeze] }].freeze
|
17
|
+
|
18
|
+
def generate
|
19
|
+
_csv_bank_statement
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def _time(datetime)
|
25
|
+
datetime.public_methods
|
26
|
+
end
|
27
|
+
|
28
|
+
def _amount(amount)
|
29
|
+
amount.to_s('F')
|
30
|
+
end
|
31
|
+
|
32
|
+
def _csv_bank_statement
|
33
|
+
::CSV.generate do |c|
|
34
|
+
c << HEADER.values
|
35
|
+
@data[:transactions].each do |t|
|
36
|
+
t2 = t.merge(_amount_wd(t))
|
37
|
+
c << HEADER.keys.map { |e| t2[e] }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def _amount_wd(transaction)
|
43
|
+
k, s = transaction[:amount] >= 0 ? [:deposit, 1] : [:withdrawal, -1]
|
44
|
+
|
45
|
+
{ k => _amount(transaction[:amount] * s) }
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -18,6 +18,13 @@ class V_2_1_1 < OFX::Base
|
|
18
18
|
].freeze
|
19
19
|
|
20
20
|
def generate
|
21
|
+
_xml = case @data[:account][:type]
|
22
|
+
when :CREDITLINE
|
23
|
+
_xml_credit_card
|
24
|
+
else
|
25
|
+
_xml_bank_statement
|
26
|
+
end
|
27
|
+
|
21
28
|
xml_b = _xml.to_xml.split("\n").drop(1) # :|
|
22
29
|
(OFX_HEADER + xml_b).join("\n")
|
23
30
|
end
|
@@ -32,7 +39,7 @@ class V_2_1_1 < OFX::Base
|
|
32
39
|
amount.to_s('F')
|
33
40
|
end
|
34
41
|
|
35
|
-
def
|
42
|
+
def _xml_bank_statement
|
36
43
|
Nokogiri::XML::Builder.new { |x|
|
37
44
|
x.OFX {
|
38
45
|
x.BANKMSGSRSV1 {
|
@@ -64,6 +71,37 @@ class V_2_1_1 < OFX::Base
|
|
64
71
|
}
|
65
72
|
}
|
66
73
|
end
|
74
|
+
|
75
|
+
def _xml_credit_card
|
76
|
+
Nokogiri::XML::Builder.new { |x|
|
77
|
+
x.OFX {
|
78
|
+
x.BANKMSGSRSV1 {
|
79
|
+
x.CCSTMTTRNRS {
|
80
|
+
x.CCSTMTRS {
|
81
|
+
x.CURDEF @data[:currency]
|
82
|
+
x.CCACCTFROM {
|
83
|
+
x.ACCTID @data[:account][:id]
|
84
|
+
}
|
85
|
+
x.BANKTRANLIST {
|
86
|
+
@data[:transactions].each { |transaction|
|
87
|
+
x.STMTTRN {
|
88
|
+
x.TRNTYPE transaction[:type]
|
89
|
+
x.DTPOSTED _time(transaction[:posted_at])
|
90
|
+
x.TRNAMT _amount(transaction[:amount])
|
91
|
+
x.NAME transaction[:name]
|
92
|
+
}
|
93
|
+
}
|
94
|
+
}
|
95
|
+
x.LEDGERBAL {
|
96
|
+
x.BALAMT _amount(@data[:balance][:ledger][:amount])
|
97
|
+
x.DTASOF _time(@data[:balance][:ledger][:balanced_at])
|
98
|
+
}
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
104
|
+
end
|
67
105
|
|
68
106
|
end
|
69
107
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bank-account-statement
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- tiredpixel
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
ppcrIPUDXq0GfUcVaX1wKZDweDEt5M4u27/HsVj1UI9KmjN0aRdNuag/gSlLzyCG
|
31
31
|
Kz+K4HAQyy8zLmjScK7mREMnzwrVZyboiP6a2w==
|
32
32
|
-----END CERTIFICATE-----
|
33
|
-
date: 2015-12-
|
33
|
+
date: 2015-12-19 00:00:00.000000000 Z
|
34
34
|
dependencies:
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: nokogiri
|
@@ -116,8 +116,10 @@ files:
|
|
116
116
|
- CHANGELOG.md
|
117
117
|
- Gemfile
|
118
118
|
- LICENSE.txt
|
119
|
+
- MD5SUMS.md5
|
119
120
|
- README.md
|
120
121
|
- Rakefile
|
122
|
+
- SHA256SUMS.sha256
|
121
123
|
- bank-account-statement.gemspec
|
122
124
|
- bin/console
|
123
125
|
- bin/setup
|
@@ -126,14 +128,24 @@ files:
|
|
126
128
|
- lib/bank-account-statement.rb
|
127
129
|
- lib/bank-account-statement/app.rb
|
128
130
|
- lib/bank-account-statement/inputs.rb
|
131
|
+
- lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/CreditCard/V_2011_04_09.rb
|
132
|
+
- lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/CreditCard/V_2015_05_27.rb
|
133
|
+
- lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/CreditCard/base.rb
|
129
134
|
- lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Current/V_2011_05_07.rb
|
130
135
|
- lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Current/V_2015_03_03.rb
|
131
136
|
- lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Current/base.rb
|
132
137
|
- lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Savings/V_2011_05_07.rb
|
133
138
|
- lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Savings/V_2015_03_03.rb
|
139
|
+
- lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/base.rb
|
134
140
|
- lib/bank-account-statement/inputs/HTML/base.rb
|
141
|
+
- lib/bank-account-statement/inputs/TXT/CPBKGB22/Business/Current/V_2015_12_06.rb
|
142
|
+
- lib/bank-account-statement/inputs/TXT/CPBKGB22/Business/Current/base.rb
|
143
|
+
- lib/bank-account-statement/inputs/TXT/CPBKGB22/Business/base.rb
|
144
|
+
- lib/bank-account-statement/inputs/TXT/base.rb
|
135
145
|
- lib/bank-account-statement/inputs/base.rb
|
136
146
|
- lib/bank-account-statement/outputs.rb
|
147
|
+
- lib/bank-account-statement/outputs/CSV/base.rb
|
148
|
+
- lib/bank-account-statement/outputs/CSV/column_2.rb
|
137
149
|
- lib/bank-account-statement/outputs/OFX/V_2_1_1.rb
|
138
150
|
- lib/bank-account-statement/outputs/OFX/base.rb
|
139
151
|
- lib/bank-account-statement/outputs/base.rb
|
metadata.gz.sig
CHANGED
Binary file
|