[FIX] account: Fix corner cases of https://github.com/odoo/odoo/pull/70303

Original issue:
Create a payment like follow:

Payment type: receive money
Partner type: customer
Destination account: whatever receivable
Amount: define an amount (i.e. 90.00)
On Save, open the draft jounral entry created.
Lower the liquidity or counterpart amount and add a writeoff
Go back to the payment, change the amount and save.
The journal entry writeoff will swap from debit to credit at every
change made to the amount. This occur also when changing currency on the
payment

opw-2475223

Otherwise, this fix is not covering all cases:
inbound with write-off in debit
inbound with write-off in credit
outbound with write-off in debit
outbound with write-off in credit

closes odoo/odoo#70903

X-original-commit: 01b9bff35e85fafb1a50bb0819123d186acf4925
Signed-off-by: Laurent Smet <smetl@users.noreply.github.com>
This commit is contained in:
Laurent Smet
2021-05-21 13:33:02 +00:00
parent 6b9acb23bc
commit f985eaa71d
2 changed files with 240 additions and 26 deletions
+40 -26
View File
@@ -193,23 +193,32 @@ class AccountPayment(models.Model):
self.journal_id.display_name))
# Compute amounts.
write_off_amount = write_off_line_vals.get('amount', 0.0)
write_off_amount_currency = write_off_line_vals.get('amount', 0.0)
if self.payment_type == 'inbound':
# Receive money.
counterpart_amount = -self.amount
write_off_amount *= -1
liquidity_amount_currency = self.amount
elif self.payment_type == 'outbound':
# Send money.
counterpart_amount = self.amount
liquidity_amount_currency = -self.amount
write_off_amount_currency *= -1
else:
counterpart_amount = 0.0
write_off_amount = 0.0
liquidity_amount_currency = write_off_amount_currency = 0.0
balance = self.currency_id._convert(counterpart_amount, self.company_id.currency_id, self.company_id, self.date)
counterpart_amount_currency = counterpart_amount
write_off_balance = self.currency_id._convert(write_off_amount, self.company_id.currency_id, self.company_id, self.date)
write_off_amount_currency = write_off_amount
write_off_balance = self.currency_id._convert(
write_off_amount_currency,
self.company_id.currency_id,
self.company_id,
self.date,
)
liquidity_balance = self.currency_id._convert(
liquidity_amount_currency,
self.company_id.currency_id,
self.company_id,
self.date,
)
counterpart_amount_currency = -liquidity_amount_currency - write_off_amount_currency
counterpart_balance = -liquidity_balance - write_off_balance
currency_id = self.currency_id.id
if self.is_internal_transfer:
@@ -242,33 +251,33 @@ class AccountPayment(models.Model):
{
'name': liquidity_line_name or default_line_name,
'date_maturity': self.date,
'amount_currency': -counterpart_amount_currency,
'amount_currency': liquidity_amount_currency,
'currency_id': currency_id,
'debit': balance < 0.0 and -balance or 0.0,
'credit': balance > 0.0 and balance or 0.0,
'debit': liquidity_balance if liquidity_balance > 0.0 else 0.0,
'credit': -liquidity_balance if liquidity_balance < 0.0 else 0.0,
'partner_id': self.partner_id.id,
'account_id': self.journal_id.payment_debit_account_id.id if balance < 0.0 else self.journal_id.payment_credit_account_id.id,
'account_id': self.journal_id.payment_credit_account_id.id if liquidity_balance < 0.0 else self.journal_id.payment_debit_account_id.id,
},
# Receivable / Payable.
{
'name': self.payment_reference or default_line_name,
'date_maturity': self.date,
'amount_currency': counterpart_amount_currency + write_off_amount_currency if currency_id else 0.0,
'amount_currency': counterpart_amount_currency,
'currency_id': currency_id,
'debit': balance + write_off_balance > 0.0 and balance + write_off_balance or 0.0,
'credit': balance + write_off_balance < 0.0 and -balance - write_off_balance or 0.0,
'debit': counterpart_balance if counterpart_balance > 0.0 else 0.0,
'credit': -counterpart_balance if counterpart_balance < 0.0 else 0.0,
'partner_id': self.partner_id.id,
'account_id': self.destination_account_id.id,
},
]
if write_off_balance:
if not self.currency_id.is_zero(write_off_amount_currency):
# Write-off line.
line_vals_list.append({
'name': write_off_line_vals.get('name') or default_line_name,
'amount_currency': -write_off_amount_currency,
'amount_currency': write_off_amount_currency,
'currency_id': currency_id,
'debit': write_off_balance < 0.0 and -write_off_balance or 0.0,
'credit': write_off_balance > 0.0 and write_off_balance or 0.0,
'debit': write_off_balance if write_off_balance > 0.0 else 0.0,
'credit': -write_off_balance if write_off_balance < 0.0 else 0.0,
'partner_id': self.partner_id.id,
'account_id': write_off_line_vals.get('account_id'),
})
@@ -757,16 +766,21 @@ class AccountPayment(models.Model):
# This allows to create a new payment with custom 'line_ids'.
if writeoff_lines:
counterpart_amount = sum(counterpart_lines.mapped('amount_currency'))
writeoff_amount = sum(writeoff_lines.mapped('amount_currency'))
counterpart_amount = counterpart_lines['amount_currency']
if writeoff_amount > 0.0 and counterpart_amount > 0.0:
sign = 1
else:
# To be consistent with the payment_difference made in account.payment.register,
# 'writeoff_amount' needs to be signed regarding the 'amount' field before the write.
# Since the write is already done at this point, we need to base the computation on accounting values.
if (counterpart_amount > 0.0) == (writeoff_amount > 0.0):
sign = -1
else:
sign = 1
writeoff_amount = abs(writeoff_amount) * sign
write_off_line_vals = {
'name': writeoff_lines[0].name,
'amount': writeoff_amount * sign,
'amount': writeoff_amount,
'account_id': writeoff_lines[0].account_id.id,
}
else:
@@ -323,6 +323,206 @@ class TestAccountPayment(AccountTestInvoicingCommon):
},
])
def test_inbound_payment_sync_writeoff_debit_sign(self):
payment = self.env['account.payment'].create({
'amount': 100.0,
'payment_type': 'inbound',
'partner_type': 'customer',
})
# ==== Edit the account.move.line ====
liquidity_lines, counterpart_lines, dummy = payment._seek_for_lines()
payment.move_id.write({
'line_ids': [
(1, liquidity_lines.id, {'debit': 100.0}),
(1, counterpart_lines.id, {'credit': 125.0}),
(0, 0, {'debit': 25.0, 'account_id': self.company_data['default_account_revenue'].id}),
],
})
self.assertRecordValues(payment, [{
'payment_type': 'inbound',
'partner_type': 'customer',
'amount': 100.0,
}])
# ==== Edit the account.payment amount ====
payment.write({
'partner_type': 'supplier',
'amount': 100.1,
'destination_account_id': self.company_data['default_account_payable'].id,
})
self.assertRecordValues(payment.line_ids.sorted('balance'), [
{
'debit': 0.0,
'credit': 125.1,
'account_id': self.company_data['default_account_payable'].id,
},
{
'debit': 25.0,
'credit': 0.0,
'account_id': self.company_data['default_account_revenue'].id,
},
{
'debit': 100.1,
'credit': 0.0,
'account_id': self.payment_debit_account_id.id,
},
])
def test_inbound_payment_sync_writeoff_credit_sign(self):
payment = self.env['account.payment'].create({
'amount': 100.0,
'payment_type': 'inbound',
'partner_type': 'customer',
})
# ==== Edit the account.move.line ====
liquidity_lines, counterpart_lines, dummy = payment._seek_for_lines()
payment.move_id.write({
'line_ids': [
(1, liquidity_lines.id, {'debit': 100.0}),
(1, counterpart_lines.id, {'credit': 75.0}),
(0, 0, {'credit': 25.0, 'account_id': self.company_data['default_account_revenue'].id}),
],
})
self.assertRecordValues(payment, [{
'payment_type': 'inbound',
'partner_type': 'customer',
'amount': 100.0,
}])
# ==== Edit the account.payment amount ====
payment.write({
'partner_type': 'supplier',
'amount': 100.1,
'destination_account_id': self.company_data['default_account_payable'].id,
})
self.assertRecordValues(payment.line_ids.sorted('balance'), [
{
'debit': 0.0,
'credit': 75.1,
'account_id': self.company_data['default_account_payable'].id,
},
{
'debit': 0.0,
'credit': 25.0,
'account_id': self.company_data['default_account_revenue'].id,
},
{
'debit': 100.1,
'credit': 0.0,
'account_id': self.payment_debit_account_id.id,
},
])
def test_outbound_payment_sync_writeoff_debit_sign(self):
payment = self.env['account.payment'].create({
'amount': 100.0,
'payment_type': 'outbound',
'partner_type': 'supplier',
})
# ==== Edit the account.move.line ====
liquidity_lines, counterpart_lines, dummy = payment._seek_for_lines()
payment.move_id.write({
'line_ids': [
(1, liquidity_lines.id, {'credit': 100.0}),
(1, counterpart_lines.id, {'debit': 75.0}),
(0, 0, {'debit': 25.0, 'account_id': self.company_data['default_account_revenue'].id}),
],
})
self.assertRecordValues(payment, [{
'payment_type': 'outbound',
'partner_type': 'supplier',
'amount': 100.0,
}])
# ==== Edit the account.payment amount ====
payment.write({
'partner_type': 'customer',
'amount': 100.1,
'destination_account_id': self.company_data['default_account_receivable'].id,
})
self.assertRecordValues(payment.line_ids.sorted('balance'), [
{
'debit': 0.0,
'credit': 100.1,
'account_id': self.payment_credit_account_id.id,
},
{
'debit': 25.0,
'credit': 0.0,
'account_id': self.company_data['default_account_revenue'].id,
},
{
'debit': 75.1,
'credit': 0.0,
'account_id': self.company_data['default_account_receivable'].id,
},
])
def test_outbound_payment_sync_writeoff_credit_sign(self):
payment = self.env['account.payment'].create({
'amount': 100.0,
'payment_type': 'outbound',
'partner_type': 'supplier',
})
# ==== Edit the account.move.line ====
liquidity_lines, counterpart_lines, dummy = payment._seek_for_lines()
payment.move_id.write({
'line_ids': [
(1, liquidity_lines.id, {'credit': 100.0}),
(1, counterpart_lines.id, {'debit': 125.0}),
(0, 0, {'credit': 25.0, 'account_id': self.company_data['default_account_revenue'].id}),
],
})
self.assertRecordValues(payment, [{
'payment_type': 'outbound',
'partner_type': 'supplier',
'amount': 100.0,
}])
# ==== Edit the account.payment amount ====
payment.write({
'partner_type': 'customer',
'amount': 100.1,
'destination_account_id': self.company_data['default_account_receivable'].id,
})
self.assertRecordValues(payment.line_ids.sorted('balance'), [
{
'debit': 0.0,
'credit': 100.1,
'account_id': self.payment_credit_account_id.id,
},
{
'debit': 0.0,
'credit': 25.0,
'account_id': self.company_data['default_account_revenue'].id,
},
{
'debit': 125.1,
'credit': 0.0,
'account_id': self.company_data['default_account_receivable'].id,
},
])
def test_internal_transfer(self):
copy_receivable = self.copy_account(self.company_data['default_account_receivable'])