[FIX] account: invoice totals using Cash Discount and price-included taxes
## Summary Currently, when using both an early payment discount (upon invoice) and price-included taxes, the invoice totals are not calculated correctly. ## Steps to Reproduce 1. Set `Cash Discount Tax Reduction` to `Always (upon invoice)`. 2. Create an invoice with the payment term set to `2/7 Net 30`. 3. Add an invoice line with a price of 100 and a 21% price-included tax. 4. Save the invoice: The `Untaxed Amount` should be 100, but it incorrectly shows 100.35 instead. ## Cause When calculating the totals, the price-included tax is applied to one of the early payment discount lines. Since the tax is price-included, the system recalculates a new base price for the EPD line, which ultimately leads to an incorrect `Untaxed Amount`. opw-3239904 Enterprise PR: odoo/enterprise#39007 closes odoo/odoo#119892 X-original-commit: 43b78a96a8961b83cf1c18f58bd549dabc90c50f Related: odoo/enterprise#40404 Signed-off-by: Nshimiyimana Serge Séna (sesn) <sesn@odoo.com> Signed-off-by: Brice Bartoletti (bib) <bib@odoo.com> Co-authored-by: Laurent Smet <las@odoo.com>
This commit is contained in:
co-authored by
Laurent Smet
parent
1502c6cf52
commit
cfeffc3c74
@@ -784,7 +784,7 @@ class AccountTax(models.Model):
|
||||
partner=None, currency=None, product=None, taxes=None, price_unit=None, quantity=None,
|
||||
discount=None, account=None, analytic_distribution=None, price_subtotal=None,
|
||||
is_refund=False, rate=None,
|
||||
handle_price_include=None,
|
||||
handle_price_include=True,
|
||||
extra_context=None,
|
||||
):
|
||||
return {
|
||||
@@ -881,12 +881,6 @@ class AccountTax(models.Model):
|
||||
price_unit_after_discount = remaining_part_to_consider * price_unit_after_discount
|
||||
|
||||
if taxes:
|
||||
|
||||
if handle_price_include is None:
|
||||
manage_price_include = bool(base_line['handle_price_include'])
|
||||
else:
|
||||
manage_price_include = handle_price_include
|
||||
|
||||
taxes_res = taxes.with_context(**base_line['extra_context']).compute_all(
|
||||
price_unit_after_discount,
|
||||
currency=currency,
|
||||
@@ -894,7 +888,7 @@ class AccountTax(models.Model):
|
||||
product=base_line['product'],
|
||||
partner=base_line['partner'],
|
||||
is_refund=base_line['is_refund'],
|
||||
handle_price_include=manage_price_include,
|
||||
handle_price_include=base_line['handle_price_include'],
|
||||
include_caba_tags=include_caba_tags,
|
||||
)
|
||||
|
||||
@@ -912,7 +906,7 @@ class AccountTax(models.Model):
|
||||
product=base_line['product'],
|
||||
partner=base_line['partner'],
|
||||
is_refund=base_line['is_refund'],
|
||||
handle_price_include=manage_price_include,
|
||||
handle_price_include=base_line['handle_price_include'],
|
||||
include_caba_tags=include_caba_tags,
|
||||
)
|
||||
for tax_res, new_taxes_res in zip(taxes_res['taxes'], new_taxes_res['taxes']):
|
||||
@@ -1082,7 +1076,6 @@ class AccountTax(models.Model):
|
||||
for base_line in base_lines:
|
||||
to_update_vals, tax_values_list = self._compute_taxes_for_single_line(
|
||||
base_line,
|
||||
handle_price_include=handle_price_include,
|
||||
include_caba_tags=include_caba_tags,
|
||||
)
|
||||
to_process.append((base_line, to_update_vals, tax_values_list))
|
||||
|
||||
@@ -24,6 +24,24 @@ class TestAccountEarlyPaymentDiscount(AccountTestInvoicingCommon):
|
||||
})]
|
||||
})
|
||||
|
||||
def assert_tax_totals(self, document, expected_values):
|
||||
main_keys_to_ignore = {
|
||||
'formatted_amount_total', 'formatted_amount_untaxed', 'display_tax_base', 'subtotals_order'}
|
||||
group_keys_to_ignore = {'group_key', 'tax_group_id', 'tax_group_name',
|
||||
'formatted_tax_group_amount', 'formatted_tax_group_base_amount'}
|
||||
subtotals_keys_to_ignore = {'formatted_amount'}
|
||||
to_compare = document.copy()
|
||||
for key in main_keys_to_ignore:
|
||||
del to_compare[key]
|
||||
for key in group_keys_to_ignore:
|
||||
for groups in to_compare['groups_by_subtotal'].values():
|
||||
for group in groups:
|
||||
del group[key]
|
||||
for key in subtotals_keys_to_ignore:
|
||||
for subtotal in to_compare['subtotals']:
|
||||
del subtotal[key]
|
||||
self.assertEqual(to_compare, expected_values)
|
||||
|
||||
# ========================== Tests Payment Terms ==========================
|
||||
def test_early_payment_end_date(self):
|
||||
inv_1200_10_percents_discount_no_tax = self.env['account.move'].create({
|
||||
@@ -326,22 +344,6 @@ class TestAccountEarlyPaymentDiscount(AccountTestInvoicingCommon):
|
||||
])
|
||||
|
||||
def test_mixed_epd_with_draft_invoice(self):
|
||||
def assert_tax_totals(document, expected_values):
|
||||
main_keys_to_ignore = {'formatted_amount_total', 'formatted_amount_untaxed', 'display_tax_base', 'subtotals_order'}
|
||||
group_keys_to_ignore = {'group_key', 'tax_group_id', 'tax_group_name', 'formatted_tax_group_amount', 'formatted_tax_group_base_amount'}
|
||||
subtotals_keys_to_ignore = {'formatted_amount'}
|
||||
to_compare = document
|
||||
for key in main_keys_to_ignore:
|
||||
del to_compare[key]
|
||||
for key in group_keys_to_ignore:
|
||||
for groups in to_compare['groups_by_subtotal'].values():
|
||||
for group in groups:
|
||||
del group[key]
|
||||
for key in subtotals_keys_to_ignore:
|
||||
for subtotal in to_compare['subtotals']:
|
||||
del subtotal[key]
|
||||
self.assertEqual(to_compare, expected_values)
|
||||
|
||||
self.early_pay_10_percents_10_days.early_pay_discount_computation = 'mixed'
|
||||
tax = self.env['account.tax'].create({
|
||||
'name': 'WonderTax',
|
||||
@@ -357,7 +359,7 @@ class TestAccountEarlyPaymentDiscount(AccountTestInvoicingCommon):
|
||||
line_form.quantity = 1
|
||||
line_form.tax_ids.clear()
|
||||
line_form.tax_ids.add(tax)
|
||||
assert_tax_totals(invoice._values['tax_totals'], {
|
||||
self.assert_tax_totals(invoice._values['tax_totals'], {
|
||||
'amount_untaxed': 1000,
|
||||
'amount_total': 1090,
|
||||
'groups_by_subtotal': {
|
||||
@@ -511,3 +513,52 @@ class TestAccountEarlyPaymentDiscount(AccountTestInvoicingCommon):
|
||||
{'balance': -200.0},
|
||||
{'balance': 200.0},
|
||||
])
|
||||
|
||||
def test_mixed_epd_with_tax_included(self):
|
||||
early_pay_2_percents_10_days = self.env['account.payment.term'].create({
|
||||
'name': '2% discount if paid within 10 days',
|
||||
'company_id': self.company_data['company'].id,
|
||||
'early_discount': True,
|
||||
'discount_percentage': 2,
|
||||
'discount_days': 10,
|
||||
'early_pay_discount_computation': 'mixed',
|
||||
'line_ids': [Command.create({
|
||||
'value': 'percent',
|
||||
'nb_days': 0,
|
||||
'value_amount': 100,
|
||||
})]
|
||||
})
|
||||
tax = self.env['account.tax'].create({
|
||||
'name': 'Tax 21% included',
|
||||
'amount': 21,
|
||||
'price_include': True,
|
||||
})
|
||||
|
||||
with Form(self.env['account.move'].with_context(default_move_type='out_invoice')) as invoice:
|
||||
invoice.partner_id = self.partner_a
|
||||
invoice.invoice_date = fields.Date.from_string('2022-02-21')
|
||||
invoice.invoice_payment_term_id = early_pay_2_percents_10_days
|
||||
with invoice.invoice_line_ids.new() as line_form:
|
||||
line_form.product_id = self.product_a
|
||||
line_form.price_unit = 121
|
||||
line_form.quantity = 1
|
||||
line_form.tax_ids.clear()
|
||||
line_form.tax_ids.add(tax)
|
||||
self.assert_tax_totals(invoice._values['tax_totals'], {
|
||||
'amount_untaxed': 100,
|
||||
'amount_total': 120.58,
|
||||
'groups_by_subtotal': {
|
||||
'Untaxed Amount': [
|
||||
{
|
||||
'tax_group_amount': 20.58,
|
||||
'tax_group_base_amount': 98,
|
||||
},
|
||||
],
|
||||
},
|
||||
'subtotals': [
|
||||
{
|
||||
'name': "Untaxed Amount",
|
||||
'amount': 100,
|
||||
}
|
||||
],
|
||||
})
|
||||
|
||||
@@ -287,12 +287,15 @@ class SaleAdvancePaymentInv(models.TransientModel):
|
||||
order_lines = order.order_line.filtered(lambda l: not l.display_type)
|
||||
base_downpayment_lines_values = self._prepare_base_downpayment_line_values(order)
|
||||
|
||||
tax_base_line_dicts = [line._convert_to_tax_base_line_dict(
|
||||
analytic_distribution=line.analytic_distribution) for line in order_lines]
|
||||
tax_base_line_dicts = [
|
||||
line._convert_to_tax_base_line_dict(
|
||||
analytic_distribution=line.analytic_distribution,
|
||||
handle_price_include=False
|
||||
)
|
||||
for line in order_lines
|
||||
]
|
||||
computed_taxes = self.env['account.tax']._compute_taxes(
|
||||
tax_base_line_dicts,
|
||||
handle_price_include=False
|
||||
)
|
||||
tax_base_line_dicts)
|
||||
down_payment_values = []
|
||||
for line, tax_repartition in computed_taxes['base_lines_to_update']:
|
||||
taxes = line['taxes'].flatten_taxes_hierarchy()
|
||||
|
||||
Reference in New Issue
Block a user