Merge pull request #15 from NL66278/8.0_import_parsers_new_api_therp

8.0 import parsers new api therp
This commit is contained in:
Stéphane Bidoul (ACSONE)
2015-07-24 09:34:05 +02:00
42 changed files with 2680 additions and 177 deletions

View File

@@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*-
{
'name': 'Account Bank Statement Import',
'category': 'Accounting & Finance',
'category': 'Banking addons',
'version': '1.0',
'author': 'OpenERP SA,'
'Odoo Community Association (OCA)',

View File

@@ -1,15 +1,17 @@
# -*- coding: utf-8 -*-
"""Framework for importing bank statement files."""
import logging
import base64
from openerp import api, models, fields
from openerp.tools.translate import _
from openerp.exceptions import Warning
import logging
_logger = logging.getLogger(__name__)
class AccountBankStatementLine(models.Model):
"""Extend model account.bank.statement.line."""
_inherit = "account.bank.statement.line"
# Ensure transactions can be imported only once (if the import format
@@ -24,6 +26,7 @@ class AccountBankStatementLine(models.Model):
class AccountBankStatementImport(models.TransientModel):
"""Extend model account.bank.statement."""
_name = 'account.bank.statement.import'
_description = 'Import Bank Statement'
@@ -50,7 +53,7 @@ class AccountBankStatementImport(models.TransientModel):
@api.multi
def import_file(self):
""" Process the file chosen in the wizard, create bank statement(s) and
go to reconciliation. """
go to reconciliation."""
self.ensure_one()
data_file = base64.b64decode(self.data_file)
statement_ids, notifications = self.with_context(
@@ -70,15 +73,43 @@ class AccountBankStatementImport(models.TransientModel):
@api.model
def _import_file(self, data_file):
""" Create bank statement(s) from file
"""
""" Create bank statement(s) from file."""
# The appropriate implementation module returns the required data
currency_code, account_number, stmts_vals = self._parse_file(data_file)
# Check raw data
self._check_parsed_data(stmts_vals)
statement_ids = []
notifications = []
parse_result = self._parse_file(data_file)
# Check for old version result, with separate currency and account
if isinstance(parse_result, tuple) and len(parse_result) == 3:
(currency_code, account_number, statements) = parse_result
for stmt_vals in statements:
stmt_vals['currency_code'] = currency_code
stmt_vals['account_number'] = account_number
else:
statements = parse_result
# Check raw data:
self._check_parsed_data(statements)
# Import all statements:
for stmt_vals in statements:
(statement_id, new_notifications) = (
self._import_statement(stmt_vals))
if statement_id:
statement_ids.append(statement_id)
notifications.append(new_notifications)
if len(statement_ids) == 0:
raise Warning(_('You have already imported that file.'))
return statement_ids, notifications
@api.model
def _import_statement(self, stmt_vals):
"""Import a single bank-statement.
Return ids of created statements and notifications.
"""
currency_code = stmt_vals.pop('currency_code')
account_number = stmt_vals.pop('account_number')
# Try to find the bank account and currency in odoo
currency_id, bank_account_id = self._find_additional_data(
currency_code, account_number)
currency_id = self._find_currency_id(currency_code)
bank_account_id = self._find_bank_account_id(account_number)
# Create the bank account if not already existing
if not bank_account_id and account_number:
journal_id = self.env.context.get('journal_id')
@@ -90,87 +121,90 @@ class AccountBankStatementImport(models.TransientModel):
account_number, company_id=company_id,
currency_id=currency_id).id
# Find or create the bank journal
journal_id = self._get_journal(
currency_id, bank_account_id, account_number)
journal_id = self._get_journal(currency_id, bank_account_id)
# By now journal and account_number must be known
if not journal_id:
raise Warning(_('Can not determine journal for import.'))
# Prepare statement data to be used for bank statements creation
stmts_vals = self._complete_stmts_vals(
stmts_vals, journal_id, account_number)
# Create the bank statements
return self._create_bank_statements(stmts_vals)
stmt_vals = self._complete_statement(
stmt_vals, journal_id, account_number)
# Create the bank stmt_vals
return self._create_bank_statement(stmt_vals)
@api.model
def _parse_file(self, data_file):
""" Each module adding a file support must extends this method. It
rocesses the file if it can, returns super otherwise, resulting in a
processes the file if it can, returns super otherwise, resulting in a
chain of responsability.
This method parses the given file and returns the data required by
the bank statement import process, as specified below.
rtype: triplet (if a value can't be retrieved, use None)
- currency code: string (e.g: 'EUR')
The ISO 4217 currency code, case insensitive
- account number: string (e.g: 'BE1234567890')
The number of the bank account which the statement belongs
to
- bank statements data: list of dict containing (optional
items marked by o) :
- 'name': string (e.g: '000000123')
- 'date': date (e.g: 2013-06-26)
-o 'balance_start': float (e.g: 8368.56)
-o 'balance_end_real': float (e.g: 8888.88)
- 'transactions': list of dict containing :
- 'name': string
(e.g: 'KBC-INVESTERINGSKREDIET 787-5562831-01')
- 'date': date
- 'amount': float
- 'unique_import_id': string
-o 'account_number': string
Will be used to find/create the res.partner.bank
in odoo
-o 'note': string
-o 'partner_name': string
-o 'ref': string
- bank statements data: list of dict containing (optional
items marked by o) :
-o currency code: string (e.g: 'EUR')
The ISO 4217 currency code, case insensitive
-o account number: string (e.g: 'BE1234567890')
The number of the bank account which the statement
belongs to
- 'name': string (e.g: '000000123')
- 'date': date (e.g: 2013-06-26)
-o 'balance_start': float (e.g: 8368.56)
-o 'balance_end_real': float (e.g: 8888.88)
- 'transactions': list of dict containing :
- 'name': string
(e.g: 'KBC-INVESTERINGSKREDIET 787-5562831-01')
- 'date': date
- 'amount': float
- 'unique_import_id': string
-o 'account_number': string
Will be used to find/create the res.partner.bank
in odoo
-o 'note': string
-o 'partner_name': string
-o 'ref': string
"""
raise Warning(_('Could not make sense of the given file.\nDid you '
'install the module to support this type of file ?'))
raise Warning(_(
'Could not make sense of the given file.\n'
'Did you install the module to support this type of file?'
))
@api.model
def _check_parsed_data(self, stmts_vals):
def _check_parsed_data(self, statements):
""" Basic and structural verifications """
if len(stmts_vals) == 0:
if len(statements) == 0:
raise Warning(_('This file doesn\'t contain any statement.'))
no_st_line = True
for vals in stmts_vals:
if vals['transactions'] and len(vals['transactions']) > 0:
no_st_line = False
break
if no_st_line:
raise Warning(_('This file doesn\'t contain any transaction.'))
for stmt_vals in statements:
if 'transactions' in stmt_vals and stmt_vals['transactions']:
return
# If we get here, no transaction was found:
raise Warning(_('This file doesn\'t contain any transaction.'))
@api.model
def _find_additional_data(self, currency_code, account_number):
""" Get the res.currency ID and the res.partner.bank ID """
# if no currency_code is provided, we'll use the company currency
currency_id = False
def _find_currency_id(self, currency_code):
""" Get res.currency ID."""
if currency_code:
currency_ids = self.env['res.currency'].search(
[('name', '=ilike', currency_code)])
company_currency_id = self.env.user.company_id.currency_id
if currency_ids:
if currency_ids[0] != company_currency_id:
currency_id = currency_ids[0].id
return currency_ids[0].id
else:
raise Warning(_(
'Statement has invalid currency code %s') % currency_code)
# if no currency_code is provided, we'll use the company currency
return self.env.user.company_id.currency_id.id
@api.model
def _find_bank_account_id(self, account_number):
""" Get res.partner.bank ID """
bank_account_id = None
if account_number and len(account_number) > 4:
bank_account_ids = self.env['res.partner.bank'].search(
[('acc_number', '=', account_number)], limit=1)
if bank_account_ids:
bank_account_id = bank_account_ids[0].id
return currency_id, bank_account_id
return bank_account_id
@api.model
def _get_journal(self, currency_id, bank_account_id, account_number):
def _get_journal(self, currency_id, bank_account_id):
""" Find the journal """
bank_model = self.env['res.partner.bank']
# Find the journal from context, wizard or bank account
@@ -189,23 +223,45 @@ class AccountBankStatementImport(models.TransientModel):
if bank_account.journal_id.id:
journal_id = bank_account.journal_id.id
# If importing into an existing journal, its currency must be the same
# as the bank statement
if journal_id:
journal_currency_id = self.env['account.journal'].browse(
journal_id).currency.id
if currency_id and currency_id != journal_currency_id:
raise Warning(_('The currency of the bank statement is not '
'the same as the currency of the journal !'))
# If we couldn't find/create a journal, everything is lost
if not journal_id:
raise Warning(_('Cannot find in which journal import this '
'statement. Please manually select a journal.'))
# as the bank statement. When journal has no currency, currency must
# be equal to company currency.
if journal_id and currency_id:
journal_obj = self.env['account.journal'].browse(journal_id)
if journal_obj.currency:
journal_currency_id = journal_obj.currency.id
if currency_id != journal_currency_id:
# ALso log message with id's for technical analysis:
_logger.warn(
_('Statement currency id is %d,'
' but journal currency id = %d.'),
currency_id,
journal_currency_id
)
raise Warning(_(
'The currency of the bank statement is not '
'the same as the currency of the journal !'
))
else:
company_currency_id = self.env.user.company_id.currency_id.id
if currency_id != company_currency_id:
# ALso log message with id's for technical analysis:
_logger.warn(
_('Statement currency id is %d,'
' but company currency id = %d.'),
currency_id,
company_currency_id
)
raise Warning(_(
'The currency of the bank statement is not '
'the same as the company currency !'
))
return journal_id
@api.model
@api.returns('res.partner.bank')
def _create_bank_account(self, account_number, company_id=False,
currency_id=False):
def _create_bank_account(
self, account_number, company_id=False, currency_id=False):
"""Automagically create bank account, when not yet existing."""
try:
bank_type = self.env.ref('base.bank_normal')
bank_code = bank_type.code
@@ -233,95 +289,85 @@ class AccountBankStatementImport(models.TransientModel):
default_currency=currency_id).create(vals_acc)
@api.model
def _complete_stmts_vals(self, stmts_vals, journal_id, account_number):
for st_vals in stmts_vals:
st_vals['journal_id'] = journal_id
for line_vals in st_vals['transactions']:
unique_import_id = line_vals.get('unique_import_id', False)
if unique_import_id:
line_vals['unique_import_id'] = (
account_number and account_number + '-' or '') + \
unique_import_id
if not line_vals.get('bank_account_id'):
# Find the partner and his bank account or create the bank
# account. The partner selected during the reconciliation
# process will be linked to the bank when the statement is
# closed.
partner_id = False
bank_account_id = False
identifying_string = line_vals.get('account_number')
if identifying_string:
bank_model = self.env['res.partner.bank']
banks = bank_model.search(
[('acc_number', '=', identifying_string)], limit=1)
if banks:
bank_account_id = banks[0].id
partner_id = banks[0].partner_id.id
else:
bank_account_id = self._create_bank_account(
identifying_string).id
line_vals['partner_id'] = partner_id
line_vals['bank_account_id'] = bank_account_id
return stmts_vals
def _complete_statement(self, stmt_vals, journal_id, account_number):
"""Complete statement from information passed."""
stmt_vals['journal_id'] = journal_id
for line_vals in stmt_vals['transactions']:
unique_import_id = line_vals.get('unique_import_id', False)
if unique_import_id:
line_vals['unique_import_id'] = (
(account_number and account_number + '-' or '') +
unique_import_id
)
if not line_vals.get('bank_account_id'):
# Find the partner and his bank account or create the bank
# account. The partner selected during the reconciliation
# process will be linked to the bank when the statement is
# closed.
partner_id = False
bank_account_id = False
account_number = line_vals.get('account_number')
if account_number:
bank_model = self.env['res.partner.bank']
banks = bank_model.search(
[('acc_number', '=', account_number)], limit=1)
if banks:
bank_account_id = banks[0].id
partner_id = banks[0].partner_id.id
else:
bank_obj = self._create_bank_account(account_number)
bank_account_id = bank_obj and bank_obj.id or False
line_vals['partner_id'] = partner_id
line_vals['bank_account_id'] = bank_account_id
return stmt_vals
@api.model
def _create_bank_statements(self, stmts_vals):
""" Create new bank statements from imported values, filtering out
already imported transactions, and returns data used by the
def _create_bank_statement(self, stmt_vals):
""" Create bank statement from imported values, filtering out
already imported transactions, and return data used by the
reconciliation widget
"""
bs_model = self.env['account.bank.statement']
bsl_model = self.env['account.bank.statement.line']
# Filter out already imported transactions and create statements
statement_ids = []
ignored_statement_lines_import_ids = []
for st_vals in stmts_vals:
filtered_st_lines = []
for line_vals in st_vals['transactions']:
if 'unique_import_id' not in line_vals \
or not line_vals['unique_import_id'] \
or not bool(bsl_model.sudo().search(
[('unique_import_id', '=',
line_vals['unique_import_id'])],
limit=1)):
filtered_st_lines.append(line_vals)
else:
ignored_statement_lines_import_ids.append(
line_vals['unique_import_id'])
if len(filtered_st_lines) > 0:
# Remove values that won't be used to create records
st_vals.pop('transactions', None)
for line_vals in filtered_st_lines:
line_vals.pop('account_number', None)
# Create the satement
st_vals['line_ids'] = [[0, False, line] for line in
filtered_st_lines]
statement_ids.append(bs_model.create(st_vals).id)
if len(statement_ids) == 0:
raise Warning(_('You have already imported that file.'))
# Filter out already imported transactions and create statement
ignored_line_ids = []
filtered_st_lines = []
for line_vals in stmt_vals['transactions']:
unique_id = (
'unique_import_id' in line_vals and
line_vals['unique_import_id']
)
if not unique_id or not bool(bsl_model.sudo().search(
[('unique_import_id', '=', unique_id)], limit=1)):
filtered_st_lines.append(line_vals)
else:
ignored_line_ids.append(unique_id)
statement_id = False
if len(filtered_st_lines) > 0:
# Remove values that won't be used to create records
stmt_vals.pop('transactions', None)
for line_vals in filtered_st_lines:
line_vals.pop('account_number', None)
# Create the statement
stmt_vals['line_ids'] = [
[0, False, line] for line in filtered_st_lines]
statement_id = bs_model.create(stmt_vals).id
# Prepare import feedback
notifications = []
num_ignored = len(ignored_statement_lines_import_ids)
num_ignored = len(ignored_line_ids)
if num_ignored > 0:
notifications += [{
'type': 'warning',
'message': _("%d transactions had already been imported and "
"were ignored.") % num_ignored
if num_ignored > 1
else _("1 transaction had already been imported and "
"was ignored."),
'message':
_("%d transactions had already been imported and "
"were ignored.") % num_ignored
if num_ignored > 1
else _("1 transaction had already been imported and "
"was ignored."),
'details': {
'name': _('Already imported items'),
'model': 'account.bank.statement.line',
'ids': bsl_model.search(
[('unique_import_id', 'in',
ignored_statement_lines_import_ids)]).ids
}
[('unique_import_id', 'in', ignored_line_ids)]).ids}
}]
return statement_ids, notifications
return statement_id, notifications

