Skip to content

Commit

Permalink
[FIX] sale: Fix fiscal pos. mapping taxes to price included
Browse files Browse the repository at this point in the history
Using a fiscal position to map a tax to a price-included tax doesn't work.

closes odoo#70565

X-original-commit: a6fe2de
Signed-off-by: Antoine Vandevenne (anv) <AntoineVDV@users.noreply.github.com>
Signed-off-by: Laurent Smet <smetl@users.noreply.github.com>
  • Loading branch information
smetl committed May 7, 2021
1 parent 84fb098 commit 8382f19
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 3 deletions.
43 changes: 40 additions & 3 deletions addons/sale/models/sale.py
Original file line number Diff line number Diff line change
Expand Up @@ -1637,6 +1637,43 @@ def _get_display_price(self, product):
# negative discounts (= surcharge) are included in the display price
return max(base_price, final_price)

def _get_default_price_unit_from_product(self, product):
self.ensure_one()

currency = self.order_id.currency_id
fiscal_position = self.order_id.fiscal_position_id or self.order_id.partner_id.property_account_position_id
product_taxes = self.product_id.taxes_id.filtered(lambda r: r.company_id == self.order_id.company_id)
product_taxes_after_fp = fiscal_position.map_tax(product_taxes, partner=self.order_id.partner_id)
price_unit = self._get_display_price(product)

if set(product_taxes.ids) != set(product_taxes_after_fp.ids):
flattened_taxes = product_taxes._origin.flatten_taxes_hierarchy()
if any(tax.price_include for tax in flattened_taxes):
taxes_res = flattened_taxes.compute_all(
price_unit,
quantity=self.product_uom_qty,
currency=currency,
product=self.product_id,
partner=self.order_id.partner_id,
)
price_unit = currency.round(taxes_res['total_excluded'])

flattened_taxes = product_taxes_after_fp._origin.flatten_taxes_hierarchy()
if any(tax.price_include for tax in flattened_taxes):
taxes_res = flattened_taxes.compute_all(
price_unit,
quantity=self.product_uom_qty,
currency=currency,
product=self.product_id,
partner=self.order_id.partner_id,
handle_price_include=False,
)
for tax_res in taxes_res['taxes']:
tax = self.env['account.tax'].browse(tax_res['id'])
if tax.price_include:
price_unit += tax_res['amount']
return price_unit

@api.onchange('product_id')
def product_id_change(self):
if not self.product_id:
Expand Down Expand Up @@ -1665,13 +1702,13 @@ def product_id_change(self):
pricelist=self.order_id.pricelist_id.id,
uom=self.product_uom.id
)

vals.update(name=self.get_sale_order_line_multiline_description_sale(product))

self._compute_tax_id()

if self.order_id.pricelist_id and self.order_id.partner_id:
vals['price_unit'] = self.env['account.tax']._fix_tax_included_price_company(self._get_display_price(product), product.taxes_id, self.tax_id, self.company_id)
vals['price_unit'] = self._get_default_price_unit_from_product(product)

self.update(vals)

title = False
Expand Down Expand Up @@ -1704,7 +1741,7 @@ def product_uom_change(self):
uom=self.product_uom.id,
fiscal_position=self.env.context.get('fiscal_position')
)
self.price_unit = self.env['account.tax']._fix_tax_included_price_company(self._get_display_price(product), product.taxes_id, self.tax_id, self.company_id)
self.price_unit = self._get_default_price_unit_from_product(product)

def name_get(self):
result = []
Expand Down
1 change: 1 addition & 0 deletions addons/sale/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
from . import test_sale_refund
from . import test_sale_signature
from . import test_sale_flow
from . import test_sale_fiscal_position
131 changes: 131 additions & 0 deletions addons/sale/tests/test_sale_fiscal_position.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# -*- coding: utf-8 -*-
from odoo import fields
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.tests import tagged
from odoo.tests.common import Form


@tagged('post_install', '-at_install')
class TestSaleFiscalPosition(AccountTestInvoicingCommon):

