Browse Source

New backport from odoo/master

Fix bug #5
pull/129/head
Alexis de Lattre 10 years ago
committed by Pedro M. Baeza
parent
commit
483472fbc3
  1. 22
      account_bank_statement_import_qif/__openerp__.py
  2. 81
      account_bank_statement_import_qif/account_bank_statement_import_qif.py
  3. 24
      account_bank_statement_import_qif/account_bank_statement_import_qif_view.xml
  4. BIN
      account_bank_statement_import_qif/static/description/icon.png
  5. 4
      account_bank_statement_import_qif/tests/__init__.py
  6. 11
      account_bank_statement_import_qif/tests/test_import_bank_statement.py

22
account_bank_statement_import_qif/__openerp__.py

@ -1,32 +1,28 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# noqa: This is a backport from Odoo. OCA has no control over style here. # noqa: This is a backport from Odoo. OCA has no control over style here.
# flake8: noqa # flake8: noqa
{ {
'name': 'Import QIF Bank Statement', 'name': 'Import QIF Bank Statement',
'category' : 'Accounting & Finance',
'version': '1.0', 'version': '1.0',
'author': 'OpenERP SA', 'author': 'OpenERP SA',
'description': ''' 'description': '''
Module to import QIF bank statements. Module to import QIF bank statements.
====================================== ======================================
This module allows you to import the machine readable QIF Files in Odoo: they are parsed and stored in human readable format in
This module allows you to import the machine readable QIF Files in Odoo: they are parsed and stored in human readable format in
Accounting \ Bank and Cash \ Bank Statements. Accounting \ Bank and Cash \ Bank Statements.
Bank Statements may be generated containing a subset of the QIF information (only those transaction lines that are required for the
creation of the Financial Accounting records).
Backported from Odoo 9.0
When testing with the provided test file, make sure the demo data from the
base account_bank_statement_import module has been imported, or manually
create periods for the year 2013.
Important Note
---------------------------------------------
Because of the QIF format limitation, we cannot ensure the same transactions aren't imported several times or handle multicurrency.
Whenever possible, you should use a more appropriate file format like OFX.
''', ''',
'images' : [],
'images': [],
'depends': ['account_bank_statement_import'], 'depends': ['account_bank_statement_import'],
'demo': [], 'demo': [],
'data': [],
'data': ['account_bank_statement_import_qif_view.xml'],
'auto_install': False, 'auto_install': False,
'installable': True, 'installable': True,
} }
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

81
account_bank_statement_import_qif/account_bank_statement_import_qif.py

