[FIX] account,sale: fiscal position map tax incl. to incl.
Create a 21% tax included in price Create a 6% tax included in price Create a fiscal position that will replace the 21% tax included by the 6% tax included Create a customer and assign this fiscal position Create a product : Price = 121€ Tax = 21% tax included Create a SO with this customer and this product Create an invoice (from scratch) with this customer and this product SO : the unit price is 100,00€ Invoice : the unit price is 106,00€ Fix by creating a common method to calculate the price_unit opw-2699793 closes odoo/odoo#86514 X-original-commit: 8ab56af6827a640a3f41a87905778432b3786c00 Signed-off-by: Laurent Smet <las@odoo.com> Signed-off-by: Grazioso Andrea (agr) <agr@odoo.com>
This commit is contained in:
@@ -3821,67 +3821,21 @@ class AccountMoveLine(models.Model):
|
||||
|
||||
if not self.product_id:
|
||||
return 0.0
|
||||
|
||||
company = self.move_id.company_id
|
||||
currency = self.move_id.currency_id
|
||||
company_currency = company.currency_id
|
||||
product_uom = self.product_id.uom_id
|
||||
fiscal_position = self.move_id.fiscal_position_id
|
||||
is_refund_document = self.move_id.move_type in ('out_refund', 'in_refund')
|
||||
move_date = self.move_id.date or fields.Date.context_today(self)
|
||||
|
||||
if self.move_id.is_sale_document(include_receipts=True):
|
||||
product_price_unit = self.product_id.lst_price
|
||||
product_taxes = self.product_id.taxes_id
|
||||
document_type = 'sale'
|
||||
elif self.move_id.is_purchase_document(include_receipts=True):
|
||||
product_price_unit = self.product_id.standard_price
|
||||
product_taxes = self.product_id.supplier_taxes_id
|
||||
document_type = 'purchase'
|
||||
else:
|
||||
return 0.0
|
||||
product_taxes = product_taxes.filtered(lambda tax: tax.company_id == company)
|
||||
document_type = 'other'
|
||||
|
||||
# Apply unit of measure.
|
||||
if self.product_uom_id and self.product_uom_id != product_uom:
|
||||
product_price_unit = product_uom._compute_price(product_price_unit, self.product_uom_id)
|
||||
|
||||
# Apply fiscal position.
|
||||
if product_taxes and fiscal_position:
|
||||
product_taxes_after_fp = fiscal_position.map_tax(product_taxes)
|
||||
|
||||
if set(product_taxes.ids) != set(product_taxes_after_fp.ids):
|
||||
flattened_taxes_before_fp = product_taxes._origin.flatten_taxes_hierarchy()
|
||||
if any(tax.price_include for tax in flattened_taxes_before_fp):
|
||||
taxes_res = flattened_taxes_before_fp.compute_all(
|
||||
product_price_unit,
|
||||
quantity=1.0,
|
||||
currency=company_currency,
|
||||
product=self.product_id,
|
||||
partner=self.partner_id,
|
||||
is_refund=is_refund_document,
|
||||
)
|
||||
product_price_unit = company_currency.round(taxes_res['total_excluded'])
|
||||
|
||||
flattened_taxes_after_fp = product_taxes_after_fp._origin.flatten_taxes_hierarchy()
|
||||
if any(tax.price_include for tax in flattened_taxes_after_fp):
|
||||
taxes_res = flattened_taxes_after_fp.compute_all(
|
||||
product_price_unit,
|
||||
quantity=1.0,
|
||||
currency=company_currency,
|
||||
product=self.product_id,
|
||||
partner=self.partner_id,
|
||||
is_refund=is_refund_document,
|
||||
handle_price_include=False,
|
||||
)
|
||||
for tax_res in taxes_res['taxes']:
|
||||
tax = self.env['account.tax'].browse(tax_res['id'])
|
||||
if tax.price_include:
|
||||
product_price_unit += tax_res['amount']
|
||||
|
||||
# Apply currency rate.
|
||||
if currency and currency != company_currency:
|
||||
product_price_unit = company_currency._convert(product_price_unit, currency, company, move_date)
|
||||
|
||||
return product_price_unit
|
||||
return self.product_id._get_tax_included_unit_price(
|
||||
self.move_id.company_id,
|
||||
self.move_id.currency_id,
|
||||
self.move_id.date,
|
||||
document_type,
|
||||
fiscal_position=self.move_id.fiscal_position_id,
|
||||
product_uom=self.product_uom_id
|
||||
)
|
||||
|
||||
def _get_computed_account(self):
|
||||
self.ensure_one()
|
||||
|
||||
@@ -83,3 +83,76 @@ class ProductProduct(models.Model):
|
||||
|
||||
def _get_product_accounts(self):
|
||||
return self.product_tmpl_id._get_product_accounts()
|
||||
|
||||
@api.model
|
||||
def _get_tax_included_unit_price(self, company, currency, document_date, document_type,
|
||||
is_refund_document=False, product_uom=None, product_currency=None,
|
||||
product_price_unit=None, product_taxes=None, fiscal_position=None
|
||||
):
|
||||
""" Helper to get the price unit from different models.
|
||||
This is needed to compute the same unit price in different models (sale order, account move, etc.) with same parameters.
|
||||
"""
|
||||
|
||||
product = self
|
||||
|
||||
assert document_type
|
||||
|
||||
if product_uom is None:
|
||||
product_uom = product.uom_id
|
||||
if not product_currency:
|
||||
if document_type == 'sale':
|
||||
product_currency = product.currency_id
|
||||
elif document_type == 'purchase':
|
||||
product_currency = company.currency_id
|
||||
if product_price_unit is None:
|
||||
if document_type == 'sale':
|
||||
product_price_unit = product.with_company(company).lst_price
|
||||
elif document_type == 'purchase':
|
||||
product_price_unit = product.with_company(company).standard_price
|
||||
else:
|
||||
return 0.0
|
||||
if product_taxes is None:
|
||||
if document_type == 'sale':
|
||||
product_taxes = product.taxes_id.filtered(lambda x: x.company_id == company)
|
||||
elif document_type == 'purchase':
|
||||
product_taxes = product.supplier_taxes_id.filtered(lambda x: x.company_id == company)
|
||||
# Apply unit of measure.
|
||||
if product_uom and product.uom_id != product_uom:
|
||||
product_price_unit = product.uom_id._compute_price(product_price_unit, product_uom)
|
||||
|
||||
# Apply fiscal position.
|
||||
if product_taxes and fiscal_position:
|
||||
product_taxes_after_fp = fiscal_position.map_tax(product_taxes)
|
||||
|
||||
if set(product_taxes.ids) != set(product_taxes_after_fp.ids):
|
||||
flattened_taxes_before_fp = product_taxes._origin.flatten_taxes_hierarchy()
|
||||
if any(tax.price_include for tax in flattened_taxes_before_fp):
|
||||
taxes_res = flattened_taxes_before_fp.compute_all(
|
||||
product_price_unit,
|
||||
quantity=1.0,
|
||||
currency=currency,
|
||||
product=product,
|
||||
is_refund=is_refund_document,
|
||||
)
|
||||
product_price_unit = taxes_res['total_excluded']
|
||||
|
||||
flattened_taxes_after_fp = product_taxes_after_fp._origin.flatten_taxes_hierarchy()
|
||||
if any(tax.price_include for tax in flattened_taxes_after_fp):
|
||||
taxes_res = flattened_taxes_after_fp.compute_all(
|
||||
product_price_unit,
|
||||
quantity=1.0,
|
||||
currency=currency,
|
||||
product=product,
|
||||
is_refund=is_refund_document,
|
||||
handle_price_include=False,
|
||||
)
|
||||
for tax_res in taxes_res['taxes']:
|
||||
tax = self.env['account.tax'].browse(tax_res['id'])
|
||||
if tax.price_include:
|
||||
product_price_unit += tax_res['amount']
|
||||
|
||||
# Apply currency rate.
|
||||
if currency != product_currency:
|
||||
product_price_unit = product_currency._convert(product_price_unit, currency, company, document_date)
|
||||
|
||||
return product_price_unit
|
||||
|
||||
@@ -410,8 +410,16 @@ class SaleOrderLine(models.Model):
|
||||
line.price_unit = 0.0
|
||||
else:
|
||||
price = line._get_display_price()
|
||||
line.price_unit = self.env['account.tax']._fix_tax_included_price_company(
|
||||
price, line.product_id.taxes_id, line.tax_id, line.company_id)
|
||||
line.price_unit = line.product_id._get_tax_included_unit_price(
|
||||
line.company_id,
|
||||
line.order_id.currency_id,
|
||||
line.order_id.date_order,
|
||||
'sale',
|
||||
fiscal_position=line.order_id.fiscal_position_id,
|
||||
product_price_unit=price,
|
||||
product_currency=line.currency_id
|
||||
)
|
||||
|
||||
|
||||
def _get_display_price(self):
|
||||
self.ensure_one()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
from .common import TestSaleCommon
|
||||
from odoo.fields import Command
|
||||
from odoo.tests import tagged
|
||||
from odoo.tests import tagged, Form
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
@@ -148,6 +148,62 @@ class TestSaleOrder(TestSaleCommon):
|
||||
self.assertEqual(line.discount, 0.0, 'Pricelist of SO should not be applied on an order line')
|
||||
self.assertEqual(line.price_unit, line.product_id.list_price, 'Unit price of order line should be a sale price as the pricelist not applied on the other category\'s product')
|
||||
|
||||
def test_fiscalposition_application(self):
|
||||
"""Test application of a fiscal position mapping
|
||||
price included to price included tax
|
||||
"""
|
||||
|
||||
uom = self.env['uom.uom'].search([('name', '=', 'Units')])
|
||||
pricelist = self.env['product.pricelist'].search([('name', '=', 'Public Pricelist')])
|
||||
|
||||
partner = self.env['res.partner'].create({
|
||||
'name': "George"
|
||||
})
|
||||
tax_include_src = self.env['account.tax'].create({
|
||||
'name': "Include tax",
|
||||
'amount': 21.00,
|
||||
'price_include': True,
|
||||
})
|
||||
tax_include_dst = self.env['account.tax'].create({
|
||||
'name': "Exclude tax",
|
||||
'amount': 6.00,
|
||||
'price_include': True,
|
||||
})
|
||||
|
||||
product_tmpl = self.env['product.template'].create({
|
||||
'name': "Voiture",
|
||||
'list_price': 121,
|
||||
'taxes_id': [(6, 0, [tax_include_src.id])]
|
||||
})
|
||||
|
||||
product_product = product_tmpl.product_variant_id
|
||||
|
||||
fpos = self.env['account.fiscal.position'].create({
|
||||
'name': "fiscal position",
|
||||
'sequence': 1
|
||||
})
|
||||
|
||||
self.env['account.fiscal.position.tax'].create({
|
||||
'position_id' :fpos.id,
|
||||
'tax_src_id': tax_include_src.id,
|
||||
'tax_dest_id': tax_include_dst.id
|
||||
})
|
||||
|
||||
# Create the SO with one SO line and apply a pricelist and fiscal position on it
|
||||
order_form = Form(self.env['sale.order'].with_context(tracking_disable=True))
|
||||
order_form.partner_id = partner
|
||||
order_form.pricelist_id = pricelist
|
||||
order_form.fiscal_position_id = fpos
|
||||
with order_form.order_line.new() as line:
|
||||
line.name = product_product.name
|
||||
line.product_id = product_product
|
||||
line.product_uom_qty = 1.0
|
||||
line.product_uom = uom
|
||||
sale_order = order_form.save()
|
||||
|
||||
# Check the unit price of SO line
|
||||
self.assertRecordValues(sale_order.order_line, [{'price_unit': 106}])
|
||||
|
||||
def test_pricelist_application(self):
|
||||
""" Test different prices are correctly applied based on dates """
|
||||
support_product = self.env['product.product'].create({
|
||||
|
||||
Reference in New Issue
Block a user