bank-account-statement 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.gitignore +8 -0
  5. data/CHANGELOG.md +29 -0
  6. data/MD5SUMS.md5 +1 -0
  7. data/README.md +11 -2
  8. data/SHA256SUMS.sha256 +1 -0
  9. data/lib/bank-account-statement/inputs.rb +3 -0
  10. data/lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/CreditCard/V_2011_04_09.rb +75 -0
  11. data/lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/CreditCard/V_2015_05_27.rb +86 -0
  12. data/lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/CreditCard/base.rb +29 -0
  13. data/lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Current/V_2011_05_07.rb +6 -0
  14. data/lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Current/V_2015_03_03.rb +6 -0
  15. data/lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/Current/base.rb +3 -64
  16. data/lib/bank-account-statement/inputs/HTML/CPBKGB22/Personal/base.rb +83 -0
  17. data/lib/bank-account-statement/inputs/TXT/CPBKGB22/Business/Current/V_2015_12_06.rb +70 -0
  18. data/lib/bank-account-statement/inputs/TXT/CPBKGB22/Business/Current/base.rb +39 -0
  19. data/lib/bank-account-statement/inputs/TXT/CPBKGB22/Business/base.rb +72 -0
  20. data/lib/bank-account-statement/inputs/TXT/base.rb +21 -0
  21. data/lib/bank-account-statement/outputs.rb +1 -0
  22. data/lib/bank-account-statement/outputs/CSV/base.rb +18 -0
  23. data/lib/bank-account-statement/outputs/CSV/column_2.rb +52 -0
  24. data/lib/bank-account-statement/outputs/OFX/V_2_1_1.rb +39 -1
  25. data/lib/bank-account-statement/version.rb +1 -1
  26. metadata +14 -2
  27. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9c68dfeb0826852bcd185429b142c4580471111d
4
- data.tar.gz: 96b675ec2b0be0b42ff8ab45112d9f3dcf7f765d
3
+ metadata.gz: 7b980c9aeede10899ecc33c119839ed30de9af7a
4
+ data.tar.gz: c5727c92c47de0465f37dfa5f09c5584e455cddf
5
5
  SHA512:
6
- metadata.gz: 8dde34aacec4bfec90d722bd33d01daaf36e36bc521b5f459c3900ee00d68bb000bde008f573ae5bf24141c300b33420d0589ba8de21feb726c923c503d59f73
7
- data.tar.gz: 8f3288fa74bd01e37f51839562ff4025303f179338c17e9626acb206c8fddf62c316a33d26f6ef10722792203f79a11277ed9ceb89dd467a2ba27a9076a228fa
6
+ metadata.gz: 0df555ed685db1f3d350e33d5e001ce0b8ddd81d99577cb5da7938730f5970949f541d509b9ba1f33a1df7f3dc553ea711417f616166907a88cb43bcbb9e88ed
7
+ data.tar.gz: 26b0b19adda249587093611fed52eaba2999caaf02ce76908d982eb29b1078199c886a70e649761d2aa7685331a1a0979652c5f5059475a78a4cbc77f3e10f75
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
@@ -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
@@ -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 tested pull-requests for other banks and
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
 
@@ -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/)
@@ -25,6 +25,12 @@ class V_2015_03_03 < Current::Base
25
25
 
26
26
  private
27
27
 
28
+ def _clean_amount(str)
29
+ s = _clean_str(str)
30
+ m = s[-1] == '-' ? -1 : 1
31
+ BigDecimal(s) * m
32
+ end
33
+
28
34
  def _bank_account_ids
29
35
  t = @doc.xpath('//table//table//table//table//tr').text
30
36
 
@@ -1,7 +1,7 @@
1
1
  require 'bigdecimal'
2
2
  require 'date'
3
3
 
4
- require_relative '../../../base'
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 < HTML::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
@@ -1 +1,2 @@
1
+ require_relative 'outputs/CSV/column_2'
1
2
  require_relative 'outputs/OFX/V_2_1_1'
@@ -0,0 +1,18 @@
1
+ require 'csv'
2
+
3
+ require_relative '../base'
4
+
5
+
6
+ module BankAccountStatement
7
+ module Outputs
8
+ module CSV
9
+
10
+ class Base < Outputs::Base
11
+
12
+ FILE_EXT = '.csv'.freeze
13
+
14
+ end
15
+
16
+ end
17
+ end
18
+ 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 _xml
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
 
@@ -1,5 +1,5 @@
1
1
  module BankAccountStatement
2
2
 
3
- VERSION = "0.1.1"
3
+ VERSION = "1.0.0"
4
4
 
5
5
  end
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.1.1
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-08 00:00:00.000000000 Z
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