View File

@@ -6,8 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-06-08 12:02+0000\n"
"PO-Revision-Date: 2015-06-08 12:02+0000\n"
"POT-Creation-Date: 2015-06-11 12:15+0000\n"
"PO-Revision-Date: 2015-06-11 12:15+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
@@ -16,13 +16,13 @@ msgstr ""
"Plural-Forms: \n"
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:313
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:347
#, python-format
msgid "%d transactions had already been imported and were ignored."
msgstr ""
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:316
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:350
#, python-format
msgid "1 transaction had already been imported and was ignored."
msgstr ""
@@ -53,7 +53,7 @@ msgid "Account Number must be unique"
msgstr ""
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:319
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:353
#, python-format
msgid "Already imported items"
msgstr ""
@@ -73,19 +73,19 @@ msgstr ""
msgid "Bank Statement Line"
msgstr ""
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:116
#, python-format
msgid "Can not determine journal for import."
msgstr ""
#. module: account_bank_statement_import
#: view:account.bank.statement.import:account_bank_statement_import.account_bank_statement_import_view
msgid "Cancel"
msgstr ""
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:200
#, python-format
msgid "Cannot find in which journal import this statement. Please manually select a journal."
msgstr ""
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:129
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:154
#, python-format
msgid "Could not make sense of the given file.\n"
"Did you install the module to support this type of file ?"
@@ -153,31 +153,55 @@ msgid "Sanitized Account Number"
msgstr ""
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:181
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:237
#, python-format
msgid "Statement currency id is %d, but company currency id = %d."
msgstr ""
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:224
#, python-format
msgid "Statement currency id is %d, but journal currency id = %d."
msgstr ""
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:179
#, python-format
msgid "Statement has invalid currency code %s"
msgstr ""
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:207
#, python-format
msgid "The account of this statement is linked to another journal."
msgstr ""
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:195
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:242
#, python-format
msgid "The currency of the bank statement is not the same as the company currency !"
msgstr ""
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:229
#, python-format
msgid "The currency of the bank statement is not the same as the currency of the journal !"
msgstr ""
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:136
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:163
#, python-format
msgid "This file doesn't contain any statement."
msgstr ""
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:144
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:168
#, python-format
msgid "This file doesn't contain any transaction."
msgstr ""
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:305
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:88
#, python-format
msgid "You have already imported that file."
msgstr ""

