[FIX] account: price at 0, fixed tax price include

The aim of this commit is to correct the compute_all calculation
when it is dealing with an article with 0.00 price and having a fixed
tax included in it.

In this situation, this means the tax is directly taken as an expense
by the company that offers it to its client.

This also solves the situation in which we have a 100% discount because
discount is applied before calling `compute_all` by changing the
unit_price.

Before this commit:
Creating an invoice with a line having a price at 0 or a 100% discount
and a fixed tax price_include resulted in a really weird behavior: the
company had to reimburse the customer for the amount of the tax.
(what a lucky customer :D )

After this commit:
Customer is sad, he only gets the product for free.
The base is calculated as negative, resulting in a debit amount on the base
income line.
The tax is then counterbalance by it.

The form is displaying a negative untaxed amount, a positive tax and the
total is 0.

closes odoo/odoo#86223

Ticket: 2660808, 2637512
Community-pr: https://github.com/odoo/odoo/pull/85747
X-original-commit: 727d19cb4e012e9a24d94640987ac5ea6f452a2e
Signed-off-by: Laurent Smet <las@odoo.com>
Signed-off-by: Brice Bartoletti <bib@odoo.com>
This commit is contained in:
Brice bib Bartoletti
2022-03-10 20:00:40 +00:00
parent 412c007e58
commit f4c669d0e4
4 changed files with 112 additions and 4 deletions
+1 -2
View File
@@ -4023,8 +4023,7 @@ class AccountMoveLine(models.Model):
# Compute 'price_total'.
if taxes:
force_sign = -1 if move_type in ('out_invoice', 'in_refund', 'out_receipt') else 1
taxes_res = taxes._origin.with_context(force_sign=force_sign).compute_all(line_discount_price_unit,
taxes_res = taxes._origin.with_context(force_sign=1).compute_all(line_discount_price_unit,
quantity=quantity, currency=currency, product=product, partner=partner, is_refund=move_type in ('out_refund', 'in_refund'))
res['price_subtotal'] = taxes_res['total_excluded']
res['price_total'] = taxes_res['total_included']
+2 -2
View File
@@ -582,7 +582,7 @@ class AccountTax(models.Model):
sum_repartition_factor = sum(tax_repartition_lines.mapped('factor'))
#compute the tax_amount
if not skip_checkpoint and price_include and total_included_checkpoints.get(i) and sum_repartition_factor != 0:
if not skip_checkpoint and price_include and total_included_checkpoints.get(i) is not None and sum_repartition_factor != 0:
# We know the total to reach for that tax, so we make a substraction to avoid any rounding issues
tax_amount = total_included_checkpoints[i] - (base + cumulated_tax_included_amount)
cumulated_tax_included_amount = 0
@@ -594,7 +594,7 @@ class AccountTax(models.Model):
tax_amount = round(tax_amount, precision_rounding=prec)
factorized_tax_amount = round(tax_amount * sum_repartition_factor, precision_rounding=prec)
if price_include and not total_included_checkpoints.get(i):
if price_include and total_included_checkpoints.get(i) is None:
cumulated_tax_included_amount += factorized_tax_amount
# If the tax affects the base of subsequent taxes, its tax move lines must
@@ -1792,6 +1792,75 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon):
'amount_total': 260.01,
})
def test_out_invoice_line_tax_fixed_price_include_free_product(self):
''' Check that fixed tax include are correctly computed while the price_unit is 0
'''
fixed_tax_price_include = self.env['account.tax'].create({
'name': 'BEBAT 0.05',
'type_tax_use': 'sale',
'amount_type': 'fixed',
'amount': 0.05,
'price_include': True,
'include_base_amount': True,
})
invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'invoice_date': '2022-03-03',
'date': '2022-03-03',
'partner_id': self.partner_a.id,
'invoice_line_ids': [(0, 0, {
'name': 'Free product',
'price_unit': 0.0,
'account_id': self.company_data['default_account_revenue'].id,
'tax_ids': [(6, 0, fixed_tax_price_include.ids)],
})],
})
self.assertRecordValues(invoice, [{
'amount_untaxed': -0.05,
'amount_tax': 0.05,
'amount_total': 0.0,
}])
def test_out_invoice_line_taxes_fixed_price_include_free_product(self):
''' Check that fixed tax include are correctly computed while the price_unit is 0
'''
# please ensure this test remains consistent with
# test_free_product_and_price_include_fixed_tax in the sale module
fixed_tax_price_include_1 = self.env['account.tax'].create({
'name': 'BEBAT 0.05',
'type_tax_use': 'sale',
'amount_type': 'fixed',
'amount': 0.05,
'price_include': True,
'include_base_amount': True,
})
fixed_tax_price_include_2 = self.env['account.tax'].create({
'name': 'Recupel 0.25',
'type_tax_use': 'sale',
'amount_type': 'fixed',
'amount': 0.25,
'price_include': True,
'include_base_amount': True,
})
invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'invoice_date': '2022-03-03',
'date': '2022-03-03',
'partner_id': self.partner_a.id,
'invoice_line_ids': [(0, 0, {
'name': 'Free product',
'price_unit': 0.0,
'account_id': self.company_data['default_account_revenue'].id,
'tax_ids': [(6, 0, (fixed_tax_price_include_1 + fixed_tax_price_include_2).ids)],
})],
})
self.assertRecordValues(invoice, [{
'amount_untaxed': -0.30,
'amount_tax': 0.30,
'amount_total': 0.0,
}])
def test_out_invoice_create_refund(self):
self.invoice.action_post()
+40
View File
@@ -767,6 +767,46 @@ class TestSaleOrder(TestSaleCommon):
self.assertEqual(line.price_subtotal, 17527.41)
self.assertEqual(line.untaxed_amount_to_invoice, line.price_subtotal)
def test_free_product_and_price_include_fixed_tax(self):
""" Check that fixed tax include are correctly computed while the price_unit is 0
"""
# please ensure this test remains consistent with
# test_out_invoice_line_onchange_2_taxes_fixed_price_include_free_product in account module
sale_order = self.env['sale.order'].create({
'partner_id': self.partner_a.id,
'order_line': [(0, 0, {
'product_id': self.company_data['product_order_no'].id,
'product_uom_qty': 1,
'price_unit': 0.0,
})]
})
sale_order.action_confirm()
line = sale_order.order_line
line.tax_id = [
(0, 0, {
'name': 'BEBAT 0.05',
'type_tax_use': 'sale',
'amount_type': 'fixed',
'amount': 0.05,
'price_include': True,
'include_base_amount': True,
}),
(0, 0, {
'name': 'Recupel 0.25',
'type_tax_use': 'sale',
'amount_type': 'fixed',
'amount': 0.25,
'price_include': True,
'include_base_amount': True,
}),
]
sale_order.action_confirm()
self.assertRecordValues(sale_order, [{
'amount_untaxed': -0.30,
'amount_tax': 0.30,
'amount_total': 0.0,
}])
def test_so_tax_mapping(self):
uom = self.env['uom.uom'].search([('name', '=', 'Units')], limit=1)
pricelist = self.env['product.pricelist'].search([('name', '=', 'Public Pricelist')], limit=1)