[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:
@@ -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']
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user