View File

@@ -0,0 +1,236 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_bank_statement_import
# Therp BV <therp.nl>, 2015.
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-06-11 12:15+0000\n"
"PO-Revision-Date: 2015-06-11 14:37+0200\n"
"Last-Translator: Therp BV <therp.nl>\n"
"Language: nl\n"
"Language-Team: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
"X-Generator: Gtranslator 2.91.6\n"
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:347
#, python-format
msgid "%d transactions had already been imported and were ignored."
msgstr "%d Transacties zijn overgeslagen omdat ze al reeds waren geïmporteerd."
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:350
#, python-format
msgid "1 transaction had already been imported and was ignored."
msgstr "1. Transactie was al geïmporteerd en is overgeslagen."
#. module: account_bank_statement_import
#: view:account.bank.statement.import:account_bank_statement_import.account_bank_statement_import_view
msgid "1. Download your bank statements from your bank website."
msgstr "1. Download het bankafschrift bestand van de website van uw bank."
#. module: account_bank_statement_import
#: view:account.bank.statement.import:account_bank_statement_import.account_bank_statement_import_view
msgid ""
"2. Make sure you have installed the right module to support the file format."
msgstr ""
"2. Zorg ervoor dat de modules die het formaat van uw bestand ondersteunen "
"zijn geïnstalleerd."
#. module: account_bank_statement_import
#: view:account.bank.statement.import:account_bank_statement_import.account_bank_statement_import_view
msgid "3. Select the file and click 'Import'."
msgstr "3. Selecteer het bestand en klik op \"Importeren\"."
#. module: account_bank_statement_import
#: sql_constraint:account.bank.statement.line:0
msgid "A bank account transactions can be imported only once !"
msgstr "De transacties kunnen slechts eenmalig worden geïmporteerd."
#. module: account_bank_statement_import
#: sql_constraint:res.partner.bank:0
msgid "Account Number must be unique"
msgstr "Rekeningnummer moet uniek zijn"
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:353
#, python-format
msgid "Already imported items"
msgstr "Al eerder geïmporteerde regels"
#. module: account_bank_statement_import
#: model:ir.model,name:account_bank_statement_import.model_res_partner_bank
msgid "Bank Accounts"
msgstr "Bankrekeningen"
#. module: account_bank_statement_import
#: field:account.bank.statement.import,data_file:0
msgid "Bank Statement File"
msgstr "Bankafschriften bestand"
#. module: account_bank_statement_import
#: model:ir.model,name:account_bank_statement_import.model_account_bank_statement_line
msgid "Bank Statement Line"
msgstr "Bankafschrift regel"
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:116
#, python-format
msgid "Can not determine journal for import."
msgstr "Kan niet bepalen welk dagboek gebruikt moet worden."
#. module: account_bank_statement_import
#: view:account.bank.statement.import:account_bank_statement_import.account_bank_statement_import_view
msgid "Cancel"
msgstr "Annuleren"
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:154
#, python-format
msgid ""
"Could not make sense of the given file.\n"
"Did you install the module to support this type of file ?"
msgstr ""
"Kon het bestand niet interpreteren.\n"
"Heeft u de juiste modules voor dit type bestand geïnstalleerd?"
#. module: account_bank_statement_import
#: field:account.bank.statement.import,create_uid:0
msgid "Created by"
msgstr "Aangemaakt door"
#. module: account_bank_statement_import
#: field:account.bank.statement.import,create_date:0
msgid "Created on"
msgstr "Aangemaakt op"
#. module: account_bank_statement_import
#: help:account.bank.statement.import,data_file:0
msgid ""
"Get you bank statements in electronic format from your bank and select them "
"here."
msgstr ""
"Verkrijg de bankafschriften van uw bank in elektronische vorm en selecteer "
"ze hier."
#. module: account_bank_statement_import
#: view:account.bank.statement.import:account_bank_statement_import.account_bank_statement_import_view
msgid "How to import your bank statement :"
msgstr "Hoe uw bankafschrift te importeren:"
#. module: account_bank_statement_import
#: field:account.bank.statement.import,id:0
msgid "ID"
msgstr "ID"
#. module: account_bank_statement_import
#: model:ir.actions.act_window,name:account_bank_statement_import.action_account_bank_statement_import
#: model:ir.ui.menu,name:account_bank_statement_import.menu_account_bank_statement_import
msgid "Import"
msgstr "Import"
#. module: account_bank_statement_import
#: model:ir.model,name:account_bank_statement_import.model_account_bank_statement_import
msgid "Import Bank Statement"
msgstr "Geïmporteerd bankafschrift"
#. module: account_bank_statement_import
#: view:account.bank.statement.import:account_bank_statement_import.account_bank_statement_import_view
msgid "Import Bank Statements"
msgstr "Importeer bankafschriften"
#. module: account_bank_statement_import
#: field:account.bank.statement.line,unique_import_id:0
msgid "Import ID"
msgstr "Import ID"
#. module: account_bank_statement_import
#: field:account.bank.statement.import,write_uid:0
msgid "Last Updated by"
msgstr "Laatst bijgewerkt door"
#. module: account_bank_statement_import
#: field:account.bank.statement.import,write_date:0
msgid "Last Updated on"
msgstr "Laatst bijgewerkt op"
#. module: account_bank_statement_import
#: field:res.partner.bank,sanitized_acc_number:0
msgid "Sanitized Account Number"
msgstr "Gestandaardiseerd rekeningnummer"
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:237
#, python-format
msgid "Statement currency id is %d, but company currency id = %d."
msgstr "Valuta id van afschrift = %d, maar valuta id van bedrijf = %d."
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:224
#, python-format
msgid "Statement currency id is %d, but journal currency id = %d."
msgstr "Valuta id van afschrift = %d, maar valuta id van dagboek = %d."
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:179
#, python-format
msgid "Statement has invalid currency code %s"
msgstr "Bankafschrift heeft ongeldige valutacode %s"
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:207
#, python-format
msgid "The account of this statement is linked to another journal."
msgstr ""
"Het rekeningnummer van dit afschrift is gekoppeld aan een ander dagboek."
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:242
#, python-format
msgid ""
"The currency of the bank statement is not the same as the company currency !"
msgstr ""
"De valuta van het afschrift is niet gelijk aan de valuta van het bedrijf!"
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:229
#, python-format
msgid ""
"The currency of the bank statement is not the same as the currency of the "
"journal !"
msgstr ""
"De valuta van het afschrift is niet hetzelfde als de valuta van het dagboek!"
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:163
#, python-format
msgid "This file doesn't contain any statement."
msgstr "Dit bestand bevat geen enkel afschrift."
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:168
#, python-format
msgid "This file doesn't contain any transaction."
msgstr "Dit bestand bevat geen enkele transactie."
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:88
#, python-format
msgid "You have already imported that file."
msgstr "U heeft dit bestand al geïmporteerd."
#. module: account_bank_statement_import
#: view:account.bank.statement.import:account_bank_statement_import.account_bank_statement_import_view
msgid "_Import"
msgstr ""
#. module: account_bank_statement_import
#: view:account.bank.statement.import:account_bank_statement_import.account_bank_statement_import_view
msgid "or"
msgstr ""

