← Back to team overview

banking-addons-team team mailing list archive

[Merge] lp:~acsone-openerp/banking-addons/bank-statement-reconcile-70 into lp:banking-addons/bank-statement-reconcile-7.0

 

Laurent Mignon (Acsone) has proposed merging lp:~acsone-openerp/banking-addons/bank-statement-reconcile-70 into lp:banking-addons/bank-statement-reconcile-7.0.

Requested reviews:
  Banking Addons Core Editors (banking-addons-team)
Related bugs:
  Bug #1223834 in Banking Addons: "[7.0]  account_statement_base_import -  AccountStatementLine. _insert_lines  the module doesn't handle correctly sparse fields of type 'char' referencing a serialisable field"
  https://bugs.launchpad.net/banking-addons/+bug/1223834

For more details, see:
https://code.launchpad.net/~acsone-openerp/banking-addons/bank-statement-reconcile-70/+merge/185047

Fixes lp:1223834 in case of insert. Batch updates remains error prone. It would be safer to call the update method from the orm for records updating 'complex' fields.

A new completion rule based on the bank account number is also provided by the proposal.

About modules dependencies. The module 'account_statement_base_import' depends of 'account_statement_base_completion' but the file statement.py of 'account_statement_base_completion' at line 513 call the method _update_line defined in 'account_statement_base_import'. Since the 'AccountStatementLine' is defined in both addons, I've the feeling that we can merge the two overrides in 'account_statement_base_completion'. What's your opinion?

Regards,

lmi
-- 
https://code.launchpad.net/~acsone-openerp/banking-addons/bank-statement-reconcile-70/+merge/185047
Your team Banking Addons Core Editors is requested to review the proposed merge of lp:~acsone-openerp/banking-addons/bank-statement-reconcile-70 into lp:banking-addons/bank-statement-reconcile-7.0.
=== added directory 'account_statement_bankaccount_completion'
=== added file 'account_statement_bankaccount_completion/__init__.py'
--- account_statement_bankaccount_completion/__init__.py	1970-01-01 00:00:00 +0000
+++ account_statement_bankaccount_completion/__init__.py	2013-09-11 13:21:05 +0000
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+#
+#
+#    Author: Laurent Mignon
+#    Copyright 2013 'ACSONE SA/NV'
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+import statement

=== added file 'account_statement_bankaccount_completion/__openerp__.py'
--- account_statement_bankaccount_completion/__openerp__.py	1970-01-01 00:00:00 +0000
+++ account_statement_bankaccount_completion/__openerp__.py	2013-09-11 13:21:05 +0000
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+#
+#
+#    Author: Laurent Mignon
+#    Copyright 2013 'ACSONE SA/NV'
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+{'name': "Bank statement completion from bank account number",
+ 'version': '1.0',
+ 'author': 'Laurent Mignon (Acsone)',
+ 'maintainer': 'ACSONE SA/NV',
+ 'category': 'Finance',
+ 'complexity': 'normal',
+ 'depends': [
+     'account_statement_base_completion',
+     # HACK! the account_statement_base_completion need
+     # to depend from account_statement_base_import since it use specific method on the
+     # statement line during completion. (methods are defined in the 
+     # account_statement_base_impor module
+     'account_statement_base_import'
+ ],
+ 'description': """
+  Add a completion method based on the partner bank account number provided by the bank/office.
+
+  Completion will look in the partner with that bank account number to match the partner, 
+  then it will fill in the bank statement line with it to ease the reconciliation.
+
+ """,
+ 'website': 'http://www.acsone.eu',
+ 'init_xml': [],
+ 'update_xml': [
+     "data.xml",
+ ],
+ 'demo_xml': [],
+ 'test': [],
+ 'installable': True,
+ 'images': [],
+ 'auto_install': True,
+ 'license': 'AGPL-3',
+ }