def test_fiscal_pos_taxes_mapping_price_included_to_price_excluded(self):
''' Test mapping a price-included tax (10%) with a price-excluded tax (20%) on a price_unit of 110.0.
The price_unit should be 100.0 after applying the fiscal position.
'''
tax_price_include = self.env['account.tax'].create({
'name': '10% incl',
'type_tax_use': 'sale',
'amount_type': 'percent',
'amount': 10,
'price_include': True,
'include_base_amount': True,
})
tax_price_exclude = self.env['account.tax'].create({
'name': '15% excl',
'type_tax_use': 'sale',
'amount_type': 'percent',
'amount': 15,
})

fiscal_position = self.env['account.fiscal.position'].create({
'name': 'fiscal_pos_a',
'tax_ids': [
(0, None, {
'tax_src_id': tax_price_include.id,
'tax_dest_id': tax_price_exclude.id,
}),
],
})

product = self.env['product.product'].create({
'name': 'product',
'uom_id': self.env.ref('uom.product_uom_unit').id,
'lst_price': 110.0,
'taxes_id': [(6, 0, tax_price_include.ids)],
})

so_form = Form(self.env['sale.order'])
so_form.partner_id = self.partner_a
so_form.date_order = fields.Date.from_string('2019-01-01')
so_form.fiscal_position_id = fiscal_position
so_form.pricelist_id = self.env.ref('product.list0')
with so_form.order_line.new() as line:
line.product_id = product
so = so_form.save()

self.assertRecordValues(so.order_line, [{
'price_unit': 100.0,
'tax_id': tax_price_exclude.ids,
}])

uom_dozen = self.env.ref('uom.product_uom_dozen')
with Form(so) as so_form:
with so_form.order_line.edit(0) as line_form:
line_form.product_uom = uom_dozen

self.assertRecordValues(so.order_line, [{
'price_unit': 1200.0,
'tax_id': tax_price_exclude.ids,
}])

def test_fiscal_pos_taxes_mapping_price_included_to_price_included(self):
''' Test mapping a price-included tax (10%) with another price-included tax (20%) on a price_unit of 110.0.
The price_unit should be 120.0 after applying the fiscal position.
'''
tax_price_include_1 = self.env['account.tax'].create({
'name': '10% incl',
'type_tax_use': 'sale',
'amount_type': 'percent',
'amount': 10,
'price_include': True,
'include_base_amount': True,
})
tax_price_include_2 = self.env['account.tax'].create({
'name': '20% incl',
'type_tax_use': 'sale',
'amount_type': 'percent',
'amount': 20,
'price_include': True,
'include_base_amount': True,
})

fiscal_position = self.env['account.fiscal.position'].create({
'name': 'fiscal_pos_a',
'tax_ids': [
(0, None, {
'tax_src_id': tax_price_include_1.id,
'tax_dest_id': tax_price_include_2.id,
}),
],
})

product = self.env['product.product'].create({
'name': 'product',
'uom_id': self.env.ref('uom.product_uom_unit').id,
'lst_price': 110.0,
'taxes_id': [(6, 0, tax_price_include_1.ids)],
})

so_form = Form(self.env['sale.order'])
so_form.partner_id = self.partner_a
so_form.date_order = fields.Date.from_string('2019-01-01')
so_form.fiscal_position_id = fiscal_position
so_form.pricelist_id = self.env.ref('product.list0')
with so_form.order_line.new() as line:
line.product_id = product
so = so_form.save()

self.assertRecordValues(so.order_line, [{
'price_unit': 120.0,
'tax_id': tax_price_include_2.ids,
}])

uom_dozen = self.env.ref('uom.product_uom_dozen')
with Form(so) as so_form:
with so_form.order_line.edit(0) as line_form:
line_form.product_uom = uom_dozen

self.assertRecordValues(so.order_line, [{
'price_unit': 1440.0,
'tax_id': tax_price_include_2.ids,
}])

0 comments on commit 8382f19

Please sign in to comment.