View File

@@ -0,0 +1,236 @@
# -*- encoding: utf-8 -*-
"""Classes and definitions used in parsing bank statements."""
##############################################################################
#
# Copyright (C) 2015 Therp BV <http://therp.nl>.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
class BankTransaction(dict):
"""Single transaction that is part of a bank statement."""
@property
def value_date(self):
"""property getter"""
return self['date']
@value_date.setter
def value_date(self, value_date):
"""property setter"""
self['date'] = value_date
@property
def name(self):
"""property getter"""
return self['name']
@name.setter
def name(self, name):
"""property setter"""
self['name'] = name
@property
def transferred_amount(self):
"""property getter"""
return self['amount']
@transferred_amount.setter
def transferred_amount(self, transferred_amount):
"""property setter"""
self['amount'] = transferred_amount
@property
def eref(self):
"""property getter"""
return self['ref']
@eref.setter
def eref(self, eref):
"""property setter"""
self['ref'] = eref
if not self.message:
self.name = eref
@property
def message(self):
"""property getter"""
return self._message
@message.setter
def message(self, message):
"""property setter"""
self._message = message
self.name = message
@property
def remote_owner(self):
"""property getter"""
return self['partner_name']
@remote_owner.setter
def remote_owner(self, remote_owner):
"""property setter"""
self['partner_name'] = remote_owner
if not (self.message or self.eref):
self.name = remote_owner
@property
def remote_account(self):
"""property getter"""
return self['account_number']
@remote_account.setter
def remote_account(self, remote_account):
"""property setter"""
self['account_number'] = remote_account
@property
def note(self):
return self['note']
@note.setter
def note(self, note):
self['note'] = note
def __init__(self):
"""Define and initialize attributes.
Not all attributes are already used in the actual import.
"""
super(BankTransaction, self).__init__()
self.transfer_type = False # Action type that initiated this message
self.execution_date = False # The posted date of the action
self.value_date = False # The value date of the action
self.remote_account = False # The account of the other party
self.remote_currency = False # The currency used by the other party
self.exchange_rate = 0.0
# The exchange rate used for conversion of local_currency and
# remote_currency
self.transferred_amount = 0.0 # actual amount transferred
self.name = ''
self._message = False # message from the remote party
self.eref = False # end to end reference for transactions
self.remote_owner = False # name of the other party
self.remote_owner_address = [] # other parties address lines
self.remote_owner_city = False # other parties city name
self.remote_owner_postalcode = False # other parties zip code
self.remote_owner_country_code = False # other parties country code
self.remote_bank_bic = False # bic of other party's bank
self.provision_costs = False # costs charged by bank for transaction
self.provision_costs_currency = False
self.provision_costs_description = False
self.error_message = False # error message for interaction with user
self.storno_retry = False
# If True, make cancelled debit eligible for a next direct debit run
self.data = '' # Raw data from which the transaction has been parsed
class BankStatement(dict):
"""A bank statement groups data about several bank transactions."""
@property
def statement_id(self):
"""property getter"""
return self['name']
def _set_transaction_ids(self):
"""Set transaction ids to statement_id with sequence-number."""
subno = 0
for transaction in self['transactions']:
subno += 1
transaction['unique_import_id'] = (
self.statement_id + str(subno).zfill(4))
@statement_id.setter
def statement_id(self, statement_id):
"""property setter"""
self['name'] = statement_id
self._set_transaction_ids()
@property
def local_account(self):
"""property getter"""
return self['account_number']
@local_account.setter
def local_account(self, local_account):
"""property setter"""
self['account_number'] = local_account
@property
def local_currency(self):
"""property getter"""
return self['currency_code']
@local_currency.setter
def local_currency(self, local_currency):
"""property setter"""
self['currency_code'] = local_currency
@property
def start_balance(self):
"""property getter"""
return self['balance_start']
@start_balance.setter
def start_balance(self, start_balance):
"""property setter"""
self['balance_start'] = start_balance
@property
def end_balance(self):
"""property getter"""
return self['balance_end']
@end_balance.setter
def end_balance(self, end_balance):
"""property setter"""
self['balance_end'] = end_balance
self['balance_end_real'] = end_balance
@property
def date(self):
"""property getter"""
return self['date']
@date.setter
def date(self, date):
"""property setter"""
self['date'] = date
def create_transaction(self):
"""Create and append transaction.
This should only be called after statement_id has been set, because
statement_id will become part of the unique transaction_id.
"""
transaction = BankTransaction()
self['transactions'].append(transaction)
# Fill default id, but might be overruled
transaction['unique_import_id'] = (
self.statement_id + str(len(self['transactions'])).zfill(4))
return transaction
def __init__(self):
super(BankStatement, self).__init__()
self['transactions'] = []
self.statement_id = ''
self.local_account = ''
self.local_currency = ''
self.date = ''
self.start_balance = 0.0
self.end_balance = 0.0

