From 2d9569cc18d47c0f9c29efaec78900ebefb0372d Mon Sep 17 00:00:00 2001
From: Arthur Maniet <arthurmaniet@me.com>
Date: Sun, 31 May 2015 14:58:04 +0200
Subject: [PATCH] [FIX] account: bank statement import - use a wizard to ask
 about creating a new bank account + journal - fix 'Import Statement' action
 from the dashboard - make l10n_be_coda new-api-compliant

---
 addons/account/account_journal_dashboard.py   |   4 +-
 .../account_bank_statement_import.py          | 145 +++++++++---------
 .../wizard/journal_creation.py                |   4 +-
 .../wizard/journal_creation.xml               |   2 +-
 .../account_bank_statement_import_qif.py      |  15 +-
 ...account_bank_statement_import_qif_view.xml |   8 +-
 addons/l10n_be/__openerp__.py                 |   1 -
 addons/l10n_be_coda/__openerp__.py            |   4 +-
 .../wizard/account_coda_import.py             |  27 ++--
 9 files changed, 103 insertions(+), 107 deletions(-)

diff --git a/addons/account/account_journal_dashboard.py b/addons/account/account_journal_dashboard.py
index a1b5716ba85b..f7cdd726a3ef 100644
--- a/addons/account/account_journal_dashboard.py
+++ b/addons/account/account_journal_dashboard.py
@@ -343,5 +343,7 @@ class account_journal(models.Model):
         action_name = 'action_account_bank_statement_import'
         ir_model_obj = self.pool['ir.model.data']
         model, action_id = ir_model_obj.get_object_reference(self._cr, self._uid, 'account_bank_statement_import', action_name)
-        action = self.pool[model].read(self._cr, self._uid, action_id, context=self._context)
+        action = self.pool[model].read(self._cr, self._uid, action_id, context=self.env.context)
+        # Note: this drops action['context'], which is a dict stored as a string, which is not easy to update
+        action.update({'context': (u"{'journal_id': " + str(self.id) + u"}")})
         return action
diff --git a/addons/account_bank_statement_import/account_bank_statement_import.py b/addons/account_bank_statement_import/account_bank_statement_import.py
index 893c1fae0ba7..c9ca9c193885 100644
--- a/addons/account_bank_statement_import/account_bank_statement_import.py
+++ b/addons/account_bank_statement_import/account_bank_statement_import.py
@@ -30,25 +30,24 @@ class AccountBankStatementImport(models.TransientModel):
     def import_file(self):
         """ Process the file chosen in the wizard, create bank statement(s) and go to reconciliation. """
         self.ensure_one()
-        rec = self.with_context(active_id=self.ids[0])
         #set the active_id in the context, so that any extension module could
         #reuse the fields chosen in the wizard if needed (see .QIF for example)
         data_file = self.data_file
-        # The appropriate implementation module returns the required data
-        currency_code, account_number, stmts_vals = rec._parse_file(base64.b64decode(data_file))
+        # Let the appropriate implementation module parse the file and return the required data
+        # The active_id is passed in context in case an implementation module requires information about the wizard state (see QIF)
+        currency_code, account_number, stmts_vals = self.with_context(active_id=self.ids[0])._parse_file(base64.b64decode(data_file))
         # Check raw data
-        rec._check_parsed_data(stmts_vals)
-        # Try to find the bank account and currency in odoo
-        currency_id, bank_account_id = rec._find_additional_data(currency_code, account_number)
-        # Find or create the bank journal
-        journal_id = rec._get_journal(currency_id, bank_account_id, account_number)
-        # Create the bank account if not already existing
-        if not bank_account_id and account_number:
-            rec._create_bank_account(account_number, journal_id=journal_id)
+        self._check_parsed_data(stmts_vals)
+        # Try to find the currency and journal in odoo
+        currency, journal, bank_account = self._find_additional_data(currency_code, account_number)
+        # If no journal found, ask the user about creating one
+        if not journal:
+            # The active_id is passed in context so the wizard can call import_file again once the journal is created
+            return self.with_context(active_id=self.ids[0])._journal_creation_wizard(currency, account_number, bank_account)
         # Prepare statement data to be used for bank statements creation
