1
# -*- coding: utf-8 -*-
2
##############################################################################
4
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
5
# (C) 2011 - 2014 Therp BV (<http://therp.nl>).
7
# All other contributions are (C) by their respective contributors
11
# This program is free software: you can redistribute it and/or modify
12
# it under the terms of the GNU Affero General Public License as
13
# published by the Free Software Foundation, either version 3 of the
14
# License, or (at your option) any later version.
16
# This program is distributed in the hope that it will be useful,
17
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
# GNU Affero General Public License for more details.
21
# You should have received a copy of the GNU Affero General Public License
22
# along with this program. If not, see <http://www.gnu.org/licenses/>.
24
##############################################################################
25
from openerp import SUPERUSER_ID
26
from openerp.osv import orm
27
from openerp.tools.translate import _
28
from openerp.addons.account_banking_iban_lookup import online
29
from openerp.addons.account_banking import sepa
30
from openerp.addons.account_banking.wizard.banktools import get_or_create_bank
33
def warning(title, message):
34
'''Convenience routine'''
35
return {'warning': {'title': title, 'message': message}}
38
class res_partner_bank(orm.Model):
40
Extended functionality:
41
1. BBAN and IBAN are considered equal
42
2. Online lookup when an API is available (providing NL in this module)
43
3. Banks are created on the fly when using IBAN + online
45
5. BBAN's are generated from IBAN when possible
47
_inherit = 'res.partner.bank'
51
Update existing iban accounts to comply to new regime
54
partner_bank_obj = self.pool.get('res.partner.bank')
55
bank_ids = partner_bank_obj.search(
56
cr, SUPERUSER_ID, [('state', '=', 'iban')], limit=0)
57
for bank in partner_bank_obj.read(cr, SUPERUSER_ID, bank_ids):
59
if bank['state'] == 'iban':
60
iban_acc = sepa.IBAN(bank['acc_number'])
62
write_vals['acc_number_domestic'] = iban_acc.localized_BBAN
63
write_vals['acc_number'] = str(iban_acc)
64
elif bank['acc_number'] != bank['acc_number'].upper():
65
write_vals['acc_number'] = bank['acc_number'].upper()
67
partner_bank_obj.write(
68
cr, SUPERUSER_ID, bank['id'], write_vals)
71
def _correct_IBAN(acc_number):
73
Routine to correct IBAN values and deduce localized values when valid.
74
Note: No check on validity IBAN/Country
76
iban = sepa.IBAN(acc_number)
77
return (str(iban), iban.localized_BBAN)
79
def create(self, cr, uid, vals, context=None):
81
Create dual function IBAN account for SEPA countries
83
if vals.get('state') == 'iban':
84
iban = (vals.get('acc_number')
85
or vals.get('acc_number_domestic', False))
86
vals['acc_number'], vals['acc_number_domestic'] = (
87
self._correct_IBAN(iban))
88
return super(res_partner_bank, self).create(
89
cr, uid, vals, context)
91
def write(self, cr, uid, ids, vals, context=None):
93
Create dual function IBAN account for SEPA countries
95
Update the domestic account number when the IBAN is
96
written, or clear the domestic number on regular account numbers.
98
if ids and isinstance(ids, (int, long)):
100
for account in self.read(
101
cr, uid, ids, ['state', 'acc_number']):
102
if 'state' in vals or 'acc_number' in vals:
104
if account['state'] == 'iban':
105
vals['acc_number'], vals['acc_number_domestic'] = (
106
self._correct_IBAN(account['acc_number']))
108
vals['acc_number_domestic'] = False
109
super(res_partner_bank, self).write(
110
cr, uid, account['id'], vals, context)
113
def onchange_acc_number(
114
self, cr, uid, ids, acc_number, acc_number_domestic,
115
state, partner_id, country_id, context=None):
117
return self.onchange_iban(
118
cr, uid, ids, acc_number, acc_number_domestic,
119
state, partner_id, country_id, context=None
122
return self.onchange_domestic(
123
cr, uid, ids, acc_number,
124
partner_id, country_id, context=None
127
def onchange_domestic(
128
self, cr, uid, ids, acc_number,
129
partner_id, country_id, context=None):
131
Trigger to find IBAN. When found:
135
TODO: prevent unnecessary assignment of country_ids and
136
browsing of the country
142
country_obj = self.pool.get('res.country')
146
# Pre fill country based on available data. This is just a default
147
# which can be overridden by the user.
148
# 1. Use provided country_id (manually filled)
150
country = country_obj.browse(cr, uid, country_id, context=context)
151
country_ids = [country_id]
152
# 2. Use country_id of found bank accounts
153
# This can be usefull when there is no country set in the partners
154
# addresses, but there was a country set in the address for the bank
155
# account itself before this method was triggered.
156
elif ids and len(ids) == 1:
157
partner_bank_obj = self.pool.get('res.partner.bank')
158
partner_bank_id = partner_bank_obj.browse(
159
cr, uid, ids[0], context=context)
160
if partner_bank_id.country_id:
161
country = partner_bank_id.country_id
162
country_ids = [country.id]
163
# 3. Use country_id of default address of partner
164
# The country_id of a bank account is a one time default on creation.
165
# It originates in the same address we are about to check, but
166
# modifications on that address afterwards are not transfered to the
167
# bank account, hence the additional check.
169
partner_obj = self.pool.get('res.partner')
170
country = partner_obj.browse(
171
cr, uid, partner_id, context=context).country
172
country_ids = country and [country.id] or []
173
# 4. Without any of the above, take the country from the company of
176
user = self.pool.get('res.users').browse(
177
cr, uid, uid, context=context)
178
# Try user companies partner (user no longer has address in 6.1)
179
if (user.company_id and
180
user.company_id.partner_id and
181
user.company_id.partner_id.country):
182
country_ids = [user.company_id.partner_id.country.id]
184
if (user.company_id and user.company_id.partner_id and
185
user.company_id.partner_id.country):
186
country_ids = [user.company_id.partner_id.country.id]
188
# Ok, tried everything, give up and leave it to the user
189
return warning(_('Insufficient data'),
190
_('Insufficient data to select online '
191
'conversion database')
193
result = {'value': values}
194
# Complete data with online database when available
196
country = country_obj.browse(
197
cr, uid, country_ids[0], context=context)
198
values['country_id'] = country_ids[0]
199
if country and country.code in sepa.IBAN.countries:
200
info = online.account_info(country.code, acc_number)
202
iban_acc = sepa.IBAN(info.iban)
204
values['acc_number_domestic'] = iban_acc.localized_BBAN
205
values['acc_number'] = unicode(iban_acc)
206
values['state'] = 'iban'
207
bank_id, country_id = get_or_create_bank(
209
info.bic or iban_acc.BIC_searchkey,
212
values['country_id'] = country_id
213
values['bank'] = bank_id or False
215
values['bank_bic'] = info.bic
219
result.update(warning(
221
_('The account number appears to be invalid for %s')
225
if country.code in sepa.IBAN.countries:
226
acc_number_fmt = sepa.BBAN(acc_number, country.code)
227
if acc_number_fmt.valid:
228
values['acc_number_domestic'] = str(acc_number_fmt)
230
result.update(warning(
232
_('The account number has the wrong format for %s')
238
self, cr, uid, ids, acc_number, acc_number_domestic,
239
state, partner_id, country_id, context=None):
241
Trigger to verify IBAN. When valid:
242
1. Extract BBAN as local account
243
2. Auto complete bank
248
iban_acc = sepa.IBAN(acc_number)
250
bank_id, country_id = get_or_create_bank(
251
self.pool, cr, uid, iban_acc.BIC_searchkey,
252
code=iban_acc.BIC_searchkey
256
acc_number_domestic=iban_acc.localized_BBAN,
257
acc_number=unicode(iban_acc),
258
country=country_id or False,
259
bank=bank_id or False,
263
_('Invalid IBAN account number!'),
264
_("The IBAN number doesn't seem to be correct"))
266
def online_account_info(
267
self, cr, uid, country_code, acc_number, context=None):
269
Overwrite API hook from account_banking
271
return online.account_info(country_code, acc_number)