banking-addons-team team mailing list archive
-
banking-addons-team team
-
Mailing list archive
-
Message #01471
[Merge] lp:~therp-nl/banking-addons/ba70-deprecate_iban_lookup into lp:banking-addons
Stefan Rijnhart (Therp) has proposed merging lp:~therp-nl/banking-addons/ba70-deprecate_iban_lookup into lp:banking-addons.
Requested reviews:
Banking Addons Core Editors (banking-addons-team)
For more details, see:
https://code.launchpad.net/~therp-nl/banking-addons/ba70-deprecate_iban_lookup/+merge/202520
Splitting off the online account number (i.e. IBAN) lookup functionality into a separate module, and labelling this as deprecated. This was only ever functional for NL and maybe BE. The long diff mainly represents moving a single block of code around. Notable exception is a modification to the existing search extension on res.partner.bank which makes sure that spaces in IBANs are now ignored when searching for equivalence.
--
https://code.launchpad.net/~therp-nl/banking-addons/ba70-deprecate_iban_lookup/+merge/202520
Your team Banking Addons Core Editors is requested to review the proposed merge of lp:~therp-nl/banking-addons/ba70-deprecate_iban_lookup into lp:banking-addons.
=== modified file 'account_banking/__init__.py'
--- account_banking/__init__.py 2013-11-17 19:45:48 +0000
+++ account_banking/__init__.py 2014-02-09 20:21:36 +0000
@@ -31,5 +31,7 @@
import parsers
import wizard
import res_partner
+import res_bank
+import res_partner_bank
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
=== modified file 'account_banking/__openerp__.py'
--- account_banking/__openerp__.py 2013-12-10 07:06:28 +0000
+++ account_banking/__openerp__.py 2014-02-09 20:21:36 +0000
@@ -33,7 +33,6 @@
'category': 'Banking addons',
'depends': [
'account_voucher',
- 'account_iban_preserve_domestic',
],
'data': [
'security/ir.model.access.csv',
@@ -47,14 +46,9 @@
'js': [
'static/src/js/account_banking.js',
],
- 'external_dependencies': {
- 'python' : ['BeautifulSoup'],
- },
'description': '''
Module to do banking.
- Note: This module is depending on BeautifulSoup.
-
This modules tries to combine all current banking import and export
schemes. Rationale for this is that it is quite common to have foreign
bank account numbers next to national bank account numbers. The current
=== modified file 'account_banking/account_banking.py'
--- account_banking/account_banking.py 2013-10-31 07:33:46 +0000
+++ account_banking/account_banking.py 2014-02-09 20:21:36 +0000
@@ -70,10 +70,6 @@
from openerp.addons.account_banking import sepa
from openerp.addons.account_banking.wizard.banktools import get_or_create_bank
-def warning(title, message):
- '''Convenience routine'''
- return {'warning': {'title': title, 'message': message}}
-
class account_banking_account_settings(orm.Model):
'''Default Journal for Bank Account'''
@@ -562,413 +558,6 @@
account_bank_statement_line()
-class res_partner_bank(orm.Model):
- '''
- This is a hack to circumvent the very limited but widely used base_iban
- dependency. The usage of __mro__ requires inside information of
- inheritence. This code is tested and works - it bypasses base_iban
- altogether. Be sure to use 'super' for inherited classes from here though.
-
- Extended functionality:
- 1. BBAN and IBAN are considered equal
- 2. Online databases are checked when available
- 3. Banks are created on the fly when using IBAN
- 4. Storage is uppercase, not lowercase
- 5. Presentation is formal IBAN
- 6. BBAN's are generated from IBAN when possible
- 7. In the absence of online databanks, BBAN's are checked on format
- using IBAN specs.
- '''
- _inherit = 'res.partner.bank'
-
- def __init__(self, *args, **kwargs):
- '''
- Locate founder (first non inherited class) in inheritance tree.
- Defaults to super()
- Algorithm should prevent moving unknown classes between
- base.res_partner_bank and this module's res_partner_bank.
- '''
- self._founder = super(res_partner_bank, self)
- self._founder.__init__(*args, **kwargs)
- mro = self.__class__.__mro__
- for i in range(len(mro)):
- if mro[i].__module__.startswith('openerp.addons.base.'):
- self._founder = mro[i]
- break
-
- def init(self, cr):
- '''
- Update existing iban accounts to comply to new regime
- '''
-
- partner_bank_obj = self.pool.get('res.partner.bank')
- bank_ids = partner_bank_obj.search(
- cr, SUPERUSER_ID, [('state', '=', 'iban')], limit=0)
- for bank in partner_bank_obj.read(cr, SUPERUSER_ID, bank_ids):
- write_vals = {}
- if bank['state'] == 'iban':
- iban_acc = sepa.IBAN(bank['acc_number'])
- if iban_acc.valid:
- write_vals['acc_number_domestic'] = iban_acc.localized_BBAN
- write_vals['acc_number'] = str(iban_acc)
- elif bank['acc_number'] != bank['acc_number'].upper():
- write_vals['acc_number'] = bank['acc_number'].upper()
- if write_vals:
- partner_bank_obj.write(
- cr, SUPERUSER_ID, bank['id'], write_vals)
-
- @staticmethod
- def _correct_IBAN(acc_number):
- '''
- Routine to correct IBAN values and deduce localized values when valid.
- Note: No check on validity IBAN/Country
- '''
- iban = sepa.IBAN(acc_number)
- return (str(iban), iban.localized_BBAN)
-
- def create(self, cr, uid, vals, context=None):
- '''
- Create dual function IBAN account for SEPA countries
- '''
- if vals.get('state') == 'iban':
- iban = (vals.get('acc_number')
- or vals.get('acc_number_domestic', False))
- vals['acc_number'], vals['acc_number_domestic'] = (
- self._correct_IBAN(iban))
- return self._founder.create(self, cr, uid, vals, context)
-
- def write(self, cr, uid, ids, vals, context=None):
- '''
- Create dual function IBAN account for SEPA countries
-
- Update the domestic account number when the IBAN is
- written, or clear the domestic number on regular account numbers.
- '''
- if ids and isinstance(ids, (int, long)):
- ids = [ids]
- for account in self.read(
- cr, uid, ids, ['state', 'acc_number']):
- if 'state' in vals or 'acc_number' in vals:
- account.update(vals)
- if account['state'] == 'iban':
- vals['acc_number'], vals['acc_number_domestic'] = (
- self._correct_IBAN(account['acc_number']))
- else:
- vals['acc_number_domestic'] = False
- self._founder.write(self, cr, uid, account['id'], vals, context)
- return True
-
- def search(self, cr, uid, args, *rest, **kwargs):
- '''
- Overwrite search, as both acc_number and iban now can be filled, so
- the original base_iban 'search and search again fuzzy' tactic now can
- result in doubled findings. Also there is now enough info to search
- for local accounts when a valid IBAN was supplied.
-
- Chosen strategy: create complex filter to find all results in just
- one search
- '''
-
- def is_term(arg):
- '''Flag an arg as term or otherwise'''
- return isinstance(arg, (list, tuple)) and len(arg) == 3
-
- def extended_filter_term(term):
- '''
- Extend the search criteria in term when appropriate.
- '''
- extra_term = None
- if term[0].lower() == 'acc_number' and term[1] in ('=', '=='):
- iban = sepa.IBAN(term[2])
- if iban.valid:
- # Some countries can't convert to BBAN
- try:
- bban = iban.localized_BBAN
- # Prevent empty search filters
- if bban:
- extra_term = ('acc_number_domestic', term[1], bban)
- except:
- pass
- if extra_term:
- return ['|', term, extra_term]
- return [term]
-
- def extended_search_expression(args):
- '''
- Extend the search expression in args when appropriate.
- The expression itself is in reverse polish notation, so recursion
- is not needed.
- '''
- if not args:
- return []
-
- all = []
- if is_term(args[0]) and len(args) > 1:
- # Classic filter, implicit '&'
- all += ['&']
-
- for arg in args:
- if is_term(arg):
- all += extended_filter_term(arg)
- else:
- all += arg
- return all
-
- # Extend search filter
- newargs = extended_search_expression(args)
-
- # Original search
- results = super(res_partner_bank, self).search(
- cr, uid, newargs, *rest, **kwargs)
- return results
-
- def read(
- self, cr, uid, ids, fields=None, context=None, load='_classic_read'):
- '''
- Convert IBAN electronic format to IBAN display format
- SR 2012-02-19: do we really need this? Fields are converted upon write already.
- '''
- if fields and 'state' not in fields:
- fields.append('state')
- records = self._founder.read(self, cr, uid, ids, fields, context, load)
- is_list = True
- if not isinstance(records, list):
- records = [records,]
- is_list = False
- for record in records:
- if 'acc_number' in record and record['state'] == 'iban':
- record['acc_number'] = unicode(sepa.IBAN(record['acc_number']))
- if is_list:
- return records
- return records[0]
-
- def check_iban(self, cr, uid, ids, context=None):
- '''
- Check IBAN number
- '''
- for bank_acc in self.browse(cr, uid, ids, context=context):
- if bank_acc.state == 'iban' and bank_acc.acc_number:
- iban = sepa.IBAN(bank_acc.acc_number)
- if not iban.valid:
- return False
- return True
-
- def get_bban_from_iban(self, cr, uid, ids, context=None):
- '''
- Return the local bank account number aka BBAN from the IBAN.
- '''
- res = {}
- for record in self.browse(cr, uid, ids, context):
- if not record.state == 'iban':
- res[record.id] = False
- else:
- iban_acc = sepa.IBAN(record.acc_number)
- try:
- res[record.id] = iban_acc.localized_BBAN
- except NotImplementedError:
- res[record.id] = False
- return res
-
- def onchange_acc_number(
- self, cr, uid, ids, acc_number, acc_number_domestic,
- state, partner_id, country_id, context=None):
- if state == 'iban':
- return self.onchange_iban(
- cr, uid, ids, acc_number, acc_number_domestic,
- state, partner_id, country_id, context=None
- )
- else:
- return self.onchange_domestic(
- cr, uid, ids, acc_number,
- partner_id, country_id, context=None
- )
-
- def onchange_domestic(
- self, cr, uid, ids, acc_number,
- partner_id, country_id, context=None):
- '''
- Trigger to find IBAN. When found:
- 1. Reformat BBAN
- 2. Autocomplete bank
-
- TODO: prevent unnecessary assignment of country_ids and
- browsing of the country
- '''
- if not acc_number:
- return {}
-
- values = {}
- country_obj = self.pool.get('res.country')
- country_ids = []
- country = False
-
- # Pre fill country based on available data. This is just a default
- # which can be overridden by the user.
- # 1. Use provided country_id (manually filled)
- if country_id:
- country = country_obj.browse(cr, uid, country_id, context=context)
- country_ids = [country_id]
- # 2. Use country_id of found bank accounts
- # This can be usefull when there is no country set in the partners
- # addresses, but there was a country set in the address for the bank
- # account itself before this method was triggered.
- elif ids and len(ids) == 1:
- partner_bank_obj = self.pool.get('res.partner.bank')
- partner_bank_id = partner_bank_obj.browse(cr, uid, ids[0], context=context)
- if partner_bank_id.country_id:
- country = partner_bank_id.country_id
- country_ids = [country.id]
- # 3. Use country_id of default address of partner
- # The country_id of a bank account is a one time default on creation.
- # It originates in the same address we are about to check, but
- # modifications on that address afterwards are not transfered to the
- # bank account, hence the additional check.
- elif partner_id:
- partner_obj = self.pool.get('res.partner')
- country = partner_obj.browse(cr, uid, partner_id, context=context).country
- country_ids = country and [country.id] or []
- # 4. Without any of the above, take the country from the company of
- # the handling user
- if not country_ids:
- user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
- # Try user companies partner (user no longer has address in 6.1)
- if (user.company_id and
- user.company_id.partner_id and
- user.company_id.partner_id.country
- ):
- country_ids = [user.company_id.partner_id.country.id]
- else:
- if (user.company_id and user.company_id.partner_id and
- user.company_id.partner_id.country):
- country_ids = [user.company_id.partner_id.country.id]
- else:
- # Ok, tried everything, give up and leave it to the user
- return warning(_('Insufficient data'),
- _('Insufficient data to select online '
- 'conversion database')
- )
- result = {'value': values}
- # Complete data with online database when available
- if country_ids:
- country = country_obj.browse(
- cr, uid, country_ids[0], context=context)
- values['country_id'] = country_ids[0]
- if country and country.code in sepa.IBAN.countries:
- try:
- info = sepa.online.account_info(country.code, acc_number)
- if info:
- iban_acc = sepa.IBAN(info.iban)
- if iban_acc.valid:
- values['acc_number_domestic'] = iban_acc.localized_BBAN
- values['acc_number'] = unicode(iban_acc)
- values['state'] = 'iban'
- bank_id, country_id = get_or_create_bank(
- self.pool, cr, uid,
- info.bic or iban_acc.BIC_searchkey,
- name = info.bank
- )
- if country_id:
- values['country_id'] = country_id
- values['bank'] = bank_id or False
- if info.bic:
- values['bank_bic'] = info.bic
- else:
- info = None
- if info is None:
- result.update(warning(
- _('Invalid data'),
- _('The account number appears to be invalid for %s')
- % country.name
- ))
- except NotImplementedError:
- if country.code in sepa.IBAN.countries:
- acc_number_fmt = sepa.BBAN(acc_number, country.code)
- if acc_number_fmt.valid:
- values['acc_number_domestic'] = str(acc_number_fmt)
- else:
- result.update(warning(
- _('Invalid format'),
- _('The account number has the wrong format for %s')
- % country.name
- ))
- return result
-
- def onchange_iban(
- self, cr, uid, ids, acc_number, acc_number_domestic,
- state, partner_id, country_id, context=None):
- '''
- Trigger to verify IBAN. When valid:
- 1. Extract BBAN as local account
- 2. Auto complete bank
- '''
- if not acc_number:
- return {}
-
- iban_acc = sepa.IBAN(acc_number)
- if iban_acc.valid:
- bank_id, country_id = get_or_create_bank(
- self.pool, cr, uid, iban_acc.BIC_searchkey,
- code=iban_acc.BIC_searchkey
- )
- return {
- 'value': dict(
- acc_number_domestic = iban_acc.localized_BBAN,
- acc_number = unicode(iban_acc),
- country = country_id or False,
- bank = bank_id or False,
- )
- }
- return warning(_('Invalid IBAN account number!'),
- _("The IBAN number doesn't seem to be correct")
- )
-
-res_partner_bank()
-
-
-class res_bank(orm.Model):
- '''
- Add a on_change trigger to automagically fill bank details from the
- online SWIFT database. Allow hand filled names to overrule SWIFT names.
- '''
- _inherit = 'res.bank'
-
- def onchange_bic(self, cr, uid, ids, bic, name, context=None):
- '''
- Trigger to auto complete other fields.
- '''
- if not bic:
- return {}
-
- info, address = sepa.online.bank_info(bic)
- if not info:
- return {}
-
- if address and address.country_id:
- country_id = self.pool.get('res.country').search(
- cr, uid, [('code','=',address.country_id)]
- )
- country_id = country_id and country_id[0] or False
- else:
- country_id = False
-
- return {
- 'value': dict(
- # Only the first eight positions of BIC are used for bank
- # transfers, so ditch the rest.
- bic = info.bic[:8],
- street = address.street,
- street2 =
- address.has_key('street2') and address.street2 or False,
- zip = address.zip,
- city = address.city,
- country = country_id,
- name = name and name or info.name,
- )
- }
-
-res_bank()
-
-
class invoice(orm.Model):
'''
Create other reference types as well.
=== modified file 'account_banking/account_banking_view.xml'
--- account_banking/account_banking_view.xml 2013-10-31 07:33:46 +0000
+++ account_banking/account_banking_view.xml 2014-02-09 20:21:36 +0000
@@ -286,35 +286,6 @@
</field>
</record>
- <record id="view_partner_bank_account_banking_form_2" model="ir.ui.view">
- <field name="name">res.partner.bank.form.banking-2</field>
- <field name="model">res.partner.bank</field>
- <field name="inherit_id" ref="base.view_partner_bank_form"/>
- <field name="priority" eval="24"/>
- <field name="arch" type="xml">
- <data>
- <field name="acc_number" position="attributes">
- <attribute name="on_change">onchange_acc_number(acc_number, acc_number_domestic, state, partner_id, country_id)</attribute>
- </field>
- <field name="acc_number_domestic" position="attributes">
- <attribute name="on_change">onchange_domestic(acc_number_domestic, partner_id, country_id)</attribute>
- </field>
- </data>
- </field>
- </record>
-
- <!-- Set trigger on BIC in res_bank form -->
- <record id="view_res_bank_account_banking_form_1" model="ir.ui.view">
- <field name="name">res.bank.form.banking-1</field>
- <field name="model">res.bank</field>
- <field name="inherit_id" ref="base.view_res_bank_form"/>
- <field name="arch" type="xml">
- <field name="bic" position="replace">
- <field name="bic" on_change="onchange_bic(bic, name)"/>
- </field>
- </field>
- </record>
-
<record model="ir.ui.view" id="view_bank_statement_line_tree">
<field name="name">Bank statement line tree view</field>
<field name="model">account.bank.statement.line</field>
=== added file 'account_banking/res_bank.py'
--- account_banking/res_bank.py 1970-01-01 00:00:00 +0000
+++ account_banking/res_bank.py 2014-02-09 20:21:36 +0000
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright 2011 - 2014 Therp BV (<http://therp.nl>).
+#
+# 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.osv import orm
+
+
+class ResBank(orm.Model):
+ _inherit = 'res.bank'
+
+ def online_bank_info(self, cr, uid, bic, context=None):
+ """
+ API hook for legacy online lookup of BICs,
+ to be removed in OpenERP 8.0.
+ """
+ return False, False
=== added file 'account_banking/res_partner_bank.py'
--- account_banking/res_partner_bank.py 1970-01-01 00:00:00 +0000
+++ account_banking/res_partner_bank.py 2014-02-09 20:21:36 +0000
@@ -0,0 +1,104 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
+# (C) 2011 - 2014 Therp BV (<http://therp.nl>).
+#
+# 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.osv import orm
+from openerp.addons.account_banking import sepa
+
+
+class ResPartnerBank(orm.Model):
+ _inherit = 'res.partner.bank'
+
+ def online_account_info(
+ self, cr, uid, country_code, acc_number, context=None):
+ """
+ API hook for legacy online lookup of account info,
+ to be removed in OpenERP 8.0.
+ """
+ return False
+
+ def search(self, cr, uid, args, *rest, **kwargs):
+ """
+ When a complete IBAN is searched, also search for its BBAN
+ if we have the domestic column. Disregard spaces
+ when comparing IBANs.
+ """
+
+ def is_term(arg):
+ '''Flag an arg as term or otherwise'''
+ return isinstance(arg, (list, tuple)) and len(arg) == 3
+
+ def extended_filter_term(term):
+ '''
+ Extend the search criteria in term when appropriate.
+ '''
+ result = [term]
+ extra_terms = []
+ if term[0].lower() == 'acc_number' and term[1] in ('=', '=='):
+ iban = sepa.IBAN(term[2])
+ if iban.valid:
+ # Disregard spaces when comparing IBANs
+ cr.execute(
+ """
+ SELECT id FROM res_partner_bank
+ WHERE replace(acc_number, ' ', '') = %s
+ """, (term[2].replace(' ', ''),))
+ ids = [row[0] for row in cr.fetchall()]
+ result = [('id', 'in', ids)]
+
+ if 'acc_number_domestic' in self._columns:
+ # Some countries can't convert to BBAN
+ try:
+ bban = iban.localized_BBAN
+ # Prevent empty search filters
+ if bban:
+ extra_terms.append(('acc_number_domestic', term[1], bban))
+ except:
+ pass
+ for extra_term in extra_terms:
+ result = ['|'] + result + [extra_term]
+ return result
+
+ def extended_search_expression(args):
+ '''
+ Extend the search expression in args when appropriate.
+ The expression itself is in reverse polish notation, so recursion
+ is not needed.
+ '''
+ if not args:
+ return []
+
+ result = []
+ if is_term(args[0]) and len(args) > 1:
+ # Classic filter, implicit '&'
+ result += ['&']
+
+ for arg in args:
+ if is_term(arg):
+ result += extended_filter_term(arg)
+ else:
+ result += arg
+ return result
+
+ # Extend search filter
+ newargs = extended_search_expression(args)
+
+ # Original search
+ return super(ResPartnerBank, self).search(
+ cr, uid, newargs, *rest, **kwargs)
=== modified file 'account_banking/sepa/__init__.py'
--- account_banking/sepa/__init__.py 2013-04-15 13:59:50 +0000
+++ account_banking/sepa/__init__.py 2014-02-09 20:21:36 +0000
@@ -19,6 +19,5 @@
#
##############################################################################
import iban
-import online
IBAN = iban.IBAN
BBAN = iban.BBAN
=== modified file 'account_banking/wizard/banktools.py'
--- account_banking/wizard/banktools.py 2013-09-14 11:54:23 +0000
+++ account_banking/wizard/banktools.py 2014-02-09 20:21:36 +0000
@@ -64,12 +64,6 @@
('acc_number', '=', account_number)
])
if not bank_account_ids:
- # SR 2012-02-19 does the search() override in res_partner_bank
- # provides this result on the previous query?
- bank_account_ids = partner_bank_obj.search(cr, uid, [
- ('acc_number_domestic', '=', account_number)
- ])
- if not bank_account_ids:
if not fail:
log.append(
_('Bank account %(account_no)s was not found in the database')
@@ -237,7 +231,7 @@
bank_id = False
if online:
- info, address = sepa.online.bank_info(bic)
+ info, address = bank_obj.online_bank_info(cr, uid, bic, context=context)
if info:
bank_id = bank_obj.create(cr, uid, dict(
code = info.code,
@@ -301,7 +295,6 @@
owner_name = holder_name,
country_id = country_id,
)
- bankcode = None
# Are we dealing with IBAN?
iban = sepa.IBAN(account_number)
@@ -309,23 +302,20 @@
# Take as much info as possible from IBAN
values.state = 'iban'
values.acc_number = str(iban)
- values.acc_number_domestic = iban.BBAN
- bankcode = iban.bankcode + iban.countrycode
else:
# No, try to convert to IBAN
values.state = 'bank'
- values.acc_number = values.acc_number_domestic = account_number
+ values.acc_number = account_number
if country_id:
country_code = pool.get('res.country').read(
cr, uid, country_id, ['code'], context=context)['code']
if country_code in sepa.IBAN.countries:
- account_info = sepa.online.account_info(
- country_code, values.acc_number)
+ account_info = pool['res.partner.bank'].online_account_info(
+ cr, uid, country_code, values.acc_number, context=context)
if account_info:
values.acc_number = iban = account_info.iban
values.state = 'iban'
- bankcode = account_info.code
bic = account_info.bic
if bic:
=== added directory 'account_banking_iban_lookup'
=== added file 'account_banking_iban_lookup/__init__.py'
--- account_banking_iban_lookup/__init__.py 1970-01-01 00:00:00 +0000
+++ account_banking_iban_lookup/__init__.py 2014-02-09 20:21:36 +0000
@@ -0,0 +1,3 @@
+from . import online
+from . import urlagent
+from . import model
=== added file 'account_banking_iban_lookup/__openerp__.py'
--- account_banking_iban_lookup/__openerp__.py 1970-01-01 00:00:00 +0000
+++ account_banking_iban_lookup/__openerp__.py 2014-02-09 20:21:36 +0000
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
+# (C) 2011 - 2014 Therp BV (<http://therp.nl>).
+#
+# 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': 'Banking Addons - Iban lookup (legacy)',
+ 'version': '0.1',
+ 'license': 'AGPL-3',
+ 'author': 'Banking addons community',
+ 'website': 'https://launchpad.net/banking-addons',
+ 'category': 'Banking addons',
+ 'depends': [
+ 'account_banking',
+ 'account_iban_preserve_domestic',
+ ],
+ 'data': [
+ 'view/res_bank.xml',
+ 'view/res_partner_bank.xml',
+ ],
+ 'external_dependencies': {
+ 'python' : ['BeautifulSoup'],
+ },
+ 'description': '''
+This addons contains the legacy infrastructure for autocompletion of IBANs
+and BBANs.
+
+The autocompletion was implemented for Dutch IBANs, but as it turns out
+the online database that it consults does not get updated. As a result,
+the autocompletion will come up with outdated IBANs and BICs.
+
+This module is deprecated and will be dropped in OpenERP 8.0.
+ ''',
+ 'auto_install': False,
+ 'installable': True,
+}
=== added directory 'account_banking_iban_lookup/model'
=== added file 'account_banking_iban_lookup/model/__init__.py'
--- account_banking_iban_lookup/model/__init__.py 1970-01-01 00:00:00 +0000
+++ account_banking_iban_lookup/model/__init__.py 2014-02-09 20:21:36 +0000
@@ -0,0 +1,2 @@
+from . import res_bank
+from . import res_partner_bank
=== added file 'account_banking_iban_lookup/model/res_bank.py'
--- account_banking_iban_lookup/model/res_bank.py 1970-01-01 00:00:00 +0000
+++ account_banking_iban_lookup/model/res_bank.py 2014-02-09 20:21:36 +0000
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
+# (C) 2011 - 2014 Therp BV (<http://therp.nl>).
+#
+# 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.osv import orm
+from openerp.addons.account_banking_iban_lookup import online
+
+
+class ResBank(orm.Model):
+ _inherit = 'res.bank'
+
+ def online_bank_info(self, cr, uid, bic, context=None):
+ """
+ Overwrite existing API hook from account_banking
+ """
+ return online.bank_info(bic)
+
+ def onchange_bic(
+ self, cr, uid, ids, bic, name, context=None):
+
+ if not bic:
+ return {}
+
+ info, address = online.bank_info(bic)
+ if not info:
+ return {}
+
+ if address and address.country_id:
+ country_id = self.pool.get('res.country').search(
+ cr, uid, [('code','=',address.country_id)]
+ )
+ country_id = country_id and country_id[0] or False
+ else:
+ country_id = False
+
+ return {
+ 'value': dict(
+ # Only the first eight positions of BIC are used for bank
+ # transfers, so ditch the rest.
+ bic = info.bic[:8],
+ street = address.street,
+ street2 =
+ address.has_key('street2') and address.street2 or False,
+ zip = address.zip,
+ city = address.city,
+ country = country_id,
+ name = name and name or info.name,
+ )
+ }
=== added file 'account_banking_iban_lookup/model/res_partner_bank.py'
--- account_banking_iban_lookup/model/res_partner_bank.py 1970-01-01 00:00:00 +0000
+++ account_banking_iban_lookup/model/res_partner_bank.py 2014-02-09 20:21:36 +0000
@@ -0,0 +1,270 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
+# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
+#
+# All other contributions are (C) by their respective contributors
+#
+# 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 import SUPERUSER_ID
+from openerp.osv import orm
+from openerp.tools.translate import _
+from openerp.addons.account_banking_iban_lookup import online
+from openerp.addons.account_banking import sepa
+from openerp.addons.account_banking.wizard.banktools import get_or_create_bank
+
+def warning(title, message):
+ '''Convenience routine'''
+ return {'warning': {'title': title, 'message': message}}
+
+
+class res_partner_bank(orm.Model):
+ '''
+ Extended functionality:
+ 1. BBAN and IBAN are considered equal
+ 2. Online lookup when an API is available (providing NL in this module)
+ 3. Banks are created on the fly when using IBAN + online
+ 4. IBAN formatting
+ 5. BBAN's are generated from IBAN when possible
+ '''
+ _inherit = 'res.partner.bank'
+
+ def init(self, cr):
+ '''
+ Update existing iban accounts to comply to new regime
+ '''
+
+ partner_bank_obj = self.pool.get('res.partner.bank')
+ bank_ids = partner_bank_obj.search(
+ cr, SUPERUSER_ID, [('state', '=', 'iban')], limit=0)
+ for bank in partner_bank_obj.read(cr, SUPERUSER_ID, bank_ids):
+ write_vals = {}
+ if bank['state'] == 'iban':
+ iban_acc = sepa.IBAN(bank['acc_number'])
+ if iban_acc.valid:
+ write_vals['acc_number_domestic'] = iban_acc.localized_BBAN
+ write_vals['acc_number'] = str(iban_acc)
+ elif bank['acc_number'] != bank['acc_number'].upper():
+ write_vals['acc_number'] = bank['acc_number'].upper()
+ if write_vals:
+ partner_bank_obj.write(
+ cr, SUPERUSER_ID, bank['id'], write_vals)
+
+ @staticmethod
+ def _correct_IBAN(acc_number):
+ '''
+ Routine to correct IBAN values and deduce localized values when valid.
+ Note: No check on validity IBAN/Country
+ '''
+ iban = sepa.IBAN(acc_number)
+ return (str(iban), iban.localized_BBAN)
+
+ def create(self, cr, uid, vals, context=None):
+ '''
+ Create dual function IBAN account for SEPA countries
+ '''
+ if vals.get('state') == 'iban':
+ iban = (vals.get('acc_number')
+ or vals.get('acc_number_domestic', False))
+ vals['acc_number'], vals['acc_number_domestic'] = (
+ self._correct_IBAN(iban))
+ return super(res_partner_bank, self).create(
+ cr, uid, vals, context)
+
+ def write(self, cr, uid, ids, vals, context=None):
+ '''
+ Create dual function IBAN account for SEPA countries
+
+ Update the domestic account number when the IBAN is
+ written, or clear the domestic number on regular account numbers.
+ '''
+ if ids and isinstance(ids, (int, long)):
+ ids = [ids]
+ for account in self.read(
+ cr, uid, ids, ['state', 'acc_number']):
+ if 'state' in vals or 'acc_number' in vals:
+ account.update(vals)
+ if account['state'] == 'iban':
+ vals['acc_number'], vals['acc_number_domestic'] = (
+ self._correct_IBAN(account['acc_number']))
+ else:
+ vals['acc_number_domestic'] = False
+ super(res_partner_bank, self).write(
+ cr, uid, account['id'], vals, context)
+ return True
+
+ def onchange_acc_number(
+ self, cr, uid, ids, acc_number, acc_number_domestic,
+ state, partner_id, country_id, context=None):
+ if state == 'iban':
+ return self.onchange_iban(
+ cr, uid, ids, acc_number, acc_number_domestic,
+ state, partner_id, country_id, context=None
+ )
+ else:
+ return self.onchange_domestic(
+ cr, uid, ids, acc_number,
+ partner_id, country_id, context=None
+ )
+
+ def onchange_domestic(
+ self, cr, uid, ids, acc_number,
+ partner_id, country_id, context=None):
+ '''
+ Trigger to find IBAN. When found:
+ 1. Reformat BBAN
+ 2. Autocomplete bank
+
+ TODO: prevent unnecessary assignment of country_ids and
+ browsing of the country
+ '''
+ if not acc_number:
+ return {}
+
+ values = {}
+ country_obj = self.pool.get('res.country')
+ country_ids = []
+ country = False
+
+ # Pre fill country based on available data. This is just a default
+ # which can be overridden by the user.
+ # 1. Use provided country_id (manually filled)
+ if country_id:
+ country = country_obj.browse(cr, uid, country_id, context=context)
+ country_ids = [country_id]
+ # 2. Use country_id of found bank accounts
+ # This can be usefull when there is no country set in the partners
+ # addresses, but there was a country set in the address for the bank
+ # account itself before this method was triggered.
+ elif ids and len(ids) == 1:
+ partner_bank_obj = self.pool.get('res.partner.bank')
+ partner_bank_id = partner_bank_obj.browse(cr, uid, ids[0], context=context)
+ if partner_bank_id.country_id:
+ country = partner_bank_id.country_id
+ country_ids = [country.id]
+ # 3. Use country_id of default address of partner
+ # The country_id of a bank account is a one time default on creation.
+ # It originates in the same address we are about to check, but
+ # modifications on that address afterwards are not transfered to the
+ # bank account, hence the additional check.
+ elif partner_id:
+ partner_obj = self.pool.get('res.partner')
+ country = partner_obj.browse(cr, uid, partner_id, context=context).country
+ country_ids = country and [country.id] or []
+ # 4. Without any of the above, take the country from the company of
+ # the handling user
+ if not country_ids:
+ user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
+ # Try user companies partner (user no longer has address in 6.1)
+ if (user.company_id and
+ user.company_id.partner_id and
+ user.company_id.partner_id.country
+ ):
+ country_ids = [user.company_id.partner_id.country.id]
+ else:
+ if (user.company_id and user.company_id.partner_id and
+ user.company_id.partner_id.country):
+ country_ids = [user.company_id.partner_id.country.id]
+ else:
+ # Ok, tried everything, give up and leave it to the user
+ return warning(_('Insufficient data'),
+ _('Insufficient data to select online '
+ 'conversion database')
+ )
+ result = {'value': values}
+ # Complete data with online database when available
+ if country_ids:
+ country = country_obj.browse(
+ cr, uid, country_ids[0], context=context)
+ values['country_id'] = country_ids[0]
+ if country and country.code in sepa.IBAN.countries:
+ try:
+ info = online.account_info(country.code, acc_number)
+ if info:
+ iban_acc = sepa.IBAN(info.iban)
+ if iban_acc.valid:
+ values['acc_number_domestic'] = iban_acc.localized_BBAN
+ values['acc_number'] = unicode(iban_acc)
+ values['state'] = 'iban'
+ bank_id, country_id = get_or_create_bank(
+ self.pool, cr, uid,
+ info.bic or iban_acc.BIC_searchkey,
+ name = info.bank
+ )
+ if country_id:
+ values['country_id'] = country_id
+ values['bank'] = bank_id or False
+ if info.bic:
+ values['bank_bic'] = info.bic
+ else:
+ info = None
+ if info is None:
+ result.update(warning(
+ _('Invalid data'),
+ _('The account number appears to be invalid for %s')
+ % country.name
+ ))
+ except NotImplementedError:
+ if country.code in sepa.IBAN.countries:
+ acc_number_fmt = sepa.BBAN(acc_number, country.code)
+ if acc_number_fmt.valid:
+ values['acc_number_domestic'] = str(acc_number_fmt)
+ else:
+ result.update(warning(
+ _('Invalid format'),
+ _('The account number has the wrong format for %s')
+ % country.name
+ ))
+ return result
+
+ def onchange_iban(
+ self, cr, uid, ids, acc_number, acc_number_domestic,
+ state, partner_id, country_id, context=None):
+ '''
+ Trigger to verify IBAN. When valid:
+ 1. Extract BBAN as local account
+ 2. Auto complete bank
+ '''
+ if not acc_number:
+ return {}
+
+ iban_acc = sepa.IBAN(acc_number)
+ if iban_acc.valid:
+ bank_id, country_id = get_or_create_bank(
+ self.pool, cr, uid, iban_acc.BIC_searchkey,
+ code=iban_acc.BIC_searchkey
+ )
+ return {
+ 'value': dict(
+ acc_number_domestic = iban_acc.localized_BBAN,
+ acc_number = unicode(iban_acc),
+ country = country_id or False,
+ bank = bank_id or False,
+ )
+ }
+ return warning(_('Invalid IBAN account number!'),
+ _("The IBAN number doesn't seem to be correct")
+ )
+
+ def online_account_info(
+ self, cr, uid, country_code, acc_number, context=None):
+ """
+ Overwrite API hook from account_banking
+ """
+ return online.account_info(country_code, acc_number)
=== renamed file 'account_banking/sepa/online.py' => 'account_banking_iban_lookup/online.py'
--- account_banking/sepa/online.py 2014-01-05 02:07:34 +0000
+++ account_banking_iban_lookup/online.py 2014-02-09 20:21:36 +0000
@@ -1,4 +1,4 @@
-# -*- encoding: utf-8 -*-
+# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
@@ -26,7 +26,7 @@
import urllib, urllib2
from BeautifulSoup import BeautifulSoup
from openerp.addons.account_banking.sepa import postalcode
-from openerp.addons.account_banking.sepa.urlagent import URLAgent, SoupForm
+from openerp.addons.account_banking_iban_lookup.urlagent import URLAgent, SoupForm
from openerp.addons.account_banking.sepa.iban import IBAN
from openerp.addons.account_banking.struct import struct
=== renamed file 'account_banking/sepa/urlagent.py' => 'account_banking_iban_lookup/urlagent.py'
--- account_banking/sepa/urlagent.py 2013-04-15 13:59:50 +0000
+++ account_banking_iban_lookup/urlagent.py 2014-02-09 20:21:36 +0000
@@ -25,7 +25,6 @@
'''
import urllib
-from BeautifulSoup import BeautifulSoup
__all__ = ['urlsplit', 'urljoin', 'pathbase', 'urlbase', 'SoupForm',
'URLAgent'
=== added directory 'account_banking_iban_lookup/view'
=== added file 'account_banking_iban_lookup/view/res_bank.xml'
--- account_banking_iban_lookup/view/res_bank.xml 1970-01-01 00:00:00 +0000
+++ account_banking_iban_lookup/view/res_bank.xml 2014-02-09 20:21:36 +0000
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <record id="view_res_bank_account_banking_form_1" model="ir.ui.view">
+ <field name="name">Add BIC lookup to bank form</field>
+ <field name="model">res.bank</field>
+ <field name="inherit_id" ref="base.view_res_bank_form"/>
+ <field name="arch" type="xml">
+ <field name="bic" position="replace">
+ <field name="bic" on_change="onchange_bic(bic, name)"/>
+ </field>
+ </field>
+ </record>
+ </data>
+</openerp>
=== added file 'account_banking_iban_lookup/view/res_partner_bank.xml'
--- account_banking_iban_lookup/view/res_partner_bank.xml 1970-01-01 00:00:00 +0000
+++ account_banking_iban_lookup/view/res_partner_bank.xml 2014-02-09 20:21:36 +0000
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <record id="view_partner_bank_account_banking_form_2" model="ir.ui.view">
+ <field name="name">Add autocompletion methods to partner bank form</field>
+ <field name="model">res.partner.bank</field>
+ <field name="inherit_id" ref="base.view_partner_bank_form"/>
+ <field name="priority" eval="24"/>
+ <field name="arch" type="xml">
+ <data>
+ <field name="acc_number" position="attributes">
+ <attribute name="on_change">onchange_acc_number(acc_number, acc_number_domestic, state, partner_id, country_id)</attribute>
+ </field>
+ <field name="acc_number_domestic" position="attributes">
+ <attribute name="on_change">onchange_domestic(acc_number_domestic, partner_id, country_id)</attribute>
+ </field>
+ </data>
+ </field>
+ </record>
+ </data>
+</openerp>
+
+
=== modified file 'account_banking_nl_clieop/__openerp__.py'
--- account_banking_nl_clieop/__openerp__.py 2013-05-28 15:18:26 +0000
+++ account_banking_nl_clieop/__openerp__.py 2014-02-09 20:21:36 +0000
@@ -24,7 +24,10 @@
'author': 'EduSense BV',
'website': 'http://www.edusense.nl',
'category': 'Account Banking',
- 'depends': ['account_banking_payment'],
+ 'depends': [
+ 'account_banking_payment',
+ 'account_iban_preserve_domestic',
+ ],
'data': [
'account_banking_nl_clieop.xml',
'wizard/export_clieop_view.xml',
=== modified file 'account_banking_nl_multibank/__openerp__.py'
--- account_banking_nl_multibank/__openerp__.py 2013-04-15 13:56:18 +0000
+++ account_banking_nl_multibank/__openerp__.py 2014-02-09 20:21:36 +0000
@@ -20,7 +20,7 @@
##############################################################################
{
- 'name': 'Account Banking',
+ 'name': 'Account Banking - NL Multibank import',
'version': '0.62',
'license': 'AGPL-3',
'author': 'EduSense BV',
=== modified file 'account_banking_payment/model/banking_import_transaction.py'
--- account_banking_payment/model/banking_import_transaction.py 2013-06-04 13:44:24 +0000
+++ account_banking_payment/model/banking_import_transaction.py 2014-02-09 20:21:36 +0000
@@ -116,14 +116,26 @@
'''
# TODO: Not sure what side effects are created when payments are done
# for credited customer invoices, which will be matched later on too.
+
+ def bank_match(account, partner_bank):
+ """
+ Returns whether a given account number is equivalent to a
+ partner bank in the database. We simply call the search method,
+ which checks IBAN, domestic and disregards from spaces in IBANs.
+
+ :param account: string representation of a bank account number
+ :param partner_bank: browse record of model res.partner.bank
+ """
+ return partner_bank.id in self.pool['res.partner.bank'].search(
+ cr, uid, [('acc_number', '=', account)])
+
digits = dp.get_precision('Account')(cr)[1]
candidates = [
- x for x in payment_lines
- if x.communication == trans.reference
- and round(x.amount, digits) == -round(
- trans.statement_line_id.amount, digits)
- and trans.remote_account in (x.bank_id.acc_number,
- x.bank_id.acc_number_domestic)
+ line for line in payment_lines
+ if (line.communication == trans.reference
+ and round(line.amount, digits) == -round(
+ trans.statement_line_id.amount, digits)
+ and bank_match(trans.remote_account, line.bank_id))
]
if len(candidates) == 1:
candidate = candidates[0]
References