From 52e490104f19310c784f62b38d8c6588716f4cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Grand-Guillaume?= Date: Wed, 20 Jun 2012 16:01:43 +0200 Subject: [PATCH] [DOC] Comment the whole code of the bank statement improvement modules suite. (lp:c2c-financial-addons/6.1 rev 24.1.24) --- .../__openerp__.py | 8 +- account_statement_base_completion/partner.py | 59 +----- .../statement.py | 148 ++++++++------ .../statement_view.xml | 5 +- account_statement_base_import/__openerp__.py | 19 +- .../parser/__init__.py | 2 +- .../parser/file_parser.py | 75 +++++-- .../parser/generic_file_parser.py | 21 +- .../parser/parser.py | 78 +++++--- account_statement_base_import/statement.py | 52 +++-- .../wizard/import_statement.py | 9 - account_statement_ext/__openerp__.py | 27 +-- account_statement_ext/account.py | 6 +- account_statement_ext/report.xml | 5 +- .../report/bank_statement_report.mako | 100 +++++----- .../report/bank_statement_report.py | 6 +- account_statement_ext/statement.py | 185 ++++++++++++------ .../__openerp__.py | 1 - .../statement.py | 9 +- .../__openerp__.py | 4 +- .../parser/transactionid_file_parser.py | 21 +- 21 files changed, 499 insertions(+), 341 deletions(-) diff --git a/account_statement_base_completion/__openerp__.py b/account_statement_base_completion/__openerp__.py index b8fc3f56..d9f61447 100644 --- a/account_statement_base_completion/__openerp__.py +++ b/account_statement_base_completion/__openerp__.py @@ -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', diff --git a/account_statement_base_completion/partner.py b/account_statement_base_completion/partner.py index 1c18cf3d..978de421 100644 --- a/account_statement_base_completion/partner.py +++ b/account_statement_base_completion/partner.py @@ -1,7 +1,8 @@ # -*- encoding: utf-8 -*- ################################################################################# # # -# Copyright (C) 2011 Akretion Sébastien BEAU # +# 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() diff --git a/account_statement_base_completion/statement.py b/account_statement_base_completion/statement.py index 4a6b149c..551707ac 100644 --- a/account_statement_base_completion/statement.py +++ b/account_statement_base_completion/statement.py @@ -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 ignore line for which already_completed is ticked! + 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. + + :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 diff --git a/account_statement_base_completion/statement_view.xml b/account_statement_base_completion/statement_view.xml index e0d10538..4c1fb2b3 100644 --- a/account_statement_base_completion/statement_view.xml +++ b/account_statement_base_completion/statement_view.xml @@ -80,8 +80,9 @@ tree,form - - + diff --git a/account_statement_base_import/__openerp__.py b/account_statement_base_import/__openerp__.py index 4ca05299..176ff813 100644 --- a/account_statement_base_import/__openerp__.py +++ b/account_statement_base_import/__openerp__.py @@ -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: @@ -50,7 +45,11 @@ * commission_amount : amount of the comission for each line * 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', diff --git a/account_statement_base_import/parser/__init__.py b/account_statement_base_import/parser/__init__.py index 262ce699..40e8d043 100644 --- a/account_statement_base_import/parser/__init__.py +++ b/account_statement_base_import/parser/__init__.py @@ -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 diff --git a/account_statement_base_import/parser/file_parser.py b/account_statement_base_import/parser/file_parser.py index 02d91deb..0edb74a3 100644 --- a/account_statement_base_import/parser/file_parser.py +++ b/account_statement_base_import/parser/file_parser.py @@ -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 diff --git a/account_statement_base_import/parser/generic_file_parser.py b/account_statement_base_import/parser/generic_file_parser.py index a7be128b..b61ac25a 100644 --- a/account_statement_base_import/parser/generic_file_parser.py +++ b/account_statement_base_import/parser/generic_file_parser.py @@ -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: diff --git a/account_statement_base_import/parser/parser.py b/account_statement_base_import/parser/parser.py index 6c734317..c39098fa 100644 --- a/account_statement_base_import/parser/parser.py +++ b/account_statement_base_import/parser/parser.py @@ -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...""" + """ + 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 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 to validate the self.result_row_list instance - property and raise an error if not valid.""" + """ + 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 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 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 _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.""" - 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) diff --git a/account_statement_base_import/statement.py b/account_statement_base_import/statement.py index 3ef87198..c5978dc7 100644 --- a/account_statement_base_import/statement.py +++ b/account_statement_base_import/statement.py @@ -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={ diff --git a/account_statement_base_import/wizard/import_statement.py b/account_statement_base_import/wizard/import_statement.py index 85053eab..0c76946b 100644 --- a/account_statement_base_import/wizard/import_statement.py +++ b/account_statement_base_import/wizard/import_statement.py @@ -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: diff --git a/account_statement_ext/__openerp__.py b/account_statement_ext/__openerp__.py index d25750bb..4e2a5563 100644 --- a/account_statement_ext/__openerp__.py +++ b/account_statement_ext/__openerp__.py @@ -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 diff --git a/account_statement_ext/account.py b/account_statement_ext/account.py index b72a80c9..701ba042 100644 --- a/account_statement_ext/account.py +++ b/account_statement_ext/account.py @@ -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: diff --git a/account_statement_ext/report.xml b/account_statement_ext/report.xml index 446735e4..7d7cf06f 100644 --- a/account_statement_ext/report.xml +++ b/account_statement_ext/report.xml @@ -5,10 +5,11 @@ diff --git a/account_statement_ext/report/bank_statement_report.mako b/account_statement_ext/report/bank_statement_report.mako index 4feb28dd..ce217f04 100644 --- a/account_statement_ext/report/bank_statement_report.mako +++ b/account_statement_ext/report/bank_statement_report.mako @@ -6,63 +6,63 @@ - <%! - 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: -
-
-
${_('Bordereau')}
-
${_('Date')}
+
+
+
${_('Bordereau')}
+
${_('Date')}
+
+
+
${ statement.name }
+
${ formatLang(statement.date,date=True) }
+
-
-
${ statement.name }
-
${ formatLang(statement.date,date=True) }
-
-
- -
-
-
- ## date -
${_('Reference')}
- ## period -
${_('Partenaire')}
- ## move -
${_('Montant')}
- ## journal + +
+
+
+ ## date +
${_('Reference')}
+ ## period +
${_('Partenaire')}
+ ## move +
${_('Montant')}
+ ## journal +
+
+ <% sum_statement = 0.0 %> + %for statementline in statement.line_ids: +
+ ## curency code +
${statementline.name or '' }
+ ## currency balance +
${statementline.partner_id.name or '' }
+ ## currency balance cumulated +
${formatLang(statementline.amount) | amount }
+ <% sum_statement += statementline.amount %> +
+ %endfor +
+ ## curency code +
+ ## currency balance +
Total
+ ## currency balance cumulated +
${formatLang(sum_statement) }
-
- <% sum_statement = 0.0 %> - %for statementline in statement.line_ids: -
- ## curency code -
${statementline.name or '' }
- ## currency balance -
${statementline.partner_id.name or '' }
- ## currency balance cumulated -
${formatLang(statementline.amount) | amount }
- <% sum_statement += statementline.amount %> -
- %endfor -
- ## curency code -
- ## currency balance -
Total
- ## currency balance cumulated -
${formatLang(sum_statement) }
-
- -
- %endfor +
+ %endfor + diff --git a/account_statement_ext/report/bank_statement_report.py b/account_statement_ext/report/bank_statement_report.py index a06c1d9a..19ba2efe 100644 --- a/account_statement_ext/report/bank_statement_report.py +++ b/account_statement_ext/report/bank_statement_report.py @@ -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) diff --git a/account_statement_ext/statement.py b/account_statement_ext/statement.py index 97b498b8..276ce259 100644 --- a/account_statement_ext/statement.py +++ b/account_statement_ext/statement.py @@ -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,17 +60,16 @@ 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'), } - - _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) diff --git a/account_statement_transactionid_completion/__openerp__.py b/account_statement_transactionid_completion/__openerp__.py index 70933306..caddd6bf 100644 --- a/account_statement_transactionid_completion/__openerp__.py +++ b/account_statement_transactionid_completion/__openerp__.py @@ -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': [], diff --git a/account_statement_transactionid_completion/statement.py b/account_statement_transactionid_completion/statement.py index 8ef15581..4d25d85e 100644 --- a/account_statement_transactionid_completion/statement.py +++ b/account_statement_transactionid_completion/statement.py @@ -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) diff --git a/account_statement_transactionid_import/__openerp__.py b/account_statement_transactionid_import/__openerp__.py index 91343c86..20d7f082 100644 --- a/account_statement_transactionid_import/__openerp__.py +++ b/account_statement_transactionid_import/__openerp__.py @@ -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 diff --git a/account_statement_transactionid_import/parser/transactionid_file_parser.py b/account_statement_transactionid_import/parser/transactionid_file_parser.py index 59f8a5e5..084bb90d 100644 --- a/account_statement_transactionid_import/parser/transactionid_file_parser.py +++ b/account_statement_transactionid_import/parser/transactionid_file_parser.py @@ -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: