[IMP] account_statement_base_import: Fix PEP8

This commit is contained in:
Pedro M. Baeza
2014-08-04 18:42:00 +02:00
parent aebcbfdd5d
commit d9a943d2d3
7 changed files with 192 additions and 231 deletions

View File

@@ -31,30 +31,34 @@
], ],
'description': """ 'description': """
This module brings basic methods and fields on bank statement to deal with This module brings basic methods and fields on bank statement to deal with
the importation of different bank and offices. A generic abstract method is defined and an the importation of different bank and offices. A generic abstract method is
example that gives you a basic way of importing bank statement through a standard file is provided. defined and an example that gives you a basic way of importing bank statement
through a standard file is provided.
This module improves the bank statement and allows you to import your bank transactions with This module improves the bank statement and allows you to import your bank
a standard .csv or .xls file (you'll find it in the 'data' folder). It respects the profile transactions with a standard .csv or .xls file (you'll find it in the 'data'
(provided by the accouhnt_statement_ext module) to pass the entries. That means, folder). It respects the profile (provided by the accouhnt_statement_ext
you'll have to choose a file format for each profile. module) to pass the entries. That means, you'll have to choose a file format
In order to achieve this it uses the `xlrd` Python module which you will need to install for each profile.
separately in your environment. In order to achieve this it uses the `xlrd` Python module which you will need
to install separately in your environment.
This module can handle a commission taken by the payment office and has the following format: This module can handle a commission taken by the payment office and has the
following format:
* ref : the SO number, INV number or any matching ref found. It'll be used as reference * __ref__: the SO number, INV number or any matching ref found. It'll be used
in the generated entries and will be useful for reconciliation process as reference in the generated entries and will be useful for reconciliation
* date : date of the payment process
* amount : amount paid in the currency of the journal used in the importation profile * __date__: date of the payment
* label : the comunication given by the payment office, used as communication in the * __amount__: amount paid in the currency of the journal used in the
generated entries. importation profile
* __label__: the comunication given by the payment office, used as
The goal is here to populate the statement lines of a bank statement with the infos that the communication in the generated entries.
bank or office give you. Fell free to inherit from this module to add your own format.Then,
if you need to complete data from there, add your own account_statement_*_completion module and implement
the needed rules.
The goal is here to populate the statement lines of a bank statement with the
infos that the bank or office give you. Fell free to inherit from this module
to add your own format. Then, if you need to complete data from there, add your
own account_statement_*_completion module and implement the needed rules.
""", """,
'website': 'http://www.camptocamp.com', 'website': 'http://www.camptocamp.com',
'data': [ 'data': [

View File

@@ -18,7 +18,7 @@
# #
############################################################################## ##############################################################################
from openerp.tools.translate import _ from openerp.tools.translate import _
from openerp.osv.osv import except_osv from openerp.osv.orm import except_orm
import tempfile import tempfile
import datetime import datetime
from parser import BankStatementImportParser from parser import BankStatementImportParser
@@ -36,26 +36,28 @@ def float_or_zero(val):
class FileParser(BankStatementImportParser): class FileParser(BankStatementImportParser):
"""Generic abstract class for defining parser for .csv, .xls or .xlsx file
""" format.
Generic abstract class for defining parser for .csv, .xls or .xlsx file format.
""" """
def __init__(self, parse_name, ftype='csv', extra_fields=None, header=None, **kwargs): def __init__(self, parse_name, ftype='csv', extra_fields=None, header=None,
**kwargs):
""" """
:param char: parse_name: The name of the parser :param char: parse_name: The name of the parser
:param char: ftype: extension of the file (could be csv, xls or xlsx) :param char: ftype: extension of the file (could be csv, xls or \
:param dict: extra_fields: extra fields to add to the conversion dict. In the format xlsx)
{fieldname: fieldtype} :param dict: extra_fields: extra fields to add to the conversion \
:param list: header : specify header fields if the csv file has no header dict. In the format {fieldname: fieldtype}
:param list: header : specify header fields if the csv file has no \
header
""" """
super(FileParser, self).__init__(parse_name, **kwargs) super(FileParser, self).__init__(parse_name, **kwargs)
if ftype in ('csv', 'xls', 'xlsx'): if ftype in ('csv', 'xls', 'xlsx'):
self.ftype = ftype[0:3] self.ftype = ftype[0:3]
else: else:
raise except_osv(_('User Error'), raise except_orm(
_('Invalid file type %s. Please use csv, xls or xlsx') % ftype) _('User Error'),
_('Invalid file type %s. Please use csv, xls or xlsx') % ftype)
self.conversion_dict = { self.conversion_dict = {
'ref': unicode, 'ref': unicode,
'label': unicode, 'label': unicode,
@@ -71,23 +73,17 @@ class FileParser(BankStatementImportParser):
# Set in _parse_xls, from the contents of the file # Set in _parse_xls, from the contents of the file
def _custom_format(self, *args, **kwargs): def _custom_format(self, *args, **kwargs):
""" """No other work on data are needed in this parser."""
No other work on data are needed in this parser.
"""
return True return True
def _pre(self, *args, **kwargs): def _pre(self, *args, **kwargs):
""" """No pre-treatment needed for this parser."""
No pre-treatment needed for this parser.
"""
return True return True
def _parse(self, *args, **kwargs): def _parse(self, *args, **kwargs):
""" """Launch the parsing through .csv, .xls or .xlsx depending on the
Launch the parsing through .csv, .xls or .xlsx depending on the
given ftype given ftype
""" """
res = None res = None
if self.ftype == 'csv': if self.ftype == 'csv':
res = self._parse_csv() res = self._parse_csv()
@@ -97,31 +93,27 @@ class FileParser(BankStatementImportParser):
return True return True
def _validate(self, *args, **kwargs): def _validate(self, *args, **kwargs):
""" """We check that all the key of the given file (means header) are
We check that all the key of the given file (means header) are present present in the validation key provided. Otherwise, we raise an
in the validation key provided. Otherwise, we raise an Exception. Exception. We skip the validation step if the file header is provided
We skip the validation step if the file header is provided separately separately (in the field: fieldnames).
(in the field: fieldnames).
""" """
if self.fieldnames is None: if self.fieldnames is None:
parsed_cols = self.result_row_list[0].keys() parsed_cols = self.result_row_list[0].keys()
for col in self.keys_to_validate: for col in self.keys_to_validate:
if col not in parsed_cols: if col not in parsed_cols:
raise except_osv(_('Invalid data'), raise except_orm(_('Invalid data'),
_('Column %s not present in file') % col) _('Column %s not present in file') % col)
return True return True
def _post(self, *args, **kwargs): def _post(self, *args, **kwargs):
""" """Cast row type depending on the file format .csv or .xls after
Cast row type depending on the file format .csv or .xls after parsing the file. parsing the file."""
"""
self.result_row_list = self._cast_rows(*args, **kwargs) self.result_row_list = self._cast_rows(*args, **kwargs)
return True return True
def _parse_csv(self): def _parse_csv(self):
""" """:return: list of dict from csv file (line/rows)"""
:return: list of dict from csv file (line/rows)
"""
csv_file = tempfile.NamedTemporaryFile() csv_file = tempfile.NamedTemporaryFile()
csv_file.write(self.filebuffer) csv_file.write(self.filebuffer)
csv_file.flush() csv_file.flush()
@@ -130,9 +122,7 @@ class FileParser(BankStatementImportParser):
return list(reader) return list(reader)
def _parse_xls(self): def _parse_xls(self):
""" """:return: dict of dict from xls/xlsx file (line/rows)"""
:return: dict of dict from xls/xlsx file (line/rows)
"""
wb_file = tempfile.NamedTemporaryFile() wb_file = tempfile.NamedTemporaryFile()
wb_file.write(self.filebuffer) wb_file.write(self.filebuffer)
# We ensure that cursor is at beginig of file # We ensure that cursor is at beginig of file
@@ -147,8 +137,7 @@ class FileParser(BankStatementImportParser):
return res return res
def _from_csv(self, result_set, conversion_rules): def _from_csv(self, result_set, conversion_rules):
""" """Handle the converstion from the dict and handle date format from
Handle the converstion from the dict and handle date format from
an .csv file. an .csv file.
""" """
for line in result_set: for line in result_set:
@@ -159,72 +148,60 @@ class FileParser(BankStatementImportParser):
line[rule] = datetime.datetime.strptime(date_string, line[rule] = datetime.datetime.strptime(date_string,
'%Y-%m-%d') '%Y-%m-%d')
except ValueError as err: except ValueError as err:
raise except_osv(_("Date format is not valid."), raise except_orm(
_(" It should be YYYY-MM-DD for column: %s" _("Date format is not valid."),
" value: %s \n \n" _(" It should be YYYY-MM-DD for column: %s"
" \n Please check the line with ref: %s" " value: %s \n \n \n Please check the line with "
" \n \n Detail: %s") % (rule, "ref: %s \n \n Detail: %s") %
line.get( (rule, line.get(rule, _('Missing')),
rule, _('Missing')), line.get('ref', line), repr(err)))
line.get(
'ref', line),
repr(err)))
else: else:
try: try:
line[rule] = conversion_rules[rule](line[rule]) line[rule] = conversion_rules[rule](line[rule])
except Exception as err: except Exception as err:
raise except_osv(_('Invalid data'), raise except_orm(
_("Value %s of column %s is not valid." _('Invalid data'),
"\n Please check the line with ref %s:" _("Value %s of column %s is not valid.\n Please "
"\n \n Detail: %s") % (line.get(rule, _('Missing')), "check the line with ref %s:\n \n Detail: %s") %
rule, (line.get(rule, _('Missing')), rule,
line.get( line.get('ref', line), repr(err)))
'ref', line),
repr(err)))
return result_set return result_set
def _from_xls(self, result_set, conversion_rules): def _from_xls(self, result_set, conversion_rules):
""" """Handle the converstion from the dict and handle date format from
Handle the converstion from the dict and handle date format from
an .csv, .xls or .xlsx file. an .csv, .xls or .xlsx file.
""" """
for line in result_set: for line in result_set:
for rule in conversion_rules: for rule in conversion_rules:
if conversion_rules[rule] == datetime.datetime: if conversion_rules[rule] == datetime.datetime:
try: try:
t_tuple = xlrd.xldate_as_tuple( t_tuple = xlrd.xldate_as_tuple(line[rule],
line[rule], self._datemode) self._datemode)
line[rule] = datetime.datetime(*t_tuple) line[rule] = datetime.datetime(*t_tuple)
except Exception as err: except Exception as err:
raise except_osv(_("Date format is not valid"), raise except_orm(
_("Please modify the cell formatting to date format" _("Date format is not valid"),
" for column: %s" _("Please modify the cell formatting to date format"
" value: %s" " for column: %s value: %s\n Please check the "
"\n Please check the line with ref: %s" "line with ref: %s\n \n Detail: %s") %
"\n \n Detail: %s") % (rule, (rule, line.get(rule, _('Missing')),
line.get( line.get('ref', line), repr(err)))
rule, _('Missing')),
line.get(
'ref', line),
repr(err)))
else: else:
try: try:
line[rule] = conversion_rules[rule](line[rule]) line[rule] = conversion_rules[rule](line[rule])
except Exception as err: except Exception as err:
raise except_osv(_('Invalid data'), raise except_orm(
_("Value %s of column %s is not valid." _('Invalid data'),
"\n Please check the line with ref %s:" _("Value %s of column %s is not valid.\n Please "
"\n \n Detail: %s") % (line.get(rule, _('Missing')), "check the line with ref %s:\n \n Detail: %s") %
rule, (line.get(rule, _('Missing')), rule,
line.get( line.get('ref', line), repr(err)))
'ref', line),
repr(err)))
return result_set return result_set
def _cast_rows(self, *args, **kwargs): def _cast_rows(self, *args, **kwargs):
""" """Convert the self.result_row_list using the self.conversion_dict
Convert the self.result_row_list using the self.conversion_dict providen. providen. We call here _from_xls or _from_csv depending on the
We call here _from_xls or _from_csv depending on the self.ftype variable. self.ftype variable.
""" """
func = getattr(self, '_from_%s' % self.ftype) func = getattr(self, '_from_%s' % self.ftype)
res = func(self.result_row_list, self.conversion_dict) res = func(self.result_row_list, self.conversion_dict)

View File

@@ -31,9 +31,7 @@ except:
class GenericFileParser(FileParser): class GenericFileParser(FileParser):
"""Standard parser that use a define format in csv or xls to import into a
"""
Standard parser that use a define format in csv or xls to import into a
bank statement. This is mostely an example of how to proceed to create a new bank statement. This is mostely an example of how to proceed to create a new
parser, but will also be useful as it allow to import a basic flat file. parser, but will also be useful as it allow to import a basic flat file.
""" """
@@ -44,8 +42,7 @@ class GenericFileParser(FileParser):
@classmethod @classmethod
def parser_for(cls, parser_name): def parser_for(cls, parser_name):
""" """Used by the new_bank_statement_parser class factory. Return true if
Used by the new_bank_statement_parser class factory. Return true if
the providen name is generic_csvxls_so the providen name is generic_csvxls_so
""" """
return parser_name == 'generic_csvxls_so' return parser_name == 'generic_csvxls_so'
@@ -56,9 +53,10 @@ class GenericFileParser(FileParser):
method of statement line in order to record it. It is the responsibility method of statement line in order to record it. It is the responsibility
of every parser to give this dict of vals, so each one can implement his of every parser to give this dict of vals, so each one can implement his
own way of recording the lines. own way of recording the lines.
:param: line: a dict of vals that represent a line of result_row_list :param: line: a dict of vals that represent a line of \
:return: dict of values to give to the create method of statement line, result_row_list
it MUST contain at least: :return: dict of values to give to the create method of statement \
line, it MUST contain at least:
{ {
'name':value, 'name':value,
'date':value, 'date':value,

View File

@@ -21,6 +21,7 @@
import base64 import base64
import csv import csv
from datetime import datetime from datetime import datetime
from openerp.tools.translate import _
def UnicodeDictReader(utf8_data, **kwargs): def UnicodeDictReader(utf8_data, **kwargs):
@@ -31,7 +32,8 @@ def UnicodeDictReader(utf8_data, **kwargs):
dialect = sniffer.sniff(sample_data, delimiters=',;\t') dialect = sniffer.sniff(sample_data, delimiters=',;\t')
csv_reader = csv.DictReader(utf8_data, dialect=dialect, **kwargs) csv_reader = csv.DictReader(utf8_data, dialect=dialect, **kwargs)
for row in csv_reader: for row in csv_reader:
yield dict([(key, unicode(value, 'utf-8')) for key, value in row.iteritems()]) yield dict([(key, unicode(value, 'utf-8')) for key, value in
row.iteritems()])
class BankStatementImportParser(object): class BankStatementImportParser(object):
@@ -61,23 +63,19 @@ class BankStatementImportParser(object):
@classmethod @classmethod
def parser_for(cls, parser_name): def parser_for(cls, parser_name):
""" """Override this method for every new parser, so that
Override this method for every new parser, so that new_bank_statement_parser can new_bank_statement_parser can return the good class from his name.
return the good class from his name.
""" """
return False return False
def _decode_64b_stream(self): def _decode_64b_stream(self):
""" """Decode self.filebuffer in base 64 and override it"""
Decode self.filebuffer in base 64 and override it
"""
self.filebuffer = base64.b64decode(self.filebuffer) self.filebuffer = base64.b64decode(self.filebuffer)
return True return True
def _format(self, decode_base_64=True, **kwargs): def _format(self, decode_base_64=True, **kwargs):
""" """Decode into base 64 if asked and Format the given filebuffer by
Decode into base 64 if asked and Format the given filebuffer by calling calling _custom_format method.
_custom_format method.
""" """
if decode_base_64: if decode_base_64:
self._decode_64b_stream() self._decode_64b_stream()
@@ -85,44 +83,40 @@ class BankStatementImportParser(object):
return True return True
def _custom_format(self, *args, **kwargs): def _custom_format(self, *args, **kwargs):
""" """Implement a method in your parser to convert format, encoding and so
Implement a method in your parser to convert format, encoding and so on before on before starting to work on datas. Work on self.filebuffer
starting to work on datas. Work on self.filebuffer
""" """
return NotImplementedError return NotImplementedError
def _pre(self, *args, **kwargs): def _pre(self, *args, **kwargs):
""" """Implement a method in your parser to make a pre-treatment on datas
Implement a method in your parser to make a pre-treatment on datas before parsing before parsing them, like concatenate stuff, and so... Work on
them, like concatenate stuff, and so... Work on self.filebuffer self.filebuffer
""" """
return NotImplementedError return NotImplementedError
def _parse(self, *args, **kwargs): def _parse(self, *args, **kwargs):
""" """Implement a method in your parser to save the result of parsing
Implement a method in your parser to save the result of parsing self.filebuffer self.filebuffer in self.result_row_list instance property.
in self.result_row_list instance property.
""" """
return NotImplementedError return NotImplementedError
def _validate(self, *args, **kwargs): def _validate(self, *args, **kwargs):
""" """Implement a method in your parser to validate the
Implement a method in your parser to validate the self.result_row_list instance self.result_row_list instance property and raise an error if not valid.
property and raise an error if not valid.
""" """
return NotImplementedError return NotImplementedError
def _post(self, *args, **kwargs): def _post(self, *args, **kwargs):
""" """Implement a method in your parser to make some last changes on the
Implement a method in your parser to make some last changes on the result of parsing result of parsing the datas, like converting dates, computing
the datas, like converting dates, computing commission, ... commission, ...
""" """
return NotImplementedError return NotImplementedError
def get_st_vals(self): def get_st_vals(self):
""" """This method return a dict of vals that ca be passed to create method
This method return a dict of vals that ca be passed to of statement.
create method of statement.
:return: dict of vals that represent additional infos for the statement :return: dict of vals that represent additional infos for the statement
""" """
return { return {
@@ -133,33 +127,32 @@ class BankStatementImportParser(object):
} }
def get_st_line_vals(self, line, *args, **kwargs): def get_st_line_vals(self, line, *args, **kwargs):
""" """Implement a method in your parser that must return a dict of vals
Implement a method in your parser that must return a dict of vals that can be that can be passed to create method of statement line in order to record
passed to create method of statement line in order to record it. It is the responsibility it. It is the responsibility of every parser to give this dict of vals,
of every parser to give this dict of vals, so each one can implement his so each one can implement his own way of recording the lines.
own way of recording the lines.
:param: line: a dict of vals that represent a line of result_row_list :param: line: a dict of vals that represent a line of result_row_list
:return: dict of values to give to the create method of statement line, :return: dict of values to give to the create method of statement line,\
it MUST contain at least: it MUST contain at least:
{ {
'name':value, 'name':value,
'date':value, 'date':value,
'amount':value, 'amount':value,
'ref':value, 'ref':value,
} }
""" """
return NotImplementedError return NotImplementedError
def parse(self, filebuffer, *args, **kwargs): def parse(self, filebuffer, *args, **kwargs):
""" """This will be the method that will be called by wizard, button and so
This will be the method that will be called by wizard, button and so
to parse a filebuffer by calling successively all the private method to parse a filebuffer by calling successively all the private method
that need to be define for each parser. that need to be define for each parser.
Return: Return:
[] of rows as {'key':value} [] of rows as {'key':value}
Note: The row_list must contain only value that are present in the account. Note: The row_list must contain only value that are present in the
bank.statement.line object !!! account.bank.statement.line object !!!
""" """
if filebuffer: if filebuffer:
self.filebuffer = filebuffer self.filebuffer = filebuffer

View File

@@ -20,16 +20,14 @@
############################################################################## ##############################################################################
import sys import sys
import traceback import traceback
from openerp.tools.translate import _ from openerp.tools.translate import _
import datetime import datetime
from openerp.osv.orm import Model from openerp.osv import fields, orm
from openerp.osv import fields, osv
from parser import new_bank_statement_parser from parser import new_bank_statement_parser
from openerp.tools.config import config from openerp.tools.config import config
class AccountStatementProfil(Model): class AccountStatementProfil(orm.Model):
_inherit = "account.statement.profile" _inherit = "account.statement.profile"
def _get_import_type_selection(self, cr, uid, context=None): def _get_import_type_selection(self, cr, uid, context=None):
@@ -62,52 +60,51 @@ class AccountStatementProfil(Model):
} }
def _write_extra_statement_lines( def _write_extra_statement_lines(
self, cr, uid, parser, result_row_list, profile, statement_id, context): self, cr, uid, parser, result_row_list, profile, statement_id,
context):
"""Insert extra lines after the main statement lines. """Insert extra lines after the main statement lines.
After the main statement lines have been created, you can override this method to create After the main statement lines have been created, you can override this
extra statement lines. method to create extra statement lines.
:param: browse_record of the current parser :param: browse_record of the current parser
:param: result_row_list: [{'key':value}] :param: result_row_list: [{'key':value}]
:param: profile: browserecord of account.statement.profile :param: profile: browserecord of account.statement.profile
:param: statement_id: int/long of the current importing statement ID :param: statement_id: int/long of the current importing \
statement ID
:param: context: global context :param: context: global context
""" """
pass pass
def write_logs_after_import(self, cr, uid, ids, statement_id, num_lines, context): def write_logs_after_import(self, cr, uid, ids, statement_id, num_lines,
""" context):
Write the log in the logger """Write the log in the logger
:param int/long statement_id: ID of the concerned account.bank.statement :param int/long statement_id: ID of the concerned account.bank.statement
:param int/long num_lines: Number of line that have been parsed :param int/long num_lines: Number of line that have been parsed
:return: True :return: True
""" """
self.message_post(cr, self.message_post(
uid, cr, uid, ids,
ids, body=_('Statement ID %s have been imported with %s '
body=_('Statement ID %s have been imported with %s lines.') % 'lines.') % (statement_id, num_lines), context=context)
(statement_id, num_lines),
context=context)
return True return True
# Deprecated remove on V8 # Deprecated remove on V8
def prepare_statetement_lines_vals(self, *args, **kwargs): def prepare_statetement_lines_vals(self, *args, **kwargs):
return self.prepare_statement_lines_vals(*args, **kwargs) return self.prepare_statement_lines_vals(*args, **kwargs)
def prepare_statement_lines_vals( def prepare_statement_lines_vals(self, cr, uid, parser_vals,
self, cr, uid, parser_vals, statement_id, context):
statement_id, context): """Hook to build the values of a line from the parser returned values.
""" At least it fullfill the statement_id. Overide it to add your own
Hook to build the values of a line from the parser returned values. At completion if needed.
least it fullfill the statement_id. Overide it to add your
own completion if needed.
:param dict of vals from parser for account.bank.statement.line (called by :param dict of vals from parser for account.bank.statement.line \
parser.get_st_line_vals) (called by parser.get_st_line_vals)
:param int/long statement_id: ID of the concerned account.bank.statement :param int/long statement_id: ID of the concerned account.bank.statement
:return: dict of vals that will be passed to create method of statement line. :return: dict of vals that will be passed to create method of \
statement line.
""" """
statement_line_obj = self.pool['account.bank.statement.line'] statement_line_obj = self.pool['account.bank.statement.line']
values = parser_vals values = parser_vals
@@ -121,10 +118,8 @@ class AccountStatementProfil(Model):
values['period_id'] = period_memoizer[date] values['period_id'] = period_memoizer[date]
else: else:
# This is awfully slow... # This is awfully slow...
periods = self.pool.get('account.period').find(cr, uid, periods = self.pool.get('account.period').find(
dt=values.get( cr, uid, dt=values.get('date'), context=context)
'date'),
context=context)
values['period_id'] = periods[0] values['period_id'] = periods[0]
period_memoizer[date] = periods[0] period_memoizer[date] = periods[0]
values = statement_line_obj._add_missing_default_values( values = statement_line_obj._add_missing_default_values(
@@ -133,8 +128,7 @@ class AccountStatementProfil(Model):
def prepare_statement_vals(self, cr, uid, profile_id, result_row_list, def prepare_statement_vals(self, cr, uid, profile_id, result_row_list,
parser, context=None): parser, context=None):
""" """Hook to build the values of the statement from the parser and
Hook to build the values of the statement from the parser and
the profile. the profile.
""" """
vals = {'profile_id': profile_id} vals = {'profile_id': profile_id}
@@ -151,9 +145,8 @@ class AccountStatementProfil(Model):
def multi_statement_import(self, cr, uid, ids, profile_id, file_stream, def multi_statement_import(self, cr, uid, ids, profile_id, file_stream,
ftype="csv", context=None): ftype="csv", context=None):
""" """Create multiple bank statements from values given by the parser for
Create multiple bank statements from values given by the parser for the the given profile.
givenprofile.
:param int/long profile_id: ID of the profile used to import the file :param int/long profile_id: ID of the profile used to import the file
:param filebuffer file_stream: binary of the providen file :param filebuffer file_stream: binary of the providen file
@@ -162,23 +155,26 @@ class AccountStatementProfil(Model):
""" """
prof_obj = self.pool['account.statement.profile'] prof_obj = self.pool['account.statement.profile']
if not profile_id: if not profile_id:
raise osv.except_osv(_("No Profile!"), raise orm.except_orm(
_("You must provide a valid profile to import a bank statement!")) _("No Profile!"),
_("You must provide a valid profile to import a bank "
"statement!"))
prof = prof_obj.browse(cr, uid, profile_id, context=context) prof = prof_obj.browse(cr, uid, profile_id, context=context)
parser = new_bank_statement_parser(prof, ftype=ftype) parser = new_bank_statement_parser(prof, ftype=ftype)
res = [] res = []
for result_row_list in parser.parse(file_stream): for result_row_list in parser.parse(file_stream):
statement_id = self._statement_import(cr, uid, ids, prof, parser, statement_id = self._statement_import(
file_stream, ftype=ftype, context=context) cr, uid, ids, prof, parser, file_stream, ftype=ftype,
context=context)
res.append(statement_id) res.append(statement_id)
return res return res
def _statement_import(self, cr, uid, ids, prof, parser, file_stream, ftype="csv", context=None): def _statement_import(self, cr, uid, ids, prof, parser, file_stream,
""" ftype="csv", context=None):
Create a bank statement with the given profile and parser. It will fullfill the bank statement """Create a bank statement with the given profile and parser. It will
with the values of the file providen, but will not complete data (like finding the partner, or fullfill the bank statement with the values of the file providen, but
the right account). This will be done in a second step with the completion rules. will not complete data (like finding the partner, or the right account).
This will be done in a second step with the completion rules.
:param prof : The profile used to import the file :param prof : The profile used to import the file
:param parser: the parser :param parser: the parser
@@ -186,28 +182,25 @@ class AccountStatementProfil(Model):
:param char: ftype represent the file exstension (csv by default) :param char: ftype represent the file exstension (csv by default)
:return: ID of the created account.bank.statemênt :return: ID of the created account.bank.statemênt
""" """
statement_obj = self.pool.get('account.bank.statement') statement_obj = self.pool['account.bank.statement']
statement_line_obj = self.pool.get('account.bank.statement.line') statement_line_obj = self.pool['account.bank.statement.line']
attachment_obj = self.pool.get('ir.attachment') attachment_obj = self.pool['ir.attachment']
result_row_list = parser.result_row_list result_row_list = parser.result_row_list
# Check all key are present in account.bank.statement.line!! # Check all key are present in account.bank.statement.line!!
if not result_row_list: if not result_row_list:
raise osv.except_osv(_("Nothing to import"), raise orm.except_orm(_("Nothing to import"),
_("The file is empty")) _("The file is empty"))
parsed_cols = parser.get_st_line_vals(result_row_list[0]).keys() parsed_cols = parser.get_st_line_vals(result_row_list[0]).keys()
for col in parsed_cols: for col in parsed_cols:
if col not in statement_line_obj._columns: if col not in statement_line_obj._columns:
raise osv.except_osv(_("Missing column!"), raise orm.except_orm(
_("Column %s you try to import is not " _("Missing column!"),
"present in the bank statement line!") % col) _("Column %s you try to import is not present in the bank "
"statement line!") % col)
statement_vals = self.prepare_statement_vals( statement_vals = self.prepare_statement_vals(
cr, uid, prof.id, result_row_list, parser, context) cr, uid, prof.id, result_row_list, parser, context)
statement_id = statement_obj.create(cr, uid, statement_id = statement_obj.create(
statement_vals, cr, uid, statement_vals, context=context)
context=context)
try: try:
# Record every line in the bank statement # Record every line in the bank statement
statement_store = [] statement_store = []
@@ -220,7 +213,6 @@ class AccountStatementProfil(Model):
# Hack to bypass ORM poor perfomance. Sob... # Hack to bypass ORM poor perfomance. Sob...
statement_line_obj._insert_lines( statement_line_obj._insert_lines(
cr, uid, statement_store, context=context) cr, uid, statement_store, context=context)
self._write_extra_statement_lines( self._write_extra_statement_lines(
cr, uid, parser, result_row_list, prof, statement_id, context) cr, uid, parser, result_row_list, prof, statement_id, context)
# Trigger store field computation if someone has better idea # Trigger store field computation if someone has better idea
@@ -229,27 +221,24 @@ class AccountStatementProfil(Model):
start_bal = start_bal['balance_start'] start_bal = start_bal['balance_start']
statement_obj.write( statement_obj.write(
cr, uid, [statement_id], {'balance_start': start_bal}) cr, uid, [statement_id], {'balance_start': start_bal})
attachment_data = { attachment_data = {
'name': 'statement file', 'name': 'statement file',
'datas': file_stream, 'datas': file_stream,
'datas_fname': "%s.%s" % (datetime.datetime.now().date(), ftype), 'datas_fname': "%s.%s" % (datetime.datetime.now().date(),
ftype),
'res_model': 'account.bank.statement', 'res_model': 'account.bank.statement',
'res_id': statement_id, 'res_id': statement_id,
} }
attachment_obj.create(cr, uid, attachment_data, context=context) attachment_obj.create(cr, uid, attachment_data, context=context)
# If user ask to launch completion at end of import, do it! # If user ask to launch completion at end of import, do it!
if prof.launch_import_completion: if prof.launch_import_completion:
statement_obj.button_auto_completion( statement_obj.button_auto_completion(
cr, uid, [statement_id], context) cr, uid, [statement_id], context)
# Write the needed log infos on profile # Write the needed log infos on profile
self.write_logs_after_import(cr, uid, prof.id, self.write_logs_after_import(cr, uid, prof.id,
statement_id, statement_id,
len(result_row_list), len(result_row_list),
context) context)
except Exception: except Exception:
error_type, error_value, trbk = sys.exc_info() error_type, error_value, trbk = sys.exc_info()
st = "Error: %s\nDescription: %s\nTraceback:" % ( st = "Error: %s\nDescription: %s\nTraceback:" % (
@@ -260,6 +249,6 @@ class AccountStatementProfil(Model):
# For now we avoid re-catching error in debug mode # For now we avoid re-catching error in debug mode
if config['debug_mode']: if config['debug_mode']:
raise raise
raise osv.except_osv(_("Statement import error"), raise orm.except_orm(_("Statement import error"),
_("The statement cannot be created: %s") % st) _("The statement cannot be created: %s") % st)
return statement_id return statement_id

View File

@@ -22,11 +22,10 @@
import base64 import base64
import inspect import inspect
import os import os
from openerp.tests import common from openerp.tests import common
class test_coda_import(common.TransactionCase): class TestCodaImport(common.TransactionCase):
def prepare(self): def prepare(self):
self.company_a = self.browse_ref('base.main_company') self.company_a = self.browse_ref('base.main_company')
@@ -36,7 +35,6 @@ class test_coda_import(common.TransactionCase):
# create the 2009 fiscal year since imported coda file reference # create the 2009 fiscal year since imported coda file reference
# statement lines in 2009 # statement lines in 2009
self.fiscalyear_id = self._create_fiscalyear("2011", self.company_a.id) self.fiscalyear_id = self._create_fiscalyear("2011", self.company_a.id)
self.account_id = self.ref("account.a_recv") self.account_id = self.ref("account.a_recv")
self.journal_id = self.ref("account.bank_journal") self.journal_id = self.ref("account.bank_journal")
self.import_wizard_obj = self.registry('credit.statement.import') self.import_wizard_obj = self.registry('credit.statement.import')
@@ -77,7 +75,8 @@ class test_coda_import(common.TransactionCase):
self.cr, self.uid, wizard_id) self.cr, self.uid, wizard_id)
statement_id = self.account_bank_statement_obj.search( statement_id = self.account_bank_statement_obj.search(
self.cr, self.uid, eval(res['domain'])) self.cr, self.uid, eval(res['domain']))
return self.account_bank_statement_obj.browse(self.cr, self.uid, statement_id)[0] return self.account_bank_statement_obj.browse(
self.cr, self.uid, statement_id)[0]
def test_simple_xls(self): def test_simple_xls(self):
"""Test import from xls """Test import from xls

View File

@@ -36,7 +36,8 @@ class CreditPartnerStatementImporter(orm.TransientModel):
if context is None: if context is None:
context = {} context = {}
res = {} res = {}
if (context.get('active_model', False) == 'account.statement.profile' and if (context.get('active_model', False) ==
'account.statement.profile' and
context.get('active_ids', False)): context.get('active_ids', False)):
ids = context['active_ids'] ids = context['active_ids']
assert len( assert len(
@@ -57,8 +58,8 @@ class CreditPartnerStatementImporter(orm.TransientModel):
'journal_id': fields.many2one('account.journal', 'journal_id': fields.many2one('account.journal',
'Financial journal to use transaction'), 'Financial journal to use transaction'),
'file_name': fields.char('File Name', size=128), 'file_name': fields.char('File Name', size=128),
'receivable_account_id': fields.many2one('account.account', 'receivable_account_id': fields.many2one(
'Force Receivable/Payable Account'), 'account.account', 'Force Receivable/Payable Account'),
'force_partner_on_bank': fields.boolean( 'force_partner_on_bank': fields.boolean(
'Force partner on bank move', 'Force partner on bank move',
help="Tic that box if you want to use the credit insitute partner " help="Tic that box if you want to use the credit insitute partner "
@@ -73,12 +74,12 @@ class CreditPartnerStatementImporter(orm.TransientModel):
def onchange_profile_id(self, cr, uid, ids, profile_id, context=None): def onchange_profile_id(self, cr, uid, ids, profile_id, context=None):
res = {} res = {}
if profile_id: if profile_id:
c = self.pool.get("account.statement.profile").browse( c = self.pool["account.statement.profile"].browse(
cr, uid, profile_id, context=context) cr, uid, profile_id, context=context)
res = {'value': res = {'value':
{'partner_id': c.partner_id and c.partner_id.id or False, {'partner_id': c.partner_id and c.partner_id.id or False,
'journal_id': c.journal_id and c.journal_id.id or False, 'journal_id': c.journal_id and c.journal_id.id or False,
'receivable_account_id': c.receivable_account_id and c.receivable_account_id.id or False, 'receivable_account_id': c.receivable_account_id.id,
'force_partner_on_bank': c.force_partner_on_bank, 'force_partner_on_bank': c.force_partner_on_bank,
'balance_check': c.balance_check, 'balance_check': c.balance_check,
} }