Browse Source

[11.0][MIG] account_bank_statement_import_camt

pull/134/head
Andrea 7 years ago
parent
commit
83b5d6483c
  1. 30
      account_bank_statement_import_camt/README.rst
  2. 1
      account_bank_statement_import_camt/__init__.py
  3. 3
      account_bank_statement_import_camt/__manifest__.py
  4. 1
      account_bank_statement_import_camt/models/__init__.py
  5. 5
      account_bank_statement_import_camt/models/account_bank_statement_import.py
  6. 47
      account_bank_statement_import_camt/models/parser.py
  7. 1
      account_bank_statement_import_camt/tests/__init__.py
  8. 69
      account_bank_statement_import_camt/tests/test_import_bank_statement.py

30
account_bank_statement_import_camt/README.rst

@ -1,25 +1,28 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:alt: License: AGPL-3
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: https://www.gnu.org/licenses/agpl
:alt: License: AGPL-3
=========================
Bank Statement Parse Camt Bank Statement Parse Camt
========================= =========================
Module to import SEPA CAMT.053 Format bank statement files.
Module to import SEPA CAMT.053 and CAMT.054 Format bank statement files.
Based on the Banking addons framework. Based on the Banking addons framework.
Known issues / Roadmap
======================
* None
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/174/11.0
Bug Tracker Bug Tracker
=========== ===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/bank-statement-import/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback
`here <https://github.com/OCA/bank-statement-import/issues/new?body=module:%20account_bank_statement_import%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/bank-statement-import/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.
Credits Credits
@ -31,6 +34,9 @@ Contributors
* Holger Brunn <hbrunn@therp.nl> * Holger Brunn <hbrunn@therp.nl>
* Stefan Rijnhart <srijnhart@therp.nl> * Stefan Rijnhart <srijnhart@therp.nl>
* Ronald Portier <rportier@therp.nl> * Ronald Portier <rportier@therp.nl>
* Andrea Stirpe <a.stirpe@onestein.nl>
Do not contact contributors directly about support or help with technical issues.
Maintainer Maintainer
---------- ----------
@ -45,6 +51,4 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and mission is to support the collaborative development of Odoo features and
promote its widespread use. promote its widespread use.
To contribute to this module, please visit http://odoo-community.org.
This module should make it easy to migrate bank statement import
modules written for earlies versions of Odoo/OpenERP.
To contribute to this module, please visit https://odoo-community.org.

1
account_bank_statement_import_camt/__init__.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# © 2013-2016 Therp BV <http://therp.nl> # © 2013-2016 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import models from . import models

3
account_bank_statement_import_camt/__manifest__.py

@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
# © 2013-2017 Therp BV <http://therp.nl> # © 2013-2017 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{ {
'name': 'CAMT Format Bank Statements Import', 'name': 'CAMT Format Bank Statements Import',
'version': '10.0.1.1.0',
'version': '11.0.1.0.0',
'license': 'AGPL-3', 'license': 'AGPL-3',
'author': 'Odoo Community Association (OCA), Therp BV', 'author': 'Odoo Community Association (OCA), Therp BV',
'website': 'https://github.com/OCA/bank-statement-import', 'website': 'https://github.com/OCA/bank-statement-import',

1
account_bank_statement_import_camt/models/__init__.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# © 2013-2016 Therp BV <http://therp.nl> # © 2013-2016 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import parser from . import parser

5
account_bank_statement_import_camt/models/account_bank_statement_import.py

@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
"""Add process_camt method to account.bank.statement.import.""" """Add process_camt method to account.bank.statement.import."""
# © 2013-2016 Therp BV <http://therp.nl> # © 2013-2016 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging import logging
import StringIO
from io import BytesIO
import zipfile import zipfile
from odoo import api, models from odoo import api, models
@ -23,7 +22,7 @@ class AccountBankStatementImport(models.TransientModel):
return parser.parse(data_file) return parser.parse(data_file)
except ValueError: except ValueError:
try: try:
with zipfile.ZipFile(StringIO.StringIO(data_file)) as data:
with zipfile.ZipFile(BytesIO(data_file)) as data:
currency = None currency = None
account_number = None account_number = None
transactions = [] transactions = []

47
account_bank_statement_import_camt/models/parser.py

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""Class to parse camt files.""" """Class to parse camt files."""
# © 2013-2016 Therp BV <http://therp.nl> # © 2013-2016 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
@ -9,8 +8,8 @@ from odoo import models
class CamtParser(models.AbstractModel): class CamtParser(models.AbstractModel):
_name = 'account.bank.statement.import.camt.parser'
"""Parser for camt bank statement import files.""" """Parser for camt bank statement import files."""
_name = 'account.bank.statement.import.camt.parser'
def parse_amount(self, ns, node): def parse_amount(self, ns, node):
"""Parse element that contains Amount and CreditDebitIndicator.""" """Parse element that contains Amount and CreditDebitIndicator."""
@ -78,10 +77,6 @@ class CamtParser(models.AbstractModel):
if party_node: if party_node:
self.add_value_from_node( self.add_value_from_node(
ns, party_node[0], './ns:Nm', transaction, 'partner_name') ns, party_node[0], './ns:Nm', transaction, 'partner_name')
self.add_value_from_node(
ns, party_node[0], './ns:PstlAdr/ns:Ctry', transaction,
'partner_country'
)
address_node = party_node[0].xpath( address_node = party_node[0].xpath(
'./ns:PstlAdr/ns:AdrLine', namespaces={'ns': ns}) './ns:PstlAdr/ns:AdrLine', namespaces={'ns': ns})
if address_node: if address_node:
@ -96,12 +91,6 @@ class CamtParser(models.AbstractModel):
'./ns:IBAN', namespaces={'ns': ns}) './ns:IBAN', namespaces={'ns': ns})
if iban_node: if iban_node:
transaction['account_number'] = iban_node[0].text transaction['account_number'] = iban_node[0].text
bic_node = node.xpath(
'./ns:RltdAgts/ns:%sAgt/ns:FinInstnId/ns:BIC' % party_type,
namespaces={'ns': ns}
)
if bic_node:
transaction['account_bic'] = bic_node[0].text
else: else:
self.add_value_from_node( self.add_value_from_node(
ns, account_node[0], './ns:Othr/ns:Id', transaction, ns, account_node[0], './ns:Othr/ns:Id', transaction,
@ -111,16 +100,12 @@ class CamtParser(models.AbstractModel):
def parse_transaction(self, ns, node): def parse_transaction(self, ns, node):
"""Parse transaction (entry) node.""" """Parse transaction (entry) node."""
transaction = {} transaction = {}
self.add_value_from_node(
ns, node, './ns:BkTxCd/ns:Prtry/ns:Cd', transaction,
'transfer_type'
)
self.add_value_from_node( self.add_value_from_node(
ns, node, './ns:BookgDt/ns:Dt', transaction, 'date') ns, node, './ns:BookgDt/ns:Dt', transaction, 'date')
self.add_value_from_node(
ns, node, './ns:BookgDt/ns:Dt', transaction, 'execution_date')
self.add_value_from_node(
ns, node, './ns:ValDt/ns:Dt', transaction, 'value_date')
if not transaction.get('date'):
self.add_value_from_node(
ns, node, './ns:ValDt/ns:Dt', transaction, 'date')
transaction['amount'] = self.parse_amount(ns, node) transaction['amount'] = self.parse_amount(ns, node)
@ -140,7 +125,7 @@ class CamtParser(models.AbstractModel):
], ],
transaction, 'ref' transaction, 'ref'
) )
transaction['data'] = etree.tostring(node)
return transaction return transaction
def get_balance_amounts(self, ns, node): def get_balance_amounts(self, ns, node):
@ -176,6 +161,19 @@ class CamtParser(models.AbstractModel):
self.parse_amount(ns, end_balance_node) self.parse_amount(ns, end_balance_node)
) )
def get_statement_date(self, ns, node):
"""Return statement date.
The date might be in the balance node:
CLBD = ClosingBalance
"""
code_expr = \
'./ns:Bal/ns:Tp/ns:CdOrPrtry/ns:Cd[text()="CLBD"]' \
'/../../../ns:Dt/ns:Dt/text()'
date_node = node.xpath(code_expr, namespaces={'ns': ns})
date = date_node and date_node[0] or None
return date
def parse_statement(self, ns, node): def parse_statement(self, ns, node):
"""Parse a single Stmt node.""" """Parse a single Stmt node."""
result = {} result = {}
@ -193,6 +191,7 @@ class CamtParser(models.AbstractModel):
ns, node, './ns:Acct/ns:Ccy', result, 'currency') ns, node, './ns:Acct/ns:Ccy', result, 'currency')
result['balance_start'], result['balance_end_real'] = ( result['balance_start'], result['balance_end_real'] = (
self.get_balance_amounts(ns, node)) self.get_balance_amounts(ns, node))
result['date'] = self.get_statement_date(ns, node)
transaction_nodes = node.xpath('./ns:Ntry', namespaces={'ns': ns}) transaction_nodes = node.xpath('./ns:Ntry', namespaces={'ns': ns})
result['transactions'] = [] result['transactions'] = []
for entry_node in transaction_nodes: for entry_node in transaction_nodes:
@ -210,10 +209,12 @@ class CamtParser(models.AbstractModel):
) )
if not re_camt.search(ns): if not re_camt.search(ns):
raise ValueError('no camt: ' + ns) raise ValueError('no camt: ' + ns)
# Check wether version 052 or 053:
# Check wether version 052 ,053 or 054:
re_camt_version = re.compile( re_camt_version = re.compile(
r'(^urn:iso:std:iso:20022:tech:xsd:camt.053.'
r'(^urn:iso:std:iso:20022:tech:xsd:camt.054.'
r'|^urn:iso:std:iso:20022:tech:xsd:camt.053.'
r'|^urn:iso:std:iso:20022:tech:xsd:camt.052.' r'|^urn:iso:std:iso:20022:tech:xsd:camt.052.'
r'|^ISO:camt.054.'
r'|^ISO:camt.053.' r'|^ISO:camt.053.'
r'|^ISO:camt.052.)' r'|^ISO:camt.052.)'
) )

1
account_bank_statement_import_camt/tests/__init__.py

@ -1,4 +1,3 @@
# -*- encoding: utf-8 -*-
"""Test import of bank statement for camt.053.""" """Test import of bank statement for camt.053."""
# © 2013-2016 Therp BV <http://therp.nl> # © 2013-2016 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

69
account_bank_statement_import_camt/tests/test_import_bank_statement.py

@ -1,22 +1,13 @@
# -*- coding: utf-8 -*-
"""Run test to import camt.053 import.""" """Run test to import camt.053 import."""
# © 2013-2016 Therp BV <http://therp.nl> # © 2013-2016 Therp BV <http://therp.nl>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import base64 import base64
from odoo.tests.common import TransactionCase from odoo.tests.common import TransactionCase
from odoo.tools.misc import file_open
from odoo.modules.module import get_module_resource
class TestImport(TransactionCase): class TestImport(TransactionCase):
"""Run test to import camt import.""" """Run test to import camt import."""
transactions = [
{
'account_number': 'NL46ABNA0499998748',
'amount': -754.25,
'date': '2014-01-05',
'ref': '435005714488-ABNO33052620',
},
]
def setUp(self): def setUp(self):
super(TestImport, self).setUp() super(TestImport, self).setUp()
@ -35,34 +26,48 @@ class TestImport(TransactionCase):
def test_statement_import(self): def test_statement_import(self):
"""Test correct creation of single statement.""" """Test correct creation of single statement."""
action = {}
with file_open(
'account_bank_statement_import_camt/test_files/test-camt053'
) as testfile:
action = self.env['account.bank.statement.import'].create({
'data_file': base64.b64encode(testfile.read()),
}).import_file()
testfile = get_module_resource(
'account_bank_statement_import_camt',
'test_files',
'test-camt053',
)
datafile = open(testfile, 'rb').read()
action = self.env['account.bank.statement.import'].create({
'data_file': base64.b64encode(datafile),
}).import_file()
for statement in self.env['account.bank.statement'].browse( for statement in self.env['account.bank.statement'].browse(
action['context']['statement_ids'] action['context']['statement_ids']
): ):
self.assertTrue(any(
all(
line[key] == self.transactions[0][key]
for key in ['amount', 'date', 'ref']
) and
line.bank_account_id.acc_number ==
self.transactions[0]['account_number']
for line in statement.line_ids
))
self.assertTrue(statement.name)
self.assertEqual(statement.date, '2014-01-05')
self.assertEqual(statement.difference, -504.16)
self.assertEqual(statement.journal_type, 'bank')
self.assertEqual(len(statement.line_ids), 3)
for line in statement.line_ids:
self.assertIn(line.amount, [1405.31, -594.05, -754.25])
self.assertEqual(line.date, '2014-01-05')
self.assertTrue(line.ref)
self.assertTrue(line.journal_id)
self.assertTrue(line.name)
self.assertTrue(line.note)
self.assertEqual(line.state, 'open')
self.assertIn(
line.bank_account_id.acc_number,
['NL69ABNA0522123643',
'NL46ABNA0499998748']
)
def test_zip_import(self): def test_zip_import(self):
"""Test import of multiple statements from zip file.""" """Test import of multiple statements from zip file."""
with file_open(
'account_bank_statement_import_camt/test_files/test-camt053.zip'
) as testfile:
action = self.env['account.bank.statement.import'].create({
'data_file': base64.b64encode(testfile.read()),
}).import_file()
testfile = get_module_resource(
'account_bank_statement_import_camt',
'test_files',
'test-camt053.zip',
)
datafile = open(testfile, 'rb').read()
action = self.env['account.bank.statement.import'].create({
'data_file': base64.b64encode(datafile),
}).import_file()
for statement in self.env['account.bank.statement'].browse( for statement in self.env['account.bank.statement'].browse(
action['context']['statement_ids'] action['context']['statement_ids']
): ):

Loading…
Cancel
Save