summaryrefslogtreecommitdiff
path: root/addons/account/wizard
diff options
context:
space:
mode:
authorLaurent Smet <las@odoo.com>2020-11-23 07:49:17 +0000
committerLaurent Smet <las@odoo.com>2020-11-23 12:33:40 +0000
commit967a2ab323e273c853aee0edd5ce404f4d14e6ab (patch)
treeb863005d825f7f1778c9509863320eb2f8278df3 /addons/account/wizard
parentd22daa8baa78fdcc9202657ab94e849f5690187f (diff)
[FIX] account: mark invoice as paid when paying all in foreign currency
Activate multicurrency: * EUR with rate 1.0 * USD with rate 100.0 Have a company in currency USD Create an invoice (in USD) * invoice line of 12.15, no tax Save, post and register payment In the payment widget change currency to EUR. The amount will be automatically converted in 0.12 Register payment Invoice will be still marked to paid as the amount paid is 12.0. This occur because the system does not check if the amount to be paid, expressed in payment currency, is what has been paid. opw-2376336 closes odoo/odoo#61938 Signed-off-by: Laurent Smet <smetl@users.noreply.github.com>
Diffstat (limited to 'addons/account/wizard')
-rw-r--r--addons/account/wizard/account_payment_register.py43
1 files changed, 41 insertions, 2 deletions
diff --git a/addons/account/wizard/account_payment_register.py b/addons/account/wizard/account_payment_register.py
index f5fc12b11d2..3fc91f716ad 100644
--- a/addons/account/wizard/account_payment_register.py
+++ b/addons/account/wizard/account_payment_register.py
@@ -417,7 +417,7 @@ class AccountPaymentRegister(models.TransientModel):
'destination_account_id': self.line_ids[0].account_id.id
}
- if self.payment_difference and self.payment_difference_handling == 'reconcile':
+ if not self.currency_id.is_zero(self.payment_difference) and self.payment_difference_handling == 'reconcile':
payment_vals['write_off_line_vals'] = {
'name': self.writeoff_label,
'amount': self.payment_difference,
@@ -444,9 +444,10 @@ class AccountPaymentRegister(models.TransientModel):
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_reconcile = []
- if self.can_edit_wizard and (len(batches[0]['lines']) == 1 or self.group_payment):
+ if edit_mode:
payment_vals = self._create_payment_vals_from_wizard()
payment_vals_list = [payment_vals]
to_reconcile.append(batches[0]['lines'])
@@ -468,6 +469,44 @@ class AccountPaymentRegister(models.TransientModel):
to_reconcile.append(batch_result['lines'])
payments = self.env['account.payment'].create(payment_vals_list)
+
+ # 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()
+ source_balance = abs(sum(lines.mapped('amount_residual')))
+ payment_rate = liquidity_lines[0].amount_currency / liquidity_lines[0].balance
+ source_balance_converted = abs(source_balance) * payment_rate
+
+ # Translate the balance into the payment currency is order to be able to compare them.
+ # In case in both have the same value (12.15 * 0.01 ~= 0.12 in our example), it means the user
+ # attempt to fully paid the source lines and then, we need to manually fix them to get a perfect
+ # match.
+ payment_balance = abs(sum(counterpart_lines.mapped('balance')))
+ payment_amount_currency = abs(sum(counterpart_lines.mapped('amount_currency')))
+ if not payment.currency_id.is_zero(source_balance_converted - payment_amount_currency):
+ continue
+
+ delta_balance = source_balance - payment_balance
+
+ # Balance are already the same.
+ if self.company_currency_id.is_zero(delta_balance):
+ continue
+
+ # Fix the balance but make sure to peek the liquidity and counterpart lines first.
+ debit_lines = (liquidity_lines + counterpart_lines).filtered('debit')
+ credit_lines = (liquidity_lines + counterpart_lines).filtered('credit')
+
+ payment.move_id.write({'line_ids': [
+ (1, debit_lines[0].id, {'debit': debit_lines[0].debit + delta_balance}),
+ (1, credit_lines[0].id, {'credit': credit_lines[0].credit + delta_balance}),
+ ]})
+
payments.action_post()
domain = [('account_internal_type', 'in', ('receivable', 'payable')), ('reconciled', '=', False)]