View File

@@ -1,3 +1,5 @@
# -*- encoding: utf-8 -*-
"""Define tests to be run."""
from . import test_res_partner_bank
from . import test_import_bank_statement
from .test_import_file import TestStatementFile

View File

@@ -25,13 +25,13 @@
from openerp.tests.common import TransactionCase
class TestAccountBankStatemetImport(TransactionCase):
class TestAccountBankStatementImport(TransactionCase):
"""Tests for import bank statement file import
(account.bank.statement.import)
"""
def setUp(self):
super(TestAccountBankStatemetImport, self).setUp()
super(TestAccountBankStatementImport, self).setUp()
self.statement_import_model = self.env[
'account.bank.statement.import']
self.account_journal_model = self.env['account.journal']
@@ -65,10 +65,7 @@ class TestAccountBankStatemetImport(TransactionCase):
"""
journal = self.account_journal_model.browse(self.journal_id)
expected_id = journal.company_id.partner_id.id
st_import = self.statement_import_model.sudo(self.other_user_id_a.id)
bank = st_import._create_bank_account(
'001251882303', company_id=self.company_id)
self.assertEqual(bank.partner_id.id,
expected_id)
self.assertEqual(bank.partner_id.id, expected_id)

View File

@@ -0,0 +1,131 @@
# -*- coding: utf-8 -*-
"""Provide common base for bank statement import tests."""
##############################################################################
#
# Copyright (C) 2015 Therp BV <http://therp.nl>.
#
# All other contributions are (C) by their respective contributors
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import logging
from openerp.tests.common import TransactionCase
from openerp.modules.module import get_module_resource
_logger = logging.getLogger(__name__)
class TestStatementFile(TransactionCase):
"""Check wether statements with transactions correctly imported.
No actual tests are done in this class, implementations are in
subclasses in actual import modules.
"""
def _test_transaction(
self, statement_obj, remote_account=False,
transferred_amount=False, value_date=False, ref=False):
"""Check wether transaction with attributes passed was created.
Actually this method also tests wether automatic creation of
partner bank accounts is working.
"""
transaction_model = self.env['account.bank.statement.line']
partner_bank_model = self.env['res.partner.bank']
domain = [('statement_id', '=', statement_obj.id)]
if remote_account:
bids = partner_bank_model.search(
[('acc_number', '=', remote_account)])
self.assertTrue(
bids,
'Bank-account %s not found after parse.' % remote_account
)
domain.append(('bank_account_id', '=', bids[0].id))
if transferred_amount:
domain.append(('amount', '=', transferred_amount))
if value_date:
domain.append(('date', '=', value_date))
if ref:
domain.append(('ref', '=', ref))
ids = transaction_model.search(domain)
if not ids:
# We will get assertion error, but to solve we need to see
# what transactions have been added:
self.cr.execute(
"select name, date, amount, ref, bank_account_id"
" from account_bank_statement_line"
" where statement_id=%d" % statement_obj.id)
_logger.error(
"Transaction not found in %s" %
str(self.cr.fetchall())
)
self.assertTrue(
ids,
'Transaction %s not found after parse.' % str(domain)
)
def _test_statement_import(
self, module_name, file_name, statement_name, local_account=False,
start_balance=False, end_balance=False, transactions=None):
"""Test correct creation of single statement."""
import_model = self.env['account.bank.statement.import']
partner_bank_model = self.env['res.partner.bank']
statement_model = self.env['account.bank.statement']
statement_path = get_module_resource(
module_name,
'test_files',
file_name
)
statement_file = open(
statement_path, 'rb').read().encode('base64')
bank_statement_id = import_model.create(
dict(
data_file=statement_file,
)
)
bank_statement_id.import_file()
# Check wether bank account has been created:
if local_account:
bids = partner_bank_model.search(
[('acc_number', '=', local_account)])
self.assertTrue(
bids,
'Bank account %s not created from statement' % local_account
)
# statement name is account number + '-' + date of last 62F line:
ids = statement_model.search([('name', '=', statement_name)])
self.assertTrue(
ids,
'Statement %s not found after parse.' % statement_name
)
statement_obj = ids[0]
if start_balance:
self.assertTrue(
abs(statement_obj.balance_start - start_balance) < 0.00001,
'Start balance %f not equal to expected %f' %
(statement_obj.balance_start, start_balance)
)
if end_balance:
self.assertTrue(
abs(statement_obj.balance_end_real - end_balance) < 0.00001,
'End balance %f not equal to expected %f' %
(statement_obj.balance_end_real, end_balance)
)
# Maybe we need to test transactions?
if transactions:
for transaction in transactions:
self._test_transaction(statement_obj, **transaction)