=== added file 'account_statement_bankaccount_completion/data.xml'
--- account_statement_bankaccount_completion/data.xml	1970-01-01 00:00:00 +0000
+++ account_statement_bankaccount_completion/data.xml	2013-09-11 13:21:05 +0000
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+<data noupdate="1">
+
+    <record id="bank_statement_completion_rule_10" model="account.statement.completion.rule">
+         <field name="name">Match from bank account number (Nomal or IBAN))</field>
+         <field name="sequence">10</field>
+         <field name="function_to_call">get_from_bank_account</field>
+     </record>
+    
+</data>
+</openerp>

=== added file 'account_statement_bankaccount_completion/statement.py'
--- account_statement_bankaccount_completion/statement.py	1970-01-01 00:00:00 +0000
+++ account_statement_bankaccount_completion/statement.py	2013-09-11 13:21:05 +0000
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+#
+#
+#    Author: Laurent Mignon
+#    Copyright 2013 'ACSONE SA/NV'
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU Affero General Public License as
+#    published by the Free Software Foundation, either version 3 of the
+#    License, or (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU Affero General Public License for more details.
+#
+#    You should have received a copy of the GNU Affero General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+
+from openerp.tools.translate import _
+from openerp.osv.orm import Model
+from openerp.osv import fields
+from openerp.addons.account_statement_base_completion.statement import ErrorTooManyPartner
+
+
+class AccountStatementCompletionRule(Model):
+
+    """Add a rule based on transaction ID"""
+
+    _inherit = "account.statement.completion.rule"
+
+    def _get_functions(self, cr, uid, context=None):
+        res = super(AccountStatementCompletionRule, self)._get_functions(
+            cr, uid, context=context)
+        res.append(('get_from_bank_account',
+                    'From bank account number (Nomal or IBAN)'))
+        return res
+
+    _columns = {
+        'function_to_call': fields.selection(_get_functions, 'Method'),
+    }
+
+    def get_from_bank_account(self, cr, uid, st_line, context=None):
+        """
+        Match the partner based on the partner account number field
+        Then, call the generic st_line method to complete other values.
+        :param dict st_line: read 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 st_line['partner_acc_number'] == False:
+            return {}
+        st_obj = self.pool.get('account.bank.statement.line')
+        res = {}
+        res_bank_obj = self.pool.get('res.partner.bank')
+        ids = res_bank_obj.search(cr,
+                                  uid,
+                                  [('acc_number', '=', st_line['partner_acc_number'])],
+                                  context=context)
+        if len(ids) > 1:
+            raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more than '
+                                        'one partner.') % (st_line['name'], st_line['ref']))
+        if len(ids) == 1:
+            partner = res_bank_obj.browse(cr, uid, ids[0], context=context).partner_id
+            res['partner_id'] = partner.id
+            st_vals = st_obj.get_values_for_line(cr,
+                                                 uid,
+                                                 profile_id=st_line['profile_id'],
+                                                 master_account_id=st_line['master_account_id'],
+                                                 partner_id=res.get('partner_id', False),
+                                                 line_type=st_line['type'],
+                                                 amount=st_line['amount'] if st_line['amount'] else 0.0,
+                                                 context=context)
+            res.update(st_vals)
+        return res
+
+
+class AccountStatementLine(Model):
+    _inherit = "account.bank.statement.line"
+
+    _columns = {
+        # 'additionnal_bank_fields' : fields.serialized('Additionnal infos from bank', help="Used by completion and import system."),
+        'partner_acc_number': fields.sparse(
+            type='char',
+            string='Account Number',
+            size=64,
+            serialization_field='additionnal_bank_fields',
+            help="Account number of the partner"),
+    }

=== added directory 'account_statement_bankaccount_completion/tests'
=== added file 'account_statement_bankaccount_completion/tests/__init__.py'
--- account_statement_bankaccount_completion/tests/__init__.py	1970-01-01 00:00:00 +0000
+++ account_statement_bankaccount_completion/tests/__init__.py	2013-09-11 13:21:05 +0000
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+#
+#
+# Authors: Laurent Mignon
+# Copyright (c) 2013 Acsone SA/NV (http://www.acsone.eu)
+# All Rights Reserved
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+
+from . import test_bankaccount_completion
+
+checks = [
+    test_bankaccount_completion
+]

=== added file 'account_statement_bankaccount_completion/tests/test_bankaccount_completion.py'
--- account_statement_bankaccount_completion/tests/test_bankaccount_completion.py	1970-01-01 00:00:00 +0000
+++ account_statement_bankaccount_completion/tests/test_bankaccount_completion.py	2013-09-11 13:21:05 +0000
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+#
+#
+# Authors: Laurent Mignon
+# Copyright (c) 2013 Acsone SA/NV (http://www.acsone.eu)
+# All Rights Reserved
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+#
+from openerp.tests import common
+import inspect
+import os
+import base64
+import time
+
+ACC_NUMBER = "BE38733040385372"
+
+
+class bankaccount_completion(common.TransactionCase):
+
+    def prepare(self):
+        self.company_a = self.browse_ref('base.main_company')
+        self.profile_obj = self.registry("account.statement.profile")
+        self.account_bank_statement_obj = self.registry("account.bank.statement")
+        self.account_bank_statement_line_obj = self.registry("account.bank.statement.line")
+        self.completion_rule_id = self.ref('account_statement_bankaccount_completion.bank_statement_completion_rule_10')
+        self.journal_id = self.registry("ir.model.data").get_object_reference(self.cr, self. uid, "account", "bank_journal")[1]
+        self.partner_id = self.ref('base.main_partner')
+        # Create the profile
+        self.account_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "account", "a_recv")[1]
+        self.journal_id = self.registry("ir.model.data").get_object_reference(self.cr, self. uid, "account", "bank_journal")[1]
+        self.profile_id = self.profile_obj.create(self.cr, self.uid, {
+            "name": "TEST",
+            "commission_account_id": self.account_id,
+            "journal_id": self.journal_id,
+            "import_type": 'generic_csvxls_so',
+            "rule_ids": [(6, 0, [self.completion_rule_id])]})
+        # Create the completion rule
+
+        # Create a bank statement
+        self.statement_id = self.account_bank_statement_obj.create(self.cr, self.uid, {
+            "balance_end_real": 0.0,
+            "balance_start": 0.0,
+            "date": time.strftime('%Y-%m-%d'),
+            "journal_id": self.journal_id,
+            "profile_id": self.profile_id
+
+        })
+
+        # Create bank a statement line
+        self.statement_line_id = self.account_bank_statement_line_obj.create(self.cr, self.uid, {
+            'amount': 1000.0,
+            'name': 'EXT001',
+            'ref': 'My ref',
+            'statement_id': self.statement_id,
+            'partner_acc_number': ACC_NUMBER
+        })
+
+        # Add a bank account number to the partner
+        res_bank_obj = self.registry('res.partner.bank')
+        res_bank_id = res_bank_obj.create(self.cr, self.uid, {
+                                          "state": "bank",
+                                          "company_id": self.company_a.id,
+                                          "partner_id": self.partner_id,
+                                          "acc_number": ACC_NUMBER,
+                                          "footer": True,
+                                          "bank_name": "Reserve"
+                                          })
+
+    def test_OO(self):
+        """Test complete partner_id from bank account number
+        
+        Test the automatic completion of the partner_id based on the account number associated to the
+        statement line
+        """
+        self.prepare()
+        statement_line = self.account_bank_statement_line_obj.browse(self.cr, self.uid, self.statement_line_id)
+        # before import, the
+        self.assertFalse(statement_line.partner_id, "Partner_id must be blank before completion")
+        statement_obj = self.account_bank_statement_obj.browse(self.cr, self.uid, self.statement_id)
+        statement_obj.button_auto_completion()
+        statement_line = self.account_bank_statement_line_obj.browse(self.cr, self.uid, self.statement_line_id)
+        self.assertEquals(self.partner_id, statement_line.partner_id['id'], "Missing expected partner id after completion")

=== modified file 'account_statement_base_import/statement.py'
--- account_statement_base_import/statement.py	2013-05-03 19:57:26 +0000
+++ account_statement_base_import/statement.py	2013-09-11 13:21:05 +0000
@@ -28,6 +28,7 @@
 from openerp.osv.orm import Model
 from openerp.osv import fields, osv
 from parser import new_bank_statement_parser
+import simplejson
 
 
 class AccountStatementProfil(Model):
@@ -225,14 +226,40 @@
     """
     _inherit = "account.bank.statement.line"
 
-    def _get_available_columns(self, statement_store):
+    def _get_available_columns(self, statement_store, include_serializable=False):
         """Return writeable by SQL columns"""
         statement_line_obj = self.pool['account.bank.statement.line']
         model_cols = statement_line_obj._columns
         avail = [k for k, col in model_cols.iteritems() if not hasattr(col, '_fnct')]
         keys = [k for k in statement_store[0].keys() if k in avail]
+        # add sparse fields..
+        if include_serializable:
+            for k, col in model_cols.iteritems():
+                if  k in statement_store[0].keys() and \
+                      isinstance(col, fields.sparse) and \
+                      col.serialization_field not in keys and \
+                      col._type == 'char':
+                    keys.append(col.serialization_field)
         keys.sort()
         return keys
+    
+    def _get_values(self, cols, statement_store):
+        statement_line_obj = self.pool['account.bank.statement.line']
+        model_cols = statement_line_obj._columns
+        sparse_fields = dict([(k , col) for k, col in model_cols.iteritems() if isinstance(col, fields.sparse) and col._type == 'char'])
+        values = []
+        for statement in statement_store:
+            to_json_k = set()
+            for k, col in sparse_fields.iteritems():
+                if k in statement:
+                    to_json_k.add(col.serialization_field)
+                    serialized = statement.setdefault(col.serialization_field, {})
+                    serialized[k] = statement[k]
+            for k in to_json_k:
+                statement[k] =  simplejson.dumps(statement[k])
+            values.append(statement)
+        return values
+        
 
     def _insert_lines(self, cr, uid, statement_store, context=None):
         """ Do raw insert into database because ORM is awfully slow
@@ -241,11 +268,11 @@
         statement_line_obj = self.pool['account.bank.statement.line']
         statement_line_obj.check_access_rule(cr, uid, [], 'create')
         statement_line_obj.check_access_rights(cr, uid, 'create', raise_exception=True)
-        cols = self._get_available_columns(statement_store)
+        cols = self._get_available_columns(statement_store, include_serializable=True)
         tmp_vals = (', '.join(cols), ', '.join(['%%(%s)s' % i for i in cols]))
         sql = "INSERT INTO account_bank_statement_line (%s) VALUES (%s);" % tmp_vals
         try:
-            cr.executemany(sql, tuple(statement_store))
+            cr.executemany(sql, tuple(self._get_values(cols, statement_store)))
         except psycopg2.Error as sql_err:
             cr.rollback()
             raise osv.except_osv(_("ORM bypass error"),

=== modified file 'account_statement_ext/statement.py'
--- account_statement_ext/statement.py	2013-05-24 09:38:35 +0000
+++ account_statement_ext/statement.py	2013-09-11 13:21:05 +0000
@@ -553,7 +553,11 @@
         if context is None:
             context = {}
         date = context.get('date')
-        periods = self.pool.get('account.period').find(cr, uid, dt=date)
+        try:
+            periods = self.pool.get('account.period').find(cr, uid, dt=date)
+        except osv.except_osv:
+            # if no period defined, we are certainly at installation time
+            return False
         return periods and periods[0] or False
 
     def _get_default_account(self, cr, uid, context=None):

=== modified file 'account_statement_transactionid_completion/statement.py'
--- account_statement_transactionid_completion/statement.py	2013-04-25 11:30:23 +0000
+++ account_statement_transactionid_completion/statement.py	2013-09-11 13:21:05 +0000
@@ -90,5 +90,5 @@
             string='Transaction ID',
             size=128,
             serialization_field='additionnal_bank_fields',
-            help="Transction id from the financial institute"),
+            help="Transaction id from the financial institute"),
     }


Follow ups