Skip to content

Commit 38d7f8c

Browse files
author
Hong-Thai Nguyen
authored
Merge pull request #5 from thaichat04/render-accounts
Render accounts feature
2 parents 47fbccd + e5b70ca commit 38d7f8c

File tree

18 files changed

+381
-216
lines changed

18 files changed

+381
-216
lines changed

.idea/.gitignore

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/inspectionProfiles/profiles_settings.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/python-cfonb.iml

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cfonb/tests/test_statement.py

Lines changed: 21 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1,167 +1,36 @@
11
# python import - http://docs.python.org/library/unittest.html
22
import unittest
3-
from StringIO import StringIO
3+
from datetime import date
4+
from io import StringIO
45

5-
# import cfonb module
6-
from cfonb.parser import Row, ParsingError, statement as p
6+
from nose.tools import assert_is_not_none, assert_equal
77

8-
9-
HEAD_LINE = '0130002 00447 0000888899H 160811 0000000132303H '
10-
11-
12-
HEAD_VALUES = [
13-
'01',
14-
'30002',
15-
' ',
16-
'00447',
17-
' ',
18-
' ',
19-
' ',
20-
'0000888899H',
21-
' ',
22-
'160811',
23-
' ',
24-
'0000000132303H',
25-
' ',
26-
]
27-
28-
29-
CONTENT_4_LINE = '0430002013400447EUR2E0000431040H21210811 210811VIREMENT COMBELL 0000000 0000000020000} '
30-
31-
CONTENT_4_VALUES = [
32-
'04',
33-
'30002',
34-
'0134',
35-
'00447',
36-
'EUR',
37-
'2',
38-
'E',
39-
'0000431040H',
40-
'21',
41-
'210811',
42-
' ',
43-
'210811',
44-
'VIREMENT COMBELL ',
45-
' ',
46-
'0000000',
47-
' ',
48-
' ',
49-
'0000000020000}',
50-
' ',
51-
]
52-
53-
54-
FOOT_LINE = '0730002 00447 0000888899H 280911 0000000118711D '
55-
56-
57-
FOOT_VALUES = [
58-
'07',
59-
'30002',
60-
' ',
61-
'00447',
62-
' ',
63-
' ',
64-
' ',
65-
'0000888899H',
66-
' ',
67-
'280911',
68-
' ',
69-
'0000000118711D',
70-
' ',
71-
]
8+
from cfonb import StatementReader
9+
from cfonb.writer.statement import Statement
7210

7311

7412
class TestStatement(unittest.TestCase):
7513

76-
def setUp(self):
77-
pass
78-
79-
def tearDown(self):
80-
pass
81-
82-
def test_parse_head(self):
83-
row = p.parse_head(HEAD_LINE)
84-
# little type check
85-
self.assertIsInstance(row, Row, 'invalid row type: %s' % type(row))
86-
# list copy for check
87-
list_1 = row.as_list()[:]
88-
list_2 = row.as_dict().values()[:]
89-
# sort list to be compared
90-
list_1.sort()
91-
list_2.sort()
92-
# list check
93-
self.assertEqual(list_1, list_2)
94-
# check dict and obj format
95-
for i in range(13):
96-
self.assertEqual(row.as_list()[i], getattr(row.as_obj(), p.HEAD_KEYS[i]))
97-
self.assertEqual(row.as_list()[i], HEAD_VALUES[i])
98-
99-
def test_parse_content_4(self):
100-
row = p.parse_content_4(CONTENT_4_LINE)
101-
# little type check
102-
self.assertIsInstance(row, Row, 'invalid row type: %s' % type(row))
103-
# list copy for check
104-
list_1 = row.as_list()[:]
105-
list_2 = row.as_dict().values()[:]
106-
# sort list to be compared
107-
list_1.sort()
108-
list_2.sort()
109-
# list check
110-
self.assertEqual(list_1, list_2)
111-
# check dict and obj format
112-
for i in range(19):
113-
self.assertEqual(row.as_list()[i], getattr(row.as_obj(), p.CONTENT_4_KEYS[i]))
114-
self.assertEqual(row.as_list()[i], CONTENT_4_VALUES[i])
115-
116-
def test_parse_footer(self):
117-
row = p.parse_footer(FOOT_LINE)
118-
# little type check
119-
self.assertIsInstance(row, Row, 'invalid row type: %s' % type(row))
120-
# list copy for check
121-
list_1 = row.as_list()[:]
122-
list_2 = row.as_dict().values()[:]
123-
# sort list to be compared
124-
list_1.sort()
125-
list_2.sort()
126-
# list check
127-
self.assertEqual(list_1, list_2)
128-
# check dict and obj format
129-
for i in range(13):
130-
self.assertEqual(row.as_list()[i], getattr(row.as_obj(), p.FOOT_KEYS[i]))
131-
self.assertEqual(row.as_list()[i], FOOT_VALUES[i])
132-
133-
def test_statement(self):
134-
# prepare file obj
135-
file_obj = StringIO()
136-
file_obj.write("%s\n" % HEAD_LINE)
137-
file_obj.write("%s\n" % CONTENT_4_LINE)
138-
file_obj.write("%s\n" % FOOT_LINE)
139-
file_obj.seek(0)
140-
# init statement
141-
statement = p.Statement()
142-
statement.parse(file_obj)
143-
# prepare values to compare
144-
header_line = p.parse_head(HEAD_LINE)
145-
content_line = p.parse_content_4(CONTENT_4_LINE)
146-
footer_line = p.parse_footer(FOOT_LINE)
147-
# some tests
148-
self.assertIsInstance(statement, p.Statement)
149-
self.assertIsInstance(statement.header, Row)
150-
self.assertIsInstance(statement.footer, Row)
151-
self.assertIsInstance(statement.lines, list)
152-
self.assertEqual(len(statement.lines), 1)
153-
self.assertIsInstance(statement.lines[0], Row)
154-
self.assertEqual(statement.header, header_line)
155-
self.assertEqual(statement.footer, footer_line)
156-
self.assertEqual(statement.lines[0], content_line)
14+
def test_render_parse_cfonb(self):
15+
content = Statement().header('20002', '90005', 'EUR', '01711467640', date(2011, 10, 14), 12345.67)\
16+
.add('20002', '1234567', '90005', 'EUR', '01711467640', date(2011, 10, 14), 'label 1', 1234.56, 'reference1')\
17+
.add('20002', '1234567', '90005', 'EUR', '01711467640', date(2011, 10, 13), 'label 2', 123.45, 'reference2')\
18+
.render()
19+
print('content: {}'.format(content))
20+
reader = StatementReader()
21+
result = reader.parse(StringIO(content))
22+
for account in result:
23+
assert_is_not_none(account.account_nb)
24+
assert_equal(account.header.get('account_nb'), '01711467640')
25+
assert_equal(account.header.get('currency_code'), 'EUR')
26+
assert_equal(account.header.get('nb_of_dec'), '2')
27+
assert_equal(account.header.get('bank_code'), '20002')
28+
print('{}'.format(account.header))
15729

15830

15931
def suite():
16032
suite = unittest.TestSuite()
161-
suite.addTest(TestStatement('test_parse_head'))
162-
suite.addTest(TestStatement('test_parse_content_4'))
163-
suite.addTest(TestStatement('test_parse_footer'))
164-
suite.addTest(TestStatement('test_statement'))
33+
suite.addTest(TestStatement('test_parse_cfonb'))
16534
return suite
16635

16736

cfonb/tests/test_transfert.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88

99
def _print(res):
10-
print res.replace('\r\n','\\r\\n').replace(' ','.')
10+
print(res.replace('\r\n','\\r\\n').replace(' ','.'))
1111

1212

1313
class TestTransfert(unittest.TestCase):

cfonb/writer/common.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
BR_LINE = '\r\n'
2+
3+
4+
def write(input_, length, rpad=False, fill_char=' '):
5+
input_ = str(input_)
6+
if (rpad):
7+
return input_.rjust(length, fill_char)[:length]
8+
else:
9+
return input_.ljust(length, fill_char)[:length]
10+
11+
12+
def save(header, body, footer, filename=None):
13+
content = header
14+
content += body
15+
content += footer
16+
if filename is not None:
17+
f = open(filename,'w')
18+
f.write(content)
19+
f.close()
20+
return content
21+
22+
23+
def date_format(date):
24+
return date.strftime(format='%d%m') + date.strftime(format='%y')

