[DOC] Comment the whole code of the bank statement improvement modules suite.

(lp:c2c-financial-addons/6.1 rev 24.1.24)
This commit is contained in:
Joël Grand-Guillaume
2012-06-20 16:01:43 +02:00
parent 0e70448fac
commit 52e490104f
21 changed files with 499 additions and 341 deletions

View File

@@ -38,13 +38,19 @@
3) Match from statement line reference (based on SO number)
3) Match from statement line reference (based on Invoice number)
You can easily override this module and add your own rules in your own one. The basic rules only
fullfill the partner, but you can use them to complete all values of the line (in the future, we'll
add rule to automatically match and reconcile the line).
It add as well a label on the bank statement line (on which the pre-define rules can match) and
a char field on the partner called 'Bank Statement Label'. Using the pre-define rules, you'll be
able to match various label for a partner.
The reference of the line is always used by the reconciliation process. We're supposed to copy
there (or write manually) the matching string. That can be : the order Number or an invoice number,
or anything that will be found in the invoice entry part to make the match.
or anything that will be found in the invoice accounting entry part to make the match.
You can use it with our account_advanced_reconcile module to automatize the reconciliation process.
""",
'website': 'http://www.camptocamp.com',

View File

@@ -1,7 +1,8 @@
# -*- encoding: utf-8 -*-
#################################################################################
# #
# Copyright (C) 2011 Akretion Sébastien BEAU <sebastien.beau@akretion.com> #
# Copyright (C) 2011 Akretion & Camptocamp
# Author : Sébastien BEAU, Joel Grand-Guillaume #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU Affero General Public License as #
@@ -21,63 +22,17 @@
from osv import fields, osv
class res_partner(osv.osv):
"""Add a bank label on the partner so that we can use it to match
this partner when we found this in a statement line."""
"""
Add a bank label on the partner so that we can use it to match
this partner when we found this in a statement line.
"""
_inherit = 'res.partner'
_columns = {
'bank_statement_label':fields.char('Bank Statement Label', size=100,
help="Enter the various label found on your bank statement separated by a ; If \
one of this label is include in the bank statement line, the partner will be automatically \
filled (as long as you use thi method in your statement profile)."),
filled (as long as you use this method/rules in your statement profile)."),
}
# def get_partner_from_order_ref(self, cr, uid, order_ref, context=None):
# order_obj = self.pool.get('sale.order')
# so_id = order_obj.search(cr, uid, [('name', '=', order_ref)], context=context)
# if so_id:
# so = order_obj.browse(cr, uid, so_id, context=context)[0]
# return so.partner_id.id
# return False
#
# def get_partner_from_name(self, cr, uid, partner_name, context=None):
# #print 'try to get partner from name', partner_name
# return False
#
# def get_partner_from_email(self, cr, uid, partner_email, context=None):
# address_ids = self.pool.get('res.partner.address').search(cr, uid, [['email', '=', partner_email]], context=context)
# if address_ids:
# partner_id = self.search(cr, uid, [['address', 'in', address_ids]], context=context)
# return partner_id and partner_id[0]
# return False
# def get_partner_from_label_based_on_bank_statement_label(self, cr, uid, label, context=None):
# ids = self.search(cr, uid, [['bank_statement_label', '!=', False]], context=context)
# for partner in self.browse(cr, uid, ids, context=context):
# for partner_label in partner.bank_statement_label.split(';'):
# if partner_label in label:
# return partner.id
# return False
#
# def get_supplier_partner_from_label_based_on_name(self, cr, uid, label, context=None):
# supplier_ids = self.search(cr, uid, [['supplier', '=', True]], context=context)
# for partner in self.browse(cr, uid, supplier_ids, context=context):
# if partner.name in label:
# return partner.id
# return False
#
# def get_partner_account(self, cr, uid, id, amount, context=None):
# partner = self.browse(cr, uid, id, context=context)
# if partner.supplier and not partner.customer:
# return partner.property_account_payable.id
# if partner.customer and not partner.supplier:
# return partner.property_account_receivable.id
#
# if amount >0:
# return partner.property_account_receivable.id
# else:
# return partner.property_account_payable.id
res_partner()

View File

@@ -26,6 +26,10 @@ from openerp.osv import fields, osv
from operator import itemgetter, attrgetter
class ErrorTooManyPartner(Exception):
"""
New Exception definition that is raised when more than one partner is matched by
the completion rule.
"""
def __init__(self, value):
self.value = value
def __str__(self):
@@ -33,12 +37,19 @@ class ErrorTooManyPartner(Exception):
class AccountStatementProfil(Model):
"""
Extend the class to add rules per profil that will match at least the partner,
but it could also be used to match other values as well.
"""
_inherit = "account.statement.profil"
_columns={
# For now, we don't implement this features, but this would probably be there:
# @Akretion : For now, we don't implement this features, but this would probably be there:
# 'auto_completion': fields.text('Auto Completion'),
# 'transferts_account_id':fields.many2one('account.account', 'Transferts Account'),
# => You can implement it in a module easily, we design it with your needs in mind
# as well !
'rule_ids':fields.many2many('account.statement.completion.rule',
string='Related statement profiles',
@@ -47,16 +58,18 @@ class AccountStatementProfil(Model):
}
def find_values_from_rules(self, cr, uid, id, line_id, context=None):
"""This method will execute all rules, in their sequence order,
to match a partner for the given statement.line and return his id.
"""
This method will execute all related rules, in their sequence order,
to retrieve all the values returned by the first rules that will match.
:param int/long line_id: eee
:return: A dict of value that can be passed directly to the write method of
the statement line:
{'partner_id': value,
'account_id' : value,
...
}
:param int/long line_id: id of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id' : value,
...}
"""
if not context:
context={}
@@ -74,20 +87,23 @@ class AccountStatementProfil(Model):
class AccountStatementCompletionRule(Model):
"""This will represent all the completion method that we can have to
fullfill the bank statement. You'll be able to extend them in you own module
"""
This will represent all the completion method that we can have to
fullfill the bank statement lines. You'll be able to extend them in you own module
and choose those to apply for every statement profile.
The goal of a rule is to fullfill at least the partner of the line, but
if possible also the reference because we'll use it in the reconciliation
process. The reference should contain the invoice number or the SO number
or any reference that will be matched by the invoice genertaed move.
or any reference that will be matched by the invoice accounting move.
"""
_name = "account.statement.completion.rule"
_order = "sequence asc"
def _get_functions(self, cr, uid, context=None):
"""List of available methods for rules. Override this to add you own."""
"""
List of available methods for rules. Override this to add you own.
"""
return [
('get_from_ref_and_invoice', 'From line reference (based on invoice number)'),
('get_from_ref_and_so', 'From line reference (based on SO number)'),
@@ -105,14 +121,18 @@ class AccountStatementCompletionRule(Model):
}
def get_from_ref_and_invoice(self, cursor, uid, line_id, context=None):
"""Match the partner based on the invoice number and the reference of the statement
line. Then, call the generic st_line method to complete other values.
If more than one partner matched, raise an error.
Return:
"""
Match the partner based on the invoice number and the reference of the statement
line. Then, call the generic get_values_for_line method to complete other values.
If more than one partner matched, raise the ErrorTooManyPartner error.
:param int/long line_id: id of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id' : value,
...}
"""
st_obj = self.pool.get('account.bank.statement.line')
@@ -133,14 +153,18 @@ class AccountStatementCompletionRule(Model):
return res
def get_from_ref_and_so(self, cursor, uid, line_id, context=None):
"""Match the partner based on the SO number and the reference of the statement
line. Then, call the generic st_line method to complete other values.
If more than one partner matched, raise an error.
Return:
"""
Match the partner based on the SO number and the reference of the statement
line. Then, call the generic get_values_for_line method to complete other values.
If more than one partner matched, raise the ErrorTooManyPartner error.
:param int/long line_id: id of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id' : value,
...}
"""
st_obj = self.pool.get('account.bank.statement.line')
@@ -162,16 +186,20 @@ class AccountStatementCompletionRule(Model):
def get_from_label_and_partner_field(self, cursor, uid, line_id, context=None):
"""Match the partner based on the label field of the statement line
"""
Match the partner based on the label field of the statement line
and the text defined in the 'bank_statement_label' field of the partner.
Remember that we can have values separated with ; Then, call the generic
st_line method to complete other values.
If more than one partner matched, raise an error.
Return:
get_values_for_line method to complete other values.
If more than one partner matched, raise the ErrorTooManyPartner error.
:param int/long line_id: id of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id' : value,
...}
"""
partner_obj = self.pool.get('res.partner')
@@ -195,10 +223,14 @@ class AccountStatementCompletionRule(Model):
return res
def get_from_label_and_partner_name(self, cursor, uid, line_id, context=None):
"""Match the partner based on the label field of the statement line
"""
Match the partner based on the label field of the statement line
and the name of the partner.
Then, call the generic st_line method to complete other values.
Return:
Then, call the generic get_values_for_line method to complete other values.
If more than one partner matched, raise the ErrorTooManyPartner error.
:param int/long line_id: id of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
@@ -206,6 +238,7 @@ class AccountStatementCompletionRule(Model):
...}
"""
# This Method has not been tested yet !
res = {}
st_obj = self.pool.get('account.bank.statement.line')
st_line = st_obj.browse(cursor,uid,line_id)
@@ -225,22 +258,26 @@ class AccountStatementCompletionRule(Model):
class AccountStatementLine(Model):
"""Add sparse field on the statement line to allow to store all the
bank infos that are given by an office. You can then add you own in your
module."""
"""
Add sparse field on the statement line to allow to store all the
bank infos that are given by a bank/office. You can then add you own in your
module. The idea here is to store all bank/office infos in the additionnal_bank_fields
serialized field when importing the file. If many values, add a tab in the bank
statement line to store your specific one. Have a look in account_statement_base_import
module to see how we've done it.
"""
_inherit = "account.bank.statement.line"
_columns={
# Only label for a start, but other module can add their own
'additionnal_bank_fields' : fields.serialized('Additionnal infos from bank',
help="Used by completion and import system. Adds every field that is present in your bank/office \
statement file"),
'label': fields.sparse(type='char', string='Label',
serialization_field='additionnal_bank_fields'),
serialization_field='additionnal_bank_fields',
help="Generiy field to store a label given from the bank/office on which we can \
base the default/standard providen rule."),
'already_completed': fields.boolean("Auto-Completed",
help="When this checkbox is ticked, the auto-completion process/button will ignore it."),
help="When this checkbox is ticked, the auto-completion process/button will ignore this line."),
}
_defaults = {
'already_completed': False,
@@ -249,10 +286,16 @@ class AccountStatementLine(Model):
def get_line_values_from_rules(self, cr, uid, ids, context=None):
"""
We'll try to find out the values related to the line based on what we
have and rules setted on the profile..
We'll try to find out the values related to the line based on rules setted on
the profile.. We will ignore line for which already_completed is ticked.
We ignore line for which already_completed is ticked!
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
'account_id' : value,
...}
"""
profile_obj = self.pool.get('account.statement.profil')
st_obj = self.pool.get('account.bank.statement.line')
@@ -279,13 +322,17 @@ class AccountStatementLine(Model):
class AccountBankSatement(Model):
"""
We add a basic button and stuff to support the auto-completion
of the bank statement once line have been imported or manually entred.
of the bank statement once line have been imported or manually fullfill.
"""
_inherit = "account.bank.statement"
def button_auto_completion(self, cr, uid, ids, context=None):
"""Complete line with values given by rules and tic the already_completed
checkbox so we won't compute them again until the user untick it !"""
"""
Complete line with values given by rules and tic the already_completed
checkbox so we won't compute them again unless the user untick them !
"""
# TODO: Test the errors system, we should be able to complete all line that
# passed, and raise an error for all other at once..
if not context:
context={}
stat_line_obj = self.pool.get('account.bank.statement.line')
@@ -312,16 +359,3 @@ class AccountBankSatement(Model):
}
return {'warning': warning}
return True
def auto_confirm(self, cr, uid, ids, context=None):
if not context:
context={}
ok=True
for stat in self.browse(cr, uid, ids, context=context):
for line in stat.line_ids:
if not line.partner_id or line.account_id.id == 1:
ok=False
continue
if ok:
self.button_confirm(cr, uid, [stat.id], context=context)
return True

