[FIX] hr_expense: Expense not cancelled
Current behavior: When cancelling an expense payment, the expense state was not modified and was still 'paid' when it should be 'refused'. Steps to reproduce: - Create an expense and post the expense and register a payment for the expense. - Then go the vendor payments and cancel the associated vendor payment. - The expense still shows as paid in the expenses page. opw-2711383 closes odoo/odoo#82496 X-original-commit: 16459957d23f4bdfddfe96d72ef96cbdc6fec539 Signed-off-by: Laurent Smet <las@odoo.com> Signed-off-by: Engels Robin (roen) <roen@odoo.com>
This commit is contained in:
@@ -538,41 +538,29 @@ class AccountPaymentRegister(models.TransientModel):
|
||||
'destination_account_id': batch_result['lines'][0].account_id.id
|
||||
}
|
||||
|
||||
def _create_payments(self):
|
||||
self.ensure_one()
|
||||
batches = self._get_batches()
|
||||
edit_mode = self.can_edit_wizard and (len(batches[0]['lines']) == 1 or self.group_payment)
|
||||
def _init_payments(self, to_process, edit_mode=False):
|
||||
""" Create the payments.
|
||||
|
||||
to_reconcile = []
|
||||
if edit_mode:
|
||||
payment_vals = self._create_payment_vals_from_wizard()
|
||||
payment_vals_list = [payment_vals]
|
||||
to_reconcile.append(batches[0]['lines'])
|
||||
else:
|
||||
# Don't group payments: Create one batch per move.
|
||||
if not self.group_payment:
|
||||
new_batches = []
|
||||
for batch_result in batches:
|
||||
for line in batch_result['lines']:
|
||||
new_batches.append({
|
||||
**batch_result,
|
||||
'lines': line,
|
||||
})
|
||||
batches = new_batches
|
||||
:param to_process: A list of python dictionary, one for each payment to create, containing:
|
||||
* create_vals: The values used for the 'create' method.
|
||||
* to_reconcile: The journal items to perform the reconciliation.
|
||||
* batch: A python dict containing everything you want about the source journal items
|
||||
to which a payment will be created (see '_get_batches').
|
||||
:param edit_mode: Is the wizard in edition mode.
|
||||
"""
|
||||
|
||||
payment_vals_list = []
|
||||
for batch_result in batches:
|
||||
payment_vals_list.append(self._create_payment_vals_from_batch(batch_result))
|
||||
to_reconcile.append(batch_result['lines'])
|
||||
payments = self.env['account.payment'].create([x['create_vals'] for x in to_process])
|
||||
|
||||
payments = self.env['account.payment'].create(payment_vals_list)
|
||||
for payment, vals in zip(payments, to_process):
|
||||
vals['payment'] = payment
|
||||
|
||||
# If payments are made using a currency different than the source one, ensure the balance match exactly in
|
||||
# order to fully paid the source journal items.
|
||||
# For example, suppose a new currency B having a rate 100:1 regarding the company currency A.
|
||||
# If you try to pay 12.15A using 0.12B, the computed balance will be 12.00A for the payment instead of 12.15A.
|
||||
if edit_mode:
|
||||
lines = vals['to_reconcile']
|
||||
|
||||
# If payments are made using a currency different than the source one, ensure the balance match exactly in
|
||||
# order to fully paid the source journal items.
|
||||
# For example, suppose a new currency B having a rate 100:1 regarding the company currency A.
|
||||
# If you try to pay 12.15A using 0.12B, the computed balance will be 12.00A for the payment instead of 12.15A.
|
||||
if edit_mode:
|
||||
for payment, lines in zip(payments, to_reconcile):
|
||||
# Batches are made using the same currency so making 'lines.currency_id' is ok.
|
||||
if payment.currency_id != lines.currency_id:
|
||||
liquidity_lines, counterpart_lines, writeoff_lines = payment._seek_for_lines()
|
||||
@@ -603,23 +591,78 @@ class AccountPaymentRegister(models.TransientModel):
|
||||
(1, debit_lines[0].id, {'debit': debit_lines[0].debit + delta_balance}),
|
||||
(1, credit_lines[0].id, {'credit': credit_lines[0].credit + delta_balance}),
|
||||
]})
|
||||
return payments
|
||||
|
||||
def _post_payments(self, to_process, edit_mode=False):
|
||||
""" Post the newly created payments.
|
||||
|
||||
:param to_process: A list of python dictionary, one for each payment to create, containing:
|
||||
* create_vals: The values used for the 'create' method.
|
||||
* to_reconcile: The journal items to perform the reconciliation.
|
||||
* batch: A python dict containing everything you want about the source journal items
|
||||
to which a payment will be created (see '_get_batches').
|
||||
:param edit_mode: Is the wizard in edition mode.
|
||||
"""
|
||||
payments = self.env['account.payment']
|
||||
for vals in to_process:
|
||||
payments |= vals['payment']
|
||||
payments.action_post()
|
||||
|
||||
def _reconcile_payments(self, to_process, edit_mode=False):
|
||||
""" Reconcile the payments.
|
||||
|
||||
:param to_process: A list of python dictionary, one for each payment to create, containing:
|
||||
* create_vals: The values used for the 'create' method.
|
||||
* to_reconcile: The journal items to perform the reconciliation.
|
||||
* batch: A python dict containing everything you want about the source journal items
|
||||
to which a payment will be created (see '_get_batches').
|
||||
:param edit_mode: Is the wizard in edition mode.
|
||||
"""
|
||||
domain = [('account_internal_type', 'in', ('receivable', 'payable')), ('reconciled', '=', False)]
|
||||
for payment, lines in zip(payments, to_reconcile):
|
||||
for vals in to_process:
|
||||
payment_lines = vals['payment'].line_ids.filtered_domain(domain)
|
||||
lines = vals['to_reconcile']
|
||||
|
||||
# When using the payment tokens, the payment could not be posted at this point (e.g. the transaction failed)
|
||||
# and then, we can't perform the reconciliation.
|
||||
if payment.state != 'posted':
|
||||
continue
|
||||
|
||||
payment_lines = payment.line_ids.filtered_domain(domain)
|
||||
for account in payment_lines.account_id:
|
||||
(payment_lines + lines)\
|
||||
.filtered_domain([('account_id', '=', account.id), ('reconciled', '=', False)])\
|
||||
.reconcile()
|
||||
|
||||
def _create_payments(self):
|
||||
self.ensure_one()
|
||||
batches = self._get_batches()
|
||||
edit_mode = self.can_edit_wizard and (len(batches[0]['lines']) == 1 or self.group_payment)
|
||||
to_process = []
|
||||
|
||||
if edit_mode:
|
||||
payment_vals = self._create_payment_vals_from_wizard()
|
||||
to_process.append({
|
||||
'create_vals': payment_vals,
|
||||
'to_reconcile': batches[0]['lines'],
|
||||
'batch': batches[0],
|
||||
})
|
||||
else:
|
||||
# Don't group payments: Create one batch per move.
|
||||
if not self.group_payment:
|
||||
new_batches = []
|
||||
for batch_result in batches:
|
||||
for line in batch_result['lines']:
|
||||
new_batches.append({
|
||||
**batch_result,
|
||||
'lines': line,
|
||||
})
|
||||
batches = new_batches
|
||||
|
||||
for batch_result in batches:
|
||||
to_process.append({
|
||||
'create_vals': self._create_payment_vals_from_batch(batch_result),
|
||||
'to_reconcile': batch_result['lines'],
|
||||
'batch': batch_result,
|
||||
})
|
||||
|
||||
payments = self._init_payments(to_process, edit_mode=edit_mode)
|
||||
self._post_payments(to_process, edit_mode=edit_mode)
|
||||
self._reconcile_payments(to_process, edit_mode=edit_mode)
|
||||
return payments
|
||||
|
||||
def action_create_payments(self):
|
||||
|
||||
@@ -344,5 +344,9 @@ class TestExpenses(TestExpenseCommon):
|
||||
sheet.action_sheet_move_create()
|
||||
action_data = sheet.action_register_payment()
|
||||
wizard = Form(self.env['account.payment.register'].with_context(action_data['context'])).save()
|
||||
wizard.action_create_payments()
|
||||
action = wizard.action_create_payments()
|
||||
self.assertEqual(sheet.state, 'done', 'all account.move.line linked to expenses must be reconciled after payment')
|
||||
|
||||
move = self.env['account.payment'].browse(action['res_id']).move_id
|
||||
move.button_cancel()
|
||||
self.assertEqual(sheet.state, 'cancel', 'Sheet state must be cancel when the payment linked to that sheet is canceled')
|
||||
|
||||
@@ -19,13 +19,21 @@ class AccountPaymentRegister(models.TransientModel):
|
||||
res['partner_bank_id'] = expense_sheet.employee_id.bank_account_id.id or line.partner_id.bank_ids and line.partner_id.bank_ids.ids[0]
|
||||
return res
|
||||
|
||||
def _create_payments(self):
|
||||
# OVERRIDE to set the 'done' state on expense sheets.
|
||||
payments = super()._create_payments()
|
||||
|
||||
expense_sheets = self.env['hr.expense.sheet'].search([('account_move_id', 'in', self.line_ids.move_id.ids)])
|
||||
for expense_sheet in expense_sheets:
|
||||
if expense_sheet.currency_id.is_zero(expense_sheet.amount_residual):
|
||||
expense_sheet.state = 'done'
|
||||
|
||||
def _init_payments(self, to_process, edit_mode=False):
|
||||
# OVERRIDE
|
||||
payments = super()._init_payments(to_process, edit_mode=edit_mode)
|
||||
for payment, vals in zip(payments, to_process):
|
||||
expenses = vals['batch']['lines'].expense_id
|
||||
if expenses:
|
||||
payment.line_ids.write({'expense_id': expenses[0].id})
|
||||
return payments
|
||||
|
||||
def _reconcile_payments(self, to_process, edit_mode=False):
|
||||
# OVERRIDE
|
||||
res = super()._reconcile_payments(to_process, edit_mode=edit_mode)
|
||||
for vals in to_process:
|
||||
expense_sheets = vals['batch']['lines'].expense_id.sheet_id
|
||||
for expense_sheet in expense_sheets:
|
||||
if expense_sheet.currency_id.is_zero(expense_sheet.amount_residual):
|
||||
expense_sheet.state = 'done'
|
||||
return res
|
||||
|
||||
Reference in New Issue
Block a user