diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 96a0578844b..0841eed48fe 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -2651,8 +2651,27 @@ class AccountMove(models.Model): self.journal_id.company_id.account_purchase_tax_id ) taxes = self.fiscal_position_id.map_tax(taxes) - price_untaxed = taxes.with_context(force_price_include=True).compute_all( - self.quick_edit_total_amount - self.tax_totals['amount_total'])['total_excluded'] + + # When a payment term has an early payment discount with the epd computation set to 'mixed', recomputing + # the untaxed amount should take in consideration the discount percentage otherwise we'd get a wrong value. + # We check that we have only one percentage tax as computing from multiple taxes with different types can get complicated. + # In one example: let's say: base = 100, discount = 2%, tax = 21% + # the total will be calculated as: total = base + (base * (1 - discount)) * tax + # If we manipulate the equation to get the base from the total, we'll have base = total / ((1 - discount) * tax + 1) + term = self.invoice_payment_term_id + discount_percentage = term.discount_percentage if term.early_discount else 0 + remaining_amount = self.quick_edit_total_amount - self.tax_totals['amount_total'] + + if ( + discount_percentage + and term.early_pay_discount_computation == 'mixed' + and len(taxes) == 1 + and taxes.amount_type == 'percent' + ): + price_untaxed = self.currency_id.round( + remaining_amount / (((1.0 - discount_percentage / 100.0) * (taxes.amount / 100.0)) + 1.0)) + else: + price_untaxed = taxes.with_context(force_price_include=True).compute_all(remaining_amount)['total_excluded'] return {'account_id': account_id, 'tax_ids': taxes.ids, 'price_unit': price_untaxed} @api.onchange('quick_edit_mode', 'journal_id', 'company_id') diff --git a/addons/account/tests/test_account_move_out_invoice.py b/addons/account/tests/test_account_move_out_invoice.py index 88afc0e3de2..dec0096eb36 100644 --- a/addons/account/tests/test_account_move_out_invoice.py +++ b/addons/account/tests/test_account_move_out_invoice.py @@ -3344,6 +3344,55 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): self.assertEqual(invoice.amount_tax, 17.36) self.assertEqual(len(invoice.invoice_line_ids), 2) + def test_quick_edit_total_amount_with_mixed_epd(self): + move_form = Form(self.env['account.move'].with_context(default_move_type='out_invoice')) + move_form.invoice_date = fields.Date.from_string('2022-01-01') + + # Quick edit total amount activated + self.env.company.quick_edit_mode = "out_and_in_invoices" + # 21% sale tax + self.env.company.account_sale_tax_id = self.env['account.tax'].create({ + 'name': '21%', + 'amount': 21, + 'type_tax_use': 'sale', + }) + # Create a payment term with early payment discount of 2% and computation set to mixed (Always (upon invoice)) + epd_payment_term = self.env['account.payment.term'].create({ + 'name': "2/7 Term", + 'discount_days': 7, + 'discount_percentage': 2, + 'early_discount': True, + 'early_pay_discount_computation': 'mixed', + }) + # Set the payment term to the one we just created + move_form.invoice_payment_term_id = epd_payment_term + + invoice = move_form.save() + + # Invoice of one item of price 100, discount 2% and tax 21%: + # 21% tax = 100 * (1 - 0.2) * 0.21 = 20.58 + # total_amount = 100 + 20.58 = 120.58 + + # Make sure the quick edit added one line with the correct values + with Form(invoice) as move_form: + move_form.quick_edit_total_amount = 120.58 + self.assertRecordValues(invoice, [{'amount_total': 120.58, 'amount_untaxed': 100, 'amount_tax': 20.58}]) + self.assertEqual(len(invoice.invoice_line_ids), 1) + + # Modify one invoice line + with Form(invoice) as move_form: + with move_form.invoice_line_ids.edit(0) as line_form: + line_form.price_unit = 70 + self.assertRecordValues(invoice, [{'amount_total': 84.41, 'amount_untaxed': 70, 'amount_tax': 14.41}]) + self.assertEqual(len(invoice.invoice_line_ids), 1) + + # Suggest the new amount such that the total is equal to the quick amount + with Form(invoice) as move_form: + with move_form.invoice_line_ids.new() as line_form: + self.assertEqual(line_form.price_unit, 30) + self.assertRecordValues(invoice, [{'amount_total': 120.58, 'amount_untaxed': 100, 'amount_tax': 20.58}]) + self.assertEqual(len(invoice.invoice_line_ids), 2) + def test_out_invoice_depreciated_account(self): move = self.env['account.move'].create({ 'move_type': 'out_invoice',