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.
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