Browse Source

Merge pull request #80 from akretion/10-mig-ofx

[10.0] port OFX module
pull/89/head
Pedro M. Baeza 8 years ago
committed by GitHub
parent
commit
271c52cf16
  1. 2
      .travis.yml
  2. 28
      account_bank_statement_import_ofx/README.rst
  3. 5
      account_bank_statement_import_ofx/__init__.py
  4. 21
      account_bank_statement_import_ofx/__manifest__.py
  5. 78
      account_bank_statement_import_ofx/account_bank_statement_import_ofx.py
  6. 27
      account_bank_statement_import_ofx/demo/demo_data.xml
  7. 4
      account_bank_statement_import_ofx/tests/__init__.py
  8. 41
      account_bank_statement_import_ofx/tests/test_import_bank_statement.py
  9. 0
      account_bank_statement_import_ofx/tests/test_ofx_file/test_ofx.ofx
  10. 12
      account_bank_statement_import_ofx/views/view_account_bank_statement_import.xml
  11. 2
      account_bank_statement_import_ofx/wizard/__init__.py
  12. 94
      account_bank_statement_import_ofx/wizard/account_bank_statement_import.py

2
.travis.yml

@ -30,7 +30,7 @@ virtualenv:
install:
- git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools
- export PATH=${HOME}/maintainer-quality-tools/travis:${PATH}
- pip install ofxparse
- pip install git+https://github.com/jseutter/ofxparse.git
- travis_install_nightly
# example: dependency
# - git clone https://github.com/OCA/webkit-tools -b ${VERSION} $HOME/webkit-tools

28
account_bank_statement_import_ofx/README.rst

@ -1,20 +1,15 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:alt: License: AGPL-3
=========================
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
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
============
@ -22,6 +17,13 @@ The module requires one additional python lib:
* `ofxparse <http://pypi.python.org/pypi/ofxparse>`_
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
======================
@ -30,11 +32,10 @@ Known issues / Roadmap
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
=======
@ -46,6 +47,7 @@ Contributors
* Alexis de Lattre <alexis@via.ecp.fr>
* Laurent Mignon <laurent.mignon@acsone.eu>
* Ronald Portier <rportier@therp.nl>
* Sylvain LE GAL <https://twitter.com/legalsylvain>
Maintainer
----------
@ -60,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
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.

5
account_bank_statement_import_ofx/__init__.py

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

21
account_bank_statement_import_ofx/__manifest__.py

@ -1,20 +1,23 @@
# -*- encoding: utf-8 -*-
# -*- coding: utf-8 -*-
{
'name': 'Import OFX Bank Statement',
'category': 'Banking addons',
'version': '8.0.1.0.0',
'author': 'OpenERP SA,'
'version': '10.0.1.0.0',
'license': 'AGPL-3',
'author': 'Odoo SA,'
'Akretion,'
'La Louve,'
'GRAP,'
'Odoo Community Association (OCA)',
'website': 'https://github.com/OCA/bank-statement-import',
'website': 'https://odoo-community.org/',
'depends': [
'account_bank_statement_import'
'account_bank_statement_import',
],
'demo': [
'demo/demo_data.xml',
'data': [
'views/view_account_bank_statement_import.xml',
],
'external_dependencies': {
'python': ['ofxparse'],
},
'auto_install': False,
'installable': False,
'installable': True,
}

78
account_bank_statement_import_ofx/account_bank_statement_import_ofx.py

@ -1,78 +0,0 @@
# -*- coding: utf-8 -*-
import logging
import StringIO
from openerp import api, models
from openerp.tools.translate import _
from openerp.exceptions import Warning
_logger = logging.getLogger(__name__)
try:
from ofxparse import OfxParser as ofxparser
except ImportError:
_logger.warn("ofxparse not found, OFX parsing disabled.")
ofxparser = None
class AccountBankStatementImport(models.TransientModel):
_inherit = 'account.bank.statement.import'
@api.model
def _check_ofx(self, data_file):
if ofxparser is None:
return False
try:
ofx = ofxparser.parse(StringIO.StringIO(data_file))
except:
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(
[('owner_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 Warning(_("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]

27
account_bank_statement_import_ofx/demo/demo_data.xml

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="ofx_bank_journal" model="account.journal">
<field name="name">Bank Journal - (test ofx)</field>
<field name="code">TBNKOFX</field>
<field name="type">bank</field>
<field name="sequence_id" ref="account.sequence_bank_journal"/>
<field name="default_debit_account_id" ref="account.usd_bnk"/>
<field name="default_credit_account_id" ref="account.usd_bnk"/>
<field name="user_id" ref="base.user_root"/>
<field name="currency" ref="base.USD"/>
</record>
<record id="ofx_company_bank" model="res.partner.bank">
<field name="owner_name">Your Company</field>
<field name="acc_number">123456</field>
<field name="partner_id" ref="base.partner_root"></field>
<field name="company_id" ref="base.main_company"></field>
<field name="journal_id" ref="ofx_bank_journal"></field>
<field name="state">bank</field>
<field name="bank" ref="base.res_bank_1"/>
</record>
</data>
</openerp>

4
account_bank_statement_import_ofx/tests/__init__.py

@ -1,4 +1,2 @@
# -*- encoding: utf-8 -*-
# noqa: This is a backport from Odoo. OCA has no control over style here.
# flake8: noqa
# -*- coding: utf-8 -*-
from . import test_import_bank_statement

41
account_bank_statement_import_ofx/tests/test_import_bank_statement.py

@ -1,6 +1,6 @@
# -*- 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):
@ -10,26 +10,39 @@ class TestOfxFile(TransactionCase):
def setUp(self):
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):
ofx_file_path = get_module_resource(
'account_bank_statement_import_ofx',
'test_ofx_file', 'test_ofx.ofx')
'tests/test_ofx_file/', 'test_ofx.ofx')
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))
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_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.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')

0
account_bank_statement_import_ofx/test_ofx_file/test_ofx.ofx → account_bank_statement_import_ofx/tests/test_ofx_file/test_ofx.ofx

12
account_bank_statement_import_ofx/views/view_account_bank_statement_import.xml

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_account_bank_statement_import_form" model="ir.ui.view">
<field name="model">account.bank.statement.import</field>
<field name="inherit_id" ref="account_bank_statement_import.account_bank_statement_import_view"/>
<field name="arch" type="xml">
<xpath expr="//ul[@id='statement_format']" position="inside">
<li>Open Financial Exchange (.OFX Money)</li>
</xpath>
</field>
</record>
</odoo>

2
account_bank_statement_import_ofx/wizard/__init__.py

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import account_bank_statement_import

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