cfonb/writer/statement.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# coding: UTF-8
2+
3+
"""
4+
This file is part of CFONB.
5+
6+
CFONB is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU Lesser General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
Foobar is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU Lesser General Public License
17+
along with CFONB. If not, see <http://www.gnu.org/licenses/>.
18+
19+
Coryright 2020, Dhatim
20+
Créer un fichier d'opérations bancaires avec
21+
- l'entête (01)
22+
- ligne détail (04)
23+
- ligne total (facultative)
24+
25+
"""
26+
from cfonb.writer.common import write, date_format, save, BR_LINE
27+
28+
29+
class Statement(object):
30+
def __init__(self):
31+
self._header = {}
32+
self._content = []
33+
self._footer = {}
34+
35+
def header(self, bank_code, agency_code, currency, account_number, date, amount):
36+
self._header['bank_code'] = bank_code
37+
self._header['agency_code'] = agency_code
38+
self._header['currency'] = currency
39+
self._header['account_number'] = account_number
40+
self._header['date'] = date
41+
self._header['amount'] = amount
42+
return self
43+
44+
def footer(self, bank_code, agency_code, currency, account_number, date, amount):
45+
self._footer['bank_code'] = bank_code
46+
self._footer['agency_code'] = agency_code
47+
self._footer['currency'] = currency
48+
self._footer['account_number'] = account_number
49+
self._footer['date'] = date
50+
self._footer['amount'] = amount
51+
return self
52+
53+
def add(self, bank_code, operation_code, agency_code, currency,
54+
account_number, date, label, amount, reference):
55+
line = write('04', 2)
56+
line += write(bank_code, 5)
57+
line += write(operation_code, 4)
58+
line += write(agency_code, 5)
59+
line += write(currency, 3)
60+
line += write('2', 1) # number of decimal
61+
line += write(' ', 1) # SIT code
62+
line += write(account_number, 11)
63+
line += write('08', 2) # interbank code
64+
line += write(date_format(date), 6)
65+
line += write(' ', 2) # rejected code
66+
line += write(date_format(date), 6)
67+
line += write(label, 31)
68+
line += write(' ', 2) # reserve zone
69+
line += write(' ', 7) # entry writing code
70+
line += write(' ', 1) # exoneration code
71+
line += write(' ', 1) # reserve zone
72+
line += write(str(amount).replace('.', '') + 'A', 14, rpad=True, fill_char='0')
73+
line += write(reference, 16)
74+
self._content.append(line)
75+
return self
76+
77+
def render(self, filename=None):
78+
return save(self._render_header(), BR_LINE.join(self._content) + BR_LINE, self._render_footer(), filename)
79+
80+
def _render_header(self):
81+
if self._header:
82+
line = write('01', 2)
83+
line += write(self._header['bank_code'], 5)
84+
line += write(' ', 4) # reserved zone
85+
line += write(self._header['agency_code'], 5)
86+
line += write(self._header['currency'], 3)
87+
line += write('2', 1) # number of decimal
88+
line += write(' ', 1) # reserved zone
89+
line += write(self._header['account_number'], 11)
90+
line += write(' ', 2) # reserved zone
91+
line += write(date_format(self._header['date']), 6)
92+
line += write(' ', 50) # reserved zone
93+
line += write(str(self._header['amount']).replace('.', '') + 'A', 14, rpad=True, fill_char='0')
94+
line += write(' ', 16) # reserved zone
95+
return line + BR_LINE
96+
return ''
97+
98+
def _render_footer(self):
99+
if self._footer:
100+
line = write('07', 2)
101+
line += write(self._footer['bank_code'], 5)
102+
line += write(' ', 4) # reserved zone
103+
line += write(self._footer['agency_code'], 5)
104+
line += write(self._footer['currency'], 3)
105+
line += write('2', 1) # number of decimal
106+
line += write(' ', 1) # reserved zone
107+
line += write(self._footer['account_number'], 11)
108+
line += write(' ', 2) # reserved zone
109+
line += write(date_format(self._footer['date']), 6)
110+
line += write(' ', 50) # reserved zone
111+
line += write(str(self._footer['amount']).replace('.', '') + 'A', 14, rpad=True, fill_char='0')
112+
line += write(' ', 16) # reserved zone
113+
return line + BR_LINE
114+
return ''

0 commit comments

Comments
 (0)