Browse Source

[MIG] account_bank_statement_import_ofx: Migration to 10.0

* Remove the code that matches partners, which is wrong and cannot work
* Better 'name' on bank statement
* Move from models directory to wizard
* Remove demo data to tests
* Use latest version of ofxparse from github
pull/200/head
Alexis de Lattre 8 years ago
committed by Benjamin
parent
commit
a2e552cf92
  1. 37
      account_bank_statement_import_ofx/README.rst
  2. 2
      account_bank_statement_import_ofx/__init__.py
  3. 10
      account_bank_statement_import_ofx/__manifest__.py
  4. 23
      account_bank_statement_import_ofx/demo/demo_data.xml
  5. 75
      account_bank_statement_import_ofx/models/account_bank_statement_import.py
  6. 40
      account_bank_statement_import_ofx/models/ofx.py
  7. 39
      account_bank_statement_import_ofx/tests/test_import_bank_statement.py
  8. 2
      account_bank_statement_import_ofx/views/view_account_bank_statement_import.xml
  9. 0
      account_bank_statement_import_ofx/wizard/__init__.py
  10. 94
      account_bank_statement_import_ofx/wizard/account_bank_statement_import.py

37
account_bank_statement_import_ofx/README.rst

@ -5,17 +5,11 @@
Import OFX Bank Statement Import OFX Bank Statement
========================= =========================
This module allows you to import the machine readable OFX Files in Odoo: they are parsed and stored in human readable format in
Accounting \ Bank and Cash \ Bank Statements.
This module adds support for the import of bank statements in `OFX format <https://en.wikipedia.org/wiki/Open_Financial_Exchange>`_.
Bank Statements may be generated containing a subset of the OFX information (only those transaction lines that are required for the Bank Statements may be generated containing a subset of the OFX information (only those transaction lines that are required for the
creation of the Financial Accounting records). creation of the Financial Accounting records).
The module has been initiated by a backport of the new framework developed
by Odoo for V9 at its early stage. It's no more kept in sync with the V9 since
it has reach a stage where maintaining a pure backport of 9.0 in 8.0 is not
feasible anymore
Installation Installation
============ ============
@ -23,20 +17,12 @@ The module requires one additional python lib:
* `ofxparse <http://pypi.python.org/pypi/ofxparse>`_ * `ofxparse <http://pypi.python.org/pypi/ofxparse>`_
Technical Note
==============
this module is based on ofxparse python lib and overload some of its functions.
* For the time being the default ofxparse lib available with
'pip install ofxparse' do not manage correctly european amount that are
written with ',' and not with '.'. (For exemple, The Credit Cooperatif
French Bank provides OFX 1.0 with amounts written with coma)
April, 27 2016: this problem has been fixed here:
https://github.com/jseutter/ofxparse/commit/283f89c3246ed3fedccc3ef5c96078b7d5b94579
but it is not available in the pip lib for the time being.
Usage
=====
.. 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/10.0
Known issues / Roadmap Known issues / Roadmap
====================== ======================
@ -46,11 +32,10 @@ Known issues / Roadmap
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_ofx%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 smashing it by providing a detailed and welcomed feedback.
Credits Credits
======= =======
@ -77,4 +62,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.
To contribute to this module, please visit https://odoo-community.org.

2
account_bank_statement_import_ofx/__init__.py

@ -1,2 +1,2 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from . import models
from . import wizard

10
account_bank_statement_import_ofx/__manifest__.py

@ -2,26 +2,22 @@
{ {
'name': 'Import OFX Bank Statement', 'name': 'Import OFX Bank Statement',
'category': 'Banking addons', 'category': 'Banking addons',
'version': '9.0.0.0.0',
'version': '10.0.1.0.0',
'license': 'AGPL-3', 'license': 'AGPL-3',
'author': 'OpenERP SA,'
'author': 'Odoo SA,'
'Akretion,'
'La Louve,' 'La Louve,'
'GRAP,' 'GRAP,'
'Odoo Community Association (OCA)', 'Odoo Community Association (OCA)',
'website': 'https://odoo-community.org/', 'website': 'https://odoo-community.org/',
'depends': [ 'depends': [
'l10n_generic_coa',
'account_bank_statement_import', 'account_bank_statement_import',
], ],
'data': [ 'data': [
'views/view_account_bank_statement_import.xml', 'views/view_account_bank_statement_import.xml',
], ],
'demo': [
'demo/demo_data.xml',
],
'external_dependencies': { 'external_dependencies': {
'python': ['ofxparse'], 'python': ['ofxparse'],
}, },
'auto_install': False,
'installable': True, 'installable': True,
} }

23
account_bank_statement_import_ofx/demo/demo_data.xml

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="ofx_sequence" model="ir.sequence">
<field name="name">Bank Journal - (test ofx)</field>
<field name="prefix">OFX/%(range_year)s/</field>
<field name="implementation">no_gap</field>
<field name="padding">4</field>
<field name="use_date_range">1</field>
</record>
<record id="ofx_bank_journal" model="account.journal">
<field name="name">Bank Journal - (test ofx)</field>
<field name="bank_acc_number">123456</field>
<field name="code">TBNKOFX</field>
<field name="type">bank</field>
<field name="sequence_id" ref="ofx_sequence"/>
<field name="user_id" ref="base.user_root"/>
<field name="currency_id" ref="base.USD"/>
</record>
</odoo>

75
account_bank_statement_import_ofx/models/account_bank_statement_import.py

@ -1,75 +0,0 @@
# -*- coding: utf-8 -*-
import logging
import StringIO
from openerp import api, models
from openerp.tools.translate import _
from openerp.exceptions import Warning as UserError
from .ofx import OfxParser, OfxParser_ok
_logger = logging.getLogger(__name__)
class AccountBankStatementImport(models.TransientModel):
_inherit = 'account.bank.statement.import'
@api.model
def _check_ofx(self, data_file):
if not OfxParser_ok:
return False
try:
ofx = OfxParser.parse(StringIO.StringIO(data_file))
except Exception as e:
_logger.debug(e)
return False
return ofx
@api.model
def _parse_file(self, data_file):
ofx = self._check_ofx(data_file)
if not ofx:
return super(AccountBankStatementImport, self)._parse_file(
data_file)
transactions = []
total_amt = 0.00
try:
for transaction in ofx.account.statement.transactions:
# Since ofxparse doesn't provide account numbers, we'll have
# to find res.partner and res.partner.bank here
# (normal behavious is to provide 'account_number', which the
# generic module uses to find partner/bank)
bank_account_id = partner_id = False
banks = self.env['res.partner.bank'].search(
[('bank_name', '=', transaction.payee)], limit=1)
if banks:
bank_account = banks[0]
bank_account_id = bank_account.id
partner_id = bank_account.partner_id.id
vals_line = {
'date': transaction.date,
'name': transaction.payee + (
transaction.memo and ': ' + transaction.memo or ''),
'ref': transaction.id,
'amount': transaction.amount,
'unique_import_id': transaction.id,
'bank_account_id': bank_account_id,
'partner_id': partner_id,
}
total_amt += float(transaction.amount)
transactions.append(vals_line)
except Exception, e:
raise UserError(_("The following problem occurred during import. "
"The file might not be valid.\n\n %s" % e.message))
vals_bank_statement = {
'name': ofx.account.routing_number,
'transactions': transactions,
'balance_start': ofx.account.statement.balance,
'balance_end_real':
float(ofx.account.statement.balance) + total_amt,
}
return ofx.account.statement.currency, ofx.account.number, [
vals_bank_statement]

40
account_bank_statement_import_ofx/models/ofx.py

@ -1,40 +0,0 @@
# -*- coding: utf-8 -*-
import logging
_logger = logging.getLogger(__name__)
try:
from ofxparse import OfxParser as OfxParserOriginal
OfxParser_ok = True
except ImportError:
_logger.warn("ofxparse not found, OFX parsing disabled.")
OfxParserOriginal = object
OfxParser_ok = False
class OfxParser(OfxParserOriginal):
""" Custom changes in the OFX Parser.
"""
@classmethod
def _tagToDecimal(self, tag):
tag.string = tag.string.replace(',', '.')
@classmethod
def parseStatement(cls_, stmt_ofx):
"""Amount with ',' replaced by '.' in the following tags :
//LEDGERBAL/BALAMT
"""
ledgerbal_tag = stmt_ofx.find('ledgerbal')
if hasattr(ledgerbal_tag, "contents"):
cls_._tagToDecimal(ledgerbal_tag.find('balamt'))
return super(OfxParser, cls_).parseStatement(stmt_ofx)
@classmethod
def parseTransaction(cls_, txn_ofx):
"""Amount with ',' replaced by '.' in the following tags :
//TRNAMT
"""
cls_._tagToDecimal(txn_ofx.find('trnamt'))
return super(OfxParser, cls_).parseTransaction(txn_ofx)

39
account_bank_statement_import_ofx/tests/test_import_bank_statement.py

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from openerp.tests.common import TransactionCase
from openerp.modules.module import get_module_resource
from odoo.tests.common import TransactionCase
from odoo.modules.module import get_module_resource
class TestOfxFile(TransactionCase): class TestOfxFile(TransactionCase):
@ -10,26 +10,39 @@ class TestOfxFile(TransactionCase):
def setUp(self): def setUp(self):
super(TestOfxFile, self).setUp() super(TestOfxFile, self).setUp()
self.statement_import_model = self.env['account.bank.statement.import']
self.bank_statement_model = self.env['account.bank.statement']
self.absi_model = self.env['account.bank.statement.import']
self.abs_model = self.env['account.bank.statement']
self.absl_model = self.env['account.bank.statement.line']
cur = self.env.ref('base.USD')
self.env.ref('base.main_company').currency_id = cur.id
bank = self.env['res.partner.bank'].create({
'acc_number': '123456',
'partner_id': self.env.ref('base.main_partner').id,
'company_id': self.env.ref('base.main_company').id,
'bank_id': self.env.ref('base.res_bank_1').id,
})
self.env['account.journal'].create({
'name': 'Bank Journal TEST OFX',
'code': 'BNK12',
'type': 'bank',
'bank_account_id': bank.id,
})
def test_ofx_file_import(self): def test_ofx_file_import(self):
ofx_file_path = get_module_resource( ofx_file_path = get_module_resource(
'account_bank_statement_import_ofx', 'account_bank_statement_import_ofx',
'tests/test_ofx_file/', 'test_ofx.ofx') 'tests/test_ofx_file/', 'test_ofx.ofx')
ofx_file = open(ofx_file_path, 'rb').read().encode('base64') ofx_file = open(ofx_file_path, 'rb').read().encode('base64')
bank_statement = self.statement_import_model.create(
bank_statement = self.absi_model.create(
dict(data_file=ofx_file)) dict(data_file=ofx_file))
bank_statement.import_file() bank_statement.import_file()
bank_st_record = self.bank_statement_model.search(
[('name', '=', '000000123')])[0]
bank_st_record = self.abs_model.search(
[('name', 'like', '123456')])[0]
self.assertEquals(bank_st_record.balance_start, 2156.56) self.assertEquals(bank_st_record.balance_start, 2156.56)
self.assertEquals(bank_st_record.balance_end_real, 1796.56) self.assertEquals(bank_st_record.balance_end_real, 1796.56)
line = bank_st_record.line_ids[0]
self.assertEquals(line.name, 'Agrolait')
line = self.absl_model.search([
('name', '=', 'Agrolait'),
('statement_id', '=', bank_st_record.id)])[0]
self.assertEquals(line.ref, '219378') self.assertEquals(line.ref, '219378')
self.assertEquals(line.partner_id.id, self.ref('base.res_partner_2'))
self.assertEquals(
line.bank_account_id.id,
self.ref('account_bank_statement_import.ofx_partner_bank_1'))
self.assertEquals(line.date, '2013-08-24')

2
account_bank_statement_import_ofx/views/view_account_bank_statement_import.xml

@ -1,4 +1,4 @@
<?xml version="1.0" ?>
<?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<record id="view_account_bank_statement_import_form" model="ir.ui.view"> <record id="view_account_bank_statement_import_form" model="ir.ui.view">
<field name="model">account.bank.statement.import</field> <field name="model">account.bank.statement.import</field>

0
account_bank_statement_import_ofx/models/__init__.py → account_bank_statement_import_ofx/wizard/__init__.py

94
account_bank_statement_import_ofx/wizard/account_bank_statement_import.py

@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
import logging
import StringIO
from odoo import api, models, fields, _
from odoo.exceptions import UserError
from odoo.tools import float_is_zero
_logger = logging.getLogger(__name__)
try:
from ofxparse import OfxParser
except ImportError:
_logger.debug("ofxparse not found.")
OfxParser = None
class AccountBankStatementImport(models.TransientModel):
_inherit = 'account.bank.statement.import'
@api.model
def _check_ofx(self, data_file):
if not OfxParser:
return False
try:
ofx = OfxParser.parse(StringIO.StringIO(data_file))
except Exception as e:
_logger.debug(e)
return False
return ofx
@api.model
def _prepare_ofx_transaction_line(self, transaction):
# since odoo 9, the account module defines a constraint
# on account.bank.statement.line: 'amount' must be != 0
# But some banks have some transactions with amount=0
# for bank charges that are offered, which blocks the import
precision = self.env['decimal.precision'].precision_get('Account')
if float_is_zero(
float(transaction.amount), precision_digits=precision):
return False
# Since ofxparse doesn't provide account numbers,
# we cannot provide the key 'bank_account_id',
# nor the key 'account_number'
# If you read odoo10/addons/account_bank_statement_import/
# account_bank_statement_import.py, it's the only 2 keys
# we can provide to match a partner.
vals = {
'date': transaction.date,
'name': transaction.payee + (
transaction.memo and ': ' + transaction.memo or ''),
'ref': transaction.id,
'amount': float(transaction.amount),
'unique_import_id': transaction.id,
}
return vals
@api.model
def _parse_file(self, data_file):
ofx = self._check_ofx(data_file)
if not ofx:
return super(AccountBankStatementImport, self)._parse_file(
data_file)
transactions = []
total_amt = 0.00
start_date = end_date = False
try:
for transaction in ofx.account.statement.transactions:
vals = self._prepare_ofx_transaction_line(transaction)
if vals:
transactions.append(vals)
total_amt += vals['amount']
tdate = fields.Date.to_string(vals['date'])
if not start_date or tdate < start_date:
start_date = tdate
if not end_date or tdate > end_date:
end_date = tdate
except Exception, e:
raise UserError(_(
"The following problem occurred during import. "
"The file might not be valid.\n\n %s") % e.message)
vals_bank_statement = {
'name': _('Account %s %s > %s') % (
ofx.account.number, start_date, end_date),
'transactions': transactions,
'balance_start': ofx.account.statement.balance,
'balance_end_real':
float(ofx.account.statement.balance) + total_amt,
}
return ofx.account.statement.currency, ofx.account.number, [
vals_bank_statement]
Loading…
Cancel
Save