View File

@@ -80,8 +80,9 @@
<field name="view_mode">tree,form</field>
</record>
<menuitem string="Statement Completion Rule" action="action_st_completion_rule_tree" id="menu_action_st_completion_rule_tree_menu" parent="account.menu_configuration_misc" sequence="30"/>
<menuitem string="Statement Completion Rule" action="action_st_completion_rule_tree"
id="menu_action_st_completion_rule_tree_menu" parent="account.menu_configuration_misc"
sequence="30"/>
</data>
</openerp>

View File

@@ -28,18 +28,13 @@
'depends': ['account_statement_ext','account_statement_base_completion'],
'description': """
This module bring 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 example that provide a basic way of importing
bank statement through a standard .csv or .xml file is providen.
The goal is here to populate the statement lines of a bank statement with the infos that the
bank or offic give you. Then, if you need to complete data from there, add your own
statement_*_completion module and implement the needed rules.
the importation of different bank and offices. A generic abstract method is defined and an
example that gives you a basic way of importing bank statement through a standard file is providen.
This module improves the bank statement and allow you to import your bank transactions with
a standard .csv or .xls file (you'll find it in the 'datas' folder). It'll respect the profil
you'll choose (providen by the accouhnt_statement_ext module) to pass the entries.
you'll choose (providen by the accouhnt_statement_ext module) to pass the entries. That means,
you'll have to choose a file format for each profile.
This module can handle a commission taken by the payment office and has the following format:
@@ -51,6 +46,10 @@
* label : the comunication given by the payment office, used as communication in the
generated entries.
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',

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Nicolas Bessi
# Author: Nicolas Bessi, Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify

View File

@@ -23,7 +23,6 @@ import base64
import csv
import tempfile
import datetime
# from . import parser
from parser import BankStatementImportParser
from parser import UnicodeDictReader
try:
@@ -32,11 +31,15 @@ except:
raise Exception(_('Please install python lib xlrd'))
class FileParser(BankStatementImportParser):
"""Abstract clall for that help to build a specific parser for all
.csv and .xls files."""
"""
Generic abstract class for defining parser for .csv or .xls file format.
"""
def __init__(self, parse_name, keys_to_validate={}, ftype='csv', convertion_dict=None, *args, **kwargs):
def __init__(self, parse_name, keys_to_validate=[], ftype='csv', convertion_dict=None, *args, **kwargs):
"""
:param char: parse_name : The name of the parser
:param list: keys_to_validate : contain the key that need to be present in the file
:param char ftype: extension of the file (could be csv or xls)
:param: convertion_dict : keys and type to convert of every column in the file like
{
'ref': unicode,
@@ -45,7 +48,6 @@ class FileParser(BankStatementImportParser):
'amount': float,
'commission_amount': float
}
"""
super(FileParser, self).__init__(parse_name, *args, **kwargs)
@@ -57,26 +59,22 @@ class FileParser(BankStatementImportParser):
self.convertion_dict = convertion_dict
def _custom_format(self, *args, **kwargs):
"""
No other work on data are needed in this parser.
"""
return True
def _pre(self, *args, **kwargs):
return True
def _validate(self, *args, **kwargs):
parsed_cols = self.result_row_list[0].keys()
for col in self.keys_to_validate:
if col not in parsed_cols:
raise Exception(_('Column %s not present in file') % (col))
return True
def _post(self, *args, **kwargs):
"""Cast row type depending on the file format .csv or .xls"""
self.result_row_list = self._cast_rows(*args, **kwargs)
"""
No pre-treatment needed for this parser.
"""
return True
def _parse(self, *args, **kwargs):
"""Launch the parsing through .csv or .xls depending on the
given ftype"""
"""
Launch the parsing through .csv or .xls depending on the
given ftype
"""
res = None
if self.ftype == 'csv':
@@ -86,8 +84,29 @@ class FileParser(BankStatementImportParser):
self.result_row_list = res
return True
def _validate(self, *args, **kwargs):
"""
We check that all the key of the given file (means header) are present
in the validation key providen. Otherwise, we raise an Exception.
"""
parsed_cols = self.result_row_list[0].keys()
for col in self.keys_to_validate:
if col not in parsed_cols:
raise Exception(_('Column %s not present in file') % (col))
return True
def _post(self, *args, **kwargs):
"""
Cast row type depending on the file format .csv or .xls after parsing the file.
"""
self.result_row_list = self._cast_rows(*args, **kwargs)
return True
def _parse_csv(self, delimiter=';'):
"return an array of dict from csv file"
"""
:return: dict of dict from csv file (line/rows)
"""
csv_file = tempfile.NamedTemporaryFile()
csv_file.write(self.filebuffer)
# We ensure that cursor is at beginig of file
@@ -99,7 +118,9 @@ class FileParser(BankStatementImportParser):
return [x for x in reader]
def _parse_xls(self):
"return an array of dict from xls file"
"""
:return: dict of dict from xls file (line/rows)
"""
wb_file = tempfile.NamedTemporaryFile()
wb_file.write(self.filebuffer)
# We ensure that cursor is at beginig of file
@@ -117,6 +138,10 @@ class FileParser(BankStatementImportParser):
return res
def _from_csv(self, result_set, conversion_rules):
"""
Handle the converstion from the dict and handle date format from
an .csv file.
"""
for line in result_set:
for rule in conversion_rules:
if conversion_rules[rule] == datetime.datetime:
@@ -128,6 +153,10 @@ class FileParser(BankStatementImportParser):
return result_set
def _from_xls(self, result_set, conversion_rules):
"""
Handle the converstion from the dict and handle date format from
an .xls file.
"""
for line in result_set:
for rule in conversion_rules:
if conversion_rules[rule] == datetime.datetime:
@@ -138,6 +167,10 @@ class FileParser(BankStatementImportParser):
return result_set
def _cast_rows(self, *args, **kwargs):
"""
Convert the self.result_row_list using the self.convertion_dict providen.
We call here _from_xls or _from_csv depending on the self.ftype variable.
"""
func = getattr(self, '_from_%s'%(self.ftype))
res = func(self.result_row_list, self.convertion_dict)
return res

View File

@@ -31,11 +31,11 @@ except:
raise Exception(_('Please install python lib xlrd'))
class GenericFileParser(FileParser):
"""Generic parser that use a define format in csv or xls to import
"""
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
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.
"""
def __init__(self, parse_name, ftype='csv'):
convertion_dict = {
@@ -47,16 +47,19 @@ class GenericFileParser(FileParser):
}
# Order of cols does not matter but first row of the file has to be header
keys_to_validate = ['ref', 'label', 'date', 'amount', 'commission_amount']
super(GenericFileParser,self).__init__(parse_name, keys_to_validate=keys_to_validate, ftype=ftype, convertion_dict=convertion_dict)
@classmethod
def parser_for(cls, parser_name):
"""
Used by the new_bank_statement_parser class factory. Return true if
the providen name is generic_csvxls_so
"""
return parser_name == 'generic_csvxls_so'
def get_st_line_vals(self, line, *args, **kwargs):
"""This method must return a dict of vals that can be passed to create
"""
This method must return a dict of vals that can be passed to create
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
own way of recording the lines.
@@ -84,7 +87,9 @@ class GenericFileParser(FileParser):
}
def _post(self, *args, **kwargs):
"""Compute the commission from value of each line"""
"""
Compute the commission from value of each line
"""
res = super(GenericFileParser, self)._post(*args, **kwargs)
val = 0.0
for row in self.result_row_list:

View File

@@ -21,16 +21,18 @@
import base64
import csv
def UnicodeDictReader(utf8_data, **kwargs):
csv_reader = csv.DictReader(utf8_data, **kwargs)
for row in csv_reader:
yield dict([(key, unicode(value, 'utf-8')) for key, value in row.iteritems()])
class BankStatementImportParser(object):
"""Abstract class for defining parser for different files and
format to import in a bank statement"""
"""
Generic abstract class for defining parser for different files and
format to import in a bank statement. Inherit from it to create your
own. If your file is a .csv or .xls format, you should consider inheirt
from the FileParser instead.
"""
def __init__(self, parser_name, *args, **kwargs):
# The name of the parser as it will be called
@@ -46,48 +48,73 @@ class BankStatementImportParser(object):
@classmethod
def parser_for(cls, parser_name):
"""
Override this method for every new parser, so that new_bank_statement_parser can
return the good class from his name.
"""
return False
def _decode_64b_stream(self):
"""
Decode self.filebuffer in base 64 and override it
"""
self.filebuffer = base64.b64decode(self.filebuffer)
return True
def _format(self, decode_base_64=True, **kwargs):
"""
Decode into base 64 if asked and Format the given filebuffer by calling
_custom_format method.
"""
if decode_base_64:
self._decode_64b_stream()
self._custom_format(kwargs)
return True
def _custom_format(self, *args, **kwargs):
"""Implement a method to convert format, encoding and so on before
starting to work on datas."""
"""
Implement a method in your parser to convert format, encoding and so on before
starting to work on datas. Work on self.filebuffer
"""
return NotImplementedError
def _pre(self, *args, **kwargs):
"""Implement a method to make a pre-treatment on datas before parsing
them, like concatenate stuff, and so..."""
return NotImplementedError
def _validate(self, *args, **kwargs):
"""Implement a method to validate the self.result_row_list instance
property and raise an error if not valid."""
return NotImplementedError
def _post(self, *args, **kwargs):
"""Implement a method to make some last changes on the result of parsing
the datas, like converting dates, computing commission, ... """
"""
Implement a method in your parser to make a pre-treatment on datas before parsing
them, like concatenate stuff, and so... Work on self.filebuffer
"""
return NotImplementedError
def _parse(self, *args, **kwargs):
"""Implement a method to save the result of parsing self.filebuffer
in self.result_row_list instance property. Put the commission global
amount in the self.commission_global_amount one."""
"""
Implement a method in your parser to save the result of parsing self.filebuffer
in self.result_row_list instance property.
"""
return NotImplementedError
def _validate(self, *args, **kwargs):
"""
Implement a method in your parser to validate the self.result_row_list instance
property and raise an error if not valid.
"""
return NotImplementedError
def _post(self, *args, **kwargs):
"""
Implement a method in your parser to make some last changes on the result of parsing
the datas, like converting dates, computing commission, ...
Work on self.result_row_list and put the commission global amount if any
in the self.commission_global_amount one.
"""
return NotImplementedError
def get_st_line_vals(self, line, *args, **kwargs):
"""This method must return a dict of vals that can be passed to create
method of statement line in order to record it. It is the responsibility
"""
Implement a method in your parser that must return a dict of vals that can be
passed to create 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
own way of recording the lines.
:param: line: a dict of vals that represent a line of result_row_list
@@ -103,16 +130,18 @@ class BankStatementImportParser(object):
return NotImplementedError
def get_st_line_commision(self, *args, **kwargs):
"""This is called by the importation method to create the commission line in
"""
This is called by the importation method to create the commission line in
the bank statement. We will always create one line for the commission in the
bank statement, but it could be computated from a value of each line, or given
in a single line for the whole file.
return: float of the whole commission
return: float of the whole commission (self.commission_global_amount)
"""
return self.commission_global_amount
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
that need to be define for each parser.
Return:
@@ -172,6 +201,11 @@ def itersubclasses(cls, _seen=None):
yield sub
def new_bank_statement_parser(parser_name, *args, **kwargs):
"""
Return an instance of the good parser class base on the providen name
:param char: parser_name
:return: class instance of parser_name providen.
"""
for cls in itersubclasses(BankStatementImportParser):
if cls.parser_for(parser_name):
return cls(parser_name, *args, **kwargs)

View File

@@ -53,6 +53,14 @@ class AccountStatementProfil(Model):
}
def write_logs_after_import(self, cr, uid, ids, statement_id, num_lines, context):
"""
Write the log in the logger + in the log field of the profile to report the user about
what has been done.
:param int/long statement_id: ID of the concerned account.bank.statement
:param int/long num_lines: Number of line that have been parsed
:return: True
"""
if type(ids) is int:
ids = [ids]
for id in ids:
@@ -69,18 +77,17 @@ class AccountStatementProfil(Model):
def prepare_global_commission_line_vals(self, cr, uid, parser,
result_row_list, profile, statement_id, context):
"""Prepare the global commission line if there is one. The global commission is computed by
summing the commission column of each row. Feel free to override the methode to compute
"""
Prepare the global commission line if there is one. The global commission is computed by
by calling the get_st_line_commision of the parser. Feel free to override the methode to compute
your own commission line from the result_row_list.
:param: cr: The DB Cursor
:param: uid: int/long ID of the current user in the system
:param: browse_record of the current parser
:param: result_row_list: [{'key':value}]
:param: profile: browserecord of account.statement.profile
:param: statement_id : int/long of the current importing statement ID
:param: context: global context
return: dict of vals that will be passed to create method of statement line.
"""
"""
comm_values = False
if parser.get_st_line_commision():
partner_id = profile.partner_id and profile.partner_id.id or False
@@ -104,9 +111,18 @@ class AccountStatementProfil(Model):
def prepare_statetement_lines_vals(self, cursor, uid, parser_vals,
account_payable, account_receivable, statement_id, context):
"""Hook to build the values of a line from the parser returned values. At
"""
Hook to build the values of a line from the parser returned values. At
least it fullfill the statement_id and account_id. Overide it to add your
own completion if needed. """
own completion if needed.
:param dict of vals from parser for account.bank.statement.line (called by
parser.get_st_line_vals)
:param int/long account_payable: ID of the receivable account to use
:param int/long account_receivable: ID of the payable account to use
: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.
"""
statement_obj = self.pool.get('account.bank.statement')
values = parser_vals
values['statement_id']= statement_id
@@ -120,10 +136,17 @@ class AccountStatementProfil(Model):
return values
def statement_import(self, cursor, uid, ids, profile_id, file_stream, ftype="csv", context=None):
"""Create a bank statement with the given profile and parser. It will fullfill the bank statement
with the values of the file providen, but will not complete data (like finding the partner, or
the right account). This will be done in a second step with the completion rules.
It will also create the commission line if it apply.
"""
Create a bank statement with the given profile and parser. It will fullfill the bank statement
with the values of the file providen, but will not complete data (like finding the partner, or
the right account). This will be done in a second step with the completion rules.
It will also create the commission line if it apply and record the providen file as
an attachement of the bank statement.
:param int/long profile_id: ID of the profile used to import the file
:param filebuffer file_stream: binary of the providen file
:param char: ftype represent the file exstension (csv by default)
:return: ID of the created account.bank.statemênt
"""
context = context or {}
statement_obj = self.pool.get('account.bank.statement')
@@ -196,8 +219,11 @@ class AccountStatementProfil(Model):
class AccountStatementLine(Model):
"""Add sparse field on the statement line to allow to store all the
bank infos that are given by an office."""
"""
Add sparse field on the statement line to allow to store all the
bank infos that are given by an office. In this basic sample case
it concern only commission_amount.
"""
_inherit = "account.bank.statement.line"
_columns={

View File

@@ -28,8 +28,6 @@ from tools.translate import _
import os
class CreditPartnerStatementImporter(osv.osv_memory):
"""Import Credit statement"""
_name = "credit.statement.import"
def default_get(self, cr, uid, fields, context=None):
@@ -75,13 +73,6 @@ class CreditPartnerStatementImporter(osv.osv_memory):
),
}
# _defaults = _get_default_values
# {
# 'profile_id': _get_profile,
# }
def onchange_profile_id(self, cr, uid, ids, profile_id, context=None):
res={}
if profile_id:

View File

@@ -27,33 +27,38 @@
'complexity': 'normal', #easy, normal, expert
'depends': ['account'],
'description': """
The goal of this module is to improve the basic bank statement, help dealing with huge volume of
reconciliation through payment offices like Paypal, Lazer, Visa, Amazon and so on.
The goal of this module is to improve the basic bank statement, by adding various new features,
and help dealing with huge volume of reconciliation through payment offices (like Paypal, Lazer,
Visa, Amazon,...).
It will be mostly used for E-commerce but can be usefule for other use cases as it introduce a
notion of profil on the bank statement to have more control on the generated entries. It will
be the base for all new features developped to improve the reconciliation process.
be the base for all new features developped to improve the reconciliation process (see our other
set of modules, like : account_statement_base_completion, account_statement_base_import,
account_advanced_reconcile).
Features:
1) This module improves the bank statement that allow and you to define profile for each
Office or Bank that will generate the entries based on some criteria. You can setup:
1) This module improves the bank statement in the way it allows you to define profiles (for each
Office or Bank). The bank statement will then generate the entries based on some criteria chosen
in the selected profile. You can setup on the profile:
- Account commission and partner relation
- Can force an account for the reconciliation
- The journal to use
- Choose to use balance check or not
- Analytic account for commission
- Account commission and Analytic account for commission
- Partner concerned by the profile (used in commission and optionaly on generated credit move)
- Can force a to use a specifi credit account (instead of the receivalble/payable default one)
- Force Partner on the counter-part move (e.g. 100.- debit, Partner: M.Martin; 100.- credit, Partner: HSBC)
2) Adds a report on bank statement that can be used for Checks
2) Adds a report on bank statement that can be used for checks remittance
3) When an error occurs in a bank statement confirmation, it will go through all line anyway and summarize
all the erronous line in a same popup instead of raising and crashing on every step.
all the erronous line in a same popup instead of raising and crashing on every step.
4) Remove the period on the bank statement, and compute it for each line based on their date instead.
5) Cancelling a bank statement is much more easy and will cancel all related entries, unreconcile them,
and finally delete it.
and finally delete them.
6) Add the ID in entries view so that you can easily filter on a statement ID to reconcile all related
entries at once (e.g. one statement (ID 100) for paypal on an intermediate account, and then another for

View File

@@ -26,8 +26,10 @@ class account_move(Model):
_inherit='account.move'
def unlink(self, cr, uid, ids, context=None):
"""Delete the reconciliation when we delete the moves. This
allow an easier way of cancelling the bank statement."""
"""
Delete the reconciliation when we delete the moves. This
allow an easier way of cancelling the bank statement.
"""
for move in self.browse(cr, uid, ids, context=context):
for move_line in move.line_id:
if move_line.reconcile_id:

View File

@@ -5,10 +5,11 @@
<report auto="False"
id="report_bank_statement_webkit"
model="account.bank.statement"
name="account_statement_import.report_bank_statement_webkit"
file="account_statement_import/report/bank_statement_report.mako"
name="bank_statement_webkit"
file="account_statement_ext/report/bank_statement_report.mako"
string="Bank Statement"
report_type="webkit"
webkit_header="account_statement_ext.bank_statement_landscape_header"
header="1"/>
<record id="action_print_bank_statement_webkit" model="ir.values">

View File

@@ -6,61 +6,61 @@
</style>
</head>
<body>
<%!
def amount(text):
return text.replace('-', '&#8209;') # replace by a non-breaking hyphen (it will not word-wrap between hyphen and numbers)
%>
<%!
def amount(text):
return text.replace('-', '&#8209;') # replace by a non-breaking hyphen (it will not word-wrap between hyphen and numbers)
%>
<%setLang(user.context_lang)%>
%for statement in objects:
<%setLang(user.context_lang)%>
%for statement in objects:
<div class="act_as_table data_table">
<div class="act_as_row labels">
<div class="act_as_cell">${_('Bordereau')}</div>
<div class="act_as_cell">${_('Date')}</div>
<div class="act_as_table data_table">
<div class="act_as_row labels">
<div class="act_as_cell">${_('Bordereau')}</div>
<div class="act_as_cell">${_('Date')}</div>
</div>
<div class="act_as_row">
<div class="act_as_cell">${ statement.name }</div>
<div class="act_as_cell">${ formatLang(statement.date,date=True) }</div>
</div>
</div>
<div class="act_as_row">
<div class="act_as_cell">${ statement.name }</div>
<div class="act_as_cell">${ formatLang(statement.date,date=True) }</div>
</div>
</div>
<!-- we use div with css instead of table for tabular data because div do not cut rows at half at page breaks -->
<div class="act_as_table list_table" style="margin-top: 10px;">
<div class="act_as_thead">
<div class="act_as_row labels">
## date
<div class="act_as_cell first_column" style="width: 100px;">${_('Reference')}</div>
## period
<div class="act_as_cell" style="width: 175px;">${_('Partenaire')}</div>
## move
<div class="act_as_cell" style="width: 60px;">${_('Montant')}</div>
## journal
<!-- we use div with css instead of table for tabular data because div do not cut rows at half at page breaks -->
<div class="act_as_table list_table" style="margin-top: 10px;">
<div class="act_as_thead">
<div class="act_as_row labels">
## date
<div class="act_as_cell first_column" style="width: 100px;">${_('Reference')}</div>
## period
<div class="act_as_cell" style="width: 175px;">${_('Partenaire')}</div>
## move
<div class="act_as_cell" style="width: 60px;">${_('Montant')}</div>
## journal
</div>
</div>
<% sum_statement = 0.0 %>
%for statementline in statement.line_ids:
<div class="act_as_tbody">
## curency code
<div class="act_as_cell">${statementline.name or '' }</div>
## currency balance
<div class="act_as_cell">${statementline.partner_id.name or '' }</div>
## currency balance cumulated
<div class="act_as_cell amount">${formatLang(statementline.amount) | amount }</div>
<% sum_statement += statementline.amount %>
</div>
%endfor
<div class="act_as_tbody">
## curency code
<div class="act_as_cell"></div>
## currency balance
<div class="act_as_cell">Total</div>
## currency balance cumulated
<div class="act_as_cell amount_total">${formatLang(sum_statement) }</div>
</div>
</div>
<% sum_statement = 0.0 %>
%for statementline in statement.line_ids:
<div class="act_as_tbody">
## curency code
<div class="act_as_cell">${statementline.name or '' }</div>
## currency balance
<div class="act_as_cell">${statementline.partner_id.name or '' }</div>
## currency balance cumulated
<div class="act_as_cell amount">${formatLang(statementline.amount) | amount }</div>
<% sum_statement += statementline.amount %>
</div>
%endfor
<div class="act_as_tbody">
## curency code
<div class="act_as_cell"></div>
## currency balance
<div class="act_as_cell">Total</div>
## currency balance cumulated
<div class="act_as_cell amount_total">${formatLang(sum_statement) }</div>
</div>
</div>
%endfor
</div>
%endfor

View File

@@ -27,8 +27,6 @@ import pooler
from operator import add, itemgetter
from itertools import groupby
from datetime import datetime
#from common_report_header_webkit import CommonReportHeaderWebkit
from report_webkit import webkit_report
class BankStatementWebkit(report_sxw.rml_parse):
@@ -66,7 +64,7 @@ class BankStatementWebkit(report_sxw.rml_parse):
statement_lines = statement_obj.browse(self.cr,self.uid,statement_line_ids)
return statement_lines
webkit_report.WebKitParser('report.report_bank_statement_webkit',
webkit_report.WebKitParser('report.bank_statement_webkit',
'account.bank.statement',
'addons/account_statement_import/report/bank_statement_report.mako',
'addons/account_statement_ext/report/bank_statement_report.mako',
parser=BankStatementWebkit)

View File

@@ -27,9 +27,11 @@ from openerp.osv.orm import Model, fields
from openerp.osv import fields, osv
class AccountStatementProfil(Model):
"""A Profile will contain all infos related to the type of
bank statement, and related generated entries. It define the
journal to use, the partner and commision account and so on."""
"""
A Profile will contain all infos related to the type of
bank statement, and related generated entries. It defines the
journal to use, the partner and commision account and so on.
"""
_name = "account.statement.profil"
_description = "Statement Profil"
@@ -37,8 +39,9 @@ class AccountStatementProfil(Model):
'name': fields.char('Name', size=128, required=True),
'partner_id': fields.many2one('res.partner',
'Bank/Payment Office partner',
help="Put a partner if you want to have it on the commission move (and optionaly\
on the counterpart of the intermediate/banking move if you tic the corresponding checkbox)."),
help="Put a partner if you want to have it on the commission move \
(and optionaly on the counterpart of the intermediate/banking move \
if you tic the corresponding checkbox)."),
'journal_id': fields.many2one('account.journal',
'Financial journal to use for transaction',
required=True),
@@ -57,8 +60,9 @@ class AccountStatementProfil(Model):
in the counterpart of the intermediat/banking move."
),
'balance_check': fields.boolean('Balance check',
help="Tic that box if you want OpenERP to control the start/end balance\
before confirming a bank statement. If don't ticked, no balance control will be done."
help="Tic that box if you want OpenERP to control the start/end \
balance before confirming a bank statement. If don't ticked, no \
balance control will be done."
),
'bank_statement_prefix': fields.char('Bank Statement Prefix', size=32),
'bank_statement_ids': fields.one2many('account.bank.statement', 'profile_id', 'Bank Statement Imported'),
@@ -66,8 +70,6 @@ class AccountStatementProfil(Model):
}
_defaults = {}
def _check_partner(self, cr, uid, ids, context=None):
obj = self.browse(cr, uid, ids[0], context=context)
if obj.partner_id == False and obj.force_partner_on_bank:
@@ -80,14 +82,16 @@ class AccountStatementProfil(Model):
class AccountBankSatement(Model):
"""We improve the bank statement class mostly for :
"""
We improve the bank statement class mostly for :
- Removing the period and compute it from the date of each line.
- Allow to remove the balance check depending on the chosen profil
- Report errors on confirmation all at once instead of crashing onr by one
- Add a profil notion that can change the generated entries on statement
confirmation.
For this, we'll had to override quite some long method and we'll need to maintain
them up to date. Changes are point up by '#Chg' comment."""
confirmation.
For this, we had to override quite some long method and we'll need to maintain
them up to date. Changes are point up by '#Chg' comment.
"""
_inherit = "account.bank.statement"
@@ -123,7 +127,7 @@ class AccountBankSatement(Model):
def create(self, cr, uid, vals, context=None):
"""Need to pass the journal_id in vals anytime because of account.cash.statement
that need it."""
need it."""
if 'profile_id' in vals:
profil_obj = self.pool.get('account.statement.profil')
profile = profil_obj.browse(cr,uid,vals['profile_id'],context)
@@ -131,9 +135,9 @@ class AccountBankSatement(Model):
return super(AccountBankSatement, self).create(cr, uid, vals, context=context)
def _get_period(self, cursor, uid, date, context=None):
'''
"""
Find matching period for date, used in the statement line creation.
'''
"""
period_obj = self.pool.get('account.period')
periods = period_obj.find(cursor, uid, dt=date, context=context)
return periods and periods[0] or False
@@ -153,14 +157,16 @@ class AccountBankSatement(Model):
return False
return True
# Redefine the constraint, or it still refer to the original method
_constraints = [
(_check_company_id, 'The journal and period chosen have to belong to the same company.', ['journal_id','period_id']),
]
def button_cancel(self, cr, uid, ids, context={}):
"""We cancel the related move, delete them and finally put the
statement in draft state."""
"""
We cancel the related move, delete them and finally put the
statement in draft state. So no need to unreconcile all entries,
then unpost them, then finaly cancel the bank statement.
"""
done = []
for st in self.browse(cr, uid, ids, context=context):
if st.state=='draft':
@@ -175,18 +181,22 @@ class AccountBankSatement(Model):
return True
def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id, st_line_number, context=None):
"""Override a large portion of the code to compute the periode for each line instead of
"""
Override a large portion of the code to compute the periode for each line instead of
taking the period of the whole statement.
Remove the entry posting on generated account moves.
Point to account.bank.statement.line instead of account.bank.statement.line.
In Treasury Statement, unlike the Bank statement, we will change the move line generated from the
lines depending on the profil (config import):
We change the move line generated from the lines depending on the profil:
- If receivable_account_id is set, we'll use it instead of the "partner" one
- If partner_id is set, we'll us it for the commission (when imported throufh the wizard)
- If partner_id is set and force_partner_on_bank is ticked, we'll let the partner of each line
for the debit line, but we'll change it on the credit move line for the choosen partner_id
=> This will ease the reconciliation process with the bank as the partner will match the bank
statement line
:param int/long: st_line_id: account.bank.statement.line ID
:param int/long: company_currency_id: res.currency ID
:param char: st_line_number: that will be used as the name of the generated account move
:return: int/long: ID of the created account.move
"""
if context is None:
context = {}
@@ -300,13 +310,22 @@ class AccountBankSatement(Model):
account_move_obj.post(cr, uid, [move_id], context=context)
return move_id
def _get_st_number_period_profil(self, cr, uid, date, profile_id, journal_sequence_id):
"""Retrieve the name of bank statement from sequence, according to the period
corresponding to the date passed in args. Add a prefix if set in the profil."""
def _get_st_number_period_profil(self, cr, uid, date, profile_id):
"""
Retrieve the name of bank statement from sequence, according to the period
corresponding to the date passed in args. Add a prefix if set in the profil.
:param: date: date of the statement used to compute the right period
:param: int/long: profile_id: the account.statement.profil ID from which to take the
bank_statement_prefix for the name
:return: char: name of the bank statement (st_number)
"""
year = self.pool.get('account.period').browse(cr, uid, self._get_period(cr, uid, date)).fiscalyear_id.id
profile = self.pool.get('account.statement.profil').browse(cr,uid, profile_id)
c = {'fiscalyear_id': year}
obj_seq = self.pool.get('ir.sequence')
journal_sequence_id = profile.journal_id.sequence_id and profile.journal_id.sequence_id.id or False
if journal_sequence_id:
st_number = obj_seq.next_by_id(cr, uid, journal_sequence_id, context=c)
else:
@@ -316,11 +335,15 @@ class AccountBankSatement(Model):
return st_number
def button_confirm_bank(self, cr, uid, ids, context=None):
"""Completely override the method in order to have
an error message which displays all the messages
instead of having them pop one by one.
We have to copy paste a big block of code, changing the error
stack + managing period from date."""
"""
Completely override the method in order to have
an error message which displays all the messages
instead of having them pop one by one.
We have to copy paste a big block of code, changing the error
stack + managing period from date.
TODO: Log the error in a bank statement field instead of using a popup !
"""
# obj_seq = self.pool.get('irerrors_stack.sequence')
if context is None:
context = {}
@@ -341,8 +364,7 @@ class AccountBankSatement(Model):
st_number = st.name
else:
# Begin Changes
seq_id = st.journal_id.sequence_id and st.journal_id.sequence_id.id or False
st_number = self._get_st_number_period_profil(cr, uid, st.date, st.profile_id.id, seq_id)
st_number = self._get_st_number_period_profil(cr, uid, st.date, st.profile_id.id)
# End Changes
for line in st.move_line_ids:
if line.state <> 'valid':
@@ -379,10 +401,17 @@ class AccountBankSatement(Model):
def get_account_for_counterpart(self, cursor, uid,
amount, account_receivable, account_payable):
"""Give the amount, payable and receivable account (that can be found using
get_default_pay_receiv_accounts).
Return the default account to be used by statement line as the counterpart
of the journal account depending on the amount"""
"""
Give the amount, payable and receivable account (that can be found using
get_default_pay_receiv_accounts method) and receive the one to use. This method
should be use when there is no other way to know which one to take.
:param float: amount of the line
:param int/long: account_receivable the receivable account
:param int/long: account_payable the payable account
:return: int/long :the default account to be used by statement line as the counterpart
of the journal account depending on the amount.
"""
account_id = False
if amount >= 0:
account_id = account_receivable
@@ -396,8 +425,14 @@ class AccountBankSatement(Model):
return account_id
def get_default_pay_receiv_accounts(self, cursor, uid, context=None):
"""We try to determine default payable/receivable accounts to be used as counterpart
from the company default propoerty.
"""
We try to determine default payable/receivable accounts to be used as counterpart
from the company default propoerty. This is to be used if there is no otherway to
find the good one, or to find a default value that will be overriden by a completion
method (rules of account_statement_base_completion) afterwards.
:return: tuple of int/long ID that give account_receivable, account_payable based on
company default.
"""
account_receivable = False
account_payable = False
@@ -430,8 +465,14 @@ class AccountBankSatement(Model):
return account_receivable, account_payable
def balance_check(self, cr, uid, st_id, journal_type='bank', context=None):
"""Balance check depends on the profil. If no check for this profil is required,
return True"""
"""
Balance check depends on the profil. If no check for this profil is required,
return True and do nothing, otherwise call super.
:param int/long st_id: ID of the concerned account.bank.statement
:param char: journal_type that concern the bank statement
:return: True
"""
st = self.browse(cr, uid, st_id, context=context)
if st.balance_check:
return super(AccountBankSatement,self).balance_check(cr, uid, st_id, journal_type, context)
@@ -439,6 +480,12 @@ class AccountBankSatement(Model):
return True
def onchange_imp_config_id(self, cr, uid, ids, profile_id, context=None):
"""
Compute values on the change of the profile.
:param: int/long: profile_id that changed
:return dict of dict with key = name of the field
"""
if not profile_id:
return {}
import_config = self.pool.get("account.statement.profil").browse(cr,uid,profile_id)
@@ -452,9 +499,18 @@ class AccountBankSatement(Model):
class AccountBankSatementLine(Model):
"""
Override to compute the period from the date of the line, add a method to retrieve
the values for a line from the profile. Override the on_change method to take care of
the profile when fullfilling the bank statement manually. Set the reference to 64
Char long instead 32.
"""
_inherit = "account.bank.statement.line"
def _get_period(self, cursor, user, context=None):
"""
Return a period from a given date in the context.
"""
date = context.get('date', None)
periods = self.pool.get('account.period').find(cursor, user, dt=date)
return periods and periods[0] or False
@@ -469,25 +525,28 @@ class AccountBankSatementLine(Model):
}
def get_values_for_line(self, cr, uid, profile_id = False, partner_id = False, line_type = False, amount = False, context = None):
"""Return the account_id to be used in the line of a bank statement. It'll base the result as follow:
"""
Return the account_id to be used in the line of a bank statement. It'll base the result as follow:
- If a receivable_account_id is set in the profil, return this value and type = general
- Elif line_type is given, take the partner receivable/payable property (payable if type= supplier, receivable
otherwise)
otherwise)
- Elif amount is given, take the partner receivable/payable property (receivable if amount >= 0.0,
payable otherwise). In that case, we also fullfill the type (receivable = customer, payable = supplier)
so it is easier for the accountant to know why the receivable/payable has been chosen
payable otherwise). In that case, we also fullfill the type (receivable = customer, payable = supplier)
so it is easier for the accountant to know why the receivable/payable has been chosen
- Then, if no partner are given we look and take the property from the company so we always give a value
for account_id. Note that in that case, we return the receivable one.
:params: profile_id: int/long
:params: line_type: String value like 'general', 'supplier', 'customer'
:params: amount: float
:return A dict of value that can be passed directly to the write method of
the statement line:
{'partner_id': value,
'account_id' : value,
'type' : value,
...
}
for account_id. Note that in that case, we return the receivable one.
:param int/long profile_id of the related bank statement
:param int/long partner_id of the line
:param char line_type: a value from: 'general', 'supplier', 'customer'
:param float: amount of the line
:return: A dict of value that can be passed directly to the write method of
the statement line:
{'partner_id': value,
'account_id' : value,
'type' : value,
...
}
"""
if context is None:
context = {}
@@ -530,8 +589,10 @@ class AccountBankSatementLine(Model):
def onchange_partner_id(self, cr, uid, ids, partner_id, profile_id, context=None):
"""Override of the basic method as we need to pass the profile_id in the on_change_type
call."""
"""
Override of the basic method as we need to pass the profile_id in the on_change_type
call.
"""
obj_partner = self.pool.get('res.partner')
if context is None:
context = {}
@@ -553,8 +614,10 @@ class AccountBankSatementLine(Model):
return {'value': {'type': type}}
def onchange_type(self, cr, uid, line_id, partner_id, type, profile_id, context=None):
"""Keep the same features as in standard and call super. If an account is returned,
call the method to compute line values."""
"""
Keep the same features as in standard and call super. If an account is returned,
call the method to compute line values.
"""
if context is None:
context = {}
res = super(AccountBankSatementLine,self).onchange_type(cr, uid, line_id, partner_id, type, context)

View File

@@ -35,7 +35,6 @@
So this way, the reconciliation always happend on the SO name stored in ref.
""",
'website': 'http://www.camptocamp.com',
'init_xml': [],

View File

@@ -40,12 +40,13 @@ class AccountStatementCompletionRule(Model):
'function_to_call': fields.selection(_get_functions, 'Method'),
}
#TODO : Ensure we match only one partner => Otherwise raise an error !!!
def get_from_transaction_id_and_so(self, cr, uid, line_id, context=None):
"""Match the partner based on the transaction ID field of the SO.
"""
Match the partner based on the transaction ID field of the SO.
Then, call the generic st_line method to complete other values.
In that case, we always fullfill the reference of the line with the SO name.
Return:
:param int/long line_id: ID of the concerned account.bank.statement.line
:return:
A dict of value that can be passed directly to the write method of
the statement line or {}
{'partner_id': value,
@@ -63,7 +64,7 @@ class AccountStatementCompletionRule(Model):
res['partner_id'] = so.partner_id.id
res['ref'] = so.name
elif so_id and len(so_id) > 1:
raise Exception(_('Line named "%s" was matched by more than one partner.')%(st_line.name,st_line.id))
raise ErrorTooManyPartner(_('Line named "%s" was matched by more than one partner.')%(st_line.name,st_line.id))
if so_id:
st_vals = st_obj.get_values_for_line(cr, uid, profile_id = st_line.statement_id.profile_id.id,
partner_id = res.get('partner_id',False), line_type = st_line.type, st_line.amount, context)

View File

@@ -32,11 +32,11 @@
This module allow you to import your bank transactions with a standard .csv or .xls file
(you'll find it in the 'datas' folder). It'll respect the profil
you'll choose (providen by the accouhnt_statement_ext module) to generate the entries.
you'll choose (providen by the account_statement_ext module) to generate the entries.
This module can handle a commission taken by the payment office and has the following format:
* transactionID : the transaction ID given by the bank/office. It'll be used as reference
* transaction_id : the transaction ID given by the bank/office. It'll be used as reference
in the generated entries and will be useful for reconciliation process
* date : date of the payment
* amount : amount paid in the currency of the journal used in the importation profil

View File

@@ -31,10 +31,10 @@ except:
raise Exception(_('Please install python lib xlrd'))
class TransactionIDFileParser(FileParser):
"""TransactionID parser that use a define format in csv or xls to import
bank statement."""
"""
TransactionID parser that use a define format in csv or xls to import
bank statement.
"""
def __init__(self, parse_name, ftype='csv'):
convertion_dict = {
@@ -46,16 +46,19 @@ class TransactionIDFileParser(FileParser):
}
# Order of cols does not matter but first row of the file has to be header
keys_to_validate = ['transaction_id', 'label', 'date', 'amount', 'commission_amount']
super(TransactionIDFileParser,self).__init__(parse_name, keys_to_validate=keys_to_validate, ftype=ftype, convertion_dict=convertion_dict)
@classmethod
def parser_for(cls, parser_name):
"""
Used by the new_bank_statement_parser class factory. Return true if
the providen name is generic_csvxls_transaction
"""
return parser_name == 'generic_csvxls_transaction'
def get_st_line_vals(self, line, *args, **kwargs):
"""This method must return a dict of vals that can be passed to create
"""T
his method must return a dict of vals that can be passed to create
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
own way of recording the lines.
@@ -84,7 +87,9 @@ class TransactionIDFileParser(FileParser):
}
def _post(self, *args, **kwargs):
"""Compute the commission from value of each line"""
"""
Compute the commission from value of each line
"""
res = super(GenericFileParser, self)._post(*args, **kwargs)
val = 0.0
for row in self.result_row_list: