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>
131 lines
5.0 KiB
Python
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)
|