Files
odoo_source/odoo/addons/base/models/res_bank.py
T
Nicolas (vin) 68f4534aa5 [IMP] account_*, l10n_*: allows the same bank account on multiple partners
Some use cases require the use of the same account number on multiple
partners. This will relax the constraints to allows such use cases, and
adapt to avoid setting the partner or bank when we cannot be sure of the
one we should take (on bank statement line, and during reconciliation)

Task id #2692025

closes odoo/odoo#81362

Related: odoo/enterprise#22881
Signed-off-by: Laurent Smet <las@odoo.com>
2022-01-25 13:52:29 +00:00

131 lines
5.0 KiB
Python

# -*- coding: utf-8 -*-
import re
from collections.abc import Iterable
from odoo import api, fields, models, _
from odoo.osv import expression
def sanitize_account_number(acc_number):
if acc_number:
return re.sub(r'\W+', '', acc_number).upper()
return False
class Bank(models.Model):
_description = 'Bank'
_name = 'res.bank'
_order = 'name'
name = fields.Char(required=True)
street = fields.Char()
street2 = fields.Char()
zip = fields.Char()
city = fields.Char()
state = fields.Many2one('res.country.state', 'Fed. State', domain="[('country_id', '=?', country)]")
country = fields.Many2one('res.country')
email = fields.Char()
phone = fields.Char()
active = fields.Boolean(default=True)
bic = fields.Char('Bank Identifier Code', index=True, help="Sometimes called BIC or Swift.")
def name_get(self):
result = []
for bank in self:
name = bank.name + (bank.bic and (' - ' + bank.bic) or '')
result.append((bank.id, name))
return result
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
args = args or []
domain = []
if name:
domain = ['|', ('bic', '=ilike', name + '%'), ('name', operator, name)]
if operator in expression.NEGATIVE_TERM_OPERATORS:
domain = ['&'] + domain
return self._search(domain + args, limit=limit, access_rights_uid=name_get_uid)
@api.onchange('country')
def _onchange_country_id(self):
if self.country and self.country != self.state.country_id:
self.state = False
@api.onchange('state')
def _onchange_state(self):
if self.state.country_id:
self.country = self.state.country_id
class ResPartnerBank(models.Model):
_name = 'res.partner.bank'
_rec_name = 'acc_number'
_description = 'Bank Accounts'
_order = 'sequence, id'
@api.model
def get_supported_account_types(self):
return self._get_supported_account_types()
@api.model
def _get_supported_account_types(self):
return [('bank', _('Normal'))]
active = fields.Boolean(default=True)
acc_type = fields.Selection(selection=lambda x: x.env['res.partner.bank'].get_supported_account_types(), compute='_compute_acc_type', string='Type', help='Bank account type: Normal or IBAN. Inferred from the bank account number.')
acc_number = fields.Char('Account Number', required=True)
sanitized_acc_number = fields.Char(compute='_compute_sanitized_acc_number', string='Sanitized Account Number', readonly=True, store=True)
acc_holder_name = fields.Char(string='Account Holder Name', help="Account holder name, in case it is different than the name of the Account Holder")
partner_id = fields.Many2one('res.partner', 'Account Holder', ondelete='cascade', index=True, domain=['|', ('is_company', '=', True), ('parent_id', '=', False)], required=True)
bank_id = fields.Many2one('res.bank', string='Bank')
bank_name = fields.Char(related='bank_id.name', readonly=False)
bank_bic = fields.Char(related='bank_id.bic', readonly=False)
sequence = fields.Integer(default=10)
currency_id = fields.Many2one('res.currency', string='Currency')
company_id = fields.Many2one('res.company', 'Company', related='partner_id.company_id', store=True, readonly=True)
_sql_constraints = [(
'unique_number',
'unique(sanitized_acc_number, partner_id)',
'The combination Account Number/Partner must be unique.'
)]
@api.depends('acc_number')
def _compute_sanitized_acc_number(self):
for bank in self:
bank.sanitized_acc_number = sanitize_account_number(bank.acc_number)
@api.depends('acc_number')
def _compute_acc_type(self):
for bank in self:
bank.acc_type = self.retrieve_acc_type(bank.acc_number)
@api.model
def retrieve_acc_type(self, acc_number):
""" To be overridden by subclasses in order to support other account_types.
"""
return 'bank'
def name_get(self):
return [(acc.id, '{} - {}'.format(acc.acc_number, acc.bank_id.name) if acc.bank_id else acc.acc_number)
for acc in self]
@api.model
def _search(self, args, offset=0, limit=None, order=None, count=False, access_rights_uid=None):
pos = 0
while pos < len(args):
# DLE P14
if args[pos][0] == 'acc_number':
op = args[pos][1]
value = args[pos][2]
if not isinstance(value, str) and isinstance(value, Iterable):
value = [sanitize_account_number(i) for i in value]
else:
value = sanitize_account_number(value)
if 'like' in op:
value = '%' + value + '%'
args[pos] = ('sanitized_acc_number', op, value)
pos += 1
return super(ResPartnerBank, self)._search(args, offset, limit, order, count=count, access_rights_uid=access_rights_uid)