diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 3234ccba89e..a612544e302 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -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'] diff --git a/addons/account/models/account_tax.py b/addons/account/models/account_tax.py index 62788dc1142..b89db2fb099 100644 --- a/addons/account/models/account_tax.py +++ b/addons/account/models/account_tax.py @@ -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 diff --git a/addons/account/tests/test_account_move_out_invoice.py b/addons/account/tests/test_account_move_out_invoice.py index 936800ac736..f8d797a91bd 100644 --- a/addons/account/tests/test_account_move_out_invoice.py +++ b/addons/account/tests/test_account_move_out_invoice.py @@ -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() diff --git a/addons/sale/tests/test_sale_order.py b/addons/sale/tests/test_sale_order.py index 9cc0b31fc67..08bd2106be5 100644 --- a/addons/sale/tests/test_sale_order.py +++ b/addons/sale/tests/test_sale_order.py @@ -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)