diff options
| author | Laurent Smet <las@odoo.com> | 2021-05-14 09:04:22 +0000 |
|---|---|---|
| committer | Laurent Smet <las@odoo.com> | 2021-05-14 14:23:07 +0000 |
| commit | 87f6a2fd3f86d13ebe320c058336d752362b8f44 (patch) | |
| tree | 3d3a8b15a9e263879456ab744f4981b812c18b15 | |
| parent | dc296afac29a5a716b162ecef7d4a296399c2da3 (diff) | |
[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#70807
Signed-off-by: Laurent Smet <smetl@users.noreply.github.com>
| -rw-r--r-- | addons/account/models/account_payment.py | 62 | ||||
| -rw-r--r-- | addons/account/tests/test_account_payment.py | 230 |
2 files changed, 208 insertions, 84 deletions
diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py index 46f04073bcd..15bd9198373 100644 --- a/addons/account/models/account_payment.py +++ b/addons/account/models/account_payment.py @@ -187,23 +187,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: @@ -236,33 +245,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'), }) @@ -713,16 +722,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: + + # 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: diff --git a/addons/account/tests/test_account_payment.py b/addons/account/tests/test_account_payment.py index 15bb5ae81cd..b2a0df04492 100644 --- a/addons/account/tests/test_account_payment.py +++ b/addons/account/tests/test_account_payment.py @@ -324,93 +324,203 @@ class TestAccountPayment(AccountTestInvoicingCommon): }, ]) - def test_payment_move_sync_writeoff_sign(self): - copy_receivable = self.copy_account(self.company_data['default_account_receivable']) + def test_inbound_payment_sync_writeoff_debit_sign(self): + payment = self.env['account.payment'].create({ + 'amount': 100.0, + 'payment_type': 'inbound', + 'partner_type': 'customer', + }) - pay_form = Form(self.env['account.payment'].with_context(default_journal_id=self.company_data['default_journal_bank'].id)) - pay_form.amount = 50.0 - pay_form.payment_type = 'inbound' - pay_form.partner_type = 'customer' - pay_form.destination_account_id = copy_receivable - payment = pay_form.save() + # ==== Edit the account.move.line ==== - expected_payment_values = { - 'amount': 50.0, + liquidity_lines, counterpart_lines, writeoff_lines = 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', - 'payment_reference': False, - 'is_reconciled': False, - 'currency_id': self.company_data['currency'].id, - 'partner_id': False, - 'destination_account_id': copy_receivable.id, - 'payment_method_id': self.env.ref('account.account_payment_method_manual_in').id, - } - expected_move_values = { - 'currency_id': self.company_data['currency'].id, - 'partner_id': False, - } - expected_liquidity_line = { - 'debit': 50.0, - 'credit': 0.0, - 'amount_currency': 50.0, - 'currency_id': self.company_data['currency'].id, - 'account_id': self.payment_debit_account_id.id, - } - expected_counterpart_line = { - 'debit': 0.0, - 'credit': 50.0, - 'amount_currency': -50.0, - 'currency_id': self.company_data['currency'].id, - 'account_id': self.partner_a.property_account_payable_id.id, - } + '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 ==== - move_form = Form(payment.move_id) - with move_form.line_ids.edit(0) as line_form: - line_form.currency_id = self.company_data['currency'] - line_form.amount_currency = 100.0 - with move_form.line_ids.edit(1) as line_form: - line_form.currency_id = self.company_data['currency'] - line_form.amount_currency = -75.0 - line_form.account_id = copy_receivable - with move_form.line_ids.new() as line_form: - line_form.currency_id = self.company_data['currency'] - line_form.amount_currency = -25.0 - line_form.account_id = self.company_data['default_account_revenue'] - move_form.save() + liquidity_lines, counterpart_lines, writeoff_lines = 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, [{ - **expected_payment_values, + 'payment_type': 'inbound', + 'partner_type': 'customer', 'amount': 100.0, }]) # ==== Edit the account.payment amount ==== - pay_form = Form(payment) - pay_form.partner_type = 'supplier' - pay_form.amount = 100.1 - pay_form.partner_id = self.partner_a - payment = pay_form.save() + 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'), [ { - **expected_counterpart_line, 'debit': 0.0, 'credit': 75.1, - 'amount_currency': -75.1, + 'account_id': self.company_data['default_account_payable'].id, }, { 'debit': 0.0, 'credit': 25.0, - 'amount_currency': -25.0, - 'currency_id': self.company_data['currency'].id, 'account_id': self.company_data['default_account_revenue'].id, }, { - **expected_liquidity_line, 'debit': 100.1, 'credit': 0.0, - 'amount_currency': 100.1, + '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, writeoff_lines = 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, writeoff_lines = 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, }, ]) |
