mirror of
https://github.com/OCA/account-reconcile.git
synced 2025-01-20 12:27:39 +02:00
[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:
@@ -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',
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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={
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -6,61 +6,61 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<%!
|
||||
def amount(text):
|
||||
return text.replace('-', '‑') # replace by a non-breaking hyphen (it will not word-wrap between hyphen and numbers)
|
||||
%>
|
||||
<%!
|
||||
def amount(text):
|
||||
return text.replace('-', '‑') # 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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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': [],
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user