[FIX] account: Fix Receive bank account on register payment wizard

- Create a partner with 2 bank accounts: BNK1 and BNK2

Wrong flow with vendor bill:
- Create an vendor bill => BNK1 is set by default
- Change BNK1 to BNK2
- Register a payment
=> BNK1 is proposed by default instead of BNK2

Wrong flow with customer invoice:
- Set BNK3 on your company
- Create a customer invoice
- Register a payment
=> BNK1 is suggested instead of BNK3

closes odoo/odoo#80175

Issue: 2683197
X-original-commit: ec0d807b2b6ff3596d9b591bd8ec7d8889d41d8a
Signed-off-by: Olivier Colson <oco@odoo.com>
Signed-off-by: Laurent Smet <las@openerp.com>
This commit is contained in:
Laurent Smet
2021-11-23 08:53:39 +00:00
parent 64c8e4eadd
commit 6227007b7a
5 changed files with 131 additions and 100 deletions
+10 -7
View File
@@ -369,18 +369,21 @@ class AccountPayment(models.Model):
else:
payment.amount_signed = payment.amount
@api.depends('partner_id', 'destination_journal_id', 'is_internal_transfer')
@api.depends('partner_id', 'company_id', 'payment_type', 'destination_journal_id', 'is_internal_transfer')
def _compute_partner_bank_id(self):
''' The default partner_bank_id will be the first available on the partner. '''
for pay in self:
if pay.is_internal_transfer:
pay.partner_bank_id = self.destination_journal_id.bank_account_id
if pay.payment_type == 'inbound':
bank_partner = pay.company_id.partner_id
else:
available_partner_bank_accounts = pay.partner_id.bank_ids.filtered(lambda x: x.company_id in (False, pay.company_id))
if available_partner_bank_accounts:
bank_partner = pay.partner_id
available_partner_bank_accounts = bank_partner.bank_ids.filtered(lambda x: x.company_id in (False, pay.company_id))
if available_partner_bank_accounts:
if pay.partner_bank_id not in available_partner_bank_accounts:
pay.partner_bank_id = available_partner_bank_accounts[0]._origin
else:
pay.partner_bank_id = False
else:
pay.partner_bank_id = False
@api.depends('partner_id', 'destination_account_id', 'journal_id')
def _compute_is_internal_transfer(self):
+27 -13
View File
@@ -16,21 +16,27 @@ class TestAccountPayment(AccountTestInvoicingCommon):
cls.payment_debit_account_id = cls.copy_account(company_id.account_journal_payment_debit_account_id)
cls.payment_credit_account_id = cls.copy_account(company_id.account_journal_payment_credit_account_id)
cls.partner_bank_account = cls.env['res.partner.bank'].create({
'acc_number': 'BE32707171912447',
cls.partner_bank_account1 = cls.env['res.partner.bank'].create({
'acc_number': "0123456789",
'partner_id': cls.partner_a.id,
'acc_type': 'bank',
})
cls.partner_bank_account2 = cls.env['res.partner.bank'].create({
'acc_number': "9876543210",
'partner_id': cls.partner_a.id,
'acc_type': 'bank',
})
cls.comp_bank_account = cls.env['res.partner.bank'].create({
'acc_number': "985632147",
'partner_id': cls.env.company.partner_id.id,
'acc_type': 'bank',
})
company_id.write({
'account_journal_payment_debit_account_id': cls.payment_debit_account_id.id,
'account_journal_payment_credit_account_id': cls.payment_credit_account_id.id
})
cls.partner_a.write({
'bank_ids': [(6, 0, cls.partner_bank_account.ids)],
})
def test_payment_move_sync_create_write(self):
copy_receivable = self.copy_account(self.company_data['default_account_receivable'])
@@ -51,12 +57,10 @@ class TestAccountPayment(AccountTestInvoicingCommon):
'partner_id': False,
'destination_account_id': copy_receivable.id,
'payment_method_line_id': self.inbound_payment_method_line.id,
'partner_bank_id': False,
}
expected_move_values = {
'currency_id': self.company_data['currency'].id,
'partner_id': False,
'partner_bank_id': False,
}
expected_liquidity_line = {
'debit': 50.0,
@@ -94,13 +98,11 @@ class TestAccountPayment(AccountTestInvoicingCommon):
'destination_account_id': self.partner_a.property_account_payable_id.id,
'currency_id': self.currency_data['currency'].id,
'partner_id': self.partner_a.id,
'partner_bank_id': self.partner_bank_account.id,
}])
self.assertRecordValues(payment.move_id, [{
**expected_move_values,
'currency_id': self.currency_data['currency'].id,
'partner_id': self.partner_a.id,
'partner_bank_id': self.partner_bank_account.id,
}])
self.assertRecordValues(payment.line_ids.sorted('balance'), [
{
@@ -124,7 +126,6 @@ class TestAccountPayment(AccountTestInvoicingCommon):
liquidity_lines, counterpart_lines, writeoff_lines = payment._seek_for_lines()
payment.move_id.write({
'partner_bank_id': False,
'line_ids': [
(1, counterpart_lines.id, {
'debit': 0.0,
@@ -798,7 +799,6 @@ class TestAccountPayment(AccountTestInvoicingCommon):
'currency_id': self.company_data['currency'].id,
'partner_id': False,
'payment_method_line_id': self.inbound_payment_method_line.id,
'partner_bank_id': False,
}])
payment.payment_type = 'outbound'
@@ -811,5 +811,19 @@ class TestAccountPayment(AccountTestInvoicingCommon):
'currency_id': self.company_data['currency'].id,
'partner_id': False,
'payment_method_line_id': self.outbound_payment_method_line.id,
'partner_bank_id': False,
}])
def test_payment_partner_bank_inbound(self):
""" Test the bank account is well recomputed for inbound payments. In that case, the recipient
bank account must be the one set on the company.
"""
payment = self.env['account.payment'].create({
'amount': 50.0,
'payment_type': 'outbound',
'partner_type': 'supplier',
'partner_id': self.partner_a.id,
})
self.assertRecordValues(payment, [{'partner_bank_id': self.partner_bank_account1.id}])
payment.payment_type = 'inbound'
self.assertRecordValues(payment, [{'partner_bank_id': self.comp_bank_account.id}])
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.exceptions import UserError
from odoo.tests import tagged
from odoo.tests import tagged, Form
@tagged('post_install', '-at_install')
@@ -21,6 +21,22 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon):
cls.payment_debit_account_id = cls.company_data['default_journal_bank'].company_id.account_journal_payment_debit_account_id.copy()
cls.payment_credit_account_id = cls.company_data['default_journal_bank'].company_id.account_journal_payment_credit_account_id.copy()
cls.partner_bank_account = cls.env['res.partner.bank'].create({
'acc_number': "0123456789",
'partner_id': cls.partner_a.id,
'acc_type': 'bank',
})
cls.comp_bank_account1 = cls.env['res.partner.bank'].create({
'acc_number': "985632147",
'partner_id': cls.env.company.partner_id.id,
'acc_type': 'bank',
})
cls.comp_bank_account2 = cls.env['res.partner.bank'].create({
'acc_number': "741258963",
'partner_id': cls.env.company.partner_id.id,
'acc_type': 'bank',
})
# Customer invoices sharing the same batch.
cls.out_invoice_1 = cls.env['account.move'].create({
'move_type': 'out_invoice',
@@ -389,6 +405,7 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon):
payments = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'group_payment': True,
})._create_payments()
self.assertRecordValues(payments, [
{
'ref': 'BILL/2017/01/0001 BILL/2017/01/0002 RBILL/2017/01/0001',
@@ -416,23 +433,23 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon):
def test_register_payment_multiple_batch_grouped_with_credit_note(self):
''' Do not batch payments if multiple partner_bank_id '''
test_bank = self.env['res.bank'].create({'name': 'test'})
bank1 = self.env['res.partner.bank'].create({
'acc_number': 'BE43798822936101',
'partner_id': self.partner_a.id,
'bank_id': test_bank.id,
})
bank2 = self.env['res.partner.bank'].create({
'acc_number': 'BE85812541345906',
'partner_id': self.partner_a.id,
'bank_id': test_bank.id,
})
self.in_invoice_1.partner_bank_id = bank1
self.in_invoice_2.partner_bank_id = bank2
active_ids = (self.in_invoice_1 + self.in_invoice_2 + self.in_refund_1).ids
payments = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'group_payment': True,
})._create_payments()
self.assertRecordValues(payments, [
{
'ref': 'BILL/2017/01/0001',
@@ -497,13 +514,11 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon):
'reconciled': False,
},
])
self.in_invoice_1.partner_bank_id = None
self.in_invoice_2.partner_bank_id = None
def test_register_payment_multi_batches_grouped(self):
''' Choose to pay multiple batches, one with two customer invoices (1000 + 2000)
and one with a vendor bill of 600, by grouping payments.
'''
and one with a vendor bill of 600, by grouping payments.
'''
active_ids = (self.in_invoice_1 + self.in_invoice_2 + self.in_invoice_3).ids
payments = self.env['account.payment.register'].with_context(active_model='account.move', active_ids=active_ids).create({
'group_payment': True,
@@ -633,6 +648,27 @@ class TestAccountPaymentRegister(AccountTestInvoicingCommon):
},
])
def test_register_payment_custom_bank_account(self):
""" Ensure the user is able to select a custom bank account when registering a payment and this bank account
lands correctly on the generated payment.
"""
self.out_invoice_1.partner_bank_id = self.comp_bank_account1
ctx = {'active_model': 'account.move', 'active_ids': self.out_invoice_1.ids}
wizard_form = Form(self.env['account.payment.register'].with_context(**ctx))
wizard = wizard_form.save()
# The bank account set on the invoice must be the default suggested value.
self.assertRecordValues(wizard, [{'partner_bank_id': self.comp_bank_account1.id}])
wizard_form = Form(wizard)
wizard_form.partner_bank_id = self.comp_bank_account2
wizard = wizard_form.save()
payments = wizard._create_payments()
# The user should be able to set a custom bank account.
self.assertRecordValues(payments, [{'partner_bank_id': self.comp_bank_account2.id}])
def test_register_payment_constraints(self):
# Test to register a payment for a draft journal entry.
self.out_invoice_1.button_draft()
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from collections import defaultdict
from odoo import models, fields, api, _
from odoo.exceptions import UserError
@@ -29,10 +30,18 @@ class AccountPaymentRegister(models.TransientModel):
comodel_name='account.journal',
compute='_compute_available_journal_ids'
)
partner_bank_id = fields.Many2one('res.partner.bank', string="Recipient Bank Account",
readonly=False, store=True,
available_partner_bank_ids = fields.Many2many(
comodel_name='res.partner.bank',
compute='_compute_available_partner_bank_ids',
)
partner_bank_id = fields.Many2one(
comodel_name='res.partner.bank',
string="Recipient Bank Account",
readonly=False,
store=True,
compute='_compute_partner_bank_id',
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id), ('partner_id', '=', partner_id)]")
domain="[('id', 'in', available_partner_bank_ids)]",
)
company_currency_id = fields.Many2one('res.currency', string="Company Currency",
related='company_id.currency_id')
@@ -126,18 +135,23 @@ class AccountPaymentRegister(models.TransientModel):
will be grouped together.
:return: A python dictionary.
'''
res = {
move = line.move_id
partner_bank_account = self.env['res.partner.bank']
if move.is_invoice(include_receipts=True):
partner_bank_account = move.partner_bank_id._origin
return {
'partner_id': line.partner_id.id,
'account_id': line.account_id.id,
'currency_id': (line.currency_id or line.company_currency_id).id,
'partner_bank_id': partner_bank_account.id,
'partner_type': 'customer' if line.account_internal_type == 'receivable' else 'supplier',
}
return res
def _get_batches(self):
''' Group the account.move.line linked to the wizard together.
Lines are groupes if they share 'partner_id','account_id','currency_id' & 'partner_type' and if
Lines are grouped if they share 'partner_id','account_id','currency_id' & 'partner_type' and if
0 or 1 partner_bank_id can be determined for the group.
:return: A list of batches, each one containing:
* payment_values: A dictionary of payment values.
@@ -152,69 +166,21 @@ class AccountPaymentRegister(models.TransientModel):
if not lines:
raise UserError(_("You can't open the register payment wizard without at least one receivable/payable line."))
# Lines are grouped first on 'partner_id','account_id','currency_id' & 'partner_type' and
# then on 'partner_bank_id' & 'payment_type'. 'batches' data structure :
# {common_pay_val_key:{
# part_bank_pay_type_key : lines,
# part_bank_pay_type_key : lines,
# }}
batches = {}
batches = defaultdict(lambda: {'lines': self.env['account.move.line']})
for line in lines:
batch_key = self._get_line_batch_key(line)
common_pay_val_key = frozendict(batch_key)
part_bank_pay_type_key = frozendict(
payment_type='inbound' if line.balance > 0.0 else 'outbound',
partner_bank_id=line.move_id.partner_bank_id.id,
)
batches.setdefault(common_pay_val_key, {})
batches[common_pay_val_key].setdefault(part_bank_pay_type_key, self.env['account.move.line'])
batches[common_pay_val_key][part_bank_pay_type_key] += line
serialized_key = '-'.join(str(v) for v in batch_key.values())
vals = batches[serialized_key]
vals['payment_values'] = batch_key
vals['lines'] += line
res = []
# Compute 'payment_type'.
for vals in batches.values():
lines = vals['lines']
balance = sum(lines.mapped('balance'))
vals['payment_values']['payment_type'] = 'inbound' if balance > 0.0 else 'outbound'
for common_pay_val_key, common_params_group in batches.items():
# Group all lines with same 'partner_id','account_id','currency_id' & 'partner_type' in one batch if:
# - the resulting batch payment is outbound and there is max 1 partner_bank_id for all outbound lines
# or
# - the resulting batch payment is inbound and there is max 1 partner_bank_id for all inbound lines
all_batch_aml = self.env['account.move.line'].browse(
id
for lines in common_params_group.values()
for id in lines.ids
)
total = sum(all_batch_aml.mapped('balance'))
if float_compare(total, 0.0, precision_digits=line.move_id.currency_id.decimal_places) > 0.0:
if_grouped_payment_type = 'inbound'
else:
if_grouped_payment_type = 'outbound'
partner_bank_id_candidates = {
key['partner_bank_id']
for key in common_params_group
if key['partner_bank_id'] and key['payment_type'] == if_grouped_payment_type
}
if len(partner_bank_id_candidates) < 2:
# condition is met, grouping in 1 batch
partner_bank_id = partner_bank_id_candidates and partner_bank_id_candidates.pop() or None
payment_values = dict(common_pay_val_key)
payment_values['partner_bank_id'] = partner_bank_id
payment_values['payment_type'] = if_grouped_payment_type
res.append({
'payment_values': payment_values,
'lines': all_batch_aml,
})
else:
# no grouping
for part_bank_pay_type_key, lines in common_params_group.items():
payment_values = dict(common_pay_val_key)
payment_values['payment_type'] = part_bank_pay_type_key['payment_type']
payment_values['partner_bank_id'] = part_bank_pay_type_key['partner_bank_id']
res.append({
'payment_values': payment_values,
'lines': lines,
})
return res
return list(batches.values())
@api.model
def _get_wizard_values_from_batch(self, batch_result):
@@ -268,6 +234,7 @@ class AccountPaymentRegister(models.TransientModel):
'partner_id': False,
'partner_type': False,
'payment_type': wizard_values_from_batch['payment_type'],
'partner_bank_id': False,
'source_currency_id': False,
'source_amount': False,
'source_amount_currency': False,
@@ -345,13 +312,23 @@ class AccountPaymentRegister(models.TransientModel):
for wizard in self:
wizard.currency_id = wizard.journal_id.currency_id or wizard.source_currency_id or wizard.company_id.currency_id
@api.depends('partner_id')
def _compute_partner_bank_id(self):
''' The default partner_bank_id will be the first available on the partner. '''
@api.depends('company_id', 'can_edit_wizard')
def _compute_available_partner_bank_ids(self):
for wizard in self:
available_partner_bank_accounts = wizard.partner_id.bank_ids.filtered(lambda x: x.company_id in (False, wizard.company_id))
if available_partner_bank_accounts:
wizard.partner_bank_id = available_partner_bank_accounts[0]._origin
if wizard.can_edit_wizard:
batches = wizard._get_batches()
bank_partners = batches[0]['lines'].move_id.bank_partner_id
wizard.available_partner_bank_ids = bank_partners.bank_ids\
.filtered(lambda x: x.company_id in (False, wizard.company_id))._origin
else:
wizard.available_partner_bank_ids = False
@api.depends('available_partner_bank_ids')
def _compute_partner_bank_id(self):
for wizard in self:
if wizard.can_edit_wizard:
batches = wizard._get_batches()
wizard.partner_bank_id = self.env['res.partner.bank'].browse(batches[0]['payment_values']['partner_bank_id'])
else:
wizard.partner_bank_id = False
@@ -24,6 +24,7 @@
<field name="require_partner_bank_account" invisible="1"/>
<field name="available_journal_ids" invisible="1"/>
<field name="available_payment_method_line_ids" invisible="1"/>
<field name="available_partner_bank_ids" invisible="1"/>
<field name="company_currency_id" invisible="1"/>
<group>