-        stmts_vals = rec._complete_stmts_vals(stmts_vals, journal_id, account_number)
+        stmts_vals = self._complete_stmts_vals(stmts_vals, journal, account_number)
         # Create the bank statements
-        statement_ids, notifications = rec._create_bank_statements(stmts_vals)
+        statement_ids, notifications = self._create_bank_statements(stmts_vals)
         # Finally dispatch to reconciliation interface
         action = self.env.ref('account.action_bank_reconcile_bank_statements')
         return {
@@ -61,6 +60,24 @@ class AccountBankStatementImport(models.TransientModel):
             'type': 'ir.actions.client',
         }
 
+    def _journal_creation_wizard(self, currency, account_number, bank_account):
+        """ Calls a wizard that allows the user to accept/refuse journal creation """
+        return {
+            'name': _('Journal Creation'),
+            'type': 'ir.actions.act_window',
+            'res_model': 'account.bank.statement.import.journal.creation',
+            'view_type': 'form',
+            'view_mode': 'form',
+            'target': 'new',
+            'context': {
+                'statement_import_transient_id': self.env.context['active_id'],
+                'default_currency_id': currency and currency.id or False,
+                'default_account_number': account_number,
+                'bank_account_id': bank_account and bank_account.id or False,
+                'default_name': _('Bank') + ' ' + account_number,
+            }
+        }
+
     def _parse_file(self, data_file):
         """ Each module adding a file support must extends this method. It processes the file if it can, returns super otherwise, resulting in a chain of responsability.
             This method parses the given file and returns the data required by the bank statement import process, as specified below.
@@ -102,83 +119,71 @@ class AccountBankStatementImport(models.TransientModel):
 
     def _find_additional_data(self, currency_code, account_number):
         """ Get the res.currency ID and the res.partner.bank ID """
-        currency_id = False  # So if no currency_code is provided, we'll use the company currency
+        company_currency = self.env.user.company_id.currency_id
+        journal_obj = self.env['account.journal']
+        currency = None
+
         if currency_code:
             currency = self.env['res.currency'].search([('name', '=ilike', currency_code)], limit=1)
-            company_currency = self.env.user.company_id.currency_id
-            if currency.id != company_currency.id:
-                currency_id = currency.id
+            if not currency:
+                raise osv.except_osv(_("No currency found matching '%s'.") % currency_code)
+            if currency == company_currency:
+                currency = False
 
-        bank_account_id = None
+        bank_account = None
         if account_number and len(account_number) > 4:
-            account_number = account_number.replace(' ', '').replace('-', '')
-            self.env.cr.execute("select id from res_partner_bank where replace(replace(acc_number,' ',''),'-','') = %s", (account_number,))
-            bank_account_ids = [id[0] for id in self.env.cr.fetchall()]
-            bank_account_ids = self.env['res.partner.bank'].search([('id', 'in', bank_account_ids)], limit=1)
-            if bank_account_ids:
-                bank_account_id = bank_account_ids.id
-
-        return currency_id, bank_account_id
-
-    def _get_journal(self, currency_id, bank_account_id, account_number):
-        """ Find or create the journal """
-        ResPartnerBank = self.env['res.partner.bank']
+            bank_account = self.env['res.partner.bank'].search([('acc_number', '=', account_number)], limit=1)
 
         # Find the journal from context or bank account
-        journal_id = self._context.get('journal_id')
-        if bank_account_id:
-            bank_account = ResPartnerBank.browse(bank_account_id)
-            if journal_id:
-                if bank_account.journal_id.id and bank_account.journal_id.id != journal_id:
+        journal = journal_obj.browse(self._context.get('journal_id', []))
+        if bank_account:
+            if journal:
+                if bank_account.journal_id and bank_account.journal_id != journal:
                     raise UserError(_('The account of this statement is linked to another journal.'))
-                if not bank_account.journal_id.id:
-                    bank_account.write({'journal_id': journal_id})
+                if not bank_account.journal_id:
+                    bank_account.write({'journal_id': journal.id})
             else:
-                if bank_account.journal_id.id:
-                    journal_id = bank_account.journal_id.id
-
-        # If importing into an existing journal, its currency must be the same as the bank statement
-        if journal_id:
-            journal_currency_id = self.env['account.journal'].browse(journal_id).currency_id.id
-            if currency_id and currency_id != journal_currency_id:
-                raise UserError(_('The currency of the bank statement is not the same as the currency of the journal !'))
-
-        # If there is no journal, create one (and its account)
-        if not journal_id and account_number:
-            company = self.env.user.company_id
-            journal_vals = self.env['account.journal']._prepare_bank_journal(company, {'account_type': 'bank', 'acc_name': account_number, 'currency_id': currency_id})
-            journal_id = self.env['account.journal'].create(journal_vals).id
-            if bank_account_id:
-                bank_account.write({'journal_id': journal_id})
-
-        # If we couldn't find/create a journal, everything is lost
-        if not journal_id:
+                if bank_account.journal_id:
+                    journal = bank_account.journal_id
+
+        # If importing into an existing journal
+        if journal:
+            # The bank account cannot belong to another journal
+            if not bank_account and account_number:
+                journal_account = self.env['res.partner.bank'].search([('journal_id', '=', journal.id)], limit=1)
+                if journal_account:
+                    raise UserError(_('You are importing a file from account %s while the account of journal %s is %s.') % (account_number, journal.name, journal_account.acc_number))
+
+            # Its currency must be the same as the bank statement
+            journal_currency = journal.currency_id
+            if currency == None:
+                currency = journal_currency
+            if currency and currency != journal_currency:
+                statement_cur_code = currency == False and company_currency.name or currency.name
+                journal_cur_code = not journal_currency and company_currency.name or journal_currency.name
+                raise UserError(_('The currency of the bank statement (%s) is not the same as the currency of the journal (%s) !') % (statement_cur_code, journal_cur_code))
+
+        # If we couldn't find / can't create a journal, everything is lost
+        if not journal and not account_number:
             raise UserError(_('Cannot find in which journal import this statement. Please manually select a journal.'))
-        return journal_id
 
-    def _create_bank_account(self, account_number, journal_id=False):
+        return currency, journal, bank_account
+
+    def _create_bank_account(self, account_number):
         try:
             bank_type = self.env.ref('bank.bank_normal')
             bank_code = bank_type.code
         except ValueError:
             bank_code = 'bank'
         account_number = account_number.replace(' ', '').replace('-', '')
-        vals_acc = {
+        return self.env['res.partner.bank'].create({
             'acc_number': account_number,
             'state': bank_code,
-        }
-        # Odoo users bank accounts (which we import statement from) have company_id and journal_id set
-        # while 'counterpart' bank accounts (from which statement transactions originate) don't.
-        if journal_id:
-            vals_acc['journal_id'] = journal_id
-            vals_acc['company_id'] = self.env.user.company_id.id
-            vals_acc['partner_id'] = self.env.user.company_id.partner_id.id
-
-        return self.env['res.partner.bank'].create(vals_acc)
+        })
 
-    def _complete_stmts_vals(self, stmts_vals, journal_id, account_number):
+    def _complete_stmts_vals(self, stmts_vals, journal, account_number):
         for st_vals in stmts_vals:
-            st_vals['journal_id'] = journal_id
+            st_vals['journal_id'] = journal.id
 
             for line_vals in st_vals['transactions']:
                 unique_import_id = line_vals.get('unique_import_id')
diff --git a/addons/account_bank_statement_import/wizard/journal_creation.py b/addons/account_bank_statement_import/wizard/journal_creation.py
index d49fde7faf3f..3acd180ac50e 100644
--- a/addons/account_bank_statement_import/wizard/journal_creation.py
+++ b/addons/account_bank_statement_import/wizard/journal_creation.py
@@ -19,7 +19,7 @@ class account_bank_statement_import_journal_creation(models.TransientModel):
         currency_id = wiz.currency_id.id
         account_number = wiz.account_number
 
-        bank_account_id = self.env._context.get('bank_account_id')
+        bank_account_id = self.env.context.get('bank_account_id')
         if bank_account_id:
             vals = {'currency_id': currency_id, 'acc_name': account_number, 'account_type': 'bank'}
             vals_journal = journal_obj._prepare_bank_journal(company, vals)
@@ -38,5 +38,5 @@ class account_bank_statement_import_journal_creation(models.TransientModel):
             self.env['res.partner.bank'].create(res_partner_bank_vals)
 
         # Finish the statement import
-        statement_import_transient = import_wiz_obj.browse(self.env._context['statement_import_transient_id'])
+        statement_import_transient = import_wiz_obj.browse(self.env.context['statement_import_transient_id'])
         return statement_import_transient.import_file()
diff --git a/addons/account_bank_statement_import/wizard/journal_creation.xml b/addons/account_bank_statement_import/wizard/journal_creation.xml
index 4f9c53af1a27..3894991eddbf 100644
--- a/addons/account_bank_statement_import/wizard/journal_creation.xml
+++ b/addons/account_bank_statement_import/wizard/journal_creation.xml
@@ -11,7 +11,7 @@
                     <p>Just click OK to create the account/journal and finish the import. If this was a mistake, hit cancel to abort the import.</p>
                     <group>
                         <field name="name"/>
-                        <field name="currency_id" options="{'no_create': True}"/>
+                        <field name="currency_id" options="{'no_create': True}" attrs="{'invisible': [('currency_id', '=', False)]}"/>
                         <field name="account_number"/>
                     </group>
                     <footer>
diff --git a/addons/account_bank_statement_import_qif/account_bank_statement_import_qif.py b/addons/account_bank_statement_import_qif/account_bank_statement_import_qif.py
index 651ebff46061..5021c0e85b57 100644
--- a/addons/account_bank_statement_import_qif/account_bank_statement_import_qif.py
+++ b/addons/account_bank_statement_import_qif/account_bank_statement_import_qif.py
@@ -11,19 +11,18 @@ class AccountBankStatementImport(models.TransientModel):
     _inherit = "account.bank.statement.import"
 
     def _get_hide_journal_field(self):
-        return self._context and 'journal_id' in self._context or False
+        return self.env.context and 'journal_id' in self.env.context or False
 
     journal_id = fields.Many2one('account.journal', string='Journal', help='Accounting journal related to the bank statement you\'re importing. It has be be manually chosen for statement formats which doesn\'t allow automatic journal detection (QIF for example).')
     hide_journal_field = fields.Boolean(string='Hide the journal field in the view', default=_get_hide_journal_field)
 
-    def _get_journal(self, currency_id, bank_account_id, account_number):
+    def _find_additional_data(self, *args):
         """ As .QIF format does not allow us to detect the journal, we need to let the user choose it.
-            We set it in context before to call super so it's the same as calling the widget from a journal """
-        if self._context.get('active_id'):
-            record = self.browse(self._context.get('active_id'))
-            if record.journal_id:
-                return super(AccountBankStatementImport, self).with_context(journal_id=record.journal_id.id)._get_journal(currency_id, bank_account_id, account_number)
-        return super(AccountBankStatementImport, self)._get_journal(currency_id, bank_account_id, account_number)
+            We set it in context in the same way it's done when calling the import action from a journal.
+        """
+        if self.journal_id:
+            self.env.context = dict(self.env.context, journal_id=self.journal_id.id)
+        return super(AccountBankStatementImport, self)._find_additional_data(*args)
 
     def _check_qif(self, data_file):
         return data_file.strip().startswith('!Type:')
diff --git a/addons/account_bank_statement_import_qif/account_bank_statement_import_qif_view.xml b/addons/account_bank_statement_import_qif/account_bank_statement_import_qif_view.xml
index 3b3a3421572e..fedd7e0ddbc7 100644
--- a/addons/account_bank_statement_import_qif/account_bank_statement_import_qif_view.xml
+++ b/addons/account_bank_statement_import_qif/account_bank_statement_import_qif_view.xml
@@ -10,11 +10,9 @@
             <field name="arch" type="xml">
                 <xpath expr="//field[@name='data_file']" position="after">
                     <field name="hide_journal_field" invisible="1"/>
-                    <label for="journal_id"/>
-                    <field name="journal_id"
-                        domain="[('type', '=', 'bank')]"
-                        attrs="{'invisible': [('hide_journal_field', '=', True)]}"
-                        context="{'default_type':'bank'}"/>
+                    <group attrs="{'invisible': [('hide_journal_field', '=', True)]}">
+                        <field name="journal_id" domain="[('type', '=', 'bank')]" context="{'default_type':'bank'}"/>
+                    </group>
                 </xpath>
             </field>
         </record>
diff --git a/addons/l10n_be/__openerp__.py b/addons/l10n_be/__openerp__.py
index ec699d01bef0..b34350a15bdc 100644
--- a/addons/l10n_be/__openerp__.py
+++ b/addons/l10n_be/__openerp__.py
@@ -57,7 +57,6 @@ Wizards provided by this module:
         'account',
         'base_vat',
         'base_iban',
-        'l10n_be_coda',
         'l10n_multilang',
     ],
     'data': [
diff --git a/addons/l10n_be_coda/__openerp__.py b/addons/l10n_be_coda/__openerp__.py
index 61ef8c758656..ed08f7e72eb1 100644
--- a/addons/l10n_be_coda/__openerp__.py
+++ b/addons/l10n_be_coda/__openerp__.py
@@ -71,14 +71,14 @@ description provided by the CODA configuration tables is based upon the CODA
 V2.2 specifications.
 If required, you can manually adjust the descriptions via the CODA configuration menu.
 ''',
-    'depends': ['account_voucher', 'base_iban', 'l10n_be_invoice_bba', 'account_bank_statement_import'],
+    'depends': ['account_accountant', 'l10n_be'],
     'demo': [
         'l10n_be_coda_demo.xml',
     ],
     'data': [
         'l10n_be_coda_view.xml',
     ],
-    'auto_install': False,
+    'auto_install': True,
     'website': 'https://www.odoo.com/page/accounting',
     'installable': True,
     'license': 'AGPL-3',
diff --git a/addons/l10n_be_coda/wizard/account_coda_import.py b/addons/l10n_be_coda/wizard/account_coda_import.py
index 6a3902acde60..0b68639022ca 100644
--- a/addons/l10n_be_coda/wizard/account_coda_import.py
+++ b/addons/l10n_be_coda/wizard/account_coda_import.py
@@ -22,28 +22,23 @@
 import time
 import re
 
-from openerp.osv import osv
-from openerp.tools.translate import _
-from openerp import tools
+from openerp import models, tools, _
 from openerp.exceptions import UserError
 
-import logging
+class AccountBankStatementImport(models.TransientModel):
+    _inherit = 'account.bank.statement.import'
 
-_logger = logging.getLogger(__name__)
-
-class account_bank_statement_import(osv.TransientModel):
-    _inherit = "account.bank.statement.import"
-
-    def _check_coda(self, cr, uid, data_file, context=None):
+    def _check_coda(self, data_file):
         # Matches the first 24 characters of a CODA file, as defined by the febelfin specifications
         return re.match('0{5}\d{9}05[ D] +', data_file) != None
 
-    def _parse_file(self, cr, uid, data_file, context=None):
-        if not self._check_coda(cr, uid, data_file, context=context):
-            return super(account_bank_statement_import, self)._parse_file(cr, uid, data_file, context=context)
+    def _parse_file(self, data_file):
+        if not self._check_coda(data_file):
+            return super(AccountBankStatementImport, self)._parse_file(data_file)
+
+        def rmspaces(s):
+            return " ".join(s.split())
 
-        if context is None:
-            context = {}
         recordlist = unicode(data_file, 'windows-1252', 'strict').split('\n')
         statements = []
         globalisation_comm = {}
@@ -251,5 +246,3 @@ class account_bank_statement_import(osv.TransientModel):
         currency_code = statement['currency']
         acc_number = statements[0] and statements[0]['acc_number'] or False
         return currency_code, acc_number, ret_statements
-def rmspaces(s):
-    return " ".join(s.split())
-- 
GitLab