@ -3,29 +3,49 @@
# flake8: noqa # flake8: noqa
import dateutil.parser import dateutil.parser
import base64
from tempfile import TemporaryFile
import StringIO
from openerp.tools.translate import _ from openerp.tools.translate import _
from openerp.osv import osv
from openerp.addons.account_bank_statement_import import account_bank_statement_import as ibs
ibs.add_file_type(('qif', 'QIF'))
from openerp.osv import osv, fields
from openerp.exceptions import Warning
class account_bank_statement_import(osv.TransientModel): class account_bank_statement_import(osv.TransientModel):
_inherit = "account.bank.statement.import" _inherit = "account.bank.statement.import"
def process_qif(self, cr, uid, data_file, journal_id=False, context=None):
""" Import a file in the .QIF format"""
_columns = {
'journal_id': fields.many2one('account.journal', string='Journal', help='Accounting journal related to the bank statement you\'re importing. It has be be manually chosen for statement formats which doesn\'t allow automatic journal detection (QIF for example).'),
'hide_journal_field': fields.boolean('Hide the journal field in the view'),
}
def _get_hide_journal_field(self, cr, uid, context=None):
return context and 'journal_id' in context or False
_defaults = {
'hide_journal_field': _get_hide_journal_field,
}
def _get_journal(self, cr, uid, currency_id, bank_account_id, account_number, context=None):
""" As .QIF format does not allow us to detect the journal, we need to let the user choose it.
We set it in context before to call super so it's the same as calling the widget from a journal """
if context is None:
context = {}
if context.get('active_id'):
record = self.browse(cr, uid, context.get('active_id'), context=context)
if record.journal_id:
context['journal_id'] = record.journal_id.id
return super(account_bank_statement_import, self)._get_journal(cr, uid, currency_id, bank_account_id, account_number, context=context)
def _check_qif(self, cr, uid, data_file, context=None):
return data_file.strip().startswith('!Type:')
def _parse_file(self, cr, uid, data_file, context=None):
if not self._check_qif(cr, uid, data_file, context=context):
return super(account_bank_statement_import, self)._parse_file(cr, uid, data_file, context=context)
try: try:
fileobj = TemporaryFile('wb+')
fileobj.write(base64.b64decode(data_file))
fileobj.seek(0)
file_data = "" file_data = ""
for line in fileobj.readlines():
for line in StringIO.StringIO(data_file).readlines():
file_data += line file_data += line
fileobj.close()
if '\r' in file_data: if '\r' in file_data:
data_list = file_data.split('\r') data_list = file_data.split('\r')
else: else:
@ -33,8 +53,8 @@ class account_bank_statement_import(osv.TransientModel):
header = data_list[0].strip() header = data_list[0].strip()
header = header.split(":")[1] header = header.split(":")[1]
except: except:
raise osv.except_osv(_('Import Error!'), _('Please check QIF file format is proper or not.'))
line_ids = []
raise Warning(_('Could not decipher the QIF file.'))
transactions = []
vals_line = {} vals_line = {}
total = 0 total = 0
if header == "Bank": if header == "Bank":
@ -45,33 +65,34 @@ class account_bank_statement_import(osv.TransientModel):
continue continue
if line[0] == 'D': # date of transaction if line[0] == 'D': # date of transaction
vals_line['date'] = dateutil.parser.parse(line[1:], fuzzy=True).date() vals_line['date'] = dateutil.parser.parse(line[1:], fuzzy=True).date()
if vals_line.get('date') and not vals_bank_statement.get('period_id'):
period_ids = self.pool.get('account.period').find(cr, uid, vals_line['date'], context=context)
vals_bank_statement.update({'period_id': period_ids and period_ids[0] or False})
elif line[0] == 'T': # Total amount elif line[0] == 'T': # Total amount
total += float(line[1:].replace(',', '')) total += float(line[1:].replace(',', ''))
vals_line['amount'] = float(line[1:].replace(',', '')) vals_line['amount'] = float(line[1:].replace(',', ''))
elif line[0] == 'N': # Check number elif line[0] == 'N': # Check number
vals_line['ref'] = line[1:] vals_line['ref'] = line[1:]
elif line[0] == 'P': # Payee elif line[0] == 'P': # Payee
bank_account_id, partner_id = self._detect_partner(cr, uid, line[1:], identifying_field='owner_name', context=context)
vals_line['partner_id'] = partner_id
vals_line['bank_account_id'] = bank_account_id
vals_line['name'] = 'name' in vals_line and line[1:] + ': ' + vals_line['name'] or line[1:] vals_line['name'] = 'name' in vals_line and line[1:] + ': ' + vals_line['name'] or line[1:]
# Since QIF 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)
ids = self.pool.get('res.partner.bank').search(cr, uid, [('owner_name', '=', line[1:])], context=context)
if ids:
vals_line['bank_account_id'] = bank_account_id = ids[0]
vals_line['partner_id'] = self.pool.get('res.partner.bank').browse(cr, uid, bank_account_id, context=context).partner_id.id
elif line[0] == 'M': # Memo elif line[0] == 'M': # Memo
vals_line['name'] = 'name' in vals_line and vals_line['name'] + ': ' + line[1:] or line[1:] vals_line['name'] = 'name' in vals_line and vals_line['name'] + ': ' + line[1:] or line[1:]
elif line[0] == '^': # end of item elif line[0] == '^': # end of item
line_ids.append((0, 0, vals_line))
transactions.append(vals_line)
vals_line = {} vals_line = {}
elif line[0] == '\n': elif line[0] == '\n':
line_ids = []
transactions = []
else: else:
pass pass
else: else:
raise osv.except_osv(_('Error!'), _('Cannot support this Format !Type:%s.') % (header,))
vals_bank_statement.update({'balance_end_real': total,
'line_ids': line_ids,
'journal_id': journal_id})
return [vals_bank_statement]
raise Warning(_('This file is either not a bank statement or is not correctly formed.'))
vals_bank_statement.update({
'balance_end_real': total,
'transactions': transactions
})
return None, None, [vals_bank_statement]
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

24
account_bank_statement_import_qif/account_bank_statement_import_qif_view.xml

@ -0,0 +1,24 @@
<?xml version="1.0" ?>
<openerp>
<data>
<record id="account_bank_statement_import_view_inherited" model="ir.ui.view">
<field name="name">Import Bank Statements Inherited</field>
<field name="model">account.bank.statement.import</field>
<field name="priority" eval="20"/>
<field name="inherit_id" ref="account_bank_statement_import.account_bank_statement_import_view" />
<field name="arch" type="xml">
<xpath expr="//field[@name='data_file']" position="after">
<field name="hide_journal_field" invisible="1"/>
<label for="journal_id"/>
<field name="journal_id"
domain="[('type', '=', 'bank')]"
attrs="{'invisible': [('hide_journal_field', '=', True)]}"
context="{'default_type':'bank'}"/>
</xpath>
</field>
</record>
</data>
</openerp>

BIN
account_bank_statement_import_qif/static/description/icon.png

After

Width: 128  |  Height: 128  |  Size: 6.0 KiB

4
account_bank_statement_import_qif/tests/__init__.py

@ -2,7 +2,3 @@
# noqa: This is a backport from Odoo. OCA has no control over style here. # noqa: This is a backport from Odoo. OCA has no control over style here.
# flake8: noqa # flake8: noqa
from . import test_import_bank_statement from . import test_import_bank_statement
checks = [
test_import_bank_statement
]

11
account_bank_statement_import_qif/tests/test_import_bank_statement.py

@ -20,10 +20,13 @@ class TestQifFile(TransactionCase):
qif_file_path = get_module_resource('account_bank_statement_import_qif', 'test_qif_file', 'test_qif.qif') qif_file_path = get_module_resource('account_bank_statement_import_qif', 'test_qif_file', 'test_qif.qif')
qif_file = open(qif_file_path, 'rb').read().encode('base64') qif_file = open(qif_file_path, 'rb').read().encode('base64')
bank_statement_id = self.statement_import_model.create(cr, uid, dict( bank_statement_id = self.statement_import_model.create(cr, uid, dict(
file_type='qif',
data_file=qif_file,
))
self.statement_import_model.parse_file(cr, uid, [bank_statement_id])
data_file=qif_file,
))
context = {
'journal_id': self.registry('ir.model.data').get_object_reference(cr, uid, 'account', 'bank_journal')[1],
'allow_auto_create_journal': True,
}
self.statement_import_model.import_file(cr, uid, [bank_statement_id], context=context)
line_id = self.bank_statement_line_model.search(cr, uid, [('name', '=', 'YOUR LOCAL SUPERMARKET')])[0] line_id = self.bank_statement_line_model.search(cr, uid, [('name', '=', 'YOUR LOCAL SUPERMARKET')])[0]
statement_id = self.bank_statement_line_model.browse(cr, uid, line_id).statement_id.id statement_id = self.bank_statement_line_model.browse(cr, uid, line_id).statement_id.id
bank_st_record = self.bank_statement_model.browse(cr, uid, statement_id) bank_st_record = self.bank_statement_model.browse(cr, uid, statement_id)

Loading…
Cancel
Save