ng-bank-parser 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/README.md +11 -7
- data/lib/ng-bank-parser.rb +2 -0
- data/lib/ng-bank-parser/banks.rb +38 -20
- data/lib/ng-bank-parser/fixtures/accessbank-pdf-invalid.pdf +0 -0
- data/lib/ng-bank-parser/fixtures/accessbank-pdf-valid.pdf +0 -0
- data/lib/ng-bank-parser/fixtures/hb-pdf-invalid.pdf +160 -0
- data/lib/ng-bank-parser/fixtures/hb-pdf-valid.pdf +0 -0
- data/lib/ng-bank-parser/parsers/accessbank-pdf-parser.rb +66 -0
- data/lib/ng-bank-parser/parsers/accessbank-pdf-parser/helpers.rb +167 -0
- data/lib/ng-bank-parser/parsers/hb-pdf-parser.rb +77 -0
- data/lib/ng-bank-parser/parsers/hb-pdf-parser/hb_constants.rb +44 -0
- data/lib/ng-bank-parser/parsers/hb-pdf-parser/hb_transaction_helpers.rb +112 -0
- data/lib/ng-bank-parser/parsers/uba-pdf-parser/classes/string.rb +4 -0
- data/lib/ng-bank-parser/router.rb +5 -21
- data/lib/ng-bank-parser/version.rb +1 -1
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6521c62889ffd5475c6f76e04f79b01aa0d744bc
|
4
|
+
data.tar.gz: a8b6dd72ce1d0b64872fa4e46b4e1ac9b1859360
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15818a961c4c2745f6e6fc3ffffeffa20ff0e124c4caed2f6a8515abfebd4dbfc8516e6954f7599f3b72665d5cf55ee14d75d266d585eb006bd28f4f2aea64b6
|
7
|
+
data.tar.gz: 194188f43d96f693f8cdedfc38730bb5aa2f97ef0dd42505230e0c247ce5d06fd9c16b1c2bddc07a118410bec25e76f2f43cfc061066bbaa75d1e546fbda81ea
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -29,7 +29,7 @@ Using the gem is pretty straightforward and simple.
|
|
29
29
|
result = NgBankParser::Router.parse(bank_key, file_path, password)
|
30
30
|
```
|
31
31
|
|
32
|
-
`
|
32
|
+
`bank_key` is the key of the bank that provides the statement. There's a [list of supported banks and formats](#list-of-supported-banks) below, therefore use the key provided for each bank.
|
33
33
|
|
34
34
|
`file_path` is obviously where the file you're trying to parse exists.
|
35
35
|
|
@@ -71,27 +71,31 @@ transaction = {
|
|
71
71
|
remarks: remarks,
|
72
72
|
ref: reference_id, # as provided by the statment
|
73
73
|
}
|
74
|
-
|
74
|
+
|
75
75
|
```
|
76
76
|
|
77
77
|
## List of Supported Banks
|
78
78
|
|
79
|
-
United Bank for Africa:
|
79
|
+
United Bank for Africa:
|
80
80
|
- key: uba
|
81
81
|
- supported formats: pdf
|
82
82
|
|
83
|
-
Guaranty Trust Bank:
|
83
|
+
Guaranty Trust Bank:
|
84
84
|
- key: gtb
|
85
85
|
- supported formats: xls, xlsx
|
86
|
-
|
87
|
-
First Bank:
|
86
|
+
|
87
|
+
First Bank:
|
88
88
|
- key: firstbank
|
89
89
|
- supported formats: pdf
|
90
90
|
|
91
|
+
Heritage Bank:
|
92
|
+
- key: hb
|
93
|
+
- supported formats: pdf
|
94
|
+
|
91
95
|
## Contributing
|
92
96
|
|
93
97
|
Documentation on contribution can be found in the contribution wiki
|
94
98
|
|
95
99
|
## License
|
96
100
|
|
97
|
-
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
101
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/lib/ng-bank-parser.rb
CHANGED
@@ -4,7 +4,9 @@ require "ng-bank-parser/router"
|
|
4
4
|
require "ng-bank-parser/pdf-unlocker"
|
5
5
|
require "ng-bank-parser/parsers/gtb-excel-parser"
|
6
6
|
require "ng-bank-parser/parsers/uba-pdf-parser"
|
7
|
+
require "ng-bank-parser/parsers/hb-pdf-parser"
|
7
8
|
require "ng-bank-parser/parsers/firstbank-pdf-parser"
|
9
|
+
require "ng-bank-parser/parsers/accessbank-pdf-parser"
|
8
10
|
|
9
11
|
module NgBankParser
|
10
12
|
# Your code goes here...
|
data/lib/ng-bank-parser/banks.rb
CHANGED
@@ -4,30 +4,48 @@ module NgBankParser
|
|
4
4
|
key: "gtb",
|
5
5
|
name: "Guaranty Trust Bank",
|
6
6
|
parsers: [{
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
format: "excel",
|
8
|
+
valid: "lib/ng-bank-parser/fixtures/gtb-excel-valid.xlsx",
|
9
|
+
invalid: "lib/ng-bank-parser/fixtures/gtb-excel-invalid.pdf",
|
10
|
+
extensions: ["xls","xlsx"]
|
11
11
|
}]
|
12
12
|
},{
|
13
|
+
key: "hb",
|
14
|
+
name: "Heritage Bank",
|
15
|
+
parsers: [{
|
16
|
+
format: "pdf",
|
17
|
+
valid: "lib/ng-bank-parser/fixtures/hb-pdf-valid.pdf",
|
18
|
+
invalid: "lib/ng-bank-parser/fixtures/hb-pdf-invalid.pdf",
|
19
|
+
extensions: ["pdf"]
|
20
|
+
}]
|
21
|
+
},{
|
13
22
|
key: "uba",
|
14
23
|
name: "United Bank for Africa",
|
15
24
|
parsers: [{
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
25
|
+
format: "pdf",
|
26
|
+
valid: "lib/ng-bank-parser/fixtures/uba-pdf-valid.pdf",
|
27
|
+
invalid: "lib/ng-bank-parser/fixtures/uba-pdf-invalid.pdf",
|
28
|
+
extensions: ["pdf"]
|
29
|
+
}]
|
21
30
|
},{
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
31
|
+
key: "firstbank",
|
32
|
+
name: "First Bank",
|
33
|
+
parsers: [{
|
34
|
+
format: "pdf",
|
35
|
+
valid: "lib/ng-bank-parser/fixtures/firstbank-pdf-valid.pdf",
|
36
|
+
fixture_password: 19856,
|
37
|
+
invalid: "lib/ng-bank-parser/fixtures/firstbank-pdf-invalid.pdf",
|
38
|
+
extensions: ["pdf"]
|
39
|
+
}]
|
40
|
+
},{
|
41
|
+
key: "accessbank",
|
42
|
+
name: "Access Bank Plc",
|
43
|
+
parsers: [{
|
44
|
+
format: "pdf",
|
45
|
+
valid: "lib/ng-bank-parser/fixtures/accessbank-pdf-valid.pdf",
|
46
|
+
invalid: "lib/ng-bank-parser/fixtures/accessbank-pdf-invalid.pdf",
|
47
|
+
extensions: ["pdf"]
|
48
|
+
}]
|
49
|
+
}]
|
32
50
|
end
|
33
|
-
end
|
51
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,160 @@
|
|
1
|
+
%PDF-1.3
|
2
|
+
3 0 obj
|
3
|
+
<</Type /Page
|
4
|
+
/Parent 1 0 R
|
5
|
+
/Resources 2 0 R
|
6
|
+
/Contents 4 0 R>>
|
7
|
+
endobj
|
8
|
+
4 0 obj
|
9
|
+
<</Length 165>>
|
10
|
+
stream
|
11
|
+
0.57 w
|
12
|
+
0 G
|
13
|
+
BT
|
14
|
+
/F1 16 Tf
|
15
|
+
16 TL
|
16
|
+
0 g
|
17
|
+
56.69 785.20 Td
|
18
|
+
(Hello world!) Tj
|
19
|
+
ET
|
20
|
+
BT
|
21
|
+
/F1 16 Tf
|
22
|
+
16 TL
|
23
|
+
0 g
|
24
|
+
56.69 756.85 Td
|
25
|
+
(This a random PDF file for testing our parsers.) Tj
|
26
|
+
ET
|
27
|
+
endstream
|
28
|
+
endobj
|
29
|
+
1 0 obj
|
30
|
+
<</Type /Pages
|
31
|
+
/Kids [3 0 R ]
|
32
|
+
/Count 1
|
33
|
+
/MediaBox [0 0 595.28 841.89]
|
34
|
+
>>
|
35
|
+
endobj
|
36
|
+
5 0 obj
|
37
|
+
<</BaseFont/Helvetica/Type/Font
|
38
|
+
/Encoding/WinAnsiEncoding
|
39
|
+
/Subtype/Type1>>
|
40
|
+
endobj
|
41
|
+
6 0 obj
|
42
|
+
<</BaseFont/Helvetica-Bold/Type/Font
|
43
|
+
/Encoding/WinAnsiEncoding
|
44
|
+
/Subtype/Type1>>
|
45
|
+
endobj
|
46
|
+
7 0 obj
|
47
|
+
<</BaseFont/Helvetica-Oblique/Type/Font
|
48
|
+
/Encoding/WinAnsiEncoding
|
49
|
+
/Subtype/Type1>>
|
50
|
+
endobj
|
51
|
+
8 0 obj
|
52
|
+
<</BaseFont/Helvetica-BoldOblique/Type/Font
|
53
|
+
/Encoding/WinAnsiEncoding
|
54
|
+
/Subtype/Type1>>
|
55
|
+
endobj
|
56
|
+
9 0 obj
|
57
|
+
<</BaseFont/Courier/Type/Font
|
58
|
+
/Encoding/WinAnsiEncoding
|
59
|
+
/Subtype/Type1>>
|
60
|
+
endobj
|
61
|
+
10 0 obj
|
62
|
+
<</BaseFont/Courier-Bold/Type/Font
|
63
|
+
/Encoding/WinAnsiEncoding
|
64
|
+
/Subtype/Type1>>
|
65
|
+
endobj
|
66
|
+
11 0 obj
|
67
|
+
<</BaseFont/Courier-Oblique/Type/Font
|
68
|
+
/Encoding/WinAnsiEncoding
|
69
|
+
/Subtype/Type1>>
|
70
|
+
endobj
|
71
|
+
12 0 obj
|
72
|
+
<</BaseFont/Courier-BoldOblique/Type/Font
|
73
|
+
/Encoding/WinAnsiEncoding
|
74
|
+
/Subtype/Type1>>
|
75
|
+
endobj
|
76
|
+
13 0 obj
|
77
|
+
<</BaseFont/Times-Roman/Type/Font
|
78
|
+
/Encoding/WinAnsiEncoding
|
79
|
+
/Subtype/Type1>>
|
80
|
+
endobj
|
81
|
+
14 0 obj
|
82
|
+
<</BaseFont/Times-Bold/Type/Font
|
83
|
+
/Encoding/WinAnsiEncoding
|
84
|
+
/Subtype/Type1>>
|
85
|
+
endobj
|
86
|
+
15 0 obj
|
87
|
+
<</BaseFont/Times-Italic/Type/Font
|
88
|
+
/Encoding/WinAnsiEncoding
|
89
|
+
/Subtype/Type1>>
|
90
|
+
endobj
|
91
|
+
16 0 obj
|
92
|
+
<</BaseFont/Times-BoldItalic/Type/Font
|
93
|
+
/Encoding/WinAnsiEncoding
|
94
|
+
/Subtype/Type1>>
|
95
|
+
endobj
|
96
|
+
2 0 obj
|
97
|
+
<<
|
98
|
+
/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]
|
99
|
+
/Font <<
|
100
|
+
/F1 5 0 R
|
101
|
+
/F2 6 0 R
|
102
|
+
/F3 7 0 R
|
103
|
+
/F4 8 0 R
|
104
|
+
/F5 9 0 R
|
105
|
+
/F6 10 0 R
|
106
|
+
/F7 11 0 R
|
107
|
+
/F8 12 0 R
|
108
|
+
/F9 13 0 R
|
109
|
+
/F10 14 0 R
|
110
|
+
/F11 15 0 R
|
111
|
+
/F12 16 0 R
|
112
|
+
>>
|
113
|
+
/XObject <<
|
114
|
+
>>
|
115
|
+
>>
|
116
|
+
endobj
|
117
|
+
17 0 obj
|
118
|
+
<<
|
119
|
+
/Producer (jsPDF 20120619)
|
120
|
+
/CreationDate (D:20150829130726)
|
121
|
+
>>
|
122
|
+
endobj
|
123
|
+
18 0 obj
|
124
|
+
<<
|
125
|
+
/Type /Catalog
|
126
|
+
/Pages 1 0 R
|
127
|
+
/OpenAction [3 0 R /FitH null]
|
128
|
+
/PageLayout /OneColumn
|
129
|
+
>>
|
130
|
+
endobj
|
131
|
+
xref
|
132
|
+
0 19
|
133
|
+
0000000000 65535 f
|
134
|
+
0000000301 00000 n
|
135
|
+
0000001530 00000 n
|
136
|
+
0000000009 00000 n
|
137
|
+
0000000087 00000 n
|
138
|
+
0000000388 00000 n
|
139
|
+
0000000478 00000 n
|
140
|
+
0000000573 00000 n
|
141
|
+
0000000671 00000 n
|
142
|
+
0000000773 00000 n
|
143
|
+
0000000861 00000 n
|
144
|
+
0000000955 00000 n
|
145
|
+
0000001052 00000 n
|
146
|
+
0000001153 00000 n
|
147
|
+
0000001246 00000 n
|
148
|
+
0000001338 00000 n
|
149
|
+
0000001432 00000 n
|
150
|
+
0000001754 00000 n
|
151
|
+
0000001836 00000 n
|
152
|
+
trailer
|
153
|
+
<<
|
154
|
+
/Size 19
|
155
|
+
/Root 18 0 R
|
156
|
+
/Info 17 0 R
|
157
|
+
>>
|
158
|
+
startxref
|
159
|
+
1940
|
160
|
+
%%EOF
|
Binary file
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'pdf-reader'
|
2
|
+
require 'open-uri'
|
3
|
+
require_relative 'accessbank-pdf-parser/helpers'
|
4
|
+
require_relative '../pdf-unlocker.rb'
|
5
|
+
|
6
|
+
module NgBankParser
|
7
|
+
class AccessbankPdf
|
8
|
+
extend AccessbankPdfHelpers
|
9
|
+
|
10
|
+
def self.parse(path, password=nil)
|
11
|
+
|
12
|
+
unless File.extname(path) == '.pdf'
|
13
|
+
return error('This file format is not supported yet. Please try uploading a pdf')
|
14
|
+
end
|
15
|
+
|
16
|
+
@pdf = open_pdf(path, password)
|
17
|
+
|
18
|
+
if @pdf
|
19
|
+
pdf_as_array = pdf_to_a(@pdf)
|
20
|
+
|
21
|
+
unless has_valid_details?(pdf_as_array)
|
22
|
+
return error("Couldn't fetch account details from pdf")
|
23
|
+
end
|
24
|
+
|
25
|
+
response = get_details(pdf_as_array)
|
26
|
+
return success(response)
|
27
|
+
else
|
28
|
+
return error("Couldn't open the pdf file passed")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
def self.open_pdf(path, password)
|
35
|
+
begin
|
36
|
+
file = open(path)
|
37
|
+
@pdf = PDF::Reader.new(file)
|
38
|
+
rescue PDF::Reader::EncryptedPDFError
|
39
|
+
if password
|
40
|
+
return unlock_pdf(path, password)
|
41
|
+
else
|
42
|
+
return nil
|
43
|
+
end
|
44
|
+
rescue
|
45
|
+
return nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def self.unlock_pdf(path, password)
|
51
|
+
unlocker = PDFUnlocker.new
|
52
|
+
response = unlocker.unlock(path, password)
|
53
|
+
|
54
|
+
return nil unless response
|
55
|
+
|
56
|
+
if response.include? 'Unlock Failed'
|
57
|
+
return nil
|
58
|
+
else
|
59
|
+
temp = StringIO.new
|
60
|
+
temp.write(response)
|
61
|
+
return PDF::Reader.new(temp)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module AccessbankPdfHelpers
|
2
|
+
|
3
|
+
def pdf_to_a(pdf)
|
4
|
+
lines = []
|
5
|
+
|
6
|
+
pdf.pages.each do |page|
|
7
|
+
remove_line_spacing(page.text).each do |line|
|
8
|
+
lines << line.strip.split(/\s\s+/)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
return lines
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def get_details(pdf_array)
|
17
|
+
|
18
|
+
details = Hash.new
|
19
|
+
|
20
|
+
details[:bank_name] = 'Access Bank'
|
21
|
+
details[:account_number] = get_account_number(pdf_array)
|
22
|
+
details[:account_name] = get_account_name(pdf_array)
|
23
|
+
|
24
|
+
date_range = get_date_range(pdf_array)
|
25
|
+
|
26
|
+
details[:from_date] = string_with_slash_to_date(date_range[:from])
|
27
|
+
details[:to_date] = string_with_slash_to_date(date_range[:to])
|
28
|
+
details[:transactions] = get_transactions(pdf_array)
|
29
|
+
|
30
|
+
return details
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def has_valid_details?(pdf_array)
|
35
|
+
return (get_account_number(pdf_array) and get_account_name(pdf_array) and get_date_range(pdf_array))
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def get_account_name(pdf_array)
|
40
|
+
|
41
|
+
pdf_array.each do |row|
|
42
|
+
row.each_with_index do |cell, index|
|
43
|
+
if cell == 'Account Name'
|
44
|
+
return row[index + 1]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
return nil
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def get_account_number(pdf_array)
|
54
|
+
|
55
|
+
pdf_array.each do |row|
|
56
|
+
row.each do |cell|
|
57
|
+
if cell =~ /[0-9]{10}/
|
58
|
+
return cell
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
return nil
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def get_date_range(pdf_array)
|
68
|
+
pdf_array.each_with_index do |row, index|
|
69
|
+
if row.count == 1
|
70
|
+
if row.first =~ /([0-3][0-9])[\/]([0-1][0-9])[\/]([0-9]{4})/ and pdf_array[index + 1].first =~ /([0-3][0-9])[\/]([0-1][0-9])[\/]([0-9]{4})/
|
71
|
+
return { from: row.first, to: pdf_array[index + 1].first }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
return nil
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def get_transactions(pdf_array)
|
81
|
+
start_index = get_first_line_of_transactions(pdf_array)
|
82
|
+
transactions = []
|
83
|
+
|
84
|
+
opening_balance = pdf_array[1][1]
|
85
|
+
|
86
|
+
pdf_array[start_index..-1].each do |transaction|
|
87
|
+
if valid_transaction?(transaction)
|
88
|
+
|
89
|
+
transaction_hash= Hash.new
|
90
|
+
transaction_hash[:date] = string_with_dash_to_date(transaction[0])
|
91
|
+
transaction_hash[:remarks] = transaction[2]
|
92
|
+
|
93
|
+
if transaction.count == 6 # ref code present
|
94
|
+
transaction_hash[:ref] = transaction[3]
|
95
|
+
transaction_hash[:amount] = amount_to_f(transaction[4])
|
96
|
+
transaction_hash[:balance] = amount_to_f(transaction[5])
|
97
|
+
transaction_hash[:type] = transaction_type(transaction_hash[:balance], opening_balance)
|
98
|
+
else #no ref code
|
99
|
+
transaction_hash[:ref] = ''
|
100
|
+
transaction_hash[:amount] = amount_to_f(transaction[3])
|
101
|
+
transaction_hash[:balance] = amount_to_f(transaction[4])
|
102
|
+
transaction_hash[:type] = transaction_type(transaction_hash[:balance], opening_balance)
|
103
|
+
end
|
104
|
+
|
105
|
+
opening_balance = transaction_hash[:balance]
|
106
|
+
transactions << transaction_hash
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
return transactions
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
def error(msg)
|
117
|
+
{ status: 0, message: msg }
|
118
|
+
end
|
119
|
+
|
120
|
+
def success(data)
|
121
|
+
{ status: 1, data: data }
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
|
126
|
+
private
|
127
|
+
def remove_line_spacing(str)
|
128
|
+
str.gsub(/^$\n/, '').lines
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
def transaction_type(current_balance, former_balance)
|
133
|
+
former_balance = amount_to_f(former_balance) if former_balance.is_a?(String)
|
134
|
+
|
135
|
+
current_balance - former_balance > 1 ? 'credit' : 'debit'
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
def amount_to_f(amount)
|
140
|
+
amount.gsub(/[^\d\.]/, '').to_f
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
def get_first_line_of_transactions(pdf_array)
|
145
|
+
start = -1
|
146
|
+
|
147
|
+
pdf_array.each_with_index do |row, index|
|
148
|
+
start = index + 1 if row[0] == 'Date'
|
149
|
+
end
|
150
|
+
|
151
|
+
return start
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
def valid_transaction?(line)
|
156
|
+
line[0] =~ /([0-3][0-9])[\-]([a-zA-Z]+)[\-](1[0-9])/ #first column matches access bank transaction date pattern
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
def string_with_dash_to_date(date_str)
|
161
|
+
Date.strptime(date_str, '%d-%b-%y')
|
162
|
+
end
|
163
|
+
|
164
|
+
def string_with_slash_to_date(date_str)
|
165
|
+
Date.strptime(date_str, '%d/%m/%Y')
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'pdf-reader'
|
2
|
+
require 'open-uri'
|
3
|
+
require_relative 'hb-pdf-parser/hb_transaction_helpers'
|
4
|
+
|
5
|
+
module NgBankParser
|
6
|
+
class HbPdf
|
7
|
+
|
8
|
+
extend HbTransactionHelpers
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def parse(url, password = nil)
|
12
|
+
unless ACCEPTED_FORMATS.include? File.extname(url)
|
13
|
+
return INVALID_FILE_FORMAT_STRING
|
14
|
+
end
|
15
|
+
|
16
|
+
file = open(url)
|
17
|
+
|
18
|
+
@reader = PDF::Reader.new(file)
|
19
|
+
|
20
|
+
if pdf_is_valid? file
|
21
|
+
generate_parse
|
22
|
+
|
23
|
+
return account_statement_payload
|
24
|
+
else
|
25
|
+
return invalid_payload
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def generate_parse
|
32
|
+
set_account_name
|
33
|
+
set_account_number
|
34
|
+
set_end_date
|
35
|
+
set_start_date
|
36
|
+
extract_transactions
|
37
|
+
end
|
38
|
+
|
39
|
+
def extract_transactions
|
40
|
+
@transactions = []
|
41
|
+
|
42
|
+
@reader.pages.each do |page|
|
43
|
+
page.text.remove_empty_lines.lines.each do |line|
|
44
|
+
transaction_items_list = transaction_items(line)
|
45
|
+
if transaction_items_list[TRANSACTION_DATE_INDEX].is_date?
|
46
|
+
if transaction_is_a_debit?(transaction_items_list, line)
|
47
|
+
create_transaction(line, 'debit')
|
48
|
+
elsif transaction_is_a_credit?(transaction_items_list, line)
|
49
|
+
create_transaction(line, 'credit')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def account_statement_payload
|
57
|
+
return {
|
58
|
+
status: VALID_ACCOUNT_STATUS,
|
59
|
+
data: {
|
60
|
+
bank_name: $Banks[1][:name],
|
61
|
+
account_number: @account_number,
|
62
|
+
account_name: @account_name,
|
63
|
+
from_date: @start_date,
|
64
|
+
to_date: @end_date,
|
65
|
+
transactions: @transactions
|
66
|
+
# reader: @reader
|
67
|
+
}
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def invalid_payload
|
72
|
+
{status: INVALID_ACCOUNT_STATUS,
|
73
|
+
message: INVALID_ACCOUNT_STATEMENT}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module NgBankParser
|
2
|
+
module HbConstants
|
3
|
+
ACCEPTED_FORMATS = ['.pdf']
|
4
|
+
|
5
|
+
INVALID_ACCOUNT_STATUS = 0
|
6
|
+
VALID_ACCOUNT_STATUS = 1
|
7
|
+
|
8
|
+
SECOND_PAGE_INDEX = 1
|
9
|
+
DURATION_DATE_LINE_INDEX = 3
|
10
|
+
ACCOUNT_NAME_LINE_INDEX = 5
|
11
|
+
ACCOUNT_NUMBER_LINE_INDEX = 6
|
12
|
+
TRANSACTION_HEADER_INDEX = 11
|
13
|
+
TRANSACTION_DATE_INDEX = 0
|
14
|
+
TRANSACTION_DEBIT_INDEX = -3
|
15
|
+
TRANSACTION_CREDIT_INDEX = -2
|
16
|
+
TRANSACTION_BALANCE = 131
|
17
|
+
TRANSACTION_BALANCE_END = 155
|
18
|
+
TRANSACTION_REMARKS = 32
|
19
|
+
TRANSACTION_REMARKS_END = 92
|
20
|
+
TRANSACTION_REFERENCE = 20
|
21
|
+
TRANSACTION_REFERENCE_END = 30
|
22
|
+
TRANSACTION_DEBIT = 92
|
23
|
+
TRANSACTION_DEBIT_END = 105
|
24
|
+
TRANSACTION_CREDIT = 110
|
25
|
+
TRANSACTION_CREDIT_END = 130
|
26
|
+
|
27
|
+
|
28
|
+
ACCOUNT_NAME_FIRST_MAKER = 'name'
|
29
|
+
ACCOUNT_NAME_SECOND_MAKER = 'debit turnover'
|
30
|
+
ACCOUNT_NUMBER_MAKER = 'account number'
|
31
|
+
END_DATE_MAKER = '-'
|
32
|
+
|
33
|
+
INVALID_FILE_FORMAT_STRING = 'File Format Not Valid'
|
34
|
+
INVALID_ACCOUNT_STATEMENT = 'File Is Not A Valid Heritage Bank Account Statement'
|
35
|
+
|
36
|
+
CORRECT_RC_NUMBER = 'RC No 9868'
|
37
|
+
|
38
|
+
COLUMN_ARRANGEMENT = 'valuedatereferencenarrationdebitcreditbalance'
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require_relative 'hb_constants'
|
2
|
+
|
3
|
+
module NgBankParser
|
4
|
+
module HbTransactionHelpers
|
5
|
+
include HbConstants
|
6
|
+
def pdf_is_valid?(file)
|
7
|
+
has_transaction_page? && has_transaction_columns? && has_correct_rc_number? && !has_encryption?(file)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def has_transaction_page?
|
13
|
+
@reader.pages[SECOND_PAGE_INDEX].present?
|
14
|
+
end
|
15
|
+
|
16
|
+
def has_correct_rc_number?
|
17
|
+
@reader.pages.first.text.remove_empty_lines.lines.first.strip == CORRECT_RC_NUMBER
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_transaction_columns?
|
21
|
+
@reader.pages[SECOND_PAGE_INDEX].text.remove_empty_lines.lines[TRANSACTION_HEADER_INDEX].remove_white_spaces == COLUMN_ARRANGEMENT
|
22
|
+
end
|
23
|
+
|
24
|
+
def has_encryption?(file)
|
25
|
+
begin
|
26
|
+
@reader = PDF::Reader.new(file)
|
27
|
+
false
|
28
|
+
rescue PDF::Reader::EncryptedPDFError
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_account_name
|
34
|
+
@account_name = @reader.pages[SECOND_PAGE_INDEX].text.remove_empty_lines.lines[ACCOUNT_NAME_LINE_INDEX].get_text_between_markers(ACCOUNT_NAME_FIRST_MAKER, ACCOUNT_NAME_SECOND_MAKER).remove_white_spaces
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_account_number
|
38
|
+
@account_number = @reader.pages[SECOND_PAGE_INDEX].text.remove_empty_lines.lines[ACCOUNT_NUMBER_LINE_INDEX].strip.get_text_after_marker(ACCOUNT_NUMBER_MAKER).remove_white_spaces
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_end_date
|
42
|
+
@end_date = Date.parse(@reader.pages[SECOND_PAGE_INDEX].text.remove_empty_lines.lines[DURATION_DATE_LINE_INDEX].get_text_after_marker(END_DATE_MAKER).remove_white_spaces)
|
43
|
+
end
|
44
|
+
|
45
|
+
def set_start_date
|
46
|
+
@start_date = Date.parse(@reader.pages[SECOND_PAGE_INDEX].text.remove_empty_lines.lines[DURATION_DATE_LINE_INDEX].get_text_between_markers(' ', END_DATE_MAKER).remove_white_spaces)
|
47
|
+
end
|
48
|
+
|
49
|
+
def set_transaction_attr(line)
|
50
|
+
transaction_object = {}
|
51
|
+
transaction_object[:date] = transaction_date(line)
|
52
|
+
|
53
|
+
transaction_object[:balance] = transaction_balance(line)
|
54
|
+
transaction_object[:remarks] = transaction_remarks(line)
|
55
|
+
transaction_object[:ref] = transaction_reference(line)
|
56
|
+
transaction_object
|
57
|
+
end
|
58
|
+
|
59
|
+
def transaction_is_a_debit?(transaction_items_list, line)
|
60
|
+
transaction_items_list[TRANSACTION_DEBIT_INDEX] && debit_amount(line) > 0.00
|
61
|
+
end
|
62
|
+
|
63
|
+
def transaction_is_a_credit?(transaction_items_list, line)
|
64
|
+
transaction_items_list[TRANSACTION_CREDIT_INDEX] && credit_amount(line) > 0.00
|
65
|
+
end
|
66
|
+
|
67
|
+
def create_transaction(line, type)
|
68
|
+
transaction = set_transaction_attr(line)
|
69
|
+
set_transaction_type(transaction, type, line)
|
70
|
+
@transactions << transaction
|
71
|
+
end
|
72
|
+
|
73
|
+
def set_transaction_type(transaction, type, line)
|
74
|
+
if type == 'debit'
|
75
|
+
transaction[:amount] = debit_amount(line)
|
76
|
+
transaction[:type] = type
|
77
|
+
elsif type == 'credit'
|
78
|
+
transaction[:amount] = credit_amount(line)
|
79
|
+
transaction[:type] = type
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def transaction_date(line)
|
84
|
+
Date.parse(line[0..19].remove_white_spaces)
|
85
|
+
end
|
86
|
+
|
87
|
+
def transaction_balance(line)
|
88
|
+
line[TRANSACTION_BALANCE..TRANSACTION_BALANCE_END].get_numbers
|
89
|
+
end
|
90
|
+
|
91
|
+
def transaction_remarks(line)
|
92
|
+
line[TRANSACTION_REMARKS..TRANSACTION_REMARKS_END].remove_white_spaces
|
93
|
+
end
|
94
|
+
|
95
|
+
def transaction_reference(line)
|
96
|
+
line[TRANSACTION_REFERENCE..TRANSACTION_REFERENCE_END].remove_white_spaces
|
97
|
+
end
|
98
|
+
|
99
|
+
def debit_amount(line)
|
100
|
+
line[TRANSACTION_DEBIT..TRANSACTION_DEBIT_END].get_numbers
|
101
|
+
end
|
102
|
+
|
103
|
+
def credit_amount(line)
|
104
|
+
line[TRANSACTION_CREDIT..TRANSACTION_CREDIT_END].get_numbers
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def transaction_items(line)
|
109
|
+
line.strip.split(" ").reject(&:empty?)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'net/http'
|
2
|
-
|
3
1
|
module NgBankParser
|
4
2
|
class Router
|
5
3
|
@banks = $Banks
|
@@ -14,15 +12,11 @@ module NgBankParser
|
|
14
12
|
|
15
13
|
if @selected_bank.nil?
|
16
14
|
return {status: 0, message: "Your bank is not yet supported"}
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
extensions = @selected_bank[:parsers].map {|e| e[:extensions]}
|
24
|
-
@supported_extensions = extensions.reduce(:concat)
|
25
|
-
pick_parser()
|
15
|
+
else
|
16
|
+
extensions = @selected_bank[:parsers].map {|e| e[:extensions]}
|
17
|
+
@supported_extensions = extensions.reduce(:concat)
|
18
|
+
pick_parser()
|
19
|
+
end
|
26
20
|
end
|
27
21
|
|
28
22
|
private
|
@@ -48,15 +42,5 @@ module NgBankParser
|
|
48
42
|
parser_response = class_object.parse(@path, @password)
|
49
43
|
end
|
50
44
|
|
51
|
-
def self.file_exists(path)
|
52
|
-
return true if File.exists?(path)
|
53
|
-
|
54
|
-
uri = URI(path)
|
55
|
-
|
56
|
-
request = Net::HTTP.new uri.host
|
57
|
-
response= request.request_head uri.path
|
58
|
-
return response.code.to_i == 200
|
59
|
-
end
|
60
|
-
|
61
45
|
end
|
62
46
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ng-bank-parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Opemipo Aikomo
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: exe
|
13
13
|
cert_chain: []
|
14
|
-
date: 2015-
|
14
|
+
date: 2015-09-22 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -148,18 +148,27 @@ files:
|
|
148
148
|
- bin/setup
|
149
149
|
- lib/ng-bank-parser.rb
|
150
150
|
- lib/ng-bank-parser/banks.rb
|
151
|
+
- lib/ng-bank-parser/fixtures/accessbank-pdf-invalid.pdf
|
152
|
+
- lib/ng-bank-parser/fixtures/accessbank-pdf-valid.pdf
|
151
153
|
- lib/ng-bank-parser/fixtures/firstbank-pdf-invalid.pdf
|
152
154
|
- lib/ng-bank-parser/fixtures/firstbank-pdf-valid.pdf
|
153
155
|
- lib/ng-bank-parser/fixtures/gtb-excel-invalid.pdf
|
154
156
|
- lib/ng-bank-parser/fixtures/gtb-excel-valid.xls
|
155
157
|
- lib/ng-bank-parser/fixtures/gtb-excel-valid.xlsx
|
158
|
+
- lib/ng-bank-parser/fixtures/hb-pdf-invalid.pdf
|
159
|
+
- lib/ng-bank-parser/fixtures/hb-pdf-valid.pdf
|
156
160
|
- lib/ng-bank-parser/fixtures/uba-pdf-invalid.pdf
|
157
161
|
- lib/ng-bank-parser/fixtures/uba-pdf-valid.pdf
|
162
|
+
- lib/ng-bank-parser/parsers/accessbank-pdf-parser.rb
|
163
|
+
- lib/ng-bank-parser/parsers/accessbank-pdf-parser/helpers.rb
|
158
164
|
- lib/ng-bank-parser/parsers/firstbank-pdf-parser.rb
|
159
165
|
- lib/ng-bank-parser/parsers/firstbank-pdf-parser/helpers.rb
|
160
166
|
- lib/ng-bank-parser/parsers/firstbank-pdf-parser/statement_utils.rb
|
161
167
|
- lib/ng-bank-parser/parsers/gtb-excel-parser.rb
|
162
168
|
- lib/ng-bank-parser/parsers/gtb-excel-parser/helpers.rb
|
169
|
+
- lib/ng-bank-parser/parsers/hb-pdf-parser.rb
|
170
|
+
- lib/ng-bank-parser/parsers/hb-pdf-parser/hb_constants.rb
|
171
|
+
- lib/ng-bank-parser/parsers/hb-pdf-parser/hb_transaction_helpers.rb
|
163
172
|
- lib/ng-bank-parser/parsers/uba-pdf-parser.rb
|
164
173
|
- lib/ng-bank-parser/parsers/uba-pdf-parser/classes/string.rb
|
165
174
|
- lib/ng-bank-parser/parsers/uba-pdf-parser/classes